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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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'.freeze
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



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

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.



168
169
170
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 168

def config_path
  @config_path
end

#versionObject (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.



168
169
170
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 168

def version
  @version
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)


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

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



126
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
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 126

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.



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

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.



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

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:



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

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.



267
268
269
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 267

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.



283
284
285
286
287
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
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 283

def create_hiera3_backend_provider(name, backend, parent_data_provider, datadir, paths, hiera3_config)
  # Custom backend. Hiera v3 must be installed, it's logger configured, and it must be made aware of the loaded config
  require '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
  else
    if hiera3_config.include?(KEY_LOGGER)
      Hiera.logger = hiera3_config[KEY_LOGGER].to_s
    else
      Hiera.logger = 'puppet'
    end
  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.



183
184
185
186
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 183

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



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

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 = ''
    line.each_codepoint do |cp|
      if cp == 0x22 || cp == 0x27 # double or single quote
        if quote == cp
          quote = nil
        elsif quote.nil?
          quote = cp
        end
      elsif cp == 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)


188
189
190
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 188

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.



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

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)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/puppet/pops/lookup/hiera_config.rb', line 247

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 = 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.



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

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