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