Skip to content

Provide stdlib::ensure_resource(s) functions #1450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 245 additions & 106 deletions REFERENCE.md

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions lib/puppet/functions/ensure_resource.rb
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions lib/puppet/functions/ensure_resources.rb
Original file line number Diff line number Diff line change
@@ -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
88 changes: 88 additions & 0 deletions lib/puppet/functions/stdlib/defined_with_params.rb
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions lib/puppet/functions/stdlib/ensure_packages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions lib/puppet/functions/stdlib/ensure_resource.rb
Original file line number Diff line number Diff line change
@@ -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
58 changes: 58 additions & 0 deletions lib/puppet/functions/stdlib/ensure_resources.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions lib/puppet/parser/functions/defined_with_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
53 changes: 0 additions & 53 deletions lib/puppet/parser/functions/ensure_resource.rb

This file was deleted.

58 changes: 0 additions & 58 deletions lib/puppet/parser/functions/ensure_resources.rb

This file was deleted.

7 changes: 3 additions & 4 deletions spec/functions/ensure_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand All @@ -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
Expand Down
Loading