File: keyvalue.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (158 lines) | stat: -rw-r--r-- 5,375 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
require_relative '../../puppet/property'

module Puppet
  class Property
    # This subclass of {Puppet::Property} manages string key value pairs.
    # In order to use this property:
    #
    # * the _should_ value must be an array of key-value pairs separated by the 'separator'
    # * the retrieve method should return a hash with the keys as symbols
    # @note **IMPORTANT**: In order for this property to work there must also be a 'membership' parameter
    #   The class that inherits from property should override that method with the symbol for the membership
    # @todo The node with an important message is not very clear.
    #
    class KeyValue < Property
      class << self
        # This is a class-level variable that child properties can override
        # if they wish.
        attr_accessor :log_only_changed_or_new_keys
      end

      self.log_only_changed_or_new_keys = false

      def hash_to_key_value_s(hash)
        if self.class.log_only_changed_or_new_keys
          hash = hash.select { |k, _| @changed_or_new_keys.include?(k) }
        end

        hash.map { |*pair| pair.join(separator) }.join(delimiter)
      end

      def should_to_s(should_value)
        hash_to_key_value_s(should_value)
      end

      def is_to_s(current_value)
        hash_to_key_value_s(current_value)
      end

      def membership
        :key_value_membership
      end

      def inclusive?
        @resource[membership] == :inclusive
      end

      def hashify_should
        # Puppet casts all should values to arrays. Thus, if the user
        # passed in a hash for our property's should value, the should_value
        # parameter will be a single element array so we just extract our value
        # directly.
        if ! @should.empty? && @should.first.is_a?(Hash)
          return @should.first
        end

        # Here, should is an array of key/value pairs.
        @should.inject({}) do |hash, key_value|
          tmp = key_value.split(separator)
          hash[tmp[0].strip.intern] = tmp[1]
          hash
        end
      end

      def process_current_hash(current)
        return {} if current == :absent

        #inclusive means we are managing everything so if it isn't in should, its gone
        current.each_key { |key| current[key] = nil } if inclusive?
        current
      end

      def should
        return nil unless @should

        members = hashify_should
        current = process_current_hash(retrieve)

        #shared keys will get overwritten by members
        should_value = current.merge(members)

        # Figure out the keys that will actually change in our Puppet run.
        # This lets us reduce the verbosity of Puppet's logging for instances
        # of this class when we want to.
        #
        # NOTE: We use ||= here because we only need to compute the
        # changed_or_new_keys once (since this property will only be synced once).
        #
        @changed_or_new_keys ||= should_value.keys.select do |key|
          ! current.key?(key) || current[key] != should_value[key]
        end

        should_value
      end

      # @return [String] Returns a default separator of "="
      def separator
        "="
      end

      # @return [String] Returns a default delimiter of ";"
      def delimiter
        ";"
      end

      # Retrieves the key-hash from the provider by invoking its method named the same as this property.
      # @return [Hash] the hash from the provider, or `:absent`
      #
      def retrieve
        #ok, some 'convention' if the keyvalue property is named properties, provider should implement a properties method
        key_hash = provider.send(name) if provider
        if key_hash && key_hash != :absent
          return key_hash
        else
          return :absent
        end
      end

      # Returns true if there is no _is_ value, else returns if _is_ is equal to _should_ using == as comparison.
      # @return [Boolean] whether the property is in sync or not.
      def insync?(is)
        return true unless is

        (is == self.should)
      end

      # We only accept an array of key/value pairs (strings), a single
      # key/value pair (string) or a Hash as valid values for our property.
      # Note that for an array property value, the 'value' passed into the
      # block corresponds to the array element.
      validate do |value|
        unless value.is_a?(String) || value.is_a?(Hash)
          raise ArgumentError, _("The %{name} property must be specified as a hash or an array of key/value pairs (strings)!") % { name: name }
        end

        next if value.is_a?(Hash)

        unless value.include?("#{separator}")
          raise ArgumentError, _("Key/value pairs must be separated by '%{separator}'") % {separator: separator}
        end
      end

      # The validate step ensures that our passed-in value is
      # either a String or a Hash. If our value's a string,
      # then nothing else needs to be done. Otherwise, we need
      # to stringify the hash's keys and values to match our
      # internal representation of the property's value.
      munge do |value|
        next value if value.is_a?(String)

        munged_value = value.to_a.map! do |hash_key, hash_value|
          [hash_key.to_s.strip.to_sym, hash_value.to_s]
        end

        Hash[munged_value]
      end
    end
  end
end