diff options
-rw-r--r-- | README.markdown | 56 | ||||
-rw-r--r-- | lib/puppet/parser/functions/basename.rb | 34 | ||||
-rw-r--r-- | lib/puppet/parser/functions/concat.rb | 18 | ||||
-rw-r--r-- | lib/puppet/parser/functions/validate_absolute_path.rb | 61 | ||||
-rw-r--r-- | lib/puppet/parser/functions/validate_cmd.rb | 21 | ||||
-rwxr-xr-x | spec/acceptance/concat_spec.rb | 22 | ||||
-rwxr-xr-x | spec/functions/concat_spec.rb | 17 | ||||
-rwxr-xr-x | spec/functions/validate_absolute_path_spec.rb | 38 | ||||
-rwxr-xr-x | spec/functions/validate_cmd_spec.rb | 81 | ||||
-rwxr-xr-x | spec/unit/puppet/parser/functions/basename_spec.rb | 46 |
10 files changed, 305 insertions, 89 deletions
diff --git a/README.markdown b/README.markdown index 3bdb8ed..c4022c5 100644 --- a/README.markdown +++ b/README.markdown @@ -109,6 +109,13 @@ If you want to use a standardized set of run stages for Puppet, `include stdlib` Requires an action ('encode', 'decode') and either a plain or base64-encoded string. *Type*: rvalue +* `basename`: Returns the `basename` of a path (optionally stripping an extension). For example: + * ('/path/to/a/file.ext') returns 'file.ext' + * ('relative/path/file.ext') returns 'file.ext' + * ('/path/to/a/file.ext', '.ext') returns 'file' + + *Type*: rvalue + * `bool2num`: Converts a boolean to a number. Converts values: * 'false', 'f', '0', 'n', and 'no' to 0. * 'true', 't', '1', 'y', and 'yes' to 1. @@ -467,27 +474,34 @@ You can also use this with arrays. For example, `unique(["a","a","b","b","c","c" * `uriescape`: Urlencodes a string or array of strings. Requires either a single string or an array as an input. *Type*: rvalue -* `validate_absolute_path`: Validate that 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) - ``` - - *Type*: statement +* `validate_absolute_path`: 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) + $my_path3 = ['C:/Program Files (x86)/Puppet Labs/Puppet','C:/Program Files/Puppet Labs/Puppet'] + validate_absolute_path($my_path3) + $my_path4 = ['/var/lib/puppet','/usr/share/puppet'] + validate_absolute_path($my_path4) + ``` + + The following values will fail, causing compilation to abort: + + ``` + validate_absolute_path(true) + validate_absolute_path('../var/lib/puppet') + validate_absolute_path('var/lib/puppet') + validate_absolute_path([ 'var/lib/puppet', '/var/foo' ]) + validate_absolute_path([ '/var/lib/puppet', 'var/foo' ]) + $undefined = undef + validate_absolute_path($undefined) + ``` + + *Type*: statement * `validate_array`: Validate that all passed values are array data structures. Abort catalog compilation if any value fails this check. diff --git a/lib/puppet/parser/functions/basename.rb b/lib/puppet/parser/functions/basename.rb new file mode 100644 index 0000000..f7e4438 --- /dev/null +++ b/lib/puppet/parser/functions/basename.rb @@ -0,0 +1,34 @@ +module Puppet::Parser::Functions + newfunction(:basename, :type => :rvalue, :doc => <<-EOS + Strips directory (and optional suffix) from a filename + EOS + ) do |arguments| + + if arguments.size < 1 then + raise(Puppet::ParseError, "basename(): No arguments given") + elsif arguments.size > 2 then + raise(Puppet::ParseError, "basename(): Too many arguments given (#{arguments.size})") + else + + unless arguments[0].is_a?(String) + raise(Puppet::ParseError, 'basename(): Requires string as first argument') + end + + if arguments.size == 1 then + rv = File.basename(arguments[0]) + elsif arguments.size == 2 then + + unless arguments[1].is_a?(String) + raise(Puppet::ParseError, 'basename(): Requires string as second argument') + end + + rv = File.basename(arguments[0], arguments[1]) + end + + end + + return rv + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/concat.rb b/lib/puppet/parser/functions/concat.rb index 0d35b07..618e62d 100644 --- a/lib/puppet/parser/functions/concat.rb +++ b/lib/puppet/parser/functions/concat.rb @@ -4,31 +4,35 @@ module Puppet::Parser::Functions newfunction(:concat, :type => :rvalue, :doc => <<-EOS -Appends the contents of array 2 onto array 1. +Appends the contents of multiple arrays into array 1. *Example:* - concat(['1','2','3'],['4','5','6']) + concat(['1','2','3'],['4','5','6'],['7','8','9']) Would result in: - ['1','2','3','4','5','6'] + ['1','2','3','4','5','6','7','8','9'] EOS ) do |arguments| - # Check that 2 arguments have been given ... + # Check that more than 2 arguments have been given ... raise(Puppet::ParseError, "concat(): Wrong number of arguments " + - "given (#{arguments.size} for 2)") if arguments.size != 2 + "given (#{arguments.size} for < 2)") if arguments.size < 2 a = arguments[0] - b = arguments[1] # Check that the first parameter is an array unless a.is_a?(Array) raise(Puppet::ParseError, 'concat(): Requires array to work with') end - result = a + Array(b) + result = a + arguments.shift + + arguments.each do |x| + result = result + Array(x) + end return result end diff --git a/lib/puppet/parser/functions/validate_absolute_path.rb b/lib/puppet/parser/functions/validate_absolute_path.rb index fe27974..b696680 100644 --- a/lib/puppet/parser/functions/validate_absolute_path.rb +++ b/lib/puppet/parser/functions/validate_absolute_path.rb @@ -5,15 +5,20 @@ module Puppet::Parser::Functions The following values will pass: - $my_path = "C:/Program Files (x86)/Puppet Labs/Puppet" + $my_path = 'C:/Program Files (x86)/Puppet Labs/Puppet' validate_absolute_path($my_path) - $my_path2 = "/var/lib/puppet" + $my_path2 = '/var/lib/puppet' validate_absolute_path($my_path2) - + $my_path3 = ['C:/Program Files (x86)/Puppet Labs/Puppet','C:/Program Files/Puppet Labs/Puppet'] + validate_absolute_path($my_path3) + $my_path4 = ['/var/lib/puppet','/usr/share/puppet'] + validate_absolute_path($my_path4) The following values will fail, causing compilation to abort: validate_absolute_path(true) + validate_absolute_path('../var/lib/puppet') + validate_absolute_path('var/lib/puppet') validate_absolute_path([ 'var/lib/puppet', '/var/foo' ]) validate_absolute_path([ '/var/lib/puppet', 'var/foo' ]) $undefined = undef @@ -28,28 +33,36 @@ module Puppet::Parser::Functions 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) - - # 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.") + # put arg to candidate var to be able to replace it + candidates = arg + # if arg is just a string with a path to test, convert it to an array + # to avoid test code duplication + unless arg.is_a?(Array) then + candidates = Array.new(1,arg) + end + # iterate over all pathes within the candidates array + candidates.each do |path| + # This logic was borrowed from + # [lib/puppet/file_serving/base.rb](https://github.com/puppetlabs/puppet/blob/master/lib/puppet/file_serving/base.rb) + # 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?(path, :posix) or Puppet::Util.absolute_path?(path, :windows) + raise Puppet::ParseError, ("#{path.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 = (!!(path =~ regexes[:posix])) || (!!(path =~ regexes[:windows])) + rval or raise Puppet::ParseError, ("#{path.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/lib/puppet/parser/functions/validate_cmd.rb b/lib/puppet/parser/functions/validate_cmd.rb index c6136a5..5df3c60 100644 --- a/lib/puppet/parser/functions/validate_cmd.rb +++ b/lib/puppet/parser/functions/validate_cmd.rb @@ -6,9 +6,9 @@ module Puppet::Parser::Functions Perform validation of a string with an external command. The first argument of this function should be a string to test, and the second argument should be a path to a test command - taking a file as last argument. If the command, launched against - a tempfile containing the passed string, returns a non-null value, - compilation will abort with a parse error. + taking a % as a placeholder for the file path (will default to the end). + If the command, launched against a tempfile containing the passed string, + returns a non-null value, 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. @@ -17,8 +17,12 @@ module Puppet::Parser::Functions Example: + # Defaults to end of path validate_cmd($sudoerscontent, '/usr/sbin/visudo -c -f', 'Visudo failed to validate sudoers content') + # % as file location + validate_cmd($haproxycontent, '/usr/sbin/haproxy -f % -c', 'Haproxy failed to validate config content') + ENDHEREDOC if (args.length < 2) or (args.length > 3) then raise Puppet::ParseError, ("validate_cmd(): wrong number of arguments (#{args.length}; must be 2 or 3)") @@ -34,10 +38,17 @@ module Puppet::Parser::Functions begin tmpfile.write(content) tmpfile.close + + if checkscript =~ /\s%(\s|$)/ + check_with_correct_location = checkscript.gsub(/%/,tmpfile.path) + else + check_with_correct_location = "#{checkscript} #{tmpfile.path}" + end + if Puppet::Util::Execution.respond_to?('execute') - Puppet::Util::Execution.execute("#{checkscript} #{tmpfile.path}") + Puppet::Util::Execution.execute(check_with_correct_location) else - Puppet::Util.execute("#{checkscript} #{tmpfile.path}") + Puppet::Util.execute(check_with_correct_location) end rescue Puppet::ExecutionFailure => detail msg += "\n#{detail}" diff --git a/spec/acceptance/concat_spec.rb b/spec/acceptance/concat_spec.rb index 7bda365..caf2f7d 100755 --- a/spec/acceptance/concat_spec.rb +++ b/spec/acceptance/concat_spec.rb @@ -14,5 +14,27 @@ describe 'concat function', :unless => UNSUPPORTED_PLATFORMS.include?(fact('oper apply_manifest(pp, :catch_failures => true) end + it 'should concat arrays and primitives to array' do + pp = <<-EOS + $output = concat(['1','2','3'],'4','5','6',['7','8','9']) + validate_array($output) + if size($output) != 9 { + fail("${output} should have 9 elements.") + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + it 'should concat multiple arrays to one' do + pp = <<-EOS + $output = concat(['1','2','3'],['4','5','6'],['7','8','9']) + validate_array($output) + if size($output) != 6 { + fail("${output} should have 9 elements.") + } + EOS + + apply_manifest(pp, :catch_failures => true) + end end end diff --git a/spec/functions/concat_spec.rb b/spec/functions/concat_spec.rb index 49cb2ad..49fa6bb 100755 --- a/spec/functions/concat_spec.rb +++ b/spec/functions/concat_spec.rb @@ -4,14 +4,19 @@ require 'spec_helper' describe "the concat function" do let(:scope) { PuppetlabsSpec::PuppetInternals.scope } - it "should raise a ParseError if the client does not provide two arguments" do + it "should raise a ParseError if the client does not provide at least two arguments" do expect { scope.function_concat([]) }.to(raise_error(Puppet::ParseError)) + expect { scope.function_concat([[1]]) }.to(raise_error(Puppet::ParseError)) end it "should raise a ParseError if the first parameter is not an array" do expect { scope.function_concat([1, []])}.to(raise_error(Puppet::ParseError)) end + it "should not raise a ParseError if the client provides more than two arguments" do + expect { scope.function_concat([[1],[2],[3]]) }.not_to raise_error + end + it "should be able to concat an array" do result = scope.function_concat([['1','2','3'],['4','5','6']]) expect(result).to(eq(['1','2','3','4','5','6'])) @@ -32,4 +37,14 @@ describe "the concat function" do result = scope.function_concat([array_original,['4','5','6']]) array_original.should(eq(['1','2','3'])) end + + it "should be able to concat multiple arrays" do + result = scope.function_concat([['1','2','3'],['4','5','6'],['7','8','9']]) + expect(result).to(eq(['1','2','3','4','5','6','7','8','9'])) + end + + it "should be able to concat mix of primitives and arrays to a final array" do + result = scope.function_concat([['1','2','3'],'4',['5','6','7']]) + expect(result).to(eq(['1','2','3','4','5','6','7'])) + end end diff --git a/spec/functions/validate_absolute_path_spec.rb b/spec/functions/validate_absolute_path_spec.rb index 342ae84..36c836b 100755 --- a/spec/functions/validate_absolute_path_spec.rb +++ b/spec/functions/validate_absolute_path_spec.rb @@ -39,6 +39,11 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do expect { subject.call [path] }.not_to raise_error end end + valid_paths do + it "validate_absolute_path(#{valid_paths.inspect}) should not fail" do + expect { subject.call [valid_paths] }.not_to raise_error + end + end end context "Puppet without mocking" do @@ -47,6 +52,11 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do expect { subject.call [path] }.not_to raise_error end end + valid_paths do + it "validate_absolute_path(#{valid_paths.inspect}) should not fail" do + expect { subject.call [valid_paths] }.not_to raise_error + end + end end end @@ -55,6 +65,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do [ nil, [ nil ], + [ nil, nil ], { 'foo' => 'bar' }, { }, '', @@ -66,19 +77,28 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do end context 'Relative paths' do - %w{ - relative1 - . - .. - ./foo - ../foo - etc/puppetlabs/puppet - opt/puppet/bin - }.each do |path| + def self.rel_paths + %w{ + relative1 + . + .. + ./foo + ../foo + etc/puppetlabs/puppet + opt/puppet/bin + } + end + rel_paths.each do |path| it "validate_absolute_path(#{path.inspect}) should fail" do expect { subject.call [path] }.to raise_error Puppet::ParseError end end + rel_paths do + it "validate_absolute_path(#{rel_paths.inspect}) should fail" do + expect { subject.call [rel_paths] }.to raise_error Puppet::ParseError + end + end end end end + diff --git a/spec/functions/validate_cmd_spec.rb b/spec/functions/validate_cmd_spec.rb index a6e68df..7cb9782 100755 --- a/spec/functions/validate_cmd_spec.rb +++ b/spec/functions/validate_cmd_spec.rb @@ -12,37 +12,74 @@ describe Puppet::Parser::Functions.function(:validate_cmd) do scope.method(function_name) end - describe "with an explicit failure message" do - it "prints the failure message on error" do - expect { - subject.call ['', '/bin/false', 'failure message!'] - }.to raise_error Puppet::ParseError, /failure message!/ + context 'with no % placeholder' do + describe "with an explicit failure message" do + it "prints the failure message on error" do + expect { + subject.call ['', '/bin/false', 'failure message!'] + }.to raise_error Puppet::ParseError, /failure message!/ + end end - end - describe "on validation failure" do - it "includes the command error output" do - expect { - subject.call ['', "#{TOUCHEXE} /cant/touch/this"] - }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + describe "on validation failure" do + it "includes the command error output" do + expect { + subject.call ['', "#{TOUCHEXE} /cant/touch/this"] + }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + end + + it "includes the command return value" do + expect { + subject.call ['', '/cant/run/this'] + }.to raise_error Puppet::ParseError, /returned 1\b/ + end end - it "includes the command return value" do - expect { - subject.call ['', '/cant/run/this'] - }.to raise_error Puppet::ParseError, /returned 1\b/ + describe "when performing actual validation" do + it "can positively validate file content" do + expect { subject.call ["non-empty", "#{TESTEXE} -s"] }.to_not raise_error + end + + it "can negatively validate file content" do + expect { + subject.call ["", "#{TESTEXE} -s"] + }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + end end end - describe "when performing actual validation" do - it "can positively validate file content" do - expect { subject.call ["non-empty", "#{TESTEXE} -s"] }.to_not raise_error + context 'with % placeholder' do + describe "with an explicit failure message" do + it "prints the failure message on error" do + expect { + subject.call ['', '/bin/false % -f', 'failure message!'] + }.to raise_error Puppet::ParseError, /failure message!/ + end end + describe "on validation failure" do + it "includes the command error output" do + expect { + subject.call ['', "#{TOUCHEXE} /cant/touch/this"] + }.to raise_error Puppet::ParseError, /(cannot touch|o such file or)/ + end + + it "includes the command return value" do + expect { + subject.call ['', '/cant/run/this % -z'] + }.to raise_error Puppet::ParseError, /Execution of '\/cant\/run\/this .+ -z' returned 1/ + end + end + + describe "when performing actual validation" do + it "can positively validate file content" do + expect { subject.call ["non-empty", "#{TESTEXE} -s %"] }.to_not raise_error + end - it "can negatively validate file content" do - expect { - subject.call ["", "#{TESTEXE} -s"] - }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + it "can negatively validate file content" do + expect { + subject.call ["", "#{TESTEXE} -s %"] + }.to raise_error Puppet::ParseError, /failed to validate.*test -s/ + end end end end diff --git a/spec/unit/puppet/parser/functions/basename_spec.rb b/spec/unit/puppet/parser/functions/basename_spec.rb new file mode 100755 index 0000000..8a2d0dc --- /dev/null +++ b/spec/unit/puppet/parser/functions/basename_spec.rb @@ -0,0 +1,46 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the basename function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + Puppet::Parser::Functions.function("basename").should == "function_basename" + end + + it "should raise a ParseError if there is less than 1 argument" do + lambda { scope.function_basename([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there are more than 2 arguments" do + lambda { scope.function_basename(['a', 'b', 'c']) }.should( raise_error(Puppet::ParseError)) + end + + it "should return basename for an absolute path" do + result = scope.function_basename(['/path/to/a/file.ext']) + result.should(eq('file.ext')) + end + + it "should return basename for a relative path" do + result = scope.function_basename(['path/to/a/file.ext']) + result.should(eq('file.ext')) + end + + it "should strip extention when extension specified (absolute path)" do + result = scope.function_basename(['/path/to/a/file.ext', '.ext']) + result.should(eq('file')) + end + + it "should strip extention when extension specified (relative path)" do + result = scope.function_basename(['path/to/a/file.ext', '.ext']) + result.should(eq('file')) + end + + it "should complain about non-string first argument" do + lambda { scope.function_basename([[]]) }.should( raise_error(Puppet::ParseError)) + end + + it "should complain about non-string second argument" do + lambda { scope.function_basename(['/path/to/a/file.ext', []]) }.should( raise_error(Puppet::ParseError)) + end +end |