require_relative 'function_provider'
module Puppet::Pops
module Lookup
# @api private
class DataDigFunctionProvider < FunctionProvider
TAG = 'data_dig'.freeze
# Performs a lookup with the assumption that a recursive check has been made.
#
# @param key [LookupKey] The key to lookup
# @param lookup_invocation [Invocation] The current lookup invocation
# @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies "first found")
# @return [Object] the found object
# @throw :no_such_key when the object is not found
def unchecked_key_lookup(key, lookup_invocation, merge)
lookup_invocation.with(:data_provider, self) do
MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|
invoke_with_location(lookup_invocation, location, key, merge)
end
end
end
def invoke_with_location(lookup_invocation, location, key, merge)
if location.nil?
key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, nil, merge)))
else
lookup_invocation.with(:location, location) do
key.undig(lookup_invocation.report_found(key, validated_data_dig(key, lookup_invocation, location, merge)))
end
end
end
def label
'Data Dig'
end
def validated_data_dig(key, lookup_invocation, location, merge)
validate_data_value(data_dig(key, lookup_invocation, location, merge)) do
msg = "Value for key '#{key}', returned from #{full_name}"
location.nil? ? msg : "#{msg}, when using location '#{location}',"
end
end
private
def data_dig(key, lookup_invocation, location, merge)
unless location.nil? || location.exist?
lookup_invocation.report_location_not_found
throw :no_such_key
end
ctx = function_context(lookup_invocation, location)
ctx.data_hash ||= {}
catch(:no_such_key) do
hash = ctx.data_hash
hash[key] = ctx.function.call(lookup_invocation.scope, key.to_a, options(location), Context.new(ctx, lookup_invocation)) unless hash.include?(key)
return hash[key]
end
lookup_invocation.report_not_found(key)
throw :no_such_key
end
end
# @api private
class V3BackendFunctionProvider < DataDigFunctionProvider
TAG = 'hiera3_backend'.freeze
def data_dig(key, lookup_invocation, location, merge)
@backend ||= instantiate_backend(lookup_invocation)
# A merge_behavior retrieved from hiera.yaml must not be converted here. Instead, passing the symbol :hash
# tells the V3 backend to pick it up from the config.
resolution_type = lookup_invocation.hiera_v3_merge_behavior? ? :hash : convert_merge(merge)
@backend.lookup(key.to_s, lookup_invocation.scope, lookup_invocation.hiera_v3_location_overrides, resolution_type, {:recurse_guard => nil})
end
def full_name
"hiera version 3 backend '#{options[HieraConfig::KEY_BACKEND]}'"
end
def value_is_validated?
false
end
private
def instantiate_backend(lookup_invocation)
backend_name = options[HieraConfig::KEY_BACKEND]
begin
require 'hiera/backend'
require "hiera/backend/#{backend_name.downcase}_backend"
backend = Hiera::Backend.const_get("#{backend_name.capitalize}_backend").new
return backend.method(:lookup).arity == 4 ? Hiera::Backend::Backend1xWrapper.new(backend) : backend
rescue LoadError => e
lookup_invocation.report_text { "Unable to load backend '#{backend_name}': #{e.message}" }
throw :no_such_key
rescue NameError => e
lookup_invocation.report_text { "Unable to instantiate backend '#{backend_name}': #{e.message}" }
throw :no_such_key
end
end
# Converts a lookup 'merge' parameter argument into a Hiera 'resolution_type' argument.
#
# @param merge [String,Hash,nil] The lookup 'merge' argument
# @return [Symbol,Hash,nil] The Hiera 'resolution_type'
def convert_merge(merge)
case merge
when nil
when 'first', 'default'
# Nil is OK. Defaults to Hiera :priority
nil
when Puppet::Pops::MergeStrategy
convert_merge(merge.configuration)
when 'unique'
# Equivalent to Hiera :array
:array
when 'hash'
# Equivalent to Hiera :hash with default :native merge behavior. A Hash must be passed here
# to override possible Hiera deep merge config settings.
{ :behavior => :native }
when 'deep', 'unconstrained_deep'
# Equivalent to Hiera :hash with :deeper merge behavior.
{ :behavior => :deeper }
when 'reverse_deep'
# Equivalent to Hiera :hash with :deep merge behavior.
{ :behavior => :deep }
when Hash
strategy = merge['strategy']
case strategy
when 'deep', 'unconstrained_deep', 'reverse_deep'
result = { :behavior => strategy == 'reverse_deep' ? :deep : :deeper }
# Remaining entries must have symbolic keys
merge.each_pair { |k,v| result[k.to_sym] = v unless k == 'strategy' }
result
else
convert_merge(strategy)
end
else
raise Puppet::DataBinding::LookupError, "Unrecognized value for request 'merge' parameter: '#{merge}'"
end
end
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez