diff options
author | varac <varacanero@zeromail.org> | 2017-01-13 12:41:58 +0100 |
---|---|---|
committer | varac <varacanero@zeromail.org> | 2017-01-13 12:41:58 +0100 |
commit | 066c08f8362d53f0f30897cb8710d11260c726ea (patch) | |
tree | a6369eecd88bb731fe413d0bbc8af73d74d1f447 /lib/puppet | |
parent | 71123634744b9fe2ec7d6a3e38e9789fd84801e3 (diff) | |
parent | b65dd1f45d10e10e45455358aeabb29167990e2c (diff) |
Merge remote-tracking branch 'origin/master' into leap_master
Diffstat (limited to 'lib/puppet')
110 files changed, 2347 insertions, 246 deletions
diff --git a/lib/puppet/functions/deprecation.rb b/lib/puppet/functions/deprecation.rb new file mode 100644 index 0000000..39d9bc7 --- /dev/null +++ b/lib/puppet/functions/deprecation.rb @@ -0,0 +1,29 @@ +# Function to print deprecation warnings, Logs a warning once for a given key. The uniqueness key - can appear once. The msg is the message text including any positional information that is formatted by the user/caller of the method It is affected by the puppet setting 'strict', which can be set to :error (outputs as an error message), :off (no message / error is displayed) and :warning (default, outputs a warning) *Type*: String, String. +# + +Puppet::Functions.create_function(:deprecation) do + dispatch :deprecation do + param 'String', :key + param 'String', :message + end + + def deprecation(key, message) + if defined? Puppet::Pops::PuppetStack.stacktrace() + stacktrace = Puppet::Pops::PuppetStack.stacktrace() + file = stacktrace[0] + line = stacktrace[1] + message = "#{message} at #{file}:#{line}" + end + # depending on configuration setting of strict + case Puppet.settings[:strict] + when :off + # do nothing + when :error + fail("deprecation. #{key}. #{message}") + else + unless ENV['STDLIB_LOG_DEPRECATIONS'] == 'false' + Puppet.deprecation_warning(message, key) + end + end + end +end diff --git a/lib/puppet/functions/is_a.rb b/lib/puppet/functions/is_a.rb new file mode 100644 index 0000000..da98b03 --- /dev/null +++ b/lib/puppet/functions/is_a.rb @@ -0,0 +1,32 @@ +# Boolean check to determine whether a variable is of a given data type. This is equivalent to the `=~` type checks. +# +# @example how to check a data type +# # check a data type +# foo = 3 +# $bar = [1,2,3] +# $baz = 'A string!' +# +# if $foo.is_a(Integer) { +# notify { 'foo!': } +# } +# if $bar.is_a(Array) { +# notify { 'bar!': } +# } +# if $baz.is_a(String) { +# notify { 'baz!': } +# } +# +# See the documentation for "The Puppet Type System" for more information about types. +# See the `assert_type()` function for flexible ways to assert the type of a value. +# +Puppet::Functions.create_function(:is_a) do + dispatch :is_a do + param 'Any', :value + param 'Type', :type + end + + def is_a(value, type) + # See puppet's lib/puppet/pops/evaluator/evaluator_impl.rb eval_MatchExpression + Puppet::Pops::Types::TypeCalculator.instance?(type, value) + end +end diff --git a/lib/puppet/functions/is_absolute_path.rb b/lib/puppet/functions/is_absolute_path.rb new file mode 100644 index 0000000..b61064a --- /dev/null +++ b/lib/puppet/functions/is_absolute_path.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_absolute_path) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_absolute_path', "This method is deprecated, please use match expressions with Stdlib::Compat::Absolute_Path instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_absolute_path", args) + end +end diff --git a/lib/puppet/functions/is_array.rb b/lib/puppet/functions/is_array.rb new file mode 100644 index 0000000..a29fe8a --- /dev/null +++ b/lib/puppet/functions/is_array.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_array) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_array', "This method is deprecated, please use match expressions with Stdlib::Compat::Array instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_array", args) + end +end diff --git a/lib/puppet/functions/is_bool.rb b/lib/puppet/functions/is_bool.rb new file mode 100644 index 0000000..6e2c22b --- /dev/null +++ b/lib/puppet/functions/is_bool.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_bool) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_bool', "This method is deprecated, please use match expressions with Stdlib::Compat::Bool instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_bool", args) + end +end diff --git a/lib/puppet/functions/is_float.rb b/lib/puppet/functions/is_float.rb new file mode 100644 index 0000000..c91aa5d --- /dev/null +++ b/lib/puppet/functions/is_float.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_float) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_float', "This method is deprecated, please use match expressions with Stdlib::Compat::Float instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_float", args) + end +end diff --git a/lib/puppet/functions/is_ip_address.rb b/lib/puppet/functions/is_ip_address.rb new file mode 100644 index 0000000..4c72037 --- /dev/null +++ b/lib/puppet/functions/is_ip_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_ip_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_ip_address', "This method is deprecated, please use match expressions with Stdlib::Compat::Ip_address instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_ip_address", args) + end +end diff --git a/lib/puppet/functions/is_ipv4_address.rb b/lib/puppet/functions/is_ipv4_address.rb new file mode 100644 index 0000000..97b01ae --- /dev/null +++ b/lib/puppet/functions/is_ipv4_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_ipv4_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_ipv4_address', "This method is deprecated, please use match expressions with Stdlib::Compat::Ipv4 instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_ipv4_address", args) + end +end diff --git a/lib/puppet/functions/is_ipv6_address.rb b/lib/puppet/functions/is_ipv6_address.rb new file mode 100644 index 0000000..be0c98a --- /dev/null +++ b/lib/puppet/functions/is_ipv6_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_ipv6_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_ipv4_address', "This method is deprecated, please use match expressions with Stdlib::Compat::Ipv6 instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_ipv6_address", args) + end +end diff --git a/lib/puppet/functions/is_numeric.rb b/lib/puppet/functions/is_numeric.rb new file mode 100644 index 0000000..f5e9d41 --- /dev/null +++ b/lib/puppet/functions/is_numeric.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_numeric) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_numeric', "This method is deprecated, please use match expressions with Stdlib::Compat::Numeric instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_numeric", args) + end +end diff --git a/lib/puppet/functions/is_string.rb b/lib/puppet/functions/is_string.rb new file mode 100644 index 0000000..a05a796 --- /dev/null +++ b/lib/puppet/functions/is_string.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:is_string) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'is_string', "This method is deprecated, please use match expressions with Stdlib::Compat::String instead. They are described at https://docs.puppet.com/puppet/latest/reference/lang_data_type.html#match-expressions.") + scope.send("function_is_string", args) + end +end diff --git a/lib/puppet/functions/type_of.rb b/lib/puppet/functions/type_of.rb index 02cdd4d..01f1f49 100644 --- a/lib/puppet/functions/type_of.rb +++ b/lib/puppet/functions/type_of.rb @@ -10,6 +10,8 @@ # See the documentation for "The Puppet Type System" for more information about types. # See the `assert_type()` function for flexible ways to assert the type of a value. # +# The built-in type() function in puppet is generally preferred over this function +# this function is provided for backwards compatibility. Puppet::Functions.create_function(:type_of) do def type_of(value) Puppet::Pops::Types::TypeCalculator.infer_set(value) diff --git a/lib/puppet/functions/validate_absolute_path.rb b/lib/puppet/functions/validate_absolute_path.rb new file mode 100644 index 0000000..a3c696d --- /dev/null +++ b/lib/puppet/functions/validate_absolute_path.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_absolute_path) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_absolute_path', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Absolute_Path. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_absolute_path", args) + end +end diff --git a/lib/puppet/functions/validate_array.rb b/lib/puppet/functions/validate_array.rb new file mode 100644 index 0000000..f59c6b4 --- /dev/null +++ b/lib/puppet/functions/validate_array.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_array) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_array', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Array. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_array", args) + end +end diff --git a/lib/puppet/functions/validate_bool.rb b/lib/puppet/functions/validate_bool.rb new file mode 100644 index 0000000..5cfb2ac --- /dev/null +++ b/lib/puppet/functions/validate_bool.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_bool) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_bool', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Bool. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_bool", args) + end +end diff --git a/lib/puppet/functions/validate_hash.rb b/lib/puppet/functions/validate_hash.rb new file mode 100644 index 0000000..89ad9ab --- /dev/null +++ b/lib/puppet/functions/validate_hash.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_hash) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_hash', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Hash. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_hash", args) + end +end diff --git a/lib/puppet/functions/validate_integer.rb b/lib/puppet/functions/validate_integer.rb new file mode 100644 index 0000000..475ea0f --- /dev/null +++ b/lib/puppet/functions/validate_integer.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_integer) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_integer', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Integer. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_integer", args) + end +end diff --git a/lib/puppet/functions/validate_ip_address.rb b/lib/puppet/functions/validate_ip_address.rb new file mode 100644 index 0000000..1521c08 --- /dev/null +++ b/lib/puppet/functions/validate_ip_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_ip_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_ip_address', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ip_Address. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_ip_address", args) + end +end diff --git a/lib/puppet/functions/validate_ipv4_address.rb b/lib/puppet/functions/validate_ipv4_address.rb new file mode 100644 index 0000000..fe66ab3 --- /dev/null +++ b/lib/puppet/functions/validate_ipv4_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_ipv4_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_ipv4_address', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv4_Address. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_ipv4_address", args) + end +end diff --git a/lib/puppet/functions/validate_ipv6_address.rb b/lib/puppet/functions/validate_ipv6_address.rb new file mode 100644 index 0000000..7cc3cbd --- /dev/null +++ b/lib/puppet/functions/validate_ipv6_address.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_ipv6_address) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_ipv6_address', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv6_address. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_ipv6_address", args) + end +end diff --git a/lib/puppet/functions/validate_legacy.rb b/lib/puppet/functions/validate_legacy.rb new file mode 100644 index 0000000..c9d1f56 --- /dev/null +++ b/lib/puppet/functions/validate_legacy.rb @@ -0,0 +1,62 @@ +Puppet::Functions.create_function(:validate_legacy) do + # The function checks a value against both the target_type (new) and the previous_validation function (old). + + dispatch :validate_legacy do + param 'Any', :scope + param 'Type', :target_type + param 'String', :function_name + param 'Any', :value + repeated_param 'Any', :args + end + + dispatch :validate_legacy_s do + param 'Any', :scope + param 'String', :type_string + param 'String', :function_name + param 'Any', :value + repeated_param 'Any', :args + end + + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + + def validate_legacy_s(scope, type_string, *args) + t = Puppet::Pops::Types::TypeParser.new.parse(type_string, scope) + validate_legacy(scope, t, *args) + end + + def validate_legacy(scope, target_type, function_name, value, *prev_args) + if assert_type(target_type, value) + if previous_validation(scope, function_name, value, *prev_args) + # Silently passes + else + Puppet.notice("Accepting previously invalid value for target type '#{target_type}'") + end + else + inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value) + error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch("validate_legacy(#{function_name})", target_type, inferred_type) + if previous_validation(scope, function_name, value, *prev_args) + call_function('deprecation', 'validate_legacy', error_msg) + else + call_function('fail', error_msg) + end + end + end + + def previous_validation(scope, function_name, value, *prev_args) + # Call the previous validation function and catch any errors. Return true if no errors are thrown. + begin + scope.send("function_#{function_name}".to_s, [value, *prev_args]) + true + rescue Puppet::ParseError + false + end + end + + def assert_type(type, value) + Puppet::Pops::Types::TypeCalculator.instance?(type, value) + end +end diff --git a/lib/puppet/functions/validate_numeric.rb b/lib/puppet/functions/validate_numeric.rb new file mode 100644 index 0000000..3052d35 --- /dev/null +++ b/lib/puppet/functions/validate_numeric.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_numeric) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_numeric', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Numeric. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_numeric", args) + end +end diff --git a/lib/puppet/functions/validate_re.rb b/lib/puppet/functions/validate_re.rb new file mode 100644 index 0000000..19443a8 --- /dev/null +++ b/lib/puppet/functions/validate_re.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_re) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_re', "This method is deprecated, please use the stdlib validate_legacy function, with Pattern[]. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_re", args) + end +end diff --git a/lib/puppet/functions/validate_slength.rb b/lib/puppet/functions/validate_slength.rb new file mode 100644 index 0000000..584232a --- /dev/null +++ b/lib/puppet/functions/validate_slength.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_slength) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_slength', "This method is deprecated, please use the stdlib validate_legacy function, with String[]. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_slength", args) + end +end diff --git a/lib/puppet/functions/validate_string.rb b/lib/puppet/functions/validate_string.rb new file mode 100644 index 0000000..91ff004 --- /dev/null +++ b/lib/puppet/functions/validate_string.rb @@ -0,0 +1,15 @@ +Puppet::Functions.create_function(:validate_string) do + dispatch :deprecation_gen do + param 'Any', :scope + repeated_param 'Any', :args + end + # Workaround PUP-4438 (fixed: https://github.com/puppetlabs/puppet/commit/e01c4dc924cd963ff6630008a5200fc6a2023b08#diff-c937cc584953271bb3d3b3c2cb141790R221) to support puppet < 4.1.0 and puppet < 3.8.1. + def call(scope, *args) + manipulated_args = [scope] + args + self.class.dispatcher.dispatch(self, scope, manipulated_args) + end + def deprecation_gen(scope, *args) + call_function('deprecation', 'validate_string', "This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::String. There is further documentation for validate_legacy function in the README.") + scope.send("function_validate_string", args) + end +end diff --git a/lib/puppet/parser/functions/any2bool.rb b/lib/puppet/parser/functions/any2bool.rb new file mode 100644 index 0000000..f0f8f83 --- /dev/null +++ b/lib/puppet/parser/functions/any2bool.rb @@ -0,0 +1,55 @@ +# +# any2bool.rb +# + +module Puppet::Parser::Functions + newfunction(:any2bool, :type => :rvalue, :doc => <<-EOS +This converts 'anything' to a boolean. In practise it does the following: + +* Strings such as Y,y,1,T,t,TRUE,yes,'true' will return true +* Strings such as 0,F,f,N,n,FALSE,no,'false' will return false +* Booleans will just return their original value +* Number (or a string representation of a number) > 0 will return true, otherwise false +* undef will return false +* Anything else will return true + EOS + ) do |arguments| + + raise(Puppet::ParseError, "any2bool(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size < 1 + + # If argument is already Boolean, return it + if !!arguments[0] == arguments[0] + return arguments[0] + end + + arg = arguments[0] + + if arg == nil + return false + end + + if arg == :undef + return false + end + + valid_float = !!Float(arg) rescue false + + if arg.is_a?(Numeric) + return function_num2bool( [ arguments[0] ] ) + end + + if arg.is_a?(String) + if valid_float + return function_num2bool( [ arguments[0] ] ) + else + return function_str2bool( [ arguments[0] ] ) + end + end + + return true + + end +end + +# vim: set ts=2 sw=2 et : 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/base64.rb b/lib/puppet/parser/functions/base64.rb index 617ba31..a8998f2 100644 --- a/lib/puppet/parser/functions/base64.rb +++ b/lib/puppet/parser/functions/base64.rb @@ -6,14 +6,19 @@ module Puppet::Parser::Functions Usage: - $encodestring = base64('encode','thestring') - $decodestring = base64('decode','dGhlc3RyaW5n') + $encodestring = base64('encode', 'thestring') + $decodestring = base64('decode', 'dGhlc3RyaW5n') + + # explicitly define encode/decode method: default, strict, urlsafe + $method = 'default' + $encodestring = base64('encode', 'thestring', $method) + $decodestring = base64('decode', 'dGhlc3RyaW5n', $method) ENDHEREDOC require 'base64' - raise Puppet::ParseError, ("base64(): Wrong number of arguments (#{args.length}; must be = 2)") unless args.length == 2 + raise Puppet::ParseError, ("base64(): Wrong number of arguments (#{args.length}; must be >= 2)") unless args.length >= 2 actions = ['encode','decode'] @@ -25,11 +30,37 @@ module Puppet::Parser::Functions raise Puppet::ParseError, ("base64(): the second argument must be a string to base64") end + method = ['default','strict','urlsafe'] + + if args.length <= 2 + chosenMethod = 'default' + else + chosenMethod = args[2] + end + + unless method.include?(chosenMethod) + raise Puppet::ParseError, ("base64(): the third argument must be one of 'default', 'strict', or 'urlsafe'") + end + case args[0] when 'encode' - result = Base64.encode64(args[1]) + case chosenMethod + when 'default' + result = Base64.encode64(args[1]) + when 'strict' + result = Base64.strict_encode64(args[1]) + when 'urlsafe' + result = Base64.urlsafe_encode64(args[1]) + end when 'decode' - result = Base64.decode64(args[1]) + case chosenMethod + when 'default' + result = Base64.decode64(args[1]) + when 'strict' + result = Base64.strict_decode64(args[1]) + when 'urlsafe' + result = Base64.urlsafe_decode64(args[1]) + end end return result diff --git a/lib/puppet/parser/functions/bool2str.rb b/lib/puppet/parser/functions/bool2str.rb index fcd3791..7e36474 100644 --- a/lib/puppet/parser/functions/bool2str.rb +++ b/lib/puppet/parser/functions/bool2str.rb @@ -4,15 +4,29 @@ module Puppet::Parser::Functions newfunction(:bool2str, :type => :rvalue, :doc => <<-EOS - Converts a boolean to a string. + Converts a boolean to a string using optionally supplied arguments. The + optional second and third arguments represent what true and false will be + converted to respectively. If only one argument is given, it will be + converted from a boolean to a string containing 'true' or 'false'. + + *Examples:* + + bool2str(true) => 'true' + bool2str(true, 'yes', 'no') => 'yes' + bool2str(false, 't', 'f') => 'f' + Requires a single boolean as an input. EOS ) do |arguments| - raise(Puppet::ParseError, "bool2str(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 + unless arguments.size == 1 or arguments.size == 3 + raise(Puppet::ParseError, "bool2str(): Wrong number of arguments " + + "given (#{arguments.size} for 3)") + end value = arguments[0] + true_string = arguments[1] || 'true' + false_string = arguments[2] || 'false' klass = value.class # We can have either true or false, and nothing else @@ -20,7 +34,11 @@ module Puppet::Parser::Functions raise(Puppet::ParseError, 'bool2str(): Requires a boolean to work with') end - return value.to_s + unless [true_string, false_string].all?{|x| x.kind_of?(String)} + raise(Puppet::ParseError, "bool2str(): Requires strings to convert to" ) + end + + return value ? true_string : false_string end 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/clamp.rb b/lib/puppet/parser/functions/clamp.rb new file mode 100644 index 0000000..432c7c1 --- /dev/null +++ b/lib/puppet/parser/functions/clamp.rb @@ -0,0 +1,30 @@ +# +# clamp.rb +# + +module Puppet::Parser::Functions + newfunction(:clamp, :type => :rvalue, :arity => -2, :doc => <<-EOS + Clamps value to a range. + EOS + ) do |args| + + args.flatten! + + raise(Puppet::ParseError, 'clamp(): Wrong number of arguments, ' + + 'need three to clamp') if args.size != 3 + + # check values out + args.each do |value| + case [value.class] + when [String] + raise(Puppet::ParseError, "clamp(): Required explicit numeric (#{value}:String)") unless value =~ /^\d+$/ + when [Hash] + raise(Puppet::ParseError, "clamp(): The Hash type is not allowed (#{value})") + end + end + + # convert to numeric each element + # then sort them and get a middle value + args.map{ |n| n.to_i }.sort[1] + end +end diff --git a/lib/puppet/parser/functions/concat.rb b/lib/puppet/parser/functions/concat.rb index 618e62d..91edb4e 100644 --- a/lib/puppet/parser/functions/concat.rb +++ b/lib/puppet/parser/functions/concat.rb @@ -31,7 +31,7 @@ Would result in: arguments.shift arguments.each do |x| - result = result + Array(x) + result = result + (x.is_a?(Array) ? x : [x]) end return result diff --git a/lib/puppet/parser/functions/convert_base.rb b/lib/puppet/parser/functions/convert_base.rb new file mode 100644 index 0000000..0fcbafe --- /dev/null +++ b/lib/puppet/parser/functions/convert_base.rb @@ -0,0 +1,35 @@ +module Puppet::Parser::Functions + + newfunction(:convert_base, :type => :rvalue, :arity => 2, :doc => <<-'ENDHEREDOC') do |args| + + Converts a given integer or base 10 string representing an integer to a specified base, as a string. + + Usage: + + $binary_repr = convert_base(5, 2) # $binary_repr is now set to "101" + $hex_repr = convert_base("254", "16") # $hex_repr is now set to "fe" + + ENDHEREDOC + + raise Puppet::ParseError, ("convert_base(): First argument must be either a string or an integer") unless (args[0].is_a?(Integer) or args[0].is_a?(String)) + raise Puppet::ParseError, ("convert_base(): Second argument must be either a string or an integer") unless (args[1].is_a?(Integer) or args[1].is_a?(String)) + + if args[0].is_a?(String) + raise Puppet::ParseError, ("convert_base(): First argument must be an integer or a string corresponding to an integer in base 10") unless args[0] =~ /^[0-9]+$/ + end + + if args[1].is_a?(String) + raise Puppet::ParseError, ("convert_base(): First argument must be an integer or a string corresponding to an integer in base 10") unless args[1] =~ /^[0-9]+$/ + end + + number_to_convert = args[0] + new_base = args[1] + + number_to_convert = number_to_convert.to_i() + new_base = new_base.to_i() + + raise Puppet::ParseError, ("convert_base(): base must be at least 2 and must not be greater than 36") unless new_base >= 2 and new_base <= 36 + + return number_to_convert.to_s(new_base) + end +end diff --git a/lib/puppet/parser/functions/defined_with_params.rb b/lib/puppet/parser/functions/defined_with_params.rb index d7df306..99687ae 100644 --- a/lib/puppet/parser/functions/defined_with_params.rb +++ b/lib/puppet/parser/functions/defined_with_params.rb @@ -26,7 +26,10 @@ ENDOFDOC ret = false if resource = findresource(reference.to_s) matches = params.collect do |key, value| - resource[key] == value + # eql? avoids bugs caused by monkeypatching in puppet + resource_is_undef = resource[key].eql?(:undef) || resource[key].nil? + value_is_undef = value.eql?(:undef) || value.nil? + (resource_is_undef && value_is_undef) || (resource[key] == value) end ret = params.empty? || !matches.include?(false) end diff --git a/lib/puppet/parser/functions/delete.rb b/lib/puppet/parser/functions/delete.rb index f548b44..466c55c 100644 --- a/lib/puppet/parser/functions/delete.rb +++ b/lib/puppet/parser/functions/delete.rb @@ -2,8 +2,6 @@ # delete.rb # -# TODO(Krzysztof Wilczynski): We need to add support for regular expression ... - module Puppet::Parser::Functions newfunction(:delete, :type => :rvalue, :doc => <<-EOS Deletes all instances of a given element from an array, substring from a @@ -25,10 +23,8 @@ string, or key from a hash. EOS ) do |arguments| - if (arguments.size != 2) then - raise(Puppet::ParseError, "delete(): Wrong number of arguments "+ - "given #{arguments.size} for 2.") - end + raise(Puppet::ParseError, "delete(): Wrong number of arguments "+ + "given #{arguments.size} for 2") unless arguments.size == 2 collection = arguments[0].dup Array(arguments[1]).each do |item| diff --git a/lib/puppet/parser/functions/delete_regex.rb b/lib/puppet/parser/functions/delete_regex.rb new file mode 100644 index 0000000..d72b3e9 --- /dev/null +++ b/lib/puppet/parser/functions/delete_regex.rb @@ -0,0 +1,45 @@ +# +# delete_regex.rb +# + +module Puppet::Parser::Functions + newfunction(:delete_regex, :type => :rvalue, :doc => <<-EOS +deletes all instances of a given element that match a regular expression +from an array or key from a hash. Multiple regular expressions are assumed +to be matched as an OR. + +*Examples:* + + delete_regex(['a','b','c','b'], 'b') + Would return: ['a','c'] + + delete_regex(['a','b','c','b'], ['b', 'c']) + Would return: ['a'] + + delete_regex({'a'=>1,'b'=>2,'c'=>3}, 'b') + Would return: {'a'=>1,'c'=>3} + + delete_regex({'a'=>1,'b'=>2,'c'=>3}, '^a$') + Would return: {'b'=>2,'c'=>3} + + EOS + ) do |arguments| + + raise(Puppet::ParseError, "delete_regex(): Wrong number of arguments "+ + "given #{arguments.size} for 2") unless arguments.size == 2 + + collection = arguments[0].dup + Array(arguments[1]).each do |item| + case collection + when Array, Hash, String + collection.reject! { |coll_item| (coll_item =~ %r{\b#{item}\b}) } + else + raise(TypeError, "delete_regex(): First argument must be an Array, " + + "Hash, or String. Given an argument of class #{collection.class}.") + end + end + collection + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/deprecation.rb b/lib/puppet/parser/functions/deprecation.rb new file mode 100644 index 0000000..cd64fe2 --- /dev/null +++ b/lib/puppet/parser/functions/deprecation.rb @@ -0,0 +1,17 @@ +module Puppet::Parser::Functions + newfunction(:deprecation, :doc => <<-EOS + Function to print deprecation warnings (this is the 3.X version of it), The uniqueness key - can appear once. The msg is the message text including any positional information that is formatted by the user/caller of the method.). +EOS + ) do |arguments| + + raise(Puppet::ParseError, "deprecation: Wrong number of arguments " + + "given (#{arguments.size} for 2)") unless arguments.size == 2 + + key = arguments[0] + message = arguments[1] + + if ENV['STDLIB_LOG_DEPRECATIONS'] == "true" + warning("deprecation. #{key}. #{message}") + end + end +end diff --git a/lib/puppet/parser/functions/dig.rb b/lib/puppet/parser/functions/dig.rb new file mode 100644 index 0000000..34fa701 --- /dev/null +++ b/lib/puppet/parser/functions/dig.rb @@ -0,0 +1,16 @@ +# +# dig.rb +# + +module Puppet::Parser::Functions + newfunction(:dig, :type => :rvalue, :doc => <<-EOS + DEPRECATED: This function has been replaced in Puppet 4.5.0, please use dig44() for backwards compatibility or use the new version. + EOS + ) do |arguments| + warning("dig() DEPRECATED: This function has been replaced in Puppet 4.5.0, please use dig44() for backwards compatibility or use the new version.") + if ! Puppet::Parser::Functions.autoloader.loaded?(:dig44) + Puppet::Parser::Functions.autoloader.load(:dig44) + end + function_dig44(arguments) + end +end diff --git a/lib/puppet/parser/functions/dig44.rb b/lib/puppet/parser/functions/dig44.rb new file mode 100644 index 0000000..1e7c318 --- /dev/null +++ b/lib/puppet/parser/functions/dig44.rb @@ -0,0 +1,73 @@ +# +# dig44.rb +# + +module Puppet::Parser::Functions + newfunction( + :dig44, + :type => :rvalue, + :arity => -2, + :doc => <<-eos +DEPRECATED: This function has been replaced in puppet 4.5.0. + +Looks up into a complex structure of arrays and hashes and returns a value +or the default value if nothing was found. + +Key can contain slashes to describe path components. The function will go down +the structure and try to extract the required value. + +$data = { + 'a' => { + 'b' => [ + 'b1', + 'b2', + 'b3', + ] + } +} + +$value = dig44($data, ['a', 'b', '2'], 'not_found') +=> $value = 'b3' + +a -> first hash key +b -> second hash key +2 -> array index starting with 0 + +not_found -> (optional) will be returned if there is no value or the path +did not match. Defaults to nil. + +In addition to the required "key" argument, the function accepts a default +argument. It will be returned if no value was found or a path component is +missing. And the fourth argument can set a variable path separator. + eos + ) do |arguments| + # Two arguments are required + raise(Puppet::ParseError, "dig44(): Wrong number of arguments " + + "given (#{arguments.size} for at least 2)") if arguments.size < 2 + + data, path, default = *arguments + + unless data.is_a?(Hash) or data.is_a?(Array) + raise(Puppet::ParseError, "dig44(): first argument must be a hash or an array, " << + "given #{data.class.name}") + end + + unless path.is_a? Array + raise(Puppet::ParseError, "dig44(): second argument must be an array, " << + "given #{path.class.name}") + end + + value = path.reduce(data) do |structure, key| + if structure.is_a? Hash or structure.is_a? Array + if structure.is_a? Array + key = Integer key rescue break + end + break if structure[key].nil? or structure[key] == :undef + structure[key] + else + break + end + end + value.nil? ? default : value + end +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/dos2unix.rb b/lib/puppet/parser/functions/dos2unix.rb new file mode 100644 index 0000000..ccac899 --- /dev/null +++ b/lib/puppet/parser/functions/dos2unix.rb @@ -0,0 +1,15 @@ +# Custom Puppet function to convert dos to unix format +module Puppet::Parser::Functions + newfunction(:dos2unix, :type => :rvalue, :arity => 1, :doc => <<-EOS + Returns the Unix version of the given string. + Takes a single string argument. + EOS + ) do |arguments| + + unless arguments[0].is_a?(String) + raise(Puppet::ParseError, 'dos2unix(): Requires string as argument') + end + + arguments[0].gsub(/\r\n/, "\n") + end +end diff --git a/lib/puppet/parser/functions/empty.rb b/lib/puppet/parser/functions/empty.rb index cca620f..b5a3cde 100644 --- a/lib/puppet/parser/functions/empty.rb +++ b/lib/puppet/parser/functions/empty.rb @@ -13,14 +13,18 @@ Returns true if the variable is empty. value = arguments[0] - unless value.is_a?(Array) || value.is_a?(Hash) || value.is_a?(String) + unless value.is_a?(Array) || value.is_a?(Hash) || value.is_a?(String) || value.is_a?(Numeric) raise(Puppet::ParseError, 'empty(): Requires either ' + - 'array, hash or string to work with') + 'array, hash, string or integer to work with') end - result = value.empty? + if value.is_a?(Numeric) + return false + else + result = value.empty? - return result + return result + end end end diff --git a/lib/puppet/parser/functions/enclose_ipv6.rb b/lib/puppet/parser/functions/enclose_ipv6.rb new file mode 100644 index 0000000..80ffc3a --- /dev/null +++ b/lib/puppet/parser/functions/enclose_ipv6.rb @@ -0,0 +1,45 @@ +# +# enclose_ipv6.rb +# + +module Puppet::Parser::Functions + newfunction(:enclose_ipv6, :type => :rvalue, :doc => <<-EOS +Takes an array of ip addresses and encloses the ipv6 addresses with square brackets. + EOS + ) do |arguments| + + require 'ipaddr' + + rescuable_exceptions = [ ArgumentError ] + if defined?(IPAddr::InvalidAddressError) + rescuable_exceptions << IPAddr::InvalidAddressError + end + + if (arguments.size != 1) then + raise(Puppet::ParseError, "enclose_ipv6(): Wrong number of arguments "+ + "given #{arguments.size} for 1") + end + unless arguments[0].is_a?(String) or arguments[0].is_a?(Array) then + raise(Puppet::ParseError, "enclose_ipv6(): Wrong argument type "+ + "given #{arguments[0].class} expected String or Array") + end + + input = [arguments[0]].flatten.compact + result = [] + + input.each do |val| + unless val == '*' + begin + ip = IPAddr.new(val) + rescue *rescuable_exceptions + raise(Puppet::ParseError, "enclose_ipv6(): Wrong argument "+ + "given #{val} is not an ip address.") + end + val = "[#{ip.to_s}]" if ip.ipv6? + end + result << val + end + + return result.uniq + end +end diff --git a/lib/puppet/parser/functions/ensure_packages.rb b/lib/puppet/parser/functions/ensure_packages.rb index f1da4aa..439af1e 100644 --- a/lib/puppet/parser/functions/ensure_packages.rb +++ b/lib/puppet/parser/functions/ensure_packages.rb @@ -17,18 +17,29 @@ third argument to the ensure_resource() function. raise(Puppet::ParseError, 'ensure_packages(): Requires second argument to be a Hash') end - packages = Array(arguments[0]) + if arguments[0].is_a?(Hash) + if arguments[1] + defaults = { 'ensure' => 'present' }.merge(arguments[1]) + else + defaults = { 'ensure' => 'present' } + end - if arguments[1] - defaults = { 'ensure' => 'present' }.merge(arguments[1]) + Puppet::Parser::Functions.function(:ensure_resources) + function_ensure_resources(['package', arguments[0].dup, defaults ]) else - defaults = { 'ensure' => 'present' } - end + packages = Array(arguments[0]) + + if arguments[1] + defaults = { 'ensure' => 'present' }.merge(arguments[1]) + else + defaults = { 'ensure' => 'present' } + end - Puppet::Parser::Functions.function(:ensure_resource) - packages.each { |package_name| - function_ensure_resource(['package', package_name, defaults ]) + Puppet::Parser::Functions.function(:ensure_resource) + packages.each { |package_name| + function_ensure_resource(['package', package_name, defaults ]) } + end end end diff --git a/lib/puppet/parser/functions/ensure_resources.rb b/lib/puppet/parser/functions/ensure_resources.rb new file mode 100644 index 0000000..b3c51e6 --- /dev/null +++ b/lib/puppet/parser/functions/ensure_resources.rb @@ -0,0 +1,54 @@ +require 'puppet/parser/functions' + +Puppet::Parser::Functions.newfunction(:ensure_resources, + :type => :statement, + :doc => <<-'ENDOFDOC' +Takes a resource type, title (only hash), and a list of attributes that describe a +resource. + + user { 'dan': + gid => 'mygroup', + ensure => present, + } + +An hash of resources should be passed in and each will be created with +the type and parameters specified if it doesn't already exist. + + ensure_resources('user', {'dan' => { gid => 'mygroup', uid => '600' } , 'alex' => { gid => 'mygroup' }}, {'ensure' => 'present'}) + +From Hiera Backend: + +userlist: + dan: + gid: 'mygroup' + uid: '600' + alex: + gid: 'mygroup' + +Call: +ensure_resources('user', hiera_hash('userlist'), {'ensure' => 'present'}) + +ENDOFDOC +) do |vals| + type, title, params = vals + raise(ArgumentError, 'Must specify a type') unless type + raise(ArgumentError, 'Must specify a title') unless title + params ||= {} + + if title.is_a?(Hash) + resource_hash = title.dup + resources = resource_hash.keys + + Puppet::Parser::Functions.function(:ensure_resource) + resources.each { |resource_name| + if resource_hash[resource_name] + params_merged = params.merge(resource_hash[resource_name]) + else + params_merged = params + end + function_ensure_resource([ type, resource_name, params_merged ]) + } + else + raise(Puppet::ParseError, 'ensure_resources(): Requires second argument to be a Hash') + end +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..2bb1287 --- /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_string(): first argument must be a positive integer") unless function_is_integer([args[0]]) and args[0].to_i > 0 + raise(ArgumentError, "fqdn_rand_string(): 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 7f4d37d..b66431d 100644 --- a/lib/puppet/parser/functions/fqdn_rotate.rb +++ b/lib/puppet/parser/functions/fqdn_rotate.rb @@ -2,16 +2,23 @@ # fqdn_rotate.rb # -module Puppet::Parser::Functions - newfunction(:fqdn_rotate, :type => :rvalue, :doc => <<-EOS -Rotates an array a random number of times based on a nodes fqdn. - EOS - ) do |arguments| +Puppet::Parser::Functions.newfunction( + :fqdn_rotate, + :type => :rvalue, + :doc => "Usage: `fqdn_rotate(VALUE, [SEED])`. VALUE is required and + must be an array or a string. SEED is optional and may be any number + or string. + + Rotates VALUE a random number of times, combining the `$fqdn` fact and + the value of SEED for repeatable randomness. (That is, each node will + get a different random rotation 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 rotation.") do |args| raise(Puppet::ParseError, "fqdn_rotate(): Wrong number of arguments " + - "given (#{arguments.size} for 1)") if arguments.size < 1 + "given (#{args.size} for 1)") if args.size < 1 - value = arguments[0] + value = args.shift require 'digest/md5' unless value.is_a?(Array) || value.is_a?(String) @@ -31,15 +38,26 @@ 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'),args].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 + old_seed = srand(seed) + offset = rand(elements) + srand(old_seed) + end + end + offset.times { result.push result.shift } result = string ? result.join : result return result - end end # vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/fqdn_uuid.rb b/lib/puppet/parser/functions/fqdn_uuid.rb new file mode 100644 index 0000000..30205d0 --- /dev/null +++ b/lib/puppet/parser/functions/fqdn_uuid.rb @@ -0,0 +1,92 @@ +require 'digest/sha1' + +module Puppet::Parser::Functions + newfunction(:fqdn_uuid, :type => :rvalue, :doc => <<-END) do |args| + + Creates a UUID based on a given string, assumed to be the FQDN + + For example, to generate a UUID based on the FQDN of a system: + + Usage: + + $uuid = fqdn_uuid($::fqdn) + + The generated UUID will be the same for the given hostname + + The resulting UUID is returned on the form: + + 1d839dea-5e10-5243-88eb-e66815bd7d5c + + (u.e. without any curly braces.) + + The generated UUID is a version 5 UUID with the V5 DNS namespace: + + 6ba7b810-9dad-11d1-80b4-00c04fd430c8 + + This only supports a the V5 SHA-1 hash, using the DNS namespace. + + Please consult http://www.ietf.org/rfc/rfc4122.txt for the details on + UUID generation and example implementation. + + No verification is present at the moment as whether the domain name given + is in fact a correct fully-qualified domain name. Therefore any arbitrary + string and/or alpha-numeric value can subside for a domain name. + EOS + + END + + if args.length == 0 + raise(ArgumentError, "fqdn_uuid: No arguments given") + elsif args.length == 1 + fqdn = args[0] + else + raise(ArgumentError, "fqdn_uuid: Too many arguments given (#{args.length})") + end + + # Code lovingly taken from + # https://github.com/puppetlabs/marionette-collective/blob/master/lib/mcollective/ssl.rb + + # This is the UUID version 5 type DNS name space which is as follows: + # + # 6ba7b810-9dad-11d1-80b4-00c04fd430c8 + # + uuid_name_space_dns = [0x6b, + 0xa7, + 0xb8, + 0x10, + 0x9d, + 0xad, + 0x11, + 0xd1, + 0x80, + 0xb4, + 0x00, + 0xc0, + 0x4f, + 0xd4, + 0x30, + 0xc8 + ].map {|b| b.chr}.join + + sha1 = Digest::SHA1.new + sha1.update(uuid_name_space_dns) + sha1.update(fqdn) + + # first 16 bytes.. + bytes = sha1.digest[0, 16].bytes.to_a + + # version 5 adjustments + bytes[6] &= 0x0f + bytes[6] |= 0x50 + + # variant is DCE 1.1 + bytes[8] &= 0x3f + bytes[8] |= 0x80 + + bytes = [4, 2, 2, 2, 6].collect do |i| + bytes.slice!(0, i).pack('C*').unpack('H*') + end + + bytes.join('-') + end +end diff --git a/lib/puppet/parser/functions/getparam.rb b/lib/puppet/parser/functions/getparam.rb index 6d51006..0a5cbe0 100644 --- a/lib/puppet/parser/functions/getparam.rb +++ b/lib/puppet/parser/functions/getparam.rb @@ -28,7 +28,7 @@ ENDOFDOC return '' if param.empty? if resource = findresource(reference.to_s) - return resource[param] if resource[param] + return resource[param] unless resource[param].nil? end return '' diff --git a/lib/puppet/parser/functions/getvar.rb b/lib/puppet/parser/functions/getvar.rb index fb336b6..3af8d48 100644 --- a/lib/puppet/parser/functions/getvar.rb +++ b/lib/puppet/parser/functions/getvar.rb @@ -20,7 +20,13 @@ module Puppet::Parser::Functions end begin - self.lookupvar("#{args[0]}") + result = nil + catch(:undefined_variable) do + result = self.lookupvar("#{args[0]}") + end + + # avoid relying on incosistent behaviour around ruby return values from catch + result rescue Puppet::ParseError # Eat the exception if strict_variables = true is set end diff --git a/lib/puppet/parser/functions/has_interface_with.rb b/lib/puppet/parser/functions/has_interface_with.rb index 3691524..e762798 100644 --- a/lib/puppet/parser/functions/has_interface_with.rb +++ b/lib/puppet/parser/functions/has_interface_with.rb @@ -38,8 +38,11 @@ has_interface_with("lo") => true # 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) + begin + catch :undefined_variable do + factval = lookupvar(kind) + end + rescue Puppet::ParseError # Eat the exception if strict_variables = true is set end if factval == value return true diff --git a/lib/puppet/parser/functions/hash.rb b/lib/puppet/parser/functions/hash.rb index 8cc4823..89d0e07 100644 --- a/lib/puppet/parser/functions/hash.rb +++ b/lib/puppet/parser/functions/hash.rb @@ -29,7 +29,7 @@ Would return: {'a'=>1,'b'=>2,'c'=>3} # This is to make it compatible with older version of Ruby ... array = array.flatten result = Hash[*array] - rescue Exception + rescue StandardError raise(Puppet::ParseError, 'hash(): Unable to compute ' + 'hash from array given') end diff --git a/lib/puppet/parser/functions/intersection.rb b/lib/puppet/parser/functions/intersection.rb index 48f02e9..bfbb4ba 100644 --- a/lib/puppet/parser/functions/intersection.rb +++ b/lib/puppet/parser/functions/intersection.rb @@ -4,13 +4,13 @@ module Puppet::Parser::Functions newfunction(:intersection, :type => :rvalue, :doc => <<-EOS -This function returns an array an intersection of two. +This function returns an array of the intersection of two. *Examples:* - intersection(["a","b","c"],["b","c","d"]) + intersection(["a","b","c"],["b","c","d"]) # returns ["b","c"] + intersection(["a","b","c"],[1,2,3,4]) # returns [] (true, when evaluated as a Boolean) -Would return: ["b","c"] EOS ) do |arguments| diff --git a/lib/puppet/parser/functions/is_absolute_path.rb b/lib/puppet/parser/functions/is_absolute_path.rb new file mode 100644 index 0000000..e64777f --- /dev/null +++ b/lib/puppet/parser/functions/is_absolute_path.rb @@ -0,0 +1,50 @@ +module Puppet::Parser::Functions + newfunction(:is_absolute_path, :type => :rvalue, :arity => 1, :doc => <<-'ENDHEREDOC') do |args| + Returns boolean true if the string represents an absolute path in the filesystem. This function works + for windows and unix style paths. + + The following values will return true: + + $my_path = 'C:/Program Files (x86)/Puppet Labs/Puppet' + is_absolute_path($my_path) + $my_path2 = '/var/lib/puppet' + is_absolute_path($my_path2) + $my_path3 = ['C:/Program Files (x86)/Puppet Labs/Puppet'] + is_absolute_path($my_path3) + $my_path4 = ['/var/lib/puppet'] + is_absolute_path($my_path4) + + The following values will return false: + + is_absolute_path(true) + is_absolute_path('../var/lib/puppet') + is_absolute_path('var/lib/puppet') + $undefined = undef + is_absolute_path($undefined) + + ENDHEREDOC + function_deprecation([:is_absolute_path, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Absolute_path. There is further documentation for validate_legacy function in the README.']) + require 'puppet/util' + + path = args[0] + # 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 + value = (Puppet::Util.absolute_path?(path, :posix) or Puppet::Util.absolute_path?(path, :windows)) + 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!^/! + } + value = (!!(path =~ regexes[:posix])) || (!!(path =~ regexes[:windows])) + end + value + end +end diff --git a/lib/puppet/parser/functions/is_array.rb b/lib/puppet/parser/functions/is_array.rb index b39e184..1d2c0fa 100644 --- a/lib/puppet/parser/functions/is_array.rb +++ b/lib/puppet/parser/functions/is_array.rb @@ -8,6 +8,8 @@ Returns true if the variable passed to this function is an array. EOS ) do |arguments| + function_deprecation([:is_array, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Array. There is further documentation for validate_legacy function in the README.']) + raise(Puppet::ParseError, "is_array(): Wrong number of arguments " + "given (#{arguments.size} for 1)") if arguments.size < 1 @@ -18,5 +20,3 @@ Returns true if the variable passed to this function is an array. return result end end - -# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_bool.rb b/lib/puppet/parser/functions/is_bool.rb index 8bbdbc8..83d2ebe 100644 --- a/lib/puppet/parser/functions/is_bool.rb +++ b/lib/puppet/parser/functions/is_bool.rb @@ -8,6 +8,8 @@ Returns true if the variable passed to this function is a boolean. EOS ) do |arguments| + function_deprecation([:is_bool, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Bool. There is further documentation for validate_legacy function in the README.']) + raise(Puppet::ParseError, "is_bool(): Wrong number of arguments " + "given (#{arguments.size} for 1)") if arguments.size != 1 @@ -18,5 +20,3 @@ Returns true if the variable passed to this function is a boolean. return result end end - -# vim: set ts=2 sw=2 et : 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/is_email_address.rb b/lib/puppet/parser/functions/is_email_address.rb new file mode 100644 index 0000000..4fb0229 --- /dev/null +++ b/lib/puppet/parser/functions/is_email_address.rb @@ -0,0 +1,21 @@ +# +# is_email_address.rb +# + +module Puppet::Parser::Functions + newfunction(:is_email_address, :type => :rvalue, :doc => <<-EOS +Returns true if the string passed to this function is a valid email address. + EOS + ) do |arguments| + if arguments.size != 1 + raise(Puppet::ParseError, 'is_email_address(): Wrong number of arguments '\ + "given #{arguments.size} for 1") + end + + # Taken from http://emailregex.com/ (simpler regex) + valid_email_regex = %r{\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z} + return (arguments[0] =~ valid_email_regex) == 0 + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_float.rb b/lib/puppet/parser/functions/is_float.rb index a2da943..1186458 100644 --- a/lib/puppet/parser/functions/is_float.rb +++ b/lib/puppet/parser/functions/is_float.rb @@ -8,6 +8,8 @@ Returns true if the variable passed to this function is a float. EOS ) do |arguments| + function_deprecation([:is_float, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Float. There is further documentation for validate_legacy function in the README.']) + if (arguments.size != 1) then raise(Puppet::ParseError, "is_float(): Wrong number of arguments "+ "given #{arguments.size} for 1") diff --git a/lib/puppet/parser/functions/is_integer.rb b/lib/puppet/parser/functions/is_integer.rb index c03d28d..e04fd1f 100644 --- a/lib/puppet/parser/functions/is_integer.rb +++ b/lib/puppet/parser/functions/is_integer.rb @@ -13,6 +13,8 @@ If given any other argument `false` is returned. EOS ) do |arguments| + function_deprecation([:is_integer, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Integer. There is further documentation for validate_legacy function in the README.']) + if (arguments.size != 1) then raise(Puppet::ParseError, "is_integer(): Wrong number of arguments "+ "given #{arguments.size} for 1") diff --git a/lib/puppet/parser/functions/is_ip_address.rb b/lib/puppet/parser/functions/is_ip_address.rb index a90adab..5f1d765 100644 --- a/lib/puppet/parser/functions/is_ip_address.rb +++ b/lib/puppet/parser/functions/is_ip_address.rb @@ -10,6 +10,8 @@ Returns true if the string passed to this function is a valid IP address. require 'ipaddr' + function_deprecation([:is_ip_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ip_address. There is further documentation for validate_legacy function in the README.']) + if (arguments.size != 1) then raise(Puppet::ParseError, "is_ip_address(): Wrong number of arguments "+ "given #{arguments.size} for 1") diff --git a/lib/puppet/parser/functions/is_ipv4_address.rb b/lib/puppet/parser/functions/is_ipv4_address.rb new file mode 100644 index 0000000..1764e61 --- /dev/null +++ b/lib/puppet/parser/functions/is_ipv4_address.rb @@ -0,0 +1,30 @@ +# +# is_ipv4_address.rb +# + +module Puppet::Parser::Functions + newfunction(:is_ipv4_address, :type => :rvalue, :doc => <<-EOS +Returns true if the string passed to this function is a valid IPv4 address. + EOS + ) do |arguments| + + require 'ipaddr' + + function_deprecation([:is_ipv4_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv4. There is further documentation for validate_legacy function in the README.']) + + if (arguments.size != 1) then + raise(Puppet::ParseError, "is_ipv4_address(): Wrong number of arguments "+ + "given #{arguments.size} for 1") + end + + begin + ip = IPAddr.new(arguments[0]) + rescue ArgumentError + return false + end + + return ip.ipv4? + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_ipv6_address.rb b/lib/puppet/parser/functions/is_ipv6_address.rb new file mode 100644 index 0000000..7ca4997 --- /dev/null +++ b/lib/puppet/parser/functions/is_ipv6_address.rb @@ -0,0 +1,30 @@ +# +# is_ipv6_address.rb +# + +module Puppet::Parser::Functions + newfunction(:is_ipv6_address, :type => :rvalue, :doc => <<-EOS +Returns true if the string passed to this function is a valid IPv6 address. + EOS + ) do |arguments| + + function_deprecation([:is_ipv6_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv6. There is further documentation for validate_legacy function in the README.']) + + require 'ipaddr' + + if (arguments.size != 1) then + raise(Puppet::ParseError, "is_ipv6_address(): Wrong number of arguments "+ + "given #{arguments.size} for 1") + end + + begin + ip = IPAddr.new(arguments[0]) + rescue ArgumentError + return false + end + + return ip.ipv6? + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_mac_address.rb b/lib/puppet/parser/functions/is_mac_address.rb index 1b3088a..2619d44 100644 --- a/lib/puppet/parser/functions/is_mac_address.rb +++ b/lib/puppet/parser/functions/is_mac_address.rb @@ -15,7 +15,7 @@ Returns true if the string passed to this function is a valid mac address. mac = arguments[0] - if /^[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}:[a-fA-F0-9]{1,2}$/.match(mac) then + if /^[a-f0-9]{1,2}(:[a-f0-9]{1,2}){5}$/i.match(mac) then return true else return false diff --git a/lib/puppet/parser/functions/is_numeric.rb b/lib/puppet/parser/functions/is_numeric.rb index e7e1d2a..4a55225 100644 --- a/lib/puppet/parser/functions/is_numeric.rb +++ b/lib/puppet/parser/functions/is_numeric.rb @@ -24,6 +24,8 @@ Valid examples: EOS ) do |arguments| + function_deprecation([:is_numeric, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Numeric. There is further documentation for validate_legacy function in the README.']) + if (arguments.size != 1) then raise(Puppet::ParseError, "is_numeric(): Wrong number of arguments "+ "given #{arguments.size} for 1") @@ -71,5 +73,3 @@ Valid examples: end end end - -# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/is_string.rb b/lib/puppet/parser/functions/is_string.rb index f5bef04..31ee91e 100644 --- a/lib/puppet/parser/functions/is_string.rb +++ b/lib/puppet/parser/functions/is_string.rb @@ -8,12 +8,15 @@ Returns true if the variable passed to this function is a string. EOS ) do |arguments| + function_deprecation([:is_string, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::String. There is further documentation for validate_legacy function in the README.']) + raise(Puppet::ParseError, "is_string(): Wrong number of arguments " + "given (#{arguments.size} for 1)") if arguments.size < 1 type = arguments[0] - result = type.is_a?(String) + # when called through the v4 API shim, undef gets translated to nil + result = type.is_a?(String) || type.nil? if result and (type == type.to_f.to_s or type == type.to_i.to_s) then return false diff --git a/lib/puppet/parser/functions/join_keys_to_values.rb b/lib/puppet/parser/functions/join_keys_to_values.rb index e9924fe..e3baf9f 100644 --- a/lib/puppet/parser/functions/join_keys_to_values.rb +++ b/lib/puppet/parser/functions/join_keys_to_values.rb @@ -5,7 +5,8 @@ module Puppet::Parser::Functions newfunction(:join_keys_to_values, :type => :rvalue, :doc => <<-EOS This function joins each key of a hash to that key's corresponding value with a -separator. Keys and values are cast to strings. The return value is an array in +separator. Keys are cast to strings. If values are arrays, multiple keys +are added for each element. The return value is an array in which each element is one joined key/value pair. *Examples:* @@ -13,6 +14,10 @@ which each element is one joined key/value pair. join_keys_to_values({'a'=>1,'b'=>2}, " is ") Would result in: ["a is 1","b is 2"] + + join_keys_to_values({'a'=>1,'b'=>[2,3]}, " is ") + +Would result in: ["a is 1","b is 2","b is 3"] EOS ) do |arguments| @@ -38,8 +43,12 @@ Would result in: ["a is 1","b is 2"] # Join the keys to their values. hash.map do |k,v| - String(k) + separator + String(v) - end + if v.is_a?(Array) + v.map { |va| String(k) + separator + String(va) } + else + String(k) + separator + String(v) + end + end.flatten end end diff --git a/lib/puppet/parser/functions/load_module_metadata.rb b/lib/puppet/parser/functions/load_module_metadata.rb new file mode 100644 index 0000000..c9b8488 --- /dev/null +++ b/lib/puppet/parser/functions/load_module_metadata.rb @@ -0,0 +1,24 @@ +module Puppet::Parser::Functions + newfunction(:load_module_metadata, :type => :rvalue, :doc => <<-EOT + EOT + ) do |args| + raise(Puppet::ParseError, "load_module_metadata(): Wrong number of arguments, expects one or two") unless [1,2].include?(args.size) + mod = args[0] + allow_empty_metadata = args[1] + module_path = function_get_module_path([mod]) + metadata_json = File.join(module_path, 'metadata.json') + + metadata_exists = File.exists?(metadata_json) + if metadata_exists + metadata = PSON.load(File.read(metadata_json)) + else + if allow_empty_metadata + metadata = {} + else + raise(Puppet::ParseError, "load_module_metadata(): No metadata.json file for module #{mod}") + end + end + + return metadata + end +end diff --git a/lib/puppet/parser/functions/loadjson.rb b/lib/puppet/parser/functions/loadjson.rb new file mode 100644 index 0000000..3a3372b --- /dev/null +++ b/lib/puppet/parser/functions/loadjson.rb @@ -0,0 +1,34 @@ +module Puppet::Parser::Functions + newfunction(:loadjson, :type => :rvalue, :arity => -2, :doc => <<-'ENDHEREDOC') do |args| +Load a JSON file containing an array, string, or hash, and return the data +in the corresponding native data type. +The second parameter is the default value. It will be returned if the file +was not found or could not be parsed. + +For example: + + $myhash = loadjson('/etc/puppet/data/myhash.json') + $myhash = loadjson('no-file.json', {'default' => 'value'}) + ENDHEREDOC + + raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless args.length >= 1 + + if File.exists?(args[0]) + begin + content = File.read(args[0]) + PSON::load(content) || args[1] + rescue Exception => e + if args[1] + args[1] + else + raise e + end + end + else + warning("Can't load '#{args[0]}' File does not exist!") + args[1] + end + + end + +end diff --git a/lib/puppet/parser/functions/loadyaml.rb b/lib/puppet/parser/functions/loadyaml.rb index 10c4005..9696362 100644 --- a/lib/puppet/parser/functions/loadyaml.rb +++ b/lib/puppet/parser/functions/loadyaml.rb @@ -1,20 +1,34 @@ module Puppet::Parser::Functions + newfunction(:loadyaml, :type => :rvalue, :arity => -2, :doc => <<-'ENDHEREDOC') do |args| +Load a YAML file containing an array, string, or hash, and return the data +in the corresponding native data type. +The second parameter is the default value. It will be returned if the file +was not found or could not be parsed. - newfunction(:loadyaml, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Load a YAML file containing an array, string, or hash, and return the data - in the corresponding native data type. +For example: - For example: + $myhash = loadyaml('/etc/puppet/data/myhash.yaml') + $myhash = loadyaml('no-file.yaml', {'default' => 'value'}) + ENDHEREDOC - $myhash = loadyaml('/etc/puppet/data/myhash.yaml') - ENDHEREDOC + raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless args.length >= 1 + require 'yaml' - unless args.length == 1 - raise Puppet::ParseError, ("loadyaml(): wrong number of arguments (#{args.length}; must be 1)") + if File.exists?(args[0]) + begin + YAML::load_file(args[0]) || args[1] + rescue Exception => e + if args[1] + args[1] + else + raise e + end + end + else + warning("Can't load '#{args[0]}' File does not exist!") + args[1] end - YAML.load_file(args[0]) - end end diff --git a/lib/puppet/parser/functions/member.rb b/lib/puppet/parser/functions/member.rb index 88609ce..1e5b3de 100644 --- a/lib/puppet/parser/functions/member.rb +++ b/lib/puppet/parser/functions/member.rb @@ -44,7 +44,7 @@ would return: false end if arguments[1].is_a? String or arguments[1].is_a? Fixnum - item = Array(arguments[1]) + item = [arguments[1]] else item = arguments[1] end diff --git a/lib/puppet/parser/functions/parsejson.rb b/lib/puppet/parser/functions/parsejson.rb index a9a16a4..f7c2896 100644 --- a/lib/puppet/parser/functions/parsejson.rb +++ b/lib/puppet/parser/functions/parsejson.rb @@ -4,20 +4,25 @@ module Puppet::Parser::Functions newfunction(:parsejson, :type => :rvalue, :doc => <<-EOS -This function accepts JSON as a string and converts into the correct Puppet -structure. - EOS +This function accepts JSON as a string and converts it into the correct +Puppet structure. + +The optional second argument can be used to pass a default value that will +be returned if the parsing of YAML string have failed. + EOS ) do |arguments| + raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless arguments.length >= 1 - if (arguments.size != 1) then - raise(Puppet::ParseError, "parsejson(): Wrong number of arguments "+ - "given #{arguments.size} for 1") + begin + PSON::load(arguments[0]) || arguments[1] + rescue StandardError => e + if arguments[1] + arguments[1] + else + raise e + end end - json = arguments[0] - - # PSON is natively available in puppet - PSON.load(json) end end diff --git a/lib/puppet/parser/functions/parseyaml.rb b/lib/puppet/parser/functions/parseyaml.rb index 53d54fa..ba9d98a 100644 --- a/lib/puppet/parser/functions/parseyaml.rb +++ b/lib/puppet/parser/functions/parseyaml.rb @@ -6,17 +6,26 @@ module Puppet::Parser::Functions newfunction(:parseyaml, :type => :rvalue, :doc => <<-EOS This function accepts YAML as a string and converts it into the correct Puppet structure. - EOS - ) do |arguments| - - if (arguments.size != 1) then - raise(Puppet::ParseError, "parseyaml(): Wrong number of arguments "+ - "given #{arguments.size} for 1") - end +The optional second argument can be used to pass a default value that will +be returned if the parsing of YAML string have failed. + EOS + ) do |arguments| + raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless arguments.length >= 1 require 'yaml' - YAML::load(arguments[0]) + begin + YAML::load(arguments[0]) || arguments[1] + # in ruby 1.9.3 Psych::SyntaxError is a RuntimeException + # this still needs to catch that and work also on rubies that + # do not have Psych available. + rescue StandardError, Psych::SyntaxError => e + if arguments[1] + arguments[1] + else + raise e + end + end end 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/pry.rb b/lib/puppet/parser/functions/pry.rb new file mode 100644 index 0000000..c18ef7e --- /dev/null +++ b/lib/puppet/parser/functions/pry.rb @@ -0,0 +1,30 @@ +# +# pry.rb +# + +module Puppet::Parser::Functions + newfunction(:pry, :type => :statement, :doc => <<-EOS +This function invokes a pry debugging session in the current scope object. This is useful for debugging manifest code at specific points during a compilation. + +*Examples:* + + pry() + EOS + ) do |arguments| + begin + require 'pry' + rescue LoadError + raise(Puppet::Error, "pry(): Requires the 'pry' rubygem to use, but it was not found") + end + # + ## Run `catalog` to see the contents currently compiling catalog + ## Run `cd catalog` and `ls` to see catalog methods and instance variables + ## Run `@resource_table` to see the current catalog resource table + # + if $stdout.isatty + binding.pry # rubocop:disable Lint/Debugger + else + Puppet.warning 'pry(): cowardly refusing to start the debugger on a daemonized master' + end + 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..41d4223 --- /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? + + salt = "$#{hash_type}$#{args[2]}" + + # handle weak implementations of String#crypt + if 'test'.crypt('$1$1') != '$1$1$Bp8CU9Oujr9SSEw53WV6G.' + # JRuby < 1.7.17 + if RUBY_PLATFORM == 'java' + # puppetserver bundles Apache Commons Codec + org.apache.commons.codec.digest.Crypt.crypt(password.to_java_bytes, salt) + else + # MS Windows and other systems that don't support enhanced salts + raise Puppet::ParseError, 'system does not support enhanced salts' + end + else + password.crypt(salt) + end +end diff --git a/lib/puppet/parser/functions/range.rb b/lib/puppet/parser/functions/range.rb index 49fba21..d690df7 100644 --- a/lib/puppet/parser/functions/range.rb +++ b/lib/puppet/parser/functions/range.rb @@ -25,8 +25,8 @@ integers automatically) Will return: ["a","b","c"] range("host01", "host10") - Will return: ["host01", "host02", ..., "host09", "host10"] +NB Be explicit in including trailing zeros. Otherwise the underlying ruby function will fail. Passing a third argument will cause the generated range to step by that interval, e.g. @@ -37,18 +37,17 @@ Will return: [0,2,4,6,8] EOS ) do |arguments| - # We support more than one argument but at least one is mandatory ... - raise(Puppet::ParseError, "range(): Wrong number of " + - "arguments given (#{arguments.size} for 1)") if arguments.size < 1 + raise(Puppet::ParseError, 'range(): Wrong number of ' + + 'arguments given (0 for 1)') if arguments.size == 0 if arguments.size > 1 start = arguments[0] stop = arguments[1] step = arguments[2].nil? ? 1 : arguments[2].to_i.abs - type = '..' # We select simplest type for Range available in Ruby ... + type = '..' # Use the simplest type of Range available in Ruby - elsif arguments.size > 0 + else # arguments.size == 1 value = arguments[0] if m = value.match(/^(\w+)(\.\.\.?|\-)(\w+)$/) @@ -56,16 +55,16 @@ Will return: [0,2,4,6,8] stop = m[3] type = m[2] - + step = 1 elsif value.match(/^.+$/) - raise(Puppet::ParseError, 'range(): Unable to compute range ' + - 'from the value given') + raise(Puppet::ParseError, "range(): Unable to compute range " + + "from the value: #{value}") else - raise(Puppet::ParseError, 'range(): Unknown format of range given') + raise(Puppet::ParseError, "range(): Unknown range format: #{value}") end end - # Check whether we have integer value if so then make it so ... + # If we were given an integer, ensure we work with one if start.to_s.match(/^\d+$/) start = start.to_i stop = stop.to_i @@ -76,10 +75,10 @@ Will return: [0,2,4,6,8] range = case type when /^(\.\.|\-)$/ then (start .. stop) - when /^(\.\.\.)$/ then (start ... stop) # Exclusive of last element ... + when '...' then (start ... stop) # Exclusive of last element end - result = range.step(step).collect { |i| i } # Get them all ... Pokemon ... + result = range.step(step).to_a return result end diff --git a/lib/puppet/parser/functions/regexpescape.rb b/lib/puppet/parser/functions/regexpescape.rb new file mode 100644 index 0000000..477ee87 --- /dev/null +++ b/lib/puppet/parser/functions/regexpescape.rb @@ -0,0 +1,31 @@ +# +# regexpescape.rb +# +module Puppet::Parser::Functions + newfunction(:regexpescape, :type => :rvalue, :doc => <<-EOS + Regexp escape a string or array of strings. + Requires either a single string or an array as an input. + EOS + ) do |arguments| # rubocop:disable Style/ClosingParenthesisIndentation + raise(Puppet::ParseError, 'regexpescape(): Wrong number of arguments ' \ + "given (#{arguments.size} for 1)") if arguments.empty? + + value = arguments[0] + + unless value.is_a?(Array) || value.is_a?(String) + raise(Puppet::ParseError, 'regexpescape(): Requires either ' \ + 'array or string to work with') + end + + result = if value.is_a?(Array) + # Numbers in Puppet are often string-encoded which is troublesome ... + value.collect { |i| i.is_a?(String) ? Regexp.escape(i) : i } + else + Regexp.escape(value) + end + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/seeded_rand.rb b/lib/puppet/parser/functions/seeded_rand.rb new file mode 100644 index 0000000..44e27b8 --- /dev/null +++ b/lib/puppet/parser/functions/seeded_rand.rb @@ -0,0 +1,22 @@ +Puppet::Parser::Functions::newfunction( + :seeded_rand, + :arity => 2, + :type => :rvalue, + :doc => <<-EOS +Usage: `seeded_rand(MAX, SEED)`. MAX must be a positive integer; SEED is any string. + +Generates a random whole number greater than or equal to 0 and less +than MAX, using the value of SEED for repeatable randomness. If SEED +starts with "$fqdn:", this is behaves the same as `fqdn_rand`. + +EOS +) do |args| + require 'digest/md5' + + raise(ArgumentError, "seeded_rand(): first argument must be a positive integer") unless function_is_integer([args[0]]) and args[0].to_i > 0 + raise(ArgumentError, "seeded_rand(): second argument must be a string") unless args[1].is_a? String + + max = args[0].to_i + seed = Digest::MD5.hexdigest(args[1]).hex + Puppet::Util.deterministic_rand(seed,max) +end diff --git a/lib/puppet/parser/functions/shell_escape.rb b/lib/puppet/parser/functions/shell_escape.rb new file mode 100644 index 0000000..447fe35 --- /dev/null +++ b/lib/puppet/parser/functions/shell_escape.rb @@ -0,0 +1,30 @@ +# +# shell_escape.rb +# + +require 'shellwords' + +module Puppet::Parser::Functions + newfunction(:shell_escape, :type => :rvalue, :doc => <<-EOS +Escapes a string so that it can be safely used in a Bourne shell command line. + +Note that the resulting string should be used unquoted and is not intended for use in double quotes nor in single +quotes. + +This function behaves the same as ruby's Shellwords.shellescape() function. + EOS + ) do |arguments| + + raise(Puppet::ParseError, "shell_escape(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + # explicit conversion to string is required for ruby 1.9 + string = arguments[0].to_s + + result = Shellwords.shellescape(string) + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/shell_join.rb b/lib/puppet/parser/functions/shell_join.rb new file mode 100644 index 0000000..05aeb95 --- /dev/null +++ b/lib/puppet/parser/functions/shell_join.rb @@ -0,0 +1,31 @@ +# +# shell_join.rb +# + +require 'shellwords' + +module Puppet::Parser::Functions + newfunction(:shell_join, :type => :rvalue, :doc => <<-EOS +Builds a command line string from the given array of strings. Each array item is escaped for Bourne shell. All items are +then joined together, with a single space in between. + +This function behaves the same as ruby's Shellwords.shelljoin() function + EOS + ) do |arguments| + + raise(Puppet::ParseError, "shell_join(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + array = arguments[0] + + raise Puppet::ParseError, ("First argument is not an Array: #{array.inspect}") unless array.is_a?(Array) + + # explicit conversion to string is required for ruby 1.9 + array = array.map { |item| item.to_s } + result = Shellwords.shelljoin(array) + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/shell_split.rb b/lib/puppet/parser/functions/shell_split.rb new file mode 100644 index 0000000..0446448 --- /dev/null +++ b/lib/puppet/parser/functions/shell_split.rb @@ -0,0 +1,26 @@ +# +# shell_split.rb +# + +require 'shellwords' + +module Puppet::Parser::Functions + newfunction(:shell_split, :type => :rvalue, :doc => <<-EOS +Splits a string into an array of tokens in the same way the Bourne shell does. + +This function behaves the same as ruby's Shellwords.shellsplit() function + EOS + ) do |arguments| + + raise(Puppet::ParseError, "shell_split(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size != 1 + + string = arguments[0].to_s + + result = Shellwords.shellsplit(string) + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/lib/puppet/parser/functions/size.rb b/lib/puppet/parser/functions/size.rb index cc207e3..0d6cc96 100644 --- a/lib/puppet/parser/functions/size.rb +++ b/lib/puppet/parser/functions/size.rb @@ -2,11 +2,9 @@ # size.rb # -# TODO(Krzysztof Wilczynski): Support for hashes would be nice too ... - module Puppet::Parser::Functions newfunction(:size, :type => :rvalue, :doc => <<-EOS -Returns the number of elements in a string or array. +Returns the number of elements in a string, an array or a hash EOS ) do |arguments| @@ -29,13 +27,13 @@ Returns the number of elements in a string or array. Float(item) raise(Puppet::ParseError, 'size(): Requires either ' + - 'string or array to work with') + 'string, array or hash to work with') rescue ArgumentError result = item.size end - elsif item.is_a?(Array) + elsif item.is_a?(Array) || item.is_a?(Hash) result = item.size else raise(Puppet::ParseError, 'size(): Unknown type given') diff --git a/lib/puppet/parser/functions/str2bool.rb b/lib/puppet/parser/functions/str2bool.rb index 446732e..472506d 100644 --- a/lib/puppet/parser/functions/str2bool.rb +++ b/lib/puppet/parser/functions/str2bool.rb @@ -5,8 +5,8 @@ module Puppet::Parser::Functions newfunction(:str2bool, :type => :rvalue, :doc => <<-EOS This converts a string to a boolean. This attempt to convert strings that -contain things like: y, 1, t, true to 'true' and strings that contain things -like: 0, f, n, false, no to 'false'. +contain things like: Y,y, 1, T,t, TRUE,true to 'true' and strings that contain things +like: 0, F,f, N,n, false, FALSE, no to 'false'. EOS ) do |arguments| @@ -21,7 +21,7 @@ like: 0, f, n, false, no to 'false'. end unless string.is_a?(String) - raise(Puppet::ParseError, 'str2bool(): Requires either ' + + raise(Puppet::ParseError, 'str2bool(): Requires ' + 'string to work with') end @@ -32,8 +32,8 @@ like: 0, f, n, false, no to 'false'. # 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 /^(1|t|y|true|yes)$/i then true + when /^(0|f|n|false|no)$/i then false when /^(undef|undefined)$/ then false # This is not likely to happen ... else raise(Puppet::ParseError, 'str2bool(): Unknown type of boolean given') diff --git a/lib/puppet/parser/functions/suffix.rb b/lib/puppet/parser/functions/suffix.rb index f7792d6..2908434 100644 --- a/lib/puppet/parser/functions/suffix.rb +++ b/lib/puppet/parser/functions/suffix.rb @@ -4,7 +4,8 @@ module Puppet::Parser::Functions newfunction(:suffix, :type => :rvalue, :doc => <<-EOS -This function applies a suffix to all elements in an array. +This function applies a suffix to all elements in an array, or to the keys +in a hash. *Examples:* @@ -18,10 +19,10 @@ Will return: ['ap','bp','cp'] raise(Puppet::ParseError, "suffix(): 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, "suffix(): expected first argument to be an Array, got #{array.inspect}" + unless enumerable.is_a?(Array) or enumerable.is_a?(Hash) + raise Puppet::ParseError, "suffix(): expected first argument to be an Array or a Hash, got #{enumerable.inspect}" end suffix = arguments[1] if arguments[1] @@ -32,10 +33,17 @@ Will return: ['ap','bp','cp'] end end - # Turn everything into string same as join would do ... - result = array.collect do |i| - i = i.to_s - suffix ? i + suffix : i + if enumerable.is_a?(Array) + # Turn everything into string same as join would do ... + result = enumerable.collect do |i| + i = i.to_s + suffix ? i + suffix : i + end + else + result = Hash[enumerable.map do |k,v| + k = k.to_s + [ suffix ? k + suffix : k, v ] + end] end return result diff --git a/lib/puppet/parser/functions/time.rb b/lib/puppet/parser/functions/time.rb index 0cddaf8..c574747 100644 --- a/lib/puppet/parser/functions/time.rb +++ b/lib/puppet/parser/functions/time.rb @@ -33,13 +33,14 @@ Will return something like: 1311972653 ENV['TZ'] = time_zone - time = local_time.localtime + result = local_time.localtime.strftime('%s') ENV['TZ'] = original_zone + else + result = time.localtime.strftime('%s') end # Calling Time#to_i on a receiver changes it. Trust me I am the Doctor. - result = time.strftime('%s') result = result.to_i return result diff --git a/lib/puppet/parser/functions/try_get_value.rb b/lib/puppet/parser/functions/try_get_value.rb new file mode 100644 index 0000000..fc19a23 --- /dev/null +++ b/lib/puppet/parser/functions/try_get_value.rb @@ -0,0 +1,53 @@ +module Puppet::Parser::Functions + newfunction( + :try_get_value, + :type => :rvalue, + :arity => -2, + :doc => <<-eos +DEPRECATED: this function is deprecated, please use dig() instead. + +Looks up into a complex structure of arrays and hashes and returns a value +or the default value if nothing was found. + +Key can contain slashes to describe path components. The function will go down +the structure and try to extract the required value. + +$data = { + 'a' => { + 'b' => [ + 'b1', + 'b2', + 'b3', + ] + } +} + +$value = try_get_value($data, 'a/b/2', 'not_found', '/') +=> $value = 'b3' + +a -> first hash key +b -> second hash key +2 -> array index starting with 0 + +not_found -> (optional) will be returned if there is no value or the path did not match. Defaults to nil. +/ -> (optional) path delimiter. Defaults to '/'. + +In addition to the required "key" argument, "try_get_value" accepts default +argument. It will be returned if no value was found or a path component is +missing. And the fourth argument can set a variable path separator. + eos + ) do |args| + warning("try_get_value() DEPRECATED: this function is deprecated, please use dig() instead.") + data = args[0] + path = args[1] || '' + default = args[2] + + if !(data.is_a?(Hash) || data.is_a?(Array)) || path == '' + return default || data + end + + separator = args[3] || '/' + path = path.split(separator).map{ |key| key =~ /^\d+$/ ? key.to_i : key } + function_dig([data, path, default]) + end +end diff --git a/lib/puppet/parser/functions/union.rb b/lib/puppet/parser/functions/union.rb index c91bb80..6c5bb83 100644 --- a/lib/puppet/parser/functions/union.rb +++ b/lib/puppet/parser/functions/union.rb @@ -4,7 +4,7 @@ module Puppet::Parser::Functions newfunction(:union, :type => :rvalue, :doc => <<-EOS -This function returns a union of two arrays. +This function returns a union of two or more arrays. *Examples:* @@ -14,20 +14,15 @@ Would return: ["a","b","c","d"] EOS ) do |arguments| - # Two arguments are required + # Check that 2 or more arguments have been given ... raise(Puppet::ParseError, "union(): Wrong number of arguments " + - "given (#{arguments.size} for 2)") if arguments.size != 2 + "given (#{arguments.size} for < 2)") if arguments.size < 2 - first = arguments[0] - second = arguments[1] - - unless first.is_a?(Array) && second.is_a?(Array) - raise(Puppet::ParseError, 'union(): Requires 2 arrays') + arguments.each do |argument| + raise(Puppet::ParseError, 'union(): Every parameter must be an array') unless argument.is_a?(Array) end - result = first | second - - return result + arguments.reduce(:|) end end diff --git a/lib/puppet/parser/functions/unix2dos.rb b/lib/puppet/parser/functions/unix2dos.rb new file mode 100644 index 0000000..0bd9cd1 --- /dev/null +++ b/lib/puppet/parser/functions/unix2dos.rb @@ -0,0 +1,15 @@ +# Custom Puppet function to convert unix to dos format +module Puppet::Parser::Functions + newfunction(:unix2dos, :type => :rvalue, :arity => 1, :doc => <<-EOS + Returns the DOS version of the given string. + Takes a single string argument. + EOS + ) do |arguments| + + unless arguments[0].is_a?(String) + raise(Puppet::ParseError, 'unix2dos(): Requires string as argument') + end + + arguments[0].gsub(/\r*\n/, "\r\n") + end +end diff --git a/lib/puppet/parser/functions/upcase.rb b/lib/puppet/parser/functions/upcase.rb index 4302b29..44b3bcd 100644 --- a/lib/puppet/parser/functions/upcase.rb +++ b/lib/puppet/parser/functions/upcase.rb @@ -12,23 +12,28 @@ Converts a string or an array of strings to uppercase. Will return: - ASDF - EOS + ABCD + 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] - unless value.is_a?(Array) || value.is_a?(String) - 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 a486eee..45bbed2 100644 --- a/lib/puppet/parser/functions/uriescape.rb +++ b/lib/puppet/parser/functions/uriescape.rb @@ -22,7 +22,7 @@ module Puppet::Parser::Functions 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 b696680..c73f3df 100644 --- a/lib/puppet/parser/functions/validate_absolute_path.rb +++ b/lib/puppet/parser/functions/validate_absolute_path.rb @@ -26,6 +26,9 @@ module Puppet::Parser::Functions ENDHEREDOC + # The deprecation function was being called twice, as validate_absolute_path calls is_absolute_path. I have removed it from here so it only calls deprecation once within is_absolute_path. + # function_deprecation([:validate_absolute_path, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Absolute_path. There is further documentation for validate_legacy function in the README.']) + require 'puppet/util' unless args.length > 0 then @@ -40,28 +43,10 @@ module Puppet::Parser::Functions unless arg.is_a?(Array) then candidates = Array.new(1,arg) end - # iterate over all pathes within the candidates array + # iterate over all paths 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.") + unless function_is_absolute_path([path]) + raise Puppet::ParseError, ("#{path.inspect} is not an absolute path.") end end end diff --git a/lib/puppet/parser/functions/validate_array.rb b/lib/puppet/parser/functions/validate_array.rb index 34b5118..3bf3983 100644 --- a/lib/puppet/parser/functions/validate_array.rb +++ b/lib/puppet/parser/functions/validate_array.rb @@ -18,6 +18,8 @@ module Puppet::Parser::Functions ENDHEREDOC + function_deprecation([:validate_array, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Array. There is further documentation for validate_legacy function in the README.']) + unless args.length > 0 then raise Puppet::ParseError, ("validate_array(): wrong number of arguments (#{args.length}; must be > 0)") end diff --git a/lib/puppet/parser/functions/validate_augeas.rb b/lib/puppet/parser/functions/validate_augeas.rb index 4ea4fe0..2196c3e 100644 --- a/lib/puppet/parser/functions/validate_augeas.rb +++ b/lib/puppet/parser/functions/validate_augeas.rb @@ -31,7 +31,7 @@ module Puppet::Parser::Functions ENDHEREDOC unless Puppet.features.augeas? - raise Puppet::ParseError, ("validate_augeas(): this function requires the augeas feature. See http://projects.puppetlabs.com/projects/puppet/wiki/Puppet_Augeas#Pre-requisites for how to activate it.") + raise Puppet::ParseError, ("validate_augeas(): this function requires the augeas feature. See http://docs.puppetlabs.com/guides/augeas.html#pre-requisites for how to activate it.") end if (args.length < 2) or (args.length > 4) then diff --git a/lib/puppet/parser/functions/validate_bool.rb b/lib/puppet/parser/functions/validate_bool.rb index 59a0805..49075b8 100644 --- a/lib/puppet/parser/functions/validate_bool.rb +++ b/lib/puppet/parser/functions/validate_bool.rb @@ -19,6 +19,9 @@ module Puppet::Parser::Functions ENDHEREDOC + # The deprecation function was being called twice, as validate_bool calls is_bool. I have removed it from here so it only calls deprecation once within is_bool. + # function_deprecation([:validate_bool, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Bool. There is further documentation for validate_legacy function in the README.']) + unless args.length > 0 then raise Puppet::ParseError, ("validate_bool(): wrong number of arguments (#{args.length}; must be > 0)") end diff --git a/lib/puppet/parser/functions/validate_cmd.rb b/lib/puppet/parser/functions/validate_cmd.rb index 5df3c60..685162b 100644 --- a/lib/puppet/parser/functions/validate_cmd.rb +++ b/lib/puppet/parser/functions/validate_cmd.rb @@ -53,7 +53,7 @@ module Puppet::Parser::Functions rescue Puppet::ExecutionFailure => detail msg += "\n#{detail}" raise Puppet::ParseError, msg - rescue Exception => detail + rescue StandardError => detail msg += "\n#{detail.class.name} #{detail}" raise Puppet::ParseError, msg ensure diff --git a/lib/puppet/parser/functions/validate_email_address.rb b/lib/puppet/parser/functions/validate_email_address.rb new file mode 100644 index 0000000..ddd0d25 --- /dev/null +++ b/lib/puppet/parser/functions/validate_email_address.rb @@ -0,0 +1,31 @@ +module Puppet::Parser::Functions + newfunction(:validate_email_address, :doc => <<-ENDHEREDOC + Validate that all values passed are valid email addresses. + Fail compilation if any value fails this check. + The following values will pass: + $my_email = "waldo@gmail.com" + validate_email_address($my_email) + validate_email_address("bob@gmail.com", "alice@gmail.com", $my_email) + + The following values will fail, causing compilation to abort: + $some_array = [ 'bad_email@/d/efdf.com' ] + validate_email_address($some_array) + ENDHEREDOC + ) do |args| + rescuable_exceptions = [ArgumentError] + + if args.empty? + raise Puppet::ParseError, "validate_email_address(): wrong number of arguments (#{args.length}; must be > 0)" + end + + args.each do |arg| + raise Puppet::ParseError, "#{arg.inspect} is not a string." unless arg.is_a?(String) + + begin + raise Puppet::ParseError, "#{arg.inspect} is not a valid email address" unless function_is_email_address([arg]) + rescue *rescuable_exceptions + raise Puppet::ParseError, "#{arg.inspect} is not a valid email address" + end + end + end +end diff --git a/lib/puppet/parser/functions/validate_hash.rb b/lib/puppet/parser/functions/validate_hash.rb index 9bdd543..fcdc7e1 100644 --- a/lib/puppet/parser/functions/validate_hash.rb +++ b/lib/puppet/parser/functions/validate_hash.rb @@ -18,6 +18,8 @@ module Puppet::Parser::Functions ENDHEREDOC + function_deprecation([:validate_hash, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Hash. There is further documentation for validate_legacy function in the README.']) + unless args.length > 0 then raise Puppet::ParseError, ("validate_hash(): wrong number of arguments (#{args.length}; must be > 0)") 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..2ae0293 --- /dev/null +++ b/lib/puppet/parser/functions/validate_integer.rb @@ -0,0 +1,134 @@ +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 items in arrays or maximum / minimum argument. + + ENDHEREDOC + + function_deprecation([:validate_integer, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Integer. There is further documentation for validate_legacy function in the README.']) + + # 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 + raise TypeError if arg.is_a?(Hash) + 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_ip_address.rb b/lib/puppet/parser/functions/validate_ip_address.rb new file mode 100644 index 0000000..5d80cfb --- /dev/null +++ b/lib/puppet/parser/functions/validate_ip_address.rb @@ -0,0 +1,52 @@ +module Puppet::Parser::Functions + + newfunction(:validate_ip_address, :doc => <<-ENDHEREDOC + Validate that all values passed are valid IP addresses, + regardless they are IPv4 or IPv6 + Fail compilation if any value fails this check. + The following values will pass: + $my_ip = "1.2.3.4" + validate_ip_address($my_ip) + validate_ip_address("8.8.8.8", "172.16.0.1", $my_ip) + + $my_ip = "3ffe:505:2" + validate_ip_address(1) + validate_ip_address($my_ip) + validate_ip_address("fe80::baf6:b1ff:fe19:7507", $my_ip) + + The following values will fail, causing compilation to abort: + $some_array = [ 1, true, false, "garbage string", "3ffe:505:2" ] + validate_ip_address($some_array) + ENDHEREDOC + ) do |args| + + require "ipaddr" + rescuable_exceptions = [ ArgumentError ] + + function_deprecation([:validate_ip_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ip_address. There is further documentation for validate_legacy function in the README.']) + + if defined?(IPAddr::InvalidAddressError) + rescuable_exceptions << IPAddr::InvalidAddressError + end + + unless args.length > 0 then + raise Puppet::ParseError, ("validate_ip_address(): wrong number of arguments (#{args.length}; must be > 0)") + end + + args.each do |arg| + unless arg.is_a?(String) + raise Puppet::ParseError, "#{arg.inspect} is not a string." + end + + begin + unless IPAddr.new(arg).ipv4? or IPAddr.new(arg).ipv6? + raise Puppet::ParseError, "#{arg.inspect} is not a valid IP address." + end + rescue *rescuable_exceptions + raise Puppet::ParseError, "#{arg.inspect} is not a valid IP address." + end + end + + end + +end diff --git a/lib/puppet/parser/functions/validate_ipv4_address.rb b/lib/puppet/parser/functions/validate_ipv4_address.rb index fc02748..0660abd 100644 --- a/lib/puppet/parser/functions/validate_ipv4_address.rb +++ b/lib/puppet/parser/functions/validate_ipv4_address.rb @@ -8,7 +8,7 @@ module Puppet::Parser::Functions $my_ip = "1.2.3.4" validate_ipv4_address($my_ip) - validate_bool("8.8.8.8", "172.16.0.1", $my_ip) + validate_ipv4_address("8.8.8.8", "172.16.0.1", $my_ip) The following values will fail, causing compilation to abort: @@ -18,6 +18,8 @@ module Puppet::Parser::Functions ENDHEREDOC ) do |args| + function_deprecation([:validate_ipv4_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv4. There is further documentation for validate_legacy function in the README.']) + require "ipaddr" rescuable_exceptions = [ ArgumentError ] diff --git a/lib/puppet/parser/functions/validate_ipv6_address.rb b/lib/puppet/parser/functions/validate_ipv6_address.rb index b0f2558..f5dd9e5 100644 --- a/lib/puppet/parser/functions/validate_ipv6_address.rb +++ b/lib/puppet/parser/functions/validate_ipv6_address.rb @@ -19,6 +19,8 @@ module Puppet::Parser::Functions ENDHEREDOC ) do |args| + function_deprecation([:validate_ipv6_address, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Ipv6. There is further documentation for validate_legacy function in the README.']) + require "ipaddr" rescuable_exceptions = [ ArgumentError ] diff --git a/lib/puppet/parser/functions/validate_numeric.rb b/lib/puppet/parser/functions/validate_numeric.rb new file mode 100644 index 0000000..4205b30 --- /dev/null +++ b/lib/puppet/parser/functions/validate_numeric.rb @@ -0,0 +1,96 @@ +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 + + function_deprecation([:validate_numeric, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Numeric. There is further documentation for validate_legacy function in the README.']) + + # 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 + raise TypeError if arg.is_a?(Hash) + 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_re.rb b/lib/puppet/parser/functions/validate_re.rb index ca25a70..0ac83dd 100644 --- a/lib/puppet/parser/functions/validate_re.rb +++ b/lib/puppet/parser/functions/validate_re.rb @@ -23,16 +23,26 @@ module Puppet::Parser::Functions validate_re($::puppetversion, '^2.7', 'The $puppetversion fact value does not match 2.7') + Note: Compilation will also abort, if the first argument is not a String. Always use + quotes to force stringification: + + validate_re("${::operatingsystemmajrelease}", '^[57]$') + ENDHEREDOC + + function_deprecation([:validate_re, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::Re. There is further documentation for validate_legacy function in the README.']) + 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)") + raise Puppet::ParseError, "validate_re(): wrong number of arguments (#{args.length}; must be 2 or 3)" end + raise Puppet::ParseError, "validate_re(): input needs to be a String, not a #{args[0].class}" unless args[0].is_a? String + 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| + raise Puppet::ParseError, msg unless [args[1]].flatten.any? do |re_str| args[0] =~ Regexp.compile(re_str) end diff --git a/lib/puppet/parser/functions/validate_slength.rb b/lib/puppet/parser/functions/validate_slength.rb index 7d534f3..383855c 100644 --- a/lib/puppet/parser/functions/validate_slength.rb +++ b/lib/puppet/parser/functions/validate_slength.rb @@ -3,7 +3,7 @@ module Puppet::Parser::Functions newfunction(:validate_slength, :doc => <<-'ENDHEREDOC') do |args| Validate that the first argument is a string (or an array of strings), and less/equal to than the length of the second argument. An optional third - parameter can be given a the minimum length. It fails if the first + parameter can be given the minimum length. It fails if the first argument is not a string or array of strings, and if arg 2 and arg 3 are not convertable to a number. @@ -21,6 +21,8 @@ module Puppet::Parser::Functions ENDHEREDOC + function_deprecation([:validate_slength, 'This method is deprecated, please use the stdlib validate_legacy function, with String[]. There is further documentation for validate_legacy function in the README.']) + raise Puppet::ParseError, "validate_slength(): Wrong number of arguments (#{args.length}; must be 2 or 3)" unless args.length == 2 or args.length == 3 input, max_length, min_length = *args @@ -43,9 +45,7 @@ module Puppet::Parser::Functions min_length = 0 end - if min_length > max_length - raise Puppet::ParseError, "validate_slength(): Expected second argument to be larger than third argument" - end + raise Puppet::ParseError, "validate_slength(): Expected second argument to be equal to or larger than third argument" unless max_length >= min_length validator = lambda do |str| unless str.length <= max_length and str.length >= min_length diff --git a/lib/puppet/parser/functions/validate_string.rb b/lib/puppet/parser/functions/validate_string.rb index c841f6a..6675d86 100644 --- a/lib/puppet/parser/functions/validate_string.rb +++ b/lib/puppet/parser/functions/validate_string.rb @@ -13,22 +13,25 @@ module Puppet::Parser::Functions validate_string(true) validate_string([ 'some', 'array' ]) - + 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 + function_deprecation([:validate_string, 'This method is deprecated, please use the stdlib validate_legacy function, with Stdlib::Compat::String. There is further documentation for validate_legacy function in the README.']) + unless args.length > 0 then raise Puppet::ParseError, ("validate_string(): wrong number of arguments (#{args.length}; must be > 0)") end args.each do |arg| - unless arg.is_a?(String) + # when called through the v4 API shim, undef gets translated to nil + unless arg.is_a?(String) || arg.nil? raise Puppet::ParseError, ("#{arg.inspect} is not a string. It looks to be a #{arg.class}") end end diff --git a/lib/puppet/parser/functions/validate_x509_rsa_key_pair.rb b/lib/puppet/parser/functions/validate_x509_rsa_key_pair.rb new file mode 100644 index 0000000..fc9f23f --- /dev/null +++ b/lib/puppet/parser/functions/validate_x509_rsa_key_pair.rb @@ -0,0 +1,47 @@ +module Puppet::Parser::Functions + + newfunction(:validate_x509_rsa_key_pair, :doc => <<-ENDHEREDOC + Validates a PEM-formatted X.509 certificate and RSA private key using + OpenSSL. Verifies that the certficate's signature was created from the + supplied key. + + Fail compilation if any value fails this check. + + validate_x509_rsa_key_pair($cert, $key) + + ENDHEREDOC + ) do |args| + + require 'openssl' + + NUM_ARGS = 2 unless defined? NUM_ARGS + + unless args.length == NUM_ARGS then + raise Puppet::ParseError, + ("validate_x509_rsa_key_pair(): wrong number of arguments (#{args.length}; must be #{NUM_ARGS})") + end + + args.each do |arg| + unless arg.is_a?(String) + raise Puppet::ParseError, "#{arg.inspect} is not a string." + end + end + + begin + cert = OpenSSL::X509::Certificate.new(args[0]) + rescue OpenSSL::X509::CertificateError => e + raise Puppet::ParseError, "Not a valid x509 certificate: #{e}" + end + + begin + key = OpenSSL::PKey::RSA.new(args[1]) + rescue OpenSSL::PKey::RSAError => e + raise Puppet::ParseError, "Not a valid RSA key: #{e}" + end + + unless cert.verify(key) + raise Puppet::ParseError, "Certificate signature does not match supplied key" + end + end + +end diff --git a/lib/puppet/provider/file_line/ruby.rb b/lib/puppet/provider/file_line/ruby.rb index ae1a8b3..beeb430 100644 --- a/lib/puppet/provider/file_line/ruby.rb +++ b/lib/puppet/provider/file_line/ruby.rb @@ -1,24 +1,35 @@ Puppet::Type.type(:file_line).provide(:ruby) do def exists? - lines.find do |line| - line.chomp == resource[:line].chomp + if resource[:replace].to_s != 'true' and count_matches(match_regex) > 0 + true + else + lines.find do |line| + if resource[:ensure].to_s == 'absent' and resource[:match_for_absence].to_s == 'true' + line.chomp =~ Regexp.new(resource[:match]) + else + line.chomp == resource[:line].chomp + end + end end end def create - if resource[:match] - handle_create_with_match - elsif resource[:after] - handle_create_with_after - else - append_line + unless resource[:replace].to_s != 'true' and count_matches(match_regex) > 0 + if resource[:match] + handle_create_with_match + elsif resource[:after] + handle_create_with_after + else + append_line + end end end def destroy - local_lines = lines - File.open(resource[:path],'w') do |fh| - fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join('')) + if resource[:match_for_absence].to_s == 'true' and resource[:match] + handle_destroy_with_match + else + handle_destroy_line end end @@ -32,15 +43,27 @@ Puppet::Type.type(:file_line).provide(:ruby) do @lines ||= File.readlines(resource[:path]) end + def match_regex + resource[:match] ? Regexp.new(resource[:match]) : nil + end + def handle_create_with_match() - regex = resource[:match] ? Regexp.new(resource[:match]) : nil - match_count = count_matches(regex) + regex_after = resource[:after] ? Regexp.new(resource[:after]) : nil + match_count = count_matches(match_regex) + if match_count > 1 && resource[:multiple].to_s != 'true' raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" end + File.open(resource[:path], 'w') do |fh| lines.each do |l| - fh.puts(regex.match(l) ? resource[:line] : l) + fh.puts(match_regex.match(l) ? resource[:line] : l) + if (match_count == 0 and regex_after) + if regex_after.match(l) + fh.puts(resource[:line]) + match_count += 1 #Increment match_count to indicate that the new line has been inserted. + end + end end if (match_count == 0) @@ -52,20 +75,22 @@ Puppet::Type.type(:file_line).provide(:ruby) do def handle_create_with_after regex = Regexp.new(resource[:after]) count = count_matches(regex) - case count - when 1 # find the line to put our line after - File.open(resource[:path], 'w') do |fh| - lines.each do |l| - fh.puts(l) - if regex.match(l) then - fh.puts(resource[:line]) - end + + if count > 1 && resource[:multiple].to_s != 'true' + raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern." + end + + File.open(resource[:path], 'w') do |fh| + lines.each do |l| + fh.puts(l) + if regex.match(l) then + fh.puts(resource[:line]) end end - when 0 # append the line to the end of the file + end + + if (count == 0) # append the line to the end of the file append_line - else - raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern." end end @@ -73,12 +98,34 @@ Puppet::Type.type(:file_line).provide(:ruby) do lines.select{|l| l.match(regex)}.size end + def handle_destroy_with_match + match_count = count_matches(match_regex) + if match_count > 1 && resource[:multiple].to_s != 'true' + raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" + end + + local_lines = lines + File.open(resource[:path],'w') do |fh| + fh.write(local_lines.reject{|l| match_regex.match(l) }.join('')) + end + end + + def handle_destroy_line + local_lines = lines + File.open(resource[:path],'w') do |fh| + fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join('')) + end + end + ## # append the line to the file. # # @api private def append_line - File.open(resource[:path], 'a') do |fh| + File.open(resource[:path], 'w') do |fh| + lines.each do |l| + fh.puts(l) + end fh.puts resource[:line] end end diff --git a/lib/puppet/type/file_line.rb b/lib/puppet/type/file_line.rb index df263e6..7b7d44e 100644 --- a/lib/puppet/type/file_line.rb +++ b/lib/puppet/type/file_line.rb @@ -3,9 +3,9 @@ Puppet::Type.newtype(:file_line) do desc <<-EOT Ensures that a given line is contained within a file. The implementation matches the full line, including whitespace at the beginning and end. If - the line is not contained in the given file, Puppet will add the line to - ensure the desired state. Multiple resources may be declared to manage - multiple lines in the same file. + the line is not contained in the given file, Puppet will append the line to + the end of the file to ensure the desired state. Multiple resources may + be declared to manage multiple lines in the same file. Example: @@ -13,6 +13,7 @@ Puppet::Type.newtype(:file_line) do path => '/etc/sudoers', line => '%sudo ALL=(ALL) ALL', } + file_line { 'sudo_rule_nopw': path => '/etc/sudoers', line => '%sudonopw ALL=(ALL) NOPASSWD: ALL', @@ -21,9 +22,34 @@ Puppet::Type.newtype(:file_line) do In this example, Puppet will ensure both of the specified lines are contained in the file /etc/sudoers. + Match Example: + + file_line { 'bashrc_proxy': + ensure => present, + path => '/etc/bashrc', + line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', + match => '^export\ HTTP_PROXY\=', + } + + In this code example match will look for a line beginning with export + followed by HTTP_PROXY and replace it with the value in line. + + Match Example With `ensure => absent`: + + file_line { 'bashrc_proxy': + ensure => absent, + path => '/etc/bashrc', + line => 'export HTTP_PROXY=http://squid.puppetlabs.vm:3128', + match => '^export\ HTTP_PROXY\=', + match_for_absence => true, + } + + In this code example match will look for a line beginning with export + followed by HTTP_PROXY and delete it. If multiple lines match, an + error will be raised unless the `multiple => true` parameter is set. + **Autorequires:** If Puppet is managing the file that will contain the line being managed, the file_line resource will autorequire that file. - EOT ensurable do @@ -36,40 +62,64 @@ Puppet::Type.newtype(:file_line) do end newparam(:match) do - desc 'An optional regular expression to run against existing lines in the file;\n' + - 'if a match is found, we replace that line rather than adding a new line.' + desc 'An optional ruby regular expression to run against existing lines in the file.' + + ' If a match is found, we replace that line rather than adding a new line.' + + ' A regex comparison is performed against the line value and if it does not' + + ' match an exception will be raised.' + end + + newparam(:match_for_absence) do + desc 'An optional value to determine if match should be applied when ensure => absent.' + + ' If set to true and match is set, the line that matches match will be deleted.' + + ' If set to false (the default), match is ignored when ensure => absent.' + + ' When `ensure => present`, match_for_absence is ignored.' + newvalues(true, false) + defaultto false end newparam(:multiple) do - desc 'An optional value to determine if match can change multiple lines.' + desc 'An optional value to determine if match can change multiple lines.' + + ' If set to false, an exception will be raised if more than one line matches' newvalues(true, false) end newparam(:after) do - desc 'An optional value used to specify the line after which we will add any new lines. (Existing lines are added in place)' + desc 'An optional value used to specify the line after which we will add any new lines. (Existing lines are added in place)' + + ' This is also takes a regex.' end newparam(:line) do - desc 'The line to be appended to the file located by the path parameter.' + desc 'The line to be appended to the file or used to replace matches found by the match attribute.' end newparam(:path) do desc 'The file Puppet will ensure contains the line specified by the line parameter.' validate do |value| - unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) - raise(Puppet::Error, "File paths must be fully qualified, not '#{value}'") + unless Puppet::Util.absolute_path?(value) + raise Puppet::Error, "File paths must be fully qualified, not '#{value}'" end end end + newparam(:replace) do + desc 'If true, replace line that matches. If false, do not write line if a match is found' + newvalues(true, false) + defaultto true + end + # Autorequire the file resource if it's being managed autorequire(:file) do self[:path] end validate do - unless self[:line] and self[:path] - raise(Puppet::Error, "Both line and path are required attributes") + unless self[:line] + unless (self[:ensure].to_s == 'absent') and (self[:match_for_absence].to_s == 'true') and self[:match] + raise(Puppet::Error, "line is a required attribute") + end + end + unless self[:path] + raise(Puppet::Error, "path is a required attribute") end end end |