class Puppet::Pops::Lookup::HieraConfig

@api private

Constants

ALL_FUNCTION_KEYS
CONFIG_FILE_NAME
FUNCTION_KEYS
FUNCTION_PROVIDERS
KEY_BACKEND
KEY_DATADIR
KEY_DATA_DIG
KEY_DATA_HASH
KEY_DEFAULTS
KEY_DEFAULT_HIERARCHY
KEY_EXTENSION
KEY_GLOB
KEY_GLOBS
KEY_HIERARCHY
KEY_LOGGER
KEY_LOOKUP_KEY
KEY_MAPPED_PATHS
KEY_NAME
KEY_OPTIONS
KEY_PATH
KEY_PATHS
KEY_PLAN_HIERARCHY
KEY_URI
KEY_URIS
KEY_V3_BACKEND
KEY_V3_DATA_HASH
KEY_V3_LOOKUP_KEY
KEY_V4_DATA_HASH
KEY_VERSION
LOCATION_KEYS

Attributes

config_path[R]

Public Class Methods

config_exist?(config_root) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
103 def self.config_exist?(config_root)
104   config_path = config_root + CONFIG_FILE_NAME
105   config_path.exist?
106 end
create(lookup_invocation, config_path, owner) click to toggle source

Creates a new HieraConfig from the given config_root. This is where the 'hiera.yaml' is expected to be found and is also the base location used when resolving relative paths.

@param lookup_invocation [Invocation] Invocation data containing scope, overrides, and defaults @param config_path [Pathname] Absolute path to the configuration file @param owner [ConfiguredDataProvider] The data provider that will own the created configuration @return [LookupConfiguration] the configuration

    # File lib/puppet/pops/lookup/hiera_config.rb
128 def self.create(lookup_invocation, config_path, owner)
129   if config_path.is_a?(Hash)
130     config_path = nil
131     loaded_config = config_path
132   else
133     config_root = config_path.parent
134     if config_path.exist?
135       env_context = EnvironmentContext.adapt(lookup_invocation.scope.compiler.environment)
136       loaded_config = env_context.cached_file_data(config_path) do |content|
137         parsed = Puppet::Util::Yaml.safe_load(content, [Symbol], config_path)
138 
139         # For backward compatibility, we must treat an empty file, or a yaml that doesn't
140         # produce a Hash as Hiera version 3 default.
141         if parsed.is_a?(Hash)
142           parsed
143         else
144           Puppet.warning(_("%{config_path}: File exists but does not contain a valid YAML hash. Falling back to Hiera version 3 default config") % { config_path: config_path })
145           HieraConfigV3::DEFAULT_CONFIG_HASH
146         end
147       end
148     else
149       config_path = nil
150       loaded_config = HieraConfigV5::DEFAULT_CONFIG_HASH
151     end
152   end
153 
154   version = loaded_config[KEY_VERSION] || loaded_config[:version]
155   version = version.nil? ? 3 : version.to_i
156   case version
157   when 5
158     HieraConfigV5.new(config_root, config_path, loaded_config, owner)
159   when 4
160     HieraConfigV4.new(config_root, config_path, loaded_config, owner)
161   when 3
162     HieraConfigV3.new(config_root, config_path, loaded_config, owner)
163   else
164     issue = Issues::HIERA_UNSUPPORTED_VERSION
165     raise Puppet::DataBinding::LookupError.new(
166       issue.format(:version => version),  config_path, nil, nil, nil, issue.issue_code)
167   end
168 end
new(config_root, config_path, loaded_config, owner) click to toggle source

Creates a new HieraConfig from the given config_root. This is where the 'lookup.yaml' is expected to be found and is also the base location used when resolving relative paths.

@param config_path [Pathname] Absolute path to the configuration @param loaded_config [Hash] the loaded configuration

    # File lib/puppet/pops/lookup/hiera_config.rb
