Class: Puppet::Pops::Lookup::HieraConfig Private

Inherits:
Object
  • Object
show all
Includes:
Puppet::Pops::LabelProvider, LocationResolver
Defined in:
lib/puppet/pops/lookup/hiera_config.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Direct Known Subclasses

HieraConfigV3, HieraConfigV4, HieraConfigV5

Constant Summary collapse

CONFIG_FILE_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'hiera.yaml'
KEY_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'name'
KEY_VERSION =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'version'
KEY_DATADIR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'datadir'
KEY_DEFAULT_HIERARCHY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'default_hierarchy'
KEY_HIERARCHY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'hierarchy'
KEY_PLAN_HIERARCHY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'plan_hierarchy'
KEY_LOGGER =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'logger'
KEY_OPTIONS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'options'
KEY_PATH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'path'
KEY_PATHS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'paths'
KEY_MAPPED_PATHS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'mapped_paths'
KEY_GLOB =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'glob'
KEY_GLOBS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'globs'
KEY_URI =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'uri'
KEY_URIS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'uris'
KEY_DEFAULTS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'defaults'
KEY_DATA_HASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

DataHashFunctionProvider::TAG
KEY_LOOKUP_KEY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

LookupKeyFunctionProvider::TAG
KEY_DATA_DIG =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

DataDigFunctionProvider::TAG
KEY_V3_DATA_HASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

V3DataHashFunctionProvider::TAG
KEY_V3_LOOKUP_KEY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

V3LookupKeyFunctionProvider::TAG
KEY_V3_BACKEND =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

V3BackendFunctionProvider::TAG
KEY_V4_DATA_HASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

V4DataHashFunctionProvider::TAG
KEY_BACKEND =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'backend'
KEY_EXTENSION =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'extension'
FUNCTION_KEYS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

[KEY_DATA_HASH, KEY_LOOKUP_KEY, KEY_DATA_DIG, KEY_V3_BACKEND]
ALL_FUNCTION_KEYS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

FUNCTION_KEYS + [KEY_V4_DATA_HASH]
LOCATION_KEYS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

[KEY_PATH, KEY_PATHS, KEY_GLOB, KEY_GLOBS, KEY_URI, KEY_URIS, KEY_MAPPED_PATHS]
FUNCTION_PROVIDERS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

{
  KEY_DATA_HASH => DataHashFunctionProvider,
  KEY_DATA_DIG => DataDigFunctionProvider,
  KEY_LOOKUP_KEY => LookupKeyFunctionProvider,
  KEY_V3_DATA_HASH => V3DataHashFunctionProvider,
  KEY_V3_BACKEND => V3BackendFunctionProvider,
  KEY_V3_LOOKUP_KEY => V3LookupKeyFunctionProvider,
  KEY_V4_DATA_HASH => V4DataHashFunctionProvider
}

Constants included from Puppet::Pops::LabelProvider

Puppet::Pops::LabelProvider::A, Puppet::Pops::LabelProvider::AN, Puppet::Pops::LabelProvider::SKIPPED_CHARACTERS, Puppet::Pops::LabelProvider::VOWELS

Constants included from SubLookup

SubLookup::SPECIAL

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Puppet::Pops::LabelProvider

#a_an, #a_an_uc, #article, #combine_strings, #label, #plural_s, #the, #the_uc

Methods included from LocationResolver

#expand_globs, #expand_mapped_paths, #expand_uris, #resolve_paths

Methods included from Interpolation

#interpolate

Methods included from SubLookup

#split_key, #sub_lookup

Constructor Details

#initialize(config_root, config_path, loaded_config, owner) ⇒ HieraConfig

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Parameters:

  • config_path (Pathname)

    Absolute path to the configuration

  • loaded_config (Hash)

    the loaded configuration



177
178
179
180
181
182
183
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 177

def initialize(config_root, config_path, loaded_config, owner)
  @config_root = config_root
  @config_path = config_path
  @loaded_config = loaded_config
  @config = validate_config(self.class.symkeys_to_string(@loaded_config), owner)
  @data_providers = nil
end

Instance Attribute Details

#config_pathObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



170
171
172
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 170

def config_path
  @config_path
end

Class Method Details

.config_exist?(config_root) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


102
103
104
105
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 102

def self.config_exist?(config_root)
  config_path = config_root + CONFIG_FILE_NAME
  config_path.exist?
end

