knife-windows 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +139 -135
  4. data/DOC_CHANGES.md +22 -22
  5. data/Gemfile +13 -13
  6. data/README.md +404 -404
  7. data/RELEASE_NOTES.md +9 -9
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +26 -26
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -246
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -443
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -116
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -102
  15. data/lib/chef/knife/core/windows_bootstrap_context.rb +378 -378
  16. data/lib/chef/knife/knife_windows_base.rb +33 -33
  17. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  18. data/lib/chef/knife/windows_cert_install.rb +68 -68
  19. data/lib/chef/knife/windows_helper.rb +36 -36
  20. data/lib/chef/knife/windows_listener_create.rb +107 -107
  21. data/lib/chef/knife/winrm.rb +122 -122
  22. data/lib/chef/knife/winrm_base.rb +128 -128
  23. data/lib/chef/knife/winrm_knife_base.rb +307 -307
  24. data/lib/chef/knife/winrm_session.rb +98 -98
  25. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  26. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  27. data/lib/chef/knife/wsman_test.rb +118 -118
  28. data/lib/knife-windows/path_helper.rb +242 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
  31. data/spec/assets/fake_trusted_certs/github.pem +42 -0
  32. data/spec/assets/fake_trusted_certs/google.crt +41 -0
  33. data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
  34. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  38. data/spec/assets/win_template_unrendered.txt +246 -246
  39. data/spec/dummy_winrm_connection.rb +21 -21
  40. data/spec/functional/bootstrap_download_spec.rb +236 -236
  41. data/spec/spec_helper.rb +94 -94
  42. data/spec/unit/knife/bootstrap_options_spec.rb +157 -157
  43. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -423
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +213 -177
  46. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  47. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  48. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  49. data/spec/unit/knife/winrm_session_spec.rb +95 -95
  50. data/spec/unit/knife/winrm_spec.rb +500 -500
  51. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  52. metadata +7 -3
