knife-windows 1.5.0 → 1.6.0

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