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