177 def initialize(config_root, config_path, loaded_config, owner)
178   @config_root = config_root
179   @config_path = config_path
180   @loaded_config = loaded_config
181   @config = validate_config(self.class.symkeys_to_string(@loaded_config), owner)
182   @data_providers = nil
183 end
symkeys_to_string(struct) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
108 def self.symkeys_to_string(struct)
109   case(struct)
110   when Hash
111     map = {}
112     struct.each_pair {|k,v| map[ k.is_a?(Symbol) ? k.to_s : k] = symkeys_to_string(v) }
113     map
114   when Array
115     struct.map { |v| symkeys_to_string(v) }
116   else
117     struct
118   end
119 end
v4_function_config(config_root, function_name, owner) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
 84 def self.v4_function_config(config_root, function_name, owner)
 85   unless Puppet[:strict] == :off
 86     Puppet.warn_once('deprecations', 'legacy_provider_function',
 87       _("Using of legacy data provider function '%{function_name}'. Please convert to a 'data_hash' function") % { function_name: function_name })
 88   end
 89   HieraConfigV5.new(config_root, nil,
 90     {
 91       KEY_VERSION => 5,
 92       KEY_HIERARCHY => [
 93         {
 94           KEY_NAME => "Legacy function '#{function_name}'",
 95           KEY_V4_DATA_HASH => function_name
 96         }
 97       ]
 98     }.freeze,
 99     owner
100   )
101 end

Private Class Methods

not_implemented(impl, method_name) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
342 def self.not_implemented(impl, method_name)
343   raise NotImplementedError, "The class #{impl.class.name} should have implemented the method #{method_name}()"
344 end

Public Instance Methods

configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy = false) click to toggle source

Returns the data providers for this config

@param lookup_invocation [Invocation] Invocation data containing scope, overrides, and defaults @param parent_data_provider [DataProvider] The data provider that loaded this configuration @return [Array<DataProvider>] the data providers

    # File lib/puppet/pops/lookup/hiera_config.rb
