bolt 0.16.1 → 0.16.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/bolt.rb +0 -1
- data/lib/bolt/cli.rb +12 -6
- data/lib/bolt/error.rb +8 -0
- data/lib/bolt/executor.rb +99 -113
- data/lib/bolt/inventory.rb +47 -2
- data/lib/bolt/inventory/group.rb +37 -6
- data/lib/bolt/pal.rb +28 -9
- data/lib/bolt/target.rb +0 -8
- data/lib/bolt/transport/base.rb +159 -0
- data/lib/bolt/transport/orch.rb +158 -0
- data/lib/bolt/transport/ssh.rb +135 -0
- data/lib/bolt/transport/ssh/connection.rb +278 -0
- data/lib/bolt/transport/winrm.rb +165 -0
- data/lib/bolt/transport/winrm/connection.rb +472 -0
- data/lib/bolt/version.rb +1 -1
- data/modules/boltlib/lib/puppet/datatypes/target.rb +5 -0
- data/modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -1
- data/modules/boltlib/lib/puppet/functions/file_upload.rb +1 -5
- data/modules/boltlib/lib/puppet/functions/get_targets.rb +41 -0
- data/modules/boltlib/lib/puppet/functions/run_command.rb +2 -6
- data/modules/boltlib/lib/puppet/functions/run_plan.rb +4 -1
- data/modules/boltlib/lib/puppet/functions/run_script.rb +2 -6
- data/modules/boltlib/lib/puppet/functions/run_task.rb +15 -17
- data/modules/boltlib/types/targetspec.pp +7 -0
- metadata +10 -6
- data/lib/bolt/node.rb +0 -76
- data/lib/bolt/node/orch.rb +0 -126
- data/lib/bolt/node/ssh.rb +0 -356
- data/lib/bolt/node/winrm.rb +0 -598
@@ -0,0 +1,472 @@
|
|
1
|
+
require 'bolt/node/errors'
|
2
|
+
require 'bolt/node/output'
|
3
|
+
|
4
|
+
module Bolt
|
5
|
+
module Transport
|
6
|
+
class WinRM < Base
|
7
|
+
class Connection
|
8
|
+
attr_reader :logger, :target
|
9
|
+
|
10
|
+
def initialize(target)
|
11
|
+
@target = target
|
12
|
+
|
13
|
+
default_port = target.options[:ssl] ? HTTPS_PORT : HTTP_PORT
|
14
|
+
@port = @target.port || default_port
|
15
|
+
@user = @target.user
|
16
|
+
@extensions = DEFAULT_EXTENSIONS.to_set.merge(target.options[:extensions] || [])
|
17
|
+
|
18
|
+
@logger = Logging.logger[@target.host]
|
19
|
+
end
|
20
|
+
|
21
|
+
HTTP_PORT = 5985
|
22
|
+
HTTPS_PORT = 5986
|
23
|
+
|
24
|
+
def connect
|
25
|
+
if target.options[:ssl]
|
26
|
+
scheme = 'https'
|
27
|
+
transport = :ssl
|
28
|
+
else
|
29
|
+
scheme = 'http'
|
30
|
+
transport = :negotiate
|
31
|
+
end
|
32
|
+
endpoint = "#{scheme}://#{target.host}:#{@port}/wsman"
|
33
|
+
options = { endpoint: endpoint,
|
34
|
+
user: @user,
|
35
|
+
password: target.password,
|
36
|
+
retry_limit: 1,
|
37
|
+
transport: transport,
|
38
|
+
ca_trust_path: target.options[:cacert] }
|
39
|
+
|
40
|
+
Timeout.timeout(target.options[:connect_timeout]) do
|
41
|
+
@connection = ::WinRM::Connection.new(options)
|
42
|
+
transport_logger = Logging.logger[::WinRM]
|
43
|
+
transport_logger.level = :warn
|
44
|
+
@connection.logger = transport_logger
|
45
|
+
|
46
|
+
@session = @connection.shell(:powershell)
|
47
|
+
@session.run('$PSVersionTable.PSVersion')
|
48
|
+
@logger.debug { "Opened session" }
|
49
|
+
end
|
50
|
+
rescue Timeout::Error
|
51
|
+
# If we're using the default port with SSL, a timeout probably means the
|
52
|
+
# host doesn't support SSL.
|
53
|
+
if target.options[:ssl] && @port == HTTPS_PORT
|
54
|
+
theres_your_problem = "\nUse --no-ssl if this host isn't configured to use SSL for WinRM"
|
55
|
+
end
|
56
|
+
raise Bolt::Node::ConnectError.new(
|
57
|
+
"Timeout after #{target.options[:connect_timeout]} seconds connecting to #{endpoint}#{theres_your_problem}",
|
58
|
+
'CONNECT_ERROR'
|
59
|
+
)
|
60
|
+
rescue ::WinRM::WinRMAuthorizationError
|
61
|
+
raise Bolt::Node::ConnectError.new(
|
62
|
+
"Authentication failed for #{endpoint}",
|
63
|
+
'AUTH_ERROR'
|
64
|
+
)
|
65
|
+
rescue OpenSSL::SSL::SSLError => e
|
66
|
+
# If we're using SSL with the default non-SSL port, mention that as a likely problem
|
67
|
+
if target.options[:ssl] && @port == HTTP_PORT
|
68
|
+
theres_your_problem = "\nAre you using SSL to connect to a non-SSL port?"
|
69
|
+
end
|
70
|
+
raise Bolt::Node::ConnectError.new(
|
71
|
+
"Failed to connect to #{endpoint}: #{e.message}#{theres_your_problem}",
|
72
|
+
"CONNECT_ERROR"
|
73
|
+
)
|
74
|
+
rescue StandardError => e
|
75
|
+
raise Bolt::Node::ConnectError.new(
|
76
|
+
"Failed to connect to #{endpoint}: #{e.message}",
|
77
|
+
'CONNECT_ERROR'
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def disconnect
|
82
|
+
@session.close if @session
|
83
|
+
@logger.debug { "Closed session" }
|
84
|
+
end
|
85
|
+
|
86
|
+
def shell_init
|
87
|
+
return nil if @shell_initialized
|
88
|
+
result = execute(<<-PS)
|
89
|
+
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
|
90
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
|
91
|
+
$ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
92
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
|
93
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
|
94
|
+
$ENV:RUBYLIB
|
95
|
+
|
96
|
+
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
97
|
+
$utf8 = [System.Text.Encoding]::UTF8
|
98
|
+
|
99
|
+
function Invoke-Interpreter
|
100
|
+
{
|
101
|
+
[CmdletBinding()]
|
102
|
+
Param (
|
103
|
+
[Parameter()]
|
104
|
+
[String]
|
105
|
+
$Path,
|
106
|
+
|
107
|
+
[Parameter()]
|
108
|
+
[String]
|
109
|
+
$Arguments,
|
110
|
+
|
111
|
+
[Parameter()]
|
112
|
+
[Int32]
|
113
|
+
$Timeout,
|
114
|
+
|
115
|
+
[Parameter()]
|
116
|
+
[String]
|
117
|
+
$StdinInput = $Null
|
118
|
+
)
|
119
|
+
|
120
|
+
try
|
121
|
+
{
|
122
|
+
if (-not (Get-Command $Path -ErrorAction SilentlyContinue))
|
123
|
+
{
|
124
|
+
throw "Could not find executable '$Path' in ${ENV:PATH} on target node"
|
125
|
+
}
|
126
|
+
|
127
|
+
$startInfo = New-Object System.Diagnostics.ProcessStartInfo($Path, $Arguments)
|
128
|
+
$startInfo.UseShellExecute = $false
|
129
|
+
$startInfo.WorkingDirectory = Split-Path -Parent (Get-Command $Path).Path
|
130
|
+
$startInfo.CreateNoWindow = $true
|
131
|
+
if ($StdinInput) { $startInfo.RedirectStandardInput = $true }
|
132
|
+
$startInfo.RedirectStandardOutput = $true
|
133
|
+
$startInfo.RedirectStandardError = $true
|
134
|
+
|
135
|
+
$stdoutHandler = { if (-not ([String]::IsNullOrEmpty($EventArgs.Data))) { $Host.UI.WriteLine($EventArgs.Data) } }
|
136
|
+
$stderrHandler = { if (-not ([String]::IsNullOrEmpty($EventArgs.Data))) { $Host.UI.WriteErrorLine($EventArgs.Data) } }
|
137
|
+
$invocationId = [Guid]::NewGuid().ToString()
|
138
|
+
|
139
|
+
$process = New-Object System.Diagnostics.Process
|
140
|
+
$process.StartInfo = $startInfo
|
141
|
+
$process.EnableRaisingEvents = $true
|
142
|
+
|
143
|
+
# https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standarderror(v=vs.110).aspx#Anchor_2
|
144
|
+
$stdoutEvent = Register-ObjectEvent -InputObject $process -EventName 'OutputDataReceived' -Action $stdoutHandler
|
145
|
+
$stderrEvent = Register-ObjectEvent -InputObject $process -EventName 'ErrorDataReceived' -Action $stderrHandler
|
146
|
+
$exitedEvent = Register-ObjectEvent -InputObject $process -EventName 'Exited' -SourceIdentifier $invocationId
|
147
|
+
|
148
|
+
$process.Start() | Out-Null
|
149
|
+
|
150
|
+
$process.BeginOutputReadLine()
|
151
|
+
$process.BeginErrorReadLine()
|
152
|
+
|
153
|
+
if ($StdinInput)
|
154
|
+
{
|
155
|
+
$process.StandardInput.WriteLine($StdinInput)
|
156
|
+
$process.StandardInput.Close()
|
157
|
+
}
|
158
|
+
|
159
|
+
# park current thread until the PS event is signaled upon process exit
|
160
|
+
# OR the timeout has elapsed
|
161
|
+
$waitResult = Wait-Event -SourceIdentifier $invocationId -Timeout $Timeout
|
162
|
+
if (! $process.HasExited)
|
163
|
+
{
|
164
|
+
$Host.UI.WriteErrorLine("Process $Path did not complete in $Timeout seconds")
|
165
|
+
return 1
|
166
|
+
}
|
167
|
+
|
168
|
+
return $process.ExitCode
|
169
|
+
}
|
170
|
+
catch
|
171
|
+
{
|
172
|
+
$Host.UI.WriteErrorLine($_)
|
173
|
+
return 1
|
174
|
+
}
|
175
|
+
finally
|
176
|
+
{
|
177
|
+
@($stdoutEvent, $stderrEvent, $exitedEvent) |
|
178
|
+
? { $_ -ne $Null } |
|
179
|
+
% { Unregister-Event -SourceIdentifier $_.Name }
|
180
|
+
|
181
|
+
if ($process -ne $Null)
|
182
|
+
{
|
183
|
+
if (($process.Handle -ne $Null) -and (! $process.HasExited))
|
184
|
+
{
|
185
|
+
try { $process.Kill() } catch { $Host.UI.WriteErrorLine("Failed To Kill Process $Path") }
|
186
|
+
}
|
187
|
+
$process.Dispose()
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
function Write-Stream {
|
193
|
+
PARAM(
|
194
|
+
[Parameter(Position=0)] $stream,
|
195
|
+
[Parameter(ValueFromPipeline=$true)] $string
|
196
|
+
)
|
197
|
+
PROCESS {
|
198
|
+
$bytes = $utf8.GetBytes($string)
|
199
|
+
$stream.Write( $bytes, 0, $bytes.Length )
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
function Convert-JsonToXml {
|
204
|
+
PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
|
205
|
+
BEGIN {
|
206
|
+
$mStream = New-Object System.IO.MemoryStream
|
207
|
+
}
|
208
|
+
PROCESS {
|
209
|
+
$json | Write-Stream -Stream $mStream
|
210
|
+
}
|
211
|
+
END {
|
212
|
+
$mStream.Position = 0
|
213
|
+
try {
|
214
|
+
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
|
215
|
+
$xml = New-Object Xml.XmlDocument
|
216
|
+
$xml.Load($jsonReader)
|
217
|
+
$xml
|
218
|
+
} finally {
|
219
|
+
$jsonReader.Close()
|
220
|
+
$mStream.Dispose()
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
Function ConvertFrom-Xml {
|
226
|
+
[CmdletBinding(DefaultParameterSetName="AutoType")]
|
227
|
+
PARAM(
|
228
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
|
229
|
+
[Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
|
230
|
+
[Switch] $ForceType
|
231
|
+
)
|
232
|
+
PROCESS{
|
233
|
+
if (Get-Member -InputObject $xml -Name root) {
|
234
|
+
return $xml.root.Objects | ConvertFrom-Xml
|
235
|
+
} elseif (Get-Member -InputObject $xml -Name Objects) {
|
236
|
+
return $xml.Objects | ConvertFrom-Xml
|
237
|
+
}
|
238
|
+
$propbag = @{}
|
239
|
+
foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
|
240
|
+
Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
|
241
|
+
$propbag."$Name" = Convert-Properties $xml."$name"
|
242
|
+
}
|
243
|
+
if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
|
244
|
+
if ($ForceType -and $Type) {
|
245
|
+
try {
|
246
|
+
$output = New-Object $Type -Property $propbag
|
247
|
+
} catch {
|
248
|
+
$output = New-Object PSObject -Property $propbag
|
249
|
+
$output.PsTypeNames.Insert(0, $xml.__type)
|
250
|
+
}
|
251
|
+
} elseif ($propbag.Count -ne 0) {
|
252
|
+
$output = New-Object PSObject -Property $propbag
|
253
|
+
if ($Type) {
|
254
|
+
$output.PsTypeNames.Insert(0, $Type)
|
255
|
+
}
|
256
|
+
}
|
257
|
+
return $output
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
Function Convert-Properties {
|
262
|
+
PARAM($InputObject)
|
263
|
+
switch ($InputObject.type) {
|
264
|
+
"object" {
|
265
|
+
return (ConvertFrom-Xml -Xml $InputObject)
|
266
|
+
}
|
267
|
+
"string" {
|
268
|
+
$MightBeADate = $InputObject.get_InnerText() -as [DateTime]
|
269
|
+
## Strings that are actually dates (*grumble* JSON is crap)
|
270
|
+
if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
|
271
|
+
return $MightBeADate
|
272
|
+
} else {
|
273
|
+
return $InputObject.get_InnerText()
|
274
|
+
}
|
275
|
+
}
|
276
|
+
"number" {
|
277
|
+
$number = $InputObject.get_InnerText()
|
278
|
+
if ($number -eq ($number -as [int])) {
|
279
|
+
return $number -as [int]
|
280
|
+
} elseif ($number -eq ($number -as [double])) {
|
281
|
+
return $number -as [double]
|
282
|
+
} else {
|
283
|
+
return $number -as [decimal]
|
284
|
+
}
|
285
|
+
}
|
286
|
+
"boolean" {
|
287
|
+
return [bool]::parse($InputObject.get_InnerText())
|
288
|
+
}
|
289
|
+
"null" {
|
290
|
+
return $null
|
291
|
+
}
|
292
|
+
"array" {
|
293
|
+
[object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
|
294
|
+
Convert-Properties $item
|
295
|
+
})
|
296
|
+
return $Items
|
297
|
+
}
|
298
|
+
default {
|
299
|
+
return $InputObject
|
300
|
+
}
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
Function ConvertFrom-Json2 {
|
305
|
+
[CmdletBinding()]
|
306
|
+
PARAM(
|
307
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
|
308
|
+
[Parameter(Mandatory=$true)] [Type] $Type,
|
309
|
+
[Switch] $ForceType
|
310
|
+
)
|
311
|
+
PROCESS {
|
312
|
+
$null = $PSBoundParameters.Remove("InputObject")
|
313
|
+
[Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
|
314
|
+
if ($xml) {
|
315
|
+
if ($xml.Objects) {
|
316
|
+
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
|
317
|
+
} elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
|
318
|
+
$xml.Item | ConvertFrom-Xml @PSBoundParameters
|
319
|
+
} else {
|
320
|
+
$xml | ConvertFrom-Xml @PSBoundParameters
|
321
|
+
}
|
322
|
+
} else {
|
323
|
+
Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
function ConvertFrom-PSCustomObject
|
329
|
+
{
|
330
|
+
PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
|
331
|
+
PROCESS {
|
332
|
+
if ($null -eq $InputObject) { return $null }
|
333
|
+
|
334
|
+
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
|
335
|
+
$collection = @(
|
336
|
+
foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
|
337
|
+
)
|
338
|
+
|
339
|
+
$collection
|
340
|
+
} elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
|
341
|
+
$hash = @{}
|
342
|
+
foreach ($property in $InputObject.PSObject.Properties) {
|
343
|
+
$hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
|
344
|
+
}
|
345
|
+
|
346
|
+
$hash
|
347
|
+
} else {
|
348
|
+
$InputObject
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
|
353
|
+
function Get-ContentAsJson
|
354
|
+
{
|
355
|
+
[CmdletBinding()]
|
356
|
+
PARAM(
|
357
|
+
[Parameter(Mandatory = $true)] $Text,
|
358
|
+
[Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
|
359
|
+
)
|
360
|
+
|
361
|
+
# using polyfill cmdlet on PS2, so pass type info
|
362
|
+
if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
|
363
|
+
$Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
|
364
|
+
} else {
|
365
|
+
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
366
|
+
}
|
367
|
+
}
|
368
|
+
PS
|
369
|
+
if result.exit_code != 0
|
370
|
+
raise BaseError.new("Could not initialize shell: #{result.stderr.string}", "SHELL_INIT_ERROR")
|
371
|
+
end
|
372
|
+
@shell_initialized = true
|
373
|
+
end
|
374
|
+
|
375
|
+
def execute(command, _ = {})
|
376
|
+
result_output = Bolt::Node::Output.new
|
377
|
+
|
378
|
+
@logger.debug { "Executing command: #{command}" }
|
379
|
+
|
380
|
+
output = @session.run(command) do |stdout, stderr|
|
381
|
+
result_output.stdout << stdout
|
382
|
+
@logger.debug { "stdout: #{stdout}" }
|
383
|
+
result_output.stderr << stderr
|
384
|
+
@logger.debug { "stderr: #{stderr}" }
|
385
|
+
end
|
386
|
+
result_output.exit_code = output.exitcode
|
387
|
+
if output.exitcode.zero?
|
388
|
+
@logger.debug { "Command returned successfully" }
|
389
|
+
else
|
390
|
+
@logger.info { "Command failed with exit code #{output.exitcode}" }
|
391
|
+
end
|
392
|
+
result_output
|
393
|
+
end
|
394
|
+
|
395
|
+
# 10 minutes in seconds
|
396
|
+
DEFAULT_EXECUTION_TIMEOUT = 10 * 60
|
397
|
+
|
398
|
+
def execute_process(path = '', arguments = [], stdin = nil,
|
399
|
+
timeout = DEFAULT_EXECUTION_TIMEOUT)
|
400
|
+
quoted_args = arguments.map do |arg|
|
401
|
+
"'" + arg.gsub("'", "''") + "'"
|
402
|
+
end.join(',')
|
403
|
+
|
404
|
+
execute(<<-PS)
|
405
|
+
$quoted_array = @(
|
406
|
+
#{quoted_args}
|
407
|
+
)
|
408
|
+
|
409
|
+
$invokeArgs = @{
|
410
|
+
Path = "#{path}"
|
411
|
+
Arguments = $quoted_array -Join ' '
|
412
|
+
Timeout = #{timeout}
|
413
|
+
#{stdin.nil? ? '' : "StdinInput = @'\n" + stdin + "\n'@"}
|
414
|
+
}
|
415
|
+
|
416
|
+
# winrm gem checks $? prior to using $LASTEXITCODE
|
417
|
+
# making it necessary to exit with the desired code to propagate status properly
|
418
|
+
exit $(Invoke-Interpreter @invokeArgs)
|
419
|
+
PS
|
420
|
+
end
|
421
|
+
|
422
|
+
DEFAULT_EXTENSIONS = ['.ps1', '.rb', '.pp'].freeze
|
423
|
+
|
424
|
+
PS_ARGS = %w[
|
425
|
+
-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
|
426
|
+
].freeze
|
427
|
+
|
428
|
+
def write_remote_file(source, destination)
|
429
|
+
fs = ::WinRM::FS::FileManager.new(@connection)
|
430
|
+
# TODO: raise FileError here if this fails
|
431
|
+
fs.upload(source, destination)
|
432
|
+
end
|
433
|
+
|
434
|
+
def make_tempdir
|
435
|
+
find_parent = target.options[:tmpdir] ? "\"#{target.options[:tmpdir]}\"" : '[System.IO.Path]::GetTempPath()'
|
436
|
+
result = execute(<<-PS)
|
437
|
+
$parent = #{find_parent}
|
438
|
+
$name = [System.IO.Path]::GetRandomFileName()
|
439
|
+
$path = Join-Path $parent $name
|
440
|
+
New-Item -ItemType Directory -Path $path | Out-Null
|
441
|
+
$path
|
442
|
+
PS
|
443
|
+
if result.exit_code != 0
|
444
|
+
raise Bolt::Node::FileError.new("Could not make tempdir: #{result.stderr}", 'TEMPDIR_ERROR')
|
445
|
+
end
|
446
|
+
result.stdout.string.chomp
|
447
|
+
end
|
448
|
+
|
449
|
+
def with_remote_file(file)
|
450
|
+
ext = File.extname(file)
|
451
|
+
unless @extensions.include?(ext)
|
452
|
+
raise Bolt::Node::FileError.new("File extension #{ext} is not enabled, "\
|
453
|
+
"to run it please add to 'winrm: extensions'", 'FILETYPE_ERROR')
|
454
|
+
end
|
455
|
+
file_base = File.basename(file)
|
456
|
+
dir = make_tempdir
|
457
|
+
dest = "#{dir}\\#{file_base}"
|
458
|
+
begin
|
459
|
+
write_remote_file(file, dest)
|
460
|
+
shell_init
|
461
|
+
yield dest
|
462
|
+
ensure
|
463
|
+
execute(<<-PS)
|
464
|
+
Remove-Item -Force "#{dest}"
|
465
|
+
Remove-Item -Force "#{dir}"
|
466
|
+
PS
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|