puppet 4.9.2 → 4.9.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

@@ -65,6 +65,7 @@ class Hiera::PuppetFunction < Puppet::Functions::InternalFunction
65
65
  end
66
66
  lookup_invocation = Puppet::Pops::Lookup::Invocation.new(scope, {}, {})
67
67
  adapter = lookup_invocation.lookup_adapter
68
+ lookup_invocation.set_hiera_xxx_call
68
69
  lookup_invocation.set_global_only unless adapter.global_only? || adapter.has_environment_data_provider?(lookup_invocation)
69
70
  lookup_invocation.set_hiera_v3_location_overrides(override) unless override.nil? || override.is_a?(Array) && override.empty?
70
71
  Puppet::Pops::Lookup.lookup(key, nil, default, has_default, merge_type, lookup_invocation, &default_block)
@@ -0,0 +1,3 @@
1
+ require 'puppet/util/feature'
2
+
3
+ Puppet.features.add(:hiera_eyaml, :libs => ['hiera/backend/eyaml/parser/parser'])
@@ -0,0 +1,73 @@
1
+ # @since 5.0.0
2
+ #
3
+ Puppet::Functions.create_function(:eyaml_lookup_key) do
4
+ unless Puppet.features.hiera_eyaml?
5
+ raise Puppet::DataBinding::LookupError, 'Lookup using eyaml lookup_key function is only supported when the hiera_eyaml library is present'
6
+ end
7
+
8
+ require 'hiera/backend/eyaml/encryptor'
9
+ require 'hiera/backend/eyaml/utils'
10
+ require 'hiera/backend/eyaml/options'
11
+ require 'hiera/backend/eyaml/parser/parser'
12
+
13
+ dispatch :eyaml_lookup_key do
14
+ param 'String[1]', :key
15
+ param 'Hash[String[1],Any]', :options
16
+ param 'Puppet::LookupContext', :context
17
+ end
18
+
19
+ def eyaml_lookup_key(key, options, context)
20
+ return context.cached_value(key) if context.cache_has_key(key)
21
+
22
+ # nil key is used to indicate that the cache contains the raw content of the eyaml file
23
+ raw_data = context.cached_value(nil)
24
+ if raw_data.nil?
25
+ options.each_pair do |k, v|
26
+ unless k == 'path'
27
+ Hiera::Backend::Eyaml::Options[k.to_sym] = v
28
+ context.explain { "Setting Eyaml option '#{k}' to '#{v}'" }
29
+ end
30
+ end
31
+ raw_data = load_data_hash(options)
32
+ context.cache(nil, raw_data)
33
+ end
34
+ context.not_found unless raw_data.include?(key)
35
+ context.cache(key, decrypt_value(raw_data[key], context))
36
+ end
37
+
38
+ def load_data_hash(options)
39
+ begin
40
+ data = YAML.load_file(options['path'])
41
+ Puppet::Pops::Lookup::HieraConfig.symkeys_to_string(data.is_a?(Hash) ? data : {})
42
+ rescue YAML::SyntaxError => ex
43
+ # Psych errors includes the absolute path to the file, so no need to add that
44
+ # to the message
45
+ raise Puppet::DataBinding::LookupError, "Unable to parse #{ex.message}"
46
+ end
47
+ end
48
+
49
+ def decrypt_value(value, context)
50
+ case value
51
+ when String
52
+ decrypt(value, context)
53
+ when Hash
54
+ result = {}
55
+ value.each_pair { |k, v| result[k] = decrypt_value(v, context) }
56
+ result
57
+ when Array
58
+ value.map { |v| decrypt_value(v, context) }
59
+ else
60
+ value
61
+ end
62
+ end
63
+
64
+ def decrypt(data, context)
65
+ return context.interpolate(data) unless encrypted?(data)
66
+ tokens = Hiera::Backend::Eyaml::Parser::ParserFactory.hiera_backend_parser.parse(data)
67
+ tokens.map(&:to_plain_text).join.chomp
68
+ end
69
+
70
+ def encrypted?(data)
71
+ /.*ENC\[.*?\]/ =~ data ? true : false
72
+ end
73
+ end
@@ -21,7 +21,18 @@ class GlobalDataProvider < ConfiguredDataProvider
21
21
  lookup_invocation.default_values,
22
22
  lookup_invocation.explainer)
23
23
  end
