bolt 2.42.0 → 3.3.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/Puppetfile +21 -19
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -5
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +7 -3
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
- data/lib/bolt/analytics.rb +3 -2
- data/lib/bolt/applicator.rb +11 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +9 -116
- data/lib/bolt/catalog.rb +10 -29
- data/lib/bolt/cli.rb +90 -154
- data/lib/bolt/config.rb +66 -239
- data/lib/bolt/config/options.rb +79 -102
- data/lib/bolt/config/transport/local.rb +1 -0
- data/lib/bolt/config/transport/lxd.rb +21 -0
- data/lib/bolt/config/transport/options.rb +9 -2
- data/lib/bolt/config/transport/orch.rb +1 -0
- data/lib/bolt/executor.rb +23 -6
- data/lib/bolt/inventory.rb +1 -1
- data/lib/bolt/inventory/group.rb +7 -4
- data/lib/bolt/logger.rb +123 -11
- data/lib/bolt/module_installer.rb +6 -4
- data/lib/bolt/module_installer/puppetfile.rb +2 -2
- data/lib/bolt/module_installer/resolver.rb +59 -14
- data/lib/bolt/module_installer/specs/forge_spec.rb +10 -4
- data/lib/bolt/module_installer/specs/git_spec.rb +19 -4
- data/lib/bolt/outputter/human.rb +56 -17
- data/lib/bolt/outputter/json.rb +16 -16
- data/lib/bolt/outputter/rainbow.rb +3 -3
- data/lib/bolt/pal.rb +95 -15
- data/lib/bolt/pal/yaml_plan.rb +9 -4
- data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -153
- data/lib/bolt/pal/yaml_plan/step.rb +91 -52
- data/lib/bolt/pal/yaml_plan/step/command.rb +16 -16
- data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
- data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
- data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
- data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
- data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
- data/lib/bolt/pal/yaml_plan/step/script.rb +32 -17
- data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
- data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
- data/lib/bolt/pal/yaml_plan/transpiler.rb +2 -1
- data/lib/bolt/plan_creator.rb +1 -1
- data/lib/bolt/plugin.rb +2 -2
- data/lib/bolt/plugin/cache.rb +7 -7
- data/lib/bolt/plugin/module.rb +0 -23
- data/lib/bolt/plugin/puppet_connect_data.rb +77 -0
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +54 -81
- data/lib/bolt/project_manager.rb +5 -4
- data/lib/bolt/project_manager/module_migrator.rb +7 -6
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +6 -1
- data/lib/bolt/shell.rb +16 -0
- data/lib/bolt/shell/bash.rb +57 -25
- data/lib/bolt/shell/bash/tmpdir.rb +6 -3
- data/lib/bolt/shell/powershell.rb +33 -10
- data/lib/bolt/shell/powershell/snippets.rb +37 -150
- data/lib/bolt/task.rb +2 -2
- data/lib/bolt/transport/base.rb +0 -9
- data/lib/bolt/transport/docker.rb +1 -125
- data/lib/bolt/transport/docker/connection.rb +86 -161
- data/lib/bolt/transport/local.rb +1 -9
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/lxd/connection.rb +99 -0
- data/lib/bolt/transport/orch/connection.rb +1 -1
- data/lib/bolt/transport/ssh.rb +1 -2
- data/lib/bolt/transport/ssh/connection.rb +2 -2
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/validator.rb +2 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/transport_app.rb +61 -32
- data/lib/bolt_spec/bolt_context.rb +9 -4
- data/lib/bolt_spec/plans.rb +1 -109
- data/lib/bolt_spec/plans/action_stubs.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +4 -0
- data/libexec/bolt_catalog +1 -1
- data/modules/aggregate/plans/count.pp +21 -0
- data/modules/aggregate/plans/targets.pp +21 -0
- data/modules/puppet_connect/plans/test_input_data.pp +67 -0
- data/modules/puppetdb_fact/plans/init.pp +10 -0
- metadata +13 -9
- data/modules/aggregate/plans/nodes.pp +0 -36
data/lib/bolt/project_manager.rb
CHANGED
@@ -143,7 +143,7 @@ module Bolt
|
|
143
143
|
@outputter.print_message("Migrating project #{@config.project.path}\n\n")
|
144
144
|
|
145
145
|
@outputter.print_action_step(
|
146
|
-
"Migrating a Bolt project
|
146
|
+
"Migrating a Bolt project might make irreversible changes to the project's "\
|
147
147
|
"configuration and inventory files. Before continuing, make sure the "\
|
148
148
|
"project has a backup or uses a version control system."
|
149
149
|
)
|
@@ -166,10 +166,11 @@ module Bolt
|
|
166
166
|
# Migrates the project-level configuration file to the latest version.
|
167
167
|
#
|
168
168
|
private def migrate_config
|
169
|
-
migrator
|
169
|
+
migrator = ConfigMigrator.new(@outputter)
|
170
|
+
configfile = @config.project.path + 'bolt.yaml'
|
170
171
|
|
171
172
|
migrator.migrate(
|
172
|
-
|
173
|
+
configfile,
|
173
174
|
@config.project.project_file,
|
174
175
|
@config.inventoryfile || @config.project.inventory_file,
|
175
176
|
@config.project.backup_dir
|
@@ -194,7 +195,7 @@ module Bolt
|
|
194
195
|
|
195
196
|
migrator.migrate(
|
196
197
|
@config.project,
|
197
|
-
@config.modulepath
|
198
|
+
@config.modulepath[0...-1]
|
198
199
|
)
|
199
200
|
end
|
200
201
|
end
|
@@ -6,19 +6,20 @@ module Bolt
|
|
6
6
|
class ProjectManager
|
7
7
|
class ModuleMigrator < Migrator
|
8
8
|
def migrate(project, configured_modulepath)
|
9
|
-
return true
|
9
|
+
return true if project.managed_moduledir.exist?
|
10
10
|
|
11
11
|
@outputter.print_message "Migrating project modules\n\n"
|
12
12
|
|
13
13
|
config = project.project_file
|
14
14
|
puppetfile = project.puppetfile
|
15
15
|
managed_moduledir = project.managed_moduledir
|
16
|
-
|
16
|
+
new_modulepath = [(project.path + 'modules').to_s]
|
17
|
+
old_modulepath = [(project.path + 'modules').to_s,
|
17
18
|
(project.path + 'site-modules').to_s,
|
18
19
|
(project.path + 'site').to_s]
|
19
20
|
|
20
21
|
# Notify user to manually migrate modules if using non-default modulepath
|
21
|
-
if configured_modulepath !=
|
22
|
+
if configured_modulepath != new_modulepath && configured_modulepath != old_modulepath
|
22
23
|
@outputter.print_action_step(
|
23
24
|
"Project has a non-default configured modulepath, unable to automatically "\
|
24
25
|
"migrate project modules. To migrate project modules manually, see "\
|
@@ -27,10 +28,10 @@ module Bolt
|
|
27
28
|
true
|
28
29
|
# Migrate modules from Puppetfile
|
29
30
|
elsif File.exist?(puppetfile)
|
30
|
-
migrate_modules_from_puppetfile(config, puppetfile, managed_moduledir,
|
31
|
+
migrate_modules_from_puppetfile(config, puppetfile, managed_moduledir, old_modulepath)
|
31
32
|
# Migrate modules to updated modulepath
|
32
33
|
else
|
33
|
-
consolidate_modules(
|
34
|
+
consolidate_modules(old_modulepath)
|
34
35
|
update_project_config([], config)
|
35
36
|
end
|
36
37
|
end
|
@@ -65,7 +66,7 @@ module Bolt
|
|
65
66
|
# Attempt to resolve dependencies
|
66
67
|
begin
|
67
68
|
@outputter.print_message('')
|
68
|
-
@outputter.print_action_step("Resolving module dependencies, this
|
69
|
+
@outputter.print_action_step("Resolving module dependencies, this might take a moment")
|
69
70
|
puppetfile = Bolt::ModuleInstaller::Resolver.new.resolve(specs)
|
70
71
|
rescue Bolt::Error => e
|
71
72
|
@outputter.print_action_error("#{e.message}\nSkipping module migration.")
|
data/lib/bolt/rerun.rb
CHANGED
@@ -49,7 +49,7 @@ module Bolt
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
rescue StandardError => e
|
52
|
-
Bolt::Logger.warn_once(
|
52
|
+
Bolt::Logger.warn_once("unwriteable_file", "Failed to save result to #{@path}: #{e.message}")
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/bolt/result.rb
CHANGED
@@ -203,12 +203,17 @@ module Bolt
|
|
203
203
|
end
|
204
204
|
|
205
205
|
def to_data
|
206
|
+
serialized_value = safe_value
|
207
|
+
if serialized_value.key?('_sensitive') &&
|
208
|
+
serialized_value['_sensitive'].is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
|
209
|
+
serialized_value['_sensitive'] = serialized_value['_sensitive'].to_s
|
210
|
+
end
|
206
211
|
{
|
207
212
|
"target" => @target.name,
|
208
213
|
"action" => action,
|
209
214
|
"object" => object,
|
210
215
|
"status" => status,
|
211
|
-
"value" =>
|
216
|
+
"value" => serialized_value
|
212
217
|
}
|
213
218
|
end
|
214
219
|
|
data/lib/bolt/shell.rb
CHANGED
@@ -8,6 +8,22 @@ module Bolt
|
|
8
8
|
@target = target
|
9
9
|
@conn = conn
|
10
10
|
@logger = Bolt::Logger.logger(@target.safe_name)
|
11
|
+
|
12
|
+
if Bolt::Logger.stream
|
13
|
+
Bolt::Logger.warn_once("stream_experimental",
|
14
|
+
"The 'stream' option is experimental, and might "\
|
15
|
+
"include breaking changes between minor versions.")
|
16
|
+
@stream_logger = Bolt::Logger.logger(:stream)
|
17
|
+
# Don't send stream messages to the parent logger
|
18
|
+
@stream_logger.additive = false
|
19
|
+
|
20
|
+
# Log stream messages without any other data or color
|
21
|
+
pattern = Logging.layouts.pattern(pattern: '%m\n')
|
22
|
+
@stream_logger.appenders = Logging.appenders.stdout(
|
23
|
+
'console',
|
24
|
+
layout: pattern
|
25
|
+
)
|
26
|
+
end
|
11
27
|
end
|
12
28
|
|
13
29
|
def run_command(*_args)
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -12,7 +12,6 @@ module Bolt
|
|
12
12
|
super
|
13
13
|
|
14
14
|
@run_as = nil
|
15
|
-
|
16
15
|
@sudo_id = SecureRandom.uuid
|
17
16
|
@sudo_password = @target.options['sudo-password'] || @target.password
|
18
17
|
end
|
@@ -54,19 +53,35 @@ module Bolt
|
|
54
53
|
|
55
54
|
def download(source, destination, options = {})
|
56
55
|
running_as(options[:run_as]) do
|
57
|
-
# Target OS may be either Unix or Windows. Without knowing the target OS before-hand
|
58
|
-
# we can't assume whether the path separator is '/' or '\'. Assume we're connecting
|
59
|
-
# to a target with Unix and then check if the path exists after downloading.
|
60
56
|
download = File.join(destination, Bolt::Util.unix_basename(source))
|
61
57
|
|
62
|
-
|
58
|
+
# If using run-as, the file is copied to a tmpdir and chowned to the
|
59
|
+
# connecting user. This is a workaround for limitations in net-ssh that
|
60
|
+
# only allow for downloading files as the connecting user, which is a
|
61
|
+
# problem for users who cannot connect to targets as the root user.
|
62
|
+
# This temporary copy should *always* be deleted.
|
63
|
+
if run_as
|
64
|
+
with_tmpdir(force_cleanup: true) do |dir|
|
65
|
+
tmpfile = File.join(dir.to_s, Bolt::Util.unix_basename(source))
|
66
|
+
|
67
|
+
result = execute(['cp', '-r', source, dir.to_s], sudoable: true)
|
68
|
+
|
69
|
+
if result.exit_code != 0
|
70
|
+
message = "Could not copy file '#{source}' to temporary directory '#{dir}': #{result.stderr.string}"
|
71
|
+
raise Bolt::Node::FileError.new(message, 'CP_ERROR')
|
72
|
+
end
|
73
|
+
|
74
|
+
# We need to force the chown, otherwise this will just return
|
75
|
+
# without doing anything since the chown user is the same as the
|
76
|
+
# connecting user.
|
77
|
+
dir.chown(conn.user, force: true)
|
63
78
|
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
79
|
+
conn.download_file(tmpfile, destination, download)
|
80
|
+
end
|
81
|
+
# If not using run-as, we can skip creating a temporary copy and just
|
82
|
+
# download the file directly.
|
83
|
+
else
|
84
|
+
conn.download_file(source, destination, download)
|
70
85
|
end
|
71
86
|
|
72
87
|
Bolt::Result.for_download(target, source, destination, download)
|
@@ -145,7 +160,10 @@ module Bolt
|
|
145
160
|
|
146
161
|
dir.chown(run_as)
|
147
162
|
|
148
|
-
|
163
|
+
# Don't pass parameters on stdin if using a tty, as the parameters are
|
164
|
+
# already part of the wrapper script.
|
165
|
+
execute_options[:stdin] = stdin unless stdin && target.options['tty']
|
166
|
+
|
149
167
|
execute_options[:sudoable] = true if run_as
|
150
168
|
output = execute(remote_task_path, **execute_options)
|
151
169
|
end
|
@@ -291,15 +309,15 @@ module Bolt
|
|
291
309
|
|
292
310
|
# A helper to create and delete a tmpdir on the remote system. Yields the
|
293
311
|
# directory name.
|
294
|
-
def with_tmpdir
|
312
|
+
def with_tmpdir(force_cleanup: false)
|
295
313
|
dir = make_tmpdir
|
296
314
|
yield dir
|
297
315
|
ensure
|
298
316
|
if dir
|
299
|
-
if target.options['cleanup']
|
317
|
+
if target.options['cleanup'] || force_cleanup
|
300
318
|
dir.delete
|
301
319
|
else
|
302
|
-
|
320
|
+
Bolt::Logger.warn("skip_cleanup", "Skipping cleanup of tmpdir #{dir}")
|
303
321
|
end
|
304
322
|
end
|
305
323
|
end
|
@@ -331,10 +349,15 @@ module Bolt
|
|
331
349
|
# together multiple commands into a single sh invocation
|
332
350
|
commands = [inject_interpreter(options[:interpreter], command)]
|
333
351
|
|
352
|
+
# Let the transport handle adding environment variables if it's custom.
|
334
353
|
if options[:environment]
|
335
|
-
|
336
|
-
|
337
|
-
|
354
|
+
if defined? conn.add_env_vars
|
355
|
+
conn.add_env_vars(options[:environment])
|
356
|
+
else
|
357
|
+
env_decl = options[:environment].map do |env, val|
|
358
|
+
"#{env}=#{Shellwords.shellescape(val)}"
|
359
|
+
end.join(' ')
|
360
|
+
end
|
338
361
|
end
|
339
362
|
|
340
363
|
if escalate
|
@@ -385,14 +408,23 @@ module Bolt
|
|
385
408
|
# See if we can read from out or err, or write to in
|
386
409
|
ready_read, ready_write, = select(read_streams.keys, write_stream, nil, timeout)
|
387
410
|
|
388
|
-
# Read from out and err
|
389
411
|
ready_read&.each do |stream|
|
412
|
+
stream_name = stream == out ? 'out' : 'err'
|
390
413
|
# Check for sudo prompt
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
414
|
+
to_print = if use_sudo
|
415
|
+
check_sudo(stream, inp, options[:stdin])
|
416
|
+
else
|
417
|
+
stream.readpartial(CHUNK_SIZE)
|
418
|
+
end
|
419
|
+
|
420
|
+
if !to_print.chomp.empty? && @stream_logger
|
421
|
+
formatted = to_print.lines.map do |msg|
|
422
|
+
"[#{@target.safe_name}] #{stream_name}: #{msg.chomp}"
|
423
|
+
end.join("\n")
|
424
|
+
@stream_logger.warn(formatted)
|
425
|
+
end
|
426
|
+
|
427
|
+
read_streams[stream] << to_print
|
396
428
|
rescue EOFError
|
397
429
|
end
|
398
430
|
|
@@ -440,7 +472,7 @@ module Bolt
|
|
440
472
|
when 0
|
441
473
|
@logger.trace { "Command `#{command_str}` returned successfully" }
|
442
474
|
when 126
|
443
|
-
msg = "\n\nThis
|
475
|
+
msg = "\n\nThis might be caused by the default tmpdir being mounted "\
|
444
476
|
"using 'noexec'. See http://pup.pt/task-failure for details and workarounds."
|
445
477
|
result_output.stderr << msg
|
446
478
|
@logger.trace { "Command #{command_str} failed with exit code #{result_output.exit_code}" }
|
@@ -24,8 +24,8 @@ module Bolt
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def chown(owner)
|
28
|
-
return if owner.nil? || owner == @owner
|
27
|
+
def chown(owner, force: false)
|
28
|
+
return if owner.nil? || (owner == @owner && !force)
|
29
29
|
|
30
30
|
result = @shell.execute(['id', '-g', owner])
|
31
31
|
if result.exit_code != 0
|
@@ -48,7 +48,10 @@ module Bolt
|
|
48
48
|
def delete
|
49
49
|
result = @shell.execute(['rm', '-rf', @path], sudoable: true, run_as: @owner)
|
50
50
|
if result.exit_code != 0
|
51
|
-
|
51
|
+
Bolt::Logger.warn(
|
52
|
+
"fail_cleanup",
|
53
|
+
"Failed to clean up tmpdir '#{@path}': #{result.stderr.string}"
|
54
|
+
)
|
52
55
|
end
|
53
56
|
# For testing
|
54
57
|
result.stderr.string
|
@@ -23,11 +23,10 @@ module Bolt
|
|
23
23
|
# This lets us know how many targets have Powershell 2, and lets the
|
24
24
|
# user know how many targets they have with PS2
|
25
25
|
msg = "Detected PowerShell 2 on one or more targets.\nPowerShell 2 "\
|
26
|
-
"is
|
27
|
-
"bolt-debug.log or run with '--log-level debug' to see the full "\
|
26
|
+
"is unsupported. See bolt-debug.log or run with '--log-level debug' to see the full "\
|
28
27
|
"list of targets with PowerShell 2."
|
29
28
|
|
30
|
-
Bolt::Logger.
|
29
|
+
Bolt::Logger.deprecate_once("powershell_2", msg)
|
31
30
|
@logger.debug("Detected PowerShell 2 on #{target}.")
|
32
31
|
end
|
33
32
|
end
|
@@ -163,7 +162,7 @@ module Bolt
|
|
163
162
|
if target.options['cleanup']
|
164
163
|
rmdir(@tmpdir)
|
165
164
|
else
|
166
|
-
|
165
|
+
Bolt::Logger.warn("Skipping cleanup of tmpdir '#{@tmpdir}'", "skip_cleanup")
|
167
166
|
end
|
168
167
|
end
|
169
168
|
end
|
@@ -209,16 +208,21 @@ module Bolt
|
|
209
208
|
arguments = unwrap_sensitive_args(arguments)
|
210
209
|
with_tmpdir do |dir|
|
211
210
|
script_path = write_executable(dir, script)
|
212
|
-
command = if powershell_file?(script_path)
|
211
|
+
command = if powershell_file?(script_path) && options[:pwsh_params]
|
212
|
+
# Scripts run with pwsh_params can be run like tasks
|
213
|
+
Snippets.ps_task(script_path, options[:pwsh_params])
|
214
|
+
elsif powershell_file?(script_path)
|
213
215
|
Snippets.run_script(arguments, script_path)
|
214
216
|
else
|
215
217
|
path, args = *process_from_extension(script_path)
|
216
218
|
args += escape_arguments(arguments)
|
217
219
|
execute_process(path, args)
|
218
220
|
end
|
219
|
-
|
221
|
+
env_assignments = options[:env_vars] ? env_declarations(options[:env_vars]) : []
|
222
|
+
shell_init = options[:pwsh_params] ? Snippets.shell_init : ''
|
223
|
+
|
224
|
+
output = execute([shell_init, *env_assignments, command].join("\r\n"))
|
220
225
|
|
221
|
-
output = execute(command)
|
222
226
|
Bolt::Result.for_command(target,
|
223
227
|
output.stdout.string,
|
224
228
|
output.stderr.string,
|
@@ -275,7 +279,12 @@ module Bolt
|
|
275
279
|
[]
|
276
280
|
end
|
277
281
|
|
278
|
-
output = execute([
|
282
|
+
output = execute([
|
283
|
+
Snippets.shell_init,
|
284
|
+
Snippets.append_ps_module_path(dir),
|
285
|
+
*env_assignments,
|
286
|
+
command
|
287
|
+
].join("\n"))
|
279
288
|
|
280
289
|
Bolt::Result.for_task(target, output.stdout.string,
|
281
290
|
output.stderr.string,
|
@@ -306,12 +315,26 @@ module Bolt
|
|
306
315
|
# the proper encoding so the string isn't later misinterpreted
|
307
316
|
encoding = out.external_encoding
|
308
317
|
out.binmode
|
309
|
-
|
318
|
+
to_print = out.read.force_encoding(encoding)
|
319
|
+
if !to_print.chomp.empty? && @stream_logger
|
320
|
+
formatted = to_print.lines.map do |msg|
|
321
|
+
"[#{@target.safe_name}] out: #{msg.chomp}"
|
322
|
+
end.join("\n")
|
323
|
+
@stream_logger.warn(formatted)
|
324
|
+
end
|
325
|
+
result.stdout << to_print
|
310
326
|
end
|
311
327
|
stderr = Thread.new do
|
312
328
|
encoding = err.external_encoding
|
313
329
|
err.binmode
|
314
|
-
|
330
|
+
to_print = err.read.force_encoding(encoding)
|
331
|
+
if !to_print.chomp.empty? && @stream_logger
|
332
|
+
formatted = to_print.lines.map do |msg|
|
333
|
+
"[#{@target.safe_name}] err: #{msg.chomp}"
|
334
|
+
end.join("\n")
|
335
|
+
@stream_logger.warn(formatted)
|
336
|
+
end
|
337
|
+
result.stderr << to_print
|
315
338
|
end
|
316
339
|
|
317
340
|
stdout.join
|
@@ -55,27 +55,59 @@ module Bolt
|
|
55
55
|
}
|
56
56
|
#{build_arg_list}
|
57
57
|
|
58
|
+
switch -regex ( Get-ExecutionPolicy )
|
59
|
+
{
|
60
|
+
'^AllSigned'
|
61
|
+
{
|
62
|
+
if ((Get-AuthenticodeSignature -File "#{script_path}").Status -ne 'Valid') {
|
63
|
+
$Host.UI.WriteErrorLine("Error: Target host Powershell ExecutionPolicy is set to ${_} and script '#{script_path}' does not contain a valid signature.")
|
64
|
+
exit 1;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
'^Restricted'
|
68
|
+
{
|
69
|
+
$Host.UI.WriteErrorLine("Error: Target host Powershell ExecutionPolicy is set to ${_} which denies running any scripts on the target.")
|
70
|
+
exit 1;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
if([string]::IsNullOrEmpty($invokeArgs.ScriptBlock)){
|
75
|
+
$Host.UI.WriteErrorLine("Error: Failed to obtain scriptblock from '#{script_path}'. Running scripts might be disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170");
|
76
|
+
exit 1;
|
77
|
+
}
|
78
|
+
|
58
79
|
try
|
59
80
|
{
|
60
81
|
Invoke-Command @invokeArgs
|
61
82
|
}
|
62
83
|
catch
|
63
84
|
{
|
64
|
-
|
65
|
-
exit 1
|
85
|
+
$Host.UI.WriteErrorLine("[$($_.FullyQualifiedErrorId)] Exception $($_.InvocationInfo.PositionMessage).`n$($_.Exception.Message)");
|
86
|
+
exit 1;
|
66
87
|
}
|
67
88
|
PS
|
68
89
|
end
|
69
90
|
|
91
|
+
def append_ps_module_path(directory)
|
92
|
+
<<~PS
|
93
|
+
$env:PSModulePath += ";#{directory}"
|
94
|
+
PS
|
95
|
+
end
|
96
|
+
|
70
97
|
def ps_task(path, arguments)
|
71
98
|
<<~PS
|
72
99
|
$private:tempArgs = Get-ContentAsJson (
|
73
|
-
|
100
|
+
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
74
101
|
)
|
75
102
|
$allowedArgs = (Get-Command "#{path}").Parameters.Keys
|
76
103
|
$private:taskArgs = @{}
|
77
104
|
$private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
|
78
|
-
try {
|
105
|
+
try {
|
106
|
+
& "#{path}" @taskArgs
|
107
|
+
} catch {
|
108
|
+
$Host.UI.WriteErrorLine("[$($_.FullyQualifiedErrorId)] Exception $($_.InvocationInfo.PositionMessage).`n$($_.Exception.Message)");
|
109
|
+
exit 1;
|
110
|
+
}
|
79
111
|
PS
|
80
112
|
end
|
81
113
|
|
@@ -102,151 +134,11 @@ module Bolt
|
|
102
134
|
"${boltBaseDir}\\hiera\\lib;" +
|
103
135
|
$ENV:RUBYLIB
|
104
136
|
|
105
|
-
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
106
|
-
$utf8 = [System.Text.Encoding]::UTF8
|
107
|
-
|
108
|
-
function Write-Stream {
|
109
|
-
PARAM(
|
110
|
-
[Parameter(Position=0)] $stream,
|
111
|
-
[Parameter(ValueFromPipeline=$true)] $string
|
112
|
-
)
|
113
|
-
PROCESS {
|
114
|
-
$bytes = $utf8.GetBytes($string)
|
115
|
-
$stream.Write( $bytes, 0, $bytes.Length )
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
function Convert-JsonToXml {
|
120
|
-
PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
|
121
|
-
BEGIN {
|
122
|
-
$mStream = New-Object System.IO.MemoryStream
|
123
|
-
}
|
124
|
-
PROCESS {
|
125
|
-
$json | Write-Stream -Stream $mStream
|
126
|
-
}
|
127
|
-
END {
|
128
|
-
$mStream.Position = 0
|
129
|
-
try {
|
130
|
-
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
|
131
|
-
$xml = New-Object Xml.XmlDocument
|
132
|
-
$xml.Load($jsonReader)
|
133
|
-
$xml
|
134
|
-
} finally {
|
135
|
-
$jsonReader.Close()
|
136
|
-
$mStream.Dispose()
|
137
|
-
}
|
138
|
-
}
|
139
|
-
}
|
140
|
-
|
141
|
-
Function ConvertFrom-Xml {
|
142
|
-
[CmdletBinding(DefaultParameterSetName="AutoType")]
|
143
|
-
PARAM(
|
144
|
-
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
|
145
|
-
[Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
|
146
|
-
[Switch] $ForceType
|
147
|
-
)
|
148
|
-
PROCESS{
|
149
|
-
if (Get-Member -InputObject $xml -Name root) {
|
150
|
-
return $xml.root.Objects | ConvertFrom-Xml
|
151
|
-
} elseif (Get-Member -InputObject $xml -Name Objects) {
|
152
|
-
return $xml.Objects | ConvertFrom-Xml
|
153
|
-
}
|
154
|
-
$propbag = @{}
|
155
|
-
foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^(__.*|type)$"} | Select-Object -ExpandProperty name) {
|
156
|
-
Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
|
157
|
-
$propbag."$Name" = Convert-Properties $xml."$name"
|
158
|
-
}
|
159
|
-
if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
|
160
|
-
if ($ForceType -and $Type) {
|
161
|
-
try {
|
162
|
-
$output = New-Object $Type -Property $propbag
|
163
|
-
} catch {
|
164
|
-
$output = New-Object PSObject -Property $propbag
|
165
|
-
$output.PsTypeNames.Insert(0, $xml.__type)
|
166
|
-
}
|
167
|
-
} elseif ($propbag.Count -ne 0) {
|
168
|
-
$output = New-Object PSObject -Property $propbag
|
169
|
-
if ($Type) {
|
170
|
-
$output.PsTypeNames.Insert(0, $Type)
|
171
|
-
}
|
172
|
-
}
|
173
|
-
return $output
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
Function Convert-Properties {
|
178
|
-
PARAM($InputObject)
|
179
|
-
switch ($InputObject.type) {
|
180
|
-
"object" {
|
181
|
-
return (ConvertFrom-Xml -Xml $InputObject)
|
182
|
-
}
|
183
|
-
"string" {
|
184
|
-
$MightBeADate = $InputObject.get_InnerText() -as [DateTime]
|
185
|
-
## Strings that are actually dates (*grumble* JSON is crap)
|
186
|
-
if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
|
187
|
-
return $MightBeADate
|
188
|
-
} else {
|
189
|
-
return $InputObject.get_InnerText()
|
190
|
-
}
|
191
|
-
}
|
192
|
-
"number" {
|
193
|
-
$number = $InputObject.get_InnerText()
|
194
|
-
if ($number -eq ($number -as [int])) {
|
195
|
-
return $number -as [int]
|
196
|
-
} elseif ($number -eq ($number -as [double])) {
|
197
|
-
return $number -as [double]
|
198
|
-
} else {
|
199
|
-
return $number -as [decimal]
|
200
|
-
}
|
201
|
-
}
|
202
|
-
"boolean" {
|
203
|
-
return [bool]::parse($InputObject.get_InnerText())
|
204
|
-
}
|
205
|
-
"null" {
|
206
|
-
return $null
|
207
|
-
}
|
208
|
-
"array" {
|
209
|
-
[object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
|
210
|
-
Convert-Properties $item
|
211
|
-
})
|
212
|
-
return $Items
|
213
|
-
}
|
214
|
-
default {
|
215
|
-
return $InputObject
|
216
|
-
}
|
217
|
-
}
|
218
|
-
}
|
219
|
-
|
220
|
-
Function ConvertFrom-Json2 {
|
221
|
-
[CmdletBinding()]
|
222
|
-
PARAM(
|
223
|
-
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
|
224
|
-
[Parameter(Mandatory=$true)] [Type] $Type,
|
225
|
-
[Switch] $ForceType
|
226
|
-
)
|
227
|
-
PROCESS {
|
228
|
-
$null = $PSBoundParameters.Remove("InputObject")
|
229
|
-
[Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
|
230
|
-
if ($xml) {
|
231
|
-
if ($xml.Objects) {
|
232
|
-
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
|
233
|
-
} elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
|
234
|
-
$xml.Item | ConvertFrom-Xml @PSBoundParameters
|
235
|
-
} else {
|
236
|
-
$xml | ConvertFrom-Xml @PSBoundParameters
|
237
|
-
}
|
238
|
-
} else {
|
239
|
-
Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
|
240
|
-
}
|
241
|
-
}
|
242
|
-
}
|
243
|
-
|
244
137
|
function ConvertFrom-PSCustomObject
|
245
138
|
{
|
246
139
|
PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
|
247
140
|
PROCESS {
|
248
141
|
if ($null -eq $InputObject) { return $null }
|
249
|
-
|
250
142
|
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
|
251
143
|
$collection = @(
|
252
144
|
foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
|
@@ -274,12 +166,7 @@ module Bolt
|
|
274
166
|
[Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
|
275
167
|
)
|
276
168
|
|
277
|
-
|
278
|
-
if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
|
279
|
-
$Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
|
280
|
-
} else {
|
281
|
-
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
282
|
-
}
|
169
|
+
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
283
170
|
}
|
284
171
|
PS
|
285
172
|
end
|