knife-windows 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +139 -135
  4. data/DOC_CHANGES.md +22 -22
  5. data/Gemfile +13 -13
  6. data/README.md +404 -404
  7. data/RELEASE_NOTES.md +9 -9
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +26 -26
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -246
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -443
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -116
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -102
  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 +128 -128
  23. data/lib/chef/knife/winrm_knife_base.rb +307 -307
  24. data/lib/chef/knife/winrm_session.rb +98 -98
  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 +242 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
  31. data/spec/assets/fake_trusted_certs/github.pem +42 -0
  32. data/spec/assets/fake_trusted_certs/google.crt +41 -0
  33. data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
  34. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  38. data/spec/assets/win_template_unrendered.txt +246 -246
  39. data/spec/dummy_winrm_connection.rb +21 -21
  40. data/spec/functional/bootstrap_download_spec.rb +236 -236
  41. data/spec/spec_helper.rb +94 -94
  42. data/spec/unit/knife/bootstrap_options_spec.rb +157 -157
  43. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -423
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +213 -177
  46. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  47. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  48. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  49. data/spec/unit/knife/winrm_session_spec.rb +95 -95
  50. data/spec/unit/knife/winrm_spec.rb +500 -500
  51. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  52. 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