24
- merge = config.merge_strategy if merge.is_a?(DefaultMergeStrategy)
24
+
25
+ unless config.merge_strategy.is_a?(DefaultMergeStrategy)
26
+ if lookup_invocation.hiera_xxx_call?
27
+ # Merge strategy of the hiera_xxx call should only be applied when no merge strategy is defined in the hiera config
28
+ merge = config.merge_strategy
29
+ lookup_invocation.set_hiera_v3_merge_behavior
30
+ elsif merge.is_a?(DefaultMergeStrategy)
31
+ # For all other calls, the strategy of the call overrides the strategy defined in the hiera config
32
+ merge = config.merge_strategy
33
+ lookup_invocation.set_hiera_v3_merge_behavior
34
+ end
35
+ end
25
36
  end
26
37
  super(key, lookup_invocation, merge)
27
38
  end
@@ -44,6 +44,7 @@ class HieraConfig
44
44
  KEY_LOOKUP_KEY = LookupKeyFunctionProvider::TAG
45
45
  KEY_DATA_DIG = DataDigFunctionProvider::TAG
46
46
  KEY_V3_DATA_HASH = V3DataHashFunctionProvider::TAG
47
+ KEY_V3_LOOKUP_KEY = V3LookupKeyFunctionProvider::TAG
47
48
  KEY_V3_BACKEND = V3BackendFunctionProvider::TAG
48
49
  KEY_V4_DATA_HASH = V4DataHashFunctionProvider::TAG
49
50
  KEY_BACKEND = 'backend'.freeze
@@ -57,6 +58,7 @@ class HieraConfig
57
58
  KEY_LOOKUP_KEY => LookupKeyFunctionProvider,
58
59
  KEY_V3_DATA_HASH => V3DataHashFunctionProvider,
59
60
  KEY_V3_BACKEND => V3BackendFunctionProvider,
61
+ KEY_V3_LOOKUP_KEY => V3LookupKeyFunctionProvider,
60
62
  KEY_V4_DATA_HASH => V4DataHashFunctionProvider
61
63
  }
62
64
 
@@ -294,7 +296,7 @@ class HieraConfigV3 < HieraConfig
294
296
 
295
297
  [@config[KEY_BACKENDS]].flatten.each do |backend|
296
298
  raise Puppet::DataBinding::LookupError, "#{@config_path}: Backend '#{backend}' defined more than once" if data_providers.include?(backend)
297
- original_paths = @config[KEY_HIERARCHY]
299
+ original_paths = [@config[KEY_HIERARCHY]].flatten
298
300
  backend_config = @config[backend] || EMPTY_HASH
299
301
  datadir = Pathname(interpolate(backend_config[KEY_DATADIR] || default_datadir, lookup_invocation, false))
300
302
  ext = backend == 'hocon' ? '.conf' : ".#{backend}"
@@ -304,6 +306,8 @@ class HieraConfigV3 < HieraConfig
304
306
  create_data_provider(backend, parent_data_provider, KEY_V3_DATA_HASH, "#{backend}_data", { KEY_DATADIR => datadir }, paths)
305
307
  when backend == 'hocon' && Puppet.features.hocon?
306
308
  create_data_provider(backend, parent_data_provider, KEY_V3_DATA_HASH, 'hocon_data', { KEY_DATADIR => datadir }, paths)
309
+ when backend == 'eyaml' && Puppet.features.hiera_eyaml?
310
+ create_data_provider(backend, parent_data_provider, KEY_V3_LOOKUP_KEY, 'eyaml_lookup_key', backend_config.merge(KEY_DATADIR => datadir), paths)
307
311
  else
308
312
  create_hiera3_backend_provider(backend, backend, parent_data_provider, datadir, paths, @loaded_config)
309
313
  end
@@ -352,10 +356,8 @@ class HieraConfigV3 < HieraConfig
352
356
  def create_merge_strategy
353
357
  key = @config[KEY_MERGE_BEHAVIOR]
354
358
  case key
355
- when nil
359
+ when nil, 'native'
356
360
  MergeStrategy.strategy(nil)
357
- when 'native'
358
- MergeStrategy.strategy(:first)
359
361
  when 'array'
360
362
  MergeStrategy.strategy(:unique)
361
363
  when 'deep', 'deeper'
@@ -38,6 +38,8 @@ class Invocation
38
38
  else
39
39
  @name_stack = parent_invocation.name_stack
40
40
  @adapter_class = parent_invocation.adapter_class
