bolt 0.9.0 → 0.10.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/lib/bolt/cli.rb +20 -37
- data/lib/bolt/config.rb +115 -12
- data/lib/bolt/node.rb +11 -9
- data/lib/bolt/node/orch.rb +4 -0
- data/lib/bolt/node/ssh.rb +4 -0
- data/lib/bolt/node/winrm.rb +204 -9
- data/lib/bolt/node_uri.rb +1 -1
- data/lib/bolt/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4e27492b22291a233b5fb9dfbb8e8648a15ce19
|
4
|
+
data.tar.gz: 9b2d163aa823b02cd7b6cfc6031846841d7c7b30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3d53aafec9ab3aa09d30dd5d45624aa74a20835373c70f174508f4986c33470b5b45350f40014e534b18f59a6267303395f2075cdb1269231b112c8d9ec1765
|
7
|
+
data.tar.gz: 664b5acf2aa442bf7e24dd76b61723819a4eb0523b34118bd0976f6c696db03319e979ad40521af7334bebf06cdd545b071b3eef87047b0b5ced11988f6e49a5
|
data/lib/bolt/cli.rb
CHANGED
@@ -97,9 +97,7 @@ HELP
|
|
97
97
|
def initialize(argv)
|
98
98
|
@argv = argv
|
99
99
|
@options = {
|
100
|
-
nodes: []
|
101
|
-
insecure: false,
|
102
|
-
transport: 'ssh'
|
100
|
+
nodes: []
|
103
101
|
}
|
104
102
|
@config = Bolt::Config.new
|
105
103
|
@parser = create_option_parser(@options)
|
@@ -137,7 +135,6 @@ HELP
|
|
137
135
|
results[:password] = password
|
138
136
|
end
|
139
137
|
end
|
140
|
-
results[:concurrency] = 100
|
141
138
|
opts.on('--private-key KEY',
|
142
139
|
"Private ssh key to authenticate with (Optional)") do |key|
|
143
140
|
results[:key] = key
|
@@ -157,16 +154,10 @@ HELP
|
|
157
154
|
results[:task_options] = parse_params(params)
|
158
155
|
end
|
159
156
|
|
160
|
-
results[:format] = 'human'
|
161
157
|
opts.on('--format FORMAT',
|
162
158
|
"Output format to use: human or json") do |format|
|
163
|
-
|
164
|
-
results[:format] = format
|
165
|
-
else
|
166
|
-
raise Bolt::CLIError, "Unsupported format: #{format}"
|
167
|
-
end
|
159
|
+
results[:format] = format
|
168
160
|
end
|
169
|
-
results[:insecure] = false
|
170
161
|
opts.on('-k', '--insecure',
|
171
162
|
"Whether to connect insecurely ") do |insecure|
|
172
163
|
results[:insecure] = insecure
|
@@ -183,10 +174,6 @@ HELP
|
|
183
174
|
"Program to execute for privilege escalation. " \
|
184
175
|
"Currently only sudo is supported.") do |program|
|
185
176
|
options[:sudo] = program || 'sudo'
|
186
|
-
if options[:sudo] != 'sudo'
|
187
|
-
raise Bolt::CLIError,
|
188
|
-
"Only 'sudo' is supported for privilege escalation."
|
189
|
-
end
|
190
177
|
end
|
191
178
|
opts.on('--sudo-password [PASSWORD]',
|
192
179
|
'Password for privilege escalation') do |password|
|
@@ -198,6 +185,10 @@ HELP
|
|
198
185
|
results[:sudo_password] = password
|
199
186
|
end
|
200
187
|
end
|
188
|
+
opts.on('--configfile CONFIG_PATH',
|
189
|
+
'Specify where to load the config file from') do |path|
|
190
|
+
results[:configfile] = path
|
191
|
+
end
|
201
192
|
opts.on_tail('--[no-]tty',
|
202
193
|
"Request a pseudo TTY on nodes that support it") do |tty|
|
203
194
|
results[:tty] = tty
|
@@ -227,6 +218,7 @@ HELP
|
|
227
218
|
parser.permute(@argv)
|
228
219
|
end
|
229
220
|
|
221
|
+
# Shortcut to handle help before other errors may be generated
|
230
222
|
options[:mode] = remaining.shift
|
231
223
|
|
232
224
|
if options[:mode] == 'help'
|
@@ -234,24 +226,20 @@ HELP
|
|
234
226
|
options[:mode] = remaining.shift
|
235
227
|
end
|
236
228
|
|
237
|
-
options[:action] = remaining.shift
|
238
|
-
options[:object] = remaining.shift
|
239
|
-
|
240
|
-
if options[:debug]
|
241
|
-
@config[:log_level] = Logger::DEBUG
|
242
|
-
elsif options[:verbose]
|
243
|
-
@config[:log_level] = Logger::INFO
|
244
|
-
end
|
245
|
-
|
246
|
-
@config[:key] = options[:key]
|
247
|
-
|
248
|
-
@config[:format] = options[:format]
|
249
|
-
|
250
229
|
if options[:help]
|
251
230
|
print_help(options[:mode])
|
252
231
|
raise Bolt::CLIExit
|
253
232
|
end
|
254
233
|
|
234
|
+
@config.load_file(options[:configfile])
|
235
|
+
@config.update_from_cli(options)
|
236
|
+
@config.validate
|
237
|
+
|
238
|
+
# This section handles parsing non-flag options which are
|
239
|
+
# mode specific rather then part of the config
|
240
|
+
options[:action] = remaining.shift
|
241
|
+
options[:object] = remaining.shift
|
242
|
+
|
255
243
|
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
256
244
|
if options[:task_options]
|
257
245
|
unless task_options.empty?
|
@@ -356,7 +344,7 @@ HELP
|
|
356
344
|
raise Bolt::CLIError, "Option '--nodes' must be specified"
|
357
345
|
end
|
358
346
|
|
359
|
-
if %w[task plan].include?(options[:mode]) &&
|
347
|
+
if %w[task plan].include?(options[:mode]) && @config[:modulepath].nil?
|
360
348
|
raise Bolt::CLIError,
|
361
349
|
"Option '--modulepath' must be specified when running" \
|
362
350
|
" a task or plan"
|
@@ -372,11 +360,6 @@ HELP
|
|
372
360
|
end
|
373
361
|
|
374
362
|
def execute(options)
|
375
|
-
%i[concurrency user password tty insecure transport
|
376
|
-
sudo sudo_password run_as].each do |key|
|
377
|
-
config[key] = options[key]
|
378
|
-
end
|
379
|
-
|
380
363
|
if options[:mode] == 'plan' || options[:mode] == 'task'
|
381
364
|
begin
|
382
365
|
require_relative '../../vendored/require_vendored'
|
@@ -420,7 +403,7 @@ HELP
|
|
420
403
|
when 'task'
|
421
404
|
task_name = options[:object]
|
422
405
|
|
423
|
-
path, metadata = load_task_data(task_name,
|
406
|
+
path, metadata = load_task_data(task_name, @config[:modulepath])
|
424
407
|
input_method = metadata['input_method']
|
425
408
|
|
426
409
|
input_method ||= 'both'
|
@@ -455,7 +438,7 @@ HELP
|
|
455
438
|
result = Puppet.override(bolt_executor: executor) do
|
456
439
|
run_plan(options[:object],
|
457
440
|
options[:task_options],
|
458
|
-
|
441
|
+
@config[:modulepath])
|
459
442
|
end
|
460
443
|
outputter.print_plan(result)
|
461
444
|
rescue Puppet::Error
|
@@ -527,7 +510,7 @@ HELP
|
|
527
510
|
cli << "--#{setting}" << dir
|
528
511
|
end
|
529
512
|
Puppet.initialize_settings(cli)
|
530
|
-
Puppet::Pal.in_tmp_environment('bolt', modulepath: [BOLTLIB_PATH] + modulepath) do |pal|
|
513
|
+
Puppet::Pal.in_tmp_environment('bolt', modulepath: [BOLTLIB_PATH] + modulepath, facts: {}) do |pal|
|
531
514
|
pal.with_script_compiler do |compiler|
|
532
515
|
compiler.call_function('run_plan', plan, args)
|
533
516
|
end
|
data/lib/bolt/config.rb
CHANGED
@@ -1,37 +1,140 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'yaml'
|
2
3
|
|
3
4
|
module Bolt
|
4
5
|
Config = Struct.new(
|
5
6
|
:concurrency,
|
6
7
|
:format,
|
7
|
-
:insecure,
|
8
8
|
:log_destination,
|
9
9
|
:log_level,
|
10
|
-
:
|
11
|
-
:run_as,
|
12
|
-
:sudo,
|
13
|
-
:sudo_password,
|
10
|
+
:modulepath,
|
14
11
|
:transport,
|
15
|
-
:
|
16
|
-
:tty,
|
17
|
-
:user
|
12
|
+
:transports
|
18
13
|
) do
|
14
|
+
|
19
15
|
DEFAULTS = {
|
20
16
|
concurrency: 100,
|
21
|
-
tty: false,
|
22
|
-
insecure: false,
|
23
17
|
transport: 'ssh',
|
18
|
+
format: 'human',
|
24
19
|
log_level: Logger::WARN,
|
25
20
|
log_destination: STDERR
|
26
21
|
}.freeze
|
27
22
|
|
23
|
+
TRANSPORT_OPTIONS = %i[insecure password run_as sudo sudo_password key tty user].freeze
|
24
|
+
|
25
|
+
TRANSPORT_DEFAULTS = {
|
26
|
+
insecure: false,
|
27
|
+
tty: false
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
TRANSPORTS = %i[ssh winrm pcp].freeze
|
31
|
+
|
28
32
|
def initialize(**kwargs)
|
29
33
|
super()
|
30
34
|
DEFAULTS.merge(kwargs).each { |k, v| self[k] = v }
|
35
|
+
|
36
|
+
self[:transports] ||= {}
|
37
|
+
TRANSPORTS.each do |transport|
|
38
|
+
unless self[:transports][transport]
|
39
|
+
self[:transports][transport] = {}
|
40
|
+
end
|
41
|
+
TRANSPORT_DEFAULTS.each do |k, v|
|
42
|
+
unless self[:transports][transport][k]
|
43
|
+
self[:transports][transport][k] = v
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_path
|
50
|
+
path = ['.puppetlabs', 'bolt.yml']
|
51
|
+
root_path = '~'
|
52
|
+
File.join(root_path, *path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_config_file(path)
|
56
|
+
path_passed = path
|
57
|
+
path ||= default_path
|
58
|
+
path = File.expand_path(path)
|
59
|
+
# safe_load doesn't work with psych in ruby 2.0
|
60
|
+
# The user controls the configfile so this isn't a problem
|
61
|
+
# rubocop:disable YAMLLoad
|
62
|
+
File.open(path, "r:UTF-8") { |f| YAML.load(f.read) }
|
63
|
+
rescue Errno::ENOENT
|
64
|
+
if path_passed
|
65
|
+
raise Bolt::CLIError, "Could not read config file: #{path}"
|
66
|
+
end
|
67
|
+
# In older releases of psych SyntaxError is not a subclass of Exception
|
68
|
+
rescue Psych::SyntaxError
|
69
|
+
raise Bolt::CLIError, "Could not parse config file: #{path}"
|
70
|
+
rescue Psych::Exception
|
71
|
+
raise Bolt::CLIError, "Could not parse config file: #{path}"
|
72
|
+
rescue IOError, SystemCallError
|
73
|
+
raise Bolt::CLIError, "Could not read config file: #{path}"
|
31
74
|
end
|
32
75
|
|
33
|
-
def
|
34
|
-
|
76
|
+
def update_from_file(data)
|
77
|
+
if data['modulepath']
|
78
|
+
self[:modulepath] = data['modulepath'].split(File::PATH_SEPARATOR)
|
79
|
+
end
|
80
|
+
|
81
|
+
if data['concurrency']
|
82
|
+
self[:concurrency] = data['concurrency']
|
83
|
+
end
|
84
|
+
|
85
|
+
if data['format']
|
86
|
+
self[:format] = data['format'] if data['format']
|
87
|
+
end
|
88
|
+
|
89
|
+
if data['ssh']
|
90
|
+
if data['ssh']['private-key']
|
91
|
+
self[:transports][:ssh][:key] = data['ssh']['private-key']
|
92
|
+
end
|
93
|
+
if data['ssh']['insecure']
|
94
|
+
self[:transports][:ssh][:insecure] = data['ssh']['insecure']
|
95
|
+
end
|
96
|
+
end
|
97
|
+
# if data['pcp']
|
98
|
+
# end
|
99
|
+
# if data['winrm']
|
100
|
+
# end
|
101
|
+
end
|
102
|
+
|
103
|
+
def load_file(path)
|
104
|
+
data = read_config_file(path)
|
105
|
+
update_from_file(data) if data
|
106
|
+
end
|
107
|
+
|
108
|
+
def update_from_cli(options)
|
109
|
+
%i[concurrency transport format modulepath].each do |key|
|
110
|
+
self[key] = options[key] if options[key]
|
111
|
+
end
|
112
|
+
|
113
|
+
if options[:debug]
|
114
|
+
self[:log_level] = Logger::DEBUG
|
115
|
+
elsif options[:verbose]
|
116
|
+
self[:log_level] = Logger::INFO
|
117
|
+
end
|
118
|
+
|
119
|
+
TRANSPORT_OPTIONS.each do |key|
|
120
|
+
# TODO: We should eventually make these transport specific
|
121
|
+
TRANSPORTS.each do |transport|
|
122
|
+
self[:transports][transport][key] = options[key] if options[key]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate
|
128
|
+
TRANSPORTS.each do |transport|
|
129
|
+
tconf = self[:transports][transport]
|
130
|
+
if tconf[:sudo] && tconf[:sudo] != 'sudo'
|
131
|
+
raise Bolt::CLIError, "Only 'sudo' is supported for privilege escalation."
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
unless %w[human json].include? self[:format]
|
136
|
+
raise Bolt::CLIError, "Unsupported format: '#{self[:format]}'"
|
137
|
+
end
|
35
138
|
end
|
36
139
|
end
|
37
140
|
end
|
data/lib/bolt/node.rb
CHANGED
@@ -35,15 +35,17 @@ module Bolt
|
|
35
35
|
config: Bolt::Config.new)
|
36
36
|
@host = host
|
37
37
|
@port = port
|
38
|
-
@user = user || config[:user]
|
39
|
-
@password = password || config[:password]
|
40
|
-
@key = config[:key]
|
41
|
-
@tty = config[:tty]
|
42
|
-
@insecure = config[:insecure]
|
43
38
|
@uri = uri
|
44
|
-
|
45
|
-
|
46
|
-
@
|
39
|
+
|
40
|
+
transport_conf = config[:transports][protocol.to_sym]
|
41
|
+
@user = user || transport_conf[:user]
|
42
|
+
@password = password || transport_conf[:password]
|
43
|
+
@key = transport_conf[:key]
|
44
|
+
@tty = transport_conf[:tty]
|
45
|
+
@insecure = transport_conf[:insecure]
|
46
|
+
@sudo = transport_conf[:sudo]
|
47
|
+
@sudo_password = transport_conf[:sudo_password]
|
48
|
+
@run_as = transport_conf[:run_as]
|
47
49
|
|
48
50
|
@logger = init_logger(config[:log_destination], config[:log_level])
|
49
51
|
@transport_logger = init_logger(config[:log_destination], Logger::WARN)
|
@@ -73,7 +75,7 @@ module Bolt
|
|
73
75
|
end
|
74
76
|
|
75
77
|
def run_script(script, arguments)
|
76
|
-
@logger.info { "Running script: #{
|
78
|
+
@logger.info { "Running script: #{script}" }
|
77
79
|
_run_script(script, arguments)
|
78
80
|
end
|
79
81
|
|
data/lib/bolt/node/orch.rb
CHANGED
data/lib/bolt/node/ssh.rb
CHANGED
data/lib/bolt/node/winrm.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
require 'winrm'
|
2
2
|
require 'winrm-fs'
|
3
3
|
require 'bolt/result'
|
4
|
+
require 'base64'
|
4
5
|
|
5
6
|
module Bolt
|
6
7
|
class WinRM < Node
|
8
|
+
def protocol
|
9
|
+
'winrm'
|
10
|
+
end
|
11
|
+
|
7
12
|
def initialize(host, port, user, password, shell: :powershell, **kwargs)
|
8
13
|
super(host, port, user, password, **kwargs)
|
9
14
|
|
@@ -48,6 +53,9 @@ $ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
|
48
53
|
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
|
49
54
|
$ENV:RUBYLIB
|
50
55
|
|
56
|
+
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
57
|
+
$utf8 = [System.Text.Encoding]::UTF8
|
58
|
+
|
51
59
|
function Invoke-Interpreter
|
52
60
|
{
|
53
61
|
[CmdletBinding()]
|
@@ -140,6 +148,183 @@ function Invoke-Interpreter
|
|
140
148
|
}
|
141
149
|
}
|
142
150
|
}
|
151
|
+
|
152
|
+
function Write-Stream {
|
153
|
+
PARAM(
|
154
|
+
[Parameter(Position=0)] $stream,
|
155
|
+
[Parameter(ValueFromPipeline=$true)] $string
|
156
|
+
)
|
157
|
+
PROCESS {
|
158
|
+
$bytes = $utf8.GetBytes($string)
|
159
|
+
$stream.Write( $bytes, 0, $bytes.Length )
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
function Convert-JsonToXml {
|
164
|
+
PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
|
165
|
+
BEGIN {
|
166
|
+
$mStream = New-Object System.IO.MemoryStream
|
167
|
+
}
|
168
|
+
PROCESS {
|
169
|
+
$json | Write-Stream -Stream $mStream
|
170
|
+
}
|
171
|
+
END {
|
172
|
+
$mStream.Position = 0
|
173
|
+
try {
|
174
|
+
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
|
175
|
+
$xml = New-Object Xml.XmlDocument
|
176
|
+
$xml.Load($jsonReader)
|
177
|
+
$xml
|
178
|
+
} finally {
|
179
|
+
$jsonReader.Close()
|
180
|
+
$mStream.Dispose()
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
Function ConvertFrom-Xml {
|
186
|
+
[CmdletBinding(DefaultParameterSetName="AutoType")]
|
187
|
+
PARAM(
|
188
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
|
189
|
+
[Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
|
190
|
+
[Switch] $ForceType
|
191
|
+
)
|
192
|
+
PROCESS{
|
193
|
+
if (Get-Member -InputObject $xml -Name root) {
|
194
|
+
return $xml.root.Objects | ConvertFrom-Xml
|
195
|
+
} elseif (Get-Member -InputObject $xml -Name Objects) {
|
196
|
+
return $xml.Objects | ConvertFrom-Xml
|
197
|
+
}
|
198
|
+
$propbag = @{}
|
199
|
+
foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
|
200
|
+
Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
|
201
|
+
$propbag."$Name" = Convert-Properties $xml."$name"
|
202
|
+
}
|
203
|
+
if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
|
204
|
+
if ($ForceType -and $Type) {
|
205
|
+
try {
|
206
|
+
$output = New-Object $Type -Property $propbag
|
207
|
+
} catch {
|
208
|
+
$output = New-Object PSObject -Property $propbag
|
209
|
+
$output.PsTypeNames.Insert(0, $xml.__type)
|
210
|
+
}
|
211
|
+
} elseif ($propbag.Count -ne 0) {
|
212
|
+
$output = New-Object PSObject -Property $propbag
|
213
|
+
if ($Type) {
|
214
|
+
$output.PsTypeNames.Insert(0, $Type)
|
215
|
+
}
|
216
|
+
}
|
217
|
+
return $output
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
Function Convert-Properties {
|
222
|
+
PARAM($InputObject)
|
223
|
+
switch ($InputObject.type) {
|
224
|
+
"object" {
|
225
|
+
return (ConvertFrom-Xml -Xml $InputObject)
|
226
|
+
}
|
227
|
+
"string" {
|
228
|
+
$MightBeADate = $InputObject.get_InnerText() -as [DateTime]
|
229
|
+
## Strings that are actually dates (*grumble* JSON is crap)
|
230
|
+
if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
|
231
|
+
return $MightBeADate
|
232
|
+
} else {
|
233
|
+
return $InputObject.get_InnerText()
|
234
|
+
}
|
235
|
+
}
|
236
|
+
"number" {
|
237
|
+
$number = $InputObject.get_InnerText()
|
238
|
+
if ($number -eq ($number -as [int])) {
|
239
|
+
return $number -as [int]
|
240
|
+
} elseif ($number -eq ($number -as [double])) {
|
241
|
+
return $number -as [double]
|
242
|
+
} else {
|
243
|
+
return $number -as [decimal]
|
244
|
+
}
|
245
|
+
}
|
246
|
+
"boolean" {
|
247
|
+
return [bool]::parse($InputObject.get_InnerText())
|
248
|
+
}
|
249
|
+
"null" {
|
250
|
+
return $null
|
251
|
+
}
|
252
|
+
"array" {
|
253
|
+
[object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
|
254
|
+
Convert-Properties $item
|
255
|
+
})
|
256
|
+
return $Items
|
257
|
+
}
|
258
|
+
default {
|
259
|
+
return $InputObject
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
Function ConvertFrom-Json2 {
|
265
|
+
[CmdletBinding()]
|
266
|
+
PARAM(
|
267
|
+
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
|
268
|
+
[Parameter(Mandatory=$true)] [Type] $Type,
|
269
|
+
[Switch] $ForceType
|
270
|
+
)
|
271
|
+
PROCESS {
|
272
|
+
$null = $PSBoundParameters.Remove("InputObject")
|
273
|
+
[Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
|
274
|
+
if ($xml) {
|
275
|
+
if ($xml.Objects) {
|
276
|
+
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
|
277
|
+
} elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
|
278
|
+
$xml.Item | ConvertFrom-Xml @PSBoundParameters
|
279
|
+
} else {
|
280
|
+
$xml | ConvertFrom-Xml @PSBoundParameters
|
281
|
+
}
|
282
|
+
} else {
|
283
|
+
Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}
|
287
|
+
|
288
|
+
function ConvertFrom-PSCustomObject
|
289
|
+
{
|
290
|
+
PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
|
291
|
+
PROCESS {
|
292
|
+
if ($null -eq $InputObject) { return $null }
|
293
|
+
|
294
|
+
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
|
295
|
+
$collection = @(
|
296
|
+
foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
|
297
|
+
)
|
298
|
+
|
299
|
+
$collection
|
300
|
+
} elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
|
301
|
+
$hash = @{}
|
302
|
+
foreach ($property in $InputObject.PSObject.Properties) {
|
303
|
+
$hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
|
304
|
+
}
|
305
|
+
|
306
|
+
$hash
|
307
|
+
} else {
|
308
|
+
$InputObject
|
309
|
+
}
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
function Get-ContentAsJson
|
314
|
+
{
|
315
|
+
[CmdletBinding()]
|
316
|
+
PARAM(
|
317
|
+
[Parameter(Mandatory = $true)] $Text,
|
318
|
+
[Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
|
319
|
+
)
|
320
|
+
|
321
|
+
# using polyfill cmdlet on PS2, so pass type info
|
322
|
+
if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
|
323
|
+
$Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
|
324
|
+
} else {
|
325
|
+
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
326
|
+
}
|
327
|
+
}
|
143
328
|
PS
|
144
329
|
if result.exit_code != 0
|
145
330
|
raise BaseError.new("Could not initialize shell: #{result.stderr.string}", "SHELL_INIT_ERROR")
|
@@ -333,15 +518,25 @@ catch
|
|
333
518
|
end
|
334
519
|
|
335
520
|
with_remote_file(task) do |remote_path|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
521
|
+
output =
|
522
|
+
if powershell_file?(remote_path) && stdin.nil?
|
523
|
+
# NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
|
524
|
+
# must create new powershell.exe process like other interpreters
|
525
|
+
# fortunately, using PS with stdin input_method should never happen
|
526
|
+
if input_method == 'powershell'
|
527
|
+
execute(<<-PS)
|
528
|
+
$private:taskArgs = Get-ContentAsJson (
|
529
|
+
$utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
530
|
+
)
|
531
|
+
try { & "#{remote_path}" @taskArgs } catch { exit 1 }
|
532
|
+
PS
|
533
|
+
else
|
534
|
+
execute(%(try { & "#{remote_path}" } catch { exit 1 }))
|
535
|
+
end
|
536
|
+
else
|
537
|
+
path, args = *process_from_extension(remote_path)
|
538
|
+
execute_process(path, args, stdin)
|
539
|
+
end
|
345
540
|
Bolt::TaskResult.from_output(output)
|
346
541
|
end
|
347
542
|
# TODO: we should rely on the executor for this
|
data/lib/bolt/node_uri.rb
CHANGED
data/lib/bolt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|