knife-windows 1.3.0 → 1.4.0

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/.gitignore +5 -5
  3. data/.travis.yml +26 -26
  4. data/CHANGELOG.md +112 -108
  5. data/DOC_CHANGES.md +14 -14
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +391 -385
  9. data/RELEASE_NOTES.md +34 -34
  10. data/Rakefile +21 -21
  11. data/appveyor.yml +42 -42
  12. data/ci.gemfile +15 -15
  13. data/features/knife_help.feature +20 -20
  14. data/features/support/env.rb +5 -5
  15. data/knife-windows.gemspec +25 -25
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +233 -247
  17. data/lib/chef/knife/bootstrap_windows_base.rb +449 -415
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -115
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -95
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +372 -366
  21. data/lib/chef/knife/knife_windows_base.rb +33 -33
  22. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  23. data/lib/chef/knife/windows_cert_install.rb +68 -68
  24. data/lib/chef/knife/windows_helper.rb +36 -36
  25. data/lib/chef/knife/windows_listener_create.rb +107 -107
  26. data/lib/chef/knife/winrm.rb +122 -122
  27. data/lib/chef/knife/winrm_base.rb +117 -117
  28. data/lib/chef/knife/winrm_knife_base.rb +305 -303
  29. data/lib/chef/knife/winrm_session.rb +88 -87
  30. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  31. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  32. data/lib/chef/knife/wsman_test.rb +117 -117
  33. data/lib/knife-windows/path_helper.rb +234 -234
  34. data/lib/knife-windows/version.rb +6 -6
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -217
  36. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +217 -217
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -329
  38. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +329 -329
  39. data/spec/assets/win_template_unrendered.txt +246 -246
  40. data/spec/functional/bootstrap_download_spec.rb +241 -234
  41. data/spec/spec_helper.rb +94 -93
  42. data/spec/unit/knife/bootstrap_options_spec.rb +155 -155
  43. data/spec/unit/knife/bootstrap_template_spec.rb +98 -92
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +341 -295
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -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 +65 -65
  50. data/spec/unit/knife/winrm_spec.rb +516 -516
  51. data/spec/unit/knife/wsman_test_spec.rb +202 -202
  52. metadata +23 -4
