knife-windows 1.2.1 → 1.3.0

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