Skip to content

(bug) - Fixes missing mandatory ID #234

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

Merged
merged 1 commit into from
Aug 17, 2023
Merged
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
48 changes: 23 additions & 25 deletions lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,25 @@
require 'json'

class Puppet::Provider::DscBaseProvider
# Initializes the provider, preparing the class variables which cache:
# Initializes the provider, preparing the instance variables which cache:
# - the canonicalized resources across calls
# - query results
# - logon failures
def initialize
@@cached_canonicalized_resource ||= []
@@cached_query_results ||= []
@@cached_test_results ||= []
@@logon_failures ||= []
@cached_canonicalized_resource = []
@cached_query_results = []
@cached_test_results = []
@logon_failures = []
super
end

def cached_test_results
@@cached_test_results
end
attr_reader :cached_test_results

# Look through a cache to retrieve the hashes specified, if they have been cached.
# Does so by seeing if each of the specified hashes is a subset of any of the hashes
# in the cache, so {foo: 1, bar: 2} would return if {foo: 1} was the search hash.
#
# @param cache [Array] the class variable containing cached hashes to search through
# @param cache [Array] the instance variable containing cached hashes to search through
# @param hashes [Array] the list of hashes to search the cache for
# @return [Array] an array containing the matching hashes for the search condition, if any
def fetch_cached_hashes(cache, hashes)
Expand All @@ -52,22 +50,22 @@ def canonicalize(context, resources)
# During RSAPI refresh runs mandatory parameters are stripped and not available;
# Instead of checking again and failing, search the cache for a namevar match.
namevarized_r = r.select { |k, _v| namevar_attributes(context).include?(k) }
cached_result = fetch_cached_hashes(@@cached_canonicalized_resource, [namevarized_r]).first
cached_result = fetch_cached_hashes(@cached_canonicalized_resource, [namevarized_r]).first
if cached_result.nil?
# If the resource is meant to be absent, skip canonicalization and rely on the manifest
# value; there's no reason to compare system state to desired state for casing if the
# resource is being removed.
if r[:dsc_ensure] == 'absent'
canonicalized = r.dup
@@cached_canonicalized_resource << r.dup
@cached_canonicalized_resource << r.dup
else
canonicalized = invoke_get_method(context, r)
# If the resource could not be found or was returned as absent, skip case munging and
# treat the manifest values as canonical since the resource is being created.
# rubocop:disable Metrics/BlockNesting
if canonicalized.nil? || canonicalized[:dsc_ensure] == 'absent'
canonicalized = r.dup
@@cached_canonicalized_resource << r.dup
@cached_canonicalized_resource << r.dup
else
parameters = r.select { |name, _properties| parameter_attributes(context).include?(name) }
canonicalized.merge!(parameters)
Expand All @@ -91,7 +89,7 @@ def canonicalize(context, resources)
canonicalized.delete(key) unless downcased_resource.key?(key)
end
# Cache the actually canonicalized resource separately
@@cached_canonicalized_resource << canonicalized.dup
@cached_canonicalized_resource << canonicalized.dup
end
# rubocop:enable Metrics/BlockNesting
end
Expand Down Expand Up @@ -123,13 +121,13 @@ def get(context, names = nil)
context.debug('Collecting data from the DSC Resource')

# If the resource has already been queried, do not bother querying for it again
cached_results = fetch_cached_hashes(@@cached_query_results, names)
cached_results = fetch_cached_hashes(@cached_query_results, names)
return cached_results unless cached_results.empty?