41
+ set_hiera_xxx_call if parent_invocation.hiera_xxx_call?
42
+ set_hiera_v3_merge_behavior if parent_invocation.hiera_v3_merge_behavior?
41
43
  set_global_only if parent_invocation.global_only?
42
44
  povr = parent_invocation.hiera_v3_location_overrides
43
45
  set_hiera_v3_location_overrides(povr) unless povr.empty?
@@ -193,6 +195,24 @@ class Invocation
193
195
  lookup_adapter.global_hiera_config_path
194
196
  end
195
197
 
198
+ # @return [Boolean] `true` if the invocation stems from the hiera_xxx function family
199
+ def hiera_xxx_call?
200
+ instance_variable_defined?(:@hiera_xxx_call)
201
+ end
202
+
203
+ def set_hiera_xxx_call
204
+ @hiera_xxx_call = true
205
+ end
206
+
207
+ # @return [Boolean] `true` if the invocation stems from the hiera_xxx function family
208
+ def hiera_v3_merge_behavior?
209
+ instance_variable_defined?(:@hiera_v3_merge_behavior)
210
+ end
211
+
212
+ def set_hiera_v3_merge_behavior
213
+ @hiera_v3_merge_behavior = true
214
+ end
215
+
196
216
  # Overrides passed from hiera_xxx functions down to V3DataHashFunctionProvider
197
217
  def set_hiera_v3_location_overrides(overrides)
198
218
  @hiera_v3_location_overrides = [overrides].flatten unless overrides.nil?
@@ -35,8 +35,8 @@ module Lookup
35
35
 
36
36
  def expand_globs(datadir, declared_globs, lookup_invocation)
37
37
  declared_globs.map do |declared_glob|
38
- glob = interpolate(declared_glob, lookup_invocation, false)
39
- Pathname.glob(datadir, glob).reject { |path| path.directory? }.map { |path| ResolvedLocation.new(glob, path, true) }
38
+ glob = datadir + interpolate(declared_glob, lookup_invocation, false)
39
+ Pathname.glob(glob).reject { |path| path.directory? }.map { |path| ResolvedLocation.new(glob.to_s, path, true) }
40
40
  end.flatten
41
41
  end
42
42
 
@@ -14,17 +14,22 @@ class LookupKeyFunctionProvider < FunctionProvider
14
14
  # @return [Object] the found object
15
15
  # @throw :no_such_key when the object is not found
16
16
  def unchecked_key_lookup(key, lookup_invocation, merge)
17
+ root_key = key.root_key
17
18
  lookup_invocation.with(:data_provider, self) do
18
19
  MergeStrategy.strategy(merge).lookup(locations, lookup_invocation) do |location|
19
- if location.nil?
20
- value = lookup_key(key.root_key, lookup_invocation, nil, merge)
21
- lookup_invocation.report_found(key.root_key, validate_data_value(self, value))
22
- else
23
- lookup_invocation.with(:location, location) do
24
- value = lookup_key(key.root_key, lookup_invocation, location.location, merge)
25
- lookup_invocation.report_found(key.root_key, validate_data_value(self, value))
26
- end
27
- end
20
+ invoke_with_location(lookup_invocation, location, root_key, merge)
21
+ end
22
+ end
23
+ end
24
+
25
+ def invoke_with_location(lookup_invocation, location, root_key, merge)
26
+ if location.nil?
27
+ value = lookup_key(root_key, lookup_invocation, nil, merge)
28
+ lookup_invocation.report_found(root_key, validate_data_value(self, value))
29
+ else
30
+ lookup_invocation.with(:location, location) do
31
+ value = lookup_key(root_key, lookup_invocation, location.location, merge)
32
+ lookup_invocation.report_found(root_key, validate_data_value(self, value))
28
33
  end
29
34
  end
30
35
  end
@@ -52,12 +57,43 @@ class LookupKeyFunctionProvider < FunctionProvider
52
57
  end
53
58
  end
54
59
 
