winrm 1.3.0.dev.2 → 1.3.0.dev.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +14 -6
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/Vagrantfile +8 -0
- data/bin/rwinrm +34 -70
- data/lib/winrm/exceptions/exceptions.rb +17 -1
- data/lib/winrm/helpers/powershell_script.rb +22 -0
- data/lib/winrm/http/response_handler.rb +15 -3
- data/lib/winrm/http/transport.rb +14 -1
- data/lib/winrm/output.rb +27 -0
- data/lib/winrm/winrm_service.rb +21 -11
- data/lib/winrm.rb +1 -1
- data/spec/cmd_spec.rb +32 -0
- data/spec/config-example.yml +1 -1
- data/spec/exception_spec.rb +17 -0
- data/spec/output_spec.rb +106 -0
- data/spec/powershell_spec.rb +34 -0
- data/spec/response_handler_spec.rb +14 -0
- data/spec/stubs/responses/wmi_error_v2.xml +41 -0
- data/spec/winrm_options_spec.rb +75 -0
- data/spec/winrm_primitives_spec.rb +0 -18
- data/winrm.gemspec +1 -3
- metadata +10 -38
- data/lib/winrm/file_transfer/remote_file.rb +0 -184
- data/lib/winrm/file_transfer/remote_zip_file.rb +0 -60
- data/lib/winrm/file_transfer.rb +0 -39
- data/spec/file_transfer/remote_file_spec.rb +0 -71
- data/spec/file_transfer/remote_zip_file_spec.rb +0 -51
- data/spec/file_transfer_spec.rb +0 -39
@@ -41,4 +41,18 @@ describe 'response handler', :unit => true do
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
describe "failed 500 WMI error response" do
|
46
|
+
let(:wmi_error) { File.read("spec/stubs/responses/wmi_error_v2.xml") }
|
47
|
+
|
48
|
+
it 'raises a WinRMWMIError' do
|
49
|
+
handler = WinRM::ResponseHandler.new(wmi_error, 500)
|
50
|
+
begin
|
51
|
+
handler.parse_to_xml()
|
52
|
+
rescue WinRM::WinRMWMIError => e
|
53
|
+
expect(e.error_code).to eq('2150859173')
|
54
|
+
expect(e.error).to include('The WS-Management service cannot process the request.')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
44
58
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<s:Envelope xmlns:a='http://schemas.xmlsoap.org/ws/2004/08/addressing' xmlns:e='http://schemas.xmlsoap.org/ws/2004/08/eventing' xml:lang='en-US' xmlns:n='http://schemas.xmlsoap.org/ws/2004/09/enumeration' xmlns:p='http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd' xmlns:s='http://www.w3.org/2003/05/soap-envelope' xmlns:w='http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' xmlns:x='http://schemas.xmlsoap.org/ws/2004/09/transfer'>
|
2
|
+
<s:Header>
|
3
|
+
<a:Action>http://schemas.dmtf.org/wbem/wsman/1/wsman/fault</a:Action>
|
4
|
+
<a:MessageID>uuid:B8829021-48C3-4F27-B94B-491DAC4F29B1</a:MessageID>
|
5
|
+
<a:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:To>
|
6
|
+
</s:Header>
|
7
|
+
<s:Body>
|
8
|
+
<s:Fault>
|
9
|
+
<s:Code>
|
10
|
+
<s:Value>s:Sender</s:Value>
|
11
|
+
<s:Subcode>
|
12
|
+
<s:Value>w:QuotaLimit</s:Value>
|
13
|
+
</s:Subcode>
|
14
|
+
</s:Code>
|
15
|
+
<s:Reason>
|
16
|
+
<s:Text xml:lang='en-US'>The WS-Management service cannot process the request. The maximum number of concurrent shells for this user has been exceeded. Close existing shells or raise the quota for this user. </s:Text>
|
17
|
+
</s:Reason>
|
18
|
+
<s:Detail>
|
19
|
+
<p:MSFT_WmiError b:IsCIM_Error='true' xmlns:b='http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd' xmlns:cim='http://schemas.dmtf.org/wbem/wscim/1/common' xmlns:p='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/MSFT_WmiError' xsi:type='p:MSFT_WmiError_Type' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
|
20
|
+
<p:CIMStatusCode xsi:type='cim:cimUnsignedInt'>27</p:CIMStatusCode>
|
21
|
+
<p:CIMStatusCodeDescription xsi:nil='true' xsi:type='cim:cimString'/>
|
22
|
+
<p:ErrorSource xsi:nil='true' xsi:type='cim:cimString'/>
|
23
|
+
<p:ErrorSourceFormat xsi:type='cim:cimUnsignedShort'>0</p:ErrorSourceFormat>
|
24
|
+
<p:ErrorType xsi:type='cim:cimUnsignedShort'>0</p:ErrorType>
|
25
|
+
<p:Message xsi:type='cim:cimString'>The WS-Management service cannot process the request. This user is allowed a maximum number of 30 concurrent shells, which has been exceeded. Close existing shells or raise the quota for this user. </p:Message>
|
26
|
+
<p:MessageID xsi:type='cim:cimString'>HRESULT 0x803381a5</p:MessageID>
|
27
|
+
<p:OtherErrorSourceFormat xsi:nil='true' xsi:type='cim:cimString'/>
|
28
|
+
<p:OtherErrorType xsi:nil='true' xsi:type='cim:cimString'/>
|
29
|
+
<p:OwningEntity xsi:nil='true' xsi:type='cim:cimString'/>
|
30
|
+
<p:PerceivedSeverity xsi:type='cim:cimUnsignedShort'>0</p:PerceivedSeverity>
|
31
|
+
<p:ProbableCause xsi:type='cim:cimUnsignedShort'>0</p:ProbableCause>
|
32
|
+
<p:ProbableCauseDescription xsi:nil='true' xsi:type='cim:cimString'/>
|
33
|
+
<p:error_Category xsi:type='cim:cimUnsignedInt'>30</p:error_Category>
|
34
|
+
<p:error_Code xsi:type='cim:cimUnsignedInt'>2150859173</p:error_Code>
|
35
|
+
<p:error_Type xsi:type='cim:cimString'>HRESULT</p:error_Type>
|
36
|
+
<p:error_WindowsErrorMessage xsi:type='cim:cimString'>The WS-Management service cannot process the request. This user is allowed a maximum number of 30 concurrent shells, which has been exceeded. Close existing shells or raise the quota for this user. </p:error_WindowsErrorMessage>
|
37
|
+
</p:MSFT_WmiError>
|
38
|
+
</s:Detail>
|
39
|
+
</s:Fault>
|
40
|
+
</s:Body>
|
41
|
+
</s:Envelope>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
describe 'WinRM options', :unit => true do
|
2
|
+
let(:subject) { WinRM::WinRMWebService.new('http://localhost:55985/wsman', :plaintext) }
|
3
|
+
|
4
|
+
context 'when operations timeout is set to 60' do
|
5
|
+
before(:each) { subject.set_timeout(60) }
|
6
|
+
describe '#receive_timeout' do
|
7
|
+
it 'is set to 70s' do
|
8
|
+
transportclass = subject.instance_variable_get(:@xfer)
|
9
|
+
expect(transportclass.receive_timeout).to eql(70)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
describe '#op_timeout' do
|
13
|
+
it 'is set to 60s' do
|
14
|
+
expect(subject.timeout).to eql('PT60S')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when operations timeout is set to 30 and receive timeout is set to 120' do
|
20
|
+
before(:each) { subject.set_timeout(30, 120) }
|
21
|
+
describe '#receive_timeout' do
|
22
|
+
it 'is set to 120s' do
|
23
|
+
transportclass = subject.instance_variable_get(:@xfer)
|
24
|
+
expect(transportclass.receive_timeout).to eql(120)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
describe '#op_timeout' do
|
28
|
+
it 'is set to 30s' do
|
29
|
+
expect(subject.timeout).to eql('PT30S')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when max_env_size is set to 614400' do
|
35
|
+
before(:each) { subject.max_env_size(614400) }
|
36
|
+
describe '@max_env_sz' do
|
37
|
+
it 'is set to 614400' do
|
38
|
+
expect(subject.instance_variable_get('@max_env_sz')).to eq(614400)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when locale is set to en-CA' do
|
44
|
+
before(:each) { subject.locale('en-CA') }
|
45
|
+
describe '@locale' do
|
46
|
+
it 'is set to en-CA' do
|
47
|
+
expect(subject.instance_variable_get('@locale')).to eq('en-CA')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'default' do
|
53
|
+
describe '#receive_timeout' do
|
54
|
+
it 'should be 3600ms' do
|
55
|
+
transportclass = subject.instance_variable_get(:@xfer)
|
56
|
+
expect(transportclass.receive_timeout).to eql(3600)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
describe '#timeout' do
|
60
|
+
it 'should be 60s' do
|
61
|
+
expect(subject.timeout).to eql('PT60S')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
describe '@locale' do
|
65
|
+
it 'should be en-US' do
|
66
|
+
expect(subject.instance_variable_get('@locale')).to eq('en-US')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
describe '@max_env_sz' do
|
70
|
+
it 'should be 153600' do
|
71
|
+
expect(subject.instance_variable_get('@max_env_sz')).to eq(153600)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -49,22 +49,4 @@ describe "winrm client primitives" do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
describe "simple values", :unit => true do
|
53
|
-
it 'should set #op_timeout' do
|
54
|
-
expect(@winrm.op_timeout(120)).to eq('PT2M0S')
|
55
|
-
expect(@winrm.op_timeout(1202)).to eq('PT20M2S')
|
56
|
-
expect(@winrm.op_timeout(86400)).to eq('PT24H0S')
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'should set #max_env_size' do
|
60
|
-
@winrm.max_env_size(153600 * 4)
|
61
|
-
expect(@winrm.instance_variable_get('@max_env_sz')).to eq(614400)
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'should set #locale' do
|
65
|
-
@winrm.locale('en-ca')
|
66
|
-
expect(@winrm.instance_variable_get('@locale')).to eq('en-ca')
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
52
|
end
|
data/winrm.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.bindir = "bin"
|
27
27
|
s.executables = ['rwinrm']
|
28
28
|
s.required_ruby_version = '>= 1.9.0'
|
29
|
-
s.add_runtime_dependency 'gssapi', '~> 1.
|
29
|
+
s.add_runtime_dependency 'gssapi', '~> 1.2'
|
30
30
|
s.add_runtime_dependency 'httpclient', '~> 2.2', '>= 2.2.0.2'
|
31
31
|
s.add_runtime_dependency 'rubyntlm', '~> 0.4.0'
|
32
32
|
s.add_runtime_dependency 'uuidtools', '~> 2.1.2'
|
@@ -34,8 +34,6 @@ Gem::Specification.new do |s|
|
|
34
34
|
s.add_runtime_dependency 'nori', '~> 2.0'
|
35
35
|
s.add_runtime_dependency 'gyoku', '~> 1.0'
|
36
36
|
s.add_runtime_dependency 'builder', '>= 2.1.2'
|
37
|
-
s.add_runtime_dependency 'rubyzip', '~> 1.1'
|
38
|
-
s.add_runtime_dependency 'ruby-progressbar', '~> 1.6'
|
39
37
|
s.add_development_dependency 'rspec', '~> 3.0.0'
|
40
38
|
s.add_development_dependency 'rake', '~> 10.3.2'
|
41
39
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: winrm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.0.dev.
|
4
|
+
version: 1.3.0.dev.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Wanek
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-01-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: gssapi
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '1.
|
20
|
+
version: '1.2'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '1.
|
27
|
+
version: '1.2'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: httpclient
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,34 +135,6 @@ dependencies:
|
|
135
135
|
- - '>='
|
136
136
|
- !ruby/object:Gem::Version
|
137
137
|
version: 2.1.2
|
138
|
-
- !ruby/object:Gem::Dependency
|
139
|
-
name: rubyzip
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - ~>
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '1.1'
|
145
|
-
type: :runtime
|
146
|
-
prerelease: false
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
148
|
-
requirements:
|
149
|
-
- - ~>
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '1.1'
|
152
|
-
- !ruby/object:Gem::Dependency
|
153
|
-
name: ruby-progressbar
|
154
|
-
requirement: !ruby/object:Gem::Requirement
|
155
|
-
requirements:
|
156
|
-
- - ~>
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '1.6'
|
159
|
-
type: :runtime
|
160
|
-
prerelease: false
|
161
|
-
version_requirements: !ruby/object:Gem::Requirement
|
162
|
-
requirements:
|
163
|
-
- - ~>
|
164
|
-
- !ruby/object:Gem::Version
|
165
|
-
version: '1.6'
|
166
138
|
- !ruby/object:Gem::Dependency
|
167
139
|
name: rspec
|
168
140
|
requirement: !ruby/object:Gem::Requirement
|
@@ -211,16 +183,16 @@ files:
|
|
211
183
|
- README.md
|
212
184
|
- Rakefile
|
213
185
|
- VERSION
|
186
|
+
- Vagrantfile
|
214
187
|
- bin/rwinrm
|
215
188
|
- changelog.md
|
216
189
|
- lib/winrm.rb
|
217
190
|
- lib/winrm/exceptions/exceptions.rb
|
218
|
-
- lib/winrm/file_transfer.rb
|
219
|
-
- lib/winrm/file_transfer/remote_file.rb
|
220
|
-
- lib/winrm/file_transfer/remote_zip_file.rb
|
221
191
|
- lib/winrm/helpers/iso8601_duration.rb
|
192
|
+
- lib/winrm/helpers/powershell_script.rb
|
222
193
|
- lib/winrm/http/response_handler.rb
|
223
194
|
- lib/winrm/http/transport.rb
|
195
|
+
- lib/winrm/output.rb
|
224
196
|
- lib/winrm/soap_provider.rb
|
225
197
|
- lib/winrm/winrm_service.rb
|
226
198
|
- preamble
|
@@ -228,10 +200,8 @@ files:
|
|
228
200
|
- spec/cmd_spec.rb
|
229
201
|
- spec/config-example.yml
|
230
202
|
- spec/exception_spec.rb
|
231
|
-
- spec/file_transfer/remote_file_spec.rb
|
232
|
-
- spec/file_transfer/remote_zip_file_spec.rb
|
233
|
-
- spec/file_transfer_spec.rb
|
234
203
|
- spec/matchers.rb
|
204
|
+
- spec/output_spec.rb
|
235
205
|
- spec/powershell_spec.rb
|
236
206
|
- spec/response_handler_spec.rb
|
237
207
|
- spec/spec_helper.rb
|
@@ -239,6 +209,8 @@ files:
|
|
239
209
|
- spec/stubs/responses/open_shell_v2.xml
|
240
210
|
- spec/stubs/responses/soap_fault_v1.xml
|
241
211
|
- spec/stubs/responses/soap_fault_v2.xml
|
212
|
+
- spec/stubs/responses/wmi_error_v2.xml
|
213
|
+
- spec/winrm_options_spec.rb
|
242
214
|
- spec/winrm_primitives_spec.rb
|
243
215
|
- spec/wql_spec.rb
|
244
216
|
- winrm.gemspec
|
@@ -1,184 +0,0 @@
|
|
1
|
-
require 'io/console'
|
2
|
-
require 'json'
|
3
|
-
require 'ruby-progressbar'
|
4
|
-
|
5
|
-
module WinRM
|
6
|
-
class RemoteFile
|
7
|
-
|
8
|
-
attr_reader :local_path
|
9
|
-
attr_reader :remote_path
|
10
|
-
attr_reader :closed
|
11
|
-
attr_reader :options
|
12
|
-
|
13
|
-
def initialize(service, local_path, remote_path, opts = {})
|
14
|
-
@logger = Logging.logger[self]
|
15
|
-
@options = opts
|
16
|
-
@closed = false
|
17
|
-
@service = service
|
18
|
-
@shell = service.open_shell
|
19
|
-
@local_path = local_path
|
20
|
-
@remote_path = full_remote_path(local_path, remote_path)
|
21
|
-
@logger.debug("Creating RemoteFile of local '#{local_path}' at '#{@remote_path}'")
|
22
|
-
ensure
|
23
|
-
if !shell.nil?
|
24
|
-
ObjectSpace.define_finalizer( self, self.class.close(shell, service) )
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def upload
|
29
|
-
raise WinRMUploadFailed.new("This RemoteFile is closed.") if closed
|
30
|
-
raise WinRMUploadFailed.new("Cannot find path: '#{local_path}'") unless File.exist?(local_path)
|
31
|
-
|
32
|
-
@remote_path, should_upload = powershell_batch do | builder |
|
33
|
-
builder << resolve_remote_command
|
34
|
-
builder << is_dirty_command
|
35
|
-
end
|
36
|
-
|
37
|
-
if should_upload
|
38
|
-
size = upload_to_remote
|
39
|
-
powershell_batch {|builder| builder << create_post_upload_command}
|
40
|
-
else
|
41
|
-
size = 0
|
42
|
-
logger.debug("Files are equal. Not copying #{local_path} to #{remote_path}")
|
43
|
-
end
|
44
|
-
size
|
45
|
-
end
|
46
|
-
|
47
|
-
def close
|
48
|
-
service.close_shell(shell) unless shell.nil? or closed
|
49
|
-
@closed = true
|
50
|
-
end
|
51
|
-
|
52
|
-
protected
|
53
|
-
|
54
|
-
attr_reader :logger
|
55
|
-
attr_reader :service
|
56
|
-
attr_reader :shell
|
57
|
-
|
58
|
-
def self.close(shell_id, service)
|
59
|
-
proc { service.close_shell(shell_id) }
|
60
|
-
end
|
61
|
-
|
62
|
-
def full_remote_path(local_path, remote_path)
|
63
|
-
base_file_name = File.basename(local_path)
|
64
|
-
if File.basename(remote_path) != base_file_name
|
65
|
-
remote_path = File.join(remote_path, base_file_name)
|
66
|
-
end
|
67
|
-
remote_path
|
68
|
-
end
|
69
|
-
|
70
|
-
def resolve_remote_command
|
71
|
-
<<-EOH
|
72
|
-
$dest_file_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("#{remote_path}")
|
73
|
-
|
74
|
-
if (!(Test-Path $dest_file_path)) {
|
75
|
-
$dest_dir = ([System.IO.Path]::GetDirectoryName($dest_file_path))
|
76
|
-
New-Item -ItemType directory -Force -Path $dest_dir | Out-Null
|
77
|
-
}
|
78
|
-
|
79
|
-
$dest_file_path
|
80
|
-
EOH
|
81
|
-
end
|
82
|
-
|
83
|
-
def is_dirty_command
|
84
|
-
local_md5 = Digest::MD5.file(local_path).hexdigest
|
85
|
-
<<-EOH
|
86
|
-
$dest_file_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("#{remote_path}")
|
87
|
-
|
88
|
-
if (Test-Path $dest_file_path) {
|
89
|
-
$crypto_prov = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
|
90
|
-
try {
|
91
|
-
$file = [System.IO.File]::Open($dest_file_path,
|
92
|
-
[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
|
93
|
-
$guest_md5 = ([System.BitConverter]::ToString($crypto_prov.ComputeHash($file)))
|
94
|
-
$guest_md5 = $guest_md5.Replace("-","").ToLower()
|
95
|
-
}
|
96
|
-
finally {
|
97
|
-
$file.Dispose()
|
98
|
-
}
|
99
|
-
if ($guest_md5 -eq '#{local_md5}') {
|
100
|
-
return $false
|
101
|
-
}
|
102
|
-
}
|
103
|
-
if(Test-Path $dest_file_path){remove-item $dest_file_path -Force}
|
104
|
-
return $true
|
105
|
-
EOH
|
106
|
-
end
|
107
|
-
|
108
|
-
def upload_to_remote
|
109
|
-
logger.debug("Uploading '#{local_path}' to temp file '#{remote_path}'")
|
110
|
-
base64_host_file = Base64.encode64(IO.binread(local_path)).gsub("\n", "")
|
111
|
-
base64_array = base64_host_file.chars.to_a
|
112
|
-
unless options[:quiet]
|
113
|
-
console_width = IO.console.winsize[1]
|
114
|
-
bar = ProgressBar.create(:title => "Copying #{File.basename(local_path)}...", :total => base64_array.count, :length => console_width-1)
|
115
|
-
end
|
116
|
-
base64_array.each_slice(8000 - remote_path.size) do |chunk|
|
117
|
-
cmd("echo #{chunk.join} >> \"#{remote_path}\"")
|
118
|
-
bar.progress += chunk.count unless options[:quiet]
|
119
|
-
end
|
120
|
-
base64_array.length
|
121
|
-
end
|
122
|
-
|
123
|
-
def decode_command
|
124
|
-
<<-EOH
|
125
|
-
$base64_string = Get-Content '#{remote_path}'
|
126
|
-
$bytes = [System.Convert]::FromBase64String($base64_string)
|
127
|
-
[System.IO.File]::WriteAllBytes('#{remote_path}', $bytes) | Out-Null
|
128
|
-
EOH
|
129
|
-
end
|
130
|
-
|
131
|
-
def create_post_upload_command
|
132
|
-
[decode_command]
|
133
|
-
end
|
134
|
-
|
135
|
-
def powershell_batch(&block)
|
136
|
-
ps_builder = []
|
137
|
-
yield ps_builder
|
138
|
-
|
139
|
-
commands = [ "$result = @{}" ]
|
140
|
-
idx = 0
|
141
|
-
ps_builder.flatten.each do |cmd_item|
|
142
|
-
commands << <<-EOH
|
143
|
-
$result.ret#{idx} = Invoke-Command { #{cmd_item} }
|
144
|
-
EOH
|
145
|
-
idx += 1
|
146
|
-
end
|
147
|
-
commands << "$(ConvertTo-Json $result)"
|
148
|
-
|
149
|
-
result = []
|
150
|
-
JSON.parse(powershell(commands.join("\n"))).each do |k,v|
|
151
|
-
result << v unless v.nil?
|
152
|
-
end
|
153
|
-
result unless result.empty?
|
154
|
-
end
|
155
|
-
|
156
|
-
def powershell(script)
|
157
|
-
script = "$ProgressPreference='SilentlyContinue';" + script
|
158
|
-
logger.debug("executing powershell script: \n#{script}")
|
159
|
-
script = script.encode('UTF-16LE', 'UTF-8')
|
160
|
-
script = Base64.strict_encode64(script)
|
161
|
-
cmd("powershell", ['-encodedCommand', script])
|
162
|
-
end
|
163
|
-
|
164
|
-
def cmd(command, arguments = [])
|
165
|
-
command_output = nil
|
166
|
-
out_stream = []
|
167
|
-
err_stream = []
|
168
|
-
service.run_command(shell, command, arguments) do |command_id|
|
169
|
-
command_output = service.get_command_output(shell, command_id) do |stdout, stderr|
|
170
|
-
out_stream << stdout if stdout
|
171
|
-
err_stream << stderr if stderr
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
if !command_output[:exitcode].zero? or !err_stream.empty?
|
176
|
-
raise WinRMUploadFailed,
|
177
|
-
:from => local_path,
|
178
|
-
:to => remote_path,
|
179
|
-
:message => command_output.inspect
|
180
|
-
end
|
181
|
-
out_stream.join.chomp
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'zip'
|
2
|
-
|
3
|
-
module WinRM
|
4
|
-
class RemoteZipFile < RemoteFile
|
5
|
-
|
6
|
-
attr_reader :archive
|
7
|
-
|
8
|
-
def initialize(service, remote_path, opts = {})
|
9
|
-
@logger = Logging.logger[self]
|
10
|
-
@archive = create_archive(remote_path)
|
11
|
-
@unzip_remote_path = remote_path
|
12
|
-
remote_path = "$env:temp/WinRM_file_transfer"
|
13
|
-
super(service, @archive, remote_path, opts)
|
14
|
-
end
|
15
|
-
|
16
|
-
def add_file(path)
|
17
|
-
path = path.gsub("\\","/")
|
18
|
-
logger.debug("adding '#{path}' to zip file")
|
19
|
-
raise WinRMUploadFailed.new("Cannot find path: '#{path}'") unless File.exist?(path)
|
20
|
-
File.directory?(path) ? glob = File.join(path, "**/*") : glob = path
|
21
|
-
logger.debug("iterating files in '#{glob}'")
|
22
|
-
Zip::File.open(archive, 'w') do |zipfile|
|
23
|
-
Dir.glob(glob).each do |file|
|
24
|
-
logger.debug("adding zip entry for '#{file}'")
|
25
|
-
entry = Zip::Entry.new(archive, file.sub(File.dirname(path)+'/',''), nil, nil, nil, nil, nil, nil, ::Zip::DOSTime.new(2000))
|
26
|
-
zipfile.add(entry,file)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
protected
|
32
|
-
|
33
|
-
def create_post_upload_command
|
34
|
-
super << extract_zip_command
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def create_archive(remote_path)
|
40
|
-
archive_folder = File.join(ENV['TMP'] || ENV['TMPDIR'] || '/tmp', 'WinRM_file_transfer_local')
|
41
|
-
Dir.mkdir(archive_folder) unless File.exist?(archive_folder)
|
42
|
-
archive = File.join(archive_folder,File.basename(remote_path))+'.zip'
|
43
|
-
FileUtils.rm archive, :force=>true
|
44
|
-
|
45
|
-
archive
|
46
|
-
end
|
47
|
-
|
48
|
-
def extract_zip_command
|
49
|
-
<<-EOH
|
50
|
-
$destination = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("#{@unzip_remote_path}")
|
51
|
-
$shellApplication = new-object -com shell.application
|
52
|
-
|
53
|
-
$zipPackage = $shellApplication.NameSpace('#{remote_path}')
|
54
|
-
mkdir $destination -ErrorAction SilentlyContinue | Out-Null
|
55
|
-
$destinationFolder = $shellApplication.NameSpace($destination)
|
56
|
-
$destinationFolder.CopyHere($zipPackage.Items(),0x10) | Out-Null
|
57
|
-
EOH
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
data/lib/winrm/file_transfer.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'winrm/file_transfer/remote_file'
|
2
|
-
require 'winrm/file_transfer/remote_zip_file'
|
3
|
-
|
4
|
-
module WinRM
|
5
|
-
# Perform file transfer operations between a local machine and winrm endpoint
|
6
|
-
class FileTransfer
|
7
|
-
# Upload one or more local files and directories to a remote directory
|
8
|
-
# @example copy a single directory to a winrm endpoint
|
9
|
-
#
|
10
|
-
# WinRM::FileTransfer.upload(client, 'c:/dev/my_dir', '$env:AppData')
|
11
|
-
#
|
12
|
-
# @example copy several paths to the winrm endpoint
|
13
|
-
#
|
14
|
-
# WinRM::FileTransfer.upload(client, ['c:/dev/file1.txt','c:/dev/dir1'], '$env:AppData')
|
15
|
-
#
|
16
|
-
# @param [WinRM::WinRMService] a winrm service client connected to the endpoint where the remote path resides
|
17
|
-
# @param [Array<String>] One or more paths that will be copied to the remote path. These can be files or directories to be deeply copied
|
18
|
-
# @param [String] The directory on the remote endpoint to copy the local items to. This path may contain powershell style environment variables
|
19
|
-
# @option opts [String] options to be used for the copy. Currently only :quiet is supported to suppress the progress bar
|
20
|
-
# @return [Fixnum] The total number of bytes copied
|
21
|
-
def self.upload(service, local_path, remote_path, opts = {})
|
22
|
-
file = nil
|
23
|
-
local_path = [local_path] if local_path.is_a? String
|
24
|
-
|
25
|
-
if local_path.count == 1 && !File.directory?(local_path[0])
|
26
|
-
file = RemoteFile.new(service, local_path[0], remote_path, opts)
|
27
|
-
else
|
28
|
-
file = RemoteZipFile.new(service, remote_path, opts)
|
29
|
-
local_path.each do |path|
|
30
|
-
file.add_file(path)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
file.upload
|
35
|
-
ensure
|
36
|
-
file.close unless file.nil?
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
describe WinRM::RemoteFile, :integration => true do
|
2
|
-
|
3
|
-
let(:this_file) { __FILE__ }
|
4
|
-
let(:service) { winrm_connection }
|
5
|
-
let(:destination) {"#{ENV['temp']}/WinRM_tests"}
|
6
|
-
after {
|
7
|
-
subject.close
|
8
|
-
FileUtils.rm_rf(destination)
|
9
|
-
}
|
10
|
-
|
11
|
-
context 'Upload a new file to directory path' do
|
12
|
-
subject {WinRM::RemoteFile.new(service, this_file, destination, :quiet => true)}
|
13
|
-
|
14
|
-
it 'copies the file inside the directory' do
|
15
|
-
expect(subject.upload).to be > 0
|
16
|
-
expect(File.exist?(File.join(destination, File.basename(this_file)))).to be_truthy
|
17
|
-
end
|
18
|
-
it 'copies the exact file content' do
|
19
|
-
expect(subject.upload).to be > 0
|
20
|
-
expect(File.read(File.join(destination, File.basename(this_file)))).to eq(File.read(this_file))
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'Upload an identical file to directory path' do
|
26
|
-
subject {WinRM::RemoteFile.new(service, this_file, destination, :quiet => true)}
|
27
|
-
let (:next_transfer) {WinRM::RemoteFile.new(service, this_file, destination, :quiet => true)}
|
28
|
-
|
29
|
-
it 'does not copy the file' do
|
30
|
-
expect(subject.upload).to be > 0
|
31
|
-
expect(next_transfer.upload).to be == 0
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'Upload a file to file path' do
|
36
|
-
subject {WinRM::RemoteFile.new(service, this_file, File.join(destination, File.basename(this_file)), :quiet => true)}
|
37
|
-
|
38
|
-
it 'copies the file to the exact path' do
|
39
|
-
expect(subject.upload).to be > 0
|
40
|
-
expect(File.exist?(File.join(destination, File.basename(this_file)))).to be_truthy
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'Upload a new file to nested directory' do
|
45
|
-
let (:nested) {File.join(destination, 'nested')}
|
46
|
-
subject {WinRM::RemoteFile.new(service, this_file, nested, :quiet => true)}
|
47
|
-
|
48
|
-
it 'copies the file to the nested path' do
|
49
|
-
expect(subject.upload).to be > 0
|
50
|
-
expect(File.exist?(File.join(nested, File.basename(this_file)))).to be_truthy
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'Upload a file after RemoteFile is closed' do
|
55
|
-
subject {WinRM::RemoteFile.new(service, this_file, destination, :quiet => true)}
|
56
|
-
|
57
|
-
it 'raises WinRMUploadFailed' do
|
58
|
-
expect(subject.upload).to be > 0
|
59
|
-
subject.close
|
60
|
-
expect{subject.upload}.to raise_error(WinRM::WinRMUploadFailed)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
context 'Upload a bad path' do
|
65
|
-
subject {WinRM::RemoteFile.new(service, 'c:/some/bad/path', destination, :quiet => true)}
|
66
|
-
|
67
|
-
it 'raises WinRMUploadFailed' do
|
68
|
-
expect { subject.upload }.to raise_error(WinRM::WinRMUploadFailed)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|