diff --git a/REFERENCE.md b/REFERENCE.md index 63169ab67..c42592d92 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -47,10 +47,8 @@ from an array or key from a hash. * [`dos2unix`](#dos2unix): Returns the Unix version of the given string. * [`enclose_ipv6`](#enclose_ipv6): Takes an array of ip addresses and encloses the ipv6 addresses with square brackets. * [`ensure_packages`](#ensure_packages): DEPRECATED. Use the namespaced function [`stdlib::ensure_packages`](#stdlibensure_packages) instead. -* [`ensure_resource`](#ensure_resource): Takes a resource type, title, and a list of attributes that describe a -resource. -* [`ensure_resources`](#ensure_resources): Takes a resource type, title (only hash), and a list of attributes that describe a -resource. +* [`ensure_resource`](#ensure_resource): DEPRECATED. Use the namespaced function [`stdlib::ensure_resource`](#stdlibensure_resource) instead. +* [`ensure_resources`](#ensure_resources): DEPRECATED. Use the namespaced function [`stdlib::ensure_resources`](#stdlibensure_resources) instead. * [`fact`](#fact): Digs into the facts hash using dot-notation * [`fqdn_rand_string`](#fqdn_rand_string): DEPRECATED. Use the namespaced function [`stdlib::fqdn_rand_string`](#stdlibfqdn_rand_string) instead. * [`fqdn_rotate`](#fqdn_rotate): DEPRECATED. Use the namespaced function [`stdlib::fqdn_rotate`](#stdlibfqdn_rotate) instead. @@ -116,9 +114,12 @@ the provided regular expression. * [`stdlib::batch_escape`](#stdlib--batch_escape): Escapes a string so that it can be safely used in a batch shell command line. * [`stdlib::crc32`](#stdlib--crc32): Run a CRC32 calculation against a given value. * [`stdlib::deferrable_epp`](#stdlib--deferrable_epp): This function returns either a rendered template or a deferred function to render at runtime. If any of the values in the variables hash are +* [`stdlib::defined_with_params`](#stdlib--defined_with_params): @summary Takes a resource reference and an optional hash of attributes. Returns `true` if a resource with the specified attributes has a * [`stdlib::end_with`](#stdlib--end_with): Returns true if str ends with one of the prefixes given. Each of the prefixes should be a String. * [`stdlib::ensure`](#stdlib--ensure): function to cast ensure parameter to resource specific value * [`stdlib::ensure_packages`](#stdlib--ensure_packages): Takes a list of packages and only installs them if they don't already exist. +* [`stdlib::ensure_resource`](#stdlib--ensure_resource): @summary Takes a resource type, title, and a list of attributes that describe a resource. user { 'dan': ensure => present, } @r +* [`stdlib::ensure_resources`](#stdlib--ensure_resources): @summary Takes a resource type, title (only hash), and a list of attributes that describe a resource. @return created resources wi * [`stdlib::extname`](#stdlib--extname): Returns the Extension (the Portion of Filename in Path starting from the last Period). * [`stdlib::fqdn_rand_string`](#stdlib--fqdn_rand_string): Generates a random alphanumeric string. Combining the `$fqdn` fact and an @@ -1604,125 +1605,39 @@ Data type: `Any` ### `ensure_resource` -Type: Ruby 3.x API - -user { 'dan': - ensure => present, -} - -#### Examples - -##### Example usage - -```puppet - -Creates the resource if it does not already exist: - - ensure_resource('user', 'dan', {'ensure' => 'present' }) - -If the resource already exists but does not match the specified parameters, -this function will attempt to recreate the resource leading to a duplicate -resource definition error. - -An array of resources can also be passed in and each will be created with -the type and parameters specified if it doesn't already exist. - - ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) -``` - -#### `ensure_resource()` - -user { 'dan': - ensure => present, -} +Type: Ruby 4.x API -Returns: `Any` created or recreated the passed resource with the passed type and attributes +DEPRECATED. Use the namespaced function [`stdlib::ensure_resource`](#stdlibensure_resource) instead. -##### Examples +#### `ensure_resource(Any *$args)` -###### Example usage - -```puppet +The ensure_resource function. -Creates the resource if it does not already exist: +Returns: `Any` - ensure_resource('user', 'dan', {'ensure' => 'present' }) +##### `*args` -If the resource already exists but does not match the specified parameters, -this function will attempt to recreate the resource leading to a duplicate -resource definition error. +Data type: `Any` -An array of resources can also be passed in and each will be created with -the type and parameters specified if it doesn't already exist. - ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) -``` ### `ensure_resources` -Type: Ruby 3.x API - -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'}) - -#### Examples - -##### Example usage - -```puppet - -user { 'dan': - gid => 'mygroup', - ensure => present, -} -``` - -#### `ensure_resources()` - -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'}) +Type: Ruby 4.x API - From Hiera Backend: +DEPRECATED. Use the namespaced function [`stdlib::ensure_resources`](#stdlibensure_resources) instead. - userlist: - dan: - gid: 'mygroup' - uid: '600' - alex: - gid: 'mygroup' +#### `ensure_resources(Any *$args)` - Call: - ensure_resources('user', hiera_hash('userlist'), {'ensure' => 'present'}) +The ensure_resources function. -Returns: `Any` created resources with the passed type and attributes +Returns: `Any` -##### Examples +##### `*args` -###### Example usage +Data type: `Any` -```puppet -user { 'dan': - gid => 'mygroup', - ensure => present, -} -``` ### `fact` @@ -3200,6 +3115,64 @@ Data type: `Hash` +### `stdlib::defined_with_params` + +Type: Ruby 4.x API + +@summary + Takes a resource reference and an optional hash of attributes. + + Returns `true` if a resource with the specified attributes has already been added + to the catalog, and `false` otherwise. + + ``` + user { 'dan': + ensure => present, + } + + if ! stdlib::defined_with_params(User[dan], {'ensure' => 'present' }) { + user { 'dan': ensure => present, } + } + ``` + + @return [Boolean] + returns `true` or `false` + +#### `stdlib::defined_with_params(Variant[String,Type[Resource]] $reference, Variant[String[0],Hash] $params)` + +@summary + Takes a resource reference and an optional hash of attributes. + + Returns `true` if a resource with the specified attributes has already been added + to the catalog, and `false` otherwise. + + ``` + user { 'dan': + ensure => present, + } + + if ! stdlib::defined_with_params(User[dan], {'ensure' => 'present' }) { + user { 'dan': ensure => present, } + } + ``` + + @return [Boolean] + returns `true` or `false` + +Returns: `Boolean` Returns `true` if a resource has already been added + +##### `reference` + +Data type: `Variant[String,Type[Resource]]` + +The resource reference to check for + +##### `params` + +Data type: `Variant[String[0],Hash]` + +The resource's attributes + ### `stdlib::end_with` Type: Ruby 4.x API @@ -3273,7 +3246,7 @@ Data type: `Optional[Enum['directory', 'link', 'mounted', 'service', 'file', 'pa Type: Ruby 4.x API It optionally takes a hash as a second parameter that will be passed as the -third argument to the ensure_resource() function. +third argument to the stdlib::ensure_resource() function. #### `stdlib::ensure_packages(Variant[String[1], Array[String[1]]] $packages, Optional[Hash] $default_attributes)` @@ -3291,7 +3264,7 @@ The packages to ensure are installed. Data type: `Optional[Hash]` -Default attributes to be passed to the `ensure_resource()` function +Default attributes to be passed to the `stdlib::ensure_resource()` function #### `stdlib::ensure_packages(Hash[String[1], Any] $packages, Optional[Hash] $default_attributes)` @@ -3311,6 +3284,172 @@ Data type: `Optional[Hash]` Default attributes. Package specific attributes from the `packages` parameter will take precedence. +### `stdlib::ensure_resource` + +Type: Ruby 4.x API + +@summary + Takes a resource type, title, and a list of attributes that describe a + resource. + + user { 'dan': + ensure => present, + } + + @return + created or recreated the passed resource with the passed type and attributes + + @example Example usage + + Creates the resource if it does not already exist: + + stdlib::ensure_resource('user', 'dan', {'ensure' => 'present' }) + + If the resource already exists but does not match the specified parameters, + this function will attempt to recreate the resource leading to a duplicate + resource definition error. + + An array of resources can also be passed in and each will be created with + the type and parameters specified if it doesn't already exist. + + ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) + +#### `stdlib::ensure_resource(String $type, Variant[String,Array[String]] $title, Hash $params)` + +@summary + Takes a resource type, title, and a list of attributes that describe a + resource. + + user { 'dan': + ensure => present, + } + + @return + created or recreated the passed resource with the passed type and attributes + + @example Example usage + + Creates the resource if it does not already exist: + + stdlib::ensure_resource('user', 'dan', {'ensure' => 'present' }) + + If the resource already exists but does not match the specified parameters, + this function will attempt to recreate the resource leading to a duplicate + resource definition error. + + An array of resources can also be passed in and each will be created with + the type and parameters specified if it doesn't already exist. + + ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) + +Returns: `Any` + +##### `type` + +Data type: `String` + +The resource type to create + +##### `title` + +Data type: `Variant[String,Array[String]]` + +The resource title or array of resource titles + +##### `params` + +Data type: `Hash` + +The resource parameters + +### `stdlib::ensure_resources` + +Type: Ruby 4.x API + +@summary + Takes a resource type, title (only hash), and a list of attributes that describe a + resource. + + @return + created resources with the passed type and attributes + + @example Example usage + + 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. + + stdlib::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'}) + +#### `stdlib::ensure_resources(String $type, Hash[String,Hash] $titles, Optional[Hash] $params)` + +@summary + Takes a resource type, title (only hash), and a list of attributes that describe a + resource. + + @return + created resources with the passed type and attributes + + @example Example usage + + 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. + + stdlib::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'}) + +Returns: `Any` + +##### `type` + +Data type: `String` + +The resource type to create + +##### `titles` + +Data type: `Hash[String,Hash]` + +A hash of resource titles mapping to resource parameters + +##### `params` + +Data type: `Optional[Hash]` + +A hash of default parameters to be merged with individual resource parameters + ### `stdlib::extname` Type: Ruby 4.x API diff --git a/lib/puppet/functions/ensure_resource.rb b/lib/puppet/functions/ensure_resource.rb new file mode 100644 index 000000000..94f4976ef --- /dev/null +++ b/lib/puppet/functions/ensure_resource.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# @summary DEPRECATED. Use the namespaced function [`stdlib::ensure_resource`](#stdlibensure_resource) instead. +Puppet::Functions.create_function(:ensure_resource) do + dispatch :deprecation_gen do + repeated_param 'Any', :args + end + def deprecation_gen(*args) + call_function('deprecation', 'ensure_resource', 'This function is deprecated, please use stdlib::ensure_resource instead.', false) + call_function('stdlib::ensure_resource', *args) + end +end diff --git a/lib/puppet/functions/ensure_resources.rb b/lib/puppet/functions/ensure_resources.rb new file mode 100644 index 000000000..808c555a9 --- /dev/null +++ b/lib/puppet/functions/ensure_resources.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# @summary DEPRECATED. Use the namespaced function [`stdlib::ensure_resources`](#stdlibensure_resources) instead. +Puppet::Functions.create_function(:ensure_resources) do + dispatch :deprecation_gen do + repeated_param 'Any', :args + end + def deprecation_gen(*args) + call_function('deprecation', 'ensure_resources', 'This function is deprecated, please use stdlib::ensure_resources instead.', false) + call_function('stdlib::ensure_resources', *args) + end +end diff --git a/lib/puppet/functions/stdlib/defined_with_params.rb b/lib/puppet/functions/stdlib/defined_with_params.rb new file mode 100644 index 000000000..a42817ea4 --- /dev/null +++ b/lib/puppet/functions/stdlib/defined_with_params.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# @summary +# Takes a resource reference and an optional hash of attributes. +# +# Returns `true` if a resource with the specified attributes has already been added +# to the catalog, and `false` otherwise. +# +# ``` +# user { 'dan': +# ensure => present, +# } +# +# if ! stdlib::defined_with_params(User[dan], {'ensure' => 'present' }) { +# user { 'dan': ensure => present, } +# } +# ``` +# +# @return [Boolean] +# returns `true` or `false` +Puppet::Functions.create_function(:'stdlib::defined_with_params', Puppet::Functions::InternalFunction) do + # @return [Boolean] + # Returns `true` if a resource has already been added + # + # @param reference + # The resource reference to check for + # @param params + # The resource's attributes + dispatch :defined_with_params do + scope_param + param 'Variant[String,Type[Resource]]', :reference + param 'Variant[String[0],Hash]', :params + end + def defined_with_params(scope, reference, params) + params = {} if params == '' + ret = false + + if Puppet::Util::Package.versioncmp(Puppet.version, '4.6.0') >= 0 + # Workaround for PE-20308 + if reference.is_a?(String) + type_name, title = Puppet::Resource.type_and_title(reference, nil) + type = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type_or_class(scope, type_name.downcase) + elsif reference.is_a?(Puppet::Resource) + type = reference.type + title = reference.title + elsif reference.is_a?(Puppet::Pops::Types::PResourceType) + type = reference.type_name + title = reference.title + else + raise(ArgumentError, "Reference is not understood: '#{reference.class}'") + end + # end workaround + else + type = reference.to_s + title = nil + end + + resources = if title.nil? || title.empty? + scope.catalog.resources.select { |r| r.type == type } + else + [scope.findresource(type, title)] + end + + resources.compact.each do |res| + # If you call this from within a defined type, it will find itself + Puppet.debug res.to_s, scope.resource.to_s, scope.resource.inspect + next if res.to_s == scope.resource.to_s + + matches = params.map do |key, value| + # eql? avoids bugs caused by monkeypatching in puppet + res_is_undef = res[key].eql?(:undef) || res[key].nil? + value_is_undef = value.eql?(:undef) || value.nil? + found_match = (res_is_undef && value_is_undef) || (res[key] == value) + + Puppet.debug("Matching resource is #{res}") if found_match + + found_match + end + ret = params.empty? || !matches.include?(false) + + break if ret + end + + Puppet.debug("Resource #{reference} was not determined to be defined") unless ret + + ret + end +end diff --git a/lib/puppet/functions/stdlib/ensure_packages.rb b/lib/puppet/functions/stdlib/ensure_packages.rb index 288100f06..d890221cf 100644 --- a/lib/puppet/functions/stdlib/ensure_packages.rb +++ b/lib/puppet/functions/stdlib/ensure_packages.rb @@ -3,12 +3,12 @@ # @summary Takes a list of packages and only installs them if they don't already exist. # # It optionally takes a hash as a second parameter that will be passed as the -# third argument to the ensure_resource() function. +# third argument to the stdlib::ensure_resource() function. Puppet::Functions.create_function(:'stdlib::ensure_packages', Puppet::Functions::InternalFunction) do # @param packages # The packages to ensure are installed. # @param default_attributes - # Default attributes to be passed to the `ensure_resource()` function + # Default attributes to be passed to the `stdlib::ensure_resource()` function # @return [Undef] Returns nothing. dispatch :ensure_packages do scope_param @@ -37,7 +37,7 @@ def ensure_packages(scope, packages, default_attributes = {}) # with `installed` by default but `present` if this package is already in the catalog with `ensure => present` defaults['ensure'] = default_ensure(package_name) if ['present', 'installed'].include?(defaults['ensure']) - scope.call_function('ensure_resource', ['package', package_name, defaults]) + scope.call_function('stdlib::ensure_resource', ['package', package_name, defaults]) end nil end diff --git a/lib/puppet/functions/stdlib/ensure_resource.rb b/lib/puppet/functions/stdlib/ensure_resource.rb new file mode 100644 index 000000000..6bdfc78a7 --- /dev/null +++ b/lib/puppet/functions/stdlib/ensure_resource.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# @summary +# Takes a resource type, title, and a list of attributes that describe a +# resource. +# +# user { 'dan': +# ensure => present, +# } +# +# @return +# created or recreated the passed resource with the passed type and attributes +# +# @example Example usage +# +# Creates the resource if it does not already exist: +# +# stdlib::ensure_resource('user', 'dan', {'ensure' => 'present' }) +# +# If the resource already exists but does not match the specified parameters, +# this function will attempt to recreate the resource leading to a duplicate +# resource definition error. +# +# An array of resources can also be passed in and each will be created with +# the type and parameters specified if it doesn't already exist. +# +# ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) +Puppet::Functions.create_function(:'stdlib::ensure_resource') do + # @param type + # The resource type to create + # @param title + # The resource title or array of resource titles + # @param params + # The resource parameters + dispatch :ensure_resource do + param 'String', :type + param 'Variant[String,Array[String]]', :title + param 'Hash', :params + end + def ensure_resource(type, title, params) + items = [title].flatten + + items.each do |item| + if call_function('stdlib::defined_with_params', "#{type}[#{item}]", params) + Puppet.debug("Resource #{type}[#{item}] with params #{params} not created because it already exists") + else + Puppet.debug("Create new resource #{type}[#{item}] with params #{params}") + call_function('create_resources', type.capitalize, { item => params }) + end + end + end +end diff --git a/lib/puppet/functions/stdlib/ensure_resources.rb b/lib/puppet/functions/stdlib/ensure_resources.rb new file mode 100644 index 000000000..82a17c184 --- /dev/null +++ b/lib/puppet/functions/stdlib/ensure_resources.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# @summary +# Takes a resource type, title (only hash), and a list of attributes that describe a +# resource. +# +# @return +# created resources with the passed type and attributes +# +# @example Example usage +# +# 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. +# +# stdlib::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'}) +Puppet::Functions.create_function(:'stdlib::ensure_resources') do + # @param type + # The resource type to create + # @param titles + # A hash of resource titles mapping to resource parameters + # @param params + # A hash of default parameters to be merged with individual resource parameters + dispatch :ensure_resources do + param 'String', :type + param 'Hash[String,Hash]', :titles + optional_param 'Hash', :params + end + def ensure_resources(type, titles, params) + resource_hash = titles.dup + resources = resource_hash.keys + + resources.each do |resource_name| + params_merged = if resource_hash[resource_name] + params.merge(resource_hash[resource_name]) + else + params + end + call_function('stdlib::ensure_resource', type, resource_name, params_merged) + end + end +end diff --git a/lib/puppet/parser/functions/defined_with_params.rb b/lib/puppet/parser/functions/defined_with_params.rb index 0b8b338ee..b5350a0b0 100644 --- a/lib/puppet/parser/functions/defined_with_params.rb +++ b/lib/puppet/parser/functions/defined_with_params.rb @@ -27,6 +27,10 @@ reference, params = vals raise(ArgumentError, 'Must specify a reference') unless reference + unless ENV['STDLIB_LOG_DEPRECATIONS'] == 'false' + Puppet.deprecation_warning('defined_with_params: This function is deprecated, please use stdlib::defined_with_params instead.') + end + params = {} if !params || params == '' ret = false diff --git a/lib/puppet/parser/functions/ensure_resource.rb b/lib/puppet/parser/functions/ensure_resource.rb deleted file mode 100644 index 48497ee54..000000000 --- a/lib/puppet/parser/functions/ensure_resource.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -# Test whether a given class or definition is defined -require 'puppet/parser/functions' - -Puppet::Parser::Functions.newfunction(:ensure_resource, type: :statement, doc: <<-DOC - @summary - Takes a resource type, title, and a list of attributes that describe a - resource. - - user { 'dan': - ensure => present, - } - - @return - created or recreated the passed resource with the passed type and attributes - - @example Example usage - - Creates the resource if it does not already exist: - - ensure_resource('user', 'dan', {'ensure' => 'present' }) - - If the resource already exists but does not match the specified parameters, - this function will attempt to recreate the resource leading to a duplicate - resource definition error. - - An array of resources can also be passed in and each will be created with - the type and parameters specified if it doesn't already exist. - - ensure_resource('user', ['dan','alex'], {'ensure' => 'present'}) - -DOC -) do |vals| - type, title, params = vals - raise(ArgumentError, 'Must specify a type') unless type - raise(ArgumentError, 'Must specify a title') unless title - - params ||= {} - - items = [title].flatten - - items.each do |item| - Puppet::Parser::Functions.function(:defined_with_params) - if function_defined_with_params(["#{type}[#{item}]", params]) - Puppet.debug("Resource #{type}[#{item}] with params #{params} not created because it already exists") - else - Puppet.debug("Create new resource #{type}[#{item}] with params #{params}") - Puppet::Parser::Functions.function(:create_resources) - function_create_resources([type.capitalize, { item => params }]) - end - end -end diff --git a/lib/puppet/parser/functions/ensure_resources.rb b/lib/puppet/parser/functions/ensure_resources.rb deleted file mode 100644 index a7fc9daca..000000000 --- a/lib/puppet/parser/functions/ensure_resources.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'puppet/parser/functions' - -Puppet::Parser::Functions.newfunction(:ensure_resources, type: :statement, doc: <<-DOC - @summary - Takes a resource type, title (only hash), and a list of attributes that describe a - resource. - - @return - created resources with the passed type and attributes - - @example Example usage - - 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'}) -DOC -) do |vals| - type, title, params = vals - raise(ArgumentError, 'Must specify a type') unless type - raise(ArgumentError, 'Must specify a title') unless title - - params ||= {} - - raise(Puppet::ParseError, 'ensure_resources(): Requires second argument to be a Hash') unless title.is_a?(Hash) - - resource_hash = title.dup - resources = resource_hash.keys - - Puppet::Parser::Functions.function(:ensure_resource) - resources.each do |resource_name| - params_merged = if resource_hash[resource_name] - params.merge(resource_hash[resource_name]) - else - params - end - function_ensure_resource([type, resource_name, params_merged]) - end -end diff --git a/spec/functions/ensure_resource_spec.rb b/spec/functions/ensure_resource_spec.rb index 9b04db6b6..75f020b1d 100644 --- a/spec/functions/ensure_resource_spec.rb +++ b/spec/functions/ensure_resource_spec.rb @@ -4,8 +4,8 @@ describe 'ensure_resource' do it { is_expected.not_to be_nil } - it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{Must specify a type}) } - it { is_expected.to run.with_params('type').and_raise_error(ArgumentError, %r{Must specify a title}) } + it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{'stdlib::ensure_resource' expects 3 arguments, got none}) } + it { is_expected.to run.with_params('type').and_raise_error(ArgumentError, %r{'stdlib::ensure_resource' expects 3 arguments, got 1}) } if Puppet::Util::Package.versioncmp(Puppet.version, '4.6.0') >= 0 it { is_expected.to run.with_params('type', 'title', {}, 'extras').and_raise_error(ArgumentError) } @@ -14,8 +14,7 @@ end it { - pending('should not accept numbers as arguments') - expect(subject).to run.with_params(1, 2, 3).and_raise_error(Puppet::ParseError) + expect(subject).to run.with_params(1, 2, 3).and_raise_error(ArgumentError, %r{'stdlib::ensure_resource' parameter 'type' expects a String value, got Integer}) } context 'when given an empty catalog' do diff --git a/spec/functions/ensure_resources_spec.rb b/spec/functions/ensure_resources_spec.rb index cf4de9e48..c459a3d2e 100644 --- a/spec/functions/ensure_resources_spec.rb +++ b/spec/functions/ensure_resources_spec.rb @@ -2,28 +2,30 @@ require 'spec_helper' -describe 'ensure_resources' do - it { is_expected.not_to be_nil } - it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{Must specify a type}) } - it { is_expected.to run.with_params('type').and_raise_error(ArgumentError, %r{Must specify a title}) } +['ensure_resources', 'stdlib::ensure_resources'].each do |function| + describe function do + it { is_expected.not_to be_nil } + it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{expects between 2 and 3 arguments, got none}) } + it { is_expected.to run.with_params('type').and_raise_error(ArgumentError, %r{expects between 2 and 3 arguments, got 1}) } - describe 'given a title hash of multiple resources' do - before(:each) do - subject.execute('user', { 'dan' => { 'gid' => 'mygroup', 'uid' => '600' }, 'alex' => { 'gid' => 'mygroup', 'uid' => '700' } }, 'ensure' => 'present') - end + describe 'given a title hash of multiple resources' do + before(:each) do + subject.execute('user', { 'dan' => { 'gid' => 'mygroup', 'uid' => '600' }, 'alex' => { 'gid' => 'mygroup', 'uid' => '700' } }, 'ensure' => 'present') + end - # this lambda is required due to strangeness within rspec-puppet's expectation handling - it { expect(-> { catalogue }).to contain_user('dan').with_ensure('present') } - it { expect(-> { catalogue }).to contain_user('alex').with_ensure('present') } - it { expect(-> { catalogue }).to contain_user('dan').with('gid' => 'mygroup', 'uid' => '600') } - it { expect(-> { catalogue }).to contain_user('alex').with('gid' => 'mygroup', 'uid' => '700') } - end + # this lambda is required due to strangeness within rspec-puppet's expectation handling + it { expect(-> { catalogue }).to contain_user('dan').with_ensure('present') } + it { expect(-> { catalogue }).to contain_user('alex').with_ensure('present') } + it { expect(-> { catalogue }).to contain_user('dan').with('gid' => 'mygroup', 'uid' => '600') } + it { expect(-> { catalogue }).to contain_user('alex').with('gid' => 'mygroup', 'uid' => '700') } + end - describe 'given a title hash of a single resource' do - before(:each) { subject.execute('user', { 'dan' => { 'gid' => 'mygroup', 'uid' => '600' } }, 'ensure' => 'present') } + describe 'given a title hash of a single resource' do + before(:each) { subject.execute('user', { 'dan' => { 'gid' => 'mygroup', 'uid' => '600' } }, 'ensure' => 'present') } - # this lambda is required due to strangeness within rspec-puppet's expectation handling - it { expect(-> { catalogue }).to contain_user('dan').with_ensure('present') } - it { expect(-> { catalogue }).to contain_user('dan').with('gid' => 'mygroup', 'uid' => '600') } + # this lambda is required due to strangeness within rspec-puppet's expectation handling + it { expect(-> { catalogue }).to contain_user('dan').with_ensure('present') } + it { expect(-> { catalogue }).to contain_user('dan').with('gid' => 'mygroup', 'uid' => '600') } + end end end diff --git a/spec/functions/stdlib__defined_with_params_spec.rb b/spec/functions/stdlib__defined_with_params_spec.rb new file mode 100644 index 000000000..91912532d --- /dev/null +++ b/spec/functions/stdlib__defined_with_params_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'stdlib::defined_with_params' do + describe 'when no resource is specified' do + it { is_expected.to run.with_params.and_raise_error(ArgumentError) } + end + + describe 'when compared against a resource with no attributes' do + let :pre_condition do + 'user { "dan": }' + end + + it { is_expected.to run.with_params('User[dan]', {}).and_return(true) } + it { is_expected.to run.with_params('User[bob]', {}).and_return(false) } + it { is_expected.to run.with_params('User[dan]', 'foo' => 'bar').and_return(false) } + + context 'with UTF8 and double byte characters' do + it { is_expected.to run.with_params('User[ĵĭмოү]', {}).and_return(false) } + it { is_expected.to run.with_params('User[ポーラ]', {}).and_return(false) } + end + end + + describe 'when compared against a resource with attributes' do + let :pre_condition do + 'user { "dan": ensure => present, shell => "/bin/csh", managehome => false}' + end + + it { is_expected.to run.with_params('User[dan]', {}).and_return(true) } + it { is_expected.to run.with_params('User[dan]', '').and_return(true) } + it { is_expected.to run.with_params('User[dan]', 'ensure' => 'present').and_return(true) } + it { is_expected.to run.with_params('User[dan]', 'ensure' => 'present', 'managehome' => false).and_return(true) } + it { is_expected.to run.with_params('User[dan]', 'ensure' => 'absent', 'managehome' => false).and_return(false) } + end + + describe 'when passing undef values' do + let :pre_condition do + 'file { "/tmp/a": ensure => present }' + end + let(:is_puppet_6_or_greater) { Puppet::Util::Package.versioncmp(Puppet.version, '6.0.0') >= 0 } + let(:undef_value) { is_puppet_6_or_greater ? nil : :undef } # even if :undef would work on 6.0.1, :undef should not be used + + it { is_expected.to run.with_params('File[/tmp/a]', {}).and_return(true) } + it { is_expected.to run.with_params('File[/tmp/a]', 'ensure' => 'present', 'owner' => undef_value).and_return(true) } + end + + describe 'when the reference is a' do + let :pre_condition do + 'user { "dan": }' + end + + context 'with reference' do + it { is_expected.to run.with_params(Puppet::Resource.new('User[dan]'), {}).and_return(true) } + end + + if Puppet::Util::Package.versioncmp(Puppet.version, '4.6.0') >= 0 + context 'with array' do + it 'fails' do + expect { + subject.execute(['User[dan]'], {}) + }.to raise_error(ArgumentError, %r{expects a value of type String or Type\[Resource\], got Tuple}) + end + end + end + end + + describe 'when passed a defined type' do + let :pre_condition do + <<-PRECOND + define test::deftype( + Optional $port = undef + ) { } + + test::deftype { "foo": } + test::deftype { "baz": port => 100 } + test::deftype { "adv": port => 200 } + test::deftype { "adv2": port => 200 } + + # Unsure how to stub this out below properly + if stdlib::defined_with_params(Test::Deftype, { 'port' => 200 }) { + notify { 'Duplicate found somewhere': } + } + if stdlib::defined_with_params(Test::Deftype, { 'port' => 'nope' }) { + notify { 'Should not find me': } + } + PRECOND + end + + it { is_expected.to run.with_params('Test::Deftype[foo]', {}).and_return(true) } + it { is_expected.to run.with_params('Test::Deftype[bar]', {}).and_return(false) } + it { is_expected.to run.with_params(Puppet::Resource.new('Test::Deftype[foo]'), {}).and_return(true) } + + it { + expect(subject).to run.with_params(Puppet::Resource.new('Test::Deftype[bar]'), {}).and_return(false) + + expect(catalogue.resource('Notify[Duplicate found somewhere]')).not_to be_nil + expect(catalogue.resource('Notify[Should not find me]')).to be_nil + } + end + + describe 'when called from within a defined type looking for a defined type of the same type' do + let :pre_condition do + <<-PRECOND + define test::deftype( + Optional $port = undef + ) { + if stdlib::defined_with_params(Test::Deftype, { 'port' => $port }) { + fail('Ruh Roh Shaggy') + } + } + + test::deftype { 'foo': } + test::deftype { 'bar': port => 200 } + PRECOND + end + + # Testing to make sure that the internal logic handles this case via the pre_condition + it { is_expected.to run.with_params('NoOp[noop]', {}).and_return(false) } + end + + describe 'when passed a class' do + let :pre_condition do + 'class test () { } class { "test": }' + end + + it { is_expected.to run.with_params('Class[test]', {}).and_return(true) } + it { is_expected.to run.with_params('Class["bar"]', {}).and_return(false) } + it { is_expected.to run.with_params('Class[bar]', {}).and_return(false) } + it { is_expected.to run.with_params(Puppet::Resource.new('class', 'test'), {}).and_return(true) } + it { is_expected.to run.with_params(Puppet::Resource.new('Class["bar"]'), {}).and_return(false) } + it { is_expected.to run.with_params(Puppet::Resource.new('Class[bar]'), {}).and_return(false) } + end +end