bolt 1.5.0 → 1.6.0
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/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/lib/bolt/config.rb +14 -10
- data/lib/bolt/executor.rb +10 -4
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/node/errors.rb +8 -1
- data/lib/bolt/target.rb +25 -2
- data/lib/bolt/task.rb +13 -2
- data/lib/bolt/task/remote.rb +25 -0
- data/lib/bolt/transport/base.rb +22 -6
- data/lib/bolt/transport/docker.rb +6 -0
- data/lib/bolt/transport/local.rb +64 -20
- data/lib/bolt/transport/powershell.rb +330 -0
- data/lib/bolt/transport/remote.rb +53 -0
- data/lib/bolt/transport/ssh.rb +1 -2
- data/lib/bolt/transport/winrm.rb +16 -89
- data/lib/bolt/transport/winrm/connection.rb +5 -216
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +1 -1
- metadata +19 -2
@@ -0,0 +1,330 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
module Transport
|
5
|
+
module Powershell
|
6
|
+
class << self
|
7
|
+
PS_ARGS = %w[
|
8
|
+
-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
def powershell_file?(path)
|
12
|
+
Pathname(path).extname.casecmp('.ps1').zero?
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_from_extension(path)
|
16
|
+
case Pathname(path).extname.downcase
|
17
|
+
when '.rb'
|
18
|
+
[
|
19
|
+
'ruby.exe',
|
20
|
+
['-S', "\"#{path}\""]
|
21
|
+
]
|
22
|
+
when '.ps1'
|
23
|
+
[
|
24
|
+
'powershell.exe',
|
25
|
+
[*PS_ARGS, '-File', "\"#{path}\""]
|
26
|
+
]
|
27
|
+
when '.pp'
|
28
|
+
[
|
29
|
+
'puppet.bat',
|
30
|
+
['apply', "\"#{path}\""]
|
31
|
+
]
|
32
|
+
else
|
33
|
+
# Run the script via cmd, letting Windows extension handling determine how
|
34
|
+
[
|
35
|
+
'cmd.exe',
|
36
|
+
['/c', "\"#{path}\""]
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def escape_arguments(arguments)
|
42
|
+
arguments.map do |arg|
|
43
|
+
if arg =~ / /
|
44
|
+
"\"#{arg}\""
|
45
|
+
else
|
46
|
+
arg
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_env(arg, val)
|
52
|
+
"[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute_process(path, arguments, stdin = nil)
|
56
|
+
quoted_args = arguments.map do |arg|
|
57
|
+
"'" + arg.gsub("'", "''") + "'"
|
58
|
+
end.join(' ')
|
59
|
+
|
60
|
+
exec_cmd =
|
61
|
+
if stdin.nil?
|
62
|
+
"& #{path} #{quoted_args}"
|
63
|
+
else
|
64
|
+
"@'\n#{stdin}\n'@ | & #{path} #{quoted_args}"
|
65
|
+
end
|
66
|
+
<<-PS
|
67
|
+
$OutputEncoding = [Console]::OutputEncoding
|
68
|
+
#{exec_cmd}
|
69
|
+
if (-not $? -and ($LASTEXITCODE -eq $null)) { exit 1 }
|
70
|
+
exit $LASTEXITCODE
|
71
|
+
PS
|
72
|
+
end
|
73
|
+
|
74
|
+
def mkdirs(dirs)
|
75
|
+
"mkdir -Force #{dirs.uniq.sort.join(',')}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_tempdir(parent)
|
79
|
+
<<-PS
|
80
|
+
$parent = #{parent}
|
81
|
+
$name = [System.IO.Path]::GetRandomFileName()
|
82
|
+
$path = Join-Path $parent $name
|
83
|
+
New-Item -ItemType Directory -Path $path | Out-Null
|
84
|
+
$path
|
85
|
+
PS
|
86
|
+
end
|
87
|
+
|
88
|
+
def rmdir(dir)
|
89
|
+
<<-PS
|
90
|
+
Remove-Item -Force -Recurse -Path "#{dir}"
|
91
|
+
PS
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_script(arguments, script_path)
|
95
|
+
mapped_args = arguments.map do |a|
|
96
|
+
"$invokeArgs.ArgumentList += @'\n#{a}\n'@"
|
97
|
+
end.join("\n")
|
98
|
+
<<-PS
|
99
|
+
$invokeArgs = @{
|
100
|
+
ScriptBlock = (Get-Command "#{script_path}").ScriptBlock
|
101
|
+
ArgumentList = @()
|
102
|
+
}
|
103
|
+
#{mapped_args}
|
104
|
+
|
105
|
+
try
|
106
|
+
{
|
107
|
+
Invoke-Command @invokeArgs
|
108
|
+
}
|
109
|
+
catch
|
110
|
+
{
|
111
|
+
Write-Error $_.Exception
|
112
|
+
exit 1
|
113
|
+
}
|
114
|
+
PS
|
115
|
+
end
|
116
|
+
|
117
|
+
def run_ps_task(arguments, task_path, input_method)
|
118
|
+
# NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
|
119
|
+
# must create new powershell.exe process like other interpreters
|
120
|
+
# fortunately, using PS with stdin input_method should never happen
|
121
|
+
if input_method == 'powershell'
|
122
|
+
<<-PS
|
123
|
+
$private:tempArgs = Get-ContentAsJson (
|
124
|
+
$utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
125
|
+
)
|
126
|
+
$allowedArgs = (Get-Command "#{task_path}").Parameters.Keys
|
127
|
+
$private:taskArgs = @{}
|
128
|
+
$private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
|
129
|
+
try { & "#{task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
|
130
|
+
PS
|
131
|
+
else
|
132
|
+
%(try { & "#{task_path}" } catch { Write-Error $_.Exception; exit 1 })
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def shell_init
|
137
|
+
<<-PS
|
138
|
+
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
|
139
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\bin;" +
|
140
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
|
141
|
+
$ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
142
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
|
143
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
|
144
|
+
$ENV:RUBYLIB
|
145
|
+
|
146
|
+
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
147
|
+
$utf8 = [System.Text.Encoding]::UTF8
|
148
|
+
|
149
|
+
function Write-Stream {
|
150
|
+
PARAM(
|
151
|
+
[Parameter(Position=0)] $stream,
|
152
|
+
[Parameter(ValueFromPipeline=$true)] $string
|
153
|
+
)
|
154
|
+
PROCESS {
|
155
|
+
$bytes = $utf8.GetBytes($string)
|
156
|
+
$stream.Write( $bytes, 0, $bytes.Length )
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
function Convert-JsonToXml {
|
161
|
+
PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
|
162
|
+
BEGIN {
|
163
|
+
$mStream = New-Object System.IO.MemoryStream
|
164
|
+
}
|
165
|
+
PROCESS {
|
166
|
+
$json | Write-Stream -Stream $mStream
|
167
|
+
}
|
168
|
+
END {
|
169
|
+
$mStream.Position = 0
|
170
|
+
try {
|
171
|
+
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
|
172
|
+
$xml = New-Object Xml.XmlDocument
|
173
|
+
$xml.Load($jsonReader)
|
174
|
+
$xml
|
175
|
+
} finally {
|
176
|
+
$jsonReader.Close()
|
177
|
+
$mStream.Dispose()
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
Function ConvertFrom-Xml {
|
183
|
+
[CmdletBinding(DefaultParameterSetName="AutoType")]
|
184
|
+
PARAM(
|
185
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
|
186
|
+
[Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
|
187
|
+
[Switch] $ForceType
|
188
|
+
)
|
189
|
+
PROCESS{
|
190
|
+
if (Get-Member -InputObject $xml -Name root) {
|
191
|
+
return $xml.root.Objects | ConvertFrom-Xml
|
192
|
+
} elseif (Get-Member -InputObject $xml -Name Objects) {
|
193
|
+
return $xml.Objects | ConvertFrom-Xml
|
194
|
+
}
|
195
|
+
$propbag = @{}
|
196
|
+
foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
|
197
|
+
Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
|
198
|
+
$propbag."$Name" = Convert-Properties $xml."$name"
|
199
|
+
}
|
200
|
+
if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
|
201
|
+
if ($ForceType -and $Type) {
|
202
|
+
try {
|
203
|
+
$output = New-Object $Type -Property $propbag
|
204
|
+
} catch {
|
205
|
+
$output = New-Object PSObject -Property $propbag
|
206
|
+
$output.PsTypeNames.Insert(0, $xml.__type)
|
207
|
+
}
|
208
|
+
} elseif ($propbag.Count -ne 0) {
|
209
|
+
$output = New-Object PSObject -Property $propbag
|
210
|
+
if ($Type) {
|
211
|
+
$output.PsTypeNames.Insert(0, $Type)
|
212
|
+
}
|
213
|
+
}
|
214
|
+
return $output
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
Function Convert-Properties {
|
219
|
+
PARAM($InputObject)
|
220
|
+
switch ($InputObject.type) {
|
221
|
+
"object" {
|
222
|
+
return (ConvertFrom-Xml -Xml $InputObject)
|
223
|
+
}
|
224
|
+
"string" {
|
225
|
+
$MightBeADate = $InputObject.get_InnerText() -as [DateTime]
|
226
|
+
## Strings that are actually dates (*grumble* JSON is crap)
|
227
|
+
if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
|
228
|
+
return $MightBeADate
|
229
|
+
} else {
|
230
|
+
return $InputObject.get_InnerText()
|
231
|
+
}
|
232
|
+
}
|
233
|
+
"number" {
|
234
|
+
$number = $InputObject.get_InnerText()
|
235
|
+
if ($number -eq ($number -as [int])) {
|
236
|
+
return $number -as [int]
|
237
|
+
} elseif ($number -eq ($number -as [double])) {
|
238
|
+
return $number -as [double]
|
239
|
+
} else {
|
240
|
+
return $number -as [decimal]
|
241
|
+
}
|
242
|
+
}
|
243
|
+
"boolean" {
|
244
|
+
return [bool]::parse($InputObject.get_InnerText())
|
245
|
+
}
|
246
|
+
"null" {
|
247
|
+
return $null
|
248
|
+
}
|
249
|
+
"array" {
|
250
|
+
[object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
|
251
|
+
Convert-Properties $item
|
252
|
+
})
|
253
|
+
return $Items
|
254
|
+
}
|
255
|
+
default {
|
256
|
+
return $InputObject
|
257
|
+
}
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
Function ConvertFrom-Json2 {
|
262
|
+
[CmdletBinding()]
|
263
|
+
PARAM(
|
264
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
|
265
|
+
[Parameter(Mandatory=$true)] [Type] $Type,
|
266
|
+
[Switch] $ForceType
|
267
|
+
)
|
268
|
+
PROCESS {
|
269
|
+
$null = $PSBoundParameters.Remove("InputObject")
|
270
|
+
[Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
|
271
|
+
if ($xml) {
|
272
|
+
if ($xml.Objects) {
|
273
|
+
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
|
274
|
+
} elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
|
275
|
+
$xml.Item | ConvertFrom-Xml @PSBoundParameters
|
276
|
+
} else {
|
277
|
+
$xml | ConvertFrom-Xml @PSBoundParameters
|
278
|
+
}
|
279
|
+
} else {
|
280
|
+
Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
function ConvertFrom-PSCustomObject
|
286
|
+
{
|
287
|
+
PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
|
288
|
+
PROCESS {
|
289
|
+
if ($null -eq $InputObject) { return $null }
|
290
|
+
|
291
|
+
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
|
292
|
+
$collection = @(
|
293
|
+
foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
|
294
|
+
)
|
295
|
+
|
296
|
+
$collection
|
297
|
+
} elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
|
298
|
+
$hash = @{}
|
299
|
+
foreach ($property in $InputObject.PSObject.Properties) {
|
300
|
+
$hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
|
301
|
+
}
|
302
|
+
|
303
|
+
$hash
|
304
|
+
} else {
|
305
|
+
$InputObject
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
|
310
|
+
function Get-ContentAsJson
|
311
|
+
{
|
312
|
+
[CmdletBinding()]
|
313
|
+
PARAM(
|
314
|
+
[Parameter(Mandatory = $true)] $Text,
|
315
|
+
[Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
|
316
|
+
)
|
317
|
+
|
318
|
+
# using polyfill cmdlet on PS2, so pass type info
|
319
|
+
if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
|
320
|
+
$Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
|
321
|
+
} else {
|
322
|
+
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
323
|
+
}
|
324
|
+
}
|
325
|
+
PS
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/task/remote'
|
4
|
+
require 'bolt/transport/base'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
module Transport
|
8
|
+
class Remote < Base
|
9
|
+
# The options for the remote transport not defined.
|
10
|
+
def self.filter_options(unfiltered)
|
11
|
+
unfiltered
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.validate(options)
|
15
|
+
# This will fail when validating global config
|
16
|
+
# unless options['device-type']
|
17
|
+
# raise Bolt::ValidationError, 'Must specify device-type for devices'
|
18
|
+
# end
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: this should have access to inventory so target doesn't have to
|
22
|
+
def initialize(executor)
|
23
|
+
super()
|
24
|
+
|
25
|
+
@executor = executor
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_proxy(target)
|
29
|
+
inventory = target.inventory
|
30
|
+
raise "Target was created without inventory? Not get_targets?" unless inventory
|
31
|
+
proxy = inventory.get_targets(target.options['run-on'] || 'localhost').first
|
32
|
+
|
33
|
+
if proxy.transport == 'remote'
|
34
|
+
msg = "#{proxy.name} is not a valid run-on target for #{target.name} since is also remote."
|
35
|
+
raise Bolt::Error.new(msg, 'bolt/invalid-remote-target')
|
36
|
+
end
|
37
|
+
proxy
|
38
|
+
end
|
39
|
+
|
40
|
+
# Cannot batch because arugments differ
|
41
|
+
def run_task(target, task, arguments, options = {})
|
42
|
+
proxy_target = get_proxy(target)
|
43
|
+
transport = @executor.transport(proxy_target.protocol)
|
44
|
+
arguments = arguments.merge('_target' => target.to_h.reject { |_, v| v.nil? })
|
45
|
+
|
46
|
+
remote_task = Bolt::Task::Remote.new(task.to_h)
|
47
|
+
|
48
|
+
result = transport.run_task(proxy_target, remote_task, arguments, options)
|
49
|
+
Bolt::Result.new(target, value: result.value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/bolt/transport/ssh.rb
CHANGED
@@ -121,11 +121,10 @@ module Bolt
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def run_task(target, task, arguments, options = {})
|
124
|
-
implementation =
|
124
|
+
implementation = select_implementation(target, task)
|
125
125
|
executable = implementation['path']
|
126
126
|
input_method = implementation['input_method']
|
127
127
|
extra_files = implementation['files']
|
128
|
-
input_method ||= 'both'
|
129
128
|
|
130
129
|
# unpack any Sensitive data
|
131
130
|
arguments = unwrap_sensitive_args(arguments)
|
data/lib/bolt/transport/winrm.rb
CHANGED
@@ -2,14 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'bolt/node/errors'
|
4
4
|
require 'bolt/transport/base'
|
5
|
+
require 'bolt/transport/powershell'
|
5
6
|
|
6
7
|
module Bolt
|
7
8
|
module Transport
|
8
9
|
class WinRM < Base
|
9
|
-
PS_ARGS = %w[
|
10
|
-
-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
|
11
|
-
].freeze
|
12
|
-
|
13
10
|
def self.options
|
14
11
|
%w[port user password connect-timeout ssl ssl-verify tmpdir cacert extensions]
|
15
12
|
end
|
@@ -18,6 +15,11 @@ module Bolt
|
|
18
15
|
['powershell']
|
19
16
|
end
|
20
17
|
|
18
|
+
def default_input_method(executable)
|
19
|
+
input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
|
20
|
+
input_method
|
21
|
+
end
|
22
|
+
|
21
23
|
def self.validate(options)
|
22
24
|
ssl_flag = options['ssl']
|
23
25
|
unless !!ssl_flag == ssl_flag
|
@@ -74,34 +76,14 @@ module Bolt
|
|
74
76
|
def run_script(target, script, arguments, _options = {})
|
75
77
|
# unpack any Sensitive data
|
76
78
|
arguments = unwrap_sensitive_args(arguments)
|
77
|
-
|
78
79
|
with_connection(target) do |conn|
|
79
80
|
conn.with_remote_tempdir do |dir|
|
80
81
|
remote_path = conn.write_remote_executable(dir, script)
|
81
|
-
if powershell_file?(remote_path)
|
82
|
-
|
83
|
-
"$invokeArgs.ArgumentList += @'\n#{a}\n'@"
|
84
|
-
end.join("\n")
|
85
|
-
output = conn.execute(<<-PS)
|
86
|
-
$invokeArgs = @{
|
87
|
-
ScriptBlock = (Get-Command "#{remote_path}").ScriptBlock
|
88
|
-
ArgumentList = @()
|
89
|
-
}
|
90
|
-
#{mapped_args}
|
91
|
-
|
92
|
-
try
|
93
|
-
{
|
94
|
-
Invoke-Command @invokeArgs
|
95
|
-
}
|
96
|
-
catch
|
97
|
-
{
|
98
|
-
Write-Error $_.Exception
|
99
|
-
exit 1
|
100
|
-
}
|
101
|
-
PS
|
82
|
+
if Powershell.powershell_file?(remote_path)
|
83
|
+
output = conn.execute(Powershell.run_script(arguments, remote_path))
|
102
84
|
else
|
103
|
-
path, args = *process_from_extension(remote_path)
|
104
|
-
args += escape_arguments(arguments)
|
85
|
+
path, args = *Powershell.process_from_extension(remote_path)
|
86
|
+
args += Powershell.escape_arguments(arguments)
|
105
87
|
output = conn.execute_process(path, args)
|
106
88
|
end
|
107
89
|
Bolt::Result.for_command(target, output.stdout.string, output.stderr.string, output.exit_code)
|
@@ -110,11 +92,11 @@ catch
|
|
110
92
|
end
|
111
93
|
|
112
94
|
def run_task(target, task, arguments, _options = {})
|
113
|
-
implementation =
|
95
|
+
implementation = select_implementation(target, task)
|
114
96
|
executable = implementation['path']
|
115
97
|
input_method = implementation['input_method']
|
116
98
|
extra_files = implementation['files']
|
117
|
-
input_method ||= powershell_file?(executable) ? 'powershell' : 'both'
|
99
|
+
input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
|
118
100
|
|
119
101
|
# unpack any Sensitive data
|
120
102
|
arguments = unwrap_sensitive_args(arguments)
|
@@ -140,7 +122,7 @@ catch
|
|
140
122
|
|
141
123
|
if ENVIRONMENT_METHODS.include?(input_method)
|
142
124
|
envify_params(arguments).each do |(arg, val)|
|
143
|
-
cmd =
|
125
|
+
cmd = Powershell.set_env(arg, val)
|
144
126
|
result = conn.execute(cmd)
|
145
127
|
if result.exit_code != 0
|
146
128
|
raise Bolt::Node::EnvironmentVarError.new(arg, val)
|
@@ -150,25 +132,10 @@ catch
|
|
150
132
|
|
151
133
|
conn.shell_init
|
152
134
|
output =
|
153
|
-
if powershell_file?(remote_task_path) && stdin.nil?
|
154
|
-
|
155
|
-
# must create new powershell.exe process like other interpreters
|
156
|
-
# fortunately, using PS with stdin input_method should never happen
|
157
|
-
if input_method == 'powershell'
|
158
|
-
conn.execute(<<-PS)
|
159
|
-
$private:tempArgs = Get-ContentAsJson (
|
160
|
-
$utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
161
|
-
)
|
162
|
-
$allowedArgs = (Get-Command "#{remote_task_path}").Parameters.Keys
|
163
|
-
$private:taskArgs = @{}
|
164
|
-
$private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
|
165
|
-
try { & "#{remote_task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
|
166
|
-
PS
|
167
|
-
else
|
168
|
-
conn.execute(%(try { & "#{remote_task_path}" } catch { Write-Error $_.Exception; exit 1 }))
|
169
|
-
end
|
135
|
+
if Powershell.powershell_file?(remote_task_path) && stdin.nil?
|
136
|
+
conn.execute(Powershell.run_ps_task(arguments, remote_task_path, input_method))
|
170
137
|
else
|
171
|
-
path, args = *process_from_extension(remote_task_path)
|
138
|
+
path, args = *Powershell.process_from_extension(remote_task_path)
|
172
139
|
conn.execute_process(path, args, stdin)
|
173
140
|
end
|
174
141
|
|
@@ -179,46 +146,6 @@ try { & "#{remote_task_path}" @taskArgs } catch { Write-Error $_.Exception; exit
|
|
179
146
|
end
|
180
147
|
end
|
181
148
|
|
182
|
-
def powershell_file?(path)
|
183
|
-
Pathname(path).extname.casecmp('.ps1').zero?
|
184
|
-
end
|
185
|
-
|
186
|
-
def process_from_extension(path)
|
187
|
-
case Pathname(path).extname.downcase
|
188
|
-
when '.rb'
|
189
|
-
[
|
190
|
-
'ruby.exe',
|
191
|
-
['-S', "\"#{path}\""]
|
192
|
-
]
|
193
|
-
when '.ps1'
|
194
|
-
[
|
195
|
-
'powershell.exe',
|
196
|
-
[*PS_ARGS, '-File', "\"#{path}\""]
|
197
|
-
]
|
198
|
-
when '.pp'
|
199
|
-
[
|
200
|
-
'puppet.bat',
|
201
|
-
['apply', "\"#{path}\""]
|
202
|
-
]
|
203
|
-
else
|
204
|
-
# Run the script via cmd, letting Windows extension handling determine how
|
205
|
-
[
|
206
|
-
'cmd.exe',
|
207
|
-
['/c', "\"#{path}\""]
|
208
|
-
]
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def escape_arguments(arguments)
|
213
|
-
arguments.map do |arg|
|
214
|
-
if arg =~ / /
|
215
|
-
"\"#{arg}\""
|
216
|
-
else
|
217
|
-
arg
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
149
|
def connected?(target)
|
223
150
|
with_connection(target) { true }
|
224
151
|
rescue Bolt::Node::ConnectError
|