hiera 2.0.0-x64-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +202 -0
- data/LICENSE +18 -0
- data/README.md +276 -0
- data/bin/hiera +248 -0
- data/lib/hiera/backend/json_backend.rb +58 -0
- data/lib/hiera/backend/yaml_backend.rb +63 -0
- data/lib/hiera/backend.rb +325 -0
- data/lib/hiera/config.rb +90 -0
- data/lib/hiera/console_logger.rb +13 -0
- data/lib/hiera/error.rb +4 -0
- data/lib/hiera/fallback_logger.rb +41 -0
- data/lib/hiera/filecache.rb +86 -0
- data/lib/hiera/interpolate.rb +98 -0
- data/lib/hiera/noop_logger.rb +8 -0
- data/lib/hiera/puppet_logger.rb +17 -0
- data/lib/hiera/recursive_guard.rb +20 -0
- data/lib/hiera/util.rb +47 -0
- data/lib/hiera/version.rb +89 -0
- data/lib/hiera.rb +115 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/unit/backend/json_backend_spec.rb +85 -0
- data/spec/unit/backend/yaml_backend_spec.rb +138 -0
- data/spec/unit/backend_spec.rb +743 -0
- data/spec/unit/config_spec.rb +118 -0
- data/spec/unit/console_logger_spec.rb +19 -0
- data/spec/unit/fallback_logger_spec.rb +80 -0
- data/spec/unit/filecache_spec.rb +142 -0
- data/spec/unit/fixtures/interpolate/config/hiera.yaml +6 -0
- data/spec/unit/fixtures/interpolate/data/niltest.yaml +2 -0
- data/spec/unit/fixtures/interpolate/data/recursive.yaml +3 -0
- data/spec/unit/fixtures/override/config/hiera.yaml +5 -0
- data/spec/unit/fixtures/override/data/alternate.yaml +1 -0
- data/spec/unit/fixtures/override/data/common.yaml +2 -0
- data/spec/unit/hiera_spec.rb +81 -0
- data/spec/unit/interpolate_spec.rb +36 -0
- data/spec/unit/puppet_logger_spec.rb +31 -0
- data/spec/unit/util_spec.rb +49 -0
- data/spec/unit/version_spec.rb +44 -0
- metadata +128 -0
data/lib/hiera/config.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
class Hiera::Config
|
2
|
+
class << self
|
3
|
+
##
|
4
|
+
# load takes a string or hash as input, strings are treated as filenames
|
5
|
+
# hashes are stored as data that would have been in the config file
|
6
|
+
#
|
7
|
+
# Unless specified it will only use YAML as backend with a single
|
8
|
+
# 'common' hierarchy and console logger
|
9
|
+
#
|
10
|
+
# @return [Hash] representing the configuration. e.g.
|
11
|
+
# {:backends => "yaml", :hierarchy => "common"}
|
12
|
+
def load(source)
|
13
|
+
@config = {:backends => "yaml",
|
14
|
+
:hierarchy => "common",
|
15
|
+
:merge_behavior => :native }
|
16
|
+
|
17
|
+
if source.is_a?(String)
|
18
|
+
if File.exist?(source)
|
19
|
+
config = begin
|
20
|
+
yaml_load_file(source)
|
21
|
+
rescue TypeError => detail
|
22
|
+
case detail.message
|
23
|
+
when /no implicit conversion from nil to integer/
|
24
|
+
false
|
25
|
+
else
|
26
|
+
raise detail
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@config.merge! config if config
|
30
|
+
else
|
31
|
+
raise "Config file #{source} not found"
|
32
|
+
end
|
33
|
+
elsif source.is_a?(Hash)
|
34
|
+
@config.merge! source
|
35
|
+
end
|
36
|
+
|
37
|
+
@config[:backends] = [ @config[:backends] ].flatten
|
38
|
+
|
39
|
+
if @config.include?(:logger)
|
40
|
+
Hiera.logger = @config[:logger].to_s
|
41
|
+
else
|
42
|
+
@config[:logger] = "console"
|
43
|
+
Hiera.logger = "console"
|
44
|
+
end
|
45
|
+
|
46
|
+
self.validate!
|
47
|
+
|
48
|
+
@config
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate!
|
52
|
+
case @config[:merge_behavior]
|
53
|
+
when :deep,'deep',:deeper,'deeper'
|
54
|
+
begin
|
55
|
+
require "deep_merge"
|
56
|
+
rescue LoadError
|
57
|
+
raise Hiera::Error, "Must have 'deep_merge' gem installed for the configured merge_behavior."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# yaml_load_file directly delegates to YAML.load_file and is intended to be
|
64
|
+
# a private, internal method suitable for stubbing and mocking.
|
65
|
+
#
|
66
|
+
# @return [Object] return value of {YAML.load_file}
|
67
|
+
def yaml_load_file(source)
|
68
|
+
YAML.load_file(source)
|
69
|
+
end
|
70
|
+
private :yaml_load_file
|
71
|
+
|
72
|
+
def load_backends
|
73
|
+
@config[:backends].each do |backend|
|
74
|
+
begin
|
75
|
+
require "hiera/backend/#{backend.downcase}_backend"
|
76
|
+
rescue LoadError => e
|
77
|
+
Hiera.warn "Cannot load backend #{backend}: #{e}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def include?(key)
|
83
|
+
@config.include?(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def [](key)
|
87
|
+
@config[key]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/hiera/error.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Select from a given list of loggers the first one that
|
2
|
+
# it suitable and use that as the actual logger
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
class Hiera::FallbackLogger
|
6
|
+
# Chooses the first suitable logger. For all of the loggers that are
|
7
|
+
# unsuitable it will issue a warning using the suitable logger stating that
|
8
|
+
# the unsuitable logger is not being used.
|
9
|
+
#
|
10
|
+
# @param implementations [Array<Hiera::Logger>] the implementations to choose from
|
11
|
+
# @raises when there are no suitable loggers
|
12
|
+
def initialize(*implementations)
|
13
|
+
warnings = []
|
14
|
+
@implementation = implementations.find do |impl|
|
15
|
+
if impl.respond_to?(:suitable?)
|
16
|
+
if impl.suitable?
|
17
|
+
true
|
18
|
+
else
|
19
|
+
warnings << "Not using #{impl.name}. It does not report itself to be suitable."
|
20
|
+
false
|
21
|
+
end
|
22
|
+
else
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if @implementation.nil?
|
28
|
+
raise "No suitable logging implementation found."
|
29
|
+
end
|
30
|
+
|
31
|
+
warnings.each { |message| warn(message) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def warn(message)
|
35
|
+
@implementation.warn(message)
|
36
|
+
end
|
37
|
+
|
38
|
+
def debug(message)
|
39
|
+
@implementation.debug(message)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Hiera
|
2
|
+
class Filecache
|
3
|
+
def initialize
|
4
|
+
@cache = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
# Reads a file, optionally parse it in some way check the
|
8
|
+
# output type and set a default
|
9
|
+
#
|
10
|
+
# Simply invoking it this way will return the file contents
|
11
|
+
#
|
12
|
+
# data = read("/some/file")
|
13
|
+
#
|
14
|
+
# But as most cases of file reading in hiera involves some kind
|
15
|
+
# of parsing through a serializer there's some help for those
|
16
|
+
# cases:
|
17
|
+
#
|
18
|
+
# data = read("/some/file", Hash, {}) do |data|
|
19
|
+
# JSON.parse(data)
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# In this case it will read the file, parse it using JSON then
|
23
|
+
# check that the end result is a Hash, if it's not a hash or if
|
24
|
+
# reading/parsing fails it will return {} instead
|
25
|
+
#
|
26
|
+
# Prior to calling this method you should be sure the file exist
|
27
|
+
def read(path, expected_type = Object, default=nil, &block)
|
28
|
+
read_file(path, expected_type, &block)
|
29
|
+
rescue TypeError => detail
|
30
|
+
Hiera.debug("#{detail.message}, setting defaults")
|
31
|
+
@cache[path][:data] = default
|
32
|
+
rescue => detail
|
33
|
+
error = "Reading data from #{path} failed: #{detail.class}: #{detail}"
|
34
|
+
if default.nil?
|
35
|
+
raise detail
|
36
|
+
else
|
37
|
+
Hiera.debug(error)
|
38
|
+
@cache[path][:data] = default
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Read a file when it changes. If a file is re-read and has not changed since the last time
|
43
|
+
# then the last, processed, contents will be returned.
|
44
|
+
#
|
45
|
+
# The processed data can also be checked against an expected type. If the
|
46
|
+
# type does not match a TypeError is raised.
|
47
|
+
#
|
48
|
+
# No error handling is done inside this method. Any failed reads or errors
|
49
|
+
# in processing will be propagated to the caller
|
50
|
+
def read_file(path, expected_type = Object)
|
51
|
+
if stale?(path)
|
52
|
+
data = File.read(path)
|
53
|
+
@cache[path][:data] = block_given? ? yield(data) : data
|
54
|
+
|
55
|
+
if !@cache[path][:data].is_a?(expected_type)
|
56
|
+
raise TypeError, "Data retrieved from #{path} is #{data.class} not #{expected_type}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@cache[path][:data]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def stale?(path)
|
66
|
+
meta = path_metadata(path)
|
67
|
+
|
68
|
+
@cache[path] ||= {:data => nil, :meta => nil}
|
69
|
+
|
70
|
+
if @cache[path][:meta] == meta
|
71
|
+
return false
|
72
|
+
else
|
73
|
+
@cache[path][:meta] = meta
|
74
|
+
return true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# This is based on the old caching in the YAML backend and has a
|
79
|
+
# resolution of 1 second, changes made within the same second of
|
80
|
+
# a previous read will be ignored
|
81
|
+
def path_metadata(path)
|
82
|
+
stat = File.stat(path)
|
83
|
+
{:inode => stat.ino, :mtime => stat.mtime, :size => stat.size}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'hiera/backend'
|
2
|
+
require 'hiera/recursive_guard'
|
3
|
+
|
4
|
+
|
5
|
+
class Hiera::InterpolationInvalidValue < StandardError; end
|
6
|
+
|
7
|
+
class Hiera::Interpolate
|
8
|
+
class << self
|
9
|
+
INTERPOLATION = /%\{([^\}]*)\}/
|
10
|
+
METHOD_INTERPOLATION = /%\{(scope|hiera|literal|alias)\(['"]([^"']*)["']\)\}/
|
11
|
+
|
12
|
+
def interpolate(data, scope, extra_data, context)
|
13
|
+
if data.is_a?(String)
|
14
|
+
# Wrapping do_interpolation in a gsub block ensures we process
|
15
|
+
# each interpolation site in isolation using separate recursion guards.
|
16
|
+
context ||= {}
|
17
|
+
new_context = context.clone
|
18
|
+
new_context[:recurse_guard] ||= Hiera::RecursiveGuard.new
|
19
|
+
data.gsub(INTERPOLATION) do |match|
|
20
|
+
interp_val = do_interpolation(match, scope, extra_data, new_context)
|
21
|
+
|
22
|
+
# Get interp method in case we are aliasing
|
23
|
+
if data.is_a?(String) && (match = data.match(INTERPOLATION))
|
24
|
+
interpolate_method, key = get_interpolation_method_and_key(data)
|
25
|
+
else
|
26
|
+
interpolate_method = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
if ( (interpolate_method == :alias_interpolate) and (!interp_val.is_a?(String)) )
|
30
|
+
if data.match("^#{INTERPOLATION}$")
|
31
|
+
return interp_val
|
32
|
+
else
|
33
|
+
raise Hiera::InterpolationInvalidValue, "Cannot call alias in the string context"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
interp_val
|
37
|
+
end
|
38
|
+
end
|
39
|
+
else
|
40
|
+
data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def do_interpolation(data, scope, extra_data, context)
|
45
|
+
if data.is_a?(String) && (match = data.match(INTERPOLATION))
|
46
|
+
interpolation_variable = match[1]
|
47
|
+
context[:recurse_guard].check(interpolation_variable) do
|
48
|
+
interpolate_method, key = get_interpolation_method_and_key(data)
|
49
|
+
interpolated_data = send(interpolate_method, data, key, scope, extra_data, context)
|
50
|
+
|
51
|
+
# Halt recursion if we encounter a literal.
|
52
|
+
return interpolated_data if interpolate_method == :literal_interpolate
|
53
|
+
|
54
|
+
do_interpolation(interpolated_data, scope, extra_data, context)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
data
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :do_interpolation
|
61
|
+
|
62
|
+
def get_interpolation_method_and_key(data)
|
63
|
+
if (match = data.match(METHOD_INTERPOLATION))
|
64
|
+
case match[1]
|
65
|
+
when 'hiera' then [:hiera_interpolate, match[2]]
|
66
|
+
when 'scope' then [:scope_interpolate, match[2]]
|
67
|
+
when 'literal' then [:literal_interpolate, match[2]]
|
68
|
+
when 'alias' then [:alias_interpolate, match[2]]
|
69
|
+
end
|
70
|
+
elsif (match = data.match(INTERPOLATION))
|
71
|
+
[:scope_interpolate, match[1]]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
private :get_interpolation_method_and_key
|
75
|
+
|
76
|
+
def scope_interpolate(data, key, scope, extra_data, context)
|
77
|
+
segments = key.split('.')
|
78
|
+
catch(:no_such_key) { return Hiera::Backend.qualified_lookup(segments, scope) }
|
79
|
+
catch(:no_such_key) { Hiera::Backend.qualified_lookup(segments, extra_data) }
|
80
|
+
end
|
81
|
+
private :scope_interpolate
|
82
|
+
|
83
|
+
def hiera_interpolate(data, key, scope, extra_data, context)
|
84
|
+
Hiera::Backend.lookup(key, nil, scope, context[:order_override], :priority, context)
|
85
|
+
end
|
86
|
+
private :hiera_interpolate
|
87
|
+
|
88
|
+
def literal_interpolate(data, key, scope, extra_data, context)
|
89
|
+
key
|
90
|
+
end
|
91
|
+
private :literal_interpolate
|
92
|
+
|
93
|
+
def alias_interpolate(data, key, scope, extra_data, context)
|
94
|
+
Hiera::Backend.lookup(key, nil, scope, context[:order_override], :priority, context)
|
95
|
+
end
|
96
|
+
private :alias_interpolate
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Hiera
|
2
|
+
module Puppet_logger
|
3
|
+
class << self
|
4
|
+
def suitable?
|
5
|
+
defined?(::Puppet) == "constant"
|
6
|
+
end
|
7
|
+
|
8
|
+
def warn(msg)
|
9
|
+
Puppet.notice("hiera(): #{msg}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def debug(msg)
|
13
|
+
Puppet.debug("hiera(): #{msg}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Allow for safe recursive lookup of values during variable interpolation.
|
2
|
+
#
|
3
|
+
# @api private
|
4
|
+
class Hiera::InterpolationLoop < StandardError; end
|
5
|
+
|
6
|
+
class Hiera::RecursiveGuard
|
7
|
+
def initialize
|
8
|
+
@seen = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def check(value, &block)
|
12
|
+
if @seen.include?(value)
|
13
|
+
raise Hiera::InterpolationLoop, "Detected in [#{@seen.join(', ')}]"
|
14
|
+
end
|
15
|
+
@seen.push(value)
|
16
|
+
ret = yield
|
17
|
+
@seen.pop
|
18
|
+
ret
|
19
|
+
end
|
20
|
+
end
|
data/lib/hiera/util.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class Hiera
|
2
|
+
module Util
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def posix?
|
6
|
+
require 'etc'
|
7
|
+
Etc.getpwuid(0) != nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def microsoft_windows?
|
11
|
+
return false unless file_alt_separator
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'win32/dir'
|
15
|
+
true
|
16
|
+
rescue LoadError => err
|
17
|
+
warn "Cannot run on Microsoft Windows without the win32-dir gem: #{err}"
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def config_dir
|
23
|
+
if microsoft_windows?
|
24
|
+
File.join(common_appdata, 'PuppetLabs', 'code')
|
25
|
+
else
|
26
|
+
'/etc/puppetlabs/code'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def var_dir
|
31
|
+
if microsoft_windows?
|
32
|
+
File.join(common_appdata, 'PuppetLabs', 'code', 'hieradata')
|
33
|
+
else
|
34
|
+
'/etc/puppetlabs/code/hieradata'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def file_alt_separator
|
39
|
+
File::ALT_SEPARATOR
|
40
|
+
end
|
41
|
+
|
42
|
+
def common_appdata
|
43
|
+
Dir::COMMON_APPDATA
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# The version method and constant are isolated in hiera/version.rb so that a
|
2
|
+
# simple `require 'hiera/version'` allows a rubygems gemspec or bundler
|
3
|
+
# Gemfile to get the hiera version of the gem install.
|
4
|
+
#
|
5
|
+
# The version is programatically settable because we want to allow the
|
6
|
+
# Raketasks and such to set the version based on the output of `git describe`
|
7
|
+
|
8
|
+
|
9
|
+
class Hiera
|
10
|
+
VERSION = "2.0.0"
|
11
|
+
|
12
|
+
##
|
13
|
+
# version is a public API method intended to always provide a fast and
|
14
|
+
# lightweight way to determine the version of hiera.
|
15
|
+
#
|
16
|
+
# The intent is that software external to hiera be able to determine the
|
17
|
+
# hiera version with no side-effects. The expected use is:
|
18
|
+
#
|
19
|
+
# require 'hiera/version'
|
20
|
+
# version = Hiera.version
|
21
|
+
#
|
22
|
+
# This function has the following ordering precedence. This precedence list
|
23
|
+
# is designed to facilitate automated packaging tasks by simply writing to
|
24
|
+
# the VERSION file in the same directory as this source file.
|
25
|
+
#
|
26
|
+
# 1. If a version has been explicitly assigned using the Hiera.version=
|
27
|
+
# method, return that version.
|
28
|
+
# 2. If there is a VERSION file, read the contents, trim any
|
29
|
+
# trailing whitespace, and return that version string.
|
30
|
+
# 3. Return the value of the Hiera::VERSION constant hard-coded into
|
31
|
+
# the source code.
|
32
|
+
#
|
33
|
+
# If there is no VERSION file, the method must return the version string of
|
34
|
+
# the nearest parent version that is an officially released version. That is
|
35
|
+
# to say, if a branch named 3.1.x contains 25 patches on top of the most
|
36
|
+
# recent official release of 3.1.1, then the version method must return the
|
37
|
+
# string "3.1.1" if no "VERSION" file is present.
|
38
|
+
#
|
39
|
+
# By design the version identifier is _not_ intended to vary during the life
|
40
|
+
# a process. There is no guarantee provided that writing to the VERSION file
|
41
|
+
# while a Hiera process is running will cause the version string to be
|
42
|
+
# updated. On the contrary, the contents of the VERSION are cached to reduce
|
43
|
+
# filesystem accesses.
|
44
|
+
#
|
45
|
+
# The VERSION file is intended to be used by package maintainers who may be
|
46
|
+
# applying patches or otherwise changing the software version in a manner
|
47
|
+
# that warrants a different software version identifier. The VERSION file is
|
48
|
+
# intended to be managed and owned by the release process and packaging
|
49
|
+
# related tasks, and as such should not reside in version control. The
|
50
|
+
# VERSION constant is intended to be version controlled in history.
|
51
|
+
#
|
52
|
+
# Ideally, this behavior will allow package maintainers to precisely specify
|
53
|
+
# the version of the software they're packaging as in the following example:
|
54
|
+
#
|
55
|
+
# $ git describe --match "1.2.*" > lib/hiera/VERSION
|
56
|
+
# $ ruby -r hiera/version -e 'puts Hiera.version'
|
57
|
+
# 1.2.1-9-g9fda440
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
#
|
61
|
+
# @return [String] containing the hiera version, e.g. "1.2.1"
|
62
|
+
def self.version
|
63
|
+
version_file = File.join(File.dirname(__FILE__), 'VERSION')
|
64
|
+
return @hiera_version if @hiera_version
|
65
|
+
if version = read_version_file(version_file)
|
66
|
+
@hiera_version = version
|
67
|
+
end
|
68
|
+
@hiera_version ||= VERSION
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.version=(version)
|
72
|
+
@hiera_version = version
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# read_version_file reads the content of the "VERSION" file that lives in the
|
77
|
+
# same directory as this source code file.
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
#
|
81
|
+
# @return [String] for example: "1.6.14-6-gea42046" or nil if the VERSION
|
82
|
+
# file does not exist.
|
83
|
+
def self.read_version_file(path)
|
84
|
+
if File.exists?(path)
|
85
|
+
File.read(path).chomp
|
86
|
+
end
|
87
|
+
end
|
88
|
+
private_class_method :read_version_file
|
89
|
+
end
|
data/lib/hiera.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Hiera
|
4
|
+
require "hiera/error"
|
5
|
+
require "hiera/version"
|
6
|
+
require "hiera/config"
|
7
|
+
require "hiera/util"
|
8
|
+
require "hiera/backend"
|
9
|
+
require "hiera/console_logger"
|
10
|
+
require "hiera/puppet_logger"
|
11
|
+
require "hiera/noop_logger"
|
12
|
+
require "hiera/fallback_logger"
|
13
|
+
require "hiera/filecache"
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_reader :logger
|
17
|
+
|
18
|
+
# Loggers are pluggable, just provide a class called
|
19
|
+
# Hiera::Foo_logger and respond to :warn and :debug
|
20
|
+
#
|
21
|
+
# See hiera-puppet for an example that uses the Puppet
|
22
|
+
# loging system instead of our own
|
23
|
+
def logger=(logger)
|
24
|
+
require "hiera/#{logger}_logger"
|
25
|
+
|
26
|
+
@logger = Hiera::FallbackLogger.new(
|
27
|
+
Hiera.const_get("#{logger.capitalize}_logger"),
|
28
|
+
Hiera::Console_logger)
|
29
|
+
rescue Exception => e
|
30
|
+
@logger = Hiera::Console_logger
|
31
|
+
warn("Failed to load #{logger} logger: #{e.class}: #{e}")
|
32
|
+
end
|
33
|
+
|
34
|
+
def warn(msg); @logger.warn(msg); end
|
35
|
+
def debug(msg); @logger.debug(msg); end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :options, :config
|
39
|
+
|
40
|
+
# If the config option is a string its assumed to be a filename,
|
41
|
+
# else a hash of what would have been in the YAML config file
|
42
|
+
def initialize(options={})
|
43
|
+
options[:config] ||= File.join(Util.config_dir, 'hiera.yaml')
|
44
|
+
|
45
|
+
@config = Config.load(options[:config])
|
46
|
+
|
47
|
+
Config.load_backends
|
48
|
+
end
|
49
|
+
|
50
|
+
# Calls the backends to do the actual lookup.
|
51
|
+
#
|
52
|
+
# The _scope_ can be anything that responds to `[]`, if you have input
|
53
|
+
# data like a Puppet Scope that does not you can wrap that data in a
|
54
|
+
# class that has a `[]` method that fetches the data from your source.
|
55
|
+
# See hiera-puppet for an example of this.
|
56
|
+
#
|
57
|
+
# The order-override will insert as first in the hierarchy a data source
|
58
|
+
# of your choice.
|
59
|
+
#
|
60
|
+
# Possible values for the _resolution_type_ parameter:
|
61
|
+
#
|
62
|
+
# - _:priority_ - This is the default. First found value is returned and no merge is performed
|
63
|
+
# - _:array_ - An array merge lookup assembles a value from every matching level of the hierarchy. It retrieves all
|
64
|
+
# of the (string or array) values for a given key, then flattens them into a single array of unique values.
|
65
|
+
# If _priority_ lookup can be thought of as a “default with overrides” pattern, _array_ merge lookup can be though
|
66
|
+
# of as “default with additions.”
|
67
|
+
# - _:hash_ - A hash merge lookup assembles a value from every matching level of the hierarchy. It retrieves all of
|
68
|
+
# the (hash) values for a given key, then merges the hashes into a single hash. Hash merge lookups will fail with
|
69
|
+
# an error if any of the values found in the data sources are strings or arrays. It only works when every value
|
70
|
+
# found is a hash. The actual merge behavior is determined by looking up the keys `:merge_behavior` and
|
71
|
+
# `:deep_merge_options` in the Hiera config. `:merge_behavior` can be set to `:deep`, :deeper` or `:native`
|
72
|
+
# (explained in detail below).
|
73
|
+
# - _{ deep merge options }_ - Configured values for `:merge_behavior` and `:deep_merge_options`will be completely
|
74
|
+
# ignored. Instead the _resolution_type_ will be a `:hash` merge where the `:merge_behavior` will be the value
|
75
|
+
# keyed by `:behavior` in the given hash and the `:deep_merge_options` will be the remaining top level entries of
|
76
|
+
# that same hash.
|
77
|
+
#
|
78
|
+
# Valid behaviors for the _:hash_ resolution type:
|
79
|
+
#
|
80
|
+
# - _native_ - Performs a simple hash-merge by overwriting keys of lower lookup priority.
|
81
|
+
# - _deeper_ - In a deeper hash merge, Hiera recursively merges keys and values in each source hash. For each key,
|
82
|
+
# if the value is:
|
83
|
+
# - only present in one source hash, it goes into the final hash.
|
84
|
+
# - a string/number/boolean and exists in two or more source hashes, the highest priority value goes into
|
85
|
+
# the final hash.
|
86
|
+
# - an array and exists in two or more source hashes, the values from each source are merged into a single
|
87
|
+
# array and de-duplicated (but not automatically flattened, as in an array merge lookup).
|
88
|
+
# - a hash and exists in two or more source hashes, the values from each source are recursively merged, as
|
89
|
+
# though they were source hashes.
|
90
|
+
# - mismatched between two or more source hashes, we haven’t validated the behavior. It should act as
|
91
|
+
# described in the deep_merge gem documentation.
|
92
|
+
# - _deep_ - In a deep hash merge, Hiera behaves the same as for _deeper_, except that when a string/number/boolean
|
93
|
+
# exists in two or more source hashes, the lowest priority value goes into the final hash. This is considered
|
94
|
+
# largely useless and should be avoided. Use _deeper_ instead.
|
95
|
+
#
|
96
|
+
# The _merge_ can be given as a hash with the mandatory key `:strategy` to denote the actual strategy. This
|
97
|
+
# is useful for the `:deeper` and `:deep` strategy since they can use additional options to control the behavior.
|
98
|
+
# The options can be passed as top level keys in the `merge` parameter when it is a given as a hash. Recognized
|
99
|
+
# options are:
|
100
|
+
#
|
101
|
+
# - 'knockout_prefix' Set to string value to signify prefix which deletes elements from existing element. Defaults is _undef_
|
102
|
+
# - 'sort_merged_arrays' Set to _true_ to sort all arrays that are merged together. Default is _false_
|
103
|
+
# - 'unpack_arrays' Set to string value used as a deliminator to join all array values and then split them again. Default is _undef_
|
104
|
+
# - 'merge_hash_arrays' Set to _true_ to merge hashes within arrays. Default is _false_
|
105
|
+
#
|
106
|
+
# @param key [String] The key to lookup
|
107
|
+
# @param default [Object,nil] The value to return when there is no match for _key_
|
108
|
+
# @param scope [#[],nil] The scope to use for the lookup
|
109
|
+
# @param order_override [#[]] An override that will considered the first source of lookup
|
110
|
+
# @param resolution_type [String,Hash<Symbol,String>] Symbolic resolution type or deep merge configuration
|
111
|
+
# @return [Object] The found value or the given _default_ value
|
112
|
+
def lookup(key, default, scope, order_override=nil, resolution_type=:priority)
|
113
|
+
Backend.lookup(key, default, scope, order_override, resolution_type)
|
114
|
+
end
|
115
|
+
end
|