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
|