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.
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,743 @@
1
+ require 'spec_helper'
2
+ require 'hiera/util'
3
+
4
+ class Hiera
5
+ module Backend
6
+ class Backend1x_backend
7
+ def lookup(key, scope, order_override, resolution_type)
8
+ ["a", "b"]
9
+ end
10
+ end
11
+ end
12
+
13
+ describe Backend do
14
+ describe "#datadir" do
15
+ it "interpolates any values in the configured value" do
16
+ Config.load({:rspec => {:datadir => "/tmp/%{interpolate}"}})
17
+
18
+ dir = Backend.datadir(:rspec, { "interpolate" => "my_data" })
19
+
20
+ dir.should == "/tmp/my_data"
21
+ end
22
+
23
+ it "defaults to a directory in var" do
24
+ Config.load({})
25
+ Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
26
+
27
+ Config.load({:rspec => nil})
28
+ Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
29
+
30
+ Config.load({:rspec => {}})
31
+ Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
32
+ end
33
+
34
+ it "fails when the datadir is an array" do
35
+ Config.load({:rspec => {:datadir => []}})
36
+
37
+ expect do
38
+ Backend.datadir(:rspec, {})
39
+ end.to raise_error(Hiera::InvalidConfigurationError, /datadir for rspec cannot be an array/)
40
+ end
41
+ end
42
+
43
+ describe "#datafile" do
44
+ it "translates a non-existant datafile into nil" do
45
+ Hiera.expects(:debug).with("Cannot find datafile /nonexisting/test.yaml, skipping")
46
+ Backend.expects(:datadir).returns("/nonexisting")
47
+ Backend.datafile(:yaml, {}, "test", "yaml").should == nil
48
+ end
49
+
50
+ it "concatenates the datadir and datafile and format to produce the full datafile filename" do
51
+ Backend.expects(:datadir).returns("/nonexisting")
52
+ File.expects(:exist?).with("/nonexisting/test.yaml").returns(true)
53
+ Backend.datafile(:yaml, {}, "test", "yaml").should == "/nonexisting/test.yaml"
54
+ end
55
+ end
56
+
57
+ describe "#datasources" do
58
+ it "iterates over the datasources in the order of the given hierarchy" do
59
+ expected = ["one", "two"]
60
+ Backend.datasources({}, nil, ["one", "two"]) do |backend|
61
+ backend.should == expected.delete_at(0)
62
+ end
63
+
64
+ expected.empty?.should == true
65
+ end
66
+
67
+ it "uses the configured hierarchy no specific hierarchy is given" do
68
+ Config.load(:hierarchy => "test")
69
+
70
+ Backend.datasources({}) do |backend|
71
+ backend.should == "test"
72
+ end
73
+ end
74
+
75
+ it "defaults to a hierarchy of only 'common' if not configured or given" do
76
+ Config.load({})
77
+
78
+ Backend.datasources({}) do |backend|
79
+ backend.should == "common"
80
+ end
81
+ end
82
+
83
+ it "prefixes the hierarchy with the override if an override is provided" do
84
+ Config.load({})
85
+
86
+ expected = ["override", "common"]
87
+ Backend.datasources({}, "override") do |backend|
88
+ backend.should == expected.delete_at(0)
89
+ end
90
+
91
+ expected.empty?.should == true
92
+ end
93
+
94
+ it "parses the names of the hierarchy levels using the given scope" do
95
+ Backend.expects(:parse_string).with("common", {:rspec => :tests}, {}, {:order_override => nil})
96
+ Backend.datasources({:rspec => :tests}) { }
97
+ end
98
+
99
+ it "defaults to 'common' if the hierarchy contains no hierarchies with non-empty names" do
100
+ Config.load({})
101
+
102
+ expected = ["common"]
103
+ Backend.datasources({}, "%{rspec}") do |backend|
104
+ backend.should == expected.delete_at(0)
105
+ end
106
+
107
+ expected.empty?.should == true
108
+ end
109
+ end
110
+
111
+ describe "#parse_string" do
112
+ it "passes nil through untouched" do
113
+ Backend.parse_string(nil, {}).should == nil
114
+ end
115
+
116
+ it "does not modify the input data" do
117
+ data = "%{value}"
118
+ Backend.parse_string(data, { "value" => "replacement" })
119
+
120
+ data.should == "%{value}"
121
+ end
122
+
123
+ it "passes non-string data through untouched" do
124
+ input = { "not a" => "string" }
125
+
126
+ Backend.parse_string(input, {}).should == input
127
+ end
128
+
129
+ @scope_interpolation_tests = {
130
+ "replace %{part1} and %{part2}" =>
131
+ "replace value of part1 and value of part2",
132
+ "replace %{scope('part1')} and %{scope('part2')}" =>
133
+ "replace value of part1 and value of part2"
134
+ }
135
+
136
+ @scope_interpolation_tests.each do |input, expected|
137
+ it "replaces interpolations with data looked up in the scope" do
138
+ scope = {"part1" => "value of part1", "part2" => "value of part2"}
139
+
140
+ Backend.parse_string(input, scope).should == expected
141
+ end
142
+ end
143
+
144
+ it "replaces interpolations with data looked up in extra_data when scope does not contain the value" do
145
+ input = "test_%{rspec}_test"
146
+ Backend.parse_string(input, {}, {"rspec" => "extra"}).should == "test_extra_test"
147
+ end
148
+
149
+ it "prefers data from scope over data from extra_data" do
150
+ input = "test_%{rspec}_test"
151
+ Backend.parse_string(input, {"rspec" => "test"}, {"rspec" => "fail"}).should == "test_test_test"
152
+ end
153
+
154
+ @interprets_nil_in_scope_tests = {
155
+ "test_%{rspec}_test" => "test__test",
156
+ "test_%{scope('rspec')}_test" => "test__test"
157
+ }
158
+
159
+ @interprets_nil_in_scope_tests.each do |input, expected|
160
+ it "interprets nil in scope as a non-value" do
161
+ Backend.parse_string(input, {"rspec" => nil}).should == expected
162
+ end
163
+ end
164
+
165
+ @interprets_false_in_scope_tests = {
166
+ "test_%{rspec}_test" => "test_false_test",
167
+ "test_%{scope('rspec')}_test" => "test_false_test"
168
+ }
169
+
170
+ @interprets_false_in_scope_tests.each do |input, expected|
171
+ it "interprets false in scope as a real value" do
172
+ input = "test_%{scope('rspec')}_test"
173
+ Backend.parse_string(input, {"rspec" => false}).should == expected
174
+ end
175
+ end
176
+
177
+ it "interprets false in extra_data as a real value" do
178
+ input = "test_%{rspec}_test"
179
+ Backend.parse_string(input, {}, {"rspec" => false}).should == "test_false_test"
180
+ end
181
+
182
+ it "interprets nil in extra_data as a non-value" do
183
+ input = "test_%{rspec}_test"
184
+ Backend.parse_string(input, {}, {"rspec" => nil}).should == "test__test"
185
+ end
186
+
187
+ @interprets_undefined_in_scope_tests = {
188
+ "test_%{rspec}_test" => "test__test",
189
+ "test_%{scope('rspec')}_test" => "test__test"
190
+ }
191
+
192
+ @exact_lookup_tests = {
193
+ "test_%{::rspec::data}_test" => "test_value_test",
194
+ "test_%{scope('::rspec::data')}_test" => "test_value_test"
195
+ }
196
+
197
+ @exact_lookup_tests.each do |input, expected|
198
+ it "looks up the interpolated value exactly as it appears in the input" do
199
+ Backend.parse_string(input, {"::rspec::data" => "value"}).should == expected
200
+ end
201
+ end
202
+
203
+ @surrounding_whitespace_tests = {
204
+ "test_%{\trspec::data }_test" => "test_value_test",
205
+ "test_%{scope('\trspec::data ')}_test" => "test_value_test"
206
+ }
207
+ @surrounding_whitespace_tests.each do |input, expected|
208
+ it "does not remove any surrounding whitespace when parsing the key to lookup" do
209
+ Backend.parse_string(input, {"\trspec::data " => "value"}).should == expected
210
+ end
211
+ end
212
+
213
+ @leading_double_colon_tests = {
214
+ "test_%{::rspec::data}_test" => "test__test",
215
+ "test_%{scope('::rspec::data')}_test" => "test__test"
216
+ }
217
+
218
+ @leading_double_colon_tests.each do |input, expected|
219
+ it "does not try removing leading :: when a full lookup fails (#17434)" do
220
+ Backend.parse_string(input, {"rspec::data" => "value"}).should == expected
221
+ end
222
+ end
223
+
224
+ @double_colon_key_tests = {
225
+ "test_%{::rspec::data}_test" => "test__test",
226
+ "test_%{scope('::rspec::data')}_test" => "test__test"
227
+ }
228
+ @double_colon_key_tests.each do |input, expected|
229
+ it "does not try removing leading sections separated by :: when a full lookup fails (#17434)" do
230
+ Backend.parse_string(input, {"data" => "value"}).should == expected
231
+ end
232
+ end
233
+
234
+ it "does not try removing unknown, preceeding characters when looking up values" do
235
+ input = "test_%{$var}_test"
236
+ Backend.parse_string(input, {"$var" => "value"}).should == "test_value_test"
237
+ end
238
+
239
+ it "looks up recursively" do
240
+ scope = {"rspec" => "%{first}", "first" => "%{last}", "last" => "final"}
241
+ input = "test_%{rspec}_test"
242
+ Backend.parse_string(input, scope).should == "test_final_test"
243
+ end
244
+
245
+ it "raises an error if the recursive lookup results in an infinite loop" do
246
+ scope = {"first" => "%{second}", "second" => "%{first}"}
247
+ input = "test_%{first}_test"
248
+ expect do
249
+ Backend.parse_string(input, scope)
250
+ end.to raise_error Hiera::InterpolationLoop, "Detected in [first, second]"
251
+ end
252
+
253
+ it "replaces repeated occurances of the same lookup" do
254
+ scope = {"rspec" => "value"}
255
+ input = "it replaces %{rspec} and %{rspec}"
256
+ Backend.parse_string(input, scope).should == "it replaces value and value"
257
+ end
258
+
259
+ it "replaces hiera interpolations with data looked up in hiera" do
260
+ input = "%{hiera('key1')}"
261
+ scope = {}
262
+ Config.load({:yaml => {:datadir => "/tmp"}})
263
+ Config.load_backends
264
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("key1", scope, nil, :priority, instance_of(Hash)).returns("answer")
265
+
266
+ Backend.parse_string(input, scope).should == "answer"
267
+ end
268
+
269
+ it "interpolation passes the order_override back into the backend" do
270
+ Backend.expects(:lookup).with("lookup::key", nil, {}, "order_override_datasource", :priority, instance_of(Hash))
271
+ Backend.parse_string("%{hiera('lookup::key')}", {}, {}, {:order_override => "order_override_datasource"})
272
+ end
273
+
274
+ it "replaces literal interpolations with their argument" do
275
+ scope = {}
276
+ input = "%{literal('%')}{rspec::data}"
277
+ Backend.parse_string(input, scope).should == "%{rspec::data}"
278
+ end
279
+ end
280
+
281
+ describe "#parse_answer" do
282
+ it "interpolates values in strings" do
283
+ input = "test_%{rspec}_test"
284
+ Backend.parse_answer(input, {"rspec" => "test"}).should == "test_test_test"
285
+ end
286
+
287
+ it "interpolates each string in an array" do
288
+ input = ["test_%{rspec}_test", "test_%{rspec}_test", ["test_%{rspec}_test"]]
289
+ Backend.parse_answer(input, {"rspec" => "test"}).should == ["test_test_test", "test_test_test", ["test_test_test"]]
290
+ end
291
+
292
+ it "interpolates each string in a hash" do
293
+ input = {"foo" => "test_%{rspec}_test", "bar" => "test_%{rspec}_test"}
294
+ Backend.parse_answer(input, {"rspec" => "test"}).should == {"foo"=>"test_test_test", "bar"=>"test_test_test"}
295
+ end
296
+
297
+ it "interpolates string in hash keys" do
298
+ input = {"%{rspec}" => "test"}
299
+ Backend.parse_answer(input, {"rspec" => "foo"}).should == {"foo"=>"test"}
300
+ end
301
+
302
+ it "interpolates strings in nested hash keys" do
303
+ input = {"topkey" => {"%{rspec}" => "test"}}
304
+ Backend.parse_answer(input, {"rspec" => "foo"}).should == {"topkey"=>{"foo" => "test"}}
305
+ end
306
+
307
+ it "interpolates strings in a mixed structure of arrays and hashes" do
308
+ input = {"foo" => "test_%{rspec}_test", "bar" => ["test_%{rspec}_test", "test_%{rspec}_test"]}
309
+ Backend.parse_answer(input, {"rspec" => "test"}).should == {"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}
310
+ end
311
+
312
+ it "interpolates hiera lookups values in strings" do
313
+ input = "test_%{hiera('rspec')}_test"
314
+ scope = {}
315
+ Config.load({:yaml => {:datadir => "/tmp"}})
316
+ Config.load_backends
317
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test")
318
+ Backend.parse_answer(input, scope).should == "test_test_test"
319
+ end
320
+
321
+ it "interpolates alias lookups with non-string types" do
322
+ input = "%{alias('rspec')}"
323
+ scope = {}
324
+ Config.load({:yaml => {:datadir => "/tmp"}})
325
+ Config.load_backends
326
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test'])
327
+ Backend.parse_answer(input, scope).should == ['test', 'test']
328
+ end
329
+
330
+ it 'fails if alias interpolation is attempted in a string context with a prefix' do
331
+ input = "stuff_before%{alias('rspec')}"
332
+ scope = {}
333
+ Config.load({:yaml => {:datadir => "/tmp"}})
334
+ Config.load_backends
335
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test'])
336
+ expect do
337
+ Backend.parse_answer(input, scope).should == ['test', 'test']
338
+ end.to raise_error(Hiera::InterpolationInvalidValue, 'Cannot call alias in the string context')
339
+ end
340
+
341
+ it 'fails if alias interpolation is attempted in a string context with a postfix' do
342
+ input = "%{alias('rspec')}_stiff after"
343
+ scope = {}
344
+ Config.load({:yaml => {:datadir => "/tmp"}})
345
+ Config.load_backends
346
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns(['test', 'test'])
347
+ expect do
348
+ Backend.parse_answer(input, scope).should == ['test', 'test']
349
+ end.to raise_error(Hiera::InterpolationInvalidValue, 'Cannot call alias in the string context')
350
+ end
351
+
352
+ it "interpolates hiera lookups in each string in an array" do
353
+ input = ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test", ["test_%{hiera('rspec')}_test"]]
354
+ scope = {}
355
+ Config.load({:yaml => {:datadir => "/tmp"}})
356
+ Config.load_backends
357
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test")
358
+ Backend.parse_answer(input, scope).should == ["test_test_test", "test_test_test", ["test_test_test"]]
359
+ end
360
+
361
+ it "interpolates hiera lookups in each string in a hash" do
362
+ input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{hiera('rspec')}_test"}
363
+ scope = {}
364
+ Config.load({:yaml => {:datadir => "/tmp"}})
365
+ Config.load_backends
366
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test")
367
+ Backend.parse_answer(input, scope).should == {"foo"=>"test_test_test", "bar"=>"test_test_test"}
368
+ end
369
+
370
+ it "interpolates hiera lookups in string in hash keys" do
371
+ input = {"%{hiera('rspec')}" => "test"}
372
+ scope = {}
373
+ Config.load({:yaml => {:datadir => "/tmp"}})
374
+ Config.load_backends
375
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("foo")
376
+ Backend.parse_answer(input, scope).should == {"foo"=>"test"}
377
+ end
378
+
379
+ it "interpolates hiera lookups in strings in nested hash keys" do
380
+ input = {"topkey" => {"%{hiera('rspec')}" => "test"}}
381
+ scope = {}
382
+ Config.load({:yaml => {:datadir => "/tmp"}})
383
+ Config.load_backends
384
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("foo")
385
+ Backend.parse_answer(input, scope).should == {"topkey"=>{"foo" => "test"}}
386
+ end
387
+
388
+ it "interpolates hiera lookups in strings in a mixed structure of arrays and hashes" do
389
+ input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test"]}
390
+ scope = {}
391
+ Config.load({:yaml => {:datadir => "/tmp"}})
392
+ Config.load_backends
393
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("test")
394
+ Backend.parse_answer(input, scope).should == {"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}
395
+ end
396
+
397
+ it "interpolates hiera lookups and scope lookups in the same string" do
398
+ input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{rspec2}_test"}
399
+ scope = {"rspec2" => "scope_rspec"}
400
+ Config.load({:yaml => {:datadir => "/tmp"}})
401
+ Config.load_backends
402
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("hiera_rspec")
403
+ Backend.parse_answer(input, scope).should == {"foo"=>"test_hiera_rspec_test", "bar"=>"test_scope_rspec_test"}
404
+ end
405
+
406
+ it "interpolates hiera and scope lookups with the same lookup query in a single string" do
407
+ input = "test_%{hiera('rspec')}_test_%{rspec}"
408
+ scope = {"rspec" => "scope_rspec"}
409
+ Config.load({:yaml => {:datadir => "/tmp"}})
410
+ Config.load_backends
411
+ Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority, instance_of(Hash)).returns("hiera_rspec")
412
+ Backend.parse_answer(input, scope).should == "test_hiera_rspec_test_scope_rspec"
413
+ end
414
+
415
+ it "passes integers unchanged" do
416
+ input = 1
417
+ Backend.parse_answer(input, {"rspec" => "test"}).should == 1
418
+ end
419
+
420
+ it "passes floats unchanged" do
421
+ input = 0.233
422
+ Backend.parse_answer(input, {"rspec" => "test"}).should == 0.233
423
+ end
424
+
425
+ it "passes the boolean true unchanged" do
426
+ input = true
427
+ Backend.parse_answer(input, {"rspec" => "test"}).should == true
428
+ end
429
+
430
+ it "passes the boolean false unchanged" do
431
+ input = false
432
+ Backend.parse_answer(input, {"rspec" => "test"}).should == false
433
+ end
434
+
435
+ it "interpolates lookups using single or double quotes" do
436
+ input = "test_%{scope(\"rspec\")}_test_%{scope('rspec')}"
437
+ scope = {"rspec" => "scope_rspec"}
438
+ Backend.parse_answer(input, scope).should == "test_scope_rspec_test_scope_rspec"
439
+ end
440
+ end
441
+
442
+ describe "#resolve_answer" do
443
+ it "flattens and removes duplicate values from arrays during an array lookup" do
444
+ Backend.resolve_answer(["foo", ["foo", "foo"], "bar"], :array).should == ["foo", "bar"]
445
+ end
446
+
447
+ it "returns the data unchanged during a priority lookup" do
448
+ Backend.resolve_answer(["foo", ["foo", "foo"], "bar"], :priority).should == ["foo", ["foo", "foo"], "bar"]
449
+ end
450
+ end
451
+
452
+ describe "#lookup" do
453
+ before do
454
+ Hiera.stubs(:debug)
455
+ Hiera.stubs(:warn)
456
+ end
457
+
458
+ it "caches loaded backends" do
459
+ Backend.clear!
460
+ Hiera.expects(:debug).with(regexp_matches(/Hiera YAML backend starting/)).once
461
+
462
+ Config.load({:yaml => {:datadir => "/tmp"}})
463
+ Config.load_backends
464
+
465
+ Backend.lookup("key", "default", {}, nil, nil)
466
+ Backend.lookup("key", "default", {}, nil, nil)
467
+ end
468
+
469
+ it "returns the answer from the backend" do
470
+ Config.load({:yaml => {:datadir => "/tmp"}})
471
+ Config.load_backends
472
+
473
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, nil, instance_of(Hash)).returns("answer")
474
+
475
+ Backend.lookup("key", "default", {}, nil, nil).should == "answer"
476
+ end
477
+
478
+ it "retains the datatypes as returned by the backend" do
479
+ Config.load({:yaml => {:datadir => "/tmp"}})
480
+ Config.load_backends
481
+
482
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("stringval", {}, nil, nil, instance_of(Hash)).returns("string")
483
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("boolval", {}, nil, nil, instance_of(Hash)).returns(false)
484
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("numericval", {}, nil, nil, instance_of(Hash)).returns(1)
485
+
486
+ Backend.lookup("stringval", "default", {}, nil, nil).should == "string"
487
+ Backend.lookup("boolval", "default", {}, nil, nil).should == false
488
+ Backend.lookup("numericval", "default", {}, nil, nil).should == 1
489
+ end
490
+
491
+ it "calls to all backends till an answer is found" do
492
+ backend = mock
493
+ backend.expects(:lookup).returns("answer")
494
+ Config.load({})
495
+ Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]})
496
+ Backend.instance_variable_set("@backends", {"rspec" => backend})
497
+ #Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil)
498
+ Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice
499
+
500
+ Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil).should == "answer"
501
+ end
502
+
503
+ it "calls to all backends till an answer is found when doing array lookups" do
504
+ backend = mock
505
+ backend.expects(:lookup).returns(["answer"])
506
+ Config.load({})
507
+ Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]})
508
+ Backend.instance_variable_set("@backends", {"rspec" => backend})
509
+ Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice
510
+
511
+ Backend.lookup("key", "notfound", {"rspec" => "test"}, nil, :array).should == ["answer"]
512
+ end
513
+
514
+ it "calls to all backends till an answer is found when doing hash lookups" do
515
+ thehash = {:answer => "value"}
516
+ backend = mock
517
+ backend.expects(:lookup).returns(thehash)
518
+ Config.load({})
519
+ Config.instance_variable_set("@config", {:backends => ["yaml", "rspec"]})
520
+ Backend.instance_variable_set("@backends", {"rspec" => backend})
521
+ Backend.expects(:constants).returns(["Yaml_backend", "Rspec_backend"]).twice
522
+
523
+ Backend.lookup("key", "notfound", {"rspec" => "test"}, nil, :hash).should == thehash
524
+ end
525
+
526
+ it "builds a merged hash from all backends for hash searches" do
527
+ backend1 = mock :lookup => {"a" => "answer"}
528
+ backend2 = mock :lookup => {"b" => "bnswer"}
529
+ Config.load({})
530
+ Config.instance_variable_set("@config", {:backends => ["first", "second"]})
531
+ Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2})
532
+ Backend.stubs(:constants).returns(["First_backend", "Second_backend"])
533
+
534
+ Backend.lookup("key", {}, {"rspec" => "test"}, nil, :hash).should == {"a" => "answer", "b" => "bnswer"}
535
+ end
536
+
537
+ it "builds an array from all backends for array searches" do
538
+ backend1 = mock :lookup => ["a", "b"]
539
+ backend2 = mock :lookup => ["c", "d"]
540
+ Config.load({})
541
+ Config.instance_variable_set("@config", {:backends => ["first", "second"]})
542
+ Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2})
543
+ Backend.stubs(:constants).returns(["First_backend", "Second_backend"])
544
+
545
+ Backend.lookup("key", {}, {"rspec" => "test"}, nil, :array).should == ["a", "b", "c", "d"]
546
+ end
547
+
548
+ it "uses the earliest backend result for priority searches" do
549
+ backend1 = mock
550
+ backend1.stubs(:lookup).returns(["a", "b"])
551
+ backend2 = mock
552
+ backend2.stubs(:lookup).returns(["c", "d"])
553
+ Config.load({})
554
+ Config.instance_variable_set("@config", {:backends => ["first", "second"]})
555
+ Backend.instance_variable_set("@backends", {"first" => backend1, "second" => backend2})
556
+ Backend.stubs(:constants).returns(["First_backend", "Second_backend"])
557
+
558
+ Backend.lookup("key", {}, {"rspec" => "test"}, nil, :priority).should == ["a", "b"]
559
+ end
560
+
561
+ it "parses the answers based on resolution_type" do
562
+ Config.load({:yaml => {:datadir => "/tmp"}})
563
+ Config.load_backends
564
+
565
+ Backend.expects(:resolve_answer).with("test_test", :priority).returns("parsed")
566
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, :priority, instance_of(Hash)).returns("test_test")
567
+
568
+ Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, :priority).should == "parsed"
569
+ end
570
+
571
+ it "returns the default with variables parsed if nothing is found" do
572
+ Config.load({:yaml => {:datadir => "/tmp"}})
573
+ Config.load_backends
574
+
575
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil, instance_of(Hash)).throws(:no_such_key)
576
+
577
+ Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil).should == "test_test"
578
+ end
579
+
580
+ it "returns nil instead of the default when key is found with a nil value" do
581
+ Config.load({:yaml => {:datadir => "/tmp"}})
582
+ Config.load_backends
583
+
584
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {"rspec" => "test"}, nil, nil, instance_of(Hash)).returns(nil)
585
+
586
+ Backend.lookup("key", "test_%{rspec}", {"rspec" => "test"}, nil, nil).should == nil
587
+ end
588
+
589
+ it "keeps string default data as a string" do
590
+ Config.load({:yaml => {:datadir => "/tmp"}})
591
+ Config.load_backends
592
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, nil, instance_of(Hash)).throws(:no_such_key)
593
+ Backend.lookup("key", "test", {}, nil, nil).should == "test"
594
+ end
595
+
596
+ it "keeps array default data as an array" do
597
+ Config.load({:yaml => {:datadir => "/tmp"}})
598
+ Config.load_backends
599
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, :array, instance_of(Hash)).throws(:no_such_key)
600
+ Backend.lookup("key", ["test"], {}, nil, :array).should == ["test"]
601
+ end
602
+
603
+ it "keeps hash default data as a hash" do
604
+ Config.load({:yaml => {:datadir => "/tmp"}})
605
+ Config.load_backends
606
+ Backend::Yaml_backend.any_instance.expects(:lookup).with("key", {}, nil, :hash, instance_of(Hash)).throws(:no_such_key)
607
+ Backend.lookup("key", {"test" => "value"}, {}, nil, :hash).should == {"test" => "value"}
608
+ end
609
+
610
+ it 'can use qualified key to lookup value in hash' do
611
+ Config.load({:yaml => {:datadir => '/tmp'}})
612
+ Config.load_backends
613
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns({ 'test' => 'value'})
614
+ Backend.lookup('key.test', 'dflt', {}, nil, nil).should == 'value'
615
+ end
616
+
617
+ it 'can use qualified key to lookup value in array' do
618
+ Config.load({:yaml => {:datadir => '/tmp'}})
619
+ Config.load_backends
620
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns([ 'first', 'second'])
621
+ Backend.lookup('key.1', 'dflt', {}, nil, nil).should == 'second'
622
+ end
623
+
624
+ it 'will fail when qualified key is partially found but not expected hash' do
625
+ Config.load({:yaml => {:datadir => '/tmp'}})
626
+ Config.load_backends
627
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(['value 1', 'value 2'])
628
+ expect do
629
+ Backend.lookup('key.test', 'dflt', {}, nil, nil)
630
+ end.to raise_error(Exception, /^Hiera type mismatch:/)
631
+ end
632
+
633
+ it 'will fail when qualified key used with resolution_type :hash' do
634
+ expect do
635
+ Backend.lookup('key.test', 'dflt', {}, nil, :hash)
636
+ end.to raise_error(ArgumentError, /^Resolution type :hash is illegal/)
637
+ end
638
+
639
+ it 'will fail when qualified key used with resolution_type :array' do
640
+ expect do
641
+ Backend.lookup('key.test', 'dflt', {}, nil, :array)
642
+ end.to raise_error(ArgumentError, /^Resolution type :array is illegal/)
643
+ end
644
+
645
+ it 'will succeed when qualified key used with resolution_type :priority' do
646
+ Config.load({:yaml => {:datadir => '/tmp'}})
647
+ Config.load_backends
648
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, :priority, instance_of(Hash)).returns({ 'test' => 'value'})
649
+ Backend.lookup('key.test', 'dflt', {}, nil, :priority).should == 'value'
650
+ end
651
+
652
+ it 'will fail when qualified key is partially found but not expected array' do
653
+ Config.load({:yaml => {:datadir => '/tmp'}})
654
+ Config.load_backends
655
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns({ 'test' => 'value'})
656
+ expect do
657
+ Backend.lookup('key.2', 'dflt', {}, nil, nil)
658
+ end.to raise_error(Exception, /^Hiera type mismatch:/)
659
+ end
660
+
661
+ it 'will not fail when qualified key is partially not found' do
662
+ Config.load({:yaml => {:datadir => '/tmp'}})
663
+ Config.load_backends
664
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(nil)
665
+ Backend.lookup('key.test', 'dflt', {}, nil, nil).should == 'dflt'
666
+ end
667
+
668
+ it 'will not fail when qualified key is array index out of bounds' do
669
+ Config.load({:yaml => {:datadir => '/tmp'}})
670
+ Config.load_backends
671
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', {}, nil, nil, instance_of(Hash)).returns(['value 1', 'value 2'])
672
+ Backend.lookup('key.33', 'dflt', {}, nil, nil).should == 'dflt'
673
+ end
674
+
675
+ it 'can use qualified key in interpolation to lookup value in hash' do
676
+ Config.load({:yaml => {:datadir => '/tmp'}})
677
+ Config.load_backends
678
+ Hiera::Backend.stubs(:datasourcefiles).yields('foo', 'bar')
679
+ Hiera::Filecache.any_instance.expects(:read_file).at_most(2).returns({'key' => '%{hiera(\'some.subkey\')}', 'some' => { 'subkey' => 'value' }})
680
+ Backend.lookup('key', 'dflt', {}, nil, nil).should == 'value'
681
+ end
682
+
683
+ it 'can use qualified key in interpolated default and scope' do
684
+ Config.load({:yaml => {:datadir => '/tmp'}})
685
+ Config.load_backends
686
+ scope = { 'some' => { 'test' => 'value'}}
687
+ Backend::Yaml_backend.any_instance.expects(:lookup).with('key', scope, nil, nil, instance_of(Hash))
688
+ Backend.lookup('key.notfound', '%{some.test}', scope, nil, nil).should == 'value'
689
+ end
690
+
691
+ it "handles older backend with 4 argument lookup" do
692
+ Config.load({})
693
+ Config.instance_variable_set("@config", {:backends => ["Backend1x"]})
694
+
695
+ Hiera.expects(:debug).at_least_once.with(regexp_matches /Using Hiera 1.x backend/)
696
+ Backend.lookup("key", {}, {"rspec" => "test"}, nil, :priority).should == ["a", "b"]
697
+ end
698
+ end
699
+
700
+ describe '#merge_answer' do
701
+ before do
702
+ Hiera.stubs(:debug)
703
+ Hiera.stubs(:warn)
704
+ Config.stubs(:validate!)
705
+ end
706
+
707
+ it "uses Hash.merge when configured with :merge_behavior => :native" do
708
+ Config.load({:merge_behavior => :native})
709
+ Hash.any_instance.expects(:merge).with({"b" => "bnswer"}).returns({"a" => "answer", "b" => "bnswer"})
710
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}).should == {"a" => "answer", "b" => "bnswer"}
711
+ end
712
+
713
+ it "uses deep_merge! when configured with :merge_behavior => :deeper" do
714
+ Config.load({:merge_behavior => :deeper})
715
+ Hash.any_instance.expects('deep_merge!').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"})
716
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}).should == {"a" => "answer", "b" => "bnswer"}
717
+ end
718
+
719
+ it "uses deep_merge when configured with :merge_behavior => :deep" do
720
+ Config.load({:merge_behavior => :deep})
721
+ Hash.any_instance.expects('deep_merge').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"})
722
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}).should == {"a" => "answer", "b" => "bnswer"}
723
+ end
724
+
725
+ it "disregards configuration when 'merge' parameter is given as a Hash" do
726
+ Config.load({:merge_behavior => :deep})
727
+ Hash.any_instance.expects('deep_merge!').with({"b" => "bnswer"}, {}).returns({"a" => "answer", "b" => "bnswer"})
728
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}, {:behavior => 'deeper' }).should == {"a" => "answer", "b" => "bnswer"}
729
+ end
730
+
731
+ it "propagates deep merge options when given Hash 'merge' parameter" do
732
+ Hash.any_instance.expects('deep_merge!').with({"b" => "bnswer"}, { :knockout_prefix => '-' }).returns({"a" => "answer", "b" => "bnswer"})
733
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}, {:behavior => 'deeper', :knockout_prefix => '-'}).should == {"a" => "answer", "b" => "bnswer"}
734
+ end
735
+
736
+ it "passes Config[:deep_merge_options] into calls to deep_merge" do
737
+ Config.load({:merge_behavior => :deep, :deep_merge_options => { :knockout_prefix => '-' } })
738
+ Hash.any_instance.expects('deep_merge').with({"b" => "bnswer"}, {:knockout_prefix => '-'}).returns({"a" => "answer", "b" => "bnswer"})
739
+ Backend.merge_answer({"a" => "answer"},{"b" => "bnswer"}).should == {"a" => "answer", "b" => "bnswer"}
740
+ end
741
+ end
742
+ end
743
+ end