Method: Puppet::Provider::DscBaseProvider#canonicalize

Defined in:
lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb

#canonicalize(context, resources) ⇒ Hash

Implements the canonicalize feature of the Resource API; this method is called first against any resources defined in the manifest, then again to conform the results from a get call. The method attempts to retrieve the DSC resource from the machine; if the resource is found, this method then compares the downcased values of the two hashes, overwriting the manifest value with the discovered one if they are case insensitively equivalent; this enables case insensitive but preserving behavior where a manifest declaration of a path as “c:/foo/bar” if discovered on disk as “C:FooBar” will canonicalize to the latter and prevent any flapping.

rubocop:disable Metrics/BlockLength, Metrics/MethodLength

Parameters:

  • context (Object)

    the Puppet runtime context to operate in and send feedback to

  • resources (Hash)

    the hash of the resource to canonicalize from either manifest or invocation

Returns:

  • (Hash)

    returns a hash representing the current state of the object, if it exists



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb', line 49

def canonicalize(context, resources)
  canonicalized_resources = []
  resources.collect do |r|
    # 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
    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
      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
        else
          parameters = r.select { |name, _properties| parameter_attributes(context).include?(name) }
          canonicalized.merge!(parameters)
          canonicalized[:name] = r[:name]
          if r[:dsc_psdscrunascredential].nil?
            canonicalized.delete(:dsc_psdscrunascredential)
          else
            canonicalized[:dsc_psdscrunascredential] = r[:dsc_psdscrunascredential]
          end
          downcased_result = recursively_downcase(canonicalized)
          downcased_resource = recursively_downcase(r)
          # Ensure that metaparameters are preserved when we canonicalize the resource.
          metaparams = r.select { |key, _value| Puppet::Type.metaparam?(key) }
          canonicalized.merge!(metaparams) unless metaparams.nil?
          downcased_result.each do |key, value|
            # Canonicalize to the manifest value unless the downcased strings match and the attribute is not an enum:
            # - When the values don't match at all, the manifest value is desired;
            # - When the values match case insensitively but the attribute is an enum, and the casing from invoke_get_method
            #   is not int the enum, prefer the casing of the manifest enum.
            # - When the values match case insensitively and the attribute is not an enum, or is an enum and invoke_get_method casing
            #   is in the enum, prefer the casing from invoke_get_method
            is_enum = enum_attributes(context).include?(key)
            canonicalized_value_in_enum = if is_enum
                                            enum_values(context, key).include?(canonicalized[key])
                                          else
                                            false
                                          end
            match_insensitively = same?(value, downcased_resource[key])
            canonicalized[key] = r[key] unless match_insensitively && (canonicalized_value_in_enum || !is_enum)
            canonicalized.delete(key) unless downcased_resource.key?(key)
          end
          # Cache the actually canonicalized resource separately
          @cached_canonicalized_resource << canonicalized.dup
        end
        # rubocop:enable Metrics/BlockNesting
      end
    else
      # The resource has already been canonicalized for the set values and is not being canonicalized for get
      # In this case, we do *not* want to process anything, just return the resource. We only call canonicalize
      # so we can get case insensitive but preserving values for _setting_ state.
      canonicalized = r
    end
    canonicalized_resources << canonicalized
  end
  context.debug("Canonicalized Resources: #{canonicalized_resources}")
  canonicalized_resources
end