chef-winrm-fs 1.4.2 → 1.4.4

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.
@@ -1,171 +1,193 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright 2015 Shawn Neal <sneal@sneal.net>
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- require "chef-winrm" unless defined?(WinRM::Connection)
19
- require_relative "scripts/scripts"
20
- require_relative "core/file_transporter"
21
-
22
- module WinRM
23
- module FS
24
- # Perform file transfer operations between a local machine and winrm endpoint
25
- class FileManager
26
- # Creates a new FileManager instance
27
- # @param [WinRM::Connection] WinRM web connection client
28
- def initialize(connection)
29
- @connection = connection
30
- @logger = connection.logger
31
- end
32
-
33
- # Gets the SHA1 checksum of the specified file if it exists,
34
- # otherwise ''
35
- # @param [String] The remote file path
36
- # @parms [String] The digest method
37
- def checksum(path, digest = "SHA1")
38
- @logger.debug("checksum with #{digest}: #{path}")
39
- script = WinRM::FS::Scripts.render("checksum", path: path, digest: digest)
40
- ps_run(script).exitcode == 0
41
- end
42
-
43
- # Create the specifed directory recursively
44
- # @param [String] The remote dir to create
45
- # @return [Boolean] True if successful, otherwise false
46
- def create_dir(path)
47
- @logger.debug("create_dir: #{path}")
48
- script = WinRM::FS::Scripts.render("create_dir", path: path)
49
- ps_run(script).exitcode == 0
50
- end
51
-
52
- # Deletes the file or directory at the specified path
53
- # @param [String] The path to remove
54
- # @return [Boolean] True if successful, otherwise False
55
- def delete(path)
56
- @logger.debug("deleting: #{path}")
57
- script = WinRM::FS::Scripts.render("delete", path: path)
58
- ps_run(script).exitcode == 0
59
- end
60
-
61
- # Downloads the specified remote file to the specified local path
62
- # @param [String] The full path on the remote machine
63
- # @param [String] The full path to write the file to locally
64
- # rubocop:disable Metrics/MethodLength
65
- def download(remote_path, local_path, chunk_size = 1024 * 1024, first = true)
66
- @logger.debug("downloading: #{remote_path} -> #{local_path} #{chunk_size}")
67
- index = 0
68
- output = _output_from_file(remote_path, chunk_size, index)
69
- return download_dir(remote_path, local_path, chunk_size, first) if output.exitcode == 2
70
-
71
- return false if output.exitcode >= 1
72
-
73
- File.open(local_path, "wb") do |fd|
74
- out = _write_file(fd, output)
75
- index += out.length
76
- until out.empty?
77
- output = _output_from_file(remote_path, chunk_size, index)
78
- return false if output.exitcode >= 1
79
-
80
- out = _write_file(fd, output)
81
- index += out.length
82
- end
83
- end
84
- true
85
- end
86
- # rubocop:enable Metrics/MethodLength
87
-
88
- def _output_from_file(remote_path, chunk_size, index)
89
- script = WinRM::FS::Scripts.render("download", path: remote_path, chunk_size: chunk_size, index: index)
90
- ps_run(script)
91
- end
92
-
93
- def _write_file(tofd, output)
94
- contents = output.stdout.gsub('\n\r', "")
95
- out = Base64.decode64(contents)
96
- out = out[0, out.length - 1] if out.end_with? "\x00"
97
- return out if out.empty?
98
-
99
- tofd.write(out)
100
- out
101
- end
102
-
103
- # Checks to see if the given path exists on the target file system.
104
- # @param [String] The full path to the directory or file
105
- # @return [Boolean] True if the file/dir exists, otherwise false.
106
- def exists?(path)
107
- @logger.debug("exists?: #{path}")
108
- script = WinRM::FS::Scripts.render("exists", path: path)
109
- ps_run(script).exitcode == 0
110
- end
111
-
112
- # Gets the current user's TEMP directory on the remote system, for example
113
- # 'C:/Windows/Temp'
114
- # @return [String] Full path to the temp directory
115
- def temp_dir
116
- @temp_dir ||= begin
117
- ps_run("$env:TEMP").stdout.chomp.tr("\\", "/")
118
- end
119
- end
120
-
121
- # Upload one or more local files and directories to a remote directory
122
- # @example copy a single file to a winrm endpoint
123
- #
124
- # file_manager.upload('/Users/sneal/myfile.txt', 'c:/foo/myfile.txt')
125
- #
126
- # @example copy a single directory to a winrm endpoint
127
- #
128
- # file_manager.upload('c:/dev/my_dir', '$env:AppData')
129
- #
130
- # @param [String] A path to a local directory or file that will be copied
131
- # to the remote Windows box.
132
- # @param [String] The target directory or file
133
- # This path may contain powershell style environment variables
134
- # @yieldparam [Fixnum] Number of bytes copied in current payload sent to the winrm endpoint
135
- # @yieldparam [Fixnum] The total number of bytes to be copied
136
- # @yieldparam [String] Path of file being copied
137
- # @yieldparam [String] Target path on the winrm endpoint
138
- # @return [Fixnum] The total number of bytes copied
139
- def upload(local_path, remote_path, &block)
140
- @connection.shell(:powershell) do |shell|
141
- file_transporter ||= WinRM::FS::Core::FileTransporter.new(shell)
142
- begin
143
- file_transporter.upload(local_path, remote_path, &block)[0]
144
- ensure
145
- file_transporter.close
146
- end
147
- end
148
- end
149
-
150
- private
151
-
152
- def ps_run(cmd)
153
- shell = @connection.shell(:powershell)
154
- begin
155
- shell.run(cmd)
156
- ensure
157
- shell.close
158
- end
159
- end
160
-
161
- def download_dir(remote_path, local_path, chunk_size, first)
162
- local_path = File.join(local_path, File.basename(remote_path.to_s)) if first
163
- FileUtils.mkdir_p(local_path) unless File.directory?(local_path)
164
- command = "Get-ChildItem #{remote_path} | Select-Object Name"
165
- @connection.shell(:powershell) { |e| e.run(command) }.stdout.strip.split("\n").drop(2).each do |file|
166
- download(File.join(remote_path.to_s, file.strip), File.join(local_path, file.strip), chunk_size, false)
167
- end
168
- end
169
- end
170
- end
171
- end
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2015 Shawn Neal <sneal@sneal.net>
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require "chef-winrm" unless defined?(WinRM::Connection)
19
+ require_relative "scripts/scripts"
20
+ require_relative "core/file_transporter"
21
+
22
+ module WinRM
23
+ module FS
24
+ # Perform file transfer operations between a local machine and winrm endpoint
25
+ class FileManager
26
+ # Creates a new FileManager instance
27
+ # @param [WinRM::Connection] WinRM web connection client
28
+ def initialize(connection)
29
+ @connection = connection
30
+ @logger = connection.logger
31
+ end
32
+
33
+ # Gets the SHA1 checksum of the specified file if it exists,
34
+ # otherwise ''
35
+ # @param [String] The remote file path
36
+ # @parms [String] The digest method
37
+ def checksum(path, digest = "SHA1")
38
+ @logger.debug("checksum with #{digest}: #{path}")
39
+ script = WinRM::FS::Scripts.render("checksum", path: path, digest: digest)
40
+ ps_run(script).exitcode == 0
41
+ end
42
+
43
+ # Create the specifed directory recursively
44
+ # @param [String] The remote dir to create
45
+ # @return [Boolean] True if successful, otherwise false
46
+ def create_dir(path)
47
+ @logger.debug("create_dir: #{path}")
48
+ script = WinRM::FS::Scripts.render("create_dir", path: path)
49
+ ps_run(script).exitcode == 0
50
+ end
51
+
52
+ # Deletes the file or directory at the specified path
53
+ # @param [String] The path to remove
54
+ # @return [Boolean] True if successful, otherwise False
55
+ def delete(path)
56
+ @logger.debug("deleting: #{path}")
57
+ script = WinRM::FS::Scripts.render("delete", path: path)
58
+ ps_run(script).exitcode == 0
59
+ end
60
+
61
+ # Downloads the specified remote file to the specified local path
62
+ # @param [String] The full path on the remote machine
63
+ # @param [String] The full path to write the file to locally
64
+ # rubocop:disable Metrics/MethodLength
65
+ def download(remote_path, local_path, chunk_size = 1024 * 1024, first = true)
66
+ @logger.debug("downloading: #{remote_path} -> #{local_path} #{chunk_size}")
67
+ with_shell do
68
+ index = 0
69
+ output = _output_from_file(remote_path, chunk_size, index)
70
+ return download_dir(remote_path, local_path, chunk_size, first) if output.exitcode == 2
71
+
72
+ return false if output.exitcode >= 1
73
+
74
+ File.open(local_path, "wb") do |fd|
75
+ out = _write_file(fd, output)
76
+ index += out.length
77
+ until out.empty?
78
+ output = _output_from_file(remote_path, chunk_size, index)
79
+ return false if output.exitcode >= 1
80
+
81
+ out = _write_file(fd, output)
82
+ index += out.length
83
+ end
84
+ end
85
+ end
86
+ true
87
+ end
88
+ # rubocop:enable Metrics/MethodLength
89
+
90
+ def _output_from_file(remote_path, chunk_size, index)
91
+ script = WinRM::FS::Scripts.render("download", path: remote_path, chunk_size: chunk_size, index: index)
92
+ ps_run(script)
93
+ end
94
+
95
+ def _write_file(tofd, output)
96
+ # Base64 decoding already discards any bytes outside the Base64
97
+ # alphabet, so the payload needs no scrubbing pass of its own.
98
+ out = output.stdout.unpack1("m")
99
+ # download.ps1.erb reads $chunk[0..$bytesRead], one byte past the data.
100
+ out.delete_suffix!("\x00")
101
+ return out if out.empty?
102
+
103
+ tofd.write(out)
104
+ out
105
+ end
106
+
107
+ # Checks to see if the given path exists on the target file system.
108
+ # @param [String] The full path to the directory or file
109
+ # @return [Boolean] True if the file/dir exists, otherwise false.
110
+ def exists?(path)
111
+ @logger.debug("exists?: #{path}")
112
+ script = WinRM::FS::Scripts.render("exists", path: path)
113
+ ps_run(script).exitcode == 0
114
+ end
115
+
116
+ # Gets the current user's TEMP directory on the remote system, for example
117
+ # 'C:/Windows/Temp'
118
+ # @return [String] Full path to the temp directory
119
+ def temp_dir
120
+ @temp_dir ||= begin
121
+ ps_run("$env:TEMP").stdout.chomp.tr("\\", "/")
122
+ end
123
+ end
124
+
125
+ # Upload one or more local files and directories to a remote directory
126
+ # @example copy a single file to a winrm endpoint
127
+ #
128
+ # file_manager.upload('/Users/sneal/myfile.txt', 'c:/foo/myfile.txt')
129
+ #
130
+ # @example copy a single directory to a winrm endpoint
131
+ #
132
+ # file_manager.upload('c:/dev/my_dir', '$env:AppData')
133
+ #
134
+ # @param [String] A path to a local directory or file that will be copied
135
+ # to the remote Windows box.
136
+ # @param [String] The target directory or file
137
+ # This path may contain powershell style environment variables
138
+ # @yieldparam [Fixnum] Number of bytes copied in current payload sent to the winrm endpoint
139
+ # @yieldparam [Fixnum] The total number of bytes to be copied
140
+ # @yieldparam [String] Path of file being copied
141
+ # @yieldparam [String] Target path on the winrm endpoint
142
+ # @return [Fixnum] The total number of bytes copied
143
+ def upload(local_path, remote_path, &block)
144
+ @connection.shell(:powershell) do |shell|
145
+ file_transporter ||= WinRM::FS::Core::FileTransporter.new(shell)
146
+ begin
147
+ file_transporter.upload(local_path, remote_path, &block)[0]
148
+ ensure
149
+ file_transporter.close
150
+ end
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def ps_run(cmd)
157
+ return @shell.run(cmd) if @shell
158
+
159
+ shell = @connection.shell(:powershell)
160
+ begin
161
+ shell.run(cmd)
162
+ ensure
163
+ shell.close
164
+ end
165
+ end
166
+
167
+ # Holds a single PowerShell shell open for the duration of the block, so
168
+ # that a transfer spanning many chunks pays to open and close a remote
169
+ # runspace once rather than once per chunk. Re-entrant: a nested call
170
+ # reuses the shell that is already open.
171
+ def with_shell
172
+ return yield if @shell
173
+
174
+ @shell = @connection.shell(:powershell)
175
+ begin
176
+ yield
177
+ ensure
178
+ @shell.close
179
+ @shell = nil
180
+ end
181
+ end
182
+
183
+ def download_dir(remote_path, local_path, chunk_size, first)
184
+ local_path = File.join(local_path, File.basename(remote_path.to_s)) if first
185
+ FileUtils.mkdir_p(local_path) unless File.directory?(local_path)
186
+ command = "Get-ChildItem #{remote_path} | Select-Object Name"
187
+ ps_run(command).stdout.strip.split("\n").drop(2).each do |file|
188
+ download(File.join(remote_path.to_s, file.strip), File.join(local_path, file.strip), chunk_size, false)
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -1,49 +1,49 @@
1
- $hash_file = <%= hash_file %>
2
-
3
- Function Cleanup($disposable) {
4
- if (($disposable -ne $null) -and ($disposable.GetType().GetMethod("Dispose") -ne $null)) {
5
- $disposable.Dispose()
6
- }
7
- }
8
-
9
- Function Check-Files($h) {
10
- return $h.GetEnumerator() | ForEach-Object {
11
- $dst = Unresolve-Path $_.Value.target
12
- $dst_changed = $false
13
- if(Test-Path $dst -PathType Container) {
14
- $dst_changed = $true
15
- $dst = Join-Path $dst $_.Value.src_basename
16
- }
17
- New-Object psobject -Property @{
18
- chk_exists = ($exists = Test-Path $dst -PathType Leaf)
19
- src_sha1 = ($sMd5 = $_.Key)
20
- dst_sha1 = ($dMd5 = if ($exists) { Get-SHA1Sum $dst } else { $null })
21
- chk_dirty = ($dirty = if ($sMd5 -ne $dMd5) { $true } else { $false })
22
- verifies = if ($dirty -eq $false) { $true } else { $false }
23
- target_is_folder = $dst_changed
24
- }
25
- } | Select-Object -Property chk_exists,src_sha1,dst_sha1,chk_dirty,verifies,target_is_folder
26
- }
27
-
28
- Function Get-SHA1Sum($src) {
29
- Try {
30
- $c = [System.Security.Cryptography.SHA1]::Create()
31
- $bytes = $c.ComputeHash(($in = (Get-Item $src).OpenRead()))
32
- return ([System.BitConverter]::ToString($bytes)).Replace("-", "").ToLower()
33
- }
34
- Finally {
35
- Cleanup $c
36
- Cleanup $in
37
- }
38
- }
39
-
40
- Function Unresolve-Path($path) {
41
- if ($path -eq $null) {
42
- return $null
43
- }
44
- else {
45
- return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
46
- }
47
- }
48
-
49
- Check-Files $hash_file | ConvertTo-Csv -NoTypeInformation
1
+ $hash_file = <%= hash_file %>
2
+
3
+ Function Cleanup($disposable) {
4
+ if (($disposable -ne $null) -and ($disposable.GetType().GetMethod("Dispose") -ne $null)) {
5
+ $disposable.Dispose()
6
+ }
7
+ }
8
+
9
+ Function Check-Files($h) {
10
+ return $h.GetEnumerator() | ForEach-Object {
11
+ $dst = Unresolve-Path $_.Value.target
12
+ $dst_changed = $false
13
+ if(Test-Path $dst -PathType Container) {
14
+ $dst_changed = $true
15
+ $dst = Join-Path $dst $_.Value.src_basename
16
+ }
17
+ New-Object psobject -Property @{
18
+ chk_exists = ($exists = Test-Path $dst -PathType Leaf)
19
+ src_sha1 = ($sMd5 = $_.Key)
20
+ dst_sha1 = ($dMd5 = if ($exists) { Get-SHA1Sum $dst } else { $null })
21
+ chk_dirty = ($dirty = if ($sMd5 -ne $dMd5) { $true } else { $false })
22
+ verifies = if ($dirty -eq $false) { $true } else { $false }
23
+ target_is_folder = $dst_changed
24
+ }
25
+ } | Select-Object -Property chk_exists,src_sha1,dst_sha1,chk_dirty,verifies,target_is_folder
26
+ }
27
+
28
+ Function Get-SHA1Sum($src) {
29
+ Try {
30
+ $c = [System.Security.Cryptography.SHA1]::Create()
31
+ $bytes = $c.ComputeHash(($in = (Get-Item $src).OpenRead()))
32
+ return ([System.BitConverter]::ToString($bytes)).Replace("-", "").ToLower()
33
+ }
34
+ Finally {
35
+ Cleanup $c
36
+ Cleanup $in
37
+ }
38
+ }
39
+
40
+ Function Unresolve-Path($path) {
41
+ if ($path -eq $null) {
42
+ return $null
43
+ }
44
+ else {
45
+ return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
46
+ }
47
+ }
48
+
49
+ Check-Files $hash_file | ConvertTo-Csv -NoTypeInformation
@@ -1,13 +1,13 @@
1
- $p = $ExecutionContext.SessionState.Path
2
- $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
-
4
- if (Test-Path $path -PathType Leaf) {
5
- $cryptoProv = [System.Security.Cryptography.<%= digest %>]::Create()
6
- $file = [System.IO.File]::Open($path,
7
- [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
8
- $digest = ([System.BitConverter]::ToString($cryptoProv.ComputeHash($file)))
9
- $digest = $digest.Replace("-","").ToLower()
10
- $file.Close()
11
-
12
- Write-Output $digest
13
- }
1
+ $p = $ExecutionContext.SessionState.Path
2
+ $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
+
4
+ if (Test-Path $path -PathType Leaf) {
5
+ $cryptoProv = [System.Security.Cryptography.<%= digest %>]::Create()
6
+ $file = [System.IO.File]::Open($path,
7
+ [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
8
+ $digest = ([System.BitConverter]::ToString($cryptoProv.ComputeHash($file)))
9
+ $digest = $digest.Replace("-","").ToLower()
10
+ $file.Close()
11
+
12
+ Write-Output $digest
13
+ }
@@ -1,6 +1,6 @@
1
- $p = $ExecutionContext.SessionState.Path
2
- $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
- if (!(Test-Path $path)) {
4
- New-Item -ItemType Directory -Force -Path $path | Out-Null
5
- }
6
- exit 0
1
+ $p = $ExecutionContext.SessionState.Path
2
+ $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
+ if (!(Test-Path $path)) {
4
+ New-Item -ItemType Directory -Force -Path $path | Out-Null
5
+ }
6
+ exit 0
@@ -1,6 +1,6 @@
1
- $p = $ExecutionContext.SessionState.Path
2
- $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
- if (Test-Path $path) {
4
- Remove-Item $path -Force -Recurse
5
- }
6
- exit 0
1
+ $p = $ExecutionContext.SessionState.Path
2
+ $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
+ if (Test-Path $path) {
4
+ Remove-Item $path -Force -Recurse
5
+ }
6
+ exit 0
@@ -1,17 +1,17 @@
1
- $p = $ExecutionContext.SessionState.Path
2
- $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
- $index = <%= index %>
4
- $chunkSize = <%= chunk_size %>
5
- if (Test-Path $path -PathType Leaf) {
6
- $file = [System.IO.File]::OpenRead($path)
7
- $seekedTo = $file.Seek($index, [System.IO.SeekOrigin]::Begin)
8
- $chunk = New-Object byte[] $chunkSize
9
- $bytesRead = $file.Read($chunk, 0, $chunkSize)
10
- $bytes = [System.convert]::ToBase64String($chunk[0..$bytesRead])
11
- Write-Host $bytes
12
- exit 0
13
- }
14
- ElseIf (Test-Path $path -PathType Container) {
15
- exit 2
16
- }
17
- exit 1
1
+ $p = $ExecutionContext.SessionState.Path
2
+ $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
+ $index = <%= index %>
4
+ $chunkSize = <%= chunk_size %>
5
+ if (Test-Path $path -PathType Leaf) {
6
+ $file = [System.IO.File]::OpenRead($path)
7
+ $seekedTo = $file.Seek($index, [System.IO.SeekOrigin]::Begin)
8
+ $chunk = New-Object byte[] $chunkSize
9
+ $bytesRead = $file.Read($chunk, 0, $chunkSize)
10
+ $bytes = [System.convert]::ToBase64String($chunk[0..$bytesRead])
11
+ Write-Host $bytes
12
+ exit 0
13
+ }
14
+ ElseIf (Test-Path $path -PathType Container) {
15
+ exit 2
16
+ }
17
+ exit 1
@@ -1,10 +1,10 @@
1
- $p = $ExecutionContext.SessionState.Path
2
- $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
- if (Test-Path $path) {
4
- $true
5
- exit 0
6
- }
7
- else {
8
- $false
9
- exit 1
10
- }
1
+ $p = $ExecutionContext.SessionState.Path
2
+ $path = $p.GetUnresolvedProviderPathFromPSPath("<%= path %>")
3
+ if (Test-Path $path) {
4
+ $true
5
+ exit 0
6
+ }
7
+ else {
8
+ $false
9
+ exit 1
10
+ }