From afd22ddc99af306aa476be7eaf9f02ada941c97b Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Tue, 28 Feb 2012 11:01:15 -0800 Subject: (#12357) Fix root_home fact on Windows Without this patch the root_home fact fails on windows. This patch fixes the problem by only calling methods on the object returned by the `getent passwd root` command if the object evaluates to true. Because there is no root account on Windows the code block simply returns `nil` which makes the Facter fact undefined on Windows platforms. The root cause of the failure is that we always expected the command to succeed and return something useful, and it may not on all supported platforms. --- lib/facter/root_home.rb | 4 +++- spec/unit/facter/root_home_spec.rb | 12 +++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/facter/root_home.rb b/lib/facter/root_home.rb index 61fcf39..8249f7d 100644 --- a/lib/facter/root_home.rb +++ b/lib/facter/root_home.rb @@ -7,7 +7,9 @@ module Facter::Util::RootHome def get_root_home root_ent = Facter::Util::Resolution.exec("getent passwd root") # The home directory is the sixth element in the passwd entry - root_ent.split(":")[5] + # If the platform doesn't have getent, root_ent will be nil and we should + # return it straight away. + root_ent && root_ent.split(":")[5] end end end diff --git a/spec/unit/facter/root_home_spec.rb b/spec/unit/facter/root_home_spec.rb index 8946d9d..ce80684 100644 --- a/spec/unit/facter/root_home_spec.rb +++ b/spec/unit/facter/root_home_spec.rb @@ -30,13 +30,11 @@ describe Facter::Util::RootHome do end end context "windows" do - let(:root_ent) { "FIXME TBD on Windows" } - let(:expected_root_home) { "FIXME TBD on Windows" } - - it "should return FIXME TBD on windows" do - pending "FIXME: TBD on windows" - Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) - Facter::Util::RootHome.get_root_home.should == expected_root_home + before :each do + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(nil) + end + it "should be nil on windows" do + Facter::Util::RootHome.get_root_home.should be_nil end end end -- cgit v1.2.3 From a452f6a9af21d9ec5f34e6cda40af0ec3971b422 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Wed, 29 Feb 2012 12:47:08 -0800 Subject: (#12357) Add puppet_vardir custom fact Without this patch the PE modules don't have a way to identify a filesystem path where it's OK to place variable data related to managing the target node. This is a problem when a module like pe_compliance needs to write a wrapper script to the node's filesystem. This patch addresses the problem by exposing the node's Puppet[:vardir] setting as a Facter fact. This fact value will be set to `nil` if Puppet is not loaded into memory. If Puppet is loaded, e.g. using `facter --puppet` or using `puppet agent` or `puppet apply` then the fact will automatically set the value to Puppet[:vardir] The value of this setting is subject to Puppet's run_mode. This patch implements a new utility method in the standard library module named `Facter::Util::PuppetSettings.with_puppet`. The method accepts a block and will only invoke the block if the Puppet library is loaded into the Ruby process. If Puppet is not loaded, the method always returns nil. This makes it easy to define Facter facts that only give values if Puppet is loaded in memory. --- README_DEVELOPER.markdown | 35 +++++++++++++++++++++++++++ lib/facter/puppet_vardir.rb | 16 ++++++++++++ lib/facter/util/puppet_settings.rb | 17 +++++++++++++ spec/unit/facter/util/puppet_settings_spec.rb | 35 +++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 README_DEVELOPER.markdown create mode 100644 lib/facter/puppet_vardir.rb create mode 100644 lib/facter/util/puppet_settings.rb create mode 100644 spec/unit/facter/util/puppet_settings_spec.rb diff --git a/README_DEVELOPER.markdown b/README_DEVELOPER.markdown new file mode 100644 index 0000000..e8aa27a --- /dev/null +++ b/README_DEVELOPER.markdown @@ -0,0 +1,35 @@ +Puppet Specific Facts +===================== + +Facter is meant to stand alone and apart from Puppet. However, Facter often +runs inside Puppet and all custom facts included in the stdlib module will +almost always be evaluated in the context of Puppet and Facter working +together. + +Still, we don't want to write custom facts that blow up in the users face if +Puppet is not loaded in memory. This is often the case if the user run +`facter` without also supplying the `--puppet` flag. + +Ah! But Jeff, the custom fact won't be in the `$LOAD_PATH` unless the user +supplies `--facter`! You might say... + +Not (always) true I say! If the user happens to have a CWD of +`/stdlib/lib` then the facts will automatically be evaluated and +blow up. + +In any event, it's pretty easy to write a fact that has no value if Puppet is +not loaded. Simply do it like this: + + Facter.add(:node_vardir) do + setcode do + # This will be nil if Puppet is not available. + Facter::Util::PuppetSettings.with_puppet do + Puppet[:vardir] + end + end + end + +The `Facter::Util::PuppetSettings.with_puppet` method accepts a block and +yields to it only if the Puppet library is loaded. If the Puppet library is +not loaded, then the method silently returns `nil` which Facter interprets as +an undefined fact value. The net effect is that the fact won't be set. diff --git a/lib/facter/puppet_vardir.rb b/lib/facter/puppet_vardir.rb new file mode 100644 index 0000000..755e33c --- /dev/null +++ b/lib/facter/puppet_vardir.rb @@ -0,0 +1,16 @@ +# This facter fact returns the value of the Puppet vardir setting for the node +# running puppet or puppet agent. The intent is to enable Puppet modules to +# automatically have insight into a place where they can place variable data, +# regardless of the node's platform. +# +# The value should be directly usable in a File resource path attribute. +require 'facter/util/puppet_settings' + +Facter.add(:puppet_vardir) do + setcode do + # This will be nil if Puppet is not available. + Facter::Util::PuppetSettings.with_puppet do + Puppet[:vardir] + end + end +end diff --git a/lib/facter/util/puppet_settings.rb b/lib/facter/util/puppet_settings.rb new file mode 100644 index 0000000..c8c8363 --- /dev/null +++ b/lib/facter/util/puppet_settings.rb @@ -0,0 +1,17 @@ +module Facter + module Util + module PuppetSettings + class << self + def with_puppet + begin + Module.const_get("Puppet") + rescue NameError + nil + else + yield + end + end + end + end + end +end diff --git a/spec/unit/facter/util/puppet_settings_spec.rb b/spec/unit/facter/util/puppet_settings_spec.rb new file mode 100644 index 0000000..c3ce6ea --- /dev/null +++ b/spec/unit/facter/util/puppet_settings_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require 'facter/util/puppet_settings' + +describe Facter::Util::PuppetSettings do + + describe "#with_puppet" do + context "Without Puppet loaded" do + before(:each) do + Module.expects(:const_get).with("Puppet").raises(NameError) + end + + it 'should be nil' do + subject.with_puppet { Puppet[:vardir] }.should be_nil + end + it 'should not yield to the block' do + Puppet.expects(:[]).never + subject.with_puppet { Puppet[:vardir] }.should be_nil + end + end + context "With Puppet loaded" do + module Puppet; end + let(:vardir) { "/var/lib/puppet" } + + before :each do + Puppet.expects(:[]).with(:vardir).returns vardir + end + it 'should yield to the block' do + subject.with_puppet { Puppet[:vardir] } + end + it 'should return the nodes vardir' do + subject.with_puppet { Puppet[:vardir] }.should eq vardir + end + end + end +end -- cgit v1.2.3 From 369f7304310f8cff7d3c0cb81932aa8be1488ac2 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Tue, 28 Feb 2012 11:53:15 -0800 Subject: (#12357) Make facter_dot_d look in Puppet[:confdir]/facts.d On Windows, we have no folders that match up to the default set of directories the facter_dot_d fact looks in by default. This is a problem because the Puppet Enterprise installer writes out the following facts by default, and our modules require them to be present: % cat /etc/puppetlabs/facter/facts.d/puppet_enterprise_installer.txt fact_stomp_port=61613 fact_stomp_server=puppetmaster fact_is_puppetagent=true fact_is_puppetmaster=true fact_is_puppetconsole=true On windows, the Puppet confdir is quite variable. On 2003 systems we default to the All Users application data directory. On 2008 systems we default to the ProgramData directory. The actual configuration directory varies depending on the Puppet or Puppet Enterprise branding. In order to simplify all of this variable behavior, this patch fixes the problem by automatically looking for facts in `%COMMON_APPDATA%/PuppetLabs/facter/facts.d` This patch paves the way for the MSI installer to use an IniFile element to write custom facts during installation. --- lib/facter/facter_dot_d.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/facter/facter_dot_d.rb b/lib/facter/facter_dot_d.rb index b94aacd..8543c7c 100644 --- a/lib/facter/facter_dot_d.rb +++ b/lib/facter/facter_dot_d.rb @@ -11,6 +11,9 @@ # The cache is stored in /tmp/facts_cache.yaml as a mode # 600 file and will have the end result of not calling your # fact scripts more often than is needed + +require 'facter/util/puppet_settings' + class Facter::Util::DotD require 'yaml' @@ -182,3 +185,10 @@ end Facter::Util::DotD.new("/etc/facter/facts.d").create Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create + +# Windows has a different configuration directory that defaults to a vendor +# specific sub directory of the %COMMON_APPDATA% directory. +if Dir.const_defined? 'COMMON_APPDATA' then + windows_facts_dot_d = File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'facter', 'facts.d') + Facter::Util::DotD.new(windows_facts_dot_d).create +end -- cgit v1.2.3 From 3310b9e190a85263689db8848c3855c54e0e0212 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Wed, 7 Mar 2012 11:51:31 -0800 Subject: (maint) Stop printing the directory of spec_helper Without this patch every rspec run prints out the directory of the spec_helper script. I think this was just a debugging line or whatever that accidentally got added. --- spec/spec_helper.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 87aac34..ad736d8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,6 @@ dir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift File.join(dir, 'lib') -p dir - # Don't want puppet getting the command line arguments for rake or autotest ARGV.clear -- cgit v1.2.3 From 2ce669c7f8aea6b66b2150798a5ef4d253b00a2e Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Wed, 7 Mar 2012 11:52:30 -0800 Subject: (#12357) Add validate_absolute_path() function This patch adds a new function to validate if a string is an absolute filesystem path or not. The intent of this is to make this functionality generic and reusable. Josh left a comment in another pull request I had: If node_installdir or $node_vardir is not defined, then we should raise an error, otherwise we may create a scheduled task to an untrusted directory. One solution to this comment is to validate the Puppet variable is an absolute path. Examples of this function look like: function_validate_absolute_path Using Puppet::Parser::Scope.new Garbage inputs validate_absolute_path(nil) should fail validate_absolute_path([nil]) should fail validate_absolute_path({"foo"=>"bar"}) should fail validate_absolute_path({}) should fail validate_absolute_path("") should fail relative paths validate_absolute_path("relative1") should fail validate_absolute_path(".") should fail validate_absolute_path("..") should fail validate_absolute_path("./foo") should fail validate_absolute_path("../foo") should fail validate_absolute_path("etc/puppetlabs/puppet") should fail validate_absolute_path("opt/puppet/bin") should fail absolute paths validate_absolute_path("C:/") should not fail validate_absolute_path("C:\\") should not fail validate_absolute_path("C:\\WINDOWS\\System32") should not fail validate_absolute_path("C:/windows/system32") should not fail validate_absolute_path("X:/foo/bar") should not fail validate_absolute_path("X:\\foo\\bar") should not fail validate_absolute_path("/var/tmp") should not fail validate_absolute_path("/var/lib/puppet") should not fail validate_absolute_path("/var/opt/../lib/puppet") should not fail validate_absolute_path("C:\\Program Files (x86)\\Puppet Labs\\Puppet Enterprise") should not fail validate_absolute_path("C:/Program Files (x86)/Puppet Labs/Puppet Enterprise") should not fail Finished in 0.05637 seconds 23 examples, 0 failures --- .../parser/functions/validate_absolute_path.rb | 38 +++++++++++ .../functions/validate_absolute_path_spec.rb | 76 ++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 lib/puppet/parser/functions/validate_absolute_path.rb create mode 100644 spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb diff --git a/lib/puppet/parser/functions/validate_absolute_path.rb b/lib/puppet/parser/functions/validate_absolute_path.rb new file mode 100644 index 0000000..46f86ec --- /dev/null +++ b/lib/puppet/parser/functions/validate_absolute_path.rb @@ -0,0 +1,38 @@ +module Puppet::Parser::Functions + newfunction(:validate_absolute_path, :doc => <<-'ENDHEREDOC') do |args| + Validate the string represents an absolute path in the filesystem. This function works + for windows and unix style paths. + + The following values will pass: + + $my_path = "C:/Program Files (x86)/Puppet Labs/Puppet" + validate_absolute_path($my_path) + $my_path2 = "/var/lib/puppet" + validate_absolute_path($my_path2) + + + The following values will fail, causing compilation to abort: + + validate_absolute_path(true) + validate_absolute_path([ 'var/lib/puppet', '/var/foo' ]) + validate_absolute_path([ '/var/lib/puppet', 'var/foo' ]) + $undefined = undef + validate_absolute_path($undefined) + + ENDHEREDOC + + require 'puppet/util' + + unless args.length > 0 then + raise Puppet::ParseError, ("validate_absolute_path(): wrong number of arguments (#{args.length}; must be > 0)") + end + + args.each do |arg| + # This logic was borrowed from + # [lib/puppet/file_serving/base.rb](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/file_serving/base.rb) + unless Puppet::Util.absolute_path?(arg, :posix) or Puppet::Util.absolute_path?(arg, :windows) + raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") + end + end + end +end diff --git a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb new file mode 100644 index 0000000..2b44f50 --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_absolute_path) do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + let(:scope) do + scope = Puppet::Parser::Scope.new + end + + # The subject of these examplres is the method itself. + subject do + scope.method :function_validate_absolute_path + end + + context 'Using Puppet::Parser::Scope.new' do + + describe 'Garbage inputs' do + paths = [ + nil, + [ nil ], + { 'foo' => 'bar' }, + { }, + '', + ] + + paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should fail" do + expect { subject.call [path] }.to raise_error Puppet::ParseError + end + end + end + describe 'relative paths' do + paths = %w{ + relative1 + . + .. + ./foo + ../foo + etc/puppetlabs/puppet + opt/puppet/bin + } + + paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should fail" do + expect { subject.call [path] }.to raise_error Puppet::ParseError + end + end + end + describe 'absolute paths' do + paths = %w{ + C:/ + C:\\ + C:\\WINDOWS\\System32 + C:/windows/system32 + X:/foo/bar + X:\\foo\\bar + /var/tmp + /var/lib/puppet + /var/opt/../lib/puppet + } + + paths = paths + [ + 'C:\\Program Files (x86)\\Puppet Labs\\Puppet Enterprise', + 'C:/Program Files (x86)/Puppet Labs/Puppet Enterprise', + ] + + paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should not fail" do + expect { subject.call [path] }.not_to raise_error + end + end + end + end +end -- cgit v1.2.3 From 41b07232e464f25403a8f9c786ec0061bf6dc40e Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Wed, 7 Mar 2012 15:01:11 -0800 Subject: (#12357) Add ability to display an error message from validate_re I've seen a number of times the following error displayed to the end user: validate_re(): "" does not match "^true$|^false$" at /p/t/f.pp:40 This is an absolutely horrific error message. I'm to blame for it. Users stumble over this quite often and they shouldn't have to go read the code to sort out what's happening. This patch makes an effort to fix the problem by adding a third, optional, argument to validate_re(). This third argument will be the message thrown back in the exception which will be displayed to the end user. This sets the stage for nicer error messages coming from modules we write. This patch is backwards compatible but is a new feature. --- lib/puppet/parser/functions/validate_re.rb | 15 ++-- .../puppet/parser/functions/validate_re_spec.rb | 80 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 spec/unit/puppet/parser/functions/validate_re_spec.rb diff --git a/lib/puppet/parser/functions/validate_re.rb b/lib/puppet/parser/functions/validate_re.rb index 8033ca3..9c216d8 100644 --- a/lib/puppet/parser/functions/validate_re.rb +++ b/lib/puppet/parser/functions/validate_re.rb @@ -1,5 +1,4 @@ module Puppet::Parser::Functions - newfunction(:validate_re, :doc => <<-'ENDHEREDOC') do |args| Perform simple validation of a string against one or more regular expressions. The first argument of this function should be a string to @@ -8,6 +7,9 @@ module Puppet::Parser::Functions of the regular expressions match the string passed in, compilation will abort with a parse error. + If a third argument is specified, this will be the error message raised and + seen by the user. + The following strings will validate against the regular expressions: validate_re('one', '^one$') @@ -17,17 +19,20 @@ module Puppet::Parser::Functions validate_re('one', [ '^two', '^three' ]) + A helpful error message can be returned like this: + + validate_re($::puppetversion, '^2.7', 'The $puppetversion fact value does not match 2.7') + ENDHEREDOC - if args.length != 2 then - raise Puppet::ParseError, ("validate_re(): wrong number of arguments (#{args.length}; must be 2)") + if (args.length < 2) or (args.length > 3) then + raise Puppet::ParseError, ("validate_re(): wrong number of arguments (#{args.length}; must be 2 or 3)") end - msg = "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" + msg = args[2] || "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" raise Puppet::ParseError, (msg) unless args[1].any? do |re_str| args[0] =~ Regexp.compile(re_str) end end - end diff --git a/spec/unit/puppet/parser/functions/validate_re_spec.rb b/spec/unit/puppet/parser/functions/validate_re_spec.rb new file mode 100644 index 0000000..c35ae14 --- /dev/null +++ b/spec/unit/puppet/parser/functions/validate_re_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Puppet::Parser::Functions.function(:validate_re) do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + let(:scope) do + scope = Puppet::Parser::Scope.new + end + + # The subject of these examplres is the method itself. + subject do + scope.method :function_validate_re + end + + context 'Using Puppet::Parser::Scope.new' do + + describe 'Garbage inputs' do + inputs = [ + [ nil ], + [ [ nil ] ], + [ { 'foo' => 'bar' } ], + [ { } ], + [ '' ], + [ "one", "one", "MSG to User", "4th arg" ], + ] + + inputs.each do |input| + it "validate_re(#{input.inspect}) should fail" do + expect { subject.call [input] }.to raise_error Puppet::ParseError + end + end + end + + describe 'Valid inputs' do + inputs = [ + [ '/full/path/to/something', '^/full' ], + [ '/full/path/to/something', 'full' ], + [ '/full/path/to/something', ['full', 'absent'] ], + [ '/full/path/to/something', ['full', 'absent'], 'Message to the user' ], + ] + + inputs.each do |input| + it "validate_re(#{input.inspect}) should not fail" do + expect { subject.call input }.not_to raise_error + end + end + end + describe "Valid inputs which should raise an exception without a message" do + # The intent here is to make sure valid inputs raise exceptions when they + # don't specify an error message to display. This is the behvior in + # 2.2.x and prior. + inputs = [ + [ "hello", [ "bye", "later", "adios" ] ], + [ "greetings", "salutations" ], + ] + + inputs.each do |input| + it "validate_re(#{input.inspect}) should fail" do + expect { subject.call input }.to raise_error /validate_re.*?does not match/ + end + end + end + describe "Nicer Error Messages" do + # The intent here is to make sure the function returns the 3rd argument + # in the exception thrown + inputs = [ + [ "hello", [ "bye", "later", "adios" ], "MSG to User" ], + [ "greetings", "salutations", "Error, greetings does not match salutations" ], + ] + + inputs.each do |input| + it "validate_re(#{input.inspect}) should fail" do + expect { subject.call input }.to raise_error /#{input[2]}/ + end + end + end + end +end -- cgit v1.2.3 From 010fb5e5685fec7aefe9e62901ebcdf06af19d5a Mon Sep 17 00:00:00 2001 From: Ken Barber Date: Wed, 7 Mar 2012 19:55:21 -0800 Subject: (#13018) Fix missing method any? message for ruby 1.9.x The any? method doesn't exist for 1.9.x, this converts a string to a single element array to work around the problem. --- lib/puppet/parser/functions/validate_re.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/parser/functions/validate_re.rb b/lib/puppet/parser/functions/validate_re.rb index 9c216d8..b5e8ff4 100644 --- a/lib/puppet/parser/functions/validate_re.rb +++ b/lib/puppet/parser/functions/validate_re.rb @@ -30,7 +30,7 @@ module Puppet::Parser::Functions msg = args[2] || "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" - raise Puppet::ParseError, (msg) unless args[1].any? do |re_str| + raise Puppet::ParseError, (msg) unless [args[1]].flatten.any? do |re_str| args[0] =~ Regexp.compile(re_str) end -- cgit v1.2.3 From f156e554d43f7d5517d2fd79bab41f7d9ee73688 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Fri, 9 Mar 2012 14:08:54 -0800 Subject: (maint) Comment Ken's fix to String#any? Just added a comment about why we're doing what we're doing. --- lib/puppet/parser/functions/validate_re.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/puppet/parser/functions/validate_re.rb b/lib/puppet/parser/functions/validate_re.rb index b5e8ff4..ca25a70 100644 --- a/lib/puppet/parser/functions/validate_re.rb +++ b/lib/puppet/parser/functions/validate_re.rb @@ -30,6 +30,8 @@ module Puppet::Parser::Functions msg = args[2] || "validate_re(): #{args[0].inspect} does not match #{args[1].inspect}" + # We're using a flattened array here because we can't call String#any? in + # Ruby 1.9 like we can in Ruby 1.8 raise Puppet::ParseError, (msg) unless [args[1]].flatten.any? do |re_str| args[0] =~ Regexp.compile(re_str) end -- cgit v1.2.3 From 31944c986345a135a3e68247c6d3e644ea369089 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Mon, 12 Mar 2012 15:53:02 -0700 Subject: (#12357) Fix broken compatibility with Puppet 2.6 Without this patch, the previous change set to the validate_absolute_path() parser function contains Puppet 2.6 incompatible changes. stdlib 2.x is compatible with Puppet 2.6. These changes are a problem because we cannot introduce backwards incompatible changes in a minor release. This patch fixes the problem by back porting the implementation of the `Puppet::Util.absolute_path?` from 2.7.x to the function block itself. The function block tests to see if `Puppet::Util.absolute_path?` will respond and if not, falls back to the inline back ported implementation. The spec tests have been updated to simulate the behavior of Puppet 2.6 even when running with Puppet 2.7. --- .../parser/functions/validate_absolute_path.rb | 22 +++++- .../functions/validate_absolute_path_spec.rb | 87 ++++++++++++---------- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/lib/puppet/parser/functions/validate_absolute_path.rb b/lib/puppet/parser/functions/validate_absolute_path.rb index 46f86ec..fe27974 100644 --- a/lib/puppet/parser/functions/validate_absolute_path.rb +++ b/lib/puppet/parser/functions/validate_absolute_path.rb @@ -30,8 +30,26 @@ module Puppet::Parser::Functions args.each do |arg| # This logic was borrowed from # [lib/puppet/file_serving/base.rb](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/file_serving/base.rb) - unless Puppet::Util.absolute_path?(arg, :posix) or Puppet::Util.absolute_path?(arg, :windows) - raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") + + # Puppet 2.7 and beyond will have Puppet::Util.absolute_path? Fall back to a back-ported implementation otherwise. + if Puppet::Util.respond_to?(:absolute_path?) then + unless Puppet::Util.absolute_path?(arg, :posix) or Puppet::Util.absolute_path?(arg, :windows) + raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") + end + else + # This code back-ported from 2.7.x's lib/puppet/util.rb Puppet::Util.absolute_path? + # Determine in a platform-specific way whether a path is absolute. This + # defaults to the local platform if none is specified. + # Escape once for the string literal, and once for the regex. + slash = '[\\\\/]' + name = '[^\\\\/]+' + regexes = { + :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i, + :posix => %r!^/!, + } + + rval = (!!(arg =~ regexes[:posix])) || (!!(arg =~ regexes[:windows])) + rval or raise Puppet::ParseError, ("#{arg.inspect} is not an absolute path.") end end end diff --git a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb index 2b44f50..1e0b5ac 100644 --- a/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb +++ b/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb @@ -5,34 +5,67 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do Puppet::Parser::Functions.autoloader.loadall end - let(:scope) do - scope = Puppet::Parser::Scope.new - end - # The subject of these examplres is the method itself. subject do - scope.method :function_validate_absolute_path + Puppet::Parser::Scope.new.method :function_validate_absolute_path end - context 'Using Puppet::Parser::Scope.new' do + describe "Valid Paths" do + def self.valid_paths + %w{ + C:/ + C:\\ + C:\\WINDOWS\\System32 + C:/windows/system32 + X:/foo/bar + X:\\foo\\bar + /var/tmp + /var/lib/puppet + /var/opt/../lib/puppet + } + end - describe 'Garbage inputs' do - paths = [ + context "Without Puppet::Util.absolute_path? (e.g. Puppet <= 2.6)" do + before :each do + # The intent here is to mock Puppet to behave like Puppet 2.6 does. + # Puppet 2.6 does not have the absolute_path? method. This is only a + # convenience test, stdlib should be run with the Puppet 2.6.x in the + # $LOAD_PATH in addition to 2.7.x and master. + Puppet::Util.expects(:respond_to?).with(:absolute_path?).returns(false) + end + valid_paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should not fail" do + expect { subject.call [path] }.not_to raise_error Puppet::ParseError + end + end + end + + context "Puppet without mocking" do + valid_paths.each do |path| + it "validate_absolute_path(#{path.inspect}) should not fail" do + expect { subject.call [path] }.not_to raise_error Puppet::ParseError + end + end + end + end + + describe 'Invalid paths' do + context 'Garbage inputs' do + [ nil, [ nil ], { 'foo' => 'bar' }, { }, '', - ] - - paths.each do |path| + ].each do |path| it "validate_absolute_path(#{path.inspect}) should fail" do expect { subject.call [path] }.to raise_error Puppet::ParseError end end end - describe 'relative paths' do - paths = %w{ + + context 'Relative paths' do + %w{ relative1 . .. @@ -40,37 +73,11 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do ../foo etc/puppetlabs/puppet opt/puppet/bin - } - - paths.each do |path| + }.each do |path| it "validate_absolute_path(#{path.inspect}) should fail" do expect { subject.call [path] }.to raise_error Puppet::ParseError end end end - describe 'absolute paths' do - paths = %w{ - C:/ - C:\\ - C:\\WINDOWS\\System32 - C:/windows/system32 - X:/foo/bar - X:\\foo\\bar - /var/tmp - /var/lib/puppet - /var/opt/../lib/puppet - } - - paths = paths + [ - 'C:\\Program Files (x86)\\Puppet Labs\\Puppet Enterprise', - 'C:/Program Files (x86)/Puppet Labs/Puppet Enterprise', - ] - - paths.each do |path| - it "validate_absolute_path(#{path.inspect}) should not fail" do - expect { subject.call [path] }.not_to raise_error - end - end - end end end -- cgit v1.2.3