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

Sign up to get free protection for your applications and to get access to all the features.
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