60
+ # @api private
61
+ class V3LookupKeyFunctionProvider < LookupKeyFunctionProvider
62
+ TAG = 'v3_lookup_key'.freeze
63
+
64
+ def initialize(name, parent_data_provider, function_name, options, locations)
65
+ @datadir = options.delete(HieraConfig::KEY_DATADIR)
66
+ super
67
+ end
68
+
69
+ def unchecked_key_lookup(key, lookup_invocation, merge)
70
+ extra_paths = lookup_invocation.hiera_v3_location_overrides
71
+ if extra_paths.nil? || extra_paths.empty?
72
+ super
73
+ else
74
+ # Extra paths provided. Must be resolved and placed in front of known paths
75
+ paths = parent_data_provider.config(lookup_invocation).resolve_paths(@datadir, extra_paths, lookup_invocation, false, ".#{@name}")
76
+ all_locations = paths + locations
77
+ root_key = key.root_key
78
+ lookup_invocation.with(:data_provider, self) do
79
+ MergeStrategy.strategy(merge).lookup(all_locations, lookup_invocation) do |location|
80
+ invoke_with_location(lookup_invocation, location, root_key, merge)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
55
87
  class V3BackendFunctionProvider < LookupKeyFunctionProvider
56
88
  TAG = 'hiera3_backend'.freeze
57
89
 
58
90
  def lookup_key(key, lookup_invocation, location, merge)
59
91
  @backend ||= instantiate_backend(lookup_invocation)
60
- @backend.lookup(key, lookup_invocation.scope, lookup_invocation.hiera_v3_location_overrides, convert_merge(merge), context = {:recurse_guard => nil})
92
+ config = parent_data_provider.config(lookup_invocation)
93
+
94
+ # Never pass hiera.yaml defined merge_behavior down to the backend. It will pick it up from the config
95
+ resolution_type = lookup_invocation.hiera_v3_merge_behavior? ? nil : convert_merge(merge)
96
+ @backend.lookup(key, lookup_invocation.scope, lookup_invocation.hiera_v3_location_overrides, resolution_type, context = {:recurse_guard => nil})
61
97
  end
62
98
 
63
99
  private
@@ -19,7 +19,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
19
19
  # eventually become this Puppet::Type::Package::ProviderRpm class.
20
20
  # The query format by which we identify installed packages
21
21
  self::NEVRA_FORMAT = %Q{%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\n}
22
- self::NEVRA_REGEX = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$}
22
+ self::NEVRA_REGEX = %r{^'?(\S+) (\S+) (\S+) (\S+) (\S+)$}
23
23
  self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]
24
24
 
