bolt 3.12.0 → 3.16.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 +1 -1
- 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 +3 -20
- data/lib/bolt/application.rb +620 -0
- data/lib/bolt/bolt_option_parser.rb +18 -6
- data/lib/bolt/cli.rb +592 -772
- 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/executor.rb +12 -4
- data/lib/bolt/fiber_executor.rb +63 -14
- data/lib/bolt/outputter/human.rb +201 -43
- data/lib/bolt/outputter/json.rb +68 -43
- data/lib/bolt/outputter/logger.rb +6 -0
- data/lib/bolt/pal.rb +67 -14
- 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_creator.rb +2 -20
- data/lib/bolt/plan_future.rb +23 -3
- data/lib/bolt/plan_result.rb +1 -1
- data/lib/bolt/plugin/task.rb +1 -1
- data/lib/bolt/project.rb +0 -7
- data/lib/bolt/result_set.rb +2 -1
- data/lib/bolt/transport/local/connection.rb +17 -1
- data/lib/bolt/transport/orch/connection.rb +13 -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/partials/target-ssh.json +4 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +4 -0
- data/lib/bolt_server/transport_app.rb +92 -50
- 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/resources/bolt_bash_completion.sh +1 -1
- metadata +29 -15
- data/guides/guide.txt +0 -17
- data/lib/bolt/secret.rb +0 -37
data/lib/bolt/plan_result.rb
CHANGED
data/lib/bolt/plugin/task.rb
CHANGED
@@ -59,7 +59,7 @@ module Bolt
|
|
59
59
|
run_opts = {}
|
60
60
|
run_opts[:run_as] = opts['_run_as'] if opts['_run_as']
|
61
61
|
begin
|
62
|
-
task =
|
62
|
+
task = @context.get_validated_task(opts['task'], params)
|
63
63
|
rescue Bolt::Error => e
|
64
64
|
raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, name, 'puppet_library')
|
65
65
|
end
|
data/lib/bolt/project.rb
CHANGED
@@ -209,12 +209,5 @@ module Bolt
|
|
209
209
|
Bolt::Logger.warn("missing_project_name", message)
|
210
210
|
end
|
211
211
|
end
|
212
|
-
|
213
|
-
def check_deprecated_file
|
214
|
-
if (@path + 'project.yaml').file?
|
215
|
-
msg = "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
|
216
|
-
Bolt::Logger.warn("project_yaml", msg)
|
217
|
-
end
|
218
|
-
end
|
219
212
|
end
|
220
213
|
end
|
data/lib/bolt/result_set.rb
CHANGED
@@ -10,6 +10,8 @@ module Bolt
|
|
10
10
|
module Transport
|
11
11
|
class Local < Simple
|
12
12
|
class Connection
|
13
|
+
RUBY_ENV_VARS = %w[GEM_PATH GEM_HOME RUBYLIB RUBYLIB_PREFIX RUBYOPT RUBYPATH RUBYSHELL].freeze
|
14
|
+
|
13
15
|
attr_accessor :user, :logger, :target
|
14
16
|
|
15
17
|
def initialize(target)
|
@@ -68,7 +70,21 @@ module Bolt
|
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
71
|
-
|
73
|
+
# Only do this if bundled-ruby is set to false, not nil
|
74
|
+
ruby_env_vars = if target.transport_config['bundled-ruby'] == false
|
75
|
+
RUBY_ENV_VARS.each_with_object({}) do |e, acc|
|
76
|
+
acc[e] = ENV["BOLT_ORIG_#{e}"] if ENV["BOLT_ORIG_#{e}"]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if target.transport_config['bundled-ruby'] == false &&
|
81
|
+
Gem.loaded_specs.keys.include?('bundler')
|
82
|
+
Bundler.with_unbundled_env do
|
83
|
+
Open3.popen3(ruby_env_vars || {}, *command)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
Open3.popen3(ruby_env_vars || {}, *command)
|
87
|
+
end
|
72
88
|
end
|
73
89
|
|
74
90
|
# This is used by the Bash shell to decide whether to `cd` before
|
@@ -36,7 +36,8 @@ module Bolt
|
|
36
36
|
end
|
37
37
|
logger.debug("Creating orchestrator client for #{client_opts}")
|
38
38
|
@client = OrchestratorClient.new(client_opts, true)
|
39
|
-
@
|
39
|
+
@plan_context = plan_context
|
40
|
+
@plan_job = start_plan(@plan_context)
|
40
41
|
logger.debug("Started plan #{@plan_job}")
|
41
42
|
@environment = opts["task-environment"]
|
42
43
|
end
|
@@ -87,6 +88,17 @@ module Bolt
|
|
87
88
|
def run_task(targets, task, arguments, options)
|
88
89
|
body = build_request(targets, task, arguments, options[:description])
|
89
90
|
@client.run_task(body)
|
91
|
+
rescue OrchestratorClient::ApiError => e
|
92
|
+
if e.data['kind'] == 'puppetlabs.orchestrator/plan-already-finished'
|
93
|
+
@logger.debug("Retrying the task")
|
94
|
+
# Instead of recursing, just retry once
|
95
|
+
@plan_job = start_plan(@plan_context)
|
96
|
+
# Rebuild the request with the new plan job ID
|
97
|
+
body = build_request(targets, task, arguments, options[:description])
|
98
|
+
@client.run_task(body)
|
99
|
+
else
|
100
|
+
raise e
|
101
|
+
end
|
90
102
|
end
|
91
103
|
|
92
104
|
def query_inventory(targets)
|
@@ -47,7 +47,9 @@ module Bolt
|
|
47
47
|
cmd = []
|
48
48
|
# BatchMode is SSH's noninteractive option: if key authentication
|
49
49
|
# fails it will error out instead of falling back to password prompt
|
50
|
-
|
50
|
+
batch_mode = @target.transport_config['batch-mode'] ? 'yes' : 'no'
|
51
|
+
cmd += %W[-o BatchMode=#{batch_mode}]
|
52
|
+
|
51
53
|
cmd += %W[-o Port=#{@target.port}] if @target.port
|
52
54
|
|
53
55
|
if @target.transport_config.key?('host-key-check')
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
module Util
|
5
|
+
module Format
|
6
|
+
class << self
|
7
|
+
# Stringifies an object, formatted as valid JSON.
|
8
|
+
#
|
9
|
+
# @param message [Object] The object to stringify.
|
10
|
+
# @return [String] The JSON string.
|
11
|
+
#
|
12
|
+
def stringify(message)
|
13
|
+
formatted = format_message(message)
|
14
|
+
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
15
|
+
::JSON.pretty_generate(formatted)
|
16
|
+
else
|
17
|
+
formatted
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Recursively formats an object into a format that can be represented by
|
22
|
+
# JSON.
|
23
|
+
#
|
24
|
+
# @param message [Object] The object to stringify.
|
25
|
+
# @return [Array, Hash, String]
|
26
|
+
#
|
27
|
+
private def format_message(message)
|
28
|
+
case message
|
29
|
+
when Array
|
30
|
+
message.map { |item| format_message(item) }
|
31
|
+
when Bolt::ApplyResult
|
32
|
+
format_apply_result(message)
|
33
|
+
when Bolt::Result, Bolt::ResultSet
|
34
|
+
# This is equivalent to to_s, but formattable
|
35
|
+
message.to_data
|
36
|
+
when Bolt::RunFailure
|
37
|
+
formatted_resultset = message.result_set.to_data
|
38
|
+
message.to_h.merge('result_set' => formatted_resultset)
|
39
|
+
when Hash
|
40
|
+
message.each_with_object({}) do |(k, v), h|
|
41
|
+
h[format_message(k)] = format_message(v)
|
42
|
+
end
|
43
|
+
when Integer, Float, NilClass
|
44
|
+
message
|
45
|
+
else
|
46
|
+
message.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Formats a Bolt::ApplyResult object.
|
51
|
+
#
|
52
|
+
# @param result [Bolt::ApplyResult] The apply result.
|
53
|
+
# @return [Hash]
|
54
|
+
#
|
55
|
+
private def format_apply_result(result)
|
56
|
+
logs = result.resource_logs&.map do |log|
|
57
|
+
# Omit low-level info/debug messages
|
58
|
+
next if %w[info debug].include?(log['level'])
|
59
|
+
indent(2, format_log(log))
|
60
|
+
end
|
61
|
+
hash = result.to_data
|
62
|
+
hash['logs'] = logs unless logs.empty?
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/bolt/version.rb
CHANGED
@@ -120,9 +120,20 @@ module BoltServer
|
|
120
120
|
|
121
121
|
def run_task(target, body)
|
122
122
|
validate_schema(@schemas["action-run_task"], body)
|
123
|
+
|
123
124
|
task_data = body['task']
|
124
125
|
task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
|
125
126
|
parameters = body['parameters'] || {}
|
127
|
+
# Wrap parameters marked with '"sensitive": true' in the task metadata with a
|
128
|
+
# Sensitive wrapper type. This way it's not shown in logs.
|
129
|
+
if (param_spec = task.parameters)
|
130
|
+
parameters.each do |k, v|
|
131
|
+
if param_spec[k] && param_spec[k]['sensitive']
|
132
|
+
parameters[k] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(v)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
126
137
|
@executor.run_task(target, task, parameters).each do |result|
|
127
138
|
value = result.value
|
128
139
|
next unless value.is_a?(Hash)
|
@@ -378,6 +389,63 @@ module BoltServer
|
|
378
389
|
end
|
379
390
|
end
|
380
391
|
|
392
|
+
# The provided block takes a module object and returns the list
|
393
|
+
# of directories to search through. This is similar to
|
394
|
+
# Bolt::Applicator.build_plugin_tarball.
|
395
|
+
def build_project_plugins_tarball(versioned_project, &block)
|
396
|
+
start_time = Time.now
|
397
|
+
|
398
|
+
# Fetch the plugin files
|
399
|
+
plugin_files = in_bolt_project(versioned_project) do |context|
|
400
|
+
files = {}
|
401
|
+
|
402
|
+
# Bolt also sets plugin_modulepath to user modulepath so do it here too for
|
403
|
+
# consistency
|
404
|
+
plugin_modulepath = context[:pal].user_modulepath
|
405
|
+
Puppet.lookup(:current_environment).override_with(modulepath: plugin_modulepath).modules.each do |mod|
|
406
|
+
search_dirs = block.call(mod)
|
407
|
+
|
408
|
+
files[mod] ||= []
|
409
|
+
Find.find(*search_dirs).each do |file|
|
410
|
+
files[mod] << file if File.file?(file)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
files
|
415
|
+
end
|
416
|
+
|
417
|
+
# Pack the plugin files
|
418
|
+
sio = StringIO.new
|
419
|
+
begin
|
420
|
+
output = Minitar::Output.new(Zlib::GzipWriter.new(sio))
|
421
|
+
|
422
|
+
plugin_files.each do |mod, files|
|
423
|
+
tar_dir = Pathname.new(mod.name)
|
424
|
+
mod_dir = Pathname.new(mod.path)
|
425
|
+
|
426
|
+
files.each do |file|
|
427
|
+
tar_path = tar_dir + Pathname.new(file).relative_path_from(mod_dir)
|
428
|
+
stat = File.stat(file)
|
429
|
+
content = File.binread(file)
|
430
|
+
output.tar.add_file_simple(
|
431
|
+
tar_path.to_s,
|
432
|
+
data: content,
|
433
|
+
size: content.size,
|
434
|
+
mode: stat.mode & 0o777,
|
435
|
+
mtime: stat.mtime
|
436
|
+
)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
duration = Time.now - start_time
|
441
|
+
@logger.trace("Packed plugins in #{duration * 1000} ms")
|
442
|
+
ensure
|
443
|
+
output.close
|
444
|
+
end
|
445
|
+
|
446
|
+
Base64.encode64(sio.string)
|
447
|
+
end
|
448
|
+
|
381
449
|
get '/' do
|
382
450
|
200
|
383
451
|
end
|
@@ -430,7 +498,7 @@ module BoltServer
|
|
430
498
|
'uri' => target_hash['hostname'],
|
431
499
|
'config' => {
|
432
500
|
'transport' => 'ssh',
|
433
|
-
'ssh' => opts
|
501
|
+
'ssh' => opts.slice(*Bolt::Config::Transport::SSH.options)
|
434
502
|
}
|
435
503
|
}
|
436
504
|
|
@@ -468,7 +536,7 @@ module BoltServer
|
|
468
536
|
'uri' => target_hash['hostname'],
|
469
537
|
'config' => {
|
470
538
|
'transport' => 'winrm',
|
471
|
-
'winrm' => opts
|
539
|
+
'winrm' => opts.slice(*Bolt::Config::Transport::WinRM.options)
|
472
540
|
}
|
473
541
|
}
|
474
542
|
|
@@ -688,60 +756,34 @@ module BoltServer
|
|
688
756
|
raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
|
689
757
|
content_type :json
|
690
758
|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
files = {}
|
697
|
-
|
698
|
-
# Bolt also sets plugin_modulepath to user modulepath so do it here too for
|
699
|
-
# consistency
|
700
|
-
plugin_modulepath = context[:pal].user_modulepath
|
701
|
-
Puppet.lookup(:current_environment).override_with(modulepath: plugin_modulepath).modules.each do |mod|
|
702
|
-
search_dirs = []
|
703
|
-
search_dirs << mod.plugins if mod.plugins?
|
704
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
705
|
-
|
706
|
-
files[mod] ||= []
|
707
|
-
Find.find(*search_dirs).each do |file|
|
708
|
-
files[mod] << file if File.file?(file)
|
709
|
-
end
|
710
|
-
end
|
711
|
-
|
712
|
-
files
|
759
|
+
plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
|
760
|
+
search_dirs = []
|
761
|
+
search_dirs << mod.plugins if mod.plugins?
|
762
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
763
|
+
search_dirs
|
713
764
|
end
|
714
765
|
|
715
|
-
|
716
|
-
|
717
|
-
begin
|
718
|
-
output = Minitar::Output.new(Zlib::GzipWriter.new(sio))
|
719
|
-
|
720
|
-
plugin_files.each do |mod, files|
|
721
|
-
tar_dir = Pathname.new(mod.name)
|
722
|
-
mod_dir = Pathname.new(mod.path)
|
766
|
+
[200, plugins_tarball.to_json]
|
767
|
+
end
|
723
768
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
data: content,
|
731
|
-
size: content.size,
|
732
|
-
mode: stat.mode & 0o777,
|
733
|
-
mtime: stat.mtime
|
734
|
-
)
|
735
|
-
end
|
736
|
-
end
|
769
|
+
# Returns the base64 encoded tar archive of _all_ plugin code for a project
|
770
|
+
#
|
771
|
+
# @param versioned_project [String] the versioned_project to build the plugin tarball from
|
772
|
+
get '/project_plugin_tarball' do
|
773
|
+
raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
|
774
|
+
content_type :json
|
737
775
|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
776
|
+
plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
|
777
|
+
search_dirs = []
|
778
|
+
search_dirs << mod.plugins if mod.plugins?
|
779
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
780
|
+
search_dirs << mod.files if mod.files?
|
781
|
+
type_files = "#{mod.path}/types"
|
782
|
+
search_dirs << type_files if File.exist?(type_files)
|
783
|
+
search_dirs
|
742
784
|
end
|
743
785
|
|
744
|
-
[200,
|
786
|
+
[200, plugins_tarball.to_json]
|
745
787
|
end
|
746
788
|
|
747
789
|
error 404 do
|
@@ -191,6 +191,15 @@ module BoltSpec
|
|
191
191
|
allow_out_message.expect_call
|
192
192
|
end
|
193
193
|
|
194
|
+
def allow_out_verbose
|
195
|
+
executor.stub_out_verbose.add_stub
|
196
|
+
end
|
197
|
+
alias allow_any_out_verbose allow_out_verbose
|
198
|
+
|
199
|
+
def expect_out_verbose
|
200
|
+
allow_out_verbose.expect_call
|
201
|
+
end
|
202
|
+
|
194
203
|
# Example helpers to mock other run functions
|
195
204
|
# The with_targets method makes sense for all stubs
|
196
205
|
# with_params could be reused for options
|
data/lib/bolt_spec/plans.rb
CHANGED
@@ -98,7 +98,7 @@ module BoltSpec
|
|
98
98
|
Puppet[:tasks] = true
|
99
99
|
|
100
100
|
# Ensure logger is initialized with Puppet levels so 'notice' works when running plan specs.
|
101
|
-
Logging.init :trace, :debug, :info, :notice, :warn, :error, :fatal
|
101
|
+
Logging.init :trace, :debug, :info, :notice, :warn, :error, :fatal
|
102
102
|
end
|
103
103
|
|
104
104
|
# Provided as a class so expectations can be placed on it.
|
@@ -29,6 +29,7 @@ module BoltSpec
|
|
29
29
|
@modulepath = [modulepath].flatten.map { |path| File.absolute_path(path) }
|
30
30
|
MOCKED_ACTIONS.each { |action| instance_variable_set(:"@#{action}_doubles", {}) }
|
31
31
|
@stub_out_message = nil
|
32
|
+
@stub_out_verbose = nil
|
32
33
|
@transport_features = ['puppet-agent']
|
33
34
|
@executor_real = Bolt::Executor.new
|
34
35
|
# by default, we want to execute any plan that we come across without error
|
@@ -38,6 +39,7 @@ module BoltSpec
|
|
38
39
|
# plans that are allowed to be executed by the @executor_real
|
39
40
|
@allowed_exec_plans = {}
|
40
41
|
@id = 0
|
42
|
+
@plan_futures = []
|
41
43
|
end
|
42
44
|
|
43
45
|
def module_file_id(file)
|
@@ -187,6 +189,7 @@ module BoltSpec
|
|
187
189
|
end
|
188
190
|
end
|
189
191
|
@stub_out_message.assert_called('out::message') if @stub_out_message
|
192
|
+
@stub_out_verbose.assert_called('out::verbose') if @stub_out_verbose
|
190
193
|
end
|
191
194
|
|
192
195
|
MOCKED_ACTIONS.each do |action|
|
@@ -199,6 +202,10 @@ module BoltSpec
|
|
199
202
|
@stub_out_message ||= ActionDouble.new(:PublishStub)
|
200
203
|
end
|
201
204
|
|
205
|
+
def stub_out_verbose
|
206
|
+
@stub_out_verbose ||= ActionDouble.new(:PublishStub)
|
207
|
+
end
|
208
|
+
|
202
209
|
def stub_apply
|
203
210
|
@allow_apply = true
|
204
211
|
end
|
@@ -220,12 +227,20 @@ module BoltSpec
|
|
220
227
|
end
|
221
228
|
|
222
229
|
def publish_event(event)
|
223
|
-
|
230
|
+
case event[:type]
|
231
|
+
when :message
|
224
232
|
unless @stub_out_message
|
225
233
|
@error_message = "Unexpected call to 'out::message(#{event[:message]})'"
|
226
234
|
raise UnexpectedInvocation, @error_message
|
227
235
|
end
|
228
236
|
@stub_out_message.process(event[:message])
|
237
|
+
|
238
|
+
when :verbose
|
239
|
+
unless @stub_out_verbose
|
240
|
+
@error_message = "Unexpected call to 'out::verbose(#{event[:message]})'"
|
241
|
+
raise UnexpectedInvocation, @error_message
|
242
|
+
end
|
243
|
+
@stub_out_verbose.process(event[:message])
|
229
244
|
end
|
230
245
|
end
|
231
246
|
|
@@ -266,7 +281,7 @@ module BoltSpec
|
|
266
281
|
false
|
267
282
|
end
|
268
283
|
|
269
|
-
def create_future(scope: nil, name: nil)
|
284
|
+
def create_future(plan_id:, scope: nil, name: nil)
|
270
285
|
newscope = nil
|
271
286
|
if scope
|
272
287
|
# Create the new scope
|
@@ -281,13 +296,18 @@ module BoltSpec
|
|
281
296
|
# Execute "futures" serially when running in BoltSpec
|
282
297
|
result = yield newscope
|
283
298
|
@id += 1
|
284
|
-
future = Bolt::PlanFuture.new(nil, @id, name: name)
|
299
|
+
future = Bolt::PlanFuture.new(nil, @id, name: name, plan_id: plan_id)
|
285
300
|
future.value = result
|
301
|
+
@plan_futures << future
|
286
302
|
future
|
287
303
|
end
|
288
304
|
|
289
|
-
def
|
290
|
-
|
305
|
+
def get_futures_for_plan(plan_id:)
|
306
|
+
@plan_futures.select { |future| future.plan_id == plan_id }
|
307
|
+
end
|
308
|
+
|
309
|
+
def wait(futures, **_kwargs)
|
310
|
+
futures.map(&:value)
|
291
311
|
end
|
292
312
|
|
293
313
|
# Since Futures are executed immediately once created, this will always
|
@@ -296,8 +316,12 @@ module BoltSpec
|
|
296
316
|
true
|
297
317
|
end
|
298
318
|
|
299
|
-
def
|
300
|
-
|
319
|
+
def get_current_future(fiber)
|
320
|
+
@plan_futures.select { |f| f.fiber == fiber }&.first
|
321
|
+
end
|
322
|
+
|
323
|
+
def get_current_plan_id(fiber)
|
324
|
+
get_current_future(fiber)&.current_plan
|
301
325
|
end
|
302
326
|
|
303
327
|
# Public methods on Bolt::Executor that need to be mocked so there aren't
|