chef-config 17.9.52 → 17.10.19
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 +4 -4
- data/LICENSE +201 -201
- data/Rakefile +14 -14
- data/chef-config.gemspec +39 -39
- data/lib/chef-config/config.rb +1297 -1297
- data/lib/chef-config/exceptions.rb +27 -27
- data/lib/chef-config/fips.rb +53 -53
- data/lib/chef-config/logger.rb +53 -53
- data/lib/chef-config/mixin/credentials.rb +102 -102
- data/lib/chef-config/mixin/dot_d.rb +44 -44
- data/lib/chef-config/mixin/fuzzy_hostname_matcher.rb +49 -49
- data/lib/chef-config/mixin/train_transport.rb +143 -143
- data/lib/chef-config/path_helper.rb +350 -350
- data/lib/chef-config/version.rb +19 -19
- data/lib/chef-config/windows.rb +24 -24
- data/lib/chef-config/workstation_config_loader.rb +281 -281
- data/lib/chef-config.rb +20 -20
- data/spec/spec_helper.rb +75 -75
- data/spec/unit/config_spec.rb +1390 -1390
- data/spec/unit/fips_spec.rb +128 -128
- data/spec/unit/path_helper_spec.rb +372 -372
- data/spec/unit/workstation_config_loader_spec.rb +633 -633
- metadata +4 -4
@@ -1,633 +1,633 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Daniel DeLeo (<dan@chef.io>)
|
3
|
-
# Copyright:: Copyright (c) Chef Software Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require "spec_helper"
|
20
|
-
require "tempfile" unless defined?(Tempfile)
|
21
|
-
|
22
|
-
require "chef-config/exceptions"
|
23
|
-
require "chef-utils"
|
24
|
-
require "chef-config/workstation_config_loader"
|
25
|
-
|
26
|
-
RSpec.describe ChefConfig::WorkstationConfigLoader do
|
27
|
-
|
28
|
-
let(:explicit_config_location) { nil }
|
29
|
-
|
30
|
-
let(:env) { {} }
|
31
|
-
|
32
|
-
let(:config_loader) do
|
33
|
-
described_class.new(explicit_config_location).tap do |c|
|
34
|
-
allow(c).to receive(:env).and_return(env)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
before do
|
39
|
-
# We set this to nil so that a dev workstation will
|
40
|
-
# not interfere with the tests.
|
41
|
-
ChefConfig::Config.reset
|
42
|
-
ChefConfig::Config[:config_d_dir] = nil
|
43
|
-
end
|
44
|
-
|
45
|
-
# Test methods that do I/O or reference external state which are stubbed out
|
46
|
-
# elsewhere.
|
47
|
-
describe "external dependencies" do
|
48
|
-
let(:config_loader) { described_class.new(nil) }
|
49
|
-
|
50
|
-
it "delegates to ENV for env" do
|
51
|
-
expect(config_loader.env).to equal(ENV)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "tests a path's existence" do
|
55
|
-
expect(config_loader.path_exists?("/nope/nope/nope/nope/slab/jab/nab")).to be(false)
|
56
|
-
expect(config_loader.path_exists?(__FILE__)).to be(true)
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
describe "locating the config file" do
|
62
|
-
context "without an explicit config" do
|
63
|
-
|
64
|
-
before do
|
65
|
-
allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "has no config if HOME is not set" do
|
69
|
-
expect(config_loader.config_location).to be(nil)
|
70
|
-
expect(config_loader.no_config_found?).to be(true)
|
71
|
-
end
|
72
|
-
|
73
|
-
context "when HOME is set and contains a knife.rb" do
|
74
|
-
|
75
|
-
let(:home) { "/Users/example.user" }
|
76
|
-
|
77
|
-
before do
|
78
|
-
allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_yield(File.join(home, ".chef"))
|
79
|
-
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true)
|
80
|
-
end
|
81
|
-
|
82
|
-
it "uses the config in HOME/.chef/knife.rb" do
|
83
|
-
expect(config_loader.config_location).to eq("#{home}/.chef/knife.rb")
|
84
|
-
end
|
85
|
-
|
86
|
-
context "and has a config.rb" do
|
87
|
-
|
88
|
-
before do
|
89
|
-
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/config.rb").and_return(true)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "uses the config in HOME/.chef/config.rb" do
|
93
|
-
expect(config_loader.config_location).to eq("#{home}/.chef/config.rb")
|
94
|
-
end
|
95
|
-
|
96
|
-
context "and/or a parent dir contains a .chef dir" do
|
97
|
-
|
98
|
-
let(:env_pwd) { "/path/to/cwd" }
|
99
|
-
|
100
|
-
before do
|
101
|
-
if ChefUtils.windows?
|
102
|
-
env["CD"] = env_pwd
|
103
|
-
else
|
104
|
-
env["PWD"] = env_pwd
|
105
|
-
end
|
106
|
-
|
107
|
-
allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/knife.rb").and_return(true)
|
108
|
-
allow(File).to receive(:exist?).with("#{env_pwd}/.chef").and_return(true)
|
109
|
-
allow(File).to receive(:directory?).with("#{env_pwd}/.chef").and_return(true)
|
110
|
-
end
|
111
|
-
|
112
|
-
it "prefers the config from parent_dir/.chef" do
|
113
|
-
expect(config_loader.config_location).to eq("#{env_pwd}/.chef/knife.rb")
|
114
|
-
end
|
115
|
-
|
116
|
-
context "and the parent dir's .chef dir has a config.rb" do
|
117
|
-
|
118
|
-
before do
|
119
|
-
allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/config.rb").and_return(true)
|
120
|
-
end
|
121
|
-
|
122
|
-
it "prefers the config from parent_dir/.chef" do
|
123
|
-
expect(config_loader.config_location).to eq("#{env_pwd}/.chef/config.rb")
|
124
|
-
end
|
125
|
-
|
126
|
-
context "and/or the current working directory contains a .chef dir" do
|
127
|
-
|
128
|
-
let(:cwd) { Dir.pwd }
|
129
|
-
|
130
|
-
before do
|
131
|
-
allow(config_loader).to receive(:path_exists?).with("#{cwd}/knife.rb").and_return(true)
|
132
|
-
end
|
133
|
-
|
134
|
-
it "prefers a knife.rb located in the cwd" do
|
135
|
-
expect(config_loader.config_location).to eq("#{cwd}/knife.rb")
|
136
|
-
end
|
137
|
-
|
138
|
-
context "and the CWD's .chef dir has a config.rb" do
|
139
|
-
|
140
|
-
before do
|
141
|
-
allow(config_loader).to receive(:path_exists?).with("#{cwd}/config.rb").and_return(true)
|
142
|
-
end
|
143
|
-
|
144
|
-
it "prefers a config located in the cwd" do
|
145
|
-
expect(config_loader.config_location).to eq("#{cwd}/config.rb")
|
146
|
-
end
|
147
|
-
|
148
|
-
context "and/or KNIFE_HOME is set" do
|
149
|
-
|
150
|
-
let(:knife_home) { "/path/to/knife/home" }
|
151
|
-
|
152
|
-
before do
|
153
|
-
env["KNIFE_HOME"] = knife_home
|
154
|
-
allow(config_loader).to receive(:path_exists?).with("#{knife_home}/knife.rb").and_return(true)
|
155
|
-
end
|
156
|
-
|
157
|
-
it "prefers a knife located in KNIFE_HOME" do
|
158
|
-
expect(config_loader.config_location).to eq("/path/to/knife/home/knife.rb")
|
159
|
-
end
|
160
|
-
|
161
|
-
context "and KNIFE_HOME contains a config.rb" do
|
162
|
-
|
163
|
-
before do
|
164
|
-
env["KNIFE_HOME"] = knife_home
|
165
|
-
allow(config_loader).to receive(:path_exists?).with("#{knife_home}/config.rb").and_return(true)
|
166
|
-
end
|
167
|
-
|
168
|
-
it "prefers a config.rb located in KNIFE_HOME" do
|
169
|
-
expect(config_loader.config_location).to eq("/path/to/knife/home/config.rb")
|
170
|
-
end
|
171
|
-
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context "when the current working dir is inside a symlinked directory" do
|
183
|
-
before do
|
184
|
-
# pwd according to your shell is /home/someuser/prod/chef-repo, but
|
185
|
-
# chef-repo is a symlink to /home/someuser/codes/chef-repo
|
186
|
-
env["CD"] = "/home/someuser/prod/chef-repo" # windows
|
187
|
-
env["PWD"] = "/home/someuser/prod/chef-repo" # unix
|
188
|
-
|
189
|
-
allow(Dir).to receive(:pwd).and_return("/home/someuser/codes/chef-repo")
|
190
|
-
end
|
191
|
-
|
192
|
-
it "loads the config from the non-dereferenced directory path" do
|
193
|
-
expect(File).to receive(:exist?).with("/home/someuser/prod/chef-repo/.chef").and_return(false)
|
194
|
-
expect(File).to receive(:exist?).with("/home/someuser/prod/.chef").and_return(true)
|
195
|
-
expect(File).to receive(:directory?).with("/home/someuser/prod/.chef").and_return(true)
|
196
|
-
|
197
|
-
expect(config_loader).to receive(:path_exists?).with("/home/someuser/prod/.chef/knife.rb").and_return(true)
|
198
|
-
|
199
|
-
expect(config_loader.config_location).to eq("/home/someuser/prod/.chef/knife.rb")
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
context "when given an explicit config to load" do
|
205
|
-
|
206
|
-
let(:explicit_config_location) { "/path/to/explicit/config.rb" }
|
207
|
-
|
208
|
-
it "prefers the explicit config" do
|
209
|
-
expect(config_loader.config_location).to eq(explicit_config_location)
|
210
|
-
end
|
211
|
-
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
describe "loading the config file" do
|
216
|
-
|
217
|
-
context "when no explicit config is specified and no implicit config is found" do
|
218
|
-
|
219
|
-
before do
|
220
|
-
allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
|
221
|
-
end
|
222
|
-
|
223
|
-
it "skips loading" do
|
224
|
-
expect(config_loader.config_location).to be(nil)
|
225
|
-
expect(config_loader).not_to receive(:apply_config)
|
226
|
-
config_loader.load
|
227
|
-
end
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
context "when an explicit config is given but it doesn't exist" do
|
232
|
-
|
233
|
-
let(:explicit_config_location) { "/nope/nope/nope/slab/jab/nab" }
|
234
|
-
|
235
|
-
it "raises a configuration error" do
|
236
|
-
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
237
|
-
end
|
238
|
-
|
239
|
-
end
|
240
|
-
|
241
|
-
context "when the config file exists" do
|
242
|
-
|
243
|
-
let(:config_content) { "" }
|
244
|
-
|
245
|
-
# We need to keep a reference to the tempfile because while #close does
|
246
|
-
# not unlink the file, the object being GC'd will.
|
247
|
-
let(:tempfile) do
|
248
|
-
Tempfile.new("Chef-WorkstationConfigLoader-rspec-test").tap do |t|
|
249
|
-
t.print(config_content)
|
250
|
-
t.close
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
let(:explicit_config_location) do
|
255
|
-
tempfile.path
|
256
|
-
end
|
257
|
-
|
258
|
-
after { File.unlink(explicit_config_location) if File.exist?(explicit_config_location) }
|
259
|
-
|
260
|
-
context "and is valid" do
|
261
|
-
|
262
|
-
let(:config_content) { "config_file_evaluated(true)" }
|
263
|
-
|
264
|
-
it "loads the config" do
|
265
|
-
expect(config_loader).to receive(:apply_config).and_call_original
|
266
|
-
config_loader.load
|
267
|
-
expect(ChefConfig::Config.config_file_evaluated).to be(true)
|
268
|
-
end
|
269
|
-
|
270
|
-
it "sets ChefConfig::Config.config_file" do
|
271
|
-
config_loader.load
|
272
|
-
expect(ChefConfig::Config.config_file).to eq(explicit_config_location)
|
273
|
-
end
|
274
|
-
|
275
|
-
it "loads a default value for node_name" do
|
276
|
-
allow(Etc).to receive(:getlogin).and_return("notauser")
|
277
|
-
config_loader.load
|
278
|
-
expect(ChefConfig::Config.node_name).to eq("notauser")
|
279
|
-
end
|
280
|
-
|
281
|
-
context "with a user.pem" do
|
282
|
-
before do
|
283
|
-
allow(Etc).to receive(:getlogin).and_return("notauser")
|
284
|
-
allow(FileTest).to receive(:exist?).and_call_original
|
285
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(false)
|
286
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(true)
|
287
|
-
end
|
288
|
-
|
289
|
-
it "loads a default value for client_key" do
|
290
|
-
config_loader.load
|
291
|
-
expect(ChefConfig::Config.client_key).to eq(File.expand_path("../user.pem", explicit_config_location))
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
context "with a notauser.pem" do
|
296
|
-
before do
|
297
|
-
allow(Etc).to receive(:getlogin).and_return("notauser")
|
298
|
-
allow(FileTest).to receive(:exist?).and_call_original
|
299
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(true)
|
300
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(false)
|
301
|
-
end
|
302
|
-
|
303
|
-
it "loads a default value for client_key" do
|
304
|
-
config_loader.load
|
305
|
-
expect(ChefConfig::Config.client_key).to eq(File.expand_path("../notauser.pem", explicit_config_location))
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
context "with a valclient.pem" do
|
310
|
-
before do
|
311
|
-
ChefConfig::Config.validation_client_name = "valclient"
|
312
|
-
allow(FileTest).to receive(:exist?).and_call_original
|
313
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(true)
|
314
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(false)
|
315
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
|
316
|
-
end
|
317
|
-
|
318
|
-
it "loads a default value for validation_key" do
|
319
|
-
config_loader.load
|
320
|
-
expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../valclient.pem", explicit_config_location))
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
context "with a validator.pem" do
|
325
|
-
before do
|
326
|
-
ChefConfig::Config.validation_client_name = "valclient"
|
327
|
-
allow(FileTest).to receive(:exist?).and_call_original
|
328
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(false)
|
329
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(true)
|
330
|
-
allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
|
331
|
-
end
|
332
|
-
|
333
|
-
it "loads a default value for validation_key" do
|
334
|
-
config_loader.load
|
335
|
-
expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../validator.pem", explicit_config_location))
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
context "and has a syntax error" do
|
341
|
-
|
342
|
-
let(:config_content) { "{{{{{:{{" }
|
343
|
-
|
344
|
-
it "raises a ConfigurationError" do
|
345
|
-
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
context "and raises a ruby exception during evaluation" do
|
350
|
-
|
351
|
-
let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" }
|
352
|
-
|
353
|
-
it "raises a ConfigurationError" do
|
354
|
-
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
end
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
describe "when loading config.d" do
|
363
|
-
context "when the conf.d directory exists" do
|
364
|
-
let(:config_content) { "" }
|
365
|
-
|
366
|
-
let(:tempdir) { Dir.mktmpdir("chef-workstation-test") }
|
367
|
-
|
368
|
-
let!(:confd_file) do
|
369
|
-
Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".rb"], tempdir).tap do |t|
|
370
|
-
t.print(config_content)
|
371
|
-
t.close
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
before do
|
376
|
-
ChefConfig::Config[:config_d_dir] = tempdir
|
377
|
-
allow(config_loader).to receive(:path_exists?).with(
|
378
|
-
an_instance_of(String)
|
379
|
-
).and_return(false)
|
380
|
-
end
|
381
|
-
|
382
|
-
after do
|
383
|
-
FileUtils.remove_entry_secure tempdir
|
384
|
-
end
|
385
|
-
|
386
|
-
context "and is valid" do
|
387
|
-
let(:config_content) { "config_d_file_evaluated(true)" }
|
388
|
-
|
389
|
-
it "loads the config" do
|
390
|
-
expect(config_loader).to receive(:apply_config).and_call_original
|
391
|
-
config_loader.load
|
392
|
-
expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
context "and has a syntax error" do
|
397
|
-
let(:config_content) { "{{{{{:{{" }
|
398
|
-
|
399
|
-
it "raises a ConfigurationError" do
|
400
|
-
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
context "has a non rb file" do
|
405
|
-
let(:syntax_error_content) { "{{{{{:{{" }
|
406
|
-
let(:config_content) { "config_d_file_evaluated(true)" }
|
407
|
-
|
408
|
-
let!(:not_confd_file) do
|
409
|
-
Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".foorb"], tempdir).tap do |t|
|
410
|
-
t.print(syntax_error_content)
|
411
|
-
t.close
|
412
|
-
end
|
413
|
-
end
|
414
|
-
|
415
|
-
it "does not load the non rb file" do
|
416
|
-
expect { config_loader.load }.not_to raise_error
|
417
|
-
expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
context "when the conf.d directory does not exist" do
|
423
|
-
before do
|
424
|
-
ChefConfig::Config[:config_d_dir] = "/nope/nope/nope/nope/notdoingit"
|
425
|
-
end
|
426
|
-
|
427
|
-
it "does not load anything" do
|
428
|
-
expect(config_loader).not_to receive(:apply_config)
|
429
|
-
end
|
430
|
-
end
|
431
|
-
end
|
432
|
-
|
433
|
-
describe "when loading a credentials file" do
|
434
|
-
if ChefUtils.windows?
|
435
|
-
let(:home) { "C:/Users/example.user" }
|
436
|
-
else
|
437
|
-
let(:home) { "/Users/example.user" }
|
438
|
-
end
|
439
|
-
let(:credentials_file) { "#{home}/.chef/credentials" }
|
440
|
-
let(:context_file) { "#{home}/.chef/context" }
|
441
|
-
|
442
|
-
before do
|
443
|
-
allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef"))
|
444
|
-
allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file)
|
445
|
-
allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file)
|
446
|
-
allow(File).to receive(:file?).with(context_file).and_return false
|
447
|
-
end
|
448
|
-
|
449
|
-
context "when the file exists" do
|
450
|
-
before do
|
451
|
-
expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content)
|
452
|
-
allow(File).to receive(:file?).with(credentials_file).and_return true
|
453
|
-
end
|
454
|
-
|
455
|
-
context "and has a default profile" do
|
456
|
-
let(:content) do
|
457
|
-
content = <<~EOH
|
458
|
-
[default]
|
459
|
-
node_name = 'barney'
|
460
|
-
client_key = "barney_rubble.pem"
|
461
|
-
chef_server_url = "https://api.chef.io/organizations/bedrock"
|
462
|
-
invalid_config_option1234 = "foobar"
|
463
|
-
EOH
|
464
|
-
content
|
465
|
-
end
|
466
|
-
|
467
|
-
it "applies the expected config" do
|
468
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
469
|
-
expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
|
470
|
-
expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
|
471
|
-
expect(ChefConfig::Config.profile.to_s).to eq("default")
|
472
|
-
expect(ChefConfig::Config[:invalid_config_option1234]).to eq("foobar")
|
473
|
-
end
|
474
|
-
end
|
475
|
-
|
476
|
-
context "and has a default profile with knife settings" do
|
477
|
-
let(:content) do
|
478
|
-
content = <<~EOH
|
479
|
-
[default]
|
480
|
-
node_name = 'barney'
|
481
|
-
client_key = "barney_rubble.pem"
|
482
|
-
chef_server_url = "https://api.chef.io/organizations/bedrock"
|
483
|
-
knife = {
|
484
|
-
secret_file = "/home/barney/.chef/encrypted_data_bag_secret.pem"
|
485
|
-
}
|
486
|
-
[default.knife]
|
487
|
-
ssh_user = "knife_ssh_user"
|
488
|
-
EOH
|
489
|
-
content
|
490
|
-
end
|
491
|
-
|
492
|
-
it "applies the expected knife config" do
|
493
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
494
|
-
expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
|
495
|
-
expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
|
496
|
-
expect(ChefConfig::Config.knife[:ssh_user].to_s).to eq("knife_ssh_user")
|
497
|
-
expect(ChefConfig::Config.knife[:secret_file].to_s).to eq("/home/barney/.chef/encrypted_data_bag_secret.pem")
|
498
|
-
expect(ChefConfig::Config.profile.to_s).to eq("default")
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
context "and has a profile containing a full key" do
|
503
|
-
let(:content) do
|
504
|
-
content = <<~EOH
|
505
|
-
[default]
|
506
|
-
client_key = """
|
507
|
-
-----BEGIN RSA PRIVATE KEY-----
|
508
|
-
foo
|
509
|
-
"""
|
510
|
-
EOH
|
511
|
-
content
|
512
|
-
end
|
513
|
-
|
514
|
-
it "applies the expected config" do
|
515
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
516
|
-
expect(ChefConfig::Config.client_key_contents).to eq(<<~EOH
|
517
|
-
-----BEGIN RSA PRIVATE KEY-----
|
518
|
-
foo
|
519
|
-
EOH
|
520
|
-
)
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
context "and has several profiles" do
|
525
|
-
let(:content) do
|
526
|
-
content = <<~EOH
|
527
|
-
[default]
|
528
|
-
client_name = "default"
|
529
|
-
[environment]
|
530
|
-
client_name = "environment"
|
531
|
-
[explicit]
|
532
|
-
client_name = "explicit"
|
533
|
-
[context]
|
534
|
-
client_name = "context"
|
535
|
-
EOH
|
536
|
-
content
|
537
|
-
end
|
538
|
-
|
539
|
-
let(:env) { {} }
|
540
|
-
before do
|
541
|
-
stub_const("ENV", env)
|
542
|
-
end
|
543
|
-
|
544
|
-
it "selects the correct profile explicitly" do
|
545
|
-
expect { config_loader.load_credentials("explicit") }.not_to raise_error
|
546
|
-
expect(ChefConfig::Config.node_name).to eq("explicit")
|
547
|
-
end
|
548
|
-
|
549
|
-
context "with an environment variable" do
|
550
|
-
let(:env) { { "CHEF_PROFILE" => "environment" } }
|
551
|
-
|
552
|
-
it "selects the correct profile" do
|
553
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
554
|
-
expect(ChefConfig::Config.node_name).to eq("environment")
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
|
-
it "selects the correct profile with a context file" do
|
559
|
-
allow(File).to receive(:file?).with(context_file).and_return true
|
560
|
-
expect(File).to receive(:read).with(context_file).and_return "context"
|
561
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
562
|
-
expect(ChefConfig::Config.node_name).to eq("context")
|
563
|
-
end
|
564
|
-
|
565
|
-
it "falls back to the default" do
|
566
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
567
|
-
expect(ChefConfig::Config.node_name).to eq("default")
|
568
|
-
end
|
569
|
-
end
|
570
|
-
|
571
|
-
context "and contains both node_name and client_name" do
|
572
|
-
let(:content) do
|
573
|
-
content = <<~EOH
|
574
|
-
[default]
|
575
|
-
node_name = 'barney'
|
576
|
-
client_name = 'barney'
|
577
|
-
EOH
|
578
|
-
content
|
579
|
-
end
|
580
|
-
|
581
|
-
it "raises a ConfigurationError" do
|
582
|
-
expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
context "and ssl_verify_mode is a symbol string" do
|
587
|
-
let(:content) do
|
588
|
-
content = <<~EOH
|
589
|
-
[default]
|
590
|
-
ssl_verify_mode = ":verify_none"
|
591
|
-
EOH
|
592
|
-
content
|
593
|
-
end
|
594
|
-
|
595
|
-
it "raises a ConfigurationError" do
|
596
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
597
|
-
expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
|
598
|
-
end
|
599
|
-
end
|
600
|
-
|
601
|
-
context "and ssl_verify_mode is a string" do
|
602
|
-
let(:content) do
|
603
|
-
content = <<~EOH
|
604
|
-
[default]
|
605
|
-
ssl_verify_mode = "verify_none"
|
606
|
-
EOH
|
607
|
-
content
|
608
|
-
end
|
609
|
-
|
610
|
-
it "raises a ConfigurationError" do
|
611
|
-
expect { config_loader.load_credentials }.not_to raise_error
|
612
|
-
expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
|
613
|
-
end
|
614
|
-
end
|
615
|
-
|
616
|
-
context "and has a syntax error" do
|
617
|
-
let(:content) { "<<<<<" }
|
618
|
-
|
619
|
-
it "raises a ConfigurationError" do
|
620
|
-
expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
|
621
|
-
end
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
|
-
context "when the file does not exist" do
|
626
|
-
it "does not load anything" do
|
627
|
-
allow(File).to receive(:file?).with(credentials_file).and_return false
|
628
|
-
expect(Tomlrb).not_to receive(:load_file)
|
629
|
-
config_loader.load_credentials
|
630
|
-
end
|
631
|
-
end
|
632
|
-
end
|
633
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) Chef Software Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "spec_helper"
|
20
|
+
require "tempfile" unless defined?(Tempfile)
|
21
|
+
|
22
|
+
require "chef-config/exceptions"
|
23
|
+
require "chef-utils"
|
24
|
+
require "chef-config/workstation_config_loader"
|
25
|
+
|
26
|
+
RSpec.describe ChefConfig::WorkstationConfigLoader do
|
27
|
+
|
28
|
+
let(:explicit_config_location) { nil }
|
29
|
+
|
30
|
+
let(:env) { {} }
|
31
|
+
|
32
|
+
let(:config_loader) do
|
33
|
+
described_class.new(explicit_config_location).tap do |c|
|
34
|
+
allow(c).to receive(:env).and_return(env)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
# We set this to nil so that a dev workstation will
|
40
|
+
# not interfere with the tests.
|
41
|
+
ChefConfig::Config.reset
|
42
|
+
ChefConfig::Config[:config_d_dir] = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Test methods that do I/O or reference external state which are stubbed out
|
46
|
+
# elsewhere.
|
47
|
+
describe "external dependencies" do
|
48
|
+
let(:config_loader) { described_class.new(nil) }
|
49
|
+
|
50
|
+
it "delegates to ENV for env" do
|
51
|
+
expect(config_loader.env).to equal(ENV)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "tests a path's existence" do
|
55
|
+
expect(config_loader.path_exists?("/nope/nope/nope/nope/slab/jab/nab")).to be(false)
|
56
|
+
expect(config_loader.path_exists?(__FILE__)).to be(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "locating the config file" do
|
62
|
+
context "without an explicit config" do
|
63
|
+
|
64
|
+
before do
|
65
|
+
allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "has no config if HOME is not set" do
|
69
|
+
expect(config_loader.config_location).to be(nil)
|
70
|
+
expect(config_loader.no_config_found?).to be(true)
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when HOME is set and contains a knife.rb" do
|
74
|
+
|
75
|
+
let(:home) { "/Users/example.user" }
|
76
|
+
|
77
|
+
before do
|
78
|
+
allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_yield(File.join(home, ".chef"))
|
79
|
+
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "uses the config in HOME/.chef/knife.rb" do
|
83
|
+
expect(config_loader.config_location).to eq("#{home}/.chef/knife.rb")
|
84
|
+
end
|
85
|
+
|
86
|
+
context "and has a config.rb" do
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/config.rb").and_return(true)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "uses the config in HOME/.chef/config.rb" do
|
93
|
+
expect(config_loader.config_location).to eq("#{home}/.chef/config.rb")
|
94
|
+
end
|
95
|
+
|
96
|
+
context "and/or a parent dir contains a .chef dir" do
|
97
|
+
|
98
|
+
let(:env_pwd) { "/path/to/cwd" }
|
99
|
+
|
100
|
+
before do
|
101
|
+
if ChefUtils.windows?
|
102
|
+
env["CD"] = env_pwd
|
103
|
+
else
|
104
|
+
env["PWD"] = env_pwd
|
105
|
+
end
|
106
|
+
|
107
|
+
allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/knife.rb").and_return(true)
|
108
|
+
allow(File).to receive(:exist?).with("#{env_pwd}/.chef").and_return(true)
|
109
|
+
allow(File).to receive(:directory?).with("#{env_pwd}/.chef").and_return(true)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "prefers the config from parent_dir/.chef" do
|
113
|
+
expect(config_loader.config_location).to eq("#{env_pwd}/.chef/knife.rb")
|
114
|
+
end
|
115
|
+
|
116
|
+
context "and the parent dir's .chef dir has a config.rb" do
|
117
|
+
|
118
|
+
before do
|
119
|
+
allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/config.rb").and_return(true)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "prefers the config from parent_dir/.chef" do
|
123
|
+
expect(config_loader.config_location).to eq("#{env_pwd}/.chef/config.rb")
|
124
|
+
end
|
125
|
+
|
126
|
+
context "and/or the current working directory contains a .chef dir" do
|
127
|
+
|
128
|
+
let(:cwd) { Dir.pwd }
|
129
|
+
|
130
|
+
before do
|
131
|
+
allow(config_loader).to receive(:path_exists?).with("#{cwd}/knife.rb").and_return(true)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "prefers a knife.rb located in the cwd" do
|
135
|
+
expect(config_loader.config_location).to eq("#{cwd}/knife.rb")
|
136
|
+
end
|
137
|
+
|
138
|
+
context "and the CWD's .chef dir has a config.rb" do
|
139
|
+
|
140
|
+
before do
|
141
|
+
allow(config_loader).to receive(:path_exists?).with("#{cwd}/config.rb").and_return(true)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "prefers a config located in the cwd" do
|
145
|
+
expect(config_loader.config_location).to eq("#{cwd}/config.rb")
|
146
|
+
end
|
147
|
+
|
148
|
+
context "and/or KNIFE_HOME is set" do
|
149
|
+
|
150
|
+
let(:knife_home) { "/path/to/knife/home" }
|
151
|
+
|
152
|
+
before do
|
153
|
+
env["KNIFE_HOME"] = knife_home
|
154
|
+
allow(config_loader).to receive(:path_exists?).with("#{knife_home}/knife.rb").and_return(true)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "prefers a knife located in KNIFE_HOME" do
|
158
|
+
expect(config_loader.config_location).to eq("/path/to/knife/home/knife.rb")
|
159
|
+
end
|
160
|
+
|
161
|
+
context "and KNIFE_HOME contains a config.rb" do
|
162
|
+
|
163
|
+
before do
|
164
|
+
env["KNIFE_HOME"] = knife_home
|
165
|
+
allow(config_loader).to receive(:path_exists?).with("#{knife_home}/config.rb").and_return(true)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "prefers a config.rb located in KNIFE_HOME" do
|
169
|
+
expect(config_loader.config_location).to eq("/path/to/knife/home/config.rb")
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "when the current working dir is inside a symlinked directory" do
|
183
|
+
before do
|
184
|
+
# pwd according to your shell is /home/someuser/prod/chef-repo, but
|
185
|
+
# chef-repo is a symlink to /home/someuser/codes/chef-repo
|
186
|
+
env["CD"] = "/home/someuser/prod/chef-repo" # windows
|
187
|
+
env["PWD"] = "/home/someuser/prod/chef-repo" # unix
|
188
|
+
|
189
|
+
allow(Dir).to receive(:pwd).and_return("/home/someuser/codes/chef-repo")
|
190
|
+
end
|
191
|
+
|
192
|
+
it "loads the config from the non-dereferenced directory path" do
|
193
|
+
expect(File).to receive(:exist?).with("/home/someuser/prod/chef-repo/.chef").and_return(false)
|
194
|
+
expect(File).to receive(:exist?).with("/home/someuser/prod/.chef").and_return(true)
|
195
|
+
expect(File).to receive(:directory?).with("/home/someuser/prod/.chef").and_return(true)
|
196
|
+
|
197
|
+
expect(config_loader).to receive(:path_exists?).with("/home/someuser/prod/.chef/knife.rb").and_return(true)
|
198
|
+
|
199
|
+
expect(config_loader.config_location).to eq("/home/someuser/prod/.chef/knife.rb")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "when given an explicit config to load" do
|
205
|
+
|
206
|
+
let(:explicit_config_location) { "/path/to/explicit/config.rb" }
|
207
|
+
|
208
|
+
it "prefers the explicit config" do
|
209
|
+
expect(config_loader.config_location).to eq(explicit_config_location)
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "loading the config file" do
|
216
|
+
|
217
|
+
context "when no explicit config is specified and no implicit config is found" do
|
218
|
+
|
219
|
+
before do
|
220
|
+
allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "skips loading" do
|
224
|
+
expect(config_loader.config_location).to be(nil)
|
225
|
+
expect(config_loader).not_to receive(:apply_config)
|
226
|
+
config_loader.load
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when an explicit config is given but it doesn't exist" do
|
232
|
+
|
233
|
+
let(:explicit_config_location) { "/nope/nope/nope/slab/jab/nab" }
|
234
|
+
|
235
|
+
it "raises a configuration error" do
|
236
|
+
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when the config file exists" do
|
242
|
+
|
243
|
+
let(:config_content) { "" }
|
244
|
+
|
245
|
+
# We need to keep a reference to the tempfile because while #close does
|
246
|
+
# not unlink the file, the object being GC'd will.
|
247
|
+
let(:tempfile) do
|
248
|
+
Tempfile.new("Chef-WorkstationConfigLoader-rspec-test").tap do |t|
|
249
|
+
t.print(config_content)
|
250
|
+
t.close
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
let(:explicit_config_location) do
|
255
|
+
tempfile.path
|
256
|
+
end
|
257
|
+
|
258
|
+
after { File.unlink(explicit_config_location) if File.exist?(explicit_config_location) }
|
259
|
+
|
260
|
+
context "and is valid" do
|
261
|
+
|
262
|
+
let(:config_content) { "config_file_evaluated(true)" }
|
263
|
+
|
264
|
+
it "loads the config" do
|
265
|
+
expect(config_loader).to receive(:apply_config).and_call_original
|
266
|
+
config_loader.load
|
267
|
+
expect(ChefConfig::Config.config_file_evaluated).to be(true)
|
268
|
+
end
|
269
|
+
|
270
|
+
it "sets ChefConfig::Config.config_file" do
|
271
|
+
config_loader.load
|
272
|
+
expect(ChefConfig::Config.config_file).to eq(explicit_config_location)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "loads a default value for node_name" do
|
276
|
+
allow(Etc).to receive(:getlogin).and_return("notauser")
|
277
|
+
config_loader.load
|
278
|
+
expect(ChefConfig::Config.node_name).to eq("notauser")
|
279
|
+
end
|
280
|
+
|
281
|
+
context "with a user.pem" do
|
282
|
+
before do
|
283
|
+
allow(Etc).to receive(:getlogin).and_return("notauser")
|
284
|
+
allow(FileTest).to receive(:exist?).and_call_original
|
285
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(false)
|
286
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(true)
|
287
|
+
end
|
288
|
+
|
289
|
+
it "loads a default value for client_key" do
|
290
|
+
config_loader.load
|
291
|
+
expect(ChefConfig::Config.client_key).to eq(File.expand_path("../user.pem", explicit_config_location))
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
context "with a notauser.pem" do
|
296
|
+
before do
|
297
|
+
allow(Etc).to receive(:getlogin).and_return("notauser")
|
298
|
+
allow(FileTest).to receive(:exist?).and_call_original
|
299
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../notauser.pem", explicit_config_location)).and_return(true)
|
300
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../user.pem", explicit_config_location)).and_return(false)
|
301
|
+
end
|
302
|
+
|
303
|
+
it "loads a default value for client_key" do
|
304
|
+
config_loader.load
|
305
|
+
expect(ChefConfig::Config.client_key).to eq(File.expand_path("../notauser.pem", explicit_config_location))
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "with a valclient.pem" do
|
310
|
+
before do
|
311
|
+
ChefConfig::Config.validation_client_name = "valclient"
|
312
|
+
allow(FileTest).to receive(:exist?).and_call_original
|
313
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(true)
|
314
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(false)
|
315
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "loads a default value for validation_key" do
|
319
|
+
config_loader.load
|
320
|
+
expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../valclient.pem", explicit_config_location))
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
context "with a validator.pem" do
|
325
|
+
before do
|
326
|
+
ChefConfig::Config.validation_client_name = "valclient"
|
327
|
+
allow(FileTest).to receive(:exist?).and_call_original
|
328
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../valclient.pem", explicit_config_location)).and_return(false)
|
329
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../validator.pem", explicit_config_location)).and_return(true)
|
330
|
+
allow(FileTest).to receive(:exist?).with(File.expand_path("../validation.pem", explicit_config_location)).and_return(false)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "loads a default value for validation_key" do
|
334
|
+
config_loader.load
|
335
|
+
expect(ChefConfig::Config.validation_key).to eq(File.expand_path("../validator.pem", explicit_config_location))
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context "and has a syntax error" do
|
341
|
+
|
342
|
+
let(:config_content) { "{{{{{:{{" }
|
343
|
+
|
344
|
+
it "raises a ConfigurationError" do
|
345
|
+
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
context "and raises a ruby exception during evaluation" do
|
350
|
+
|
351
|
+
let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" }
|
352
|
+
|
353
|
+
it "raises a ConfigurationError" do
|
354
|
+
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "when loading config.d" do
|
363
|
+
context "when the conf.d directory exists" do
|
364
|
+
let(:config_content) { "" }
|
365
|
+
|
366
|
+
let(:tempdir) { Dir.mktmpdir("chef-workstation-test") }
|
367
|
+
|
368
|
+
let!(:confd_file) do
|
369
|
+
Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".rb"], tempdir).tap do |t|
|
370
|
+
t.print(config_content)
|
371
|
+
t.close
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
before do
|
376
|
+
ChefConfig::Config[:config_d_dir] = tempdir
|
377
|
+
allow(config_loader).to receive(:path_exists?).with(
|
378
|
+
an_instance_of(String)
|
379
|
+
).and_return(false)
|
380
|
+
end
|
381
|
+
|
382
|
+
after do
|
383
|
+
FileUtils.remove_entry_secure tempdir
|
384
|
+
end
|
385
|
+
|
386
|
+
context "and is valid" do
|
387
|
+
let(:config_content) { "config_d_file_evaluated(true)" }
|
388
|
+
|
389
|
+
it "loads the config" do
|
390
|
+
expect(config_loader).to receive(:apply_config).and_call_original
|
391
|
+
config_loader.load
|
392
|
+
expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
context "and has a syntax error" do
|
397
|
+
let(:config_content) { "{{{{{:{{" }
|
398
|
+
|
399
|
+
it "raises a ConfigurationError" do
|
400
|
+
expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
context "has a non rb file" do
|
405
|
+
let(:syntax_error_content) { "{{{{{:{{" }
|
406
|
+
let(:config_content) { "config_d_file_evaluated(true)" }
|
407
|
+
|
408
|
+
let!(:not_confd_file) do
|
409
|
+
Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".foorb"], tempdir).tap do |t|
|
410
|
+
t.print(syntax_error_content)
|
411
|
+
t.close
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
it "does not load the non rb file" do
|
416
|
+
expect { config_loader.load }.not_to raise_error
|
417
|
+
expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context "when the conf.d directory does not exist" do
|
423
|
+
before do
|
424
|
+
ChefConfig::Config[:config_d_dir] = "/nope/nope/nope/nope/notdoingit"
|
425
|
+
end
|
426
|
+
|
427
|
+
it "does not load anything" do
|
428
|
+
expect(config_loader).not_to receive(:apply_config)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
describe "when loading a credentials file" do
|
434
|
+
if ChefUtils.windows?
|
435
|
+
let(:home) { "C:/Users/example.user" }
|
436
|
+
else
|
437
|
+
let(:home) { "/Users/example.user" }
|
438
|
+
end
|
439
|
+
let(:credentials_file) { "#{home}/.chef/credentials" }
|
440
|
+
let(:context_file) { "#{home}/.chef/context" }
|
441
|
+
|
442
|
+
before do
|
443
|
+
allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef"))
|
444
|
+
allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file)
|
445
|
+
allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file)
|
446
|
+
allow(File).to receive(:file?).with(context_file).and_return false
|
447
|
+
end
|
448
|
+
|
449
|
+
context "when the file exists" do
|
450
|
+
before do
|
451
|
+
expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content)
|
452
|
+
allow(File).to receive(:file?).with(credentials_file).and_return true
|
453
|
+
end
|
454
|
+
|
455
|
+
context "and has a default profile" do
|
456
|
+
let(:content) do
|
457
|
+
content = <<~EOH
|
458
|
+
[default]
|
459
|
+
node_name = 'barney'
|
460
|
+
client_key = "barney_rubble.pem"
|
461
|
+
chef_server_url = "https://api.chef.io/organizations/bedrock"
|
462
|
+
invalid_config_option1234 = "foobar"
|
463
|
+
EOH
|
464
|
+
content
|
465
|
+
end
|
466
|
+
|
467
|
+
it "applies the expected config" do
|
468
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
469
|
+
expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
|
470
|
+
expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
|
471
|
+
expect(ChefConfig::Config.profile.to_s).to eq("default")
|
472
|
+
expect(ChefConfig::Config[:invalid_config_option1234]).to eq("foobar")
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
context "and has a default profile with knife settings" do
|
477
|
+
let(:content) do
|
478
|
+
content = <<~EOH
|
479
|
+
[default]
|
480
|
+
node_name = 'barney'
|
481
|
+
client_key = "barney_rubble.pem"
|
482
|
+
chef_server_url = "https://api.chef.io/organizations/bedrock"
|
483
|
+
knife = {
|
484
|
+
secret_file = "/home/barney/.chef/encrypted_data_bag_secret.pem"
|
485
|
+
}
|
486
|
+
[default.knife]
|
487
|
+
ssh_user = "knife_ssh_user"
|
488
|
+
EOH
|
489
|
+
content
|
490
|
+
end
|
491
|
+
|
492
|
+
it "applies the expected knife config" do
|
493
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
494
|
+
expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
|
495
|
+
expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
|
496
|
+
expect(ChefConfig::Config.knife[:ssh_user].to_s).to eq("knife_ssh_user")
|
497
|
+
expect(ChefConfig::Config.knife[:secret_file].to_s).to eq("/home/barney/.chef/encrypted_data_bag_secret.pem")
|
498
|
+
expect(ChefConfig::Config.profile.to_s).to eq("default")
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "and has a profile containing a full key" do
|
503
|
+
let(:content) do
|
504
|
+
content = <<~EOH
|
505
|
+
[default]
|
506
|
+
client_key = """
|
507
|
+
-----BEGIN RSA PRIVATE KEY-----
|
508
|
+
foo
|
509
|
+
"""
|
510
|
+
EOH
|
511
|
+
content
|
512
|
+
end
|
513
|
+
|
514
|
+
it "applies the expected config" do
|
515
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
516
|
+
expect(ChefConfig::Config.client_key_contents).to eq(<<~EOH
|
517
|
+
-----BEGIN RSA PRIVATE KEY-----
|
518
|
+
foo
|
519
|
+
EOH
|
520
|
+
)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
context "and has several profiles" do
|
525
|
+
let(:content) do
|
526
|
+
content = <<~EOH
|
527
|
+
[default]
|
528
|
+
client_name = "default"
|
529
|
+
[environment]
|
530
|
+
client_name = "environment"
|
531
|
+
[explicit]
|
532
|
+
client_name = "explicit"
|
533
|
+
[context]
|
534
|
+
client_name = "context"
|
535
|
+
EOH
|
536
|
+
content
|
537
|
+
end
|
538
|
+
|
539
|
+
let(:env) { {} }
|
540
|
+
before do
|
541
|
+
stub_const("ENV", env)
|
542
|
+
end
|
543
|
+
|
544
|
+
it "selects the correct profile explicitly" do
|
545
|
+
expect { config_loader.load_credentials("explicit") }.not_to raise_error
|
546
|
+
expect(ChefConfig::Config.node_name).to eq("explicit")
|
547
|
+
end
|
548
|
+
|
549
|
+
context "with an environment variable" do
|
550
|
+
let(:env) { { "CHEF_PROFILE" => "environment" } }
|
551
|
+
|
552
|
+
it "selects the correct profile" do
|
553
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
554
|
+
expect(ChefConfig::Config.node_name).to eq("environment")
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
it "selects the correct profile with a context file" do
|
559
|
+
allow(File).to receive(:file?).with(context_file).and_return true
|
560
|
+
expect(File).to receive(:read).with(context_file).and_return "context"
|
561
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
562
|
+
expect(ChefConfig::Config.node_name).to eq("context")
|
563
|
+
end
|
564
|
+
|
565
|
+
it "falls back to the default" do
|
566
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
567
|
+
expect(ChefConfig::Config.node_name).to eq("default")
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
context "and contains both node_name and client_name" do
|
572
|
+
let(:content) do
|
573
|
+
content = <<~EOH
|
574
|
+
[default]
|
575
|
+
node_name = 'barney'
|
576
|
+
client_name = 'barney'
|
577
|
+
EOH
|
578
|
+
content
|
579
|
+
end
|
580
|
+
|
581
|
+
it "raises a ConfigurationError" do
|
582
|
+
expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
context "and ssl_verify_mode is a symbol string" do
|
587
|
+
let(:content) do
|
588
|
+
content = <<~EOH
|
589
|
+
[default]
|
590
|
+
ssl_verify_mode = ":verify_none"
|
591
|
+
EOH
|
592
|
+
content
|
593
|
+
end
|
594
|
+
|
595
|
+
it "raises a ConfigurationError" do
|
596
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
597
|
+
expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
context "and ssl_verify_mode is a string" do
|
602
|
+
let(:content) do
|
603
|
+
content = <<~EOH
|
604
|
+
[default]
|
605
|
+
ssl_verify_mode = "verify_none"
|
606
|
+
EOH
|
607
|
+
content
|
608
|
+
end
|
609
|
+
|
610
|
+
it "raises a ConfigurationError" do
|
611
|
+
expect { config_loader.load_credentials }.not_to raise_error
|
612
|
+
expect(ChefConfig::Config.ssl_verify_mode).to eq(:verify_none)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
context "and has a syntax error" do
|
617
|
+
let(:content) { "<<<<<" }
|
618
|
+
|
619
|
+
it "raises a ConfigurationError" do
|
620
|
+
expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
context "when the file does not exist" do
|
626
|
+
it "does not load anything" do
|
627
|
+
allow(File).to receive(:file?).with(credentials_file).and_return false
|
628
|
+
expect(Tomlrb).not_to receive(:load_file)
|
629
|
+
config_loader.load_credentials
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
end
|