-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Copy pathlookup_spec.rb
375 lines (330 loc) · 11.7 KB
/
lookup_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
require 'spec_helper'
require 'puppet_spec/files'
require 'puppet/pops'
require 'deep_merge/core'
module Puppet::Pops
module Lookup
describe 'The lookup API' do
include PuppetSpec::Files
let(:env_name) { 'spec' }
let(:code_dir) { Puppet[:environmentpath] }
let(:env_dir) { File.join(code_dir, env_name) }
let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, 'modules')]) }
let(:node) { Puppet::Node.new('test', :environment => env) }
let(:compiler) { Puppet::Parser::Compiler.new(node) }
let(:pal_compiler) { Puppet::Pal::ScriptCompiler.new(compiler) }
let(:scope) { compiler.topscope }
let(:invocation) { Invocation.new(scope) }
let(:code_dir_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
YAML
'data' => {
'common.yaml' => <<-YAML.unindent
a: a (from global)
d: d (from global)
mod::e: mod::e (from global)
YAML
}
}
end
let(:env_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
YAML
'data' => {
'common.yaml' => <<-YAML.unindent
b: b (from environment)
d: d (from environment)
mod::f: mod::f (from environment)
YAML
}
}
end
let(:mod_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
YAML
'data' => {
'common.yaml' => <<-YAML.unindent
mod::c: mod::c (from module)
mod::e: mod::e (from module)
mod::f: mod::f (from module)
mod::g:
:symbol: symbol key value
key: string key value
6: integer key value
-4: negative integer key value
2.7: float key value
'6': string integer key value
YAML
}
}
end
let(:populated_env_dir) do
all_content = code_dir_content.merge(env_name => env_content.merge('modules' => { 'mod' => mod_content }))
dir_contained_in(code_dir, all_content)
all_content.keys.each { |key| PuppetSpec::Files.record_tmp(File.join(code_dir, key)) }
env_dir
end
before(:each) do
Puppet[:hiera_config] = File.join(code_dir, 'hiera.yaml')
Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))
end
after(:each) do
Puppet.pop_context
end
context 'when doing automatic parameter lookup' do
let(:mod_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
YAML
'data' => {
'common.yaml' => <<-YAML.unindent
mod::x: mod::x (from module)
YAML
},
'manifests' => {
'init.pp' => <<-PUPPET.unindent
class mod($x) {
notify { $x: }
}
PUPPET
}
}
end
let(:logs) { [] }
let(:debugs) { logs.select { |log| log.level == :debug }.map { |log| log.message } }
it 'includes APL in explain output when debug is enabled' do
Puppet[:log_level] = 'debug'
Puppet[:code] = 'include mod'
Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
compiler.compile
end
expect(debugs).to include(/Found key: "mod::x" value: "mod::x \(from module\)"/)
end
end
context 'when hiera YAML data is corrupt' do
let(:mod_content) do
{
'hiera.yaml' => 'version: 5',
'data' => {
'common.yaml' => <<-YAML.unindent
---
#mod::classes:
- cls1
- cls2
mod::somevar: 1
YAML
},
}
end
let(:msg) { /file does not contain a valid yaml hash/ }
%w(off warning).each do |strict|
it "logs a warning when --strict is '#{strict}'" do
Puppet[:strict] = strict
logs = []
Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
expect(Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation)).to be_nil
end
expect(logs.map(&:message)).to contain_exactly(msg)
end
end
it 'fails when --strict is "error"' do
Puppet[:strict] = 'error'
expect { Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation) }.to raise_error(msg)
end
end
context 'when hiera YAML data is empty' do
let(:mod_content) do
{
'hiera.yaml' => 'version: 5',
'data' => { 'common.yaml' => '' },
}
end
let(:msg) { /file does not contain a valid yaml hash/ }
%w(off warning error).each do |strict|
it "logs a warning when --strict is '#{strict}'" do
Puppet[:strict] = strict
logs = []
Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
expect(Lookup.lookup('mod::somevar', nil, nil, true, nil, invocation)).to be_nil
end
expect(logs.map(&:message)).to contain_exactly(msg)
end
end
end
context 'when doing lookup' do
it 'finds data in global layer' do
expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')
end
it 'finds data in environment layer' do
expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('b (from environment)')
end
it 'global layer wins over environment layer' do
expect(Lookup.lookup('d', nil, 'not found', true, nil, invocation)).to eql('d (from global)')
end
it 'finds data in module layer' do
expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('mod::c (from module)')
end
it 'global layer wins over module layer' do
expect(Lookup.lookup('mod::e', nil, 'not found', true, nil, invocation)).to eql('mod::e (from global)')
end
it 'environment layer wins over module layer' do
expect(Lookup.lookup('mod::f', nil, 'not found', true, nil, invocation)).to eql('mod::f (from environment)')
end
it 'returns the correct types for hash keys' do
expect(Lookup.lookup('mod::g', nil, 'not found', true, nil, invocation)).to eql(
{
'symbol' => 'symbol key value',
'key' => 'string key value',
6 => 'integer key value',
-4 => 'negative integer key value',
2.7 => 'float key value',
'6' => 'string integer key value'
}
)
end
it 'can navigate a hash with an integer key using a dotted key' do
expect(Lookup.lookup('mod::g.6', nil, 'not found', true, nil, invocation)).to eql('integer key value')
end
it 'can navigate a hash with a negative integer key using a dotted key' do
expect(Lookup.lookup('mod::g.-4', nil, 'not found', true, nil, invocation)).to eql('negative integer key value')
end
it 'can navigate a hash with an string integer key using a dotted key with quoted integer' do
expect(Lookup.lookup("mod::g.'6'", nil, 'not found', true, nil, invocation)).to eql('string integer key value')
end
context "with 'global_only' set to true in the invocation" do
let(:invocation) { Invocation.new(scope).set_global_only }
it 'finds data in global layer' do
expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')
end
it 'does not find data in environment layer' do
expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('not found')
end
it 'does not find data in module layer' do
expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('not found')
end
end
context "with 'global_only' set to true in the lookup adapter" do
it 'finds data in global layer' do
invocation.lookup_adapter.set_global_only
expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')
end
it 'does not find data in environment layer' do
invocation.lookup_adapter.set_global_only
expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('not found')
end
it 'does not find data in module layer' do
invocation.lookup_adapter.set_global_only
expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('not found')
end
end
context 'with subclassed lookup adpater' do
let(:other_dir) { tmpdir('other') }
let(:other_dir_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
hierarchy:
- name: Common
path: common.yaml
- name: More
path: more.yaml
YAML
'data' => {
'common.yaml' => <<-YAML.unindent,
a: a (from other global)
d: d (from other global)
mixed_adapter_hash:
a:
ab: value a.ab (from other common global)
ad: value a.ad (from other common global)
mod::e: mod::e (from other global)
lookup_options:
mixed_adapter_hash:
merge: deep
YAML
'more.yaml' => <<-YAML.unindent
mixed_adapter_hash:
a:
aa: value a.aa (from other more global)
ac: value a.ac (from other more global)
YAML
}
}
end
let(:populated_other_dir) do
dir_contained_in(other_dir, other_dir_content)
other_dir
end
before(:each) do
eval(<<-RUBY.unindent)
class SpecialLookupAdapter < LookupAdapter
def initialize(compiler)
super
set_global_only
set_global_hiera_config_path(File.join('#{populated_other_dir}', 'hiera.yaml'))
end
end
RUBY
end
after(:each) do
Puppet::Pops::Lookup.send(:remove_const, :SpecialLookupAdapter)
end
let(:other_invocation) { Invocation.new(scope, EMPTY_HASH, EMPTY_HASH, nil, SpecialLookupAdapter) }
it 'finds different data in global layer' do
expect(Lookup.lookup('a', nil, nil, false, nil, other_invocation)).to eql('a (from other global)')
expect(Lookup.lookup('a', nil, nil, false, nil, invocation)).to eql('a (from global)')
end
it 'does not find data in environment layer' do
expect(Lookup.lookup('b', nil, 'not found', true, nil, other_invocation)).to eql('not found')
expect(Lookup.lookup('b', nil, 'not found', true, nil, invocation)).to eql('b (from environment)')
end
it 'does not find data in module layer' do
expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, other_invocation)).to eql('not found')
expect(Lookup.lookup('mod::c', nil, 'not found', true, nil, invocation)).to eql('mod::c (from module)')
end
it 'resolves lookup options using the custom adapter' do
expect(Lookup.lookup('mixed_adapter_hash', nil, 'not found', true, nil, other_invocation)).to eql(
{
'a' => {
'aa' => 'value a.aa (from other more global)',
'ab' => 'value a.ab (from other common global)',
'ac' => 'value a.ac (from other more global)',
'ad' => 'value a.ad (from other common global)'
}
}
)
end
end
end
context 'when using plan_hierarchy' do
let(:code_dir_content) do
{
'hiera.yaml' => <<-YAML.unindent,
version: 5
plan_hierarchy:
- path: foo.yaml
name: Common
YAML
'data' => {
'foo.yaml' => <<-YAML.unindent
pop: star
YAML
}
}
end
it 'uses plan_hierarchy when using ScriptCompiler' do
Puppet.override(pal_compiler: pal_compiler) do
expect(Lookup.lookup('pop', nil, nil, true, nil, invocation)).to eq('star')
end
end
end
end
end
end