require 'puppet/thread_local'
require_relative 'explainer'
module Puppet::Pops
module Lookup
# @api private
class Invocation
attr_reader :scope, :override_values, :default_values, :explainer, :module_name, :top_key, :adapter_class
def self.current
(@current ||= Puppet::ThreadLocal.new(nil)).value
end
def self.current=(new_value)
@current.value = new_value
end
# Creates a new instance with same settings as this instance but with a new given scope
# and yields with that scope.
#
# @param scope [Puppet::Parser::Scope] The new scope
# @return [Invocation] the new instance
def with_scope(scope)
yield(Invocation.new(scope, override_values, default_values, explainer))
end
# Creates a context object for a lookup invocation. The object contains the current scope, overrides, and default
# values and may optionally contain an {ExplanationAcceptor} instance that will receive book-keeping information
# about the progress of the lookup.
#
# If the _explain_ argument is a boolean, then _false_ means that no explanation is needed and _true_ means that
# the default explanation acceptor should be used. The _explain_ argument may also be an instance of the
# `ExplanationAcceptor` class.
#
# @param scope [Puppet::Parser::Scope] The scope to use for the lookup
# @param override_values [Hash<String,Object>|nil] A map to use as override. Values found here are returned immediately (no merge)
# @param default_values [Hash<String,Object>] A map to use as the last resort (but before default)
# @param explainer [boolean,Explanainer] An boolean true to use the default explanation acceptor or an explainer instance that will receive information about the lookup
def initialize(scope, override_values = EMPTY_HASH, default_values = EMPTY_HASH, explainer = nil, adapter_class = nil)
@scope = scope
@override_values = override_values
@default_values = default_values
parent_invocation = self.class.current
if parent_invocation && (adapter_class.nil? || adapter_class == parent_invocation.adapter_class)
# Inherit from parent invocation (track recursion)
@name_stack = parent_invocation.name_stack
@adapter_class = parent_invocation.adapter_class
# Inherit Hiera 3 legacy properties
set_hiera_xxx_call if parent_invocation.hiera_xxx_call?
set_hiera_v3_merge_behavior if parent_invocation.hiera_v3_merge_behavior?
set_global_only if parent_invocation.global_only?
povr = parent_invocation.hiera_v3_location_overrides
set_hiera_v3_location_overrides(povr) unless povr.empty?
# Inherit explainer unless a new explainer is given or disabled using false
explainer = explainer == false ? nil : parent_invocation.explainer
else
@name_stack = []
@adapter_class = adapter_class.nil? ? LookupAdapter : adapter_class
unless explainer.is_a?(Explainer)
explainer = explainer == true ? Explainer.new : nil
end
explainer = DebugExplainer.new(explainer) if Puppet[:debug] && !explainer.is_a?(DebugExplainer)
end
@explainer = explainer
end
def lookup(key, module_name = nil)
key = LookupKey.new(key) unless key.is_a?(LookupKey)
@top_key = key
@module_name = module_name.nil? ? key.module_name : module_name
save_current = self.class.current
if save_current.equal?(self)
yield
else
begin
self.class.current = self
yield
ensure
self.class.current = save_current
end
end
end
def check(name)
if @name_stack.include?(name)
raise Puppet::DataBinding::RecursiveLookupError, _("Recursive lookup detected in [%{name_stack}]") % { name_stack: @name_stack.join(', ') }
end
return unless block_given?
@name_stack.push(name)
begin
yield
rescue Puppet::DataBinding::LookupError
raise
rescue Puppet::Error => detail
raise Puppet::DataBinding::LookupError.new(detail.message, nil, nil, nil, detail)
ensure
@name_stack.pop
end
end
def emit_debug_info(preamble)
@explainer.emit_debug_info(preamble) if @explainer.is_a?(DebugExplainer)
end
def lookup_adapter
@adapter ||= @adapter_class.adapt(scope.compiler)
end
# This method is overridden by the special Invocation used while resolving interpolations in a
# Hiera configuration file (hiera.yaml) where it's used for collecting and remembering the current
# values that the configuration was based on
#
# @api private
def remember_scope_lookup(*lookup_result)
# Does nothing by default
end
# The qualifier_type can be one of:
# :global - qualifier is the data binding terminus name
# :data_provider - qualifier a DataProvider instance
# :path - qualifier is a ResolvedPath instance
# :merge - qualifier is a MergeStrategy instance
# :interpolation - qualifier is the unresolved interpolation expression
# :meta - qualifier is the module name
# :data - qualifier is the key
#
# @param qualifier [Object] A branch, a provider, or a path
def with(qualifier_type, qualifier)
if explainer.nil?
yield
else
@explainer.push(qualifier_type, qualifier)
begin
yield
ensure
@explainer.pop
end
end
end
def without_explain
if explainer.nil?
yield
else
save_explainer = @explainer
begin
@explainer = nil
yield
ensure
@explainer = save_explainer
end
end
end
def only_explain_options?
@explainer.nil? ? false : @explainer.only_explain_options?
end
def explain_options?
@explainer.nil? ? false : @explainer.explain_options?
end
def report_found_in_overrides(key, value)
@explainer.accept_found_in_overrides(key, value) unless @explainer.nil?
value
end
def report_found_in_defaults(key, value)
@explainer.accept_found_in_defaults(key, value) unless @explainer.nil?
value
end
def report_found(key, value)
@explainer.accept_found(key, value) unless @explainer.nil?
value
end
def report_merge_source(merge_source)
@explainer.accept_merge_source(merge_source) unless @explainer.nil?
end
# Report the result of a merge or fully resolved interpolated string
# @param value [Object] The result to report
# @return [Object] the given value
def report_result(value)
@explainer.accept_result(value) unless @explainer.nil?
value
end
def report_not_found(key)
@explainer.accept_not_found(key) unless @explainer.nil?
end
def report_location_not_found
@explainer.accept_location_not_found unless @explainer.nil?
end
def report_module_not_found(module_name)
@explainer.accept_module_not_found(module_name) unless @explainer.nil?
end
def report_module_provider_not_found(module_name)
@explainer.accept_module_provider_not_found(module_name) unless @explainer.nil?
end
def report_text(&block)
unless @explainer.nil?
@explainer.accept_text(block.call)
end
end
def global_only?
lookup_adapter.global_only? || (instance_variable_defined?(:@global_only) ? @global_only : false)
end
# Instructs the lookup framework to only perform lookups in the global layer
# @return [Invocation] self
def set_global_only
@global_only = true
self
end
# @return [Pathname] the full path of the hiera.yaml config file
def global_hiera_config_path
lookup_adapter.global_hiera_config_path
end
# @return [Boolean] `true` if the invocation stems from the hiera_xxx function family
def hiera_xxx_call?
instance_variable_defined?(:@hiera_xxx_call)
end
def set_hiera_xxx_call
@hiera_xxx_call = true
end
# @return [Boolean] `true` if the invocation stems from the hiera_xxx function family
def hiera_v3_merge_behavior?
instance_variable_defined?(:@hiera_v3_merge_behavior)
end
def set_hiera_v3_merge_behavior
@hiera_v3_merge_behavior = true
end
# Overrides passed from hiera_xxx functions down to V3DataHashFunctionProvider
def set_hiera_v3_location_overrides(overrides)
@hiera_v3_location_overrides = [overrides].flatten unless overrides.nil?
end
def hiera_v3_location_overrides
instance_variable_defined?(:@hiera_v3_location_overrides) ? @hiera_v3_location_overrides : EMPTY_ARRAY
end
protected
def name_stack
@name_stack.clone
end
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez