bolt 3.11.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +137 -104
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +5 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +13 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +47 -7
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +9 -49
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/guides/{debugging.txt → debugging.yaml} +5 -6
- data/guides/{inventory.txt → inventory.yaml} +6 -7
- data/guides/{links.txt → links.yaml} +3 -4
- data/guides/{logging.txt → logging.yaml} +5 -6
- data/guides/{module.txt → module.yaml} +5 -6
- data/guides/{modulepath.txt → modulepath.yaml} +5 -6
- data/guides/{project.txt → project.yaml} +6 -7
- data/guides/{targets.txt → targets.yaml} +5 -6
- data/guides/{transports.txt → transports.yaml} +6 -7
- data/lib/bolt/analytics.rb +1 -1
- data/lib/bolt/applicator.rb +23 -1
- data/lib/bolt/bolt_option_parser.rb +6 -3
- data/lib/bolt/cli.rb +34 -14
- data/lib/bolt/config/options.rb +2 -2
- data/lib/bolt/config/transport/options.rb +12 -0
- data/lib/bolt/config/transport/ssh.rb +7 -0
- data/lib/bolt/error.rb +3 -3
- data/lib/bolt/executor.rb +12 -4
- data/lib/bolt/fiber_executor.rb +57 -12
- data/lib/bolt/outputter/human.rb +124 -15
- data/lib/bolt/outputter/json.rb +5 -5
- data/lib/bolt/outputter/logger.rb +6 -0
- data/lib/bolt/pal.rb +81 -21
- data/lib/bolt/pal/yaml_plan/step.rb +2 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +0 -8
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -1
- data/lib/bolt/plan_future.rb +21 -6
- data/lib/bolt/plugin/task.rb +1 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +3 -1
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/schemas/connect-data.json +4 -1
- data/lib/bolt_server/schemas/partials/target-ssh.json +4 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +4 -0
- data/lib/bolt_server/transport_app.rb +93 -52
- data/lib/bolt_spec/bolt_context.rb +9 -0
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +31 -7
- data/lib/bolt_spec/plans/publish_stub.rb +4 -4
- data/modules/canary/plans/init.pp +1 -1
- data/resources/bolt_bash_completion.sh +1 -1
- metadata +28 -14
- data/guides/guide.txt +0 -17
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
DESCRIPTION
|
1
|
+
---
|
2
|
+
topic: modulepath
|
3
|
+
guide: |
|
5
4
|
The modulepath is an ordered list of directories that Bolt loads modules
|
6
5
|
from. When Bolt runs a command, it automatically loads modules from the
|
7
6
|
modulepath.
|
@@ -21,5 +20,5 @@ DESCRIPTION
|
|
21
20
|
|
22
21
|
To learn more about modules, see the 'module' guide.
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
documentation:
|
24
|
+
- https://pup.pt/bolt-project-reference#modulepath
|
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
DESCRIPTION
|
1
|
+
---
|
2
|
+
topic: project
|
3
|
+
guide: |
|
5
4
|
A Bolt project is a directory that serves as the launching point for Bolt
|
6
5
|
and allows you to create a shareable orchestration application. Projects
|
7
6
|
typically include a project configuration file, an inventory file, and any
|
@@ -17,6 +16,6 @@ DESCRIPTION
|
|
17
16
|
and content, including inventory files, unless the data and content are part
|
18
17
|
of a project.
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
documentation:
|
20
|
+
- https://pup.pt/bolt-projects
|
21
|
+
- https://pup.pt/bolt-project-reference
|
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
DESCRIPTION
|
1
|
+
---
|
2
|
+
topic: targets
|
3
|
+
guide: |
|
5
4
|
A target is a device that Bolt connects to and runs actions on. Targets can
|
6
5
|
be physical, such as servers, or virtual, such as containers or virtual
|
7
6
|
machines.
|
@@ -25,5 +24,5 @@ DESCRIPTION
|
|
25
24
|
project's inventory file. For more information about inventory files,
|
26
25
|
see 'bolt guide inventory'.
|
27
26
|
|
28
|
-
|
29
|
-
|
27
|
+
documentation:
|
28
|
+
- https://pup.pt/bolt-commands
|
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
DESCRIPTION
|
1
|
+
---
|
2
|
+
topic: transports
|
3
|
+
guide: |
|
5
4
|
Bolt uses transports (also known as protocols) to establish a connection
|
6
5
|
with a target in order to run actions on the target. The default transport is
|
7
6
|
SSH, and you can see available transports along with their configuration
|
@@ -18,6 +17,6 @@ DESCRIPTION
|
|
18
17
|
Finally, you can set the transport for a target in the inventory. For more
|
19
18
|
information about the Bolt inventory, run 'bolt guide inventory'.
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
documentation:
|
21
|
+
- https://pup.pt/bolt-commands#specify-a-transport
|
22
|
+
- http://pup.pt/bolt-inventory#transport-configuration
|
data/lib/bolt/analytics.rb
CHANGED
data/lib/bolt/applicator.rb
CHANGED
@@ -122,7 +122,13 @@ module Bolt
|
|
122
122
|
logs.each do |log|
|
123
123
|
bolt_level = Bolt::Util::PuppetLogLevel::MAPPING[log['level'].to_sym]
|
124
124
|
message = log['message'].chomp
|
125
|
-
|
125
|
+
|
126
|
+
case bolt_level
|
127
|
+
when :warn
|
128
|
+
handle_warning(target, message)
|
129
|
+
else
|
130
|
+
@logger.send(bolt_level, "#{target.name}: #{message}")
|
131
|
+
end
|
126
132
|
end
|
127
133
|
end
|
128
134
|
|
@@ -138,6 +144,22 @@ module Bolt
|
|
138
144
|
result
|
139
145
|
end
|
140
146
|
|
147
|
+
# Handles logging Puppet warnings, some of which are suppressable.
|
148
|
+
#
|
149
|
+
# @param target [Bolt::Target] The target the apply ran on.
|
150
|
+
# @param message [String] The log message.
|
151
|
+
#
|
152
|
+
private def handle_warning(target, message)
|
153
|
+
# Messages about exported resource declaration and collection, which are
|
154
|
+
# not supported in manifest blocks.
|
155
|
+
if message.include?(Puppet::Pops::Issues::RT_NO_STORECONFIGS_EXPORT.format) ||
|
156
|
+
message.include?(Puppet::Pops::Issues::RT_NO_STORECONFIGS.format)
|
157
|
+
Bolt::Logger.warn('exported_resources', "#{target.name}: #{message}")
|
158
|
+
else
|
159
|
+
@logger.send(:warn, "#{target.name}: #{message}")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
141
163
|
def validate_hiera_config(hiera_config)
|
142
164
|
if File.exist?(File.path(hiera_config))
|
143
165
|
data = File.open(File.path(hiera_config), "r:UTF-8") { |f| YAML.safe_load(f.read, [Symbol]) }
|
@@ -507,11 +507,14 @@ module Bolt
|
|
507
507
|
show
|
508
508
|
|
509
509
|
#{colorize(:cyan, 'Usage')}
|
510
|
-
bolt module show [options]
|
510
|
+
bolt module show [module name] [options]
|
511
511
|
|
512
512
|
#{colorize(:cyan, 'Description')}
|
513
513
|
List modules available to the Bolt project.
|
514
514
|
|
515
|
+
Providing the name of a module will display detailed documentation for
|
516
|
+
the module.
|
517
|
+
|
515
518
|
#{colorize(:cyan, 'Documentation')}
|
516
519
|
To learn more about Bolt modules, run 'bolt guide module'.
|
517
520
|
HELP
|
@@ -1088,11 +1091,11 @@ module Bolt
|
|
1088
1091
|
end
|
1089
1092
|
define('--log-level LEVEL',
|
1090
1093
|
"Set the log level for the console. Available options are",
|
1091
|
-
"trace, debug, info, warn, error, fatal
|
1094
|
+
"trace, debug, info, warn, error, fatal.") do |level|
|
1092
1095
|
@options[:log] = { 'console' => { 'level' => level } }
|
1093
1096
|
end
|
1094
1097
|
define('--clear-cache',
|
1095
|
-
"Clear plugin
|
1098
|
+
"Clear plugin, plan, and task caches before executing.") do |_|
|
1096
1099
|
@options[:clear_cache] = true
|
1097
1100
|
end
|
1098
1101
|
define('--plugin PLUGIN', 'Select the plugin to use.') do |plug|
|
data/lib/bolt/cli.rb
CHANGED
@@ -238,8 +238,10 @@ module Bolt
|
|
238
238
|
config.check_path_case('modulepath', config.modulepath)
|
239
239
|
config.project.check_deprecated_file
|
240
240
|
|
241
|
-
if options[:clear_cache]
|
242
|
-
FileUtils.rm(config.project.plugin_cache_file)
|
241
|
+
if options[:clear_cache]
|
242
|
+
FileUtils.rm(config.project.plugin_cache_file) if File.exist?(config.project.plugin_cache_file)
|
243
|
+
FileUtils.rm(config.project.task_cache_file) if File.exist?(config.project.task_cache_file)
|
244
|
+
FileUtils.rm(config.project.plan_cache_file) if File.exist?(config.project.plan_cache_file)
|
243
245
|
end
|
244
246
|
|
245
247
|
warn_inventory_overrides_cli(options)
|
@@ -494,7 +496,11 @@ module Bolt
|
|
494
496
|
when 'group'
|
495
497
|
list_groups
|
496
498
|
when 'module'
|
497
|
-
|
499
|
+
if options[:object]
|
500
|
+
show_module(options[:object])
|
501
|
+
else
|
502
|
+
list_modules
|
503
|
+
end
|
498
504
|
when 'plugin'
|
499
505
|
list_plugins
|
500
506
|
end
|
@@ -787,8 +793,8 @@ module Bolt
|
|
787
793
|
if %w[human rainbow].include?(options.fetch(:format, 'human'))
|
788
794
|
executor.subscribe(outputter)
|
789
795
|
else
|
790
|
-
# Only subscribe to out
|
791
|
-
executor.subscribe(outputter, [
|
796
|
+
# Only subscribe to out module events for JSON outputter
|
797
|
+
executor.subscribe(outputter, %i[message verbose])
|
792
798
|
end
|
793
799
|
|
794
800
|
executor.subscribe(log_outputter)
|
@@ -832,13 +838,16 @@ module Bolt
|
|
832
838
|
|
833
839
|
results = nil
|
834
840
|
elapsed_time = Benchmark.realtime do
|
835
|
-
pal.in_plan_compiler(executor, inventory, puppetdb_client) do |compiler|
|
836
|
-
compiler.call_function('apply_prep', targets)
|
841
|
+
apply_prep_results = pal.in_plan_compiler(executor, inventory, puppetdb_client) do |compiler|
|
842
|
+
compiler.call_function('apply_prep', targets, '_catch_errors' => true)
|
837
843
|
end
|
838
844
|
|
839
|
-
|
840
|
-
Puppet.lookup(:apply_executor)
|
845
|
+
apply_results = pal.with_bolt_executor(executor, inventory, puppetdb_client) do
|
846
|
+
Puppet.lookup(:apply_executor)
|
847
|
+
.apply_ast(ast, apply_prep_results.ok_set.targets, catch_errors: true, noop: noop)
|
841
848
|
end
|
849
|
+
|
850
|
+
results = Bolt::ResultSet.new(apply_prep_results.error_set.results + apply_results.results)
|
842
851
|
end
|
843
852
|
|
844
853
|
executor.shutdown
|
@@ -852,6 +861,10 @@ module Bolt
|
|
852
861
|
outputter.print_module_list(pal.list_modules)
|
853
862
|
end
|
854
863
|
|
864
|
+
def show_module(name)
|
865
|
+
outputter.print_module_info(**pal.show_module(name))
|
866
|
+
end
|
867
|
+
|
855
868
|
def list_plugins
|
856
869
|
outputter.print_plugin_list(plugins.list_plugins, pal.user_modulepath)
|
857
870
|
end
|
@@ -950,8 +963,9 @@ module Bolt
|
|
950
963
|
files = Dir.children(root_path).sort
|
951
964
|
|
952
965
|
files.each_with_object({}) do |file, guides|
|
953
|
-
next if file !~ /\.
|
954
|
-
|
966
|
+
next if file !~ /\.(yaml|yml)\z/
|
967
|
+
# The ".*" here removes any suffix
|
968
|
+
topic = File.basename(file, ".*")
|
955
969
|
guides[topic] = File.join(root_path, file)
|
956
970
|
end
|
957
971
|
rescue SystemCallError => e
|
@@ -961,7 +975,7 @@ module Bolt
|
|
961
975
|
|
962
976
|
# Display the list of available Bolt guides.
|
963
977
|
def list_topics
|
964
|
-
outputter.print_topics(guides.keys
|
978
|
+
outputter.print_topics(guides.keys)
|
965
979
|
0
|
966
980
|
end
|
967
981
|
|
@@ -971,12 +985,18 @@ module Bolt
|
|
971
985
|
analytics.event('Guide', 'known_topic', label: topic)
|
972
986
|
|
973
987
|
begin
|
974
|
-
guide =
|
988
|
+
guide = Bolt::Util.read_yaml_hash(guides[topic], 'guide')
|
975
989
|
rescue SystemCallError => e
|
976
990
|
raise Bolt::FileError("#{e.message}: unable to load guide page", filepath)
|
977
991
|
end
|
978
992
|
|
979
|
-
|
993
|
+
# Make sure both topic and guide keys are defined
|
994
|
+
unless (%w[topic guide] - guide.keys).empty?
|
995
|
+
msg = "Guide file #{guides[topic]} must have a 'topic' key and 'guide' key, but has #{guide.keys} keys."
|
996
|
+
raise Bolt::Error.new(msg, 'bolt/invalid-guide')
|
997
|
+
end
|
998
|
+
|
999
|
+
outputter.print_guide(**Bolt::Util.symbolize_top_level_keys(guide))
|
980
1000
|
else
|
981
1001
|
analytics.event('Guide', 'unknown_topic', label: topic)
|
982
1002
|
outputter.print_message("Did not find guide for topic '#{topic}'.\n\n")
|
data/lib/bolt/config/options.rb
CHANGED
@@ -202,7 +202,7 @@ module Bolt
|
|
202
202
|
"level" => {
|
203
203
|
description: "The type of information to log.",
|
204
204
|
type: String,
|
205
|
-
enum: %w[trace debug error info warn fatal
|
205
|
+
enum: %w[trace debug error info warn fatal],
|
206
206
|
_default: "warn"
|
207
207
|
}
|
208
208
|
}
|
@@ -221,7 +221,7 @@ module Bolt
|
|
221
221
|
"level" => {
|
222
222
|
description: "The type of information to log.",
|
223
223
|
type: String,
|
224
|
-
enum: %w[trace debug error info warn fatal
|
224
|
+
enum: %w[trace debug error info warn fatal],
|
225
225
|
_default: "warn"
|
226
226
|
}
|
227
227
|
}
|
@@ -16,6 +16,18 @@ module Bolt
|
|
16
16
|
_default: false,
|
17
17
|
_example: true
|
18
18
|
},
|
19
|
+
"batch-mode" => {
|
20
|
+
type: [TrueClass, FalseClass],
|
21
|
+
description: "Whether to disable password querying. When set to `false`, SSH will fall back to "\
|
22
|
+
"prompting for a password if key authentication fails. This might cause Bolt to hang. "\
|
23
|
+
"To prevent Bolt from hanging, you can configure `ssh-command` to use an SSH utility "\
|
24
|
+
"such as sshpass that supports providing a password non-interactively. For more "\
|
25
|
+
"information, see [Providing a password non-interactively using "\
|
26
|
+
"`native-ssh`](troubleshooting.md#providing-a-password-non-interactively-using-native-ssh).",
|
27
|
+
_plugin: true,
|
28
|
+
_default: true,
|
29
|
+
_example: false
|
30
|
+
},
|
19
31
|
"bundled-ruby" => {
|
20
32
|
description: "Whether to use the Ruby bundled with Bolt packages for local targets.",
|
21
33
|
type: [TrueClass, FalseClass],
|
@@ -34,6 +34,7 @@ module Bolt
|
|
34
34
|
|
35
35
|
# Options available when using the native ssh transport
|
36
36
|
NATIVE_OPTIONS = %w[
|
37
|
+
batch-mode
|
37
38
|
cleanup
|
38
39
|
copy-command
|
39
40
|
host
|
@@ -49,6 +50,7 @@ module Bolt
|
|
49
50
|
].concat(RUN_AS_OPTIONS).sort.freeze
|
50
51
|
|
51
52
|
DEFAULTS = {
|
53
|
+
"batch-mode" => true,
|
52
54
|
"cleanup" => true,
|
53
55
|
"connect-timeout" => 10,
|
54
56
|
"disconnect-timeout" => 5,
|
@@ -124,6 +126,11 @@ module Bolt
|
|
124
126
|
msg = 'Cannot use native SSH transport with load-config set to false'
|
125
127
|
raise Bolt::ValidationError, msg
|
126
128
|
end
|
129
|
+
|
130
|
+
if !@config['batch-mode'] && !@config['ssh-command']
|
131
|
+
raise Bolt::ValidationError,
|
132
|
+
'Must set ssh-command when batch-mode is set to false'
|
133
|
+
end
|
127
134
|
end
|
128
135
|
end
|
129
136
|
end
|
data/lib/bolt/error.rb
CHANGED
@@ -69,7 +69,7 @@ module Bolt
|
|
69
69
|
'value' => result.value,
|
70
70
|
'object' => result.object
|
71
71
|
}
|
72
|
-
message = "
|
72
|
+
message = "Running container '#{result.object}' failed."
|
73
73
|
super(message, 'bolt/container-failure', details)
|
74
74
|
@result = result
|
75
75
|
@error_code = 2
|
@@ -86,7 +86,7 @@ module Bolt
|
|
86
86
|
'result_set' => result_set
|
87
87
|
}
|
88
88
|
object_msg = " '#{object}'" if object
|
89
|
-
message = "
|
89
|
+
message = "#{action}#{object_msg} failed on #{result_set.error_set.length} target"
|
90
90
|
message += "s" unless result_set.error_set.length == 1
|
91
91
|
super(message, 'bolt/run-failure', details)
|
92
92
|
@result_set = result_set
|
@@ -122,7 +122,7 @@ module Bolt
|
|
122
122
|
'failed_indices' => failed_indices,
|
123
123
|
'results' => results
|
124
124
|
}
|
125
|
-
message = "
|
125
|
+
message = "parallel block failed on #{failed_indices.length} target"
|
126
126
|
message += "s" unless failed_indices.length == 1
|
127
127
|
super(message, 'bolt/parallel-failure', details)
|
128
128
|
@error_code = 2
|
data/lib/bolt/executor.rb
CHANGED
@@ -381,8 +381,16 @@ module Bolt
|
|
381
381
|
# overloaded while also minimizing the Puppet lookups needed from plan
|
382
382
|
# functions
|
383
383
|
#
|
384
|
-
def create_future(scope: nil, name: nil, &block)
|
385
|
-
@fiber_executor.create_future(scope: scope, name: name, &block)
|
384
|
+
def create_future(plan_id:, scope: nil, name: nil, &block)
|
385
|
+
@fiber_executor.create_future(scope: scope, name: name, plan_id: plan_id, &block)
|
386
|
+
end
|
387
|
+
|
388
|
+
def get_current_future(fiber:)
|
389
|
+
@fiber_executor.get_current_future(fiber: fiber)
|
390
|
+
end
|
391
|
+
|
392
|
+
def get_current_plan_id(fiber:)
|
393
|
+
@fiber_executor.get_current_plan_id(fiber: fiber)
|
386
394
|
end
|
387
395
|
|
388
396
|
def plan_complete?
|
@@ -401,8 +409,8 @@ module Bolt
|
|
401
409
|
@fiber_executor.wait(futures, **opts)
|
402
410
|
end
|
403
411
|
|
404
|
-
def
|
405
|
-
@fiber_executor.
|
412
|
+
def get_futures_for_plan(plan_id:)
|
413
|
+
@fiber_executor.get_futures_for_plan(plan_id: plan_id)
|
406
414
|
end
|
407
415
|
|
408
416
|
# Execute a plan function concurrently. This function accepts the executor
|
data/lib/bolt/fiber_executor.rb
CHANGED
@@ -5,18 +5,19 @@ require 'bolt/plan_future'
|
|
5
5
|
|
6
6
|
module Bolt
|
7
7
|
class FiberExecutor
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :active_futures, :finished_futures
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@logger = Bolt::Logger.logger(self)
|
12
12
|
@id = 0
|
13
|
-
@
|
13
|
+
@active_futures = []
|
14
|
+
@finished_futures = []
|
14
15
|
end
|
15
16
|
|
16
17
|
# Whether there is more than one fiber running in parallel.
|
17
18
|
#
|
18
19
|
def in_parallel?
|
19
|
-
|
20
|
+
active_futures.length > 1
|
20
21
|
end
|
21
22
|
|
22
23
|
# Creates a new Puppet scope from the current Plan scope so that variables
|
@@ -24,7 +25,7 @@ module Bolt
|
|
24
25
|
# Then creates a new Fiber to execute the block, wraps the Fiber in a
|
25
26
|
# Bolt::PlanFuture, and returns the Bolt::PlanFuture.
|
26
27
|
#
|
27
|
-
def create_future(scope: nil, name: nil)
|
28
|
+
def create_future(plan_id:, scope: nil, name: nil)
|
28
29
|
newscope = nil
|
29
30
|
if scope
|
30
31
|
# Save existing variables to the new scope before starting the future
|
@@ -46,13 +47,16 @@ module Bolt
|
|
46
47
|
end
|
47
48
|
|
48
49
|
# PlanFutures are assigned an ID, which is just a global incrementing
|
49
|
-
# integer. The main plan should always have ID 0.
|
50
|
+
# integer. The main plan should always have ID 0. They also have a
|
51
|
+
# plan_id, which identifies which plan spawned them. This is used for
|
52
|
+
# tracking which Futures to wait on when `wait()` is called without
|
53
|
+
# arguments.
|
50
54
|
@id += 1
|
51
|
-
future = Bolt::PlanFuture.new(future, @id, name)
|
55
|
+
future = Bolt::PlanFuture.new(future, @id, name: name, plan_id: plan_id)
|
52
56
|
@logger.trace("Created future #{future.name}")
|
53
57
|
|
54
58
|
# Register the PlanFuture with the FiberExecutor to be executed
|
55
|
-
|
59
|
+
active_futures << future
|
56
60
|
future
|
57
61
|
end
|
58
62
|
|
@@ -63,7 +67,7 @@ module Bolt
|
|
63
67
|
# the PlanFuture and remove the PlanFuture from the FiberExecutor.
|
64
68
|
#
|
65
69
|
def round_robin
|
66
|
-
|
70
|
+
active_futures.each do |future|
|
67
71
|
# If the Fiber is still running and can be resumed, then resume it
|
68
72
|
@logger.trace("Checking future '#{future.name}'")
|
69
73
|
if future.alive?
|
@@ -78,19 +82,19 @@ module Bolt
|
|
78
82
|
|
79
83
|
# If the future errored and the main plan has already exited, log the
|
80
84
|
# error at warn level.
|
81
|
-
unless
|
85
|
+
unless active_futures.map(&:id).include?(0) || future.state == "done"
|
82
86
|
Bolt::Logger.warn('errored_futures', "Error in future '#{future.name}': #{future.value}")
|
83
87
|
end
|
84
88
|
|
85
89
|
# Remove the PlanFuture from the FiberExecutor.
|
86
|
-
|
90
|
+
finished_futures.push(active_futures.delete(future))
|
87
91
|
end
|
88
92
|
|
89
93
|
# If the Fiber immediately returned or if the Fiber is blocking on a
|
90
94
|
# `wait` call, Bolt should pause for long enough that something can
|
91
95
|
# execute before checking again. This mitigates CPU
|
92
96
|
# thrashing.
|
93
|
-
return unless
|
97
|
+
return unless active_futures.all? { |f| %i[returned_immediately unfinished].include?(f.value) }
|
94
98
|
@logger.trace("Nothing can be resumed. Rechecking in 0.5 seconds.")
|
95
99
|
|
96
100
|
sleep(0.5)
|
@@ -101,12 +105,53 @@ module Bolt
|
|
101
105
|
# Bolt can exit.
|
102
106
|
#
|
103
107
|
def plan_complete?
|
104
|
-
|
108
|
+
active_futures.empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
def all_futures
|
112
|
+
active_futures + finished_futures
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get the PlanFuture object that is currently executing
|
116
|
+
#
|
117
|
+
def get_current_future(fiber:)
|
118
|
+
all_futures.select { |f| f.fiber == fiber }.first
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the plan invocation ID for the PlanFuture that is currently executing
|
122
|
+
#
|
123
|
+
def get_current_plan_id(fiber:)
|
124
|
+
get_current_future(fiber: fiber).current_plan
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get the Future objects associated with a particular plan invocation.
|
128
|
+
#
|
129
|
+
def get_futures_for_plan(plan_id:)
|
130
|
+
all_futures.select { |f| f.original_plan == plan_id }
|
105
131
|
end
|
106
132
|
|
107
133
|
# Block until the provided PlanFuture objects have finished, or the timeout is reached.
|
108
134
|
#
|
109
135
|
def wait(futures, timeout: nil, catch_errors: false, **_kwargs)
|
136
|
+
if futures.nil?
|
137
|
+
results = []
|
138
|
+
plan_id = get_current_plan_id(fiber: Fiber.current)
|
139
|
+
# Recollect the futures for this plan until all of the futures have
|
140
|
+
# finished. This ensures that we include futures created inside of
|
141
|
+
# futures being waited on.
|
142
|
+
until (futures = get_futures_for_plan(plan_id: plan_id)).map(&:alive?).none?
|
143
|
+
if futures.map(&:fiber).include?(Fiber.current)
|
144
|
+
msg = "The wait() function cannot be called with no arguments inside a "\
|
145
|
+
"background block in the same plan."
|
146
|
+
raise Bolt::Error.new(msg, 'bolt/infinite-wait')
|
147
|
+
end
|
148
|
+
# Wait for all the futures we know about so far before recollecting
|
149
|
+
# Futures for the plan and waiting again
|
150
|
+
results = wait(futures, timeout: timeout, catch_errors: catch_errors)
|
151
|
+
end
|
152
|
+
return results
|
153
|
+
end
|
154
|
+
|
110
155
|
if timeout.nil?
|
111
156
|
Fiber.yield(:unfinished) until futures.map(&:alive?).none?
|
112
157
|
else
|