winrm 1.8.1 → 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 +4 -4
- data/.gitignore +10 -11
- data/.rubocop.yml +26 -22
- data/.travis.yml +11 -12
- data/Gemfile +3 -9
- data/LICENSE +202 -202
- data/README.md +232 -215
- data/Rakefile +34 -36
- data/Vagrantfile +6 -9
- data/WinrmAppveyor.psm1 +31 -31
- data/appveyor.yml +51 -51
- data/bin/rwinrm +97 -97
- data/changelog.md +86 -86
- data/lib/winrm.rb +39 -42
- data/lib/winrm/connection.rb +82 -0
- data/lib/winrm/connection_opts.rb +87 -0
- data/lib/winrm/{exceptions/exceptions.rb → exceptions.rb} +76 -57
- data/lib/winrm/http/response_handler.rb +96 -82
- data/lib/winrm/http/transport.rb +424 -435
- data/lib/winrm/http/transport_factory.rb +68 -0
- data/lib/winrm/output.rb +59 -43
- data/lib/winrm/psrp/create_pipeline.xml.erb +167 -0
- data/lib/winrm/psrp/fragment.rb +70 -0
- data/lib/winrm/psrp/init_runspace_pool.xml.erb +224 -0
- data/lib/winrm/psrp/message.rb +130 -0
- data/lib/winrm/psrp/message_data.rb +41 -0
- data/lib/winrm/psrp/message_data/base.rb +49 -0
- data/lib/winrm/psrp/message_data/error_record.rb +68 -0
- data/lib/winrm/psrp/message_data/pipeline_host_call.rb +32 -0
- data/lib/winrm/psrp/message_data/pipeline_output.rb +49 -0
- data/lib/winrm/psrp/message_data/runspacepool_host_call.rb +32 -0
- data/lib/winrm/psrp/message_data/runspacepool_state.rb +39 -0
- data/lib/winrm/psrp/message_data/session_capability.rb +36 -0
- data/lib/winrm/psrp/message_defragmenter.rb +62 -0
- data/lib/winrm/psrp/message_factory.rb +75 -0
- data/lib/winrm/psrp/message_fragmenter.rb +60 -0
- data/lib/winrm/psrp/powershell_output_decoder.rb +120 -0
- data/lib/winrm/psrp/receive_response_reader.rb +93 -0
- data/lib/winrm/psrp/session_capability.xml.erb +7 -0
- data/lib/winrm/psrp/uuid.rb +40 -0
- data/lib/winrm/shells/base.rb +175 -0
- data/lib/winrm/shells/cmd.rb +65 -0
- data/lib/winrm/shells/power_shell.rb +201 -0
- data/lib/winrm/shells/retryable.rb +45 -0
- data/lib/winrm/shells/shell_factory.rb +50 -0
- data/lib/winrm/version.rb +7 -7
- data/lib/winrm/wsmv/base.rb +59 -0
- data/lib/winrm/wsmv/cleanup_command.rb +61 -0
- data/lib/winrm/wsmv/close_shell.rb +50 -0
- data/lib/winrm/wsmv/command.rb +101 -0
- data/lib/winrm/wsmv/command_output.rb +76 -0
- data/lib/winrm/wsmv/command_output_decoder.rb +55 -0
- data/lib/winrm/wsmv/configuration.rb +46 -0
- data/lib/winrm/wsmv/create_pipeline.rb +66 -0
- data/lib/winrm/wsmv/create_shell.rb +119 -0
- data/lib/winrm/wsmv/header.rb +203 -0
- data/lib/winrm/wsmv/init_runspace_pool.rb +95 -0
- data/lib/winrm/wsmv/iso8601_duration.rb +60 -0
- data/lib/winrm/wsmv/keep_alive.rb +68 -0
- data/lib/winrm/wsmv/receive_response_reader.rb +128 -0
- data/lib/winrm/wsmv/send_data.rb +68 -0
- data/lib/winrm/wsmv/soap.rb +51 -0
- data/lib/winrm/wsmv/wql_query.rb +79 -0
- data/lib/winrm/wsmv/write_stdin.rb +88 -0
- data/preamble +17 -17
- data/{spec → tests/integration}/auth_timeout_spec.rb +18 -16
- data/{spec → tests/integration}/cmd_spec.rb +104 -102
- data/{spec → tests/integration}/config-example.yml +16 -19
- data/{spec → tests/integration}/issue_59_spec.rb +26 -23
- data/tests/integration/powershell_spec.rb +154 -0
- data/{spec → tests/integration}/spec_helper.rb +65 -73
- data/{spec → tests/integration}/transport_spec.rb +99 -139
- data/{spec → tests/integration}/wql_spec.rb +16 -14
- data/{spec → tests}/matchers.rb +60 -74
- data/tests/spec/configuration_spec.rb +93 -0
- data/tests/spec/connection_spec.rb +39 -0
- data/{spec → tests/spec}/exception_spec.rb +50 -50
- data/tests/spec/http/transport_factory_spec.rb +68 -0
- data/tests/spec/http/transport_spec.rb +44 -0
- data/{spec → tests/spec}/output_spec.rb +127 -110
- data/tests/spec/psrp/fragment_spec.rb +62 -0
- data/tests/spec/psrp/message_data/base_spec.rb +13 -0
- data/tests/spec/psrp/message_data/error_record_spec.rb +41 -0
- data/tests/spec/psrp/message_data/pipeline_host_call_spec.rb +25 -0
- data/tests/spec/psrp/message_data/pipeline_output_spec.rb +32 -0
- data/tests/spec/psrp/message_data/runspace_pool_host_call_spec.rb +25 -0
- data/tests/spec/psrp/message_data/runspacepool_state_spec.rb +16 -0
- data/tests/spec/psrp/message_data/session_capability_spec.rb +30 -0
- data/tests/spec/psrp/message_data_spec.rb +35 -0
- data/tests/spec/psrp/message_defragmenter_spec.rb +47 -0
- data/tests/spec/psrp/message_fragmenter_spec.rb +105 -0
- data/tests/spec/psrp/powershell_output_decoder_spec.rb +84 -0
- data/tests/spec/psrp/psrp_message_spec.rb +70 -0
- data/tests/spec/psrp/recieve_response_reader_spec.rb +154 -0
- data/tests/spec/psrp/uuid_spec.rb +28 -0
- data/{spec → tests/spec}/response_handler_spec.rb +61 -61
- data/tests/spec/shells/base_spec.rb +202 -0
- data/tests/spec/shells/cmd_spec.rb +75 -0
- data/tests/spec/shells/powershell_spec.rb +175 -0
- data/tests/spec/spec_helper.rb +47 -0
- data/tests/spec/stubs/clixml/error_record.xml.erb +84 -0
- data/{spec → tests/spec}/stubs/responses/get_command_output_response.xml.erb +13 -13
- data/tests/spec/stubs/responses/get_command_output_response_not_done.xml.erb +10 -0
- data/tests/spec/stubs/responses/get_powershell_keepalive_response.xml.erb +10 -0
- data/tests/spec/stubs/responses/get_powershell_output_response.xml.erb +12 -0
- data/tests/spec/stubs/responses/get_powershell_output_response_not_done.xml.erb +9 -0
- data/{spec → tests/spec}/stubs/responses/open_shell_v1.xml +19 -19
- data/{spec → tests/spec}/stubs/responses/open_shell_v2.xml +20 -20
- data/{spec → tests/spec}/stubs/responses/soap_fault_v1.xml +36 -36
- data/{spec → tests/spec}/stubs/responses/soap_fault_v2.xml +42 -42
- data/{spec → tests/spec}/stubs/responses/wmi_error_v2.xml +41 -41
- data/tests/spec/wsmv/cleanup_command_spec.rb +22 -0
- data/tests/spec/wsmv/close_shell_spec.rb +17 -0
- data/{spec → tests/spec/wsmv}/command_output_decoder_spec.rb +37 -37
- data/tests/spec/wsmv/command_output_spec.rb +45 -0
- data/tests/spec/wsmv/command_spec.rb +19 -0
- data/tests/spec/wsmv/configuration_spec.rb +17 -0
- data/tests/spec/wsmv/create_pipeline_spec.rb +31 -0
- data/tests/spec/wsmv/create_shell_spec.rb +38 -0
- data/tests/spec/wsmv/init_runspace_pool_spec.rb +36 -0
- data/tests/spec/wsmv/keep_alive_spec.rb +21 -0
- data/tests/spec/wsmv/receive_response_reader_spec.rb +123 -0
- data/tests/spec/wsmv/send_data_spec.rb +30 -0
- data/tests/spec/wsmv/wql_query_spec.rb +13 -0
- data/tests/spec/wsmv/write_stdin_spec.rb +22 -0
- data/winrm.gemspec +42 -40
- metadata +140 -38
- data/.rspec +0 -3
- data/lib/winrm/command_executor.rb +0 -243
- data/lib/winrm/command_output_decoder.rb +0 -53
- data/lib/winrm/helpers/iso8601_duration.rb +0 -58
- data/lib/winrm/helpers/powershell_script.rb +0 -42
- data/lib/winrm/soap_provider.rb +0 -39
- data/lib/winrm/winrm_service.rb +0 -550
- data/spec/command_executor_spec.rb +0 -475
- data/spec/issue_184_spec.rb +0 -67
- data/spec/powershell_spec.rb +0 -97
- data/spec/winrm_options_spec.rb +0 -76
- data/spec/winrm_primitives_spec.rb +0 -51
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Author:: Fletcher (<fnichol@nichol.ca>)
|
|
4
|
-
#
|
|
5
|
-
# Copyright (C) 2015, Fletcher Nichol
|
|
6
|
-
#
|
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the 'License');
|
|
8
|
-
# you may not use this file except in compliance with the License.
|
|
9
|
-
# You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
# distributed under the License is distributed on an 'AS IS' BASIS,
|
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
# See the License for the specific language governing permissions and
|
|
17
|
-
# limitations under the License.
|
|
18
|
-
|
|
19
|
-
require 'winrm/command_executor'
|
|
20
|
-
|
|
21
|
-
require 'base64'
|
|
22
|
-
require 'securerandom'
|
|
23
|
-
|
|
24
|
-
describe WinRM::CommandExecutor, unit: true do
|
|
25
|
-
let(:logged_output) { StringIO.new }
|
|
26
|
-
let(:shell_id) { 'shell-123' }
|
|
27
|
-
let(:executor_args) { [service, logger] }
|
|
28
|
-
let(:executor) { WinRM::CommandExecutor.new(service) }
|
|
29
|
-
let(:service) do
|
|
30
|
-
double(
|
|
31
|
-
'winrm_service',
|
|
32
|
-
logger: Logging.logger['test'],
|
|
33
|
-
retry_limit: 1,
|
|
34
|
-
retry_delay: 1
|
|
35
|
-
)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
let(:version_output) { { xml_fragment: [{ version: '6.3.9600' }] } }
|
|
39
|
-
|
|
40
|
-
before do
|
|
41
|
-
allow(service).to receive(:open_shell).and_return(shell_id)
|
|
42
|
-
allow(service).to receive(:run_wql).and_return(version_output)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
describe '#close' do
|
|
46
|
-
it 'calls service#close_shell' do
|
|
47
|
-
executor.open
|
|
48
|
-
expect(service).to receive(:close_shell).with(shell_id)
|
|
49
|
-
|
|
50
|
-
executor.close
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it 'only calls service#close_shell once for multiple calls' do
|
|
54
|
-
executor.open
|
|
55
|
-
expect(service).to receive(:close_shell).with(shell_id).once
|
|
56
|
-
|
|
57
|
-
executor.close
|
|
58
|
-
executor.close
|
|
59
|
-
executor.close
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'undefines finalizer' do
|
|
63
|
-
allow(service).to receive(:close_shell)
|
|
64
|
-
allow(ObjectSpace).to receive(:define_finalizer) { |e, _| e == executor }
|
|
65
|
-
expect(ObjectSpace).to receive(:undefine_finalizer).with(executor)
|
|
66
|
-
executor.open
|
|
67
|
-
|
|
68
|
-
executor.close
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
describe '#open' do
|
|
73
|
-
it 'calls service#open_shell' do
|
|
74
|
-
expect(service).to receive(:open_shell).and_return(shell_id)
|
|
75
|
-
|
|
76
|
-
executor.open
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
it 'defines a finalizer' do
|
|
80
|
-
expect(ObjectSpace).to receive(:define_finalizer) do |e, _|
|
|
81
|
-
expect(e).to eq(executor)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
executor.open
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it 'returns a shell id as a string' do
|
|
88
|
-
expect(executor.open).to eq shell_id
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
describe 'failed connection attempts' do
|
|
92
|
-
let(:error) { HTTPClient::ConnectTimeoutError }
|
|
93
|
-
let(:limit) { 3 }
|
|
94
|
-
let(:delay) { 0.1 }
|
|
95
|
-
|
|
96
|
-
before do
|
|
97
|
-
allow(service).to receive(:open_shell).and_raise(error)
|
|
98
|
-
allow(service).to receive(:retry_delay).and_return(delay)
|
|
99
|
-
allow(service).to receive(:retry_limit).and_return(limit)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it 'attempts to connect :retry_limit times' do
|
|
103
|
-
begin
|
|
104
|
-
allow(service).to receive(:open_shell).exactly.times(limit)
|
|
105
|
-
executor.open
|
|
106
|
-
rescue # rubocop:disable Lint/HandleExceptions
|
|
107
|
-
# the raise is not what is being tested here, rather its side-effect
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 'raises the inner error after retries' do
|
|
112
|
-
expect { executor.open }.to raise_error(error)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
describe 'for modern windows distributions' do
|
|
117
|
-
let(:version_output) { { xml_fragment: [{ version: '10.0.10586.63' }] } }
|
|
118
|
-
|
|
119
|
-
it 'sets #max_commands to 1500 - 2' do
|
|
120
|
-
expect(executor.max_commands).to eq(1500 - 2)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
it 'sets code_page to UTF-8' do
|
|
124
|
-
expect(executor.code_page).to eq 65_001
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
describe 'for older/legacy windows distributions' do
|
|
129
|
-
let(:version_output) { { xml_fragment: [{ version: '6.1.8500' }] } }
|
|
130
|
-
|
|
131
|
-
it 'sets #max_commands to 15 - 2' do
|
|
132
|
-
expect(executor.max_commands).to eq(15 - 2)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it 'sets code_page to UTF-8' do
|
|
136
|
-
expect(executor.code_page).to eq 65_001
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
describe 'for super duper older/legacy windows distributions' do
|
|
141
|
-
let(:version_output) { { xml_fragment: [{ version: '6.0.8500' }] } }
|
|
142
|
-
|
|
143
|
-
it 'sets #max_commands to 15 - 2' do
|
|
144
|
-
expect(executor.max_commands).to eq(15 - 2)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
it 'sets code_page to MS-DOS' do
|
|
148
|
-
expect(executor.code_page).to eq 437
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
describe 'when unable to find os version' do
|
|
153
|
-
let(:version_output) { { xml_fragment: [{ funny_clowns: 'haha' }] } }
|
|
154
|
-
|
|
155
|
-
it 'raises WinRMError' do
|
|
156
|
-
expect { executor.code_page }.to raise_error(
|
|
157
|
-
::WinRM::WinRMError,
|
|
158
|
-
'Unable to determine endpoint os version'
|
|
159
|
-
)
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
describe '#run_cmd' do
|
|
165
|
-
describe 'when #open has not been previously called' do
|
|
166
|
-
it 'raises a WinRMError error' do
|
|
167
|
-
expect { executor.run_cmd('nope') }.to raise_error(
|
|
168
|
-
::WinRM::WinRMError,
|
|
169
|
-
"#{executor.class}#open must be called before any run methods are invoked"
|
|
170
|
-
)
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
describe 'when #open has been previously called' do
|
|
175
|
-
let(:command_id) { 'command-123' }
|
|
176
|
-
|
|
177
|
-
let(:echo_output) do
|
|
178
|
-
o = ::WinRM::Output.new
|
|
179
|
-
o[:exitcode] = 0
|
|
180
|
-
o[:data].concat([
|
|
181
|
-
{ stdout: 'Hello\r\n' },
|
|
182
|
-
{ stderr: 'Psst\r\n' }
|
|
183
|
-
])
|
|
184
|
-
o
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
before do
|
|
188
|
-
stub_cmd(shell_id, 'echo', ['Hello'], echo_output, command_id)
|
|
189
|
-
|
|
190
|
-
executor.open
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
it 'calls service#run_command' do
|
|
194
|
-
expect(service).to receive(:run_command).with(shell_id, 'echo', ['Hello'])
|
|
195
|
-
|
|
196
|
-
executor.run_cmd('echo', ['Hello'])
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
it 'calls service#get_command_output to get results' do
|
|
200
|
-
expect(service).to receive(:get_command_output).with(shell_id, command_id)
|
|
201
|
-
|
|
202
|
-
executor.run_cmd('echo', ['Hello'])
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
it 'calls service#get_command_output with a block to get results' do
|
|
206
|
-
blk = proc { |_, _| 'something' }
|
|
207
|
-
expect(service).to receive(:get_command_output).with(shell_id, command_id, &blk)
|
|
208
|
-
|
|
209
|
-
executor.run_cmd('echo', ['Hello'], &blk)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
it 'returns an Output object hash' do
|
|
213
|
-
expect(executor.run_cmd('echo', ['Hello'])).to eq echo_output
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
it 'runs the block in #get_command_output when given' do
|
|
217
|
-
io_out = StringIO.new
|
|
218
|
-
io_err = StringIO.new
|
|
219
|
-
stub_cmd(
|
|
220
|
-
shell_id,
|
|
221
|
-
'echo',
|
|
222
|
-
['Hello'],
|
|
223
|
-
echo_output,
|
|
224
|
-
command_id
|
|
225
|
-
).and_yield(echo_output.stdout, echo_output.stderr)
|
|
226
|
-
output = executor.run_cmd('echo', ['Hello']) do |stdout, stderr|
|
|
227
|
-
io_out << stdout if stdout
|
|
228
|
-
io_err << stderr if stderr
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
expect(io_out.string).to eq 'Hello\r\n'
|
|
232
|
-
expect(io_err.string).to eq 'Psst\r\n'
|
|
233
|
-
expect(output).to eq echo_output
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
shared_examples 'retry shell command' do
|
|
237
|
-
it 'does not close the current shell' do
|
|
238
|
-
expect(service).not_to receive(:close_shell)
|
|
239
|
-
|
|
240
|
-
executor.run_cmd('echo', ['Hello'])
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
it 'opens a new shell once' do
|
|
244
|
-
expect(service).to receive(:open_shell).once
|
|
245
|
-
|
|
246
|
-
executor.run_cmd('echo', ['Hello'])
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
it 'retries the command once' do
|
|
250
|
-
expect(service).to receive(:run_command).exactly(2).times
|
|
251
|
-
|
|
252
|
-
executor.run_cmd('echo', ['Hello'])
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
describe 'when shell is closed on server' do
|
|
257
|
-
before do
|
|
258
|
-
@times_called = 0
|
|
259
|
-
|
|
260
|
-
allow(service).to receive(:run_command) do
|
|
261
|
-
@times_called += 1
|
|
262
|
-
fail WinRM::WinRMWSManFault.new('oops', '2150858843') if @times_called == 1
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
include_examples 'retry shell command'
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
describe 'when shell accesses a deleted registry key' do
|
|
270
|
-
before do
|
|
271
|
-
@times_called = 0
|
|
272
|
-
|
|
273
|
-
allow(service).to receive(:run_command) do
|
|
274
|
-
@times_called += 1
|
|
275
|
-
fail WinRM::WinRMWSManFault.new('oops', '2147943418') if @times_called == 1
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
include_examples 'retry shell command'
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
describe 'when called many times over time' do
|
|
284
|
-
# use a 'old' version of windows with lower max_commands threshold
|
|
285
|
-
# to trigger quicker shell recyles
|
|
286
|
-
let(:version_output) { { xml_fragment: [{ version: '6.1.8500' }] } }
|
|
287
|
-
|
|
288
|
-
let(:echo_output) do
|
|
289
|
-
o = ::WinRM::Output.new
|
|
290
|
-
o[:exitcode] = 0
|
|
291
|
-
o[:data].concat([{ stdout: 'Hello\r\n' }])
|
|
292
|
-
o
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
before do
|
|
296
|
-
allow(service).to receive(:open_shell).and_return('s1', 's2')
|
|
297
|
-
allow(service).to receive(:close_shell)
|
|
298
|
-
allow(service).to receive(:run_command).and_yield('command-xxx')
|
|
299
|
-
allow(service).to receive(:get_command_output).and_return(echo_output)
|
|
300
|
-
allow(service).to receive(:run_wql).with('select version from Win32_OperatingSystem')
|
|
301
|
-
.and_return(version_output)
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
it 'resets the shell when #max_commands threshold is tripped' do
|
|
305
|
-
iterations = 35
|
|
306
|
-
reset_times = iterations / (15 - 2)
|
|
307
|
-
|
|
308
|
-
expect(service).to receive(:close_shell).exactly(reset_times).times
|
|
309
|
-
executor.open
|
|
310
|
-
iterations.times { executor.run_cmd('echo', ['Hello']) }
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
describe '#run_powershell_script' do
|
|
316
|
-
describe 'when #open has not been previously called' do
|
|
317
|
-
it 'raises a WinRMError error' do
|
|
318
|
-
expect { executor.run_powershell_script('nope') }.to raise_error(
|
|
319
|
-
::WinRM::WinRMError,
|
|
320
|
-
"#{executor.class}#open must be called before any run methods are invoked"
|
|
321
|
-
)
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
describe 'when #open has been previously called' do
|
|
326
|
-
let(:command_id) { 'command-123' }
|
|
327
|
-
|
|
328
|
-
let(:echo_output) do
|
|
329
|
-
o = ::WinRM::Output.new
|
|
330
|
-
o[:exitcode] = 0
|
|
331
|
-
o[:data].concat([
|
|
332
|
-
{ stdout: 'Hello\r\n' },
|
|
333
|
-
{ stderr: 'Psst\r\n' }
|
|
334
|
-
])
|
|
335
|
-
o
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
before do
|
|
339
|
-
stub_powershell_script(
|
|
340
|
-
shell_id,
|
|
341
|
-
'echo Hello',
|
|
342
|
-
echo_output,
|
|
343
|
-
command_id
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
executor.open
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
it 'calls service#run_command' do
|
|
350
|
-
expect(service).to receive(:run_command).with(
|
|
351
|
-
shell_id,
|
|
352
|
-
'powershell',
|
|
353
|
-
[
|
|
354
|
-
'-encodedCommand',
|
|
355
|
-
::WinRM::PowershellScript.new('echo Hello')
|
|
356
|
-
.encoded
|
|
357
|
-
]
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
executor.run_powershell_script('echo Hello')
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
it 'calls service#get_command_output to get results' do
|
|
364
|
-
expect(service).to receive(:get_command_output).with(shell_id, command_id)
|
|
365
|
-
|
|
366
|
-
executor.run_powershell_script('echo Hello')
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
it 'calls service#get_command_output with a block to get results' do
|
|
370
|
-
blk = proc { |_, _| 'something' }
|
|
371
|
-
expect(service).to receive(:get_command_output).with(shell_id, command_id, &blk)
|
|
372
|
-
|
|
373
|
-
executor.run_powershell_script('echo Hello', &blk)
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
it 'returns an Output object hash' do
|
|
377
|
-
expect(executor.run_powershell_script('echo Hello')).to eq echo_output
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
it 'runs the block in #get_command_output when given' do
|
|
381
|
-
io_out = StringIO.new
|
|
382
|
-
io_err = StringIO.new
|
|
383
|
-
stub_cmd(shell_id, 'echo', ['Hello'], echo_output, command_id)
|
|
384
|
-
.and_yield(echo_output.stdout, echo_output.stderr)
|
|
385
|
-
output = executor.run_powershell_script('echo Hello') do |stdout, stderr|
|
|
386
|
-
io_out << stdout if stdout
|
|
387
|
-
io_err << stderr if stderr
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
expect(io_out.string).to eq 'Hello\r\n'
|
|
391
|
-
expect(io_err.string).to eq 'Psst\r\n'
|
|
392
|
-
expect(output).to eq echo_output
|
|
393
|
-
end
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
describe 'when called many times over time' do
|
|
397
|
-
# use a 'old' version of windows with lower max_commands threshold
|
|
398
|
-
# to trigger quicker shell recyles
|
|
399
|
-
let(:version_output) { { xml_fragment: [{ version: '6.1.8500' }] } }
|
|
400
|
-
|
|
401
|
-
let(:echo_output) do
|
|
402
|
-
o = ::WinRM::Output.new
|
|
403
|
-
o[:exitcode] = 0
|
|
404
|
-
o[:data].concat([{ stdout: 'Hello\r\n' }])
|
|
405
|
-
o
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
before do
|
|
409
|
-
allow(service).to receive(:open_shell).and_return('s1', 's2')
|
|
410
|
-
allow(service).to receive(:close_shell)
|
|
411
|
-
allow(service).to receive(:run_command).and_yield('command-xxx')
|
|
412
|
-
allow(service).to receive(:get_command_output).and_return(echo_output)
|
|
413
|
-
allow(service).to receive(:wsman_identify).with('select version from Win32_OperatingSystem')
|
|
414
|
-
.and_return(version_output)
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
it 'resets the shell when #max_commands threshold is tripped' do
|
|
418
|
-
iterations = 35
|
|
419
|
-
reset_times = iterations / (15 - 2)
|
|
420
|
-
|
|
421
|
-
expect(service).to receive(:close_shell).exactly(reset_times).times
|
|
422
|
-
executor.open
|
|
423
|
-
iterations.times { executor.run_powershell_script('echo Hello') }
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
describe '#shell' do
|
|
429
|
-
it 'is initially nil' do
|
|
430
|
-
expect(executor.shell).to eq nil
|
|
431
|
-
end
|
|
432
|
-
|
|
433
|
-
it 'is set after #open is called' do
|
|
434
|
-
executor.open
|
|
435
|
-
|
|
436
|
-
expect(executor.shell).to eq shell_id
|
|
437
|
-
end
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def decode(powershell)
|
|
441
|
-
Base64.strict_decode64(powershell).encode('UTF-8', 'UTF-16LE')
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
def debug_line_with(msg)
|
|
445
|
-
/^D, .* : #{Regexp.escape(msg)}/
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
def regexify(string)
|
|
449
|
-
Regexp.new(Regexp.escape(string))
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
def regexify_line(string)
|
|
453
|
-
Regexp.new("^#{Regexp.escape(string)}$")
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
# rubocop:disable Metrics/ParameterLists
|
|
457
|
-
def stub_cmd(shell_id, cmd, args, output, command_id = nil, &block)
|
|
458
|
-
command_id ||= SecureRandom.uuid
|
|
459
|
-
|
|
460
|
-
allow(service).to receive(:run_command).with(shell_id, cmd, args).and_yield(command_id)
|
|
461
|
-
allow(service).to receive(:get_command_output).with(shell_id, command_id, &block)
|
|
462
|
-
.and_return(output)
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
def stub_powershell_script(shell_id, script, output, command_id = nil)
|
|
466
|
-
stub_cmd(
|
|
467
|
-
shell_id,
|
|
468
|
-
'powershell',
|
|
469
|
-
['-encodedCommand', ::WinRM::PowershellScript.new(script).encoded],
|
|
470
|
-
output,
|
|
471
|
-
command_id
|
|
472
|
-
)
|
|
473
|
-
end
|
|
474
|
-
# rubocop:enable Metrics/ParameterLists
|
|
475
|
-
end
|