require '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
Copyright 2K16 - 2K18 Indonesian Hacker Rulez