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