knife-windows 1.2.1 → 1.3.0

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/.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