winrm-fs 0.4.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -2
- data/Gemfile +2 -0
- data/README.md +5 -12
- data/VERSION +1 -1
- data/Vagrantfile +1 -1
- data/appveyor.yml +2 -2
- data/changelog.md +2 -0
- data/lib/winrm-fs/core/file_transporter.rb +80 -93
- data/lib/winrm-fs/file_manager.rb +13 -13
- data/lib/winrm-fs/scripts/check_files.ps1.erb +17 -28
- data/lib/winrm-fs/scripts/checksum.ps1.erb +1 -1
- data/lib/winrm-fs/scripts/extract_files.ps1.erb +52 -0
- data/spec/config-example.yml +3 -5
- data/spec/integration/file_manager_spec.rb +10 -4
- data/spec/spec_helper.rb +5 -6
- data/winrm-fs.gemspec +1 -1
- metadata +7 -9
- data/lib/winrm-fs/scripts/decode_files.ps1.erb +0 -58
- data/spec/unit/file_transporter_spec.rb +0 -839
@@ -1,20 +1,11 @@
|
|
1
|
-
$
|
2
|
-
$hash_file = "<%= hash_file %>"
|
1
|
+
$hash_file = <%= hash_file %>
|
3
2
|
|
4
|
-
Function Cleanup($
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
$in = (Get-Item $src).OpenRead()
|
9
|
-
$b64 = New-Object -TypeName System.Security.Cryptography.FromBase64Transform
|
10
|
-
$m = [System.Security.Cryptography.CryptoStreamMode]::Read
|
11
|
-
$d = New-Object -TypeName System.Security.Cryptography.CryptoStream $in,$b64,$m
|
12
|
-
Copy-Stream $d ($out = [System.IO.File]::OpenWrite($dst))
|
13
|
-
} Finally { Cleanup $in; Cleanup $out; Cleanup $d }
|
3
|
+
Function Cleanup($disposable) {
|
4
|
+
if (($disposable -ne $null) -and ($disposable.GetType().GetMethod("Dispose") -ne $null)) {
|
5
|
+
$disposable.Dispose()
|
6
|
+
}
|
14
7
|
}
|
15
8
|
|
16
|
-
Function Copy-Stream($src, $dst) { $b = New-Object Byte[] 4096; while (($i = $src.Read($b, 0, $b.Length)) -ne 0) { $dst.Write($b, 0, $i) } }
|
17
|
-
|
18
9
|
Function Check-Files($h) {
|
19
10
|
return $h.GetEnumerator() | ForEach-Object {
|
20
11
|
$dst = Unresolve-Path $_.Value.target
|
@@ -36,25 +27,23 @@ Function Check-Files($h) {
|
|
36
27
|
|
37
28
|
Function Get-MD5Sum($src) {
|
38
29
|
Try {
|
39
|
-
$c =
|
30
|
+
$c = [System.Security.Cryptography.MD5]::Create()
|
40
31
|
$bytes = $c.ComputeHash(($in = (Get-Item $src).OpenRead()))
|
41
32
|
return ([System.BitConverter]::ToString($bytes)).Replace("-", "").ToLower()
|
42
|
-
}
|
33
|
+
}
|
34
|
+
Finally {
|
35
|
+
Cleanup $c
|
36
|
+
Cleanup $in
|
37
|
+
}
|
43
38
|
}
|
44
39
|
|
45
|
-
Function
|
46
|
-
$
|
47
|
-
|
48
|
-
$expr = Get-Content $decoded | Out-String
|
49
|
-
Remove-Item $in,$decoded -Force
|
50
|
-
try {
|
51
|
-
return Invoke-Expression "$expr"
|
40
|
+
Function Unresolve-Path($path) {
|
41
|
+
if ($path -eq $null) {
|
42
|
+
return $null
|
52
43
|
}
|
53
|
-
|
54
|
-
|
44
|
+
else {
|
45
|
+
return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
|
55
46
|
}
|
56
47
|
}
|
57
48
|
|
58
|
-
|
59
|
-
|
60
|
-
Check-Files (Invoke-Input $hash_file) | ConvertTo-Csv -NoTypeInformation
|
49
|
+
Check-Files $hash_file | ConvertTo-Csv -NoTypeInformation
|
@@ -2,7 +2,7 @@ $p = $ExecutionContext.SessionState.Path
|
|
2
2
|
$path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
|
3
3
|
|
4
4
|
if (Test-Path $path -PathType Leaf) {
|
5
|
-
$cryptoProv =
|
5
|
+
$cryptoProv = [System.Security.Cryptography.MD5]::Create()
|
6
6
|
$file = [System.IO.File]::Open($path,
|
7
7
|
[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
|
8
8
|
$md5 = ([System.BitConverter]::ToString($cryptoProv.ComputeHash($file)))
|
@@ -0,0 +1,52 @@
|
|
1
|
+
trap {
|
2
|
+
$e = $_.Exception
|
3
|
+
$e.InvocationInfo.ScriptName
|
4
|
+
do {
|
5
|
+
$e.Message
|
6
|
+
$e = $e.InnerException
|
7
|
+
} while ($e)
|
8
|
+
break
|
9
|
+
}
|
10
|
+
|
11
|
+
function folder($path){
|
12
|
+
$path | ? {-not (test-path $_)} | % {$null = mkdir $_}
|
13
|
+
}
|
14
|
+
|
15
|
+
Function Decode-Files($hash) {
|
16
|
+
foreach ($key in $hash.keys) {
|
17
|
+
$value = $hash[$key]
|
18
|
+
$tzip, $dst = $Value["tmpzip"], $Value["dst"]
|
19
|
+
if ($tzip) {Unzip-File $tzip $dst}
|
20
|
+
New-Object psobject -Property @{dst=$dst;src_md5=$key;tmpzip=$tzip}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
Function Unzip-File($src, $dst) {
|
25
|
+
$unpack = $src -replace '\.zip'
|
26
|
+
$dst_parent = Split-Path -Path $dst -Parent
|
27
|
+
if(!(Test-Path $dst_parent)) { $dst = $dst_parent }
|
28
|
+
folder $unpack, $dst
|
29
|
+
try {
|
30
|
+
try{
|
31
|
+
[IO.Compression.ZipFile]::ExtractToDirectory($src, $unpack)
|
32
|
+
}
|
33
|
+
catch {
|
34
|
+
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
|
35
|
+
[IO.Compression.ZipFile]::ExtractToDirectory($src, $unpack)
|
36
|
+
}
|
37
|
+
}
|
38
|
+
catch {
|
39
|
+
Try {
|
40
|
+
$s = New-Object -ComObject Shell.Application
|
41
|
+
($s.NameSpace($unpack)).CopyHere(($s.NameSpace($src)).Items(), 0x610)
|
42
|
+
}
|
43
|
+
Finally {
|
44
|
+
[void][Runtime.Interopservices.Marshal]::ReleaseComObject($s)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
dir $unpack | cp -dest "$dst/" -force -recurse
|
48
|
+
rm $unpack -recurse -force
|
49
|
+
}
|
50
|
+
|
51
|
+
$hash_file = <%= hash_file %>
|
52
|
+
Decode-Files $hash_file | ConvertTo-Csv -NoTypeInformation
|
data/spec/config-example.yml
CHANGED
@@ -5,7 +5,7 @@ describe WinRM::FS::FileManager do
|
|
5
5
|
let(:dest_dir) { File.join(subject.temp_dir, "winrm_#{rand(2**16)}") }
|
6
6
|
let(:temp_upload_dir) { '$env:TEMP/winrm-upload' }
|
7
7
|
let(:spec_dir) { File.expand_path(File.dirname(File.dirname(__FILE__))) }
|
8
|
-
let(:this_file) { __FILE__ }
|
8
|
+
let(:this_file) { Pathname.new(__FILE__) }
|
9
9
|
let(:service) { winrm_connection }
|
10
10
|
|
11
11
|
subject { WinRM::FS::FileManager.new(service) }
|
@@ -40,7 +40,7 @@ describe WinRM::FS::FileManager do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
context 'upload file' do
|
43
|
-
let(:dest_file) { File.join(dest_dir, File.basename(this_file)) }
|
43
|
+
let(:dest_file) { Pathname.new(File.join(dest_dir, File.basename(this_file))) }
|
44
44
|
|
45
45
|
before(:each) do
|
46
46
|
expect(subject.delete(dest_dir)).to be true
|
@@ -57,6 +57,12 @@ describe WinRM::FS::FileManager do
|
|
57
57
|
subject.delete('c:/winrmtest.rb')
|
58
58
|
end
|
59
59
|
|
60
|
+
it 'should upload just filename' do
|
61
|
+
subject.upload(this_file, 'winrmtest.rb')
|
62
|
+
expect(subject).to have_created('winrmtest.rb').with_content(this_file)
|
63
|
+
subject.delete('winrmtest.rb')
|
64
|
+
end
|
65
|
+
|
60
66
|
it 'should upload using relative file path' do
|
61
67
|
subject.upload('./spec/integration/file_manager_spec.rb', dest_file)
|
62
68
|
expect(subject).to have_created(dest_file).with_content(this_file)
|
@@ -108,8 +114,8 @@ describe WinRM::FS::FileManager do
|
|
108
114
|
|bytes_copied, total_bytes, local_path, remote_path|
|
109
115
|
expect(total_bytes).to be > 0
|
110
116
|
total_bytes_copied = bytes_copied
|
111
|
-
expect(local_path).to eq(this_file)
|
112
|
-
expect(remote_path).to eq(dest_file)
|
117
|
+
expect(local_path).to eq(this_file.to_s)
|
118
|
+
expect(remote_path).to eq(dest_file.to_s)
|
113
119
|
block_called = true
|
114
120
|
end
|
115
121
|
expect(total_bytes_copied).to eq(total)
|
data/spec/spec_helper.rb
CHANGED
@@ -9,15 +9,14 @@ require_relative 'matchers'
|
|
9
9
|
module ConnectionHelper
|
10
10
|
# rubocop:disable AbcSize
|
11
11
|
def winrm_connection
|
12
|
-
WinRM::
|
13
|
-
config[:endpoint], config[:auth_type].to_sym, config[:options])
|
12
|
+
WinRM::Connection.new(config)
|
14
13
|
end
|
15
14
|
# rubocop:enable AbcSize
|
16
15
|
|
17
16
|
def config
|
18
17
|
@config ||= begin
|
19
18
|
cfg = symbolize_keys(YAML.load(File.read(winrm_config_path)))
|
20
|
-
cfg
|
19
|
+
cfg.merge!(basic_auth_only: true) unless cfg[:transport].eql? :kerberos
|
21
20
|
merge_environment!(cfg)
|
22
21
|
cfg
|
23
22
|
end
|
@@ -25,18 +24,18 @@ module ConnectionHelper
|
|
25
24
|
|
26
25
|
def merge_environment!(config)
|
27
26
|
merge_config_option_from_environment(config, 'user')
|
28
|
-
merge_config_option_from_environment(config, '
|
27
|
+
merge_config_option_from_environment(config, 'password')
|
29
28
|
merge_config_option_from_environment(config, 'no_ssl_peer_verification')
|
30
29
|
if ENV['use_ssl_peer_fingerprint']
|
31
30
|
config[:options][:ssl_peer_fingerprint] = ENV['winrm_cert']
|
32
31
|
end
|
33
32
|
config[:endpoint] = ENV['winrm_endpoint'] if ENV['winrm_endpoint']
|
34
|
-
config[:
|
33
|
+
config[:transport] = ENV['winrm_transport'] if ENV['winrm_transport']
|
35
34
|
end
|
36
35
|
|
37
36
|
def merge_config_option_from_environment(config, key)
|
38
37
|
env_key = 'winrm_' + key
|
39
|
-
config[
|
38
|
+
config[key.to_sym] = ENV[env_key] if ENV[env_key]
|
40
39
|
end
|
41
40
|
|
42
41
|
def winrm_config_path
|
data/winrm-fs.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_runtime_dependency 'erubis', '~> 2.7'
|
30
30
|
s.add_runtime_dependency 'logging', ['>= 1.6.1', '< 3.0']
|
31
31
|
s.add_runtime_dependency 'rubyzip', '~> 1.1'
|
32
|
-
s.add_runtime_dependency 'winrm', '~>
|
32
|
+
s.add_runtime_dependency 'winrm', '~> 2.0'
|
33
33
|
s.add_development_dependency 'pry'
|
34
34
|
s.add_development_dependency 'rspec', '~> 3.0.0'
|
35
35
|
s.add_development_dependency 'rake', '~> 10.3.2'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: winrm-fs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shawn Neal
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-08-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: erubis
|
@@ -65,14 +65,14 @@ dependencies:
|
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '2.0'
|
69
69
|
type: :runtime
|
70
70
|
prerelease: false
|
71
71
|
version_requirements: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '2.0'
|
76
76
|
- !ruby/object:Gem::Dependency
|
77
77
|
name: pry
|
78
78
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,8 +129,7 @@ dependencies:
|
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: 0.28.0
|
132
|
-
description:
|
133
|
-
Ruby library for file system operations via Windows Remote Management
|
132
|
+
description: " Ruby library for file system operations via Windows Remote Management\n"
|
134
133
|
email:
|
135
134
|
- sneal@sneal.net
|
136
135
|
- matt@mattwrock.com
|
@@ -162,17 +161,16 @@ files:
|
|
162
161
|
- lib/winrm-fs/scripts/check_files.ps1.erb
|
163
162
|
- lib/winrm-fs/scripts/checksum.ps1.erb
|
164
163
|
- lib/winrm-fs/scripts/create_dir.ps1.erb
|
165
|
-
- lib/winrm-fs/scripts/decode_files.ps1.erb
|
166
164
|
- lib/winrm-fs/scripts/delete.ps1.erb
|
167
165
|
- lib/winrm-fs/scripts/download.ps1.erb
|
168
166
|
- lib/winrm-fs/scripts/exists.ps1.erb
|
167
|
+
- lib/winrm-fs/scripts/extract_files.ps1.erb
|
169
168
|
- lib/winrm-fs/scripts/scripts.rb
|
170
169
|
- spec/config-example.yml
|
171
170
|
- spec/integration/file_manager_spec.rb
|
172
171
|
- spec/integration/tmp_zip_spec.rb
|
173
172
|
- spec/matchers.rb
|
174
173
|
- spec/spec_helper.rb
|
175
|
-
- spec/unit/file_transporter_spec.rb
|
176
174
|
- spec/unit/tmp_zip_spec.rb
|
177
175
|
- winrm-fs.gemspec
|
178
176
|
homepage: http://github.com/WinRb/winrm-fs
|
@@ -198,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
196
|
version: '0'
|
199
197
|
requirements: []
|
200
198
|
rubyforge_project:
|
201
|
-
rubygems_version: 2.6.
|
199
|
+
rubygems_version: 2.6.6
|
202
200
|
signing_key:
|
203
201
|
specification_version: 4
|
204
202
|
summary: WinRM File System
|
@@ -1,58 +0,0 @@
|
|
1
|
-
trap {$e = $_.Exception; $e.InvocationInfo.ScriptName; do {$e.Message; $e = $e.InnerException} while ($e); break;}
|
2
|
-
function Decode-Base64File($src, $dst) {folder (split-path $dst);sc -force -Encoding Byte -Path $dst -Value ([Convert]::FromBase64String([IO.File]::ReadAllLines($src)))}
|
3
|
-
function Copy-Stream($src, $dst) {$b = New-Object Byte[] 4096; while (($i = $src.Read($b, 0, $b.Length)) -ne 0) { $dst.Write($b, 0, $i) } }
|
4
|
-
function Resolve-ProviderPath{ $input | % {if ($_){(Resolve-Path $_).ProviderPath} else{$null}} }
|
5
|
-
function Get-FrameworkVersion { "Full", "Client" | % {([version](gp "HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\$_").Version)} | select -first 1}
|
6
|
-
function Test-NETStack($Version){ Get-FrameworkVersion -ge $Version }
|
7
|
-
function Test-IOCompression {($PSVersionTable.PSVersion.Major -ge 3) -and (Test-NETStack '4.5')}
|
8
|
-
function folder($path){ $path | ? {-not (test-path $_)} | % {$null = mkdir $_}}
|
9
|
-
function disposable($o){($o -is [IDisposable]) -and (($o | gm | %{$_.name}) -contains 'Dispose')}
|
10
|
-
function use($obj, [scriptblock]$sb){try {& $sb} catch [exception]{throw $_} finally {if (disposable $obj) {$obj.Dispose()}} }
|
11
|
-
set-alias RPP -Value Resolve-ProviderPath
|
12
|
-
|
13
|
-
Function Decode-Files($hash) {
|
14
|
-
foreach ($key in $hash.keys) {
|
15
|
-
$value = $hash[$key]
|
16
|
-
$tmp, $tzip, $dst = $Key, $Value["tmpzip"], $Value["dst"]
|
17
|
-
if(!$tmp.StartsWith("clean")) {
|
18
|
-
$sMd5 = (gi $tmp).BaseName.Replace("b64-", "")
|
19
|
-
$decoded = if ($tzip -ne $null) { $tzip } else { $dst }
|
20
|
-
Decode-Base64File $tmp $decoded
|
21
|
-
rm $tmp -Force
|
22
|
-
$dMd5 = Get-MD5Sum $decoded
|
23
|
-
$verifies = $sMd5 -like $dMd5
|
24
|
-
}
|
25
|
-
if ($tzip) {Unzip-File $tzip $dst}
|
26
|
-
New-Object psobject -Property @{dst=$dst;verifies=$verifies;src_md5=$sMd5;dst_md5=$dMd5;tmpfile=$tmp;tmpzip=$tzip}
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
Function Get-MD5Sum($src) {
|
31
|
-
if ($src -and (test-path $src)) {
|
32
|
-
use ($c = New-Object -TypeName Security.Cryptography.MD5CryptoServiceProvider) {
|
33
|
-
use ($in = (gi $src).OpenRead()) {([BitConverter]::ToString($c.ComputeHash($in))).Replace("-", "").ToLower()}}
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
Function Invoke-Input($in) {
|
38
|
-
$in = $in | rpp
|
39
|
-
$d = "$($in).ps1"
|
40
|
-
Decode-Base64File $in $d
|
41
|
-
$expr = gc $d | Out-String
|
42
|
-
rm $in,$d -Force
|
43
|
-
iex "$expr"
|
44
|
-
}
|
45
|
-
|
46
|
-
Function Unzip-File($src, $dst) {
|
47
|
-
$unpack = $src -replace '\.zip'
|
48
|
-
$dst_parent = Split-Path -Path $dst -Parent
|
49
|
-
if(!(Test-Path $dst_parent)) { $dst = $dst_parent }
|
50
|
-
folder $unpack, $dst
|
51
|
-
if (Test-IOCompression) {Add-Type -AN System.IO.Compression.FileSystem; [IO.Compression.ZipFile]::ExtractToDirectory($src, $unpack)}
|
52
|
-
else {Try {$s = New-Object -ComObject Shell.Application; ($s.NameSpace($unpack)).CopyHere(($s.NameSpace($src)).Items(), 0x610)} Finally {[void][Runtime.Interopservices.Marshal]::ReleaseComObject($s)}}
|
53
|
-
dir $unpack | cp -dest "$dst/" -force -recurse
|
54
|
-
rm $unpack -recurse -force
|
55
|
-
}
|
56
|
-
|
57
|
-
$hash_file = "<%= hash_file %>"
|
58
|
-
Decode-Files (Invoke-Input $hash_file) | ConvertTo-Csv -NoTypeInformation
|
@@ -1,839 +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 'base64'
|
20
|
-
require 'csv'
|
21
|
-
require 'stringio'
|
22
|
-
require 'logger'
|
23
|
-
require 'winrm'
|
24
|
-
|
25
|
-
require 'winrm-fs/core/file_transporter'
|
26
|
-
|
27
|
-
describe WinRM::FS::Core::FileTransporter do
|
28
|
-
CheckEntry = Struct.new(
|
29
|
-
:chk_exists, :src_md5, :dst_md5, :chk_dirty, :verifies)
|
30
|
-
DecodeEntry = Struct.new(
|
31
|
-
:dst, :verifies, :src_md5, :dst_md5, :tmpfile, :tmpzip)
|
32
|
-
|
33
|
-
let(:logged_output) { StringIO.new }
|
34
|
-
let(:logger) { Logger.new(logged_output) }
|
35
|
-
|
36
|
-
let(:randomness) { %w(alpha beta charlie delta).each }
|
37
|
-
let(:id_generator) { -> { randomness.next } }
|
38
|
-
let(:winrm_service) { double('winrm_service', logger: logger) }
|
39
|
-
let(:service) { double('command_executor', service: winrm_service) }
|
40
|
-
let(:transporter) do
|
41
|
-
WinRM::FS::Core::FileTransporter.new(
|
42
|
-
service,
|
43
|
-
id_generator: id_generator
|
44
|
-
)
|
45
|
-
end
|
46
|
-
|
47
|
-
before { @tempfiles = [] }
|
48
|
-
|
49
|
-
after { @tempfiles.each(&:unlink) }
|
50
|
-
|
51
|
-
describe 'when uploading a single file' do
|
52
|
-
let(:content) { '.' * 12_003 }
|
53
|
-
let(:local) { create_tempfile('input.txt', content) }
|
54
|
-
let(:remote) { 'C:\\dest' }
|
55
|
-
let(:dst) { "#{remote}/#{File.basename(local)}" }
|
56
|
-
let(:src_md5) { md5sum(local) }
|
57
|
-
let(:size) { File.size(local) }
|
58
|
-
let(:cmd_tmpfile) { "%TEMP%\\b64-#{src_md5}.txt" }
|
59
|
-
let(:ps_tmpfile) { "$env:TEMP\\b64-#{src_md5}.txt" }
|
60
|
-
|
61
|
-
let(:upload) { transporter.upload(local, remote) }
|
62
|
-
|
63
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
64
|
-
def self.common_specs_for_all_single_file_types
|
65
|
-
it 'truncates a zero-byte hash_file for check_files' do
|
66
|
-
expect(service).to receive(:run_cmd).with(
|
67
|
-
regexify(%(echo|set /p=>"%TEMP%\\hash-alpha.txt")))
|
68
|
-
.and_return(cmd_output)
|
69
|
-
|
70
|
-
upload
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'uploads the hash_file in chunks for check_files' do
|
74
|
-
hash = outdent!(<<-HASH.chomp)
|
75
|
-
@{
|
76
|
-
"#{src_md5}" = @{
|
77
|
-
"target" = "#{remote}";
|
78
|
-
"src_basename" = "#{File.basename(local)}";
|
79
|
-
"dst" = "#{remote}"
|
80
|
-
}
|
81
|
-
}
|
82
|
-
HASH
|
83
|
-
|
84
|
-
expect(service).to receive(:run_cmd)
|
85
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"))
|
86
|
-
.and_return(cmd_output).once
|
87
|
-
|
88
|
-
upload
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'sets hash_file and runs the check_files powershell script' do
|
92
|
-
expect(service).to receive(:run_powershell_script).with(
|
93
|
-
regexify(%($hash_file = "$env:TEMP\\hash-alpha.txt")) &&
|
94
|
-
regexify(
|
95
|
-
'Check-Files (Invoke-Input $hash_file) | ' \
|
96
|
-
'ConvertTo-Csv -NoTypeInformation')
|
97
|
-
).and_return(check_output)
|
98
|
-
|
99
|
-
upload
|
100
|
-
end
|
101
|
-
end
|
102
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
103
|
-
|
104
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
105
|
-
def self.common_specs_for_all_single_dirty_file_types
|
106
|
-
it 'truncates a zero-byte tempfile' do
|
107
|
-
expect(service).to receive(:run_cmd).with(
|
108
|
-
regexify(%(echo|set /p=>"#{cmd_tmpfile}"))
|
109
|
-
).and_return(cmd_output)
|
110
|
-
|
111
|
-
upload
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'ploads the file in 8k chunks' do
|
115
|
-
expect(service).to receive(:run_cmd)
|
116
|
-
.with(%(echo #{base64('.' * 6000)} >> "#{cmd_tmpfile}"))
|
117
|
-
.and_return(cmd_output).twice
|
118
|
-
expect(service).to receive(:run_cmd)
|
119
|
-
.with(%(echo #{base64('.' * 3)} >> "#{cmd_tmpfile}"))
|
120
|
-
.and_return(cmd_output).once
|
121
|
-
|
122
|
-
upload
|
123
|
-
end
|
124
|
-
|
125
|
-
describe 'with a small file' do
|
126
|
-
let(:content) { 'hello, world' }
|
127
|
-
|
128
|
-
it 'uploads the file in base64 encoding' do
|
129
|
-
expect(service).to receive(:run_cmd)
|
130
|
-
.with(%(echo #{base64(content)} >> "#{cmd_tmpfile}"))
|
131
|
-
.and_return(cmd_output)
|
132
|
-
|
133
|
-
upload
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'truncates a zero-byte hash_file for decode_files' do
|
138
|
-
expect(service).to receive(:run_cmd).with(
|
139
|
-
regexify(%(echo|set /p=>"%TEMP%\\hash-beta.txt"))
|
140
|
-
).and_return(cmd_output)
|
141
|
-
|
142
|
-
upload
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'uploads the hash_file in chunks for decode_files' do
|
146
|
-
hash = outdent!(<<-HASH.chomp)
|
147
|
-
@{
|
148
|
-
"#{ps_tmpfile}" = @{
|
149
|
-
"dst" = "#{remote}"
|
150
|
-
}
|
151
|
-
}
|
152
|
-
HASH
|
153
|
-
|
154
|
-
expect(service).to receive(:run_cmd)
|
155
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"))
|
156
|
-
.and_return(cmd_output).once
|
157
|
-
|
158
|
-
upload
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'sets hash_file and runs the decode_files powershell script' do
|
162
|
-
expect(service).to receive(:run_powershell_script).with(
|
163
|
-
regexify(%($hash_file = "$env:TEMP\\hash-beta.txt")) &&
|
164
|
-
regexify(
|
165
|
-
'Decode-Files (Invoke-Input $hash_file) | ' \
|
166
|
-
'ConvertTo-Csv -NoTypeInformation')
|
167
|
-
).and_return(check_output)
|
168
|
-
|
169
|
-
upload
|
170
|
-
end
|
171
|
-
end
|
172
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
173
|
-
|
174
|
-
describe 'for a new file' do
|
175
|
-
# let(:check_output) do
|
176
|
-
def check_output
|
177
|
-
create_check_output([
|
178
|
-
CheckEntry.new('False', src_md5, nil, 'True', 'False')
|
179
|
-
])
|
180
|
-
end
|
181
|
-
|
182
|
-
let(:cmd_output) do
|
183
|
-
o = ::WinRM::Output.new
|
184
|
-
o[:exitcode] = 0
|
185
|
-
o
|
186
|
-
end
|
187
|
-
|
188
|
-
# let(:decode_output) do
|
189
|
-
def decode_output
|
190
|
-
create_decode_output([
|
191
|
-
DecodeEntry.new(dst, 'True', src_md5, src_md5, ps_tmpfile, nil)
|
192
|
-
])
|
193
|
-
end
|
194
|
-
|
195
|
-
before do
|
196
|
-
allow(service).to receive(:run_cmd)
|
197
|
-
.and_return(cmd_output)
|
198
|
-
|
199
|
-
allow(service).to receive(:run_powershell_script)
|
200
|
-
.with(/^Check-Files .+ \| ConvertTo-Csv/)
|
201
|
-
.and_return(check_output)
|
202
|
-
|
203
|
-
allow(service).to receive(:run_powershell_script)
|
204
|
-
.with(/^Decode-Files .+ \| ConvertTo-Csv/)
|
205
|
-
.and_return(decode_output)
|
206
|
-
end
|
207
|
-
|
208
|
-
common_specs_for_all_single_file_types
|
209
|
-
|
210
|
-
common_specs_for_all_single_dirty_file_types
|
211
|
-
|
212
|
-
it 'returns a report hash' do
|
213
|
-
expect(upload[1]).to eq(
|
214
|
-
src_md5 => {
|
215
|
-
'src' => local,
|
216
|
-
'dst' => dst,
|
217
|
-
'tmpfile' => ps_tmpfile,
|
218
|
-
'tmpzip' => nil,
|
219
|
-
'src_md5' => src_md5,
|
220
|
-
'dst_md5' => src_md5,
|
221
|
-
'chk_exists' => 'False',
|
222
|
-
'chk_dirty' => 'True',
|
223
|
-
'verifies' => 'True',
|
224
|
-
'size' => size,
|
225
|
-
'xfered' => size / 3 * 4,
|
226
|
-
'chunks' => (size / 6000.to_f).ceil
|
227
|
-
}
|
228
|
-
)
|
229
|
-
end
|
230
|
-
|
231
|
-
describe 'when a failed check command is returned' do
|
232
|
-
def check_output
|
233
|
-
o = ::WinRM::Output.new
|
234
|
-
o[:exitcode] = 10
|
235
|
-
o[:data].concat([{ stderr: 'Oh noes\n' }])
|
236
|
-
o
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'raises a FileTransporterFailed error' do
|
240
|
-
expect { upload }.to raise_error(
|
241
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
describe 'when a failed decode command is returned' do
|
246
|
-
def decode_output
|
247
|
-
o = ::WinRM::Output.new
|
248
|
-
o[:exitcode] = 10
|
249
|
-
o[:data].concat([{ stderr: 'Oh noes\n' }])
|
250
|
-
o
|
251
|
-
end
|
252
|
-
|
253
|
-
it 'raises a FileTransporterFailed error' do
|
254
|
-
expect { upload }.to raise_error(
|
255
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
describe 'for an out of date (dirty) file' do
|
261
|
-
let(:check_output) do
|
262
|
-
create_check_output([
|
263
|
-
CheckEntry.new('True', src_md5, 'aabbcc', 'True', 'False')
|
264
|
-
])
|
265
|
-
end
|
266
|
-
|
267
|
-
let(:cmd_output) do
|
268
|
-
o = ::WinRM::Output.new
|
269
|
-
o[:exitcode] = 0
|
270
|
-
o
|
271
|
-
end
|
272
|
-
|
273
|
-
let(:decode_output) do
|
274
|
-
create_decode_output([
|
275
|
-
DecodeEntry.new(dst, 'True', src_md5, src_md5, ps_tmpfile, nil)
|
276
|
-
])
|
277
|
-
end
|
278
|
-
|
279
|
-
before do
|
280
|
-
allow(service).to receive(:run_cmd)
|
281
|
-
.and_return(cmd_output)
|
282
|
-
|
283
|
-
allow(service).to receive(:run_powershell_script)
|
284
|
-
.with(/^Check-Files .+ \| ConvertTo-Csv/)
|
285
|
-
.and_return(check_output)
|
286
|
-
|
287
|
-
allow(service).to receive(:run_powershell_script)
|
288
|
-
.with(/^Decode-Files .+ \| ConvertTo-Csv/)
|
289
|
-
.and_return(decode_output)
|
290
|
-
end
|
291
|
-
|
292
|
-
common_specs_for_all_single_file_types
|
293
|
-
|
294
|
-
common_specs_for_all_single_dirty_file_types
|
295
|
-
|
296
|
-
it 'returns a report hash' do
|
297
|
-
expect(upload[1]).to eq(
|
298
|
-
src_md5 => {
|
299
|
-
'src' => local,
|
300
|
-
'dst' => dst,
|
301
|
-
'tmpfile' => ps_tmpfile,
|
302
|
-
'tmpzip' => nil,
|
303
|
-
'src_md5' => src_md5,
|
304
|
-
'dst_md5' => src_md5,
|
305
|
-
'chk_exists' => 'True',
|
306
|
-
'chk_dirty' => 'True',
|
307
|
-
'verifies' => 'True',
|
308
|
-
'size' => size,
|
309
|
-
'xfered' => size / 3 * 4,
|
310
|
-
'chunks' => (size / 6000.to_f).ceil
|
311
|
-
}
|
312
|
-
)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
describe 'for an up to date (clean) file' do
|
317
|
-
let(:check_output) do
|
318
|
-
create_check_output([
|
319
|
-
CheckEntry.new('True', src_md5, src_md5, 'False', 'True')
|
320
|
-
])
|
321
|
-
end
|
322
|
-
|
323
|
-
let(:cmd_output) do
|
324
|
-
o = ::WinRM::Output.new
|
325
|
-
o[:exitcode] = 0
|
326
|
-
o
|
327
|
-
end
|
328
|
-
|
329
|
-
before do
|
330
|
-
allow(service).to receive(:run_cmd)
|
331
|
-
.and_return(cmd_output)
|
332
|
-
|
333
|
-
allow(service).to receive(:run_powershell_script)
|
334
|
-
.with(/^Check-Files .+ \| ConvertTo-Csv/)
|
335
|
-
.and_return(check_output)
|
336
|
-
end
|
337
|
-
|
338
|
-
common_specs_for_all_single_file_types
|
339
|
-
|
340
|
-
it 'uploads nothing' do
|
341
|
-
expect(service).not_to receive(:run_cmd).with(/#{remote}/)
|
342
|
-
|
343
|
-
upload
|
344
|
-
end
|
345
|
-
|
346
|
-
it 'skips the decode_files powershell script' do
|
347
|
-
expect(service).not_to receive(:run_powershell_script).with(regexify(
|
348
|
-
'Decode-Files $files | ConvertTo-Csv -NoTypeInformation')
|
349
|
-
)
|
350
|
-
|
351
|
-
upload
|
352
|
-
end
|
353
|
-
|
354
|
-
it 'returns a report hash' do
|
355
|
-
expect(upload[1]).to eq(
|
356
|
-
src_md5 => {
|
357
|
-
'src' => local,
|
358
|
-
'dst' => remote,
|
359
|
-
'size' => size,
|
360
|
-
'src_md5' => src_md5,
|
361
|
-
'dst_md5' => src_md5,
|
362
|
-
'chk_exists' => 'True',
|
363
|
-
'chk_dirty' => 'False',
|
364
|
-
'verifies' => 'True'
|
365
|
-
}
|
366
|
-
)
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
describe 'when uploading a single directory' do
|
372
|
-
let(:content) { "I'm a fake zip file" }
|
373
|
-
let(:local) { Dir.mktmpdir('input') }
|
374
|
-
let(:remote) { 'C:\\dest' }
|
375
|
-
let(:src_zip) { create_tempfile('fake.zip', content) }
|
376
|
-
let(:dst) { remote }
|
377
|
-
let(:src_md5) { md5sum(src_zip) }
|
378
|
-
let(:size) { File.size(src_zip) }
|
379
|
-
let(:cmd_tmpfile) { "%TEMP%\\b64-#{src_md5}.txt" }
|
380
|
-
let(:ps_tmpfile) { "$env:TEMP\\b64-#{src_md5}.txt" }
|
381
|
-
let(:ps_tmpzip) { "$env:TEMP\\winrm-upload\\tmpzip-#{src_md5}.zip" }
|
382
|
-
|
383
|
-
let(:tmp_zip) { double('tmp_zip') }
|
384
|
-
|
385
|
-
let(:cmd_output) do
|
386
|
-
o = ::WinRM::Output.new
|
387
|
-
o[:exitcode] = 0
|
388
|
-
o
|
389
|
-
end
|
390
|
-
|
391
|
-
let(:check_output) do
|
392
|
-
create_check_output([
|
393
|
-
CheckEntry.new('False', src_md5, nil, 'True', 'False')
|
394
|
-
])
|
395
|
-
end
|
396
|
-
|
397
|
-
let(:decode_output) do
|
398
|
-
create_decode_output([
|
399
|
-
DecodeEntry.new(dst, 'True', src_md5, src_md5, ps_tmpfile, ps_tmpzip)
|
400
|
-
])
|
401
|
-
end
|
402
|
-
|
403
|
-
before do
|
404
|
-
allow(tmp_zip).to receive(:path).and_return(Pathname(src_zip))
|
405
|
-
allow(tmp_zip).to receive(:unlink)
|
406
|
-
allow(WinRM::FS::Core::TmpZip).to receive(:new).with("#{local}/", logger)
|
407
|
-
.and_return(tmp_zip)
|
408
|
-
|
409
|
-
allow(service).to receive(:run_cmd)
|
410
|
-
.and_return(cmd_output)
|
411
|
-
|
412
|
-
allow(service).to receive(:run_powershell_script)
|
413
|
-
.with(/^Check-Files .+ \| ConvertTo-Csv/)
|
414
|
-
.and_return(check_output)
|
415
|
-
|
416
|
-
allow(service).to receive(:run_powershell_script)
|
417
|
-
.with(/^Decode-Files .+ \| ConvertTo-Csv/)
|
418
|
-
.and_return(decode_output)
|
419
|
-
end
|
420
|
-
|
421
|
-
after do
|
422
|
-
FileUtils.rm_rf(local)
|
423
|
-
end
|
424
|
-
|
425
|
-
let(:upload) { transporter.upload("#{local}/", remote) }
|
426
|
-
|
427
|
-
it 'truncates a zero-byte hash_file for check_files' do
|
428
|
-
expect(service).to receive(:run_cmd).with(regexify(%(echo|set /p=>"%TEMP%\\hash-alpha.txt"))
|
429
|
-
).and_return(cmd_output)
|
430
|
-
|
431
|
-
upload
|
432
|
-
end
|
433
|
-
|
434
|
-
it 'uploads the hash_file in chunks for check_files' do
|
435
|
-
hash = outdent!(<<-HASH.chomp)
|
436
|
-
@{
|
437
|
-
"#{src_md5}" = @{
|
438
|
-
"target" = "#{ps_tmpzip}";
|
439
|
-
"src_basename" = "#{File.basename(local)}";
|
440
|
-
"dst" = "#{dst}\\#{File.basename(local)}"
|
441
|
-
}
|
442
|
-
}
|
443
|
-
HASH
|
444
|
-
|
445
|
-
expect(service).to receive(:run_cmd)
|
446
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"))
|
447
|
-
.and_return(cmd_output).once
|
448
|
-
|
449
|
-
upload
|
450
|
-
end
|
451
|
-
|
452
|
-
it 'sets hash_file and runs the check_files powershell script' do
|
453
|
-
expect(service).to receive(:run_powershell_script).with(
|
454
|
-
regexify(%($hash_file = "$env:TEMP\\hash-alpha.txt")) &&
|
455
|
-
regexify(
|
456
|
-
'Check-Files (Invoke-Input $hash_file) | ' \
|
457
|
-
'ConvertTo-Csv -NoTypeInformation')
|
458
|
-
).and_return(check_output)
|
459
|
-
|
460
|
-
upload
|
461
|
-
end
|
462
|
-
|
463
|
-
it 'truncates a zero-byte tempfile' do
|
464
|
-
expect(service).to receive(:run_cmd).with(regexify(%(echo|set /p=>"#{cmd_tmpfile}"))
|
465
|
-
).and_return(cmd_output)
|
466
|
-
|
467
|
-
upload
|
468
|
-
end
|
469
|
-
|
470
|
-
it 'uploads the zip file in base64 encoding' do
|
471
|
-
expect(service).to receive(:run_cmd)
|
472
|
-
.with(%(echo #{base64(content)} >> "#{cmd_tmpfile}"))
|
473
|
-
.and_return(cmd_output)
|
474
|
-
|
475
|
-
upload
|
476
|
-
end
|
477
|
-
|
478
|
-
it 'truncates a zero-byte hash_file for decode_files' do
|
479
|
-
expect(service).to receive(:run_cmd).with(regexify(%(echo|set /p=>"%TEMP%\\hash-beta.txt"))
|
480
|
-
).and_return(cmd_output)
|
481
|
-
|
482
|
-
upload
|
483
|
-
end
|
484
|
-
|
485
|
-
it 'uploads the hash_file in chunks for decode_files' do
|
486
|
-
hash = outdent!(<<-HASH.chomp)
|
487
|
-
@{
|
488
|
-
"#{ps_tmpfile}" = @{
|
489
|
-
"dst" = "#{dst}\\#{File.basename(local)}";
|
490
|
-
"tmpzip" = "#{ps_tmpzip}"
|
491
|
-
}
|
492
|
-
}
|
493
|
-
HASH
|
494
|
-
|
495
|
-
expect(service).to receive(:run_cmd)
|
496
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"))
|
497
|
-
.and_return(cmd_output).once
|
498
|
-
|
499
|
-
upload
|
500
|
-
end
|
501
|
-
|
502
|
-
it 'sets hash_file and runs the decode_files powershell script' do
|
503
|
-
expect(service).to receive(:run_powershell_script).with(
|
504
|
-
regexify(%($hash_file = "$env:TEMP\\hash-beta.txt")) &&
|
505
|
-
regexify(
|
506
|
-
'Decode-Files (Invoke-Input $hash_file) | ' \
|
507
|
-
'ConvertTo-Csv -NoTypeInformation')
|
508
|
-
).and_return(check_output)
|
509
|
-
|
510
|
-
upload
|
511
|
-
end
|
512
|
-
|
513
|
-
it 'returns a report hash' do
|
514
|
-
expect(upload[1]).to eq(
|
515
|
-
src_md5 => {
|
516
|
-
'src' => "#{local}/",
|
517
|
-
'src_zip' => src_zip,
|
518
|
-
'dst' => dst,
|
519
|
-
'tmpfile' => ps_tmpfile,
|
520
|
-
'tmpzip' => ps_tmpzip,
|
521
|
-
'src_md5' => src_md5,
|
522
|
-
'dst_md5' => src_md5,
|
523
|
-
'chk_exists' => 'False',
|
524
|
-
'chk_dirty' => 'True',
|
525
|
-
'verifies' => 'True',
|
526
|
-
'size' => size,
|
527
|
-
'xfered' => size / 3 * 4,
|
528
|
-
'chunks' => (size / 6000.to_f).ceil
|
529
|
-
}
|
530
|
-
)
|
531
|
-
end
|
532
|
-
|
533
|
-
it 'cleans up the zip file' do
|
534
|
-
expect(tmp_zip).to receive(:unlink)
|
535
|
-
|
536
|
-
upload
|
537
|
-
end
|
538
|
-
|
539
|
-
describe 'when a failed check command is returned' do
|
540
|
-
def check_output
|
541
|
-
o = ::WinRM::Output.new
|
542
|
-
o[:exitcode] = 10
|
543
|
-
o[:data].concat([{ stderr: 'Oh noes\n' }])
|
544
|
-
o
|
545
|
-
end
|
546
|
-
|
547
|
-
it 'raises a FileTransporterFailed error' do
|
548
|
-
expect { upload }.to raise_error(
|
549
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
|
-
describe 'when a failed decode command is returned' do
|
554
|
-
def decode_output
|
555
|
-
o = ::WinRM::Output.new
|
556
|
-
o[:exitcode] = 10
|
557
|
-
o[:data].concat([{ stderr: 'Oh noes\n' }])
|
558
|
-
o
|
559
|
-
end
|
560
|
-
|
561
|
-
it 'raises a FileTransporterFailed error' do
|
562
|
-
expect { upload }.to raise_error(
|
563
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
564
|
-
end
|
565
|
-
end
|
566
|
-
end
|
567
|
-
|
568
|
-
describe 'when uploading multiple files' do
|
569
|
-
let(:remote) { 'C:\\Program Files' }
|
570
|
-
|
571
|
-
1.upto(3).each do |i|
|
572
|
-
let(:"local#{i}") { create_tempfile("input#{i}.txt", "input#{i}") }
|
573
|
-
let(:"src#{i}_md5") { md5sum(send("local#{i}")) }
|
574
|
-
let(:"dst#{i}") { "#{remote}" }
|
575
|
-
let(:"size#{i}") { File.size(send("local#{i}")) }
|
576
|
-
let(:"cmd#{i}_tmpfile") { "%TEMP%\\b64-#{send("src#{i}_md5")}.txt" }
|
577
|
-
let(:"ps#{i}_tmpfile") { "$env:TEMP\\b64-#{send("src#{i}_md5")}.txt" }
|
578
|
-
end
|
579
|
-
|
580
|
-
let(:check_output) do
|
581
|
-
create_check_output([
|
582
|
-
# new
|
583
|
-
CheckEntry.new('False', src1_md5, nil, 'True', 'False'),
|
584
|
-
# out-of-date
|
585
|
-
CheckEntry.new('True', src2_md5, 'aabbcc', 'True', 'False'),
|
586
|
-
# current
|
587
|
-
CheckEntry.new('True', src3_md5, src3_md5, 'False', 'True')
|
588
|
-
])
|
589
|
-
end
|
590
|
-
|
591
|
-
let(:cmd_output) do
|
592
|
-
o = ::WinRM::Output.new
|
593
|
-
o[:exitcode] = 0
|
594
|
-
o
|
595
|
-
end
|
596
|
-
|
597
|
-
let(:decode_output) do
|
598
|
-
create_decode_output([
|
599
|
-
DecodeEntry.new(dst1, 'True', src1_md5, src1_md5, ps1_tmpfile, nil),
|
600
|
-
DecodeEntry.new(dst2, 'True', src2_md5, src2_md5, ps2_tmpfile, nil)
|
601
|
-
])
|
602
|
-
end
|
603
|
-
|
604
|
-
let(:upload) { transporter.upload([local1, local2, local3], remote) }
|
605
|
-
|
606
|
-
before do
|
607
|
-
allow(service).to receive(:run_cmd)
|
608
|
-
.and_return(cmd_output)
|
609
|
-
|
610
|
-
allow(service).to receive(:run_powershell_script)
|
611
|
-
.with(/^Check-Files .+ \| ConvertTo-Csv/)
|
612
|
-
.and_return(check_output)
|
613
|
-
|
614
|
-
allow(service).to receive(:run_powershell_script)
|
615
|
-
.with(/^Decode-Files .+ \| ConvertTo-Csv/)
|
616
|
-
.and_return(decode_output)
|
617
|
-
end
|
618
|
-
|
619
|
-
it 'truncates a zero-byte hash_file for check_files' do
|
620
|
-
expect(service).to receive(:run_cmd).with(regexify(%(echo|set /p=>"%TEMP%\\hash-alpha.txt"))
|
621
|
-
).and_return(cmd_output)
|
622
|
-
|
623
|
-
upload
|
624
|
-
end
|
625
|
-
|
626
|
-
it 'uploads the hash_file in chunks for check_files' do
|
627
|
-
hash = outdent!(<<-HASH.chomp)
|
628
|
-
@{
|
629
|
-
"#{src1_md5}" = @{
|
630
|
-
"target" = "#{dst1}";
|
631
|
-
"src_basename" = "#{File.basename(local1)}";
|
632
|
-
"dst" = "#{dst1}"
|
633
|
-
};
|
634
|
-
"#{src2_md5}" = @{
|
635
|
-
"target" = "#{dst2}";
|
636
|
-
"src_basename" = "#{File.basename(local2)}";
|
637
|
-
"dst" = "#{dst2}"
|
638
|
-
};
|
639
|
-
"#{src3_md5}" = @{
|
640
|
-
"target" = "#{dst3}";
|
641
|
-
"src_basename" = "#{File.basename(local3)}";
|
642
|
-
"dst" = "#{dst3}"
|
643
|
-
}
|
644
|
-
}
|
645
|
-
HASH
|
646
|
-
|
647
|
-
expect(service).to receive(:run_cmd)
|
648
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-alpha.txt"))
|
649
|
-
.and_return(cmd_output).once
|
650
|
-
|
651
|
-
upload
|
652
|
-
end
|
653
|
-
|
654
|
-
it 'sets hash_file and runs the check_files powershell script' do
|
655
|
-
expect(service).to receive(:run_powershell_script).with(
|
656
|
-
regexify(%($hash_file = "$env:TEMP\\hash-alpha.txt")) &&
|
657
|
-
regexify(
|
658
|
-
'Check-Files (Invoke-Input $hash_file) | ' \
|
659
|
-
'ConvertTo-Csv -NoTypeInformation')
|
660
|
-
).and_return(check_output)
|
661
|
-
|
662
|
-
upload
|
663
|
-
end
|
664
|
-
|
665
|
-
it 'only uploads dirty files' do
|
666
|
-
expect(service).to receive(:run_cmd)
|
667
|
-
.with(%(echo #{base64(IO.read(local1))} >> "#{cmd1_tmpfile}"))
|
668
|
-
expect(service).to receive(:run_cmd)
|
669
|
-
.with(%(echo #{base64(IO.read(local2))} >> "#{cmd2_tmpfile}"))
|
670
|
-
expect(service).not_to receive(:run_cmd)
|
671
|
-
.with(%(echo #{base64(IO.read(local3))} >> "#{cmd3_tmpfile}"))
|
672
|
-
|
673
|
-
upload
|
674
|
-
end
|
675
|
-
|
676
|
-
it 'truncates a zero-byte hash_file for decode_files' do
|
677
|
-
expect(service).to receive(:run_cmd).with(regexify(%(echo|set /p=>"%TEMP%\\hash-beta.txt"))
|
678
|
-
).and_return(cmd_output)
|
679
|
-
|
680
|
-
upload
|
681
|
-
end
|
682
|
-
|
683
|
-
it 'uploads the hash_file in chunks for decode_files' do
|
684
|
-
hash = outdent!(<<-HASH.chomp)
|
685
|
-
@{
|
686
|
-
"#{ps1_tmpfile}" = @{
|
687
|
-
"dst" = "#{dst1}"
|
688
|
-
};
|
689
|
-
"#{ps2_tmpfile}" = @{
|
690
|
-
"dst" = "#{dst2}"
|
691
|
-
}
|
692
|
-
}
|
693
|
-
HASH
|
694
|
-
|
695
|
-
expect(service).to receive(:run_cmd)
|
696
|
-
.with(%(echo #{base64(hash)} >> "%TEMP%\\hash-beta.txt"))
|
697
|
-
.and_return(cmd_output).once
|
698
|
-
|
699
|
-
upload
|
700
|
-
end
|
701
|
-
|
702
|
-
it 'sets hash_file and runs the decode_files powershell script' do
|
703
|
-
expect(service).to receive(:run_powershell_script).with(
|
704
|
-
regexify(%($hash_file = '$env:TEMP\\hash-beta.txt')) &&
|
705
|
-
regexify(
|
706
|
-
'Decode-Files (Invoke-Input $hash_file) | ' \
|
707
|
-
'ConvertTo-Csv -NoTypeInformation')
|
708
|
-
).and_return(check_output)
|
709
|
-
|
710
|
-
upload
|
711
|
-
end
|
712
|
-
|
713
|
-
it 'returns a report hash' do
|
714
|
-
report = upload[1]
|
715
|
-
|
716
|
-
expect(report.fetch(src1_md5)).to eq(
|
717
|
-
'src' => local1,
|
718
|
-
'dst' => dst1,
|
719
|
-
'tmpfile' => ps1_tmpfile,
|
720
|
-
'tmpzip' => nil,
|
721
|
-
'src_md5' => src1_md5,
|
722
|
-
'dst_md5' => src1_md5,
|
723
|
-
'chk_exists' => 'False',
|
724
|
-
'chk_dirty' => 'True',
|
725
|
-
'verifies' => 'True',
|
726
|
-
'size' => size1,
|
727
|
-
'xfered' => size1 / 3 * 4,
|
728
|
-
'chunks' => (size1 / 6000.to_f).ceil
|
729
|
-
)
|
730
|
-
expect(report.fetch(src2_md5)).to eq(
|
731
|
-
'src' => local2,
|
732
|
-
'dst' => dst2,
|
733
|
-
'tmpfile' => ps2_tmpfile,
|
734
|
-
'tmpzip' => nil,
|
735
|
-
'src_md5' => src2_md5,
|
736
|
-
'dst_md5' => src2_md5,
|
737
|
-
'chk_exists' => 'True',
|
738
|
-
'chk_dirty' => 'True',
|
739
|
-
'verifies' => 'True',
|
740
|
-
'size' => size2,
|
741
|
-
'xfered' => size2 / 3 * 4,
|
742
|
-
'chunks' => (size2 / 6000.to_f).ceil
|
743
|
-
)
|
744
|
-
expect(report.fetch(src3_md5)).to eq(
|
745
|
-
'src' => local3,
|
746
|
-
'dst' => dst3,
|
747
|
-
'src_md5' => src3_md5,
|
748
|
-
'dst_md5' => src3_md5,
|
749
|
-
'chk_exists' => 'True',
|
750
|
-
'chk_dirty' => 'False',
|
751
|
-
'verifies' => 'True',
|
752
|
-
'size' => size3
|
753
|
-
)
|
754
|
-
end
|
755
|
-
|
756
|
-
describe 'when a failed check command is returned' do
|
757
|
-
def check_output
|
758
|
-
o = ::WinRM::Output.new
|
759
|
-
o[:exitcode] = 10
|
760
|
-
o[:data].concat([{ stderr: "Oh noes\n" }])
|
761
|
-
o
|
762
|
-
end
|
763
|
-
|
764
|
-
it 'raises a FileTransporterFailed error' do
|
765
|
-
expect { upload }.to raise_error(
|
766
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
767
|
-
end
|
768
|
-
end
|
769
|
-
|
770
|
-
describe 'when a failed decode command is returned' do
|
771
|
-
def decode_output
|
772
|
-
o = ::WinRM::Output.new
|
773
|
-
o[:exitcode] = 10
|
774
|
-
o[:data].concat([{ stderr: "Oh noes\n" }])
|
775
|
-
o
|
776
|
-
end
|
777
|
-
|
778
|
-
it 'raises a FileTransporterFailed error' do
|
779
|
-
expect { upload }.to raise_error(
|
780
|
-
WinRM::FS::Core::FileTransporterFailed, /Upload failed \(exitcode: 10\)/)
|
781
|
-
end
|
782
|
-
end
|
783
|
-
end
|
784
|
-
|
785
|
-
it 'raises an exception when local file or directory is not found' do
|
786
|
-
expect { transporter.upload('/a/b/c/nope', 'C:\\nopeland') }.to raise_error Errno::ENOENT
|
787
|
-
end
|
788
|
-
|
789
|
-
def base64(string)
|
790
|
-
Base64.strict_encode64(string)
|
791
|
-
end
|
792
|
-
|
793
|
-
def create_check_output(entries)
|
794
|
-
csv = CSV.generate(force_quotes: true) do |rows|
|
795
|
-
rows << CheckEntry.new.members.map(&:to_s)
|
796
|
-
entries.each { |entry| rows << entry.to_a }
|
797
|
-
end
|
798
|
-
|
799
|
-
o = ::WinRM::Output.new
|
800
|
-
o[:exitcode] = 0
|
801
|
-
o[:data].concat(csv.lines.map { |line| { stdout: line } })
|
802
|
-
o
|
803
|
-
end
|
804
|
-
|
805
|
-
def create_decode_output(entries)
|
806
|
-
csv = CSV.generate(force_quotes: true) do |rows|
|
807
|
-
rows << DecodeEntry.new.members.map(&:to_s)
|
808
|
-
entries.each { |entry| rows << entry.to_a }
|
809
|
-
end
|
810
|
-
|
811
|
-
o = ::WinRM::Output.new
|
812
|
-
o[:exitcode] = 0
|
813
|
-
o[:data].concat(csv.lines.map { |line| { stdout: line } })
|
814
|
-
o
|
815
|
-
end
|
816
|
-
|
817
|
-
def create_tempfile(name, content)
|
818
|
-
pre, _, ext = name.rpartition('.')
|
819
|
-
file = Tempfile.open(["#{pre}-", ".#{ext}"])
|
820
|
-
@tempfiles << file
|
821
|
-
file.write(content)
|
822
|
-
file.close
|
823
|
-
file.path
|
824
|
-
end
|
825
|
-
|
826
|
-
def md5sum(local)
|
827
|
-
Digest::MD5.file(local).hexdigest
|
828
|
-
end
|
829
|
-
|
830
|
-
def outdent!(string)
|
831
|
-
string.gsub!(/^ {#{string.index(/[^ ]/)}}/, '')
|
832
|
-
end
|
833
|
-
|
834
|
-
def regexify(str, line = :whole_line)
|
835
|
-
r = Regexp.escape(str)
|
836
|
-
r = "^#{r}$" if line == :whole_line
|
837
|
-
Regexp.new(r)
|
838
|
-
end
|
839
|
-
end
|