knife-winops 2.0.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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +30 -0
  5. data/CHANGELOG.md +147 -0
  6. data/DOC_CHANGES.md +22 -0
  7. data/Gemfile +13 -0
  8. data/LICENSE +201 -0
  9. data/README.md +430 -0
  10. data/RELEASE_NOTES.md +17 -0
  11. data/Rakefile +21 -0
  12. data/appveyor.yml +36 -0
  13. data/ci.gemfile +15 -0
  14. data/knife-winops.gemspec +26 -0
  15. data/lib/chef/knife/bootstrap/Chef_bootstrap.erb +44 -0
  16. data/lib/chef/knife/bootstrap/bootstrap.ps1 +134 -0
  17. data/lib/chef/knife/bootstrap/tail.cmd +15 -0
  18. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +302 -0
  19. data/lib/chef/knife/bootstrap_windows_base.rb +473 -0
  20. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -0
  21. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -0
  22. data/lib/chef/knife/core/windows_bootstrap_context.rb +356 -0
  23. data/lib/chef/knife/knife_windows_base.rb +33 -0
  24. data/lib/chef/knife/windows_cert_generate.rb +155 -0
  25. data/lib/chef/knife/windows_cert_install.rb +68 -0
  26. data/lib/chef/knife/windows_helper.rb +36 -0
  27. data/lib/chef/knife/windows_listener_create.rb +107 -0
  28. data/lib/chef/knife/winrm.rb +127 -0
  29. data/lib/chef/knife/winrm_base.rb +128 -0
  30. data/lib/chef/knife/winrm_knife_base.rb +315 -0
  31. data/lib/chef/knife/winrm_session.rb +101 -0
  32. data/lib/chef/knife/winrm_shared_options.rb +54 -0
  33. data/lib/chef/knife/wsman_endpoint.rb +44 -0
  34. data/lib/chef/knife/wsman_test.rb +118 -0
  35. data/lib/knife-winops/path_helper.rb +242 -0
  36. data/lib/knife-winops/version.rb +6 -0
  37. data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
  38. data/spec/assets/fake_trusted_certs/github.pem +42 -0
  39. data/spec/assets/fake_trusted_certs/google.crt +41 -0
  40. data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
  41. data/spec/dummy_winrm_connection.rb +21 -0
  42. data/spec/functional/bootstrap_download_spec.rb +229 -0
  43. data/spec/spec_helper.rb +93 -0
  44. data/spec/unit/knife/bootstrap_options_spec.rb +164 -0
  45. data/spec/unit/knife/bootstrap_template_spec.rb +98 -0
  46. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +410 -0
  47. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +292 -0
  48. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
  49. data/spec/unit/knife/windows_cert_install_spec.rb +51 -0
  50. data/spec/unit/knife/windows_listener_create_spec.rb +76 -0
  51. data/spec/unit/knife/winrm_session_spec.rb +101 -0
  52. data/spec/unit/knife/winrm_spec.rb +494 -0
  53. data/spec/unit/knife/wsman_test_spec.rb +209 -0
  54. metadata +157 -0