@@ -1,98 +1,98 @@
1
- #
2
- # Author:: Chirag Jog (<chirag@clogeny.com>)
3
- # Copyright:: Copyright (c) 2013 Chirag Jog
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
- TEMPLATE_FILE = File.expand_path(File.dirname(__FILE__)) + "/../../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
20
-
21
- require 'spec_helper'
22
-
23
- describe Chef::Knife::BootstrapWindowsWinrm do
24
- let(:template_file) { TEMPLATE_FILE }
25
- let(:options) { [] }
26
- let(:rendered_template) do
27
- knife.instance_variable_set("@template_file", template_file)
28
- knife.parse_options(options)
29
- # Avoid referencing a validation keyfile we won't find during #render_template
30
- template = IO.read(template_file).chomp
31
- knife.render_template(template)
32
- end
33
- subject(:knife) { described_class.new }
34
-
35
- before(:all) do
36
- @original_config = Chef::Config.hash_dup
37
- @original_knife_config = Chef::Config[:knife].dup
38
- end
39
-
40
- after(:all) do
41
- Chef::Config.configuration = @original_config
42
- Chef::Config[:knife] = @original_knife_config
43
- end
44
-
45
- before(:each) do
46
- Chef::Log.logger = Logger.new(StringIO.new)
47
- @knife = Chef::Knife::BootstrapWindowsWinrm.new
48
- # Merge default settings in.
49
- @knife.merge_configs
50
- @knife.config[:template_file] = template_file
51
- @stdout = StringIO.new
52
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
53
- @stderr = StringIO.new
54
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
55
- end
56
-
57
- describe "specifying no_proxy with various entries" do
58
- let(:options){ ["--bootstrap-proxy", "", "--bootstrap-no-proxy", setting] }
59
-
60
- context "via --bootstrap-no-proxy" do
61
- let(:setting) { "api.chef.io" }
62
-
63
- it "renders the client.rb with a single FQDN no_proxy entry" do
64
- expect(rendered_template).to match(%r{.*no_proxy\s*\"api.chef.io\".*})
65
- end
66
- end
67
- context "via --bootstrap-no-proxy multiple" do
68
- let(:setting) { "api.chef.io,172.16.10.*" }
69
-
70
- it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
71
- expect(rendered_template).to match(%r{.*no_proxy\s*"api.chef.io,172.16.10.\*".*})
72
- end
73
- end
74
- end
75
-
76
- describe "specifying --msi-url" do
77
- context "with explicitly provided --msi-url" do
78
- let(:options) { ["--msi-url", "file:///something.msi"] }
79
-
80
- it "bootstrap batch file must fetch from provided url" do
81
- expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=file:///something\.msi.*})
82
- end
83
- end
84
- context "with no provided --msi-url" do
85
- it "bootstrap batch file must fetch from provided url" do
86
- expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=https://www\.chef\.io/.*})
87
- end
88
- end
89
- end
90
-
91
- describe "specifying knife_config[:architecture]" do
92
- it "puts the target architecture into the msi_url" do
93
- Chef::Config[:knife][:architecture] = :x86_64
94
- expect(rendered_template).to match(/MACHINE_ARCH=x86_64/)
95
- end
96
- end
97
-
98
- end
1
+ #
2
+ # Author:: Chirag Jog (<chirag@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Chirag Jog
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
+ TEMPLATE_FILE = File.expand_path(File.dirname(__FILE__)) + "/../../../lib/chef/knife/bootstrap/windows-chef-client-msi.erb"
20
+
21
+ require 'spec_helper'
22
+
23
+ describe Chef::Knife::BootstrapWindowsWinrm do
24
+ let(:template_file) { TEMPLATE_FILE }
25
+ let(:options) { [] }
26
+ let(:rendered_template) do
27
+ knife.instance_variable_set("@template_file", template_file)
28
+ knife.parse_options(options)
29
+ # Avoid referencing a validation keyfile we won't find during #render_template
30
+ template = IO.read(template_file).chomp
31
+ knife.render_template(template)
32
+ end
33
+ subject(:knife) { described_class.new }
34
+
35
+ before(:all) do
36
+ @original_config = Chef::Config.hash_dup
37
+ @original_knife_config = Chef::Config[:knife].dup
38
+ end
39
+
40
+ after(:all) do
41
+ Chef::Config.configuration = @original_config
42
+ Chef::Config[:knife] = @original_knife_config
43
+ end
44
+
45
+ before(:each) do
46
+ Chef::Log.logger = Logger.new(StringIO.new)
47
+ @knife = Chef::Knife::BootstrapWindowsWinrm.new
48
+ # Merge default settings in.
49
+ @knife.merge_configs
50
+ @knife.config[:template_file] = template_file
51
+ @stdout = StringIO.new
52
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
53
+ @stderr = StringIO.new
54
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
55
+ end
56
+
57
+ describe "specifying no_proxy with various entries" do
58
+ let(:options){ ["--bootstrap-proxy", "", "--bootstrap-no-proxy", setting] }
59
+
60
+ context "via --bootstrap-no-proxy" do
61
+ let(:setting) { "api.chef.io" }
62
+
63
+ it "renders the client.rb with a single FQDN no_proxy entry" do
64
+ expect(rendered_template).to match(%r{.*no_proxy\s*\"api.chef.io\".*})
65
+ end
66
+ end
67
+ context "via --bootstrap-no-proxy multiple" do
68
+ let(:setting) { "api.chef.io,172.16.10.*" }
69
+
70
+ it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do
71
+ expect(rendered_template).to match(%r{.*no_proxy\s*"api.chef.io,172.16.10.\*".*})
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "specifying --msi-url" do
77
+ context "with explicitly provided --msi-url" do
78
+ let(:options) { ["--msi-url", "file:///something.msi"] }
79
+
80
+ it "bootstrap batch file must fetch from provided url" do
81
+ expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=file:///something\.msi.*})
82
+ end
83
+ end
84
+ context "with no provided --msi-url" do
85
+ it "bootstrap batch file must fetch from provided url" do
86
+ expect(rendered_template).to match(%r{.*REMOTE_SOURCE_MSI_URL=https://www\.chef\.io/.*})
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "specifying knife_config[:architecture]" do
92
+ it "puts the target architecture into the msi_url" do
93
+ Chef::Config[:knife][:architecture] = :x86_64
94
+ expect(rendered_template).to match(/MACHINE_ARCH=x86_64/)
95
+ end
96
+ end
97
+
98
+ end
@@ -1,423 +1,423 @@
1
- #
2
- # Author:: Adam Edwards(<adamed@chef.io>)
3
- # Copyright:: Copyright (c) 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 'winrm/output'
21
-
22
- Chef::Knife::Winrm.load_deps
23
-
24
- describe Chef::Knife::BootstrapWindowsWinrm do
25
- before do
26
- Chef::Config.reset
27
- bootstrap.config[:run_list] = []
28
- allow(bootstrap).to receive(:validate_options!).and_return(nil)
29
- allow(bootstrap).to receive(:sleep).and_return(10)
30
- allow(Chef::Knife::WinrmSession).to receive(:new).and_return(session)
31
- allow(File).to receive(:exist?).with(anything).and_call_original
32
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
33
- end
34
-
35
- after do
36
- allow(bootstrap).to receive(:sleep).and_return(10)
37
- end
38
-
39
- let(:session_opts) do
40
- {
41
- user: "Administrator",
42
- password: "testpassword",
43
- port: "5986",
44
- transport: :ssl,
45
- host: "localhost"
46
- }
47
- end
48
- let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', session_opts[:user], '-P', session_opts[:password], session_opts[:host]]) }
49
- let(:session) { Chef::Knife::WinrmSession.new(session_opts) }
50
- let(:arch_session_result) {
51
- o = WinRM::Output.new
52
- o << {stdout: "X86\r\n"}
53
- o
54
- }
55
- let(:arch_session_results) { [arch_session_result] }
56
- let(:initial_fail_count) { 4 }
57
-
58
- context "knife secret-file && knife secret options are passed" do
59
- before do
60
- Chef::Config.reset
61
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
62
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
63
- end
64
- it "gives preference to secret key passed under knife's secret-file option" do
65
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
66
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
67
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
68
- expect(bootstrap.load_correct_secret).to eq(
69
- "data_bag_secret_key_passed_under_knife_secret_file_option")
70
- end
71
- end
72
-
73
- context "cli secret-file && cli secret options are passed" do
74
- before do
75
- Chef::Config.reset
76
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
77
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
78
- end
79
- it "gives preference to secret key passed under cli's secret-file option" do
80
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
81
- bootstrap.config[:encrypted_data_bag_secret_file]).
82
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
83
- expect(bootstrap.load_correct_secret).to eq(
84
- "data_bag_secret_key_passed_under_cli_secret_file_option")
85
- end
86
- end
87
-
88
- context "knife secret-file, knife secret, cli secret-file && cli secret options are passed" do
89
- before do
90
- Chef::Config.reset
91
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/knife_encrypted_data_bag_secret"
92
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
93
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/cli_encrypted_data_bag_secret"
94
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
95
- end
96
- it "gives preference to secret key passed under cli's secret-file option" do
97
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
98
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
99
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
100
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
101
- bootstrap.config[:encrypted_data_bag_secret_file]).
102
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
103
- expect(bootstrap.load_correct_secret).to eq(
104
- "data_bag_secret_key_passed_under_cli_secret_file_option")
105
- end
106
- end
107
-
108
- context "knife secret-file && cli secret options are passed" do
109
- before do
110
- Chef::Config.reset
111
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
112
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
113
- end
114
- it "gives preference to secret key passed under cli's secret option" do
115
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
116
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
117
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
118
- expect(bootstrap.load_correct_secret).to eq(
119
- "data_bag_secret_key_passed_under_cli_secret_option")
120
- end
121
- end
122
-
123
- context "knife secret && cli secret-file options are passed" do
124
- before do
125
- Chef::Config.reset
126
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
127
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
128
- end
129
- it "gives preference to secret key passed under cli's secret-file option" do
130
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
131
- bootstrap.config[:encrypted_data_bag_secret_file]).
132
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
133
- expect(bootstrap.load_correct_secret).to eq(
134
- "data_bag_secret_key_passed_under_cli_secret_file_option")
135
- end
136
- end
137
-
138
- context "cli secret-file option is passed" do
139
- before do
140
- Chef::Config.reset
141
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
142
- end
143
- it "takes the secret key passed under cli's secret-file option" do
144
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
145
- bootstrap.config[:encrypted_data_bag_secret_file]).
146
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
147
- expect(bootstrap.load_correct_secret).to eq(
148
- "data_bag_secret_key_passed_under_cli_secret_file_option")
149
- end
150
- end
151
-
152
- it 'should pass exit code from failed winrm call' do
153
- allow(session).to receive(:exit_code).and_return(500)
154
- allow(bootstrap).to receive(:wait_for_remote_response)
155
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
156
- allow(session).to receive(:relay_command).and_return(arch_session_result)
157
- allow(bootstrap.ui).to receive(:info)
158
- expect {
159
- bootstrap.run_with_pretty_exceptions
160
- }.to raise_error(SystemExit) { |e|
161
- expect(e.status).to eq(500)
162
- }
163
- end
164
-
165
- it 'should retry if a 401 is received from WinRM' do
166
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
167
- call_result_sequence.push(0)
168
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
169
- allow(bootstrap).to receive(:print)
170
- allow(bootstrap.ui).to receive(:info)
171
-
172
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
173
- bootstrap.send(:wait_for_remote_response, 2)
174
- end
175
-
176
- it 'should retry if something other than a 401 is received from WinRM' do
177
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
178
- call_result_sequence.push(0)
179
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
180
- allow(bootstrap).to receive(:print)
181
- allow(bootstrap.ui).to receive(:info)
182
-
183
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
184
- bootstrap.send(:wait_for_remote_response, 2)
185
- end
186
-
187
- it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
188
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
189
- call_result_sequence.push(0)
190
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
191
- allow(bootstrap).to receive(:print)
192
- allow(bootstrap.ui).to receive(:info)
193
-
194
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
195
- bootstrap.send(:wait_for_remote_response, 2)
196
- end
197
-
198
- it 'should have a wait timeout of 2 minutes by default' do
199
- allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
200
- allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
201
- expect(bootstrap).to receive(:wait_for_remote_response).with(2)
202
-
203
- allow(bootstrap.ui).to receive(:info)
204
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
205
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
206
- end
207
-
208
- it 'should not a wait for timeout on Errno::ECONNREFUSED' do
209
- allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
210
- allow(bootstrap.ui).to receive(:info)
211
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
212
- expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
213
-
214
- # wait_for_remote_response is protected method, So define singleton test method to call it.
215
- bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
216
- expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
217
- end
218
-
219
- it 'should stop retrying if more than 2 minutes has elapsed' do
220
- times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
221
- allow(Time).to receive(:now).and_return(*times)
222
- run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
223
- allow(bootstrap).to receive(:run_command).and_return(run_command_result)
224
- allow(bootstrap).to receive(:print)
225
- allow(bootstrap.ui).to receive(:info)
226
- allow(bootstrap.ui).to receive(:error)
227
- expect(bootstrap).to receive(:run_command).exactly(1).times
228
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
229
- expect { bootstrap.bootstrap }.to raise_error /Command execution failed./
230
- end
231
-
232
- it 'successfully bootstraps' do
233
- Chef::Config[:knife][:bootstrap_architecture] = :i386
234
- allow(bootstrap).to receive(:wait_for_remote_response)
235
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
236
- allow(bootstrap).to receive(:run_command).and_return(0)
237
- expect(bootstrap.bootstrap).to eq(0)
238
- expect(Chef::Config[:knife][:architecture]).to eq(:i686)
239
- end
240
-
241
- context "when the target node is 64 bit" do
242
- it 'successfully bootstraps' do
243
- Chef::Config[:knife][:bootstrap_architecture] = :x86_64
244
- allow(bootstrap).to receive(:wait_for_remote_response)
245
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
246
- allow(bootstrap).to receive(:run_command).and_return(0)
247
- expect(bootstrap.bootstrap).to eq(0)
248
- expect(Chef::Config[:knife][:architecture]).to eq(:x86_64)
249
- end
250
- end
251
-
252
- context 'FQDN validation -' do
253
- it 'should raise an error if FQDN value is not passed' do
254
- bootstrap.instance_variable_set(:@name_args, [])
255
- allow(bootstrap.ui).to receive(:error)
256
- expect {
257
- bootstrap.run
258
- }.to raise_error(SystemExit)
259
- end
260
-
261
- it 'should not raise error if FQDN value is passed' do
262
- bootstrap.instance_variable_set(:@name_args, ["fqdn_name"])
263
- expect {
264
- bootstrap.run
265
- }.not_to raise_error(SystemExit)
266
- end
267
- end
268
-
269
- context "when validation_key is not present" do
270
- context "using chef 11", :chef_lt_12_only do
271
- before do
272
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
273
- end
274
-
275
- it 'raises an exception if validation_key is not present in chef 11' do
276
- expect(bootstrap.ui).to receive(:error)
277
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
278
- end
279
- end
280
-
281
- context "using chef 12", :chef_gte_12_only do
282
- before do
283
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
284
- bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
285
- Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
286
- end
287
-
288
- it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
289
- Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
290
- expect(bootstrap.ui).to receive(:error)
291
- expect { bootstrap.run }.to raise_error(SystemExit)
292
- end
293
-
294
- it 'raises an exception if chef_node_name is not present ' do
295
- Chef::Config[:knife] = {:chef_node_name => nil}
296
- expect(bootstrap.client_builder).not_to receive(:run)
297
- expect(bootstrap.client_builder).not_to receive(:client_path)
298
- expect(bootstrap.ui).to receive(:error)
299
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
300
- end
301
- end
302
- end
303
-
304
- context "when doing chef vault", :chef_gte_12_only do
305
- let(:vault_handler) { double('vault_handler', :doing_chef_vault? => true) }
306
- let(:node_name) { 'foo.example.com' }
307
- before do
308
- allow(bootstrap).to receive(:wait_for_remote_response)
309
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
310
- allow(bootstrap).to receive(:run_command).and_return(0)
311
- bootstrap.config[:chef_node_name] = node_name
312
- bootstrap.chef_vault_handler = vault_handler
313
- end
314
-
315
- context "builder does not respond to client" do
316
- before do
317
- bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
318
- end
319
-
320
- it "passes a node search query to the handler" do
321
- expect(vault_handler).to receive(:run).with(node_name: node_name)
322
- bootstrap.bootstrap
323
- end
324
- end
325
-
326
- context "builder responds to client" do
327
- let(:client) { Chef::ApiClient.new }
328
-
329
- before do
330
- bootstrap.client_builder = double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil, :client => client)
331
- end
332
-
333
- it "passes a node search query to the handler" do
334
- expect(vault_handler).to receive(:run).with(client)
335
- bootstrap.bootstrap
336
- end
337
- end
338
- end
339
-
340
- describe 'first_boot_attributes' do
341
- let(:first_boot_attributes) { { 'a1' => 'b1', 'a2' => 'b2', 'source' => 'hash' } }
342
- let(:json_file) { 'my_json.json' }
343
- let(:first_boot_attributes_from_file) { read_json_file(json_file) }
344
-
345
- before do
346
- File.open(json_file,"w+") do |f|
347
- f.write <<-EOH
348
- {"b2" : "a3", "a4" : "b5", "source" : "file"}
349
- EOH
350
- end
351
- end
352
-
353
- context 'when none of the json-attributes options are passed' do
354
- it 'returns an empty hash' do
355
- response = bootstrap.first_boot_attributes
356
- expect(response).to be == {}
357
- end
358
- end
359
-
360
- context 'when only --json-attributes option is passed' do
361
- before do
362
- bootstrap.config[:first_boot_attributes] = first_boot_attributes
363
- end
364
-
365
- it 'returns the hash passed by the user in --json-attributes option' do
366
- response = bootstrap.first_boot_attributes
367
- expect(response).to be == first_boot_attributes
368
- end
369
- end
370
-
371
- context 'when only --json-attribute-file option is passed' do
372
- before do
373
- bootstrap.config[:first_boot_attributes_from_file] = first_boot_attributes_from_file
374
- end
375
-
376
- it 'returns the hash passed by the user in --json-attribute-file option' do
377
- response = bootstrap.first_boot_attributes
378
- expect(response).to be == { 'b2' => 'a3', 'a4' => 'b5', 'source' => 'file' }
379
- end
380
- end
381
-
382
- context 'when both the --json-attributes option and --json-attribute-file options are passed' do
383
- before do
384
- bootstrap.config[:first_boot_attributes] = first_boot_attributes
385
- bootstrap.config[:first_boot_attributes_from_file] = first_boot_attributes_from_file
386
- end
387
-
388
- it 'returns the hash passed by the user in --json-attributes option' do
389
- response = bootstrap.first_boot_attributes
390
- expect(response).to be == first_boot_attributes
391
- end
392
- end
393
-
394
- after do
395
- FileUtils.rm_rf json_file
396
- end
397
- end
398
-
399
- describe 'render_template' do
400
- before do
401
- allow(bootstrap).to receive(:first_boot_attributes).and_return(
402
- { 'a1' => 'b3', 'a2' => 'b1' }
403
- )
404
- allow(bootstrap).to receive(:load_correct_secret).and_return(
405
- 'my_secret'
406
- )
407
- allow(Erubis::Eruby).to receive_message_chain(:new, :evaluate).and_return(
408
- 'my_template'
409
- )
410
- end
411
-
412
- it 'sets correct values into config and returns the correct response' do
413
- response = bootstrap.render_template
414
- expect(bootstrap.config[:first_boot_attributes]).to be == { 'a1' => 'b3', 'a2' => 'b1' }
415
- expect(bootstrap.config[:secret]).to be == 'my_secret'
416
- expect(response).to be == 'my_template'
417
- end
418
- end
419
- end
420
-
421
- def read_json_file(file)
422
- Chef::JSONCompat.parse(File.read(file))
423
- end
1
+ #
2
+ # Author:: Adam Edwards(<adamed@chef.io>)
3
+ # Copyright:: Copyright (c) 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 'winrm/output'
21
+
22
+ Chef::Knife::Winrm.load_deps
23
+
24
+ describe Chef::Knife::BootstrapWindowsWinrm do
25
+ before do
26
+ Chef::Config.reset
27
+ bootstrap.config[:run_list] = []
28
+ allow(bootstrap).to receive(:validate_options!).and_return(nil)
29
+ allow(bootstrap).to receive(:sleep).and_return(10)
30
+ allow(Chef::Knife::WinrmSession).to receive(:new).and_return(session)
31
+ allow(File).to receive(:exist?).with(anything).and_call_original
32
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
33
+ end
34
+
35
+ after do
36
+ allow(bootstrap).to receive(:sleep).and_return(10)
37
+ end
38
+
39
+ let(:session_opts) do
40
+ {
41
+ user: "Administrator",
42
+ password: "testpassword",
43
+ port: "5986",
44
+ transport: :ssl,
45
+ host: "localhost"
46
+ }
47
+ end
48
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', session_opts[:user], '-P', session_opts[:password], session_opts[:host]]) }
49
+ let(:session) { Chef::Knife::WinrmSession.new(session_opts) }
50
+ let(:arch_session_result) {
51
+ o = WinRM::Output.new
52
+ o << {stdout: "X86\r\n"}
53
+ o
54
+ }
55
+ let(:arch_session_results) { [arch_session_result] }
56
+ let(:initial_fail_count) { 4 }
57
+
58
+ context "knife secret-file && knife secret options are passed" do
59
+ before do
60
+ Chef::Config.reset
61
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
62
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
63
+ end
64
+ it "gives preference to secret key passed under knife's secret-file option" do
65
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
66
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
67
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
68
+ expect(bootstrap.load_correct_secret).to eq(
69
+ "data_bag_secret_key_passed_under_knife_secret_file_option")
70
+ end
71
+ end
72
+
73
+ context "cli secret-file && cli secret options are passed" do
74
+ before do
75
+ Chef::Config.reset
76
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
77
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
78
+ end
79
+ it "gives preference to secret key passed under cli's secret-file option" do
80
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
81
+ bootstrap.config[:encrypted_data_bag_secret_file]).
82
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
83
+ expect(bootstrap.load_correct_secret).to eq(
84
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
85
+ end
86
+ end
87
+
88
+ context "knife secret-file, knife secret, cli secret-file && cli secret options are passed" do
89
+ before do
90
+ Chef::Config.reset
91
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/knife_encrypted_data_bag_secret"
92
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
93
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/cli_encrypted_data_bag_secret"
94
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
95
+ end
96
+ it "gives preference to secret key passed under cli's secret-file option" do
97
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
98
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
99
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
100
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
101
+ bootstrap.config[:encrypted_data_bag_secret_file]).
102
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
103
+ expect(bootstrap.load_correct_secret).to eq(
104
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
105
+ end
106
+ end
107
+
108
+ context "knife secret-file && cli secret options are passed" do
109
+ before do
110
+ Chef::Config.reset
111
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
112
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
113
+ end
114
+ it "gives preference to secret key passed under cli's secret option" do
115
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
116
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
117
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
118
+ expect(bootstrap.load_correct_secret).to eq(
119
+ "data_bag_secret_key_passed_under_cli_secret_option")
120
+ end
121
+ end
122
+
123
+ context "knife secret && cli secret-file options are passed" do
124
+ before do
125
+ Chef::Config.reset
126
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
127
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
128
+ end
129
+ it "gives preference to secret key passed under cli's secret-file option" do
130
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
131
+ bootstrap.config[:encrypted_data_bag_secret_file]).
132
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
133
+ expect(bootstrap.load_correct_secret).to eq(
134
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
135
+ end
136
+ end
137
+
138
+ context "cli secret-file option is passed" do
139
+ before do
140
+ Chef::Config.reset
141
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
142
+ end
143
+ it "takes the secret key passed under cli's secret-file option" do
144
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
145
+ bootstrap.config[:encrypted_data_bag_secret_file]).
146
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
147
+ expect(bootstrap.load_correct_secret).to eq(
148
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
149
+ end
150
+ end
151
+
152
+ it 'should pass exit code from failed winrm call' do
153
+ allow(session).to receive(:exit_code).and_return(500)
154
+ allow(bootstrap).to receive(:wait_for_remote_response)
155
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
156
+ allow(session).to receive(:relay_command).and_return(arch_session_result)
157
+ allow(bootstrap.ui).to receive(:info)
158
+ expect {
159
+ bootstrap.run_with_pretty_exceptions
160
+ }.to raise_error(SystemExit) { |e|
161
+ expect(e.status).to eq(500)
162
+ }
163
+ end
164
+
165
+ it 'should retry if a 401 is received from WinRM' do
166
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
167
+ call_result_sequence.push(0)
168
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
169
+ allow(bootstrap).to receive(:print)
170
+ allow(bootstrap.ui).to receive(:info)
171
+
172
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
173
+ bootstrap.send(:wait_for_remote_response, 2)
174
+ end
175
+
176
+ it 'should retry if something other than a 401 is received from WinRM' do
177
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
178
+ call_result_sequence.push(0)
179
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
180
+ allow(bootstrap).to receive(:print)
181
+ allow(bootstrap.ui).to receive(:info)
182
+
183
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
184
+ bootstrap.send(:wait_for_remote_response, 2)
185
+ end
186
+
187
+ it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
188
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
189
+ call_result_sequence.push(0)
190
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
191
+ allow(bootstrap).to receive(:print)
192
+ allow(bootstrap.ui).to receive(:info)
193
+
194
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
195
+ bootstrap.send(:wait_for_remote_response, 2)
196
+ end
197
+
198
+ it 'should have a wait timeout of 2 minutes by default' do
199
+ allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
200
+ allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
201
+ expect(bootstrap).to receive(:wait_for_remote_response).with(2)
202
+
203
+ allow(bootstrap.ui).to receive(:info)
204
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
205
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
206
+ end
207
+
208
+ it 'should not a wait for timeout on Errno::ECONNREFUSED' do
209
+ allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
210
+ allow(bootstrap.ui).to receive(:info)
211
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
212
+ expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
213
+
214
+ # wait_for_remote_response is protected method, So define singleton test method to call it.
215
+ bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
216
+ expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
217
+ end
218
+
219
+ it 'should stop retrying if more than 2 minutes has elapsed' do
220
+ times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
221
+ allow(Time).to receive(:now).and_return(*times)
222
+ run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
223
+ allow(bootstrap).to receive(:run_command).and_return(run_command_result)
224
+ allow(bootstrap).to receive(:print)
225
+ allow(bootstrap.ui).to receive(:info)
226
+ allow(bootstrap.ui).to receive(:error)
227
+ expect(bootstrap).to receive(:run_command).exactly(1).times
228
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
229
+ expect { bootstrap.bootstrap }.to raise_error /Command execution failed./
230
+ end
231
+
232
+ it 'successfully bootstraps' do
233
+ Chef::Config[:knife][:bootstrap_architecture] = :i386
234
+ allow(bootstrap).to receive(:wait_for_remote_response)
235
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
236
+ allow(bootstrap).to receive(:run_command).and_return(0)
237
+ expect(bootstrap.bootstrap).to eq(0)
238
+ expect(Chef::Config[:knife][:architecture]).to eq(:i686)
239
+ end
240
+
241
+ context "when the target node is 64 bit" do
242
+ it 'successfully bootstraps' do
243
+ Chef::Config[:knife][:bootstrap_architecture] = :x86_64
244
+ allow(bootstrap).to receive(:wait_for_remote_response)
245
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
246
+ allow(bootstrap).to receive(:run_command).and_return(0)
247
+ expect(bootstrap.bootstrap).to eq(0)
248
+ expect(Chef::Config[:knife][:architecture]).to eq(:x86_64)
249
+ end
250
+ end
251
+
252
+ context 'FQDN validation -' do
253
+ it 'should raise an error if FQDN value is not passed' do
254
+ bootstrap.instance_variable_set(:@name_args, [])
255
+ allow(bootstrap.ui).to receive(:error)
256
+ expect {
257
+ bootstrap.run
258
+ }.to raise_error(SystemExit)
259
+ end
260
+
261
+ it 'should not raise error if FQDN value is passed' do
262
+ bootstrap.instance_variable_set(:@name_args, ["fqdn_name"])
263
+ expect {
264
+ bootstrap.run
265
+ }.not_to raise_error(SystemExit)
266
+ end
267
+ end
268
+
269
+ context "when validation_key is not present" do
270
+ context "using chef 11", :chef_lt_12_only do
271
+ before do
272
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
273
+ end
274
+
275
+ it 'raises an exception if validation_key is not present in chef 11' do
276
+ expect(bootstrap.ui).to receive(:error)
277
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
278
+ end
279
+ end
280
+
281
+ context "using chef 12", :chef_gte_12_only do
282
+ before do
283
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
284
+ bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
285
+ Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
286
+ end
287
+
288
+ it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
289
+ Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
290
+ expect(bootstrap.ui).to receive(:error)
291
+ expect { bootstrap.run }.to raise_error(SystemExit)
292
+ end
293
+
294
+ it 'raises an exception if chef_node_name is not present ' do
295
+ Chef::Config[:knife] = {:chef_node_name => nil}
296
+ expect(bootstrap.client_builder).not_to receive(:run)
297
+ expect(bootstrap.client_builder).not_to receive(:client_path)
298
+ expect(bootstrap.ui).to receive(:error)
299
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
300
+ end
301
+ end
302
+ end
303
+
304
+ context "when doing chef vault", :chef_gte_12_only do
305
+ let(:vault_handler) { double('vault_handler', :doing_chef_vault? => true) }
306
+ let(:node_name) { 'foo.example.com' }
307
+ before do
308
+ allow(bootstrap).to receive(:wait_for_remote_response)
309
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
310
+ allow(bootstrap).to receive(:run_command).and_return(0)
311
+ bootstrap.config[:chef_node_name] = node_name
312
+ bootstrap.chef_vault_handler = vault_handler
313
+ end
314
+
315
+ context "builder does not respond to client" do
316
+ before do
317
+ bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
318
+ end
319
+
320
+ it "passes a node search query to the handler" do
321
+ expect(vault_handler).to receive(:run).with(node_name: node_name)
322
+ bootstrap.bootstrap
323
+ end
324
+ end
325
+
326
+ context "builder responds to client" do
327
+ let(:client) { Chef::ApiClient.new }
328
+
329
+ before do
330
+ bootstrap.client_builder = double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil, :client => client)
331
+ end
332
+
333
+ it "passes a node search query to the handler" do
334
+ expect(vault_handler).to receive(:run).with(client)
335
+ bootstrap.bootstrap
336
+ end
337
+ end
338
+ end
339
+
340
+ describe 'first_boot_attributes' do
341
+ let(:first_boot_attributes) { { 'a1' => 'b1', 'a2' => 'b2', 'source' => 'hash' } }
342
+ let(:json_file) { 'my_json.json' }
343
+ let(:first_boot_attributes_from_file) { read_json_file(json_file) }
344
+
345
+ before do
346
+ File.open(json_file,"w+") do |f|
347
+ f.write <<-EOH
348
+ {"b2" : "a3", "a4" : "b5", "source" : "file"}
349
+ EOH
350
+ end
351
+ end
352
+
353
+ context 'when none of the json-attributes options are passed' do
354
+ it 'returns an empty hash' do
355
+ response = bootstrap.first_boot_attributes
356
+ expect(response).to be == {}
357
+ end
358
+ end
359
+
360
+ context 'when only --json-attributes option is passed' do
361
+ before do
362
+ bootstrap.config[:first_boot_attributes] = first_boot_attributes
363
+ end
364
+
365
+ it 'returns the hash passed by the user in --json-attributes option' do
366
+ response = bootstrap.first_boot_attributes
367
+ expect(response).to be == first_boot_attributes
368
+ end
369
+ end
370
+
371
+ context 'when only --json-attribute-file option is passed' do
372
+ before do
373
+ bootstrap.config[:first_boot_attributes_from_file] = first_boot_attributes_from_file
374
+ end
375
+
376
+ it 'returns the hash passed by the user in --json-attribute-file option' do
377
+ response = bootstrap.first_boot_attributes
378
+ expect(response).to be == { 'b2' => 'a3', 'a4' => 'b5', 'source' => 'file' }
379
+ end
380
+ end
381
+
382
+ context 'when both the --json-attributes option and --json-attribute-file options are passed' do
383
+ before do
384
+ bootstrap.config[:first_boot_attributes] = first_boot_attributes
385
+ bootstrap.config[:first_boot_attributes_from_file] = first_boot_attributes_from_file
386
+ end
387
+
388
+ it 'returns the hash passed by the user in --json-attributes option' do
389
+ response = bootstrap.first_boot_attributes
390
+ expect(response).to be == first_boot_attributes
391
+ end
392
+ end
393
+
394
+ after do
395
+ FileUtils.rm_rf json_file
396
+ end
397
+ end
398
+
399
+ describe 'render_template' do
400
+ before do
401
+ allow(bootstrap).to receive(:first_boot_attributes).and_return(
402
+ { 'a1' => 'b3', 'a2' => 'b1' }
403
+ )
404
+ allow(bootstrap).to receive(:load_correct_secret).and_return(
405
+ 'my_secret'
406
+ )
407
+ allow(Erubis::Eruby).to receive_message_chain(:new, :evaluate).and_return(
408
+ 'my_template'
409
+ )
410
+ end
411
+
412
+ it 'sets correct values into config and returns the correct response' do
413
+ response = bootstrap.render_template
414
+ expect(bootstrap.config[:first_boot_attributes]).to be == { 'a1' => 'b3', 'a2' => 'b1' }
415
+ expect(bootstrap.config[:secret]).to be == 'my_secret'
416
+ expect(response).to be == 'my_template'
417
+ end
418
+ end
419
+ end
420
+
421
+ def read_json_file(file)
422
+ Chef::JSONCompat.parse(File.read(file))
423
+ end