@@ -1,295 +1,341 @@
1
- #
2
- # Author:: Adam Edwards(<adamed@getchef.com>)
3
- # Copyright:: Copyright (c) 2014 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
-
21
- Chef::Knife::Winrm.load_deps
22
-
23
- describe Chef::Knife::BootstrapWindowsWinrm do
24
- before(:all) do
25
- Chef::Config.reset
26
- end
27
-
28
- before do
29
- bootstrap.config[:run_list] = []
30
- allow(bootstrap).to receive(:validate_options!).and_return(nil)
31
- allow(bootstrap).to receive(:sleep).and_return(10)
32
- allow(Chef::Knife::WinrmSession).to receive(:new).and_return(session)
33
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
34
- end
35
-
36
- after do
37
- allow(bootstrap).to receive(:sleep).and_return(10)
38
- end
39
-
40
- let(:session_opts) do
41
- {
42
- user: "Administrator",
43
- password: "testpassword",
44
- port: "5986",
45
- transport: :ssl,
46
- host: "localhost"
47
- }
48
- end
49
- let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', session_opts[:user], '-P', session_opts[:password], session_opts[:host]]) }
50
- let(:session) { Chef::Knife::WinrmSession.new(session_opts) }
51
- let(:initial_fail_count) { 4 }
52
-
53
- context "knife secret-file && knife secret options are passed" do
54
- before do
55
- Chef::Config.reset
56
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
57
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
58
- end
59
- it "gives preference to secret key passed under knife's secret-file option" do
60
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
61
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
62
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
63
- expect(bootstrap.load_correct_secret).to eq(
64
- "data_bag_secret_key_passed_under_knife_secret_file_option")
65
- end
66
- end
67
-
68
- context "cli secret-file && cli secret options are passed" do
69
- before do
70
- Chef::Config.reset
71
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
72
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
73
- end
74
- it "gives preference to secret key passed under cli's secret-file option" do
75
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
76
- bootstrap.config[:encrypted_data_bag_secret_file]).
77
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
78
- expect(bootstrap.load_correct_secret).to eq(
79
- "data_bag_secret_key_passed_under_cli_secret_file_option")
80
- end
81
- end
82
-
83
- context "knife secret-file, knife secret, cli secret-file && cli secret options are passed" do
84
- before do
85
- Chef::Config.reset
86
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/knife_encrypted_data_bag_secret"
87
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
88
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/cli_encrypted_data_bag_secret"
89
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
90
- end
91
- it "gives preference to secret key passed under cli's secret-file option" do
92
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
93
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
94
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
95
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
96
- bootstrap.config[:encrypted_data_bag_secret_file]).
97
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
98
- expect(bootstrap.load_correct_secret).to eq(
99
- "data_bag_secret_key_passed_under_cli_secret_file_option")
100
- end
101
- end
102
-
103
- context "knife secret-file && cli secret options are passed" do
104
- before do
105
- Chef::Config.reset
106
- Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
107
- bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
108
- end
109
- it "gives preference to secret key passed under cli's secret option" do
110
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
111
- Chef::Config[:knife][:encrypted_data_bag_secret_file]).
112
- and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
113
- expect(bootstrap.load_correct_secret).to eq(
114
- "data_bag_secret_key_passed_under_cli_secret_option")
115
- end
116
- end
117
-
118
- context "knife secret && cli secret-file options are passed" do
119
- before do
120
- Chef::Config.reset
121
- Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
122
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
123
- end
124
- it "gives preference to secret key passed under cli's secret-file option" do
125
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
126
- bootstrap.config[:encrypted_data_bag_secret_file]).
127
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
128
- expect(bootstrap.load_correct_secret).to eq(
129
- "data_bag_secret_key_passed_under_cli_secret_file_option")
130
- end
131
- end
132
-
133
- context "cli secret-file option is passed" do
134
- before do
135
- Chef::Config.reset
136
- bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
137
- end
138
- it "takes the secret key passed under cli's secret-file option" do
139
- allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
140
- bootstrap.config[:encrypted_data_bag_secret_file]).
141
- and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
142
- expect(bootstrap.load_correct_secret).to eq(
143
- "data_bag_secret_key_passed_under_cli_secret_file_option")
144
- end
145
- end
146
-
147
- it 'should pass exit code from failed winrm call' do
148
- allow(session).to receive(:exit_code).and_return(500)
149
- allow(bootstrap).to receive(:wait_for_remote_response)
150
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
151
- allow(session).to receive(:relay_command)
152
- allow(bootstrap.ui).to receive(:info)
153
- expect { bootstrap.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(500) }
154
- end
155
-
156
- it 'should retry if a 401 is received from WinRM' do
157
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
158
- call_result_sequence.push(0)
159
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
160
- allow(bootstrap).to receive(:print)
161
- allow(bootstrap.ui).to receive(:info)
162
-
163
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
164
- bootstrap.send(:wait_for_remote_response, 2)
165
- end
166
-
167
- it 'should retry if something other than a 401 is received from WinRM' do
168
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
169
- call_result_sequence.push(0)
170
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
171
- allow(bootstrap).to receive(:print)
172
- allow(bootstrap.ui).to receive(:info)
173
-
174
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
175
- bootstrap.send(:wait_for_remote_response, 2)
176
- end
177
-
178
- it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
179
- call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
180
- call_result_sequence.push(0)
181
- allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
182
- allow(bootstrap).to receive(:print)
183
- allow(bootstrap.ui).to receive(:info)
184
-
185
- expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
186
- bootstrap.send(:wait_for_remote_response, 2)
187
- end
188
-
189
- it 'should have a wait timeout of 2 minutes by default' do
190
- allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
191
- allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
192
- expect(bootstrap).to receive(:wait_for_remote_response).with(2)
193
- allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
194
-
195
- allow(bootstrap.ui).to receive(:info)
196
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
197
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
198
- end
199
-
200
- it 'should not a wait for timeout on Errno::ECONNREFUSED' do
201
- allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
202
- allow(bootstrap.ui).to receive(:info)
203
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
204
- expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
205
-
206
- # wait_for_remote_response is protected method, So define singleton test method to call it.
207
- bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
208
- expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
209
- end
210
-
211
- it 'should stop retrying if more than 2 minutes has elapsed' do
212
- times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
213
- allow(Time).to receive(:now).and_return(*times)
214
- run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
215
- allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
216
- allow(bootstrap).to receive(:run_command).and_return(run_command_result)
217
- allow(bootstrap).to receive(:print)
218
- allow(bootstrap.ui).to receive(:info)
219
- allow(bootstrap.ui).to receive(:error)
220
- expect(bootstrap).to receive(:run_command).exactly(1).times
221
- bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
222
- expect { bootstrap.bootstrap }.to raise_error RuntimeError
223
- end
224
-
225
- context "when validation_key is not present" do
226
- context "using chef 11", :chef_lt_12_only do
227
- before do
228
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
229
- end
230
-
231
- it 'raises an exception if validation_key is not present in chef 11' do
232
- expect(bootstrap.ui).to receive(:error)
233
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
234
- end
235
- end
236
-
237
- context "using chef 12", :chef_gte_12_only do
238
- before do
239
- allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
240
- bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
241
- Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
242
- end
243
-
244
- it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
245
- Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
246
- expect(bootstrap.ui).to receive(:error)
247
- expect { bootstrap.run }.to raise_error(SystemExit)
248
- end
249
-
250
- it 'raises an exception if chef_node_name is not present ' do
251
- Chef::Config[:knife] = {:chef_node_name => nil}
252
- expect(bootstrap.client_builder).not_to receive(:run)
253
- expect(bootstrap.client_builder).not_to receive(:client_path)
254
- expect(bootstrap.ui).to receive(:error)
255
- expect { bootstrap.bootstrap }.to raise_error(SystemExit)
256
- end
257
- end
258
- end
259
-
260
- context "when doing chef vault", :chef_gte_12_only do
261
- let(:vault_handler) { double('vault_handler', :doing_chef_vault? => true) }
262
- let(:node_name) { 'foo.example.com' }
263
- before do
264
- allow(bootstrap).to receive(:wait_for_remote_response)
265
- allow(bootstrap).to receive(:create_bootstrap_bat_command)
266
- allow(bootstrap).to receive(:run_command).and_return(0)
267
- bootstrap.config[:chef_node_name] = node_name
268
- bootstrap.chef_vault_handler = vault_handler
269
- end
270
-
271
- context "builder does not respond to client" do
272
- before do
273
- bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
274
- end
275
-
276
- it "passes a node search query to the handler" do
277
- expect(vault_handler).to receive(:run).with(node_name: node_name)
278
- bootstrap.bootstrap
279
- end
280
- end
281
-
282
- context "builder responds to client" do
283
- let(:client) { Chef::ApiClient.new }
284
-
285
- before do
286
- bootstrap.client_builder = double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil, :client => client)
287
- end
288
-
289
- it "passes a node search query to the handler" do
290
- expect(vault_handler).to receive(:run).with(client)
291
- bootstrap.bootstrap
292
- end
293
- end
294
- end
295
- 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(File.expand_path(Chef::Config[:validation_key])).and_return(true)
32
+ end
33
+
34
+ after do
35
+ allow(bootstrap).to receive(:sleep).and_return(10)
36
+ end
37
+
38
+ let(:session_opts) do
39
+ {
40
+ user: "Administrator",
41
+ password: "testpassword",
42
+ port: "5986",
43
+ transport: :ssl,
44
+ host: "localhost"
45
+ }
46
+ end
47
+ let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new(['winrm', '-d', 'windows-chef-client-msi', '-x', session_opts[:user], '-P', session_opts[:password], session_opts[:host]]) }
48
+ let(:session) { Chef::Knife::WinrmSession.new(session_opts) }
49
+ let(:arch_session_result) {
50
+ o = WinRM::Output.new
51
+ o[:data] << {stdout: "X86\r\n"}
52
+ o
53
+ }
54
+ let(:arch_session_results) { [arch_session_result] }
55
+ let(:initial_fail_count) { 4 }
56
+
57
+ context "knife secret-file && knife secret options are passed" do
58
+ before do
59
+ Chef::Config.reset
60
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
61
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
62
+ end
63
+ it "gives preference to secret key passed under knife's secret-file option" do
64
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
65
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
66
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
67
+ expect(bootstrap.load_correct_secret).to eq(
68
+ "data_bag_secret_key_passed_under_knife_secret_file_option")
69
+ end
70
+ end
71
+
72
+ context "cli secret-file && cli secret options are passed" do
73
+ before do
74
+ Chef::Config.reset
75
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
76
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
77
+ end
78
+ it "gives preference to secret key passed under cli's secret-file option" do
79
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
80
+ bootstrap.config[:encrypted_data_bag_secret_file]).
81
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
82
+ expect(bootstrap.load_correct_secret).to eq(
83
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
84
+ end
85
+ end
86
+
87
+ context "knife secret-file, knife secret, cli secret-file && cli secret options are passed" do
88
+ before do
89
+ Chef::Config.reset
90
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/knife_encrypted_data_bag_secret"
91
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
92
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/cli_encrypted_data_bag_secret"
93
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
94
+ end
95
+ it "gives preference to secret key passed under cli's secret-file option" do
96
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
97
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
98
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
99
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
100
+ bootstrap.config[:encrypted_data_bag_secret_file]).
101
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
102
+ expect(bootstrap.load_correct_secret).to eq(
103
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
104
+ end
105
+ end
106
+
107
+ context "knife secret-file && cli secret options are passed" do
108
+ before do
109
+ Chef::Config.reset
110
+ Chef::Config[:knife][:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
111
+ bootstrap.config[:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_cli_secret_option"
112
+ end
113
+ it "gives preference to secret key passed under cli's secret option" do
114
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
115
+ Chef::Config[:knife][:encrypted_data_bag_secret_file]).
116
+ and_return("data_bag_secret_key_passed_under_knife_secret_file_option")
117
+ expect(bootstrap.load_correct_secret).to eq(
118
+ "data_bag_secret_key_passed_under_cli_secret_option")
119
+ end
120
+ end
121
+
122
+ context "knife secret && cli secret-file options are passed" do
123
+ before do
124
+ Chef::Config.reset
125
+ Chef::Config[:knife][:encrypted_data_bag_secret] = "data_bag_secret_key_passed_under_knife_secret_option"
126
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
127
+ end
128
+ it "gives preference to secret key passed under cli's secret-file option" do
129
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
130
+ bootstrap.config[:encrypted_data_bag_secret_file]).
131
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
132
+ expect(bootstrap.load_correct_secret).to eq(
133
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
134
+ end
135
+ end
136
+
137
+ context "cli secret-file option is passed" do
138
+ before do
139
+ Chef::Config.reset
140
+ bootstrap.config[:encrypted_data_bag_secret_file] = "/tmp/encrypted_data_bag_secret"
141
+ end
142
+ it "takes the secret key passed under cli's secret-file option" do
143
+ allow(Chef::EncryptedDataBagItem).to receive(:load_secret).with(
144
+ bootstrap.config[:encrypted_data_bag_secret_file]).
145
+ and_return("data_bag_secret_key_passed_under_cli_secret_file_option")
146
+ expect(bootstrap.load_correct_secret).to eq(
147
+ "data_bag_secret_key_passed_under_cli_secret_file_option")
148
+ end
149
+ end
150
+
151
+ it 'should raise an error if the relay_command returns an unknown response' do
152
+ allow(session).to receive(:exit_code).and_return(500)
153
+ allow(bootstrap).to receive(:wait_for_remote_response)
154
+ allow(session).to receive(:relay_command).and_return(WinRM::Output.new)
155
+ allow(bootstrap.ui).to receive(:info)
156
+ expect {
157
+ bootstrap.run
158
+ }.to raise_error(/Response to 'echo %PROCESSOR_ARCHITECTURE%' command was invalid:/)
159
+ end
160
+
161
+ it 'should pass exit code from failed winrm call' do
162
+ allow(session).to receive(:exit_code).and_return(500)
163
+ allow(bootstrap).to receive(:wait_for_remote_response)
164
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
165
+ allow(session).to receive(:relay_command).and_return(arch_session_result)
166
+ allow(bootstrap.ui).to receive(:info)
167
+ expect {
168
+ bootstrap.run_with_pretty_exceptions
169
+ }.to raise_error(SystemExit) { |e|
170
+ expect(e.status).to eq(500)
171
+ }
172
+ end
173
+
174
+ it 'should retry if a 401 is received from WinRM' do
175
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '401')}}
176
+ call_result_sequence.push(0)
177
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
178
+ allow(bootstrap).to receive(:print)
179
+ allow(bootstrap.ui).to receive(:info)
180
+
181
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
182
+ bootstrap.send(:wait_for_remote_response, 2)
183
+ end
184
+
185
+ it 'should retry if something other than a 401 is received from WinRM' do
186
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
187
+ call_result_sequence.push(0)
188
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
189
+ allow(bootstrap).to receive(:print)
190
+ allow(bootstrap.ui).to receive(:info)
191
+
192
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
193
+ bootstrap.send(:wait_for_remote_response, 2)
194
+ end
195
+
196
+ it 'should keep retrying at 10s intervals if the timeout in minutes has not elapsed' do
197
+ call_result_sequence = Array.new(initial_fail_count) {lambda {raise WinRM::WinRMHTTPTransportError.new('', '500')}}
198
+ call_result_sequence.push(0)
199
+ allow(bootstrap).to receive(:run_command).and_return(*call_result_sequence)
200
+ allow(bootstrap).to receive(:print)
201
+ allow(bootstrap.ui).to receive(:info)
202
+
203
+ expect(bootstrap).to receive(:run_command).exactly(call_result_sequence.length).times
204
+ bootstrap.send(:wait_for_remote_response, 2)
205
+ end
206
+
207
+ it 'should have a wait timeout of 2 minutes by default' do
208
+ expect(bootstrap).to receive(:relay_winrm_command).with("echo %PROCESSOR_ARCHITECTURE%").and_return(arch_session_results)
209
+ allow(bootstrap).to receive(:run_command).and_raise(WinRM::WinRMHTTPTransportError.new('','500'))
210
+ allow(bootstrap).to receive(:create_bootstrap_bat_command).and_raise(SystemExit)
211
+ expect(bootstrap).to receive(:wait_for_remote_response).with(2)
212
+ allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
213
+
214
+ allow(bootstrap.ui).to receive(:info)
215
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
216
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
217
+ end
218
+
219
+ it 'should not a wait for timeout on Errno::ECONNREFUSED' do
220
+ allow(bootstrap).to receive(:run_command).and_raise(Errno::ECONNREFUSED.new)
221
+ allow(bootstrap.ui).to receive(:info)
222
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
223
+ expect(bootstrap.ui).to receive(:error).with("Connection refused connecting to localhost:5985.")
224
+
225
+ # wait_for_remote_response is protected method, So define singleton test method to call it.
226
+ bootstrap.define_singleton_method(:test_wait_for_remote_response){wait_for_remote_response(bootstrap.options[:auth_timeout][:default])}
227
+ expect { bootstrap.test_wait_for_remote_response }.to raise_error(Errno::ECONNREFUSED)
228
+ end
229
+
230
+ it 'should stop retrying if more than 2 minutes has elapsed' do
231
+ times = [ Time.new(2014, 4, 1, 22, 25), Time.new(2014, 4, 1, 22, 51), Time.new(2014, 4, 1, 22, 28) ]
232
+ allow(Time).to receive(:now).and_return(*times)
233
+ run_command_result = lambda {raise WinRM::WinRMHTTPTransportError, '401'}
234
+ allow(bootstrap).to receive(:validate_name_args!).and_return(nil)
235
+ allow(bootstrap).to receive(:run_command).and_return(run_command_result)
236
+ allow(bootstrap).to receive(:print)
237
+ allow(bootstrap.ui).to receive(:info)
238
+ allow(bootstrap.ui).to receive(:error)
239
+ expect(bootstrap).to receive(:run_command).exactly(1).times
240
+ bootstrap.config[:auth_timeout] = bootstrap.options[:auth_timeout][:default]
241
+ expect { bootstrap.bootstrap }.to raise_error /Command execution failed./
242
+ end
243
+
244
+ it 'successfully bootstraps' do
245
+ allow(bootstrap).to receive(:wait_for_remote_response)
246
+ expect(bootstrap).to receive(:relay_winrm_command).with("echo %PROCESSOR_ARCHITECTURE%").and_return(arch_session_results)
247
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
248
+ allow(bootstrap).to receive(:run_command).and_return(0)
249
+ expect(bootstrap.bootstrap).to eq(0)
250
+ expect(Chef::Config[:knife][:architecture]).to eq(:i686)
251
+ end
252
+
253
+ context "when the target node is 64 bit" do
254
+ let(:arch_session_result) {
255
+ o = WinRM::Output.new
256
+ o[:data] << {stdout: "AMD64\r\n"}
257
+ o
258
+ }
259
+
260
+ it 'successfully bootstraps' do
261
+ allow(bootstrap).to receive(:wait_for_remote_response)
262
+ expect(bootstrap).to receive(:relay_winrm_command).with("echo %PROCESSOR_ARCHITECTURE%").and_return(arch_session_results)
263
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
264
+ allow(bootstrap).to receive(:run_command).and_return(0)
265
+ expect(bootstrap.bootstrap).to eq(0)
266
+ expect(Chef::Config[:knife][:architecture]).to eq(:x86_64)
267
+ end
268
+ end
269
+
270
+ context "when validation_key is not present" do
271
+ context "using chef 11", :chef_lt_12_only do
272
+ before do
273
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
274
+ end
275
+
276
+ it 'raises an exception if validation_key is not present in chef 11' do
277
+ expect(bootstrap.ui).to receive(:error)
278
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
279
+ end
280
+ end
281
+
282
+ context "using chef 12", :chef_gte_12_only do
283
+ before do
284
+ allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
285
+ bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
286
+ Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
287
+ end
288
+
289
+ it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
290
+ Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
291
+ expect(bootstrap.ui).to receive(:error)
292
+ expect { bootstrap.run }.to raise_error(SystemExit)
293
+ end
294
+
295
+ it 'raises an exception if chef_node_name is not present ' do
296
+ Chef::Config[:knife] = {:chef_node_name => nil}
297
+ expect(bootstrap.client_builder).not_to receive(:run)
298
+ expect(bootstrap.client_builder).not_to receive(:client_path)
299
+ expect(bootstrap.ui).to receive(:error)
300
+ expect { bootstrap.bootstrap }.to raise_error(SystemExit)
301
+ end
302
+ end
303
+ end
304
+
305
+ context "when doing chef vault", :chef_gte_12_only do
306
+ let(:vault_handler) { double('vault_handler', :doing_chef_vault? => true) }
307
+ let(:node_name) { 'foo.example.com' }
308
+ before do
309
+ allow(bootstrap).to receive(:wait_for_remote_response)
310
+ expect(bootstrap).to receive(:relay_winrm_command).with("echo %PROCESSOR_ARCHITECTURE%").and_return(arch_session_results)
311
+ allow(bootstrap).to receive(:create_bootstrap_bat_command)
312
+ allow(bootstrap).to receive(:run_command).and_return(0)
313
+ bootstrap.config[:chef_node_name] = node_name
314
+ bootstrap.chef_vault_handler = vault_handler
315
+ end
316
+
317
+ context "builder does not respond to client" do
318
+ before do
319
+ bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
320
+ end
321
+
322
+ it "passes a node search query to the handler" do
323
+ expect(vault_handler).to receive(:run).with(node_name: node_name)
324
+ bootstrap.bootstrap
325
+ end
326
+ end
327
+
328
+ context "builder responds to client" do
329
+ let(:client) { Chef::ApiClient.new }
330
+
331
+ before do
332
+ bootstrap.client_builder = double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil, :client => client)
333
+ end
334
+
335
+ it "passes a node search query to the handler" do
336
+ expect(vault_handler).to receive(:run).with(client)
337
+ bootstrap.bootstrap
338
+ end
339
+ end
340
+ end
341
+ end