@@ -0,0 +1,101 @@
1
+ #
2
+ # Original knife-windows author:: Steven Murawski <smurawski@chef.io>
3
+ # Copyright:: Copyright (c) 2015-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 'dummy_winrm_connection'
21
+
22
+ Chef::Knife::Winrm.load_deps
23
+
24
+
25
+ describe Chef::Knife::WinrmSession do
26
+ let(:winrm_connection) { Dummy::Connection.new }
27
+ let(:options) { { transport: :plaintext } }
28
+
29
+ before do
30
+ @original_config = Chef::Config.hash_dup
31
+ allow(WinRM::Connection).to receive(:new).and_return(winrm_connection)
32
+ end
33
+
34
+ after do
35
+ Chef::Config.configuration = @original_config
36
+ end
37
+
38
+ subject { Chef::Knife::WinrmSession.new(options) }
39
+
40
+ describe "#initialize" do
41
+ context "when a proxy is configured" do
42
+ let(:proxy_uri) { 'blah.com' }
43
+ let(:ssl_policy) { double('DefaultSSLPolicy', :set_custom_certs => nil) }
44
+
45
+ before do
46
+ Chef::Config[:http_proxy] = proxy_uri
47
+ end
48
+
49
+ it "sets the http_proxy to the configured proxy" do
50
+ subject
51
+ expect(ENV['HTTP_PROXY']).to eq("http://#{proxy_uri}")
52
+ end
53
+
54
+ it "sets the ssl policy on the winrm client" do
55
+ expect(Chef::HTTP::DefaultSSLPolicy).to receive(:new)
56
+ .with(winrm_connection.transport.httpcli.ssl_config)
57
+ .and_return(ssl_policy)
58
+ expect(ssl_policy).to receive(:set_custom_certs)
59
+ subject
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ describe "#relay_command" do
66
+ it "run command and display commands output" do
67
+ expect(winrm_connection).to receive(:shell)
68
+ subject.relay_command("cmd.exe echo 'hi'")
69
+ end
70
+
71
+ it "exits with 401 if command execution raises a 401" do
72
+ expect(winrm_connection).to receive(:shell).and_raise(WinRM::WinRMHTTPTransportError.new('', '401'))
73
+ expect { subject.relay_command("cmd.exe echo 'hi'") }.to raise_error(WinRM::WinRMHTTPTransportError)
74
+ expect(subject.exit_code).to eql(401)
75
+ end
76
+
77
+ context "cmd shell" do
78
+ before do
79
+ options[:shell] = :cmd
80
+ options[:codepage] = 65001
81
+ end
82
+
83
+ it "creates shell and sends codepage" do
84
+ expect(winrm_connection).to receive(:shell).with(:cmd, hash_including(codepage: 65001))
85
+ subject.relay_command("cmd.exe echo 'hi'")
86
+ end
87
+ end
88
+
89
+ context "powershell shell" do
90
+ before do
91
+ options[:shell] = :powershell
92
+ options[:codepage] = 65001
93
+ end
94
+
95
+ it "does not send codepage to shell" do
96
+ expect(winrm_connection).to receive(:shell).with(:powershell)
97
+ subject.relay_command("cmd.exe echo 'hi'")
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,494 @@
1
+ #
2
+ # Original knife-windows author:: Bryan McLellan <btm@chef.io>
3
+ # Copyright:: Copyright (c) 2013-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 'dummy_winrm_connection'
21
+
22
+ Chef::Knife::Winrm.load_deps
23
+
24
+ describe Chef::Knife::Winrm do
25
+ before do
26
+ Chef::Config.reset
27
+ end
28
+
29
+ describe "#resolve_target_nodes" do
30
+ before do
31
+ @knife = Chef::Knife::Winrm.new
32
+ @knife.config[:attribute] = "fqdn"
33
+ @node_foo = Chef::Node.new
34
+ @node_foo.automatic_attrs[:fqdn] = "foo.example.org"
35
+ @node_foo.automatic_attrs[:ipaddress] = "10.0.0.1"
36
+ @node_bar = Chef::Node.new
37
+ @node_bar.automatic_attrs[:fqdn] = "bar.example.org"
38
+ @node_bar.automatic_attrs[:ipaddress] = "10.0.0.2"
39
+ @node_bar.automatic_attrs[:ec2][:public_hostname] = "somewhere.com"
40
+ @query = double("Chef::Search::Query")
41
+ end
42
+
43
+ context "when there are some hosts found but they do not have an attribute to connect with" do
44
+ before do
45
+ @knife.config[:manual] = false
46
+ @knife.config[:winrm_password] = 'P@ssw0rd!'
47
+ allow(@query).to receive(:search).and_return([[@node_foo, @node_bar]])
48
+ @node_foo.automatic_attrs[:fqdn] = nil
49
+ @node_bar.automatic_attrs[:fqdn] = nil
50
+ allow(Chef::Search::Query).to receive(:new).and_return(@query)
51
+ end
52
+
53
+ it "raises a specific error (KNIFE-222)" do
54
+ expect(@knife.ui).to receive(:fatal).with(/does not have the required attribute/)
55
+ expect(@knife).to receive(:exit).with(10)
56
+ @knife.configure_chef
57
+ @knife.resolve_target_nodes
58
+ end
59
+ end
60
+
61
+ context "when there are nested attributes" do
62
+ before do
63
+ @knife.config[:manual] = false
64
+ @knife.config[:winrm_password] = 'P@ssw0rd!'
65
+ allow(@query).to receive(:search).and_return([[@node_foo, @node_bar]])
66
+ allow(Chef::Search::Query).to receive(:new).and_return(@query)
67
+ end
68
+
69
+ it "uses the nested attributes (KNIFE-276)" do
70
+ @knife.config[:attribute] = "ec2.public_hostname"
71
+ @knife.configure_chef
72
+ @knife.resolve_target_nodes
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#configure_session" do
78
+ let(:winrm_user) { 'testuser' }
79
+ let(:transport) { 'plaintext' }
80
+ let(:password) { 'testpassword' }
81
+ let(:protocol) { 'basic' }
82
+ let(:knife_args) do
83
+ [
84
+ '-m', 'localhost',
85
+ '-x', winrm_user,
86
+ '-P', password,
87
+ '-w', transport,
88
+ '--winrm-authentication-protocol', protocol,
89
+ 'echo helloworld'
90
+ ]
91
+ end
92
+ let(:winrm_session) { double('winrm_session') }
93
+ let(:winrm_connection) { Dummy::Connection.new }
94
+
95
+ subject { Chef::Knife::Winrm.new(knife_args) }
96
+
97
+ context "when configuring the WinRM user name" do
98
+ context "when basic auth is used" do
99
+ let(:protocol) { 'basic' }
100
+
101
+ it "passes user name as given in options" do
102
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
103
+ expect(opts[:user]).to eq(winrm_user)
104
+ end.and_return(winrm_session)
105
+ subject.configure_session
106
+ end
107
+ end
108
+
109
+ context "when negotiate auth is used" do
110
+ let(:protocol) { 'negotiate' }
111
+
112
+ context "when user is prefixed with realm" do
113
+ let(:winrm_user) { "my_realm\\myself" }
114
+
115
+ it "passes user name as given in options" do
116
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
117
+ expect(opts[:user]).to eq(winrm_user)
118
+ end.and_return(winrm_session)
119
+ subject.configure_session
120
+ end
121
+ end
122
+
123
+ context "when user realm is included via email format" do
124
+ let(:winrm_user) { "myself@my_realm.com" }
125
+
126
+ it "passes user name as given in options" do
127
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
128
+ expect(opts[:user]).to eq(winrm_user)
129
+ end.and_return(winrm_session)
130
+ subject.configure_session
131
+ end
132
+ end
133
+
134
+ context "when a local user is given" do
135
+ it "prefixes user with the dot (local) realm" do
136
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
137
+ expect(opts[:user]).to eq(".\\#{winrm_user}")
138
+ end.and_return(winrm_session)
139
+ subject.configure_session
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ context "when configuring the WinRM password" do
146
+ it "passes password as given in options" do
147
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
148
+ expect(opts[:password]).to eq(password)
149
+ end.and_return(winrm_session)
150
+ subject.configure_session
151
+ end
152
+
153
+ context "when no password is given in the options" do
154
+ let(:knife_args) do
155
+ [
156
+ '-m', 'localhost',
157
+ '-x', winrm_user,
158
+ '-w', transport,
159
+ '--winrm-authentication-protocol', protocol,
160
+ 'echo helloworld'
161
+ ]
162
+ end
163
+ let(:prompted_password) { 'prompted_password' }
164
+
165
+ before do
166
+ allow(subject.ui).to receive(:ask).and_return(prompted_password)
167
+ end
168
+
169
+ it "passes password prompted" do
170
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
171
+ expect(opts[:password]).to eq(prompted_password)
172
+ end.and_return(winrm_session)
173
+ subject.configure_session
174
+ end
175
+ end
176
+ end
177
+
178
+ context "when configuring the WinRM transport" do
179
+ context "kerberos option is set" do
180
+ let(:winrm_command_http) { Chef::Knife::Winrm.new([
181
+ '-m', 'localhost',
182
+ '-x', 'testuser',
183
+ '-P', 'testpassword',
184
+ '--winrm-authentication-protocol', 'basic',
185
+ '--kerberos-realm', 'realm',
186
+ 'echo helloworld'
187
+ ]) }
188
+
189
+ it "sets the transport to kerberos" do
190
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:transport => :kerberos)).and_return(winrm_connection)
191
+ winrm_command_http.configure_chef
192
+ winrm_command_http.configure_session
193
+ end
194
+ end
195
+
196
+ context "kerberos option is set but nil" do
197
+ let(:winrm_command_http) { Chef::Knife::Winrm.new([
198
+ '-m', 'localhost',
199
+ '-x', 'testuser',
200
+ '-P', 'testpassword',
201
+ '--winrm-authentication-protocol', 'basic',
202
+ 'echo helloworld'
203
+ ]) }
204
+
205
+ it "sets the transport to plaintext" do
206
+ winrm_command_http.config[:kerberos_realm] = nil
207
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:transport => :plaintext)).and_return(winrm_connection)
208
+ winrm_command_http.configure_chef
209
+ winrm_command_http.configure_session
210
+ end
211
+ end
212
+
213
+ context "on windows workstations" do
214
+ let(:protocol) { 'negotiate' }
215
+
216
+ before do
217
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
218
+ end
219
+
220
+ it "defaults to negotiate when on a Windows host" do
221
+ expect(Chef::Knife::WinrmSession).to receive(:new) do |opts|
222
+ expect(opts[:transport]).to eq(:negotiate)
223
+ end.and_return(winrm_session)
224
+ subject.configure_session
225
+ end
226
+ end
227
+
228
+ context "on non-windows workstations" do
229
+ before do
230
+ allow(Chef::Platform).to receive(:windows?).and_return(false)
231
+ end
232
+
233
+ let(:winrm_command_http) { Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '-w', 'plaintext', '--winrm-authentication-protocol', 'basic', 'echo helloworld']) }
234
+
235
+ it "defaults to the http uri scheme" do
236
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :plaintext)).and_call_original
237
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:endpoint => 'http://localhost:5985/wsman')).and_return(winrm_connection)
238
+ winrm_command_http.configure_chef
239
+ winrm_command_http.configure_session
240
+ end
241
+
242
+ it "sets the operation timeout and verifes default" do
243
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:operation_timeout => 1800)).and_call_original
244
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:operation_timeout => 1800)).and_return(winrm_connection)
245
+ winrm_command_http.configure_chef
246
+ winrm_command_http.configure_session
247
+ end
248
+
249
+ it "sets the user specified winrm port" do
250
+ Chef::Config[:knife] = {winrm_port: "5988"}
251
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :plaintext)).and_call_original
252
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:transport => :plaintext)).and_return(winrm_connection)
253
+ winrm_command_http.configure_chef
254
+ winrm_command_http.configure_session
255
+ end
256
+
257
+ let(:winrm_command_timeout) { Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '--winrm-authentication-protocol', 'basic', '--session-timeout', '10', 'echo helloworld']) }
258
+
259
+ it "sets operation timeout and verify 10 Minute timeout" do
260
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:operation_timeout => 600)).and_call_original
261
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:operation_timeout => 600)).and_return(winrm_connection)
262
+ winrm_command_timeout.configure_chef
263
+ winrm_command_timeout.configure_session
264
+ end
265
+
266
+ let(:winrm_command_https) { Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '--winrm-transport', 'ssl', 'echo helloworld']) }
267
+
268
+ it "uses the https uri scheme if the ssl transport is specified" do
269
+ Chef::Config[:knife] = {:winrm_transport => 'ssl'}
270
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
271
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:endpoint => 'https://localhost:5986/wsman')).and_return(winrm_connection)
272
+ winrm_command_https.configure_chef
273
+ winrm_command_https.configure_session
274
+ end
275
+
276
+ it "uses the winrm port '5986' by default for ssl transport" do
277
+ Chef::Config[:knife] = {:winrm_transport => 'ssl'}
278
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
279
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:endpoint => 'https://localhost:5986/wsman')).and_return(winrm_connection)
280
+ winrm_command_https.configure_chef
281
+ winrm_command_https.configure_session
282
+ end
283
+
284
+ it "defaults to validating the server when the ssl transport is used" do
285
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
286
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:no_ssl_peer_verification => false)).and_return(winrm_connection)
287
+ winrm_command_https.configure_chef
288
+ winrm_command_https.configure_session
289
+ end
290
+
291
+ let(:winrm_command_verify_peer) { Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '--winrm-transport', 'ssl', '--winrm-ssl-verify-mode', 'verify_peer', 'echo helloworld'])}
292
+
293
+ it "validates the server when the ssl transport is used and the :winrm_ssl_verify_mode option is not configured to :verify_none" do
294
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
295
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:no_ssl_peer_verification => false)).and_return(winrm_connection)
296
+ winrm_command_verify_peer.configure_chef
297
+ winrm_command_verify_peer.configure_session
298
+ end
299
+
300
+ context "when setting verify_none" do
301
+ let(:transport) { 'ssl' }
302
+
303
+ before { knife_args << '--winrm-ssl-verify-mode' << 'verify_none' }
304
+
305
+ it "does not validate the server when the ssl transport is used and the :winrm_ssl_verify_mode option is set to :verify_none" do
306
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
307
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:no_ssl_peer_verification => true)).and_return(winrm_connection)
308
+ subject.configure_chef
309
+ subject.configure_session
310
+ end
311
+
312
+ it "prints warning output when the :winrm_ssl_verify_mode set to :verify_none to disable server validation" do
313
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
314
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:no_ssl_peer_verification => true)).and_return(winrm_connection)
315
+ expect(subject).to receive(:warn_no_ssl_peer_verification)
316
+
317
+ subject.configure_chef
318
+ subject.configure_session
319
+ end
320
+
321
+ context "when transport is plaintext" do
322
+ let(:transport) { 'plaintext' }
323
+
324
+ it "does not print warning re ssl server validation" do
325
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :plaintext)).and_call_original
326
+ expect(WinRM::Connection).to receive(:new).and_return(winrm_connection)
327
+ expect(subject).to_not receive(:warn_no_ssl_peer_verification)
328
+
329
+ subject.configure_chef
330
+ subject.configure_session
331
+ end
332
+ end
333
+ end
334
+
335
+ let(:winrm_command_ca_trust) { Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '--winrm-transport', 'ssl', '--ca-trust-file', '~/catrustroot', '--winrm-ssl-verify-mode', 'verify_none', 'echo helloworld'])}
336
+
337
+ it "validates the server when the ssl transport is used and the :ca_trust_file option is specified even if the :winrm_ssl_verify_mode option is set to :verify_none" do
338
+ expect(Chef::Knife::WinrmSession).to receive(:new).with(hash_including(:transport => :ssl)).and_call_original
339
+ expect(WinRM::Connection).to receive(:new).with(hash_including(:no_ssl_peer_verification => false)).and_return(winrm_connection)
340
+ winrm_command_ca_trust.configure_chef
341
+ winrm_command_ca_trust.configure_session
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ describe "#run" do
348
+ let(:session_opts) do
349
+ {
350
+ user: ".\\testuser",
351
+ password: "testpassword",
352
+ port: "5985",
353
+ transport: :plaintext,
354
+ host: "localhost"
355
+ }
356
+ end
357
+ let(:session) { Chef::Knife::WinrmSession.new(session_opts) }
358
+
359
+ before(:each) do
360
+ allow(Chef::Knife::WinrmSession).to receive(:new).and_return(session)
361
+ Chef::Config[:knife] = {:winrm_transport => 'plaintext'}
362
+ @winrm = Chef::Knife::Winrm.new(['-m', 'localhost', '-x', 'testuser', '-P', 'testpassword', '--winrm-authentication-protocol', 'basic', 'echo helloworld'])
363
+ end
364
+
365
+ it "returns with 0 if the command succeeds" do
366
+ allow(@winrm).to receive(:relay_winrm_command).and_return(0)
367
+ return_code = @winrm.run
368
+ expect(return_code).to be_zero
369
+ end
370
+
371
+ it "exits with exact exit status if the command fails and returns config is set to 0" do
372
+ command_status = 510
373
+
374
+ @winrm.config[:returns] = "0"
375
+ Chef::Config[:knife][:returns] = [0]
376
+
377
+ allow(@winrm).to receive(:relay_winrm_command)
378
+ allow(@winrm.ui).to receive(:error)
379
+ allow(session).to receive(:exit_code).and_return(command_status)
380
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(command_status) }
381
+ end
382
+
383
+ it "exits with non-zero status if the command fails and returns config is set to 0" do
384
+ command_status = 1
385
+ @winrm.config[:returns] = "0,53"
386
+ Chef::Config[:knife][:returns] = [0,53]
387
+ allow(@winrm).to receive(:relay_winrm_command).and_return(command_status)
388
+ allow(@winrm.ui).to receive(:error)
389
+ allow(session).to receive(:exit_code).and_return(command_status)
390
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(command_status) }
391
+ end
392
+
393
+ it "exits with a zero status if the command returns an expected non-zero status" do
394
+ command_status = 53
395
+ Chef::Config[:knife][:returns] = [0,53]
396
+ allow(@winrm).to receive(:relay_winrm_command).and_return(command_status)
397
+ allow(session).to receive(:exit_codes).and_return({"thishost" => command_status})
398
+ exit_code = @winrm.run
399
+ expect(exit_code).to be_zero
400
+ end
401
+
402
+ it "exits with a zero status if the command returns an expected non-zero status" do
403
+ command_status = 53
404
+ @winrm.config[:returns] = '0,53'
405
+ allow(@winrm).to receive(:relay_winrm_command).and_return(command_status)
406
+ allow(session).to receive(:exit_codes).and_return({"thishost" => command_status})
407
+ exit_code = @winrm.run
408
+ expect(exit_code).to be_zero
409
+ end
410
+
411
+ it "exits with 100 and no hint if command execution raises an exception other than 401" do
412
+ allow(@winrm).to receive(:relay_winrm_command).and_raise(WinRM::WinRMHTTPTransportError.new('', '500'))
413
+ allow(@winrm.ui).to receive(:error)
414
+ expect(@winrm.ui).to_not receive(:info)
415
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(100) }
416
+ end
417
+
418
+ it "exits with 100 if command execution raises a 401" do
419
+ allow(@winrm).to receive(:relay_winrm_command).and_raise(WinRM::WinRMHTTPTransportError.new('', '401'))
420
+ allow(@winrm.ui).to receive(:info)
421
+ allow(@winrm.ui).to receive(:error)
422
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit) { |e| expect(e.status).to eq(100) }
423
+ end
424
+
425
+ it "prints a hint on failure for negotiate authentication" do
426
+ @winrm.config[:winrm_authentication_protocol] = "negotiate"
427
+ @winrm.config[:winrm_transport] = "plaintext"
428
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
429
+ allow(session).to receive(:relay_command).and_raise(WinRM::WinRMAuthorizationError.new)
430
+ allow(@winrm.ui).to receive(:error)
431
+ allow(@winrm.ui).to receive(:info)
432
+ expect(@winrm.ui).to receive(:info).with(Chef::Knife::Winrm::FAILED_NOT_BASIC_HINT)
433
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit)
434
+ end
435
+
436
+ it "prints a hint on failure for basic authentication" do
437
+ @winrm.config[:winrm_authentication_protocol] = "basic"
438
+ @winrm.config[:winrm_transport] = "plaintext"
439
+ allow(session).to receive(:relay_command).and_raise(WinRM::WinRMHTTPTransportError.new('', '401'))
440
+ allow(@winrm.ui).to receive(:error)
441
+ allow(@winrm.ui).to receive(:info)
442
+ expect(@winrm.ui).to receive(:info).with(Chef::Knife::Winrm::FAILED_BASIC_HINT)
443
+ expect { @winrm.run_with_pretty_exceptions }.to raise_error(SystemExit)
444
+ end
445
+
446
+ context "when winrm_authentication_protocol specified" do
447
+ before do
448
+ Chef::Config[:knife] = {:winrm_transport => 'plaintext'}
449
+ allow(@winrm).to receive(:relay_winrm_command).and_return(0)
450
+ end
451
+
452
+ it "sets negotiate transport on windows for 'negotiate' authentication" do
453
+ @winrm.config[:winrm_authentication_protocol] = "negotiate"
454
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
455
+ allow(Chef::Knife::WinrmSession).to receive(:new) do |opts|
456
+ expect(opts[:disable_sspi]).to be(false)
457
+ expect(opts[:transport]).to be(:negotiate)
458
+ end.and_return(session)
459
+ @winrm.run
460
+ end
461
+
462
+ it "sets negotiate transport on unix for 'negotiate' authentication" do
463
+ @winrm.config[:winrm_authentication_protocol] = "negotiate"
464
+ allow(Chef::Platform).to receive(:windows?).and_return(false)
465
+ allow(Chef::Knife::WinrmSession).to receive(:new) do |opts|
466
+ expect(opts[:disable_sspi]).to be(false)
467
+ expect(opts[:transport]).to be(:negotiate)
468
+ end.and_return(session)
469
+ @winrm.run
470
+ end
471
+
472
+ it "disables sspi and skips the winrm monkey patch for 'ssl' transport and 'basic' authentication" do
473
+ @winrm.config[:winrm_authentication_protocol] = "basic"
474
+ @winrm.config[:winrm_transport] = "ssl"
475
+ @winrm.config[:winrm_port] = "5986"
476
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
477
+ allow(Chef::Knife::WinrmSession).to receive(:new) do |opts|
478
+ expect(opts[:port]).to be(@winrm.config[:winrm_port])
479
+ expect(opts[:transport]).to be(:ssl)
480
+ expect(opts[:disable_sspi]).to be(true)
481
+ expect(opts[:basic_auth_only]).to be(true)
482
+ end.and_return(session)
483
+ @winrm.run
484
+ end
485
+
486
+ it "raises an error if value is other than [basic, negotiate, kerberos]" do
487
+ @winrm.config[:winrm_authentication_protocol] = "invalid"
488
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
489
+ expect(@winrm.ui).to receive(:error)
490
+ expect { @winrm.run }.to raise_error(SystemExit)
491
+ end
492
+ end
493
+ end
494
+ end