diff options
Diffstat (limited to 'lib/puppet/parser/functions')
43 files changed, 696 insertions, 232 deletions
diff --git a/lib/puppet/parser/functions/assert_private.rb b/lib/puppet/parser/functions/assert_private.rb new file mode 100644 index 0000000..66c79cc --- /dev/null +++ b/lib/puppet/parser/functions/assert_private.rb @@ -0,0 +1,29 @@ +# +# assert_private.rb +# + +module Puppet::Parser::Functions + newfunction(:assert_private, :doc => <<-'EOS' + Sets the current class or definition as private. + Calling the class or definition from outside the current module will fail. + EOS + ) do |args| + + raise(Puppet::ParseError, "assert_private(): Wrong number of arguments "+ + "given (#{args.size}}) for 0 or 1)") if args.size > 1 + + scope = self + if scope.lookupvar('module_name') != scope.lookupvar('caller_module_name') + message = nil + if args[0] and args[0].is_a? String + message = args[0] + else + manifest_name = scope.source.name + manifest_type = scope.source.type + message = (manifest_type.to_s == 'hostclass') ? 'Class' : 'Definition' + message += " #{manifest_name} is private" + end + raise(Puppet::ParseError, message) + end + end +end 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/bool2num.rb b/lib/puppet/parser/functions/bool2num.rb index 9a07a8a..6ad6cf4 100644 --- a/lib/puppet/parser/functions/bool2num.rb +++ b/lib/puppet/parser/functions/bool2num.rb @@ -14,30 +14,7 @@ module Puppet::Parser::Functions raise(Puppet::ParseError, "bool2num(): Wrong number of arguments " + "given (#{arguments.size} for 1)") if arguments.size < 1 - value = arguments[0] - klass = value.class - - # We can have either true or false, or string which resembles boolean ... - unless [FalseClass, TrueClass, String].include?(klass) - raise(Puppet::ParseError, 'bool2num(): Requires either ' + - 'boolean or string to work with') - end - - if value.is_a?(String) - # We consider all the yes, no, y, n and so on too ... - value = case value - # - # This is how undef looks like in Puppet ... - # We yield 0 (or false if you wish) in this case. - # - when /^$/, '' then false # Empty string will be false ... - when /^(1|t|y|true|yes)$/ then true - when /^(0|f|n|false|no)$/ then false - when /^(undef|undefined)$/ then false # This is not likely to happen ... - else - raise(Puppet::ParseError, 'bool2num(): Unknown type of boolean given') - end - end + value = function_str2bool([arguments[0]]) # We have real boolean values as well ... result = value ? 1 : 0 diff --git a/lib/puppet/parser/functions/capitalize.rb b/lib/puppet/parser/functions/capitalize.rb index 640d00b..98b2d16 100644 --- a/lib/puppet/parser/functions/capitalize.rb +++ b/lib/puppet/parser/functions/capitalize.rb @@ -13,9 +13,8 @@ module Puppet::Parser::Functions "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'capitalize(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/ceiling.rb b/lib/puppet/parser/functions/ceiling.rb new file mode 100644 index 0000000..5f3b10b --- /dev/null +++ b/lib/puppet/parser/functions/ceiling.rb @@ -0,0 +1,25 @@ +module Puppet::Parser::Functions + newfunction(:ceiling, :type => :rvalue, :doc => <<-EOS + Returns the smallest integer greater or equal to the argument. + Takes a single numeric value as an argument. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "ceiling(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + begin + arg = Float(arguments[0]) + rescue TypeError, ArgumentError => e + raise(Puppet::ParseError, "ceiling(): Wrong argument type " + + "given (#{arguments[0]} for Numeric)") + end + + raise(Puppet::ParseError, "ceiling(): Wrong argument type " + + "given (#{arg.class} for Numeric)") if arg.is_a?(Numeric) == false + + arg.ceil + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/chomp.rb b/lib/puppet/parser/functions/chomp.rb index 4564a00..c55841e 100644 --- a/lib/puppet/parser/functions/chomp.rb +++ b/lib/puppet/parser/functions/chomp.rb @@ -14,9 +14,8 @@ module Puppet::Parser::Functions "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'chomp(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/chop.rb b/lib/puppet/parser/functions/chop.rb index f242af3..b24ab78 100644 --- a/lib/puppet/parser/functions/chop.rb +++ b/lib/puppet/parser/functions/chop.rb @@ -16,9 +16,8 @@ module Puppet::Parser::Functions "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'chop(): Requires either an ' + 'array or string to work with') end 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/delete.rb b/lib/puppet/parser/functions/delete.rb index d03a293..f548b44 100644 --- a/lib/puppet/parser/functions/delete.rb +++ b/lib/puppet/parser/functions/delete.rb @@ -17,27 +17,30 @@ string, or key from a hash. delete({'a'=>1,'b'=>2,'c'=>3}, 'b') Would return: {'a'=>1,'c'=>3} + delete({'a'=>1,'b'=>2,'c'=>3}, ['b','c']) + Would return: {'a'=>1} + delete('abracadabra', 'bra') Would return: 'acada' - EOS + EOS ) do |arguments| if (arguments.size != 2) then raise(Puppet::ParseError, "delete(): Wrong number of arguments "+ - "given #{arguments.size} for 2.") + "given #{arguments.size} for 2.") end collection = arguments[0].dup - item = arguments[1] - - case collection - when Array, Hash - collection.delete item - when String - collection.gsub! item, '' - else - raise(TypeError, "delete(): First argument must be an Array, " + - "String, or Hash. Given an argument of class #{collection.class}.") + Array(arguments[1]).each do |item| + case collection + when Array, Hash + collection.delete item + when String + collection.gsub! item, '' + else + raise(TypeError, "delete(): First argument must be an Array, " + + "String, or Hash. Given an argument of class #{collection.class}.") + end end collection end diff --git a/lib/puppet/parser/functions/dirname.rb b/lib/puppet/parser/functions/dirname.rb index ea8cc1e..40b300d 100644 --- a/lib/puppet/parser/functions/dirname.rb +++ b/lib/puppet/parser/functions/dirname.rb @@ -4,11 +4,17 @@ module Puppet::Parser::Functions EOS ) do |arguments| - raise(Puppet::ParseError, "dirname(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 + if arguments.size < 1 then + raise(Puppet::ParseError, "dirname(): No arguments given") + end + if arguments.size > 1 then + raise(Puppet::ParseError, "dirname(): Too many arguments given (#{arguments.size})") + end + unless arguments[0].is_a?(String) + raise(Puppet::ParseError, 'dirname(): Requires string as argument') + end - path = arguments[0] - return File.dirname(path) + return File.dirname(arguments[0]) end end diff --git a/lib/puppet/parser/functions/downcase.rb b/lib/puppet/parser/functions/downcase.rb index 4066d21..040b84f 100644 --- a/lib/puppet/parser/functions/downcase.rb +++ b/lib/puppet/parser/functions/downcase.rb @@ -12,9 +12,8 @@ Converts the case of a string or all strings in an array to lower case. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'downcase(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/empty.rb b/lib/puppet/parser/functions/empty.rb index 80ebb86..cca620f 100644 --- a/lib/puppet/parser/functions/empty.rb +++ b/lib/puppet/parser/functions/empty.rb @@ -12,9 +12,8 @@ Returns true if the variable is empty. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, Hash, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(Hash) || value.is_a?(String) raise(Puppet::ParseError, 'empty(): Requires either ' + 'array, hash or string to work with') end diff --git a/lib/puppet/parser/functions/ensure_resource.rb b/lib/puppet/parser/functions/ensure_resource.rb index 05e5593..1ba6a44 100644 --- a/lib/puppet/parser/functions/ensure_resource.rb +++ b/lib/puppet/parser/functions/ensure_resource.rb @@ -36,8 +36,9 @@ ENDOFDOC items.each do |item| Puppet::Parser::Functions.function(:defined_with_params) if function_defined_with_params(["#{type}[#{item}]", params]) - Puppet.debug("Resource #{type}[#{item}] not created because it already exists") + Puppet.debug("Resource #{type}[#{item}] with params #{params} not created because it already exists") else + Puppet.debug("Create new resource #{type}[#{item}] with params #{params}") Puppet::Parser::Functions.function(:create_resources) function_create_resources([type.capitalize, { item => params }]) end diff --git a/lib/puppet/parser/functions/fqdn_rand_string.rb b/lib/puppet/parser/functions/fqdn_rand_string.rb new file mode 100644 index 0000000..785c9fd --- /dev/null +++ b/lib/puppet/parser/functions/fqdn_rand_string.rb @@ -0,0 +1,34 @@ +Puppet::Parser::Functions::newfunction( + :fqdn_rand_string, + :arity => -2, + :type => :rvalue, + :doc => "Usage: `fqdn_rand_string(LENGTH, [CHARSET], [SEED])`. LENGTH is + required and must be a positive integer. CHARSET is optional and may be + `undef` or a string. SEED is optional and may be any number or string. + + Generates a random string LENGTH characters long using the character set + provided by CHARSET, combining the `$fqdn` fact and the value of SEED for + repeatable randomness. (That is, each node will get a different random + string from this function, but a given node's result will be the same every + time unless its hostname changes.) Adding a SEED can be useful if you need + more than one unrelated string. CHARSET will default to alphanumeric if + `undef` or an empty string.") do |args| + raise(ArgumentError, "fqdn_rand_string(): wrong number of arguments (0 for 1)") if args.size == 0 + Puppet::Parser::Functions.function('is_integer') + raise(ArgumentError, "fqdn_rand_base64(): first argument must be a positive integer") unless function_is_integer([args[0]]) and args[0].to_i > 0 + raise(ArgumentError, "fqdn_rand_base64(): second argument must be undef or a string") unless args[1].nil? or args[1].is_a? String + + Puppet::Parser::Functions.function('fqdn_rand') + + length = args.shift.to_i + charset = args.shift.to_s.chars.to_a + + charset = (0..9).map { |i| i.to_s } + ('A'..'Z').to_a + ('a'..'z').to_a if charset.empty? + + rand_string = '' + for current in 1..length + rand_string << charset[function_fqdn_rand([charset.size, (args + [current.to_s]).join(':')]).to_i] + end + + rand_string +end diff --git a/lib/puppet/parser/functions/fqdn_rotate.rb b/lib/puppet/parser/functions/fqdn_rotate.rb index 6558206..cf22d36 100644 --- a/lib/puppet/parser/functions/fqdn_rotate.rb +++ b/lib/puppet/parser/functions/fqdn_rotate.rb @@ -12,10 +12,9 @@ Rotates an array a random number of times based on a nodes fqdn. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class require 'digest/md5' - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'fqdn_rotate(): Requires either ' + 'array or string to work with') end @@ -32,8 +31,20 @@ Rotates an array a random number of times based on a nodes fqdn. elements = result.size - srand(Digest::MD5.hexdigest([lookupvar('::fqdn'),arguments].join(':')).hex) - rand(elements).times { + seed = Digest::MD5.hexdigest([lookupvar('::fqdn'),arguments].join(':')).hex + # deterministic_rand() was added in Puppet 3.2.0; reimplement if necessary + if Puppet::Util.respond_to?(:deterministic_rand) + offset = Puppet::Util.deterministic_rand(seed, elements).to_i + else + if defined?(Random) == 'constant' && Random.class == Class + offset = Random.new(seed).rand(elements) + else + srand(seed) + offset = rand(elements) + srand() + end + end + offset.times { result.push result.shift } diff --git a/lib/puppet/parser/functions/getvar.rb b/lib/puppet/parser/functions/getvar.rb index 1621149..fb336b6 100644 --- a/lib/puppet/parser/functions/getvar.rb +++ b/lib/puppet/parser/functions/getvar.rb @@ -19,7 +19,10 @@ module Puppet::Parser::Functions raise Puppet::ParseError, ("getvar(): wrong number of arguments (#{args.length}; must be 1)") end - self.lookupvar("#{args[0]}") + begin + self.lookupvar("#{args[0]}") + rescue Puppet::ParseError # Eat the exception if strict_variables = true is set + end end diff --git a/lib/puppet/parser/functions/has_interface_with.rb b/lib/puppet/parser/functions/has_interface_with.rb index 927b0df..3691524 100644 --- a/lib/puppet/parser/functions/has_interface_with.rb +++ b/lib/puppet/parser/functions/has_interface_with.rb @@ -35,13 +35,29 @@ has_interface_with("lo") => true kind, value = args - if lookupvar(kind) == value + # Bug with 3.7.1 - 3.7.3 when using future parser throws :undefined_variable + # https://tickets.puppetlabs.com/browse/PUP-3597 + factval = nil + catch :undefined_variable do + factval = lookupvar(kind) + end + if factval == value return true end result = false interfaces.each do |iface| - if value == lookupvar("#{kind}_#{iface}") + iface.downcase! + factval = nil + begin + # Bug with 3.7.1 - 3.7.3 when using future parser throws :undefined_variable + # https://tickets.puppetlabs.com/browse/PUP-3597 + catch :undefined_variable do + factval = lookupvar("#{kind}_#{iface}") + end + rescue Puppet::ParseError # Eat the exception if strict_variables = true is set + end + if value == factval result = true break end diff --git a/lib/puppet/parser/functions/is_domain_name.rb b/lib/puppet/parser/functions/is_domain_name.rb index b3fee96..90ede32 100644 --- a/lib/puppet/parser/functions/is_domain_name.rb +++ b/lib/puppet/parser/functions/is_domain_name.rb @@ -13,16 +13,16 @@ Returns true if the string passed to this function is a syntactically correct do "given #{arguments.size} for 1") end - domain = arguments[0] + # Only allow string types + return false unless arguments[0].is_a?(String) + + domain = arguments[0].dup # Limits (rfc1035, 3.1) domain_max_length=255 label_min_length=1 label_max_length=63 - # Only allow string types - return false unless domain.is_a?(String) - # Allow ".", it is the top level domain return true if domain == '.' @@ -33,6 +33,10 @@ Returns true if the string passed to this function is a syntactically correct do return false if domain.empty? return false if domain.length > domain_max_length + # The top level domain must be alphabetic if there are multiple labels. + # See rfc1123, 2.1 + return false if domain.include? '.' and not /\.[A-Za-z]+$/.match(domain) + # Check each label in the domain labels = domain.split('.') vlabels = labels.each do |label| diff --git a/lib/puppet/parser/functions/lstrip.rb b/lib/puppet/parser/functions/lstrip.rb index 3a64de3..624e4c8 100644 --- a/lib/puppet/parser/functions/lstrip.rb +++ b/lib/puppet/parser/functions/lstrip.rb @@ -12,9 +12,8 @@ Strips leading spaces to the left of a string. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'lstrip(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/member.rb b/lib/puppet/parser/functions/member.rb index 43d76af..88609ce 100644 --- a/lib/puppet/parser/functions/member.rb +++ b/lib/puppet/parser/functions/member.rb @@ -8,6 +8,7 @@ module Puppet::Parser::Functions newfunction(:member, :type => :rvalue, :doc => <<-EOS This function determines if a variable is a member of an array. +The variable can be a string, fixnum, or array. *Examples:* @@ -15,9 +16,17 @@ This function determines if a variable is a member of an array. Would return: true + member(['a', 'b', 'c'], ['a', 'b']) + +would return: true + member(['a','b'], 'c') Would return: false + + member(['a', 'b', 'c'], ['d', 'b']) + +would return: false EOS ) do |arguments| @@ -30,12 +39,21 @@ Would return: false raise(Puppet::ParseError, 'member(): Requires array to work with') end - item = arguments[1] + unless arguments[1].is_a? String or arguments[1].is_a? Fixnum or arguments[1].is_a? Array + raise(Puppet::ParseError, 'member(): Item to search for must be a string, fixnum, or array') + end + + if arguments[1].is_a? String or arguments[1].is_a? Fixnum + item = Array(arguments[1]) + else + item = arguments[1] + end + raise(Puppet::ParseError, 'member(): You must provide item ' + - 'to search for within array given') if item.empty? + 'to search for within array given') if item.respond_to?('empty?') && item.empty? - result = array.include?(item) + result = (item - array).empty? return result end diff --git a/lib/puppet/parser/functions/prefix.rb b/lib/puppet/parser/functions/prefix.rb index d02286a..ac1c58a 100644 --- a/lib/puppet/parser/functions/prefix.rb +++ b/lib/puppet/parser/functions/prefix.rb @@ -4,7 +4,7 @@ module Puppet::Parser::Functions newfunction(:prefix, :type => :rvalue, :doc => <<-EOS -This function applies a prefix to all elements in an array. +This function applies a prefix to all elements in an array or a hash. *Examples:* @@ -18,10 +18,10 @@ Will return: ['pa','pb','pc'] raise(Puppet::ParseError, "prefix(): Wrong number of arguments " + "given (#{arguments.size} for 1)") if arguments.size < 1 - array = arguments[0] + enumerable = arguments[0] - unless array.is_a?(Array) - raise Puppet::ParseError, "prefix(): expected first argument to be an Array, got #{array.inspect}" + unless enumerable.is_a?(Array) or enumerable.is_a?(Hash) + raise Puppet::ParseError, "prefix(): expected first argument to be an Array or a Hash, got #{enumerable.inspect}" end prefix = arguments[1] if arguments[1] @@ -32,10 +32,17 @@ Will return: ['pa','pb','pc'] end end - # Turn everything into string same as join would do ... - result = array.collect do |i| - i = i.to_s - prefix ? prefix + i : i + if enumerable.is_a?(Array) + # Turn everything into string same as join would do ... + result = enumerable.collect do |i| + i = i.to_s + prefix ? prefix + i : i + end + else + result = Hash[enumerable.map do |k,v| + k = k.to_s + [ prefix ? prefix + k : k, v ] + end] end return result diff --git a/lib/puppet/parser/functions/private.rb b/lib/puppet/parser/functions/private.rb index 60210d3..3b00ba1 100644 --- a/lib/puppet/parser/functions/private.rb +++ b/lib/puppet/parser/functions/private.rb @@ -4,26 +4,14 @@ module Puppet::Parser::Functions newfunction(:private, :doc => <<-'EOS' - Sets the current class or definition as private. + DEPRECATED: Sets the current class or definition as private. Calling the class or definition from outside the current module will fail. - EOS + EOS ) do |args| - - raise(Puppet::ParseError, "private(): Wrong number of arguments "+ - "given (#{args.size}}) for 0 or 1)") if args.size > 1 - - scope = self - if scope.lookupvar('module_name') != scope.lookupvar('caller_module_name') - message = nil - if args[0] and args[0].is_a? String - message = args[0] - else - manifest_name = scope.source.name - manifest_type = scope.source.type - message = (manifest_type.to_s == 'hostclass') ? 'Class' : 'Definition' - message += " #{manifest_name} is private" - end - raise(Puppet::ParseError, message) + warning("private() DEPRECATED: This function will cease to function on Puppet 4; please use assert_private() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.") + if !Puppet::Parser::Functions.autoloader.loaded?(:assert_private) + Puppet::Parser::Functions.autoloader.load(:assert_private) end + function_assert_private([(args[0] unless args.size < 1)]) end end diff --git a/lib/puppet/parser/functions/pw_hash.rb b/lib/puppet/parser/functions/pw_hash.rb new file mode 100644 index 0000000..ad3e393 --- /dev/null +++ b/lib/puppet/parser/functions/pw_hash.rb @@ -0,0 +1,56 @@ +Puppet::Parser::Functions::newfunction( + :pw_hash, + :type => :rvalue, + :arity => 3, + :doc => "Hashes a password using the crypt function. Provides a hash + usable on most POSIX systems. + + The first argument to this function is the password to hash. If it is + undef or an empty string, this function returns undef. + + The second argument to this function is which type of hash to use. It + will be converted into the appropriate crypt(3) hash specifier. Valid + hash types are: + + |Hash type |Specifier| + |---------------------|---------| + |MD5 |1 | + |SHA-256 |5 | + |SHA-512 (recommended)|6 | + + The third argument to this function is the salt to use. + + Note: this uses the Puppet Master's implementation of crypt(3). If your + environment contains several different operating systems, ensure that they + are compatible before using this function.") do |args| + raise ArgumentError, "pw_hash(): wrong number of arguments (#{args.size} for 3)" if args.size != 3 + raise ArgumentError, "pw_hash(): first argument must be a string" unless args[0].is_a? String or args[0].nil? + raise ArgumentError, "pw_hash(): second argument must be a string" unless args[1].is_a? String + hashes = { 'md5' => '1', + 'sha-256' => '5', + 'sha-512' => '6' } + hash_type = hashes[args[1].downcase] + raise ArgumentError, "pw_hash(): #{args[1]} is not a valid hash type" if hash_type.nil? + raise ArgumentError, "pw_hash(): third argument must be a string" unless args[2].is_a? String + raise ArgumentError, "pw_hash(): third argument must not be empty" if args[2].empty? + raise ArgumentError, "pw_hash(): characters in salt must be in the set [a-zA-Z0-9./]" unless args[2].match(/\A[a-zA-Z0-9.\/]+\z/) + + password = args[0] + return nil if password.nil? or password.empty? + + # handle weak implementations of String#crypt + if 'test'.crypt('$1$1') != '$1$1$Bp8CU9Oujr9SSEw53WV6G.' + # JRuby < 1.7.17 + if RUBY_PLATFORM == 'java' + # override String#crypt for password variable + def password.crypt(salt) + # puppetserver bundles Apache Commons Codec + org.apache.commons.codec.digest.Crypt.crypt(self.to_java_bytes, salt) + end + else + # MS Windows and other systems that don't support enhanced salts + raise Puppet::ParseError, 'system does not support enhanced salts' + end + end + password.crypt("$#{hash_type}$#{args[2]}") +end diff --git a/lib/puppet/parser/functions/range.rb b/lib/puppet/parser/functions/range.rb index ffbdf84..49fba21 100644 --- a/lib/puppet/parser/functions/range.rb +++ b/lib/puppet/parser/functions/range.rb @@ -65,21 +65,21 @@ Will return: [0,2,4,6,8] end end - # Check whether we have integer value if so then make it so ... - if start.match(/^\d+$/) - start = start.to_i - stop = stop.to_i - else - start = start.to_s - stop = stop.to_s - end + # Check whether we have integer value if so then make it so ... + if start.to_s.match(/^\d+$/) + start = start.to_i + stop = stop.to_i + else + start = start.to_s + stop = stop.to_s + end - range = case type - when /^(\.\.|\-)$/ then (start .. stop) - when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ... - end + range = case type + when /^(\.\.|\-)$/ then (start .. stop) + when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ... + end - result = range.step(step).collect { |i| i } # Get them all ... Pokemon ... + result = range.step(step).collect { |i| i } # Get them all ... Pokemon ... return result end diff --git a/lib/puppet/parser/functions/reverse.rb b/lib/puppet/parser/functions/reverse.rb index fe04869..7f1018f 100644 --- a/lib/puppet/parser/functions/reverse.rb +++ b/lib/puppet/parser/functions/reverse.rb @@ -12,9 +12,8 @@ Reverses the order of a string or array. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'reverse(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/rstrip.rb b/lib/puppet/parser/functions/rstrip.rb index 29b0998..0cf8d22 100644 --- a/lib/puppet/parser/functions/rstrip.rb +++ b/lib/puppet/parser/functions/rstrip.rb @@ -12,9 +12,8 @@ Strips leading spaces to the right of the string. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'rstrip(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/shuffle.rb b/lib/puppet/parser/functions/shuffle.rb index 18134ab..30c663d 100644 --- a/lib/puppet/parser/functions/shuffle.rb +++ b/lib/puppet/parser/functions/shuffle.rb @@ -12,9 +12,8 @@ Randomizes the order of a string or array elements. "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'shuffle(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/strip.rb b/lib/puppet/parser/functions/strip.rb index 5f4630d..3fac47d 100644 --- a/lib/puppet/parser/functions/strip.rb +++ b/lib/puppet/parser/functions/strip.rb @@ -19,9 +19,8 @@ Would result in: "aaa" "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'strip(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/swapcase.rb b/lib/puppet/parser/functions/swapcase.rb index b9e6632..eb7fe13 100644 --- a/lib/puppet/parser/functions/swapcase.rb +++ b/lib/puppet/parser/functions/swapcase.rb @@ -18,9 +18,8 @@ Would result in: "AbCd" "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'swapcase(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/to_bytes.rb b/lib/puppet/parser/functions/to_bytes.rb index 8ff73d1..df490ea 100644 --- a/lib/puppet/parser/functions/to_bytes.rb +++ b/lib/puppet/parser/functions/to_bytes.rb @@ -2,6 +2,8 @@ module Puppet::Parser::Functions newfunction(:to_bytes, :type => :rvalue, :doc => <<-EOS Converts the argument into bytes, for example 4 kB becomes 4096. Takes a single string value as an argument. + These conversions reflect a layperson's understanding of + 1 MB = 1024 KB, when in fact 1 MB = 1000 KB, and 1 MiB = 1024 KiB. EOS ) do |arguments| @@ -21,7 +23,8 @@ module Puppet::Parser::Functions when 'M' then return (value*(1<<20)).to_i when 'G' then return (value*(1<<30)).to_i when 'T' then return (value*(1<<40)).to_i - when 'E' then return (value*(1<<50)).to_i + when 'P' then return (value*(1<<50)).to_i + when 'E' then return (value*(1<<60)).to_i else raise Puppet::ParseError, "to_bytes(): Unknown prefix #{prefix}" end end diff --git a/lib/puppet/parser/functions/type.rb b/lib/puppet/parser/functions/type.rb index 8d85f11..016529b 100644 --- a/lib/puppet/parser/functions/type.rb +++ b/lib/puppet/parser/functions/type.rb @@ -4,46 +4,15 @@ module Puppet::Parser::Functions newfunction(:type, :type => :rvalue, :doc => <<-EOS -Returns the type when passed a variable. Type can be one of: - -* string -* array -* hash -* float -* integer -* boolean + DEPRECATED: This function will cease to function on Puppet 4; please use type3x() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system. EOS - ) do |arguments| - - raise(Puppet::ParseError, "type(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 - - value = arguments[0] - - klass = value.class - - if not [TrueClass, FalseClass, Array, Bignum, Fixnum, Float, Hash, String].include?(klass) - raise(Puppet::ParseError, 'type(): Unknown type') - end - - klass = klass.to_s # Ugly ... + ) do |args| - # We note that Integer is the parent to Bignum and Fixnum ... - result = case klass - when /^(?:Big|Fix)num$/ then 'integer' - when /^(?:True|False)Class$/ then 'boolean' - else klass + warning("type() DEPRECATED: This function will cease to function on Puppet 4; please use type3x() before upgrading to puppet 4 for backwards-compatibility, or migrate to the new parser's typing system.") + if ! Puppet::Parser::Functions.autoloader.loaded?(:type3x) + Puppet::Parser::Functions.autoloader.load(:type3x) end - - if result == "String" then - if value == value.to_i.to_s then - result = "Integer" - elsif value == value.to_f.to_s then - result = "Float" - end - end - - return result.downcase + function_type3x(args + [false]) end end diff --git a/lib/puppet/parser/functions/type3x.rb b/lib/puppet/parser/functions/type3x.rb new file mode 100644 index 0000000..0800b4a --- /dev/null +++ b/lib/puppet/parser/functions/type3x.rb @@ -0,0 +1,51 @@ +# +# type3x.rb +# + +module Puppet::Parser::Functions + newfunction(:type3x, :type => :rvalue, :doc => <<-EOS +DEPRECATED: This function will be removed when puppet 3 support is dropped; please migrate to the new parser's typing system. + +Returns the type when passed a value. Type can be one of: + +* string +* array +* hash +* float +* integer +* boolean + EOS + ) do |args| + raise(Puppet::ParseError, "type3x(): Wrong number of arguments " + + "given (#{args.size} for 1)") if args.size < 1 + + value = args[0] + + klass = value.class + + if not [TrueClass, FalseClass, Array, Bignum, Fixnum, Float, Hash, String].include?(klass) + raise(Puppet::ParseError, 'type3x(): Unknown type') + end + + klass = klass.to_s # Ugly ... + + # We note that Integer is the parent to Bignum and Fixnum ... + result = case klass + when /^(?:Big|Fix)num$/ then 'integer' + when /^(?:True|False)Class$/ then 'boolean' + else klass + end + + if result == "String" then + if value == value.to_i.to_s then + result = "Integer" + elsif value == value.to_f.to_s then + result = "Float" + end + end + + return result.downcase + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/unique.rb b/lib/puppet/parser/functions/unique.rb index 8844a74..cf770f3 100644 --- a/lib/puppet/parser/functions/unique.rb +++ b/lib/puppet/parser/functions/unique.rb @@ -28,9 +28,8 @@ This returns: "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'unique(): Requires either ' + 'array or string to work with') end diff --git a/lib/puppet/parser/functions/upcase.rb b/lib/puppet/parser/functions/upcase.rb index fe6cadc..0226a88 100644 --- a/lib/puppet/parser/functions/upcase.rb +++ b/lib/puppet/parser/functions/upcase.rb @@ -13,23 +13,27 @@ Converts a string or an array of strings to uppercase. Will return: ASDF - EOS + EOS ) do |arguments| raise(Puppet::ParseError, "upcase(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 + "given (#{arguments.size} for 1)") if arguments.size != 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) - raise(Puppet::ParseError, 'upcase(): Requires either ' + - 'array or string to work with') + unless value.is_a?(Array) || value.is_a?(Hash) || value.respond_to?(:upcase) + raise(Puppet::ParseError, 'upcase(): Requires an ' + + 'array, hash or object that responds to upcase in order to work') end if value.is_a?(Array) # Numbers in Puppet are often string-encoded which is troublesome ... - result = value.collect { |i| i.is_a?(String) ? i.upcase : i } + result = value.collect { |i| function_upcase([i]) } + elsif value.is_a?(Hash) + result = {} + value.each_pair do |k, v| + result[function_upcase([k])] = function_upcase([v]) + end else result = value.upcase end diff --git a/lib/puppet/parser/functions/uriescape.rb b/lib/puppet/parser/functions/uriescape.rb index 0d81de5..45bbed2 100644 --- a/lib/puppet/parser/functions/uriescape.rb +++ b/lib/puppet/parser/functions/uriescape.rb @@ -14,16 +14,15 @@ module Puppet::Parser::Functions "given (#{arguments.size} for 1)") if arguments.size < 1 value = arguments[0] - klass = value.class - unless [Array, String].include?(klass) + unless value.is_a?(Array) || value.is_a?(String) raise(Puppet::ParseError, 'uriescape(): Requires either ' + 'array or string to work with') end if value.is_a?(Array) # Numbers in Puppet are often string-encoded which is troublesome ... - result = value.collect { |i| i.is_a?(String) ? URI.escape(i,unsafe) : i } + result = value.collect { |i| i.is_a?(String) ? URI.escape(i) : i } else result = URI.escape(value) 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_augeas.rb b/lib/puppet/parser/functions/validate_augeas.rb index 154d660..4ea4fe0 100644 --- a/lib/puppet/parser/functions/validate_augeas.rb +++ b/lib/puppet/parser/functions/validate_augeas.rb @@ -1,3 +1,5 @@ +require 'tempfile' + module Puppet::Parser::Functions newfunction(:validate_augeas, :doc => <<-'ENDHEREDOC') do |args| Perform validation of a string using an Augeas lens diff --git a/lib/puppet/parser/functions/validate_cmd.rb b/lib/puppet/parser/functions/validate_cmd.rb index 2ebe91c..5df3c60 100644 --- a/lib/puppet/parser/functions/validate_cmd.rb +++ b/lib/puppet/parser/functions/validate_cmd.rb @@ -1,13 +1,14 @@ require 'puppet/util/execution' +require 'tempfile' module Puppet::Parser::Functions newfunction(:validate_cmd, :doc => <<-'ENDHEREDOC') do |args| 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. @@ -16,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)") @@ -33,14 +38,24 @@ 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}" raise Puppet::ParseError, msg + rescue Exception => detail + msg += "\n#{detail.class.name} #{detail}" + raise Puppet::ParseError, msg ensure tmpfile.unlink end diff --git a/lib/puppet/parser/functions/validate_integer.rb b/lib/puppet/parser/functions/validate_integer.rb new file mode 100644 index 0000000..995f8db --- /dev/null +++ b/lib/puppet/parser/functions/validate_integer.rb @@ -0,0 +1,131 @@ +module Puppet::Parser::Functions + + newfunction(:validate_integer, :doc => <<-'ENDHEREDOC') do |args| + Validate that the first argument is an integer (or an array of integers). Abort catalog compilation if any of the checks fail. + + The second argument is optional and passes a maximum. (All elements of) the first argument has to be less or equal to this max. + + The third argument is optional and passes a minimum. (All elements of) the first argument has to be greater or equal to this min. + If, and only if, a minimum is given, the second argument may be an empty string or undef, which will be handled to just check + if (all elements of) the first argument are greater or equal to the given minimum. + + It will fail if the first argument is not an integer or array of integers, and if arg 2 and arg 3 are not convertable to an integer. + + The following values will pass: + + validate_integer(1) + validate_integer(1, 2) + validate_integer(1, 1) + validate_integer(1, 2, 0) + validate_integer(2, 2, 2) + validate_integer(2, '', 0) + validate_integer(2, undef, 0) + $foo = undef + validate_integer(2, $foo, 0) + validate_integer([1,2,3,4,5], 6) + validate_integer([1,2,3,4,5], 6, 0) + + Plus all of the above, but any combination of values passed as strings ('1' or "1"). + Plus all of the above, but with (correct) combinations of negative integer values. + + The following values will not: + + validate_integer(true) + validate_integer(false) + validate_integer(7.0) + validate_integer({ 1 => 2 }) + $foo = undef + validate_integer($foo) + validate_integer($foobaridontexist) + + validate_integer(1, 0) + validate_integer(1, true) + validate_integer(1, '') + validate_integer(1, undef) + validate_integer(1, , 0) + validate_integer(1, 2, 3) + validate_integer(1, 3, 2) + validate_integer(1, 3, true) + + Plus all of the above, but any combination of values passed as strings ('false' or "false"). + Plus all of the above, but with incorrect combinations of negative integer values. + Plus all of the above, but with non-integer crap in arrays or maximum / minimum argument. + + ENDHEREDOC + + # tell the user we need at least one, and optionally up to two other parameters + raise Puppet::ParseError, "validate_integer(): Wrong number of arguments; must be 1, 2 or 3, got #{args.length}" unless args.length > 0 and args.length < 4 + + input, max, min = *args + + # check maximum parameter + if args.length > 1 + max = max.to_s + # allow max to be empty (or undefined) if we have a minimum set + if args.length > 2 and max == '' + max = nil + else + begin + max = Integer(max) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_integer(): Expected second argument to be unset or an Integer, got #{max}:#{max.class}" + end + end + else + max = nil + end + + # check minimum parameter + if args.length > 2 + begin + min = Integer(min.to_s) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_integer(): Expected third argument to be unset or an Integer, got #{min}:#{min.class}" + end + else + min = nil + end + + # ensure that min < max + if min and max and min > max + raise Puppet::ParseError, "validate_integer(): Expected second argument to be larger than third argument, got #{max} < #{min}" + end + + # create lamba validator function + validator = lambda do |num| + # check input < max + if max and num > max + raise Puppet::ParseError, "validate_integer(): Expected #{input.inspect} to be smaller or equal to #{max}, got #{input.inspect}." + end + # check input > min (this will only be checked if no exception has been raised before) + if min and num < min + raise Puppet::ParseError, "validate_integer(): Expected #{input.inspect} to be greater or equal to #{min}, got #{input.inspect}." + end + end + + # if this is an array, handle it. + case input + when Array + # check every element of the array + input.each_with_index do |arg, pos| + begin + arg = Integer(arg.to_s) + validator.call(arg) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_integer(): Expected element at array position #{pos} to be an Integer, got #{arg.class}" + end + end + # for the sake of compatibility with ruby 1.8, we need extra handling of hashes + when Hash + raise Puppet::ParseError, "validate_integer(): Expected first argument to be an Integer or Array, got #{input.class}" + # check the input. this will also fail any stuff other than pure, shiny integers + else + begin + input = Integer(input.to_s) + validator.call(input) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_integer(): Expected first argument to be an Integer or Array, got #{input.class}" + end + end + end +end diff --git a/lib/puppet/parser/functions/validate_numeric.rb b/lib/puppet/parser/functions/validate_numeric.rb new file mode 100644 index 0000000..d2e4d16 --- /dev/null +++ b/lib/puppet/parser/functions/validate_numeric.rb @@ -0,0 +1,93 @@ +module Puppet::Parser::Functions + + newfunction(:validate_numeric, :doc => <<-'ENDHEREDOC') do |args| + Validate that the first argument is a numeric value (or an array of numeric values). Abort catalog compilation if any of the checks fail. + + The second argument is optional and passes a maximum. (All elements of) the first argument has to be less or equal to this max. + + The third argument is optional and passes a minimum. (All elements of) the first argument has to be greater or equal to this min. + If, and only if, a minimum is given, the second argument may be an empty string or undef, which will be handled to just check + if (all elements of) the first argument are greater or equal to the given minimum. + + It will fail if the first argument is not a numeric (Integer or Float) or array of numerics, and if arg 2 and arg 3 are not convertable to a numeric. + + For passing and failing usage, see `validate_integer()`. It is all the same for validate_numeric, yet now floating point values are allowed, too. + + ENDHEREDOC + + # tell the user we need at least one, and optionally up to two other parameters + raise Puppet::ParseError, "validate_numeric(): Wrong number of arguments; must be 1, 2 or 3, got #{args.length}" unless args.length > 0 and args.length < 4 + + input, max, min = *args + + # check maximum parameter + if args.length > 1 + max = max.to_s + # allow max to be empty (or undefined) if we have a minimum set + if args.length > 2 and max == '' + max = nil + else + begin + max = Float(max) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_numeric(): Expected second argument to be unset or a Numeric, got #{max}:#{max.class}" + end + end + else + max = nil + end + + # check minimum parameter + if args.length > 2 + begin + min = Float(min.to_s) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_numeric(): Expected third argument to be unset or a Numeric, got #{min}:#{min.class}" + end + else + min = nil + end + + # ensure that min < max + if min and max and min > max + raise Puppet::ParseError, "validate_numeric(): Expected second argument to be larger than third argument, got #{max} < #{min}" + end + + # create lamba validator function + validator = lambda do |num| + # check input < max + if max and num > max + raise Puppet::ParseError, "validate_numeric(): Expected #{input.inspect} to be smaller or equal to #{max}, got #{input.inspect}." + end + # check input > min (this will only be checked if no exception has been raised before) + if min and num < min + raise Puppet::ParseError, "validate_numeric(): Expected #{input.inspect} to be greater or equal to #{min}, got #{input.inspect}." + end + end + + # if this is an array, handle it. + case input + when Array + # check every element of the array + input.each_with_index do |arg, pos| + begin + arg = Float(arg.to_s) + validator.call(arg) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_numeric(): Expected element at array position #{pos} to be a Numeric, got #{arg.class}" + end + end + # for the sake of compatibility with ruby 1.8, we need extra handling of hashes + when Hash + raise Puppet::ParseError, "validate_integer(): Expected first argument to be a Numeric or Array, got #{input.class}" + # check the input. this will also fail any stuff other than pure, shiny integers + else + begin + input = Float(input.to_s) + validator.call(input) + rescue TypeError, ArgumentError + raise Puppet::ParseError, "validate_numeric(): Expected first argument to be a Numeric or Array, got #{input.class}" + end + end + end +end diff --git a/lib/puppet/parser/functions/validate_string.rb b/lib/puppet/parser/functions/validate_string.rb index e667794..c841f6a 100644 --- a/lib/puppet/parser/functions/validate_string.rb +++ b/lib/puppet/parser/functions/validate_string.rb @@ -13,9 +13,14 @@ module Puppet::Parser::Functions validate_string(true) validate_string([ 'some', 'array' ]) - $undefined = undef - validate_string($undefined) - + + Note: validate_string(undef) will not fail in this version of the + functions API (incl. current and future parser). Instead, use: + + if $var == undef { + fail('...') + } + ENDHEREDOC unless args.length > 0 then diff --git a/lib/puppet/parser/functions/values_at.rb b/lib/puppet/parser/functions/values_at.rb index d3e69d9..f350f53 100644 --- a/lib/puppet/parser/functions/values_at.rb +++ b/lib/puppet/parser/functions/values_at.rb @@ -49,6 +49,7 @@ Would return ['a','c','d']. indices_list = [] indices.each do |i| + i = i.to_s if m = i.match(/^(\d+)(\.\.\.?|\-)(\d+)$/) start = m[1].to_i stop = m[3].to_i diff --git a/lib/puppet/parser/functions/zip.rb b/lib/puppet/parser/functions/zip.rb index 2b56e9c..3074f28 100644 --- a/lib/puppet/parser/functions/zip.rb +++ b/lib/puppet/parser/functions/zip.rb @@ -27,33 +27,7 @@ Would result in: raise(Puppet::ParseError, 'zip(): Requires array to work with') end - flatten = arguments[2] if arguments[2] - - if flatten - klass = flatten.class - - # We can have either true or false, or string which resembles boolean ... - unless [FalseClass, TrueClass, String].include?(klass) - raise(Puppet::ParseError, 'zip(): Requires either ' + - 'boolean or string to work with') - end - - if flatten.is_a?(String) - # We consider all the yes, no, y, n and so on too ... - flatten = case flatten - # - # This is how undef looks like in Puppet ... - # We yield false in this case. - # - when /^$/, '' then false # Empty string will be false ... - when /^(1|t|y|true|yes)$/ then true - when /^(0|f|n|false|no)$/ then false - when /^(undef|undefined)$/ then false # This is not likely to happen ... - else - raise(Puppet::ParseError, 'zip(): Unknown type of boolean given') - end - end - end + flatten = function_str2bool([arguments[2]]) if arguments[2] result = a.zip(b) result = flatten ? result.flatten : result |