hiera 2.0.0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +202 -0
  3. data/LICENSE +18 -0
  4. data/README.md +276 -0
  5. data/bin/hiera +248 -0
  6. data/lib/hiera/backend/json_backend.rb +58 -0
  7. data/lib/hiera/backend/yaml_backend.rb +63 -0
  8. data/lib/hiera/backend.rb +325 -0
  9. data/lib/hiera/config.rb +90 -0
  10. data/lib/hiera/console_logger.rb +13 -0
  11. data/lib/hiera/error.rb +4 -0
  12. data/lib/hiera/fallback_logger.rb +41 -0
  13. data/lib/hiera/filecache.rb +86 -0
  14. data/lib/hiera/interpolate.rb +98 -0
  15. data/lib/hiera/noop_logger.rb +8 -0
  16. data/lib/hiera/puppet_logger.rb +17 -0
  17. data/lib/hiera/recursive_guard.rb +20 -0
  18. data/lib/hiera/util.rb +47 -0
  19. data/lib/hiera/version.rb +89 -0
  20. data/lib/hiera.rb +115 -0
  21. data/spec/spec_helper.rb +78 -0
  22. data/spec/unit/backend/json_backend_spec.rb +85 -0
  23. data/spec/unit/backend/yaml_backend_spec.rb +138 -0
  24. data/spec/unit/backend_spec.rb +743 -0
  25. data/spec/unit/config_spec.rb +118 -0
  26. data/spec/unit/console_logger_spec.rb +19 -0
  27. data/spec/unit/fallback_logger_spec.rb +80 -0
  28. data/spec/unit/filecache_spec.rb +142 -0
  29. data/spec/unit/fixtures/interpolate/config/hiera.yaml +6 -0
  30. data/spec/unit/fixtures/interpolate/data/niltest.yaml +2 -0
  31. data/spec/unit/fixtures/interpolate/data/recursive.yaml +3 -0
  32. data/spec/unit/fixtures/override/config/hiera.yaml +5 -0
  33. data/spec/unit/fixtures/override/data/alternate.yaml +1 -0
  34. data/spec/unit/fixtures/override/data/common.yaml +2 -0
  35. data/spec/unit/hiera_spec.rb +81 -0
  36. data/spec/unit/interpolate_spec.rb +36 -0
  37. data/spec/unit/puppet_logger_spec.rb +31 -0
  38. data/spec/unit/util_spec.rb +49 -0
  39. data/spec/unit/version_spec.rb +44 -0
  40. metadata +128 -0
@@ -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
@@ -0,0 +1,13 @@
1
+ class Hiera
2
+ module Console_logger
3
+ class << self
4
+ def warn(msg)
5
+ STDERR.puts("WARN: %s: %s" % [Time.now.to_s, msg])
6
+ end
7
+
8
+ def debug(msg)
9
+ STDERR.puts("DEBUG: %s: %s" % [Time.now.to_s, msg])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ class Hiera
2
+ class Error < StandardError; end
3
+ class InvalidConfigurationError < Error; end
4
+ end
@@ -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,8 @@
1
+ class Hiera
2
+ module Noop_logger
3
+ class << self
4
+ def warn(msg);end
5
+ def debug(msg);end
6
+ end
7
+ end
8
+ 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