199 def configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy = false)
200   unless @data_providers && scope_interpolations_stable?(lookup_invocation)
201     if @data_providers
202       lookup_invocation.report_text { _('Hiera configuration recreated due to change of scope variables used in interpolation expressions') }
203     end
204     slc_invocation = ScopeLookupCollectingInvocation.new(lookup_invocation.scope)
205     begin
206       @data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, false)
207       if has_default_hierarchy?
208         @default_data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, true)
209       end
210     rescue StandardError => e
211       # Raise a LookupError with a RUNTIME_ERROR issue to prevent this being translated to an evaluation error triggered in the pp file
212       # where the lookup started
213       if e.message =~ /^Undefined variable '([^']+)'/
214         var = $1
215         fail(Issues::HIERA_UNDEFINED_VARIABLE, { :name => var }, find_line_matching(/%\{['"]?#{var}['"]?}/))
216       end
217       raise e
218     end
219     @scope_interpolations = slc_invocation.scope_interpolations
220   end
221   use_default_hierarchy ? @default_data_providers : @data_providers
222 end
create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy) click to toggle source

@api private

    # File lib/puppet/pops/lookup/hiera_config.rb
269 def create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy)
270   self.class.not_implemented(self, 'create_configured_data_providers')
271 end
create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
285 def create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config)
286   # Custom backend. Hiera 3 must be installed, its logger configured, and it must be made aware of the loaded config
287   raise Puppet::DataBinding::LookupError, 'Hiera 3 is not installed' if !Puppet.features.hiera?
288 
289   if Hiera::Config.instance_variable_defined?(:@config) && (current_config = Hiera::Config.instance_variable_get(:@config)).is_a?(Hash)
290     current_config.each_pair do |key, val|
291       case key
292       when :hierarchy, :backends
293         hiera3_config[key] = ([val] + [hiera3_config[key]]).flatten.uniq
294       else
295         hiera3_config[key] = val
296       end
297     end
298   else
299     if hiera3_config.include?(KEY_LOGGER)
300       Hiera.logger = hiera3_config[KEY_LOGGER].to_s
301     else
302       Hiera.logger = 'puppet'
303     end
304   end
305 
306   unless Hiera::Interpolate.const_defined?(:PATCHED_BY_HIERA_5)
307     # Replace the class methods 'hiera_interpolate' and 'alias_interpolate' with a method that wires back and performs global
308     # lookups using the lookup framework. This is necessary since the classic Hiera is made aware only of custom backends.
309     class << Hiera::Interpolate
310       hiera_interpolate = Proc.new do |data, key, scope, extra_data, context|
311         override = context[:order_override]
312         invocation = Puppet::Pops::Lookup::Invocation.current
313         unless override.nil? && invocation.global_only?
314           invocation = Puppet::Pops::Lookup::Invocation.new(scope)
315           invocation.set_global_only
316           invocation.set_hiera_v3_location_overrides(override) unless override.nil?
317         end
318         Puppet::Pops::Lookup::LookupAdapter.adapt(scope.compiler).lookup(key, invocation, nil)
319       end
320 
321       send(:remove_method, :hiera_interpolate)
322       send(:remove_method, :alias_interpolate)
323       send(:define_method, :hiera_interpolate, hiera_interpolate)
324       send(:define_method, :alias_interpolate, hiera_interpolate)
325     end
326     Hiera::Interpolate.send(:const_set, :PATCHED_BY_HIERA_5, true)
327   end
328 
329   Hiera::Config.instance_variable_set(:@config, hiera3_config)
330 
331   # Use a special lookup_key that delegates to the backend
332   paths = nil if !paths.nil? && paths.empty?
333   create_data_provider(name, parent_data_provider, KEY_V3_BACKEND, 'hiera_v3_data', { KEY_DATADIR => datadir, KEY_BACKEND => backend }, paths)
334 end
fail(issue, args = EMPTY_HASH, line = nil) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
185 def fail(issue, args = EMPTY_HASH, line = nil)
186   raise Puppet::DataBinding::LookupError.new(
187     issue.format(args.merge(:label => self)),  @config_path, line, nil, nil, issue.issue_code)
188 end
find_line_matching(regexp, start_line = 1) click to toggle source

Find first line in configuration that matches regexp after given line. Comments are stripped

    # File lib/puppet/pops/lookup/hiera_config.rb
225 def find_line_matching(regexp, start_line = 1)
226   line_number = 0
227   File.foreach(@config_path) do |line|
228     line_number += 1
229     next if line_number < start_line
230     quote = nil
231     stripped = String.new
232     line.each_codepoint do |cp|
233       if cp == 0x22 || cp == 0x27 # double or single quote
234         if quote == cp
235           quote = nil
236         elsif quote.nil?
237           quote = cp
238         end
239       elsif cp == 0x23 # unquoted hash mark
240         break
241       end
242       stripped << cp
243     end
244     return line_number if stripped =~ regexp
245   end
246   nil
247 end
has_default_hierarchy?() click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
190 def has_default_hierarchy?
191   false
192 end
name() click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
281 def name
282   "hiera configuration version #{version}"
283 end
scope_interpolations_stable?(lookup_invocation) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
249 def scope_interpolations_stable?(lookup_invocation)
250   if @scope_interpolations.empty?
251     true
252   else
253     scope = lookup_invocation.scope
254     lookup_invocation.without_explain do
255       @scope_interpolations.all? do |key, root_key, segments, old_value|
256         value = Puppet.override(avoid_hiera_interpolation_errors: true) { scope[root_key] }
257         unless value.nil? || segments.empty?
258           found = nil;
259           catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }
260           value = found;
261         end
262         old_value.eql?(value)
263       end
264     end
265   end
266 end
validate_config(config, owner) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
273 def validate_config(config, owner)
274   self.class.not_implemented(self, 'validate_config')
275 end
version() click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
277 def version
278   self.class.not_implemented(self, 'version')
279 end

Private Instance Methods

create_data_provider(name, parent_data_provider, function_kind, function_name, options, locations) click to toggle source
    # File lib/puppet/pops/lookup/hiera_config.rb
338 def create_data_provider(name, parent_data_provider, function_kind, function_name, options, locations)
339   FUNCTION_PROVIDERS[function_kind].new(name, parent_data_provider, function_name, options, locations)
340 end