bolt 3.8.1 → 3.11.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 +4 -4
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +61 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +5 -9
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +28 -13
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +5 -15
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +5 -17
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +8 -17
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +8 -15
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +5 -17
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +91 -0
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/guides/debugging.txt +28 -0
- data/guides/inventory.txt +5 -0
- data/lib/bolt/applicator.rb +3 -2
- data/lib/bolt/bolt_option_parser.rb +51 -4
- data/lib/bolt/cli.rb +38 -9
- data/lib/bolt/config/transport/docker.rb +1 -1
- data/lib/bolt/config/transport/lxd.rb +1 -1
- data/lib/bolt/config/transport/podman.rb +1 -1
- data/lib/bolt/error.rb +11 -1
- data/lib/bolt/executor.rb +55 -72
- data/lib/bolt/fiber_executor.rb +141 -0
- data/lib/bolt/module_installer/installer.rb +1 -1
- data/lib/bolt/outputter/human.rb +46 -2
- data/lib/bolt/outputter/json.rb +9 -0
- data/lib/bolt/pal.rb +117 -17
- data/lib/bolt/plan_future.rb +66 -0
- data/lib/bolt/plugin.rb +38 -0
- data/lib/bolt/plugin/env_var.rb +8 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/plugin/prompt.rb +8 -1
- data/lib/bolt/plugin/puppet_connect_data.rb +8 -1
- data/lib/bolt/plugin/puppetdb.rb +7 -1
- data/lib/bolt/plugin/task.rb +9 -1
- data/lib/bolt/project.rb +2 -1
- data/lib/bolt/task.rb +7 -0
- data/lib/bolt/transport/docker/connection.rb +5 -2
- data/lib/bolt/transport/lxd/connection.rb +4 -0
- data/lib/bolt/transport/podman/connection.rb +4 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/request_error.rb +11 -0
- data/lib/bolt_server/transport_app.rb +133 -95
- data/lib/bolt_spec/plans/mock_executor.rb +40 -45
- data/lib/bolt_spec/run.rb +4 -1
- data/modules/puppet_connect/plans/test_input_data.pp +8 -3
- data/resources/bolt_bash_completion.sh +214 -0
- metadata +10 -3
- data/lib/bolt/yarn.rb +0 -23
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/logger'
|
4
|
+
require 'bolt/plan_future'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class FiberExecutor
|
8
|
+
attr_reader :plan_futures
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@logger = Bolt::Logger.logger(self)
|
12
|
+
@id = 0
|
13
|
+
@plan_futures = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Whether there is more than one fiber running in parallel.
|
17
|
+
#
|
18
|
+
def in_parallel?
|
19
|
+
plan_futures.length > 1
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a new Puppet scope from the current Plan scope so that variables
|
23
|
+
# can be used inside the block and won't interact with the outer scope.
|
24
|
+
# Then creates a new Fiber to execute the block, wraps the Fiber in a
|
25
|
+
# Bolt::PlanFuture, and returns the Bolt::PlanFuture.
|
26
|
+
#
|
27
|
+
def create_future(scope: nil, name: nil)
|
28
|
+
newscope = nil
|
29
|
+
if scope
|
30
|
+
# Save existing variables to the new scope before starting the future
|
31
|
+
# itself so that if the plan returns before the backgrounded block
|
32
|
+
# starts, we still have the variables.
|
33
|
+
newscope = Puppet::Parser::Scope.new(scope.compiler)
|
34
|
+
local = Puppet::Parser::Scope::LocalScope.new
|
35
|
+
|
36
|
+
# Compress the current scopes into a single vars hash to add to the new scope
|
37
|
+
scope.to_hash(true, true).each_pair { |k, v| local[k] = v }
|
38
|
+
newscope.push_ephemerals([local])
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a new Fiber that will execute the provided block.
|
42
|
+
future = Fiber.new do
|
43
|
+
# Yield the new scope - this should be ignored by the block if
|
44
|
+
# `newscope` is nil.
|
45
|
+
yield newscope
|
46
|
+
end
|
47
|
+
|
48
|
+
# PlanFutures are assigned an ID, which is just a global incrementing
|
49
|
+
# integer. The main plan should always have ID 0.
|
50
|
+
@id += 1
|
51
|
+
future = Bolt::PlanFuture.new(future, @id, name)
|
52
|
+
@logger.trace("Created future #{future.name}")
|
53
|
+
|
54
|
+
# Register the PlanFuture with the FiberExecutor to be executed
|
55
|
+
plan_futures << future
|
56
|
+
future
|
57
|
+
end
|
58
|
+
|
59
|
+
# Visit each PlanFuture registered with the FiberExecutor and resume it.
|
60
|
+
# Fibers will yield themselves back, either if they kicked off a
|
61
|
+
# long-running process or if the current long-running process hasn't
|
62
|
+
# completed. If the Fiber finishes after being resumed, store the result in
|
63
|
+
# the PlanFuture and remove the PlanFuture from the FiberExecutor.
|
64
|
+
#
|
65
|
+
def round_robin
|
66
|
+
plan_futures.each do |future|
|
67
|
+
# If the Fiber is still running and can be resumed, then resume it
|
68
|
+
@logger.trace("Checking future '#{future.name}'")
|
69
|
+
if future.alive?
|
70
|
+
@logger.trace("Resuming future '#{future.name}'")
|
71
|
+
future.resume
|
72
|
+
end
|
73
|
+
|
74
|
+
# Once we've restarted the Fiber, check to see if it's finished again
|
75
|
+
# and cleanup if it has.
|
76
|
+
next if future.alive?
|
77
|
+
@logger.trace("Cleaning up future '#{future.name}'")
|
78
|
+
|
79
|
+
# If the future errored and the main plan has already exited, log the
|
80
|
+
# error at warn level.
|
81
|
+
unless plan_futures.map(&:id).include?(0) || future.state == "done"
|
82
|
+
Bolt::Logger.warn('errored_futures', "Error in future '#{future.name}': #{future.value}")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Remove the PlanFuture from the FiberExecutor.
|
86
|
+
plan_futures.delete(future)
|
87
|
+
end
|
88
|
+
|
89
|
+
# If the Fiber immediately returned or if the Fiber is blocking on a
|
90
|
+
# `wait` call, Bolt should pause for long enough that something can
|
91
|
+
# execute before checking again. This mitigates CPU
|
92
|
+
# thrashing.
|
93
|
+
return unless plan_futures.all? { |f| %i[returned_immediately unfinished].include?(f.value) }
|
94
|
+
@logger.trace("Nothing can be resumed. Rechecking in 0.5 seconds.")
|
95
|
+
|
96
|
+
sleep(0.5)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Whether all PlanFutures have finished executing, indicating that the
|
100
|
+
# entire plan (main plan and any PlanFutures it spawned) has finished and
|
101
|
+
# Bolt can exit.
|
102
|
+
#
|
103
|
+
def plan_complete?
|
104
|
+
plan_futures.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Block until the provided PlanFuture objects have finished, or the timeout is reached.
|
108
|
+
#
|
109
|
+
def wait(futures, timeout: nil, catch_errors: false, **_kwargs)
|
110
|
+
if timeout.nil?
|
111
|
+
Fiber.yield(:unfinished) until futures.map(&:alive?).none?
|
112
|
+
else
|
113
|
+
start = Time.now
|
114
|
+
Fiber.yield(:unfinished) until (Time.now - start > timeout) || futures.map(&:alive?).none?
|
115
|
+
# Raise an error for any futures that are still alive
|
116
|
+
futures.each do |f|
|
117
|
+
if f.alive?
|
118
|
+
f.raise(Bolt::FutureTimeoutError.new(f.name, timeout))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
results = futures.map(&:value)
|
124
|
+
|
125
|
+
failed_indices = results.each_index.select do |i|
|
126
|
+
results[i].is_a?(Bolt::Error)
|
127
|
+
end
|
128
|
+
|
129
|
+
if failed_indices.any?
|
130
|
+
if catch_errors
|
131
|
+
failed_indices.each { |i| results[i] = results[i].to_puppet_error }
|
132
|
+
else
|
133
|
+
# Do this after handling errors for simplicity and pretty printing
|
134
|
+
raise Bolt::ParallelFailure.new(results, failed_indices)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
results
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -30,7 +30,7 @@ module Bolt
|
|
30
30
|
|
31
31
|
settings = R10K::Settings.global_settings.evaluate(@config)
|
32
32
|
R10K::Initializers::GlobalInitializer.new(settings).call
|
33
|
-
install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil)
|
33
|
+
install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil, {})
|
34
34
|
|
35
35
|
# Override the r10k logger with a proxy to our own logger
|
36
36
|
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/container_result'
|
3
4
|
require 'bolt/pal'
|
4
5
|
|
5
6
|
module Bolt
|
@@ -173,11 +174,22 @@ module Bolt
|
|
173
174
|
if result.message?
|
174
175
|
@stream.puts(remove_trail(indent(2, result.message)))
|
175
176
|
end
|
176
|
-
|
177
177
|
case result.action
|
178
178
|
when 'command', 'script'
|
179
179
|
safe_value = result.safe_value
|
180
|
-
|
180
|
+
if safe_value["merged_output"]
|
181
|
+
@stream.puts(indent(2, safe_value['merged_output'])) unless safe_value['merged_output'].strip.empty?
|
182
|
+
|
183
|
+
else # output stdout or stderr
|
184
|
+
unless safe_value['stdout'].nil? || safe_value['stdout'].strip.empty?
|
185
|
+
@stream.puts(indent(2, "STDOUT:"))
|
186
|
+
@stream.puts(indent(4, safe_value['stdout']))
|
187
|
+
end
|
188
|
+
unless safe_value['stderr'].nil? || safe_value['stderr'].strip.empty?
|
189
|
+
@stream.puts(indent(2, "STDERR:"))
|
190
|
+
@stream.puts(indent(4, safe_value['stderr']))
|
191
|
+
end
|
192
|
+
end
|
181
193
|
when 'lookup'
|
182
194
|
@stream.puts(indent(2, result['value']))
|
183
195
|
else
|
@@ -459,6 +471,10 @@ module Bolt
|
|
459
471
|
@stream.puts(guide)
|
460
472
|
end
|
461
473
|
|
474
|
+
def print_plan_lookup(value)
|
475
|
+
@stream.puts(value)
|
476
|
+
end
|
477
|
+
|
462
478
|
def print_module_list(module_list)
|
463
479
|
module_list.each do |path, modules|
|
464
480
|
if (mod = modules.find { |m| m[:internal_module_group] })
|
@@ -487,6 +503,34 @@ module Bolt
|
|
487
503
|
end
|
488
504
|
end
|
489
505
|
|
506
|
+
def print_plugin_list(plugin_list, modulepath)
|
507
|
+
info = +''
|
508
|
+
length = plugin_list.values.map(&:keys).flatten.map(&:length).max + 4
|
509
|
+
|
510
|
+
plugin_list.each do |hook, plugins|
|
511
|
+
next if plugins.empty?
|
512
|
+
next if hook == :validate_resolve_reference
|
513
|
+
|
514
|
+
info << colorize(:cyan, "#{hook}\n")
|
515
|
+
|
516
|
+
plugins.each do |name, description|
|
517
|
+
info << indent(2, name.ljust(length))
|
518
|
+
info << truncate(description, 80 - length) if description
|
519
|
+
info << "\n"
|
520
|
+
end
|
521
|
+
|
522
|
+
info << "\n"
|
523
|
+
end
|
524
|
+
|
525
|
+
info << colorize(:cyan, "Modulepath\n")
|
526
|
+
info << indent(2, "#{modulepath.join(File::PATH_SEPARATOR)}\n\n")
|
527
|
+
|
528
|
+
info << colorize(:cyan, "Additional information\n")
|
529
|
+
info << indent(2, "For more information about using plugins see https://pup.pt/bolt-plugins")
|
530
|
+
|
531
|
+
@stream.puts info.chomp
|
532
|
+
end
|
533
|
+
|
490
534
|
def print_targets(target_list, inventory_source, default_inventory, target_flag)
|
491
535
|
adhoc = colorize(:yellow, "(Not found in inventory file)")
|
492
536
|
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -60,6 +60,11 @@ module Bolt
|
|
60
60
|
print_table('tasks' => tasks, 'modulepath' => modulepath)
|
61
61
|
end
|
62
62
|
|
63
|
+
def print_plugin_list(plugins, modulepath)
|
64
|
+
plugins.delete(:validate_resolve_reference)
|
65
|
+
print_table('plugins' => plugins, 'modulepath' => modulepath)
|
66
|
+
end
|
67
|
+
|
63
68
|
def print_plan_info(plan)
|
64
69
|
path = plan.delete('module')
|
65
70
|
plan['module_dir'] = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
@@ -98,6 +103,10 @@ module Bolt
|
|
98
103
|
}.to_json)
|
99
104
|
end
|
100
105
|
|
106
|
+
def print_plan_lookup(value)
|
107
|
+
@stream.puts(value.to_json)
|
108
|
+
end
|
109
|
+
|
101
110
|
def print_puppetfile_result(success, puppetfile, moduledir)
|
102
111
|
@stream.puts({ success: success,
|
103
112
|
puppetfile: puppetfile,
|
data/lib/bolt/pal.rb
CHANGED
@@ -163,9 +163,10 @@ module Bolt
|
|
163
163
|
# Runs a block in a PAL script compiler configured for Bolt. Catches
|
164
164
|
# exceptions thrown by the block and re-raises them ensuring they are
|
165
165
|
# Bolt::Errors since the script compiler block will squash all exceptions.
|
166
|
-
def in_bolt_compiler
|
166
|
+
def in_bolt_compiler(compiler_params: {})
|
167
167
|
# TODO: If we always call this inside a bolt_executor we can remove this here
|
168
168
|
setup
|
169
|
+
compiler_params = compiler_params.merge(set_local_facts: false)
|
169
170
|
r = Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath, facts: {}) do |pal|
|
170
171
|
# Only load the project if it a) exists, b) has a name it can be loaded with
|
171
172
|
Puppet.override(bolt_project: @project,
|
@@ -174,7 +175,7 @@ module Bolt
|
|
174
175
|
# of modules, it must happen *after* we have overridden
|
175
176
|
# bolt_project or the project will be ignored
|
176
177
|
detect_project_conflict(@project, Puppet.lookup(:environments).get('bolt'))
|
177
|
-
pal.with_script_compiler(
|
178
|
+
pal.with_script_compiler(**compiler_params) do |compiler|
|
178
179
|
alias_types(compiler)
|
179
180
|
register_resource_types(Puppet.lookup(:loaders)) if @resource_types
|
180
181
|
begin
|
@@ -299,6 +300,49 @@ module Bolt
|
|
299
300
|
end
|
300
301
|
end
|
301
302
|
|
303
|
+
def list_tasks_with_cache(filter_content: false)
|
304
|
+
# Don't filter content yet, so that if users update their task filters
|
305
|
+
# we don't need to refresh the cache
|
306
|
+
task_names = list_tasks(filter_content: false).map(&:first)
|
307
|
+
task_cache = if @project
|
308
|
+
Bolt::Util.read_optional_json_file(@project.task_cache_file, 'Task cache file')
|
309
|
+
else
|
310
|
+
{}
|
311
|
+
end
|
312
|
+
updated = false
|
313
|
+
|
314
|
+
task_list = task_names.each_with_object([]) do |task_name, list|
|
315
|
+
data = task_cache[task_name] || get_task_info(task_name, with_mtime: true)
|
316
|
+
|
317
|
+
# Make sure all the keys are strings - if we get data from
|
318
|
+
# get_task_info they will be symbols
|
319
|
+
data = Bolt::Util.walk_keys(data, &:to_s)
|
320
|
+
|
321
|
+
# If any files in the task were updated, refresh the cache
|
322
|
+
if data['files']&.any?
|
323
|
+
# For all the files that are part of the task
|
324
|
+
data['files'].each do |f|
|
325
|
+
# If any file has been updated since we last cached, update the
|
326
|
+
# cache
|
327
|
+
next if File.mtime(f['path']).to_s == f['mtime']
|
328
|
+
data = get_task_info(task_name, with_mtime: true)
|
329
|
+
data = Bolt::Util.walk_keys(data, &:to_s)
|
330
|
+
# Tell Bolt to write to the cache file once we're done
|
331
|
+
updated = true
|
332
|
+
# Update the cache data
|
333
|
+
task_cache[task_name] = data
|
334
|
+
end
|
335
|
+
end
|
336
|
+
metadata = data['metadata'] || {}
|
337
|
+
# Don't add tasks to the list to return if they are private
|
338
|
+
list << [task_name, metadata['description']] unless metadata['private']
|
339
|
+
end
|
340
|
+
|
341
|
+
# Write the cache if any entries were updated
|
342
|
+
File.write(@project.task_cache_file, task_cache.to_json) if updated
|
343
|
+
filter_content ? filter_content(task_list, @project&.tasks) : task_list
|
344
|
+
end
|
345
|
+
|
302
346
|
def list_tasks(filter_content: false)
|
303
347
|
in_bolt_compiler do |compiler|
|
304
348
|
tasks = compiler.list_tasks.map(&:name).sort.each_with_object([]) do |task_name, data|
|
@@ -350,14 +394,20 @@ module Bolt
|
|
350
394
|
end
|
351
395
|
end
|
352
396
|
|
353
|
-
def get_task(task_name)
|
397
|
+
def get_task(task_name, with_mtime: false)
|
354
398
|
task = task_signature(task_name)
|
355
399
|
|
356
400
|
if task.nil?
|
357
401
|
raise Bolt::Error.unknown_task(task_name)
|
358
402
|
end
|
359
403
|
|
360
|
-
Bolt::Task.from_task_signature(task)
|
404
|
+
task = Bolt::Task.from_task_signature(task)
|
405
|
+
task.add_mtimes if with_mtime
|
406
|
+
task
|
407
|
+
end
|
408
|
+
|
409
|
+
def get_task_info(task_name, with_mtime: false)
|
410
|
+
get_task(task_name, with_mtime: with_mtime).to_h
|
361
411
|
end
|
362
412
|
|
363
413
|
def list_plans_with_cache(filter_content: false)
|
@@ -372,20 +422,20 @@ module Bolt
|
|
372
422
|
updated = false
|
373
423
|
|
374
424
|
plan_list = plan_names.each_with_object([]) do |plan_name, list|
|
375
|
-
|
425
|
+
data = plan_cache[plan_name] || get_plan_info(plan_name, with_mtime: true)
|
376
426
|
|
377
427
|
# If the plan is a 'local' plan (in the project itself, or the
|
378
428
|
# modules/ directory) then verify it hasn't been updated since we
|
379
429
|
# cached it. If it has been updated, refresh the cache and use the
|
380
430
|
# new data.
|
381
|
-
if
|
382
|
-
|
383
|
-
|
431
|
+
if data['file'] &&
|
432
|
+
File.mtime(data.dig('file', 'path')).to_s != data.dig('file', 'mtime')
|
433
|
+
data = get_plan_info(plan_name, with_mtime: true)
|
384
434
|
updated = true
|
385
|
-
plan_cache[plan_name] =
|
435
|
+
plan_cache[plan_name] = data
|
386
436
|
end
|
387
437
|
|
388
|
-
list << [plan_name,
|
438
|
+
list << [plan_name, data['description']] unless data['private']
|
389
439
|
end
|
390
440
|
|
391
441
|
File.write(@project.plan_cache_file, plan_cache.to_json) if updated
|
@@ -585,6 +635,7 @@ module Bolt
|
|
585
635
|
inputs = generator.find_inputs(:pcore)
|
586
636
|
FileUtils.mkdir_p(@resource_types)
|
587
637
|
cache_plan_info if @project && cache
|
638
|
+
cache_task_info if @project && cache
|
588
639
|
generator.generate(inputs, @resource_types, true)
|
589
640
|
end
|
590
641
|
end
|
@@ -600,6 +651,17 @@ module Bolt
|
|
600
651
|
File.write(@project.plan_cache_file, plans_info.to_json)
|
601
652
|
end
|
602
653
|
|
654
|
+
def cache_task_info
|
655
|
+
# task_name is an array here
|
656
|
+
tasks_info = list_tasks(filter_content: false).map do |task_name,|
|
657
|
+
data = get_task_info(task_name, with_mtime: true)
|
658
|
+
{ task_name => data }
|
659
|
+
end.reduce({}, :merge)
|
660
|
+
|
661
|
+
FileUtils.touch(@project.task_cache_file)
|
662
|
+
File.write(@project.task_cache_file, tasks_info.to_json)
|
663
|
+
end
|
664
|
+
|
603
665
|
def run_task(task_name, targets, params, executor, inventory, description = nil)
|
604
666
|
in_task_compiler(executor, inventory) do |compiler|
|
605
667
|
params = params.merge('_bolt_api_call' => true, '_catch_errors' => true)
|
@@ -607,16 +669,46 @@ module Bolt
|
|
607
669
|
end
|
608
670
|
end
|
609
671
|
|
610
|
-
def run_plan(plan_name, params, executor
|
672
|
+
def run_plan(plan_name, params, executor, inventory = nil, pdb_client = nil, applicator = nil)
|
673
|
+
# Start the round robin inside the plan compiler, so that
|
674
|
+
# backgrounded tasks can finish once the main plan exits
|
611
675
|
in_plan_compiler(executor, inventory, pdb_client, applicator) do |compiler|
|
612
|
-
|
613
|
-
|
676
|
+
# Create a Fiber for the main plan. This will be run along with any
|
677
|
+
# other Fibers created during the plan run in the round_robin, with the
|
678
|
+
# main plan always taking precedence in being resumed.
|
679
|
+
future = executor.create_future(name: plan_name) do |_scope|
|
680
|
+
r = compiler.call_function('run_plan', plan_name, params.merge('_bolt_api_call' => true))
|
681
|
+
Bolt::PlanResult.from_pcore(r, 'success')
|
682
|
+
rescue Bolt::Error => e
|
683
|
+
Bolt::PlanResult.new(e, 'failure')
|
684
|
+
end
|
685
|
+
|
686
|
+
# Round robin until all Fibers, including the main plan, have finished.
|
687
|
+
# This will stay alive until backgrounded tasks have finished.
|
688
|
+
executor.round_robin until executor.plan_complete?
|
689
|
+
|
690
|
+
# Return the result from the main plan
|
691
|
+
future.value
|
614
692
|
end
|
615
693
|
rescue Bolt::Error => e
|
616
694
|
Bolt::PlanResult.new(e, 'failure')
|
617
695
|
end
|
618
696
|
|
619
|
-
def
|
697
|
+
def plan_hierarchy_lookup(key, plan_vars: {})
|
698
|
+
# Do a lookup with a script compiler, which uses the 'plan_hierarchy' key in
|
699
|
+
# Hiera config.
|
700
|
+
with_puppet_settings do
|
701
|
+
# We want all of the setup and teardown that `in_bolt_compiler` does,
|
702
|
+
# but also want to pass keys to the script compiler.
|
703
|
+
in_bolt_compiler(compiler_params: { variables: plan_vars }) do |compiler|
|
704
|
+
compiler.call_function('lookup', key)
|
705
|
+
end
|
706
|
+
rescue Puppet::Error => e
|
707
|
+
raise PALError.from_error(e)
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
def lookup(key, targets, inventory, executor, plan_vars: {})
|
620
712
|
# Install the puppet-agent package and collect facts. Facts are
|
621
713
|
# automatically added to the targets.
|
622
714
|
in_plan_compiler(executor, inventory, nil) do |compiler|
|
@@ -638,21 +730,29 @@ module Bolt
|
|
638
730
|
|
639
731
|
trusted = Puppet::Context::TrustedInformation.local(node).to_h
|
640
732
|
|
733
|
+
# Separate environment configuration from interpolation data the same
|
734
|
+
# way we do when compiling Puppet catalogs.
|
641
735
|
env_conf = {
|
642
736
|
modulepath: @modulepath.full_modulepath,
|
643
|
-
facts: target.facts
|
644
|
-
|
737
|
+
facts: target.facts
|
738
|
+
}
|
739
|
+
|
740
|
+
interpolations = {
|
741
|
+
variables: plan_vars,
|
742
|
+
target_variables: target.vars
|
645
743
|
}
|
646
744
|
|
647
745
|
with_puppet_settings do
|
648
746
|
Puppet::Pal.in_tmp_environment(target.name, **env_conf) do |pal|
|
649
747
|
Puppet.override(overrides) do
|
650
748
|
Puppet.lookup(:pal_current_node).trusted_data = trusted
|
651
|
-
pal.with_catalog_compiler do |compiler|
|
749
|
+
pal.with_catalog_compiler(**interpolations) do |compiler|
|
652
750
|
Bolt::Result.for_lookup(target, key, compiler.call_function('lookup', key))
|
653
751
|
rescue StandardError => e
|
654
752
|
Bolt::Result.from_exception(target, e)
|
655
753
|
end
|
754
|
+
rescue Puppet::Error => e
|
755
|
+
raise PALError.from_error(e)
|
656
756
|
end
|
657
757
|
end
|
658
758
|
end
|