winrm-fs 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -5
- data/Rakefile +2 -4
- data/VERSION +1 -1
- data/appveyor.yml +39 -0
- data/changelog.md +9 -0
- data/lib/winrm-fs/core/file_transporter.rb +506 -0
- data/lib/winrm-fs/core/tmp_zip.rb +166 -0
- data/lib/winrm-fs/file_manager.rb +14 -15
- data/lib/winrm-fs/scripts/check_files.ps1.erb +48 -0
- data/lib/winrm-fs/scripts/decode_files.ps1.erb +59 -0
- data/lib/winrm-fs/scripts/download.ps1.erb +2 -1
- data/spec/config-example.yml +1 -1
- data/spec/{file_manager_spec.rb → integration/file_manager_spec.rb} +38 -12
- data/spec/integration/tmp_zip_spec.rb +26 -0
- data/spec/spec_helper.rb +28 -4
- data/spec/unit/file_transporter_spec.rb +819 -0
- data/spec/unit/tmp_zip_spec.rb +79 -0
- data/winrm-fs.gemspec +2 -1
- metadata +27 -12
- data/benchmark.rb +0 -20
- data/lib/winrm-fs/core/command_executor.rb +0 -74
- data/lib/winrm-fs/core/file_uploader.rb +0 -84
- data/lib/winrm-fs/core/temp_zip_file.rb +0 -187
- data/lib/winrm-fs/core/upload_orchestrator.rb +0 -119
- data/lib/winrm-fs/scripts/decode_file.ps1.erb +0 -36
- data/spec/temp_zip_file_spec.rb +0 -55
@@ -0,0 +1,166 @@
|
|
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 'delegate'
|
20
|
+
require 'pathname'
|
21
|
+
require 'tempfile'
|
22
|
+
require 'zip'
|
23
|
+
|
24
|
+
module WinRM
|
25
|
+
module FS
|
26
|
+
module Core
|
27
|
+
# A temporary Zip file for a given directory.
|
28
|
+
#
|
29
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
30
|
+
class TmpZip
|
31
|
+
# Contructs a new Zip file for the given directory.
|
32
|
+
#
|
33
|
+
# There are 2 ways to interpret the directory path:
|
34
|
+
#
|
35
|
+
# * If the directory has no path separator terminator, then the
|
36
|
+
# directory basename will be used as the base directory in the
|
37
|
+
# resulting zip file.
|
38
|
+
# * If the directory has a path separator terminator (such as `/` or
|
39
|
+
# `\\`), then the entries under the directory will be added to the
|
40
|
+
# resulting zip file.
|
41
|
+
#
|
42
|
+
# The following emaples assume a directory tree structure of:
|
43
|
+
#
|
44
|
+
# src
|
45
|
+
# |-- alpha.txt
|
46
|
+
# |-- beta.txt
|
47
|
+
# \-- sub
|
48
|
+
# \-- charlie.txt
|
49
|
+
#
|
50
|
+
# @example Including the base directory in the zip file
|
51
|
+
#
|
52
|
+
# TmpZip.new("/path/to/src")
|
53
|
+
# # produces a zip file with entries:
|
54
|
+
# # - src/alpha.txt
|
55
|
+
# # - src/beta.txt
|
56
|
+
# # - src/sub/charlie.txt
|
57
|
+
#
|
58
|
+
# @example Excluding the base directory in the zip file
|
59
|
+
#
|
60
|
+
# TmpZip.new("/path/to/src/")
|
61
|
+
# # produces a zip file with entries:
|
62
|
+
# # - alpha.txt
|
63
|
+
# # - beta.txt
|
64
|
+
# # - sub/charlie.txt
|
65
|
+
#
|
66
|
+
# @param dir [String,Pathname,#to_s] path to the directory
|
67
|
+
# @param logger [#debug,#debug?] an optional logger/ui object that
|
68
|
+
# responds to `#debug` and `#debug?` (default `nil`)
|
69
|
+
def initialize(dir, logger = nil)
|
70
|
+
@logger = logger || Logging.logger[self]
|
71
|
+
@dir = Pathname.new(dir)
|
72
|
+
@zip_io = Tempfile.open(['tmpzip-', '.zip'], binmode: true)
|
73
|
+
write_zip
|
74
|
+
@zip_io.close
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Pathname] path to zip file
|
78
|
+
def path
|
79
|
+
Pathname.new(zip_io.path) if zip_io.path
|
80
|
+
end
|
81
|
+
|
82
|
+
# Unlinks (deletes) the zip file from the filesystem.
|
83
|
+
def unlink
|
84
|
+
zip_io.unlink
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# @return [Pathname] the directory used to create the Zip file
|
90
|
+
# @api private
|
91
|
+
attr_reader :dir
|
92
|
+
|
93
|
+
# @return [#debug] the logger
|
94
|
+
# @api private
|
95
|
+
attr_reader :logger
|
96
|
+
|
97
|
+
# @return [IO] the Zip file IO
|
98
|
+
# @api private
|
99
|
+
attr_reader :zip_io
|
100
|
+
|
101
|
+
# @return [Array<Pathname] all recursive files under the base
|
102
|
+
# directory, excluding directories
|
103
|
+
# @api private
|
104
|
+
def entries
|
105
|
+
Pathname.glob(dir.join('**/*')).delete_if(&:directory?).sort
|
106
|
+
end
|
107
|
+
|
108
|
+
# (see Logging.log_subject)
|
109
|
+
# @api private
|
110
|
+
def log_subject
|
111
|
+
@log_subject ||= [self.class.to_s.split('::').last, path].join('::')
|
112
|
+
end
|
113
|
+
|
114
|
+
# Adds all file entries to the Zip output stream.
|
115
|
+
#
|
116
|
+
# @param zos [Zip::OutputStream] zip output stream
|
117
|
+
# @api private
|
118
|
+
def produce_zip_entries(zos)
|
119
|
+
entries.each do |entry|
|
120
|
+
entry_path = entry.sub(/#{dir}\//i, '')
|
121
|
+
logger.debug "+++ Adding #{entry_path}"
|
122
|
+
zos.put_next_entry(
|
123
|
+
zip_entry(entry_path),
|
124
|
+
nil, nil, ::Zip::Entry::DEFLATED, Zlib::BEST_COMPRESSION)
|
125
|
+
entry.open('rb') { |src| IO.copy_stream(src, zos) }
|
126
|
+
end
|
127
|
+
logger.debug '=== All files added.'
|
128
|
+
end
|
129
|
+
|
130
|
+
# Writes out a temporary Zip file.
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
def write_zip
|
134
|
+
logger.debug 'Populating files'
|
135
|
+
Zip::OutputStream.write_buffer(NoDupIO.new(zip_io)) do |zos|
|
136
|
+
produce_zip_entries(zos)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def zip_entry(entry_path)
|
141
|
+
Zip::Entry.new(
|
142
|
+
zip_io.path,
|
143
|
+
entry_path.to_s,
|
144
|
+
nil, nil, nil, nil, nil, nil,
|
145
|
+
::Zip::DOSTime.new(2000)
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Simple delegate wrapper to prevent `#dup` calls being made on IO
|
150
|
+
# objects. This is used to bypass an issue in the `Zip::Outputstream`
|
151
|
+
# constructor where an incoming IO is duplicated, leading to races
|
152
|
+
# on flushing the final stream to disk.
|
153
|
+
#
|
154
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
155
|
+
# @api private
|
156
|
+
class NoDupIO < SimpleDelegator
|
157
|
+
# @return [self] returns self and does *not* return a duplicate
|
158
|
+
# object
|
159
|
+
def dup
|
160
|
+
self
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -14,8 +14,9 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require 'winrm'
|
17
18
|
require_relative 'scripts/scripts'
|
18
|
-
require_relative 'core/
|
19
|
+
require_relative 'core/file_transporter'
|
19
20
|
|
20
21
|
module WinRM
|
21
22
|
module FS
|
@@ -25,7 +26,7 @@ module WinRM
|
|
25
26
|
# @param [WinRMWebService] WinRM web service client
|
26
27
|
def initialize(service)
|
27
28
|
@service = service
|
28
|
-
@logger =
|
29
|
+
@logger = service.logger
|
29
30
|
end
|
30
31
|
|
31
32
|
# Gets the MD5 checksum of the specified file if it exists,
|
@@ -34,7 +35,7 @@ module WinRM
|
|
34
35
|
def checksum(path)
|
35
36
|
@logger.debug("checksum: #{path}")
|
36
37
|
script = WinRM::FS::Scripts.render('checksum', path: path)
|
37
|
-
@service.
|
38
|
+
@service.create_executor { |e| e.run_powershell_script(script).stdout.chomp }
|
38
39
|
end
|
39
40
|
|
40
41
|
# Create the specifed directory recursively
|
@@ -43,7 +44,7 @@ module WinRM
|
|
43
44
|
def create_dir(path)
|
44
45
|
@logger.debug("create_dir: #{path}")
|
45
46
|
script = WinRM::FS::Scripts.render('create_dir', path: path)
|
46
|
-
@service.
|
47
|
+
@service.create_executor { |e| e.run_powershell_script(script)[:exitcode] == 0 }
|
47
48
|
end
|
48
49
|
|
49
50
|
# Deletes the file or directory at the specified path
|
@@ -52,7 +53,7 @@ module WinRM
|
|
52
53
|
def delete(path)
|
53
54
|
@logger.debug("deleting: #{path}")
|
54
55
|
script = WinRM::FS::Scripts.render('delete', path: path)
|
55
|
-
@service.
|
56
|
+
@service.create_executor { |e| e.run_powershell_script(script)[:exitcode] == 0 }
|
56
57
|
end
|
57
58
|
|
58
59
|
# Downloads the specified remote file to the specified local path
|
@@ -61,7 +62,7 @@ module WinRM
|
|
61
62
|
def download(remote_path, local_path)
|
62
63
|
@logger.debug("downloading: #{remote_path} -> #{local_path}")
|
63
64
|
script = WinRM::FS::Scripts.render('download', path: remote_path)
|
64
|
-
output = @service.
|
65
|
+
output = @service.create_executor { |e| e.run_powershell_script(script) }
|
65
66
|
return false if output[:exitcode] != 0
|
66
67
|
contents = output.stdout.gsub('\n\r', '')
|
67
68
|
out = Base64.decode64(contents)
|
@@ -75,14 +76,16 @@ module WinRM
|
|
75
76
|
def exists?(path)
|
76
77
|
@logger.debug("exists?: #{path}")
|
77
78
|
script = WinRM::FS::Scripts.render('exists', path: path)
|
78
|
-
@service.
|
79
|
+
@service.create_executor { |e| e.run_powershell_script(script)[:exitcode] == 0 }
|
79
80
|
end
|
80
81
|
|
81
82
|
# Gets the current user's TEMP directory on the remote system, for example
|
82
83
|
# 'C:/Windows/Temp'
|
83
84
|
# @return [String] Full path to the temp directory
|
84
85
|
def temp_dir
|
85
|
-
@guest_temp ||=
|
86
|
+
@guest_temp ||= begin
|
87
|
+
(@service.create_executor { |e| e.run_cmd('echo %TEMP%') }).stdout.chomp.gsub('\\', '/')
|
88
|
+
end
|
86
89
|
end
|
87
90
|
|
88
91
|
# Upload one or more local files and directories to a remote directory
|
@@ -104,13 +107,9 @@ module WinRM
|
|
104
107
|
# @yieldparam [String] Target path on the winrm endpoint
|
105
108
|
# @return [Fixnum] The total number of bytes copied
|
106
109
|
def upload(local_path, remote_path, &block)
|
107
|
-
@
|
108
|
-
|
109
|
-
|
110
|
-
if File.file?(local_path)
|
111
|
-
upload_orchestrator.upload_file(local_path, remote_path, &block)
|
112
|
-
else
|
113
|
-
upload_orchestrator.upload_directory(local_path, remote_path, &block)
|
110
|
+
@service.create_executor do |executor|
|
111
|
+
file_transporter ||= WinRM::FS::Core::FileTransporter.new(executor)
|
112
|
+
file_transporter.upload(local_path, remote_path, &block)[0]
|
114
113
|
end
|
115
114
|
end
|
116
115
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
$hash_file = "<%= hash_file %>"
|
2
|
+
|
3
|
+
Function Cleanup($o) { if (($o -ne $null) -and ($o.GetType().GetMethod("Dispose") -ne $null)) { $o.Dispose() } }
|
4
|
+
|
5
|
+
Function Decode-Base64File($src, $dst) {
|
6
|
+
Try {
|
7
|
+
$in = (Get-Item $src).OpenRead()
|
8
|
+
$b64 = New-Object -TypeName System.Security.Cryptography.FromBase64Transform
|
9
|
+
$m = [System.Security.Cryptography.CryptoStreamMode]::Read
|
10
|
+
$d = New-Object -TypeName System.Security.Cryptography.CryptoStream $in,$b64,$m
|
11
|
+
Copy-Stream $d ($out = [System.IO.File]::OpenWrite($dst))
|
12
|
+
} Finally { Cleanup $in; Cleanup $out; Cleanup $d }
|
13
|
+
}
|
14
|
+
|
15
|
+
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) } }
|
16
|
+
|
17
|
+
Function Check-Files($h) {
|
18
|
+
return $h.GetEnumerator() | ForEach-Object {
|
19
|
+
$dst = Unresolve-Path $_.Key
|
20
|
+
New-Object psobject -Property @{
|
21
|
+
chk_exists = ($exists = Test-Path $dst -PathType Leaf)
|
22
|
+
src_md5 = ($sMd5 = $_.Value)
|
23
|
+
dst_md5 = ($dMd5 = if ($exists) { Get-MD5Sum $dst } else { $null })
|
24
|
+
chk_dirty = ($dirty = if ($sMd5 -ne $dMd5) { $true } else { $false })
|
25
|
+
verifies = if ($dirty -eq $false) { $true } else { $false }
|
26
|
+
}
|
27
|
+
} | Select-Object -Property chk_exists,src_md5,dst_md5,chk_dirty,verifies
|
28
|
+
}
|
29
|
+
|
30
|
+
Function Get-MD5Sum($src) {
|
31
|
+
Try {
|
32
|
+
$c = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
|
33
|
+
$bytes = $c.ComputeHash(($in = (Get-Item $src).OpenRead()))
|
34
|
+
return ([System.BitConverter]::ToString($bytes)).Replace("-", "").ToLower()
|
35
|
+
} Finally { Cleanup $c; Cleanup $in }
|
36
|
+
}
|
37
|
+
|
38
|
+
Function Invoke-Input($in) {
|
39
|
+
$in = Unresolve-Path $in
|
40
|
+
Decode-Base64File $in ($decoded = "$($in).ps1")
|
41
|
+
$expr = Get-Content $decoded | Out-String
|
42
|
+
Remove-Item $in,$decoded -Force
|
43
|
+
return Invoke-Expression "$expr"
|
44
|
+
}
|
45
|
+
|
46
|
+
Function Unresolve-Path($p) { if ($p -eq $null) { return $null } else { return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p) } }
|
47
|
+
|
48
|
+
Check-Files (Invoke-Input $hash_file) | ConvertTo-Csv -NoTypeInformation
|
@@ -0,0 +1,59 @@
|
|
1
|
+
trap {$e = $_.Exception; $e.InvocationInfo.ScriptName; do {$e.Message; $e = $e.InnerException} while ($e); break;}
|
2
|
+
$progresspreference = 'SilentlyContinue'
|
3
|
+
function Decode-Base64File($src, $dst) {folder (split-path $dst);sc -force -Encoding Byte -Path $dst -Value ([Convert]::FromBase64String([IO.File]::ReadAllLines($src)))}
|
4
|
+
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) } }
|
5
|
+
function Resolve-ProviderPath{ $input | % {if ($_){(Resolve-Path $_).ProviderPath} else{$null}} }
|
6
|
+
function Get-FrameworkVersion { "Full", "Client" | % {([version](gp "HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\$_").Version)} | select -first 1}
|
7
|
+
function Test-NETStack($Version){ Get-FrameworkVersion -ge $Version }
|
8
|
+
function Test-IOCompression {($PSVersionTable.PSVersion.Major -ge 3) -and (Test-NETStack '4.5')}
|
9
|
+
function folder($path){ $path | ? {-not (test-path $_)} | % {$null = mkdir $_}}
|
10
|
+
function disposable($o){($o -is [IDisposable]) -and (($o | gm | %{$_.name}) -contains 'Dispose')}
|
11
|
+
function use($obj, [scriptblock]$sb){try {& $sb} catch [exception]{throw $_} finally {if (disposable $obj) {$obj.Dispose()}} }
|
12
|
+
set-alias RPP -Value Resolve-ProviderPath
|
13
|
+
|
14
|
+
Function Decode-Files($hash) {
|
15
|
+
foreach ($key in $hash.keys) {
|
16
|
+
$value = $hash[$key]
|
17
|
+
$tmp, $tzip, $dst = $Key, $Value["tmpzip"], $Value["dst"]
|
18
|
+
if($tmp -ne "") {
|
19
|
+
$sMd5 = (gi $tmp).BaseName.Replace("b64-", "")
|
20
|
+
$decoded = if ($tzip -ne $null) { $tzip } else { $dst }
|
21
|
+
Decode-Base64File $tmp $decoded
|
22
|
+
rm $tmp -Force
|
23
|
+
$dMd5 = Get-MD5Sum $decoded
|
24
|
+
$verifies = $sMd5 -like $dMd5
|
25
|
+
}
|
26
|
+
if ($tzip) {Unzip-File $tzip $dst}
|
27
|
+
New-Object psobject -Property @{dst=$dst;verifies=$verifies;src_md5=$sMd5;dst_md5=$dMd5;tmpfile=$tmp;tmpzip=$tzip}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
Function Get-MD5Sum($src) {
|
32
|
+
if ($src -and (test-path $src)) {
|
33
|
+
use ($c = New-Object -TypeName Security.Cryptography.MD5CryptoServiceProvider) {
|
34
|
+
use ($in = (gi $src).OpenRead()) {([BitConverter]::ToString($c.ComputeHash($in))).Replace("-", "").ToLower()}}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
Function Invoke-Input($in) {
|
39
|
+
$in = $in | rpp
|
40
|
+
$d = "$($in).ps1"
|
41
|
+
Decode-Base64File $in $d
|
42
|
+
$expr = gc $d | Out-String
|
43
|
+
rm $in,$d -Force
|
44
|
+
iex "$expr"
|
45
|
+
}
|
46
|
+
|
47
|
+
Function Unzip-File($src, $dst) {
|
48
|
+
$unpack = $src -replace '\.zip'
|
49
|
+
$dst_parent = Split-Path -Path $dst -Parent
|
50
|
+
if(!(Test-Path $dst_parent)) { $dst = $dst_parent }
|
51
|
+
folder $unpack, $dst
|
52
|
+
if (Test-IOCompression) {Add-Type -AN System.IO.Compression.FileSystem; [IO.Compression.ZipFile]::ExtractToDirectory($src, $unpack)}
|
53
|
+
else {Try {$s = New-Object -ComObject Shell.Application; ($s.NameSpace($unpack)).CopyHere(($s.NameSpace($src)).Items(), 0x610)} Finally {[void][Runtime.Interopservices.Marshal]::ReleaseComObject($s)}}
|
54
|
+
dir $unpack | cp -dest "$dst/" -force -recurse
|
55
|
+
rm $unpack -recurse -force
|
56
|
+
}
|
57
|
+
|
58
|
+
$hash_file = "<%= hash_file %>"
|
59
|
+
Decode-Files (Invoke-Input $hash_file) | ConvertTo-Csv -NoTypeInformation
|
@@ -1,7 +1,8 @@
|
|
1
1
|
$p = $ExecutionContext.SessionState.Path
|
2
2
|
$path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
|
3
3
|
if (Test-Path $path -PathType Leaf) {
|
4
|
-
[System.convert]::ToBase64String([System.IO.File]::ReadAllBytes($path))
|
4
|
+
$bytes = [System.convert]::ToBase64String([System.IO.File]::ReadAllBytes($path))
|
5
|
+
Write-Host $bytes
|
5
6
|
exit 0
|
6
7
|
}
|
7
8
|
exit 1
|
data/spec/config-example.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'pathname'
|
3
3
|
|
4
|
-
describe WinRM::FS::FileManager
|
4
|
+
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
|
-
let(:
|
7
|
+
let(:spec_dir) { File.expand_path(File.dirname(File.dirname(__FILE__))) }
|
8
8
|
let(:this_file) { __FILE__ }
|
9
9
|
let(:service) { winrm_connection }
|
10
10
|
|
@@ -35,7 +35,7 @@ describe WinRM::FS::FileManager, integration: true do
|
|
35
35
|
|
36
36
|
context 'temp_dir' do
|
37
37
|
it 'should return the remote users temp dir' do
|
38
|
-
expect(subject.temp_dir).to match(%r{C:/Users/\
|
38
|
+
expect(subject.temp_dir).to match(%r{C:/Users/\S+/AppData/Local/Temp})
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -58,7 +58,7 @@ describe WinRM::FS::FileManager, integration: true do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'should upload using relative file path' do
|
61
|
-
subject.upload('./spec/file_manager_spec.rb', dest_file)
|
61
|
+
subject.upload('./spec/integration/file_manager_spec.rb', dest_file)
|
62
62
|
expect(subject).to have_created(dest_file).with_content(this_file)
|
63
63
|
end
|
64
64
|
|
@@ -88,14 +88,16 @@ describe WinRM::FS::FileManager, integration: true do
|
|
88
88
|
|
89
89
|
it 'yields progress data' do
|
90
90
|
block_called = false
|
91
|
+
total_bytes_copied = 0
|
91
92
|
total = subject.upload(this_file, dest_file) do \
|
92
93
|
|bytes_copied, total_bytes, local_path, remote_path|
|
93
94
|
expect(total_bytes).to be > 0
|
94
|
-
|
95
|
+
total_bytes_copied = bytes_copied
|
95
96
|
expect(local_path).to eq(this_file)
|
96
97
|
expect(remote_path).to eq(dest_file)
|
97
98
|
block_called = true
|
98
99
|
end
|
100
|
+
expect(total_bytes_copied).to eq(total)
|
99
101
|
expect(block_called).to be true
|
100
102
|
expect(total).to be > 0
|
101
103
|
end
|
@@ -107,7 +109,7 @@ describe WinRM::FS::FileManager, integration: true do
|
|
107
109
|
end
|
108
110
|
|
109
111
|
it 'should upload when content differs' do
|
110
|
-
matchers_file = File.join(
|
112
|
+
matchers_file = File.join(spec_dir, 'matchers.rb')
|
111
113
|
subject.upload(matchers_file, dest_file)
|
112
114
|
bytes_uploaded = subject.upload(this_file, dest_file)
|
113
115
|
expect(bytes_uploaded).to be > 0
|
@@ -119,32 +121,48 @@ describe WinRM::FS::FileManager, integration: true do
|
|
119
121
|
end
|
120
122
|
|
121
123
|
context 'upload empty file' do
|
122
|
-
let(:empty_src_file) { Tempfile.new('empty')
|
124
|
+
let(:empty_src_file) { Tempfile.new('empty') }
|
123
125
|
let(:dest_file) { File.join(dest_dir, 'emptyfile.txt') }
|
124
126
|
|
125
127
|
it 'creates a new empty file' do
|
126
|
-
expect(subject.upload(empty_src_file, dest_file)).to be 0
|
128
|
+
expect(subject.upload(empty_src_file.path, dest_file)).to be 0
|
127
129
|
expect(subject).to have_created(dest_file).with_content('')
|
128
130
|
end
|
129
131
|
|
130
132
|
it 'overwrites an existing file' do
|
131
133
|
expect(subject.upload(this_file, dest_file)).to be > 0
|
132
|
-
expect(subject.upload(empty_src_file, dest_file)).to be 0
|
134
|
+
expect(subject.upload(empty_src_file.path, dest_file)).to be 0
|
133
135
|
expect(subject).to have_created(dest_file).with_content('')
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
137
139
|
context 'upload directory' do
|
138
|
-
let(:root_dir) { File.expand_path('
|
140
|
+
let(:root_dir) { File.expand_path('../../', File.dirname(__FILE__)) }
|
139
141
|
let(:winrm_fs_dir) { File.join(root_dir, 'lib/winrm-fs') }
|
140
142
|
let(:core_dir) { File.join(root_dir, 'lib/winrm-fs/core') }
|
141
143
|
|
142
|
-
it 'copies the
|
144
|
+
it 'copies the directory contents recursively when directory does not exist' do
|
143
145
|
bytes_uploaded = subject.upload(winrm_fs_dir, dest_dir)
|
144
146
|
expect(bytes_uploaded).to be > 0
|
145
147
|
|
146
148
|
Dir.glob(winrm_fs_dir + '/**/*.rb').each do |host_file|
|
147
|
-
host_file_rel = Pathname.new(host_file).relative_path_from(
|
149
|
+
host_file_rel = Pathname.new(host_file).relative_path_from(
|
150
|
+
Pathname.new(winrm_fs_dir)
|
151
|
+
).to_s
|
152
|
+
remote_file = File.join(dest_dir, host_file_rel)
|
153
|
+
expect(subject).to have_created(remote_file).with_content(host_file)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'copies the directory recursively when directory does exist' do
|
158
|
+
subject.create_dir(dest_dir)
|
159
|
+
bytes_uploaded = subject.upload(winrm_fs_dir, dest_dir)
|
160
|
+
expect(bytes_uploaded).to be > 0
|
161
|
+
|
162
|
+
Dir.glob(winrm_fs_dir + '/**/*.rb').each do |host_file|
|
163
|
+
host_file_rel = Pathname.new(host_file).relative_path_from(
|
164
|
+
Pathname.new(winrm_fs_dir).dirname
|
165
|
+
).to_s
|
148
166
|
remote_file = File.join(dest_dir, host_file_rel)
|
149
167
|
expect(subject).to have_created(remote_file).with_content(host_file)
|
150
168
|
end
|
@@ -156,6 +174,14 @@ describe WinRM::FS::FileManager, integration: true do
|
|
156
174
|
expect(bytes_uploaded).to eq 0
|
157
175
|
end
|
158
176
|
|
177
|
+
it 'unzips the directory when cached content is the same' do
|
178
|
+
subject.upload(winrm_fs_dir, dest_dir)
|
179
|
+
subject.delete(dest_dir)
|
180
|
+
expect(subject.exists?(dest_dir)).to be false
|
181
|
+
subject.upload(winrm_fs_dir, dest_dir)
|
182
|
+
expect(subject.exists?(dest_dir)).to be true
|
183
|
+
end
|
184
|
+
|
159
185
|
it 'copies the directory when content differs' do
|
160
186
|
subject.upload(winrm_fs_dir, dest_dir)
|
161
187
|
bytes_uploaded = subject.upload(core_dir, dest_dir)
|