knife-windows 1.0.0.rc.1 → 1.0.0.rc.2

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