chef-config 14.9.13 → 14.10.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,602 +1,602 @@
1
- #
2
- # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright 2014-2016, 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"
21
-
22
- require "chef-config/exceptions"
23
- require "chef-config/windows"
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/frab/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 ChefConfig.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 specifed 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/frab/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)).and_return(false)
379
- end
380
-
381
- after do
382
- FileUtils.remove_entry_secure tempdir
383
- end
384
-
385
- context "and is valid" do
386
- let(:config_content) { "config_d_file_evaluated(true)" }
387
-
388
- it "loads the config" do
389
- expect(config_loader).to receive(:apply_config).and_call_original
390
- config_loader.load
391
- expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
392
- end
393
- end
394
-
395
- context "and has a syntax error" do
396
- let(:config_content) { "{{{{{:{{" }
397
-
398
- it "raises a ConfigurationError" do
399
- expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
400
- end
401
- end
402
-
403
- context "has a non rb file" do
404
- let(:sytax_error_content) { "{{{{{:{{" }
405
- let(:config_content) { "config_d_file_evaluated(true)" }
406
-
407
- let!(:not_confd_file) do
408
- Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".foorb"], tempdir).tap do |t|
409
- t.print(sytax_error_content)
410
- t.close
411
- end
412
- end
413
-
414
- it "does not load the non rb file" do
415
- expect { config_loader.load }.not_to raise_error
416
- expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
417
- end
418
- end
419
- end
420
-
421
- context "when the conf.d directory does not exist" do
422
- before do
423
- ChefConfig::Config[:config_d_dir] = "/nope/nope/nope/nope/notdoingit"
424
- end
425
-
426
- it "does not load anything" do
427
- expect(config_loader).not_to receive(:apply_config)
428
- end
429
- end
430
- end
431
-
432
- describe "when loading a credentials file" do
433
- if ChefConfig.windows?
434
- let(:home) { "C:/Users/example.user" }
435
- else
436
- let(:home) { "/Users/example.user" }
437
- end
438
- let(:credentials_file) { "#{home}/.chef/credentials" }
439
- let(:context_file) { "#{home}/.chef/context" }
440
-
441
- before do
442
- allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef"))
443
- allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file)
444
- allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file)
445
- allow(File).to receive(:file?).with(context_file).and_return false
446
- end
447
-
448
- context "when the file exists" do
449
- before do
450
- expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content)
451
- allow(File).to receive(:file?).with(credentials_file).and_return true
452
- end
453
-
454
- context "and has a default profile" do
455
- let(:content) do
456
- content = <<~EOH
457
- [default]
458
- node_name = 'barney'
459
- client_key = "barney_rubble.pem"
460
- chef_server_url = "https://api.chef.io/organizations/bedrock"
461
- invalid_config_option1234 = "foobar"
462
- EOH
463
- content
464
- end
465
-
466
- it "applies the expected config" do
467
- expect { config_loader.load_credentials }.not_to raise_error
468
- expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
469
- expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
470
- expect(ChefConfig::Config.profile.to_s).to eq("default")
471
- expect(ChefConfig::Config[:invalid_config_option1234]).to eq("foobar")
472
- end
473
- end
474
-
475
- context "and has a default profile with knife settings" do
476
- let(:content) do
477
- content = <<~EOH
478
- [default]
479
- node_name = 'barney'
480
- client_key = "barney_rubble.pem"
481
- chef_server_url = "https://api.chef.io/organizations/bedrock"
482
- knife = {
483
- secret_file = "/home/barney/.chef/encrypted_data_bag_secret.pem"
484
- }
485
- [default.knife]
486
- ssh_user = "knife_ssh_user"
487
- EOH
488
- content
489
- end
490
-
491
- it "applies the expected knife config" do
492
- expect { config_loader.load_credentials }.not_to raise_error
493
- expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
494
- expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
495
- expect(ChefConfig::Config.knife[:ssh_user].to_s).to eq("knife_ssh_user")
496
- expect(ChefConfig::Config.knife[:secret_file].to_s).to eq("/home/barney/.chef/encrypted_data_bag_secret.pem")
497
- expect(ChefConfig::Config.profile.to_s).to eq("default")
498
- end
499
- end
500
-
501
- context "and has a profile containing a full key" do
502
- let(:content) do
503
- content = <<~EOH
504
- [default]
505
- client_key = """
506
- -----BEGIN RSA PRIVATE KEY-----
507
- foo
508
- """
509
- EOH
510
- content
511
- end
512
-
513
- it "applies the expected config" do
514
- expect { config_loader.load_credentials }.not_to raise_error
515
- expect(ChefConfig::Config.client_key_contents).to eq(<<~EOH
516
- -----BEGIN RSA PRIVATE KEY-----
517
- foo
518
- EOH
519
- )
520
- end
521
- end
522
-
523
- context "and has several profiles" do
524
- let(:content) do
525
- content = <<~EOH
526
- [default]
527
- client_name = "default"
528
- [environment]
529
- client_name = "environment"
530
- [explicit]
531
- client_name = "explicit"
532
- [context]
533
- client_name = "context"
534
- EOH
535
- content
536
- end
537
-
538
- let(:env) { {} }
539
- before do
540
- stub_const("ENV", env)
541
- end
542
-
543
- it "selects the correct profile explicitly" do
544
- expect { config_loader.load_credentials("explicit") }.not_to raise_error
545
- expect(ChefConfig::Config.node_name).to eq("explicit")
546
- end
547
-
548
- context "with an environment variable" do
549
- let(:env) { { "CHEF_PROFILE" => "environment" } }
550
-
551
- it "selects the correct profile" do
552
- expect { config_loader.load_credentials }.not_to raise_error
553
- expect(ChefConfig::Config.node_name).to eq("environment")
554
- end
555
- end
556
-
557
- it "selects the correct profile with a context file" do
558
- allow(File).to receive(:file?).with(context_file).and_return true
559
- expect(File).to receive(:read).with(context_file).and_return "context"
560
- expect { config_loader.load_credentials }.not_to raise_error
561
- expect(ChefConfig::Config.node_name).to eq("context")
562
- end
563
-
564
- it "falls back to the default" do
565
- expect { config_loader.load_credentials }.not_to raise_error
566
- expect(ChefConfig::Config.node_name).to eq("default")
567
- end
568
- end
569
-
570
- context "and contains both node_name and client_name" do
571
- let(:content) do
572
- content = <<~EOH
573
- [default]
574
- node_name = 'barney'
575
- client_name = 'barney'
576
- EOH
577
- content
578
- end
579
-
580
- it "raises a ConfigurationError" do
581
- expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
582
- end
583
- end
584
-
585
- context "and has a syntax error" do
586
- let(:content) { "<<<<<" }
587
-
588
- it "raises a ConfigurationError" do
589
- expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
590
- end
591
- end
592
- end
593
-
594
- context "when the file does not exist" do
595
- it "does not load anything" do
596
- allow(File).to receive(:file?).with(credentials_file).and_return false
597
- expect(Tomlrb).not_to receive(:load_file)
598
- config_loader.load_credentials
599
- end
600
- end
601
- end
602
- end
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Copyright:: Copyright 2014-2016, 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"
21
+
22
+ require "chef-config/exceptions"
23
+ require "chef-config/windows"
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/frab/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 ChefConfig.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 specifed 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/frab/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)).and_return(false)
379
+ end
380
+
381
+ after do
382
+ FileUtils.remove_entry_secure tempdir
383
+ end
384
+
385
+ context "and is valid" do
386
+ let(:config_content) { "config_d_file_evaluated(true)" }
387
+
388
+ it "loads the config" do
389
+ expect(config_loader).to receive(:apply_config).and_call_original
390
+ config_loader.load
391
+ expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
392
+ end
393
+ end
394
+
395
+ context "and has a syntax error" do
396
+ let(:config_content) { "{{{{{:{{" }
397
+
398
+ it "raises a ConfigurationError" do
399
+ expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
400
+ end
401
+ end
402
+
403
+ context "has a non rb file" do
404
+ let(:sytax_error_content) { "{{{{{:{{" }
405
+ let(:config_content) { "config_d_file_evaluated(true)" }
406
+
407
+ let!(:not_confd_file) do
408
+ Tempfile.new(["Chef-WorkstationConfigLoader-rspec-test", ".foorb"], tempdir).tap do |t|
409
+ t.print(sytax_error_content)
410
+ t.close
411
+ end
412
+ end
413
+
414
+ it "does not load the non rb file" do
415
+ expect { config_loader.load }.not_to raise_error
416
+ expect(ChefConfig::Config.config_d_file_evaluated).to be(true)
417
+ end
418
+ end
419
+ end
420
+
421
+ context "when the conf.d directory does not exist" do
422
+ before do
423
+ ChefConfig::Config[:config_d_dir] = "/nope/nope/nope/nope/notdoingit"
424
+ end
425
+
426
+ it "does not load anything" do
427
+ expect(config_loader).not_to receive(:apply_config)
428
+ end
429
+ end
430
+ end
431
+
432
+ describe "when loading a credentials file" do
433
+ if ChefConfig.windows?
434
+ let(:home) { "C:/Users/example.user" }
435
+ else
436
+ let(:home) { "/Users/example.user" }
437
+ end
438
+ let(:credentials_file) { "#{home}/.chef/credentials" }
439
+ let(:context_file) { "#{home}/.chef/context" }
440
+
441
+ before do
442
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef"))
443
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file)
444
+ allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file)
445
+ allow(File).to receive(:file?).with(context_file).and_return false
446
+ end
447
+
448
+ context "when the file exists" do
449
+ before do
450
+ expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content)
451
+ allow(File).to receive(:file?).with(credentials_file).and_return true
452
+ end
453
+
454
+ context "and has a default profile" do
455
+ let(:content) do
456
+ content = <<~EOH
457
+ [default]
458
+ node_name = 'barney'
459
+ client_key = "barney_rubble.pem"
460
+ chef_server_url = "https://api.chef.io/organizations/bedrock"
461
+ invalid_config_option1234 = "foobar"
462
+ EOH
463
+ content
464
+ end
465
+
466
+ it "applies the expected config" do
467
+ expect { config_loader.load_credentials }.not_to raise_error
468
+ expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
469
+ expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
470
+ expect(ChefConfig::Config.profile.to_s).to eq("default")
471
+ expect(ChefConfig::Config[:invalid_config_option1234]).to eq("foobar")
472
+ end
473
+ end
474
+
475
+ context "and has a default profile with knife settings" do
476
+ let(:content) do
477
+ content = <<~EOH
478
+ [default]
479
+ node_name = 'barney'
480
+ client_key = "barney_rubble.pem"
481
+ chef_server_url = "https://api.chef.io/organizations/bedrock"
482
+ knife = {
483
+ secret_file = "/home/barney/.chef/encrypted_data_bag_secret.pem"
484
+ }
485
+ [default.knife]
486
+ ssh_user = "knife_ssh_user"
487
+ EOH
488
+ content
489
+ end
490
+
491
+ it "applies the expected knife config" do
492
+ expect { config_loader.load_credentials }.not_to raise_error
493
+ expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock")
494
+ expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem")
495
+ expect(ChefConfig::Config.knife[:ssh_user].to_s).to eq("knife_ssh_user")
496
+ expect(ChefConfig::Config.knife[:secret_file].to_s).to eq("/home/barney/.chef/encrypted_data_bag_secret.pem")
497
+ expect(ChefConfig::Config.profile.to_s).to eq("default")
498
+ end
499
+ end
500
+
501
+ context "and has a profile containing a full key" do
502
+ let(:content) do
503
+ content = <<~EOH
504
+ [default]
505
+ client_key = """
506
+ -----BEGIN RSA PRIVATE KEY-----
507
+ foo
508
+ """
509
+ EOH
510
+ content
511
+ end
512
+
513
+ it "applies the expected config" do
514
+ expect { config_loader.load_credentials }.not_to raise_error
515
+ expect(ChefConfig::Config.client_key_contents).to eq(<<~EOH
516
+ -----BEGIN RSA PRIVATE KEY-----
517
+ foo
518
+ EOH
519
+ )
520
+ end
521
+ end
522
+
523
+ context "and has several profiles" do
524
+ let(:content) do
525
+ content = <<~EOH
526
+ [default]
527
+ client_name = "default"
528
+ [environment]
529
+ client_name = "environment"
530
+ [explicit]
531
+ client_name = "explicit"
532
+ [context]
533
+ client_name = "context"
534
+ EOH
535
+ content
536
+ end
537
+
538
+ let(:env) { {} }
539
+ before do
540
+ stub_const("ENV", env)
541
+ end
542
+
543
+ it "selects the correct profile explicitly" do
544
+ expect { config_loader.load_credentials("explicit") }.not_to raise_error
545
+ expect(ChefConfig::Config.node_name).to eq("explicit")
546
+ end
547
+
548
+ context "with an environment variable" do
549
+ let(:env) { { "CHEF_PROFILE" => "environment" } }
550
+
551
+ it "selects the correct profile" do
552
+ expect { config_loader.load_credentials }.not_to raise_error
553
+ expect(ChefConfig::Config.node_name).to eq("environment")
554
+ end
555
+ end
556
+
557
+ it "selects the correct profile with a context file" do
558
+ allow(File).to receive(:file?).with(context_file).and_return true
559
+ expect(File).to receive(:read).with(context_file).and_return "context"
560
+ expect { config_loader.load_credentials }.not_to raise_error
561
+ expect(ChefConfig::Config.node_name).to eq("context")
562
+ end
563
+
564
+ it "falls back to the default" do
565
+ expect { config_loader.load_credentials }.not_to raise_error
566
+ expect(ChefConfig::Config.node_name).to eq("default")
567
+ end
568
+ end
569
+
570
+ context "and contains both node_name and client_name" do
571
+ let(:content) do
572
+ content = <<~EOH
573
+ [default]
574
+ node_name = 'barney'
575
+ client_name = 'barney'
576
+ EOH
577
+ content
578
+ end
579
+
580
+ it "raises a ConfigurationError" do
581
+ expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
582
+ end
583
+ end
584
+
585
+ context "and has a syntax error" do
586
+ let(:content) { "<<<<<" }
587
+
588
+ it "raises a ConfigurationError" do
589
+ expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError)
590
+ end
591
+ end
592
+ end
593
+
594
+ context "when the file does not exist" do
595
+ it "does not load anything" do
596
+ allow(File).to receive(:file?).with(credentials_file).and_return false
597
+ expect(Tomlrb).not_to receive(:load_file)
598
+ config_loader.load_credentials
599
+ end
600
+ end
601
+ end
602
+ end