if @@cached_canonicalized_resource.empty?
if @cached_canonicalized_resource.empty?
mandatory_properties = {}
else
canonicalized_resource = @@cached_canonicalized_resource[0].dup
canonicalized_resource = @cached_canonicalized_resource[0].dup
mandatory_properties = canonicalized_resource.select do |attribute, _value|
(mandatory_get_attributes(context) - namevar_attributes(context)).include?(attribute)
end
Expand Down Expand Up @@ -266,9 +264,9 @@ def invoke_dsc_resource(context, name_hash, props, method)
if error.include?('Logon failure: the user has not been granted the requested logon type at this computer')
logon_error = "PSDscRunAsCredential account specified (#{name_hash[:dsc_psdscrunascredential]['user']}) does not have appropriate logon rights; are they an administrator?"
name_hash[:name].nil? ? context.err(logon_error) : context.err(name_hash[:name], logon_error)
@@logon_failures << name_hash[:dsc_psdscrunascredential].dup
@logon_failures << name_hash[:dsc_psdscrunascredential].dup
# This is a hack to handle the query cache to prevent a second lookup
@@cached_query_results << name_hash # if fetch_cached_hashes(@@cached_query_results, [data]).empty?
@cached_query_results << name_hash # if fetch_cached_hashes(@cached_query_results, [data]).empty?
else
context.err(error)
end
Expand All @@ -292,7 +290,7 @@ def invoke_dsc_resource(context, name_hash, props, method)
def insync?(context, name, _property_name, _is_hash, should_hash)
return nil if should_hash[:validation_mode] != 'resource'

prior_result = fetch_cached_hashes(@@cached_test_results, [name])
prior_result = fetch_cached_hashes(@cached_test_results, [name])
prior_result.empty? ? invoke_test_method(context, name, should_hash) : prior_result.first[:in_desired_state]
end

Expand Down Expand Up @@ -361,7 +359,7 @@ def invoke_get_method(context, name_hash)
data = recursively_sort(data)

# Cache the query to prevent a second lookup
@@cached_query_results << data.dup if fetch_cached_hashes(@@cached_query_results, [data]).empty?
@cached_query_results << data.dup if fetch_cached_hashes(@cached_query_results, [data]).empty?
context.debug("Returned to Puppet as #{data}")
data
end
Expand Down Expand Up @@ -400,7 +398,7 @@ def invoke_test_method(context, name, should)
return nil if data.nil?

in_desired_state = data['indesiredstate']
@@cached_test_results << name.merge({ in_desired_state: in_desired_state })
@cached_test_results << name.merge({ in_desired_state: in_desired_state })

return in_desired_state if in_desired_state

Expand Down Expand Up @@ -451,7 +449,7 @@ def vendored_modules_path(module_name)
# path to allow multiple modules to with shared dsc_resources to be installed side by side
# The old vendored_modules_path: puppet_x/dsc_resources
# The new vendored_modules_path: puppet_x/<module_name>/dsc_resources
root_module_path = load_path.find { |path| path.match?(%r{#{puppetize_name(module_name)}/lib}) }
root_module_path = load_path.grep(%r{#{puppetize_name(module_name)}/lib}).first
vendored_path = if root_module_path.nil?
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + "puppet_x/#{puppetize_name(module_name)}/dsc_resources") # rubocop:disable Style/StringConcatenation
else
Expand Down Expand Up @@ -509,12 +507,12 @@ def random_variable_name
#
# @return [Hash] containing all instantiated variables and the properties that they define
def instantiated_variables
@@instantiated_variables ||= {}
@instantiated_variables ||= {}
end

# Clear the instantiated variables hash to be ready for the next run
def clear_instantiated_variables!
@@instantiated_variables = {}
@instantiated_variables = {}
end

# Return true if the specified credential hash has already failed to execute a DSC resource due to
Expand All @@ -523,7 +521,7 @@ def clear_instantiated_variables!
# @param [Hash] a credential hash with a user and password keys where the password is a sensitive string
# @return [Bool] true if the credential_hash has already failed logon, false otherwise
def logon_failed_already?(credential_hash)
@@logon_failures.any? do |failure_hash|
@logon_failures.any? do |failure_hash|
failure_hash['user'] == credential_hash['user'] && failure_hash['password'].unwrap == credential_hash['password'].unwrap
end
end
Expand Down
Loading