.create(lookup_invocation, config_path, owner) ⇒ LookupConfiguration

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Parameters:

  • lookup_invocation (Invocation)

    Invocation data containing scope, overrides, and defaults

  • config_path (Pathname)

    Absolute path to the configuration file

  • owner (ConfiguredDataProvider)

    The data provider that will own the created configuration

Returns:

  • (LookupConfiguration)

    the configuration



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
159
160
161
162
163
164
165
166
167
168
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 127

def self.create(lookup_invocation, config_path, owner)
  if config_path.is_a?(Hash)
    config_path = nil
    loaded_config = config_path
  else
    config_root = config_path.parent
    if config_path.exist?
      env_context = EnvironmentContext.adapt(lookup_invocation.scope.compiler.environment)
      loaded_config = env_context.cached_file_data(config_path) do |content|
        parsed = Puppet::Util::Yaml.safe_load(content, [Symbol], config_path)

        # For backward compatibility, we must treat an empty file, or a yaml that doesn't
        # produce a Hash as Hiera version 3 default.
        if parsed.is_a?(Hash)
          parsed
        else
          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 })
          HieraConfigV3::DEFAULT_CONFIG_HASH
        end
      end
    else
      config_path = nil
      loaded_config = HieraConfigV5::DEFAULT_CONFIG_HASH
    end
  end

  version = loaded_config[KEY_VERSION] || loaded_config[:version]
  version = version.nil? ? 3 : version.to_i
  case version
  when 5
    HieraConfigV5.new(config_root, config_path, loaded_config, owner)
  when 4
    HieraConfigV4.new(config_root, config_path, loaded_config, owner)
  when 3
    HieraConfigV3.new(config_root, config_path, loaded_config, owner)
  else
    issue = Issues::HIERA_UNSUPPORTED_VERSION
    raise Puppet::DataBinding::LookupError.new(
      issue.format(:version => version), config_path, nil, nil, nil, issue.issue_code
    )
  end
end

.symkeys_to_string(struct) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 107

def self.symkeys_to_string(struct)
  case (struct)
  when Hash
    map = {}
    struct.each_pair { |k, v| map[k.is_a?(Symbol) ? k.to_s : k] = symkeys_to_string(v) }
    map
  when Array
    struct.map { |v| symkeys_to_string(v) }
  else
    struct
  end
end

.v4_function_config(config_root, function_name, owner) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 84

def self.v4_function_config(config_root, function_name, owner)
  unless Puppet[:strict] == :off
    Puppet.warn_once('deprecations', 'legacy_provider_function',
                     _("Using of legacy data provider function '%{function_name}'. Please convert to a 'data_hash' function") % { function_name: function_name })
  end
  HieraConfigV5.new(config_root, nil,
                    {
                      KEY_VERSION => 5,
                      KEY_HIERARCHY => [
                        {
                          KEY_NAME => "Legacy function '#{function_name}'",
                          KEY_V4_DATA_HASH => function_name
                        }
                      ]
                    }.freeze,
                    owner)
end

Instance Method Details

#configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy = false) ⇒ Array<DataProvider>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the data providers for this config

Parameters:

  • lookup_invocation (Invocation)

    Invocation data containing scope, overrides, and defaults

  • parent_data_provider (DataProvider)

    The data provider that loaded this configuration

Returns:



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 200

def configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy = false)
  unless @data_providers && scope_interpolations_stable?(lookup_invocation)
    if @data_providers
      lookup_invocation.report_text { _('Hiera configuration recreated due to change of scope variables used in interpolation expressions') }
    end
    slc_invocation = ScopeLookupCollectingInvocation.new(lookup_invocation.scope)
    begin
      @data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, false)
      if has_default_hierarchy?
        @default_data_providers = create_configured_data_providers(slc_invocation, parent_data_provider, true)
      end
    rescue StandardError => e
      # Raise a LookupError with a RUNTIME_ERROR issue to prevent this being translated to an evaluation error triggered in the pp file
      # where the lookup started
      if e.message =~ /^Undefined variable '([^']+)'/
        var = $1
        fail(Issues::HIERA_UNDEFINED_VARIABLE, { :name => var }, find_line_matching(/%\{['"]?#{var}['"]?}/))
      end
      raise e
    end
    @scope_interpolations = slc_invocation.scope_interpolations
  end
  use_default_hierarchy ? @default_data_providers : @data_providers
end

#create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



272
273
274
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 272

def create_configured_data_providers(lookup_invocation, parent_data_provider, use_default_hierarchy)
  self.class.not_implemented(self, 'create_configured_data_providers')
end

#create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 288

def create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config)
  # Custom backend. Hiera 3 must be installed, its logger configured, and it must be made aware of the loaded config
  raise Puppet::DataBinding::LookupError, 'Hiera 3 is not installed' if !Puppet.features.hiera?

  if Hiera::Config.instance_variable_defined?(:@config) && (current_config = Hiera::Config.instance_variable_get(:@config)).is_a?(Hash)
    current_config.each_pair do |key, val|
      case key
      when :hierarchy, :backends
        hiera3_config[key] = ([val] + [hiera3_config[key]]).flatten.uniq
      else
        hiera3_config[key] = val
      end
    end
  elsif hiera3_config.include?(KEY_LOGGER)
    Hiera.logger = hiera3_config[KEY_LOGGER].to_s
  else
    Hiera.logger = 'puppet'
  end

  unless Hiera::Interpolate.const_defined?(:PATCHED_BY_HIERA_5)
    # Replace the class methods 'hiera_interpolate' and 'alias_interpolate' with a method that wires back and performs global
    # lookups using the lookup framework. This is necessary since the classic Hiera is made aware only of custom backends.
    class << Hiera::Interpolate
      hiera_interpolate = Proc.new do |_data, key, scope, _extra_data, context|
        override = context[:order_override]
        invocation = Puppet::Pops::Lookup::Invocation.current
        unless override.nil? && invocation.global_only?
          invocation = Puppet::Pops::Lookup::Invocation.new(scope)
          invocation.set_global_only
          invocation.set_hiera_v3_location_overrides(override) unless override.nil?
        end
        Puppet::Pops::Lookup::LookupAdapter.adapt(scope.compiler).lookup(key, invocation, nil)
      end

      send(:remove_method, :hiera_interpolate)
      send(:remove_method, :alias_interpolate)
      send(:define_method, :hiera_interpolate, hiera_interpolate)
      send(:define_method, :alias_interpolate, hiera_interpolate)
    end
    Hiera::Interpolate.send(:const_set, :PATCHED_BY_HIERA_5, true)
  end

  Hiera::Config.instance_variable_set(:@config, hiera3_config)

  # Use a special lookup_key that delegates to the backend
  paths = nil if !paths.nil? && paths.empty?
  create_data_provider(name, parent_data_provider, KEY_V3_BACKEND, 'hiera_v3_data', { KEY_DATADIR => datadir, KEY_BACKEND => backend }, paths)
end

#fail(issue, args = EMPTY_HASH, line = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



185
186
187
188
189
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 185

def fail(issue, args = EMPTY_HASH, line = nil)
  raise Puppet::DataBinding::LookupError.new(
    issue.format(args.merge(:label => self)), @config_path, line, nil, nil, issue.issue_code
  )
end

#find_line_matching(regexp, start_line = 1) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 226

def find_line_matching(regexp, start_line = 1)
  line_number = 0
  File.foreach(@config_path) do |line|
    line_number += 1
    next if line_number < start_line

    quote = nil
    stripped = ''.dup
    line.each_codepoint do |cp|
      case cp
      when 0x22, 0x27 # double or single quote
        if quote == cp
          quote = nil
        elsif quote.nil?
          quote = cp
        end
      when 0x23 # unquoted hash mark
        break
      end
      stripped << cp
    end
    return line_number if stripped =~ regexp
  end
  nil
end

#has_default_hierarchy?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


191
192
193
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 191

def has_default_hierarchy?
  false
end

#nameObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



284
285
286
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 284

def name
  "hiera configuration version #{version}"
end

#scope_interpolations_stable?(lookup_invocation) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 252

def scope_interpolations_stable?(lookup_invocation)
  if @scope_interpolations.empty?
    true
  else
    scope = lookup_invocation.scope
    lookup_invocation.without_explain do
      @scope_interpolations.all? do |key, root_key, segments, old_value|
        value = Puppet.override(avoid_hiera_interpolation_errors: true) { scope[root_key] }
        unless value.nil? || segments.empty?
          found = nil;
          catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }
          value = found;
        end
        old_value.eql?(value)
      end
    end
  end
end

#validate_config(config, owner) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



276
277
278
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 276

def validate_config(config, owner)
  self.class.not_implemented(self, 'validate_config')
end

#versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



280
281
282
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 280

def version
  self.class.not_implemented(self, 'version')
end