25
25
  ARCH_LIST = [
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
  module Puppet
10
- PUPPETVERSION = '4.9.2'
10
+ PUPPETVERSION = '4.9.3'
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and
@@ -29,10 +29,10 @@ class SemVer < Numeric
29
29
  SemVer.new(pre(r), suppress_deprecation_warning) .. SemVer.new(r, suppress_deprecation_warning)
30
30
  when SemVer::SIMPLE_RANGE
31
31
  r += ".0" unless SemVer.valid?(r.gsub(/x/i, '0'))
32
- SemVer.new(r.gsub(/x/i, '0'))...SemVer.new(r.gsub(/(\d+)\.x/i) { "#{$1.to_i + 1}.0" } + '-', suppress_deprecation_warning)
32
+ SemVer.new(r.gsub(/x/i, '0'), suppress_deprecation_warning)...SemVer.new(r.gsub(/(\d+)\.x/i) { "#{$1.to_i + 1}.0" } + '-', suppress_deprecation_warning)
33
33
  when /\s+-\s+/
34
34
  a, b = r.split(/\s+-\s+/)
35
- SemVer.new(pre(a)) .. SemVer.new(b, suppress_deprecation_warning)
35
+ SemVer.new(pre(a), suppress_deprecation_warning) .. SemVer.new(b, suppress_deprecation_warning)
36
36
  when /^~/
37
37
  ver = r.sub(/~/, '').split('.').map(&:to_i)
38
38
  start = (ver + [0] * (3 - ver.length)).join('.')
@@ -8,107 +8,123 @@ describe 'when calling' do
8
8
 
9
9
  let(:global_dir) { tmpdir('global') }
10
10
  let(:env_config) { {} }
11
- let(:global_files) do
11
+ let(:hiera_yaml) { <<-YAML.unindent }
12
+ ---
13
+ :backends:
14
+ - yaml
15
+ - custom
16
+ :yaml:
17
+ :datadir: #{global_dir}/hieradata
18
+ :hierarchy:
19
+ - first
20
+ - second
21
+ YAML
22
+
23
+ let(:ruby_stuff_files) do
24
+ {
25
+ 'hiera' => {
26
+ 'backend' => {
27
+ 'custom_backend.rb' => <<-RUBY.unindent
28
+ class Hiera::Backend::Custom_backend
29
+ def initialize(cache = nil)
30
+ Hiera.debug('Custom_backend starting')
31
+ end
32
+
33
+ def lookup(key, scope, order_override, resolution_type, context)
34
+ case key
35
+ when 'datasources'
36
+ Hiera::Backend.datasources(scope, order_override) { |source| source }
37
+ when 'resolution_type'
38
+ "resolution_type=\#{resolution_type}"
39
+ else
40
+ throw :no_such_key
41
+ end
42
+ end
43
+ end
44
+ RUBY
45
+ }
46
+ }
47
+ }
48
+ end
49
+
50
+ let(:hieradata_files) do
12
51
  {
13
- 'hiera.yaml' => <<-YAML.unindent,
52
+ 'first.yaml' => <<-YAML.unindent,
14
53
  ---
15
- :backends:
16
- - yaml
17
- - custom
18
- :yaml:
19
- :datadir: #{global_dir}/hieradata
20
- :hierarchy:
21
- - first
22
- - second
54
+ a: first a
55
+ class_name: "-- %{calling_class} --"
56
+ class_path: "-- %{calling_class_path} --"
57
+ module: "-- %{calling_module} --"
58
+ mod_name: "-- %{module_name} --"
59
+ database_user:
60
+ name: postgres
61
+ uid: 500
62
+ gid: 500
63
+ b:
64
+ b1: first b1
65
+ b2: first b2
66
+ fbb:
67
+ - mod::foo
68
+ - mod::bar
69
+ - mod::baz
70
+ empty_array: []
23
71
  YAML
24
- 'ruby_stuff' => {
25
- 'hiera' => {
26
- 'backend' => {
27
- 'custom_backend.rb' => <<-RUBY.unindent
28
- class Hiera::Backend::Custom_backend
29
- def initialize(cache = nil)
30
- Hiera.debug('Custom_backend starting')
31
- end
32
-
33
- def lookup(key, scope, order_override, resolution_type, context)
34
- case key
35
- when 'datasources'
36
- Hiera::Backend.datasources(scope, order_override) { |source| source }
37
- else
38
- throw :no_such_key
39
- end
40
- end
41
- end
42
- RUBY
43
- }
44
- }
45
- },
46
- 'hieradata' => {
47
- 'first.yaml' => <<-YAML.unindent,
48
- ---
49
- a: first a
50
- class_name: "-- %{calling_class} --"
51
- class_path: "-- %{calling_class_path} --"
52
- module: "-- %{calling_module} --"
53
- mod_name: "-- %{module_name} --"
54
- database_user:
55
- name: postgres
56
- uid: 500
57
- gid: 500
58
- b:
59
- b1: first b1
60
- b2: first b2
61
- fbb:
62
- - mod::foo
63
- - mod::bar
64
- - mod::baz
65
- empty_array: []
66
- YAML
67
- 'second.yaml' => <<-YAML.unindent,
68
- ---
69
- a: second a
70
- b:
71
- b1: second b1
72
- b3: second b3
73
- YAML
74
- 'the_override.yaml' => <<-YAML.unindent
75
- ---
76
- key: foo_result
77
- YAML
78
- },
79
- 'environments' => {
80
- 'test' => {
81
- 'modules' => {
82
- 'mod' => {
83
- 'manifests' => {
84
- 'foo.pp' => <<-PUPPET.unindent,
85
- class mod::foo {
86
- notice(hiera('class_name'))
87
- notice(hiera('class_path'))
88
- notice(hiera('module'))
89
- notice(hiera('mod_name'))
90
- }
91
- PUPPET
92
- 'bar.pp' => <<-PUPPET.unindent,
93
- class mod::bar {}
94
- PUPPET
95
- 'baz.pp' => <<-PUPPET.unindent
96
- class mod::baz {}
97
- PUPPET
72
+ 'second.yaml' => <<-YAML.unindent,
73
+ ---
74
+ a: second a
75
+ b:
76
+ b1: second b1
77
+ b3: second b3
78
+ YAML
79
+ 'the_override.yaml' => <<-YAML.unindent
80
+ ---
81
+ key: foo_result
82
+ YAML
83
+ }
84
+ end
85
+
86
+ let(:environment_files) do
87
+ {
88
+ 'test' => {
89
+ 'modules' => {
90
+ 'mod' => {
91
+ 'manifests' => {
92
+ 'foo.pp' => <<-PUPPET.unindent,
93
+ class mod::foo {
94
+ notice(hiera('class_name'))
95
+ notice(hiera('class_path'))
96
+ notice(hiera('module'))
97
+ notice(hiera('mod_name'))
98
+ }
99
+ PUPPET
100
+ 'bar.pp' => <<-PUPPET.unindent,
101
+ class mod::bar {}
102
+ PUPPET
103
+ 'baz.pp' => <<-PUPPET.unindent
104
+ class mod::baz {}
105
+ PUPPET
98
106
  },
99
- 'hiera.yaml' => <<-YAML.unindent,
100
- ---
101
- version: 5
107
+ 'hiera.yaml' => <<-YAML.unindent,
108
+ ---
109
+ version: 5
110
+ YAML
111
+ 'data' => {
112
+ 'common.yaml' => <<-YAML.unindent
113
+ mod::c: mod::c (from module)
102
114
  YAML
103
- 'data' => {
104
- 'common.yaml' => <<-YAML.unindent
105
- mod::c: mod::c (from module)
106
- YAML
107
- }
108
115
  }
109
116
  }
110
117
  }
111
- }
118
+ }.merge(env_config)
119
+ }
120
+ end
121
+
122
+ let(:global_files) do
123
+ {
124
+ 'hiera.yaml' => hiera_yaml,
125
+ 'ruby_stuff' => ruby_stuff_files,
126
+ 'hieradata' => hieradata_files,
127
+ 'environments' => environment_files
112
128
  }
113
129
  end
114
130
 
@@ -129,7 +145,7 @@ describe 'when calling' do
129
145
  around(:each) do |example|
130
146
  # Faking the load path to enable 'require' to load from 'ruby_stuff'. It removes the need for a static fixture
131
147
  # for the custom backend
132
- dir_contained_in(global_dir, DeepMerge.deep_merge!(global_files, { 'environments' => { 'test' => env_config } } ))
148
+ dir_contained_in(global_dir, global_files)
133
149
  $LOAD_PATH.unshift(File.join(global_dir, 'ruby_stuff'))
134
150
  begin
135
151
  Puppet.override(:environments => environments, :current_environment => env) do
@@ -332,4 +348,70 @@ describe 'when calling' do
332
348
  expect(func('foo') { |k| ['mod::bar', "mod::#{k}"] }.map { |c| c.class_name }).to eql(%w[mod::bar mod::foo])
333
349
  end
334
350
  end
351
+
352
+ context 'with merge_behavior declared in hiera.yaml' do
353
+ let(:hiera_yaml) do
354
+ <<-YAML.unindent
355
+ ---
356
+ :backends:
357
+ - yaml
358
+ - custom
359
+ :yaml:
360
+ :datadir: #{global_dir}/hieradata
361
+ :hierarchy:
362
+ - other
363
+ - common
364
+ :merge_behavior: deeper
365
+ YAML
366
+ end
367
+
368
+ let(:global_files) do
369
+ {
370
+ 'hiera.yaml' => hiera_yaml,
371
+ 'hieradata' => {
372
+ 'common.yaml' => <<-YAML.unindent,
373
+ dm:
374
+ dm1:
375
+ dm11: value of dm11 (from common)
376
+ dm12: value of dm12 (from common)
377
+ dm2:
378
+ dm21: value of dm21 (from common)
379
+ YAML
380
+ 'other.yaml' => <<-YAML.unindent,
381
+ dm:
382
+ dm1:
383
+ dm11: value of dm11 (from other)
384
+ dm13: value of dm13 (from other)
385
+ dm3:
386
+ dm31: value of dm31 (from other)
387
+ YAML
388
+ },
389
+ 'ruby_stuff' => ruby_stuff_files
390
+ }
391
+ end
392
+
393
+ context 'hiera_hash' do
394
+ let(:the_func) { Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'hiera_hash') }
395
+
396
+ it 'the imposed hash strategy does not override declared merge_behavior' do
397
+ expect(func('dm')).to eql({
398
+ 'dm1' => {
399
+ 'dm11' => 'value of dm11 (from other)',
400
+ 'dm12' => 'value of dm12 (from common)',
401
+ 'dm13' => 'value of dm13 (from other)'
402
+ },
403
+ 'dm2' => {
404
+ 'dm21' => 'value of dm21 (from common)'
405
+ },
406
+ 'dm3' => {
407
+ 'dm31' => 'value of dm31 (from other)'
408
+ }
409
+ })
410
+ end
411
+
412
+ it "the merge behavior is not propagated to a custom backend as 'resolution_type'" do
413
+ expect(func('resolution_type')).to eql('resolution_type=')
414
+ end
415
+ end
416
+ end
335
417
  end