bolt 3.11.0 → 3.15.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 +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
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class PAL
|
5
|
+
class YamlPlan
|
6
|
+
class Step
|
7
|
+
class Verbose < Step
|
8
|
+
def self.allowed_keys
|
9
|
+
super + Set['verbose']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_keys
|
13
|
+
Set['verbose']
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns an array of arguments to pass to the step's function call
|
17
|
+
#
|
18
|
+
private def format_args(body)
|
19
|
+
[body['verbose']]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the function corresponding to the step
|
23
|
+
#
|
24
|
+
private def function
|
25
|
+
'out::verbose'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -30,7 +30,7 @@ module Bolt
|
|
30
30
|
plan_string = String.new('')
|
31
31
|
plan_string << "# #{plan_object.description}\n" if plan_object.description
|
32
32
|
plan_string << "# WARNING: This is an autogenerated plan. It might not behave as expected.\n"
|
33
|
-
plan_string << "# @
|
33
|
+
plan_string << "# @api #{plan_object.private ? 'private' : 'public'}\n" unless plan_object.private.nil?
|
34
34
|
plan_string << "#{param_descriptions}\n" unless param_descriptions.empty?
|
35
35
|
|
36
36
|
plan_string << "plan #{plan_object.name}("
|
data/lib/bolt/plan_future.rb
CHANGED
@@ -5,13 +5,28 @@ require 'fiber'
|
|
5
5
|
module Bolt
|
6
6
|
class PlanFuture
|
7
7
|
attr_reader :fiber, :id
|
8
|
-
attr_accessor :value
|
8
|
+
attr_accessor :value, :plan_stack
|
9
9
|
|
10
|
-
def initialize(fiber, id, name
|
11
|
-
@fiber
|
12
|
-
@id
|
13
|
-
@name
|
14
|
-
@value
|
10
|
+
def initialize(fiber, id, plan_id:, name: nil)
|
11
|
+
@fiber = fiber
|
12
|
+
@id = id
|
13
|
+
@name = name
|
14
|
+
@value = nil
|
15
|
+
# The plan invocation ID when the Future is created may be
|
16
|
+
# different from the plan ID of the Future when we switch to it if a new
|
17
|
+
# plan was run inside the Future, so keep track of the plans that a
|
18
|
+
# Future is executing in as a stack. When one plan finishes, pop it off
|
19
|
+
# since now we're in the calling plan. These IDs are unique to each plan
|
20
|
+
# invocation, not just plan names.
|
21
|
+
@plan_stack = [plan_id]
|
22
|
+
end
|
23
|
+
|
24
|
+
def original_plan
|
25
|
+
@plan_stack.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_plan
|
29
|
+
@plan_stack.first
|
15
30
|
end
|
16
31
|
|
17
32
|
def name
|
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
|
@@ -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
|
|
@@ -637,11 +705,10 @@ module BoltServer
|
|
637
705
|
#
|
638
706
|
# @param versioned_project [String] the versioned_project to compute the inventory from
|
639
707
|
post '/project_inventory_targets' do
|
640
|
-
raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
|
641
708
|
content_type :json
|
642
709
|
body = JSON.parse(request.body.read)
|
643
710
|
validate_schema(@schemas["connect-data"], body)
|
644
|
-
in_bolt_project(
|
711
|
+
in_bolt_project(body['versioned_project']) do |context|
|
645
712
|
if context[:config].inventoryfile &&
|
646
713
|
context[:config].project.inventory_file.to_s !=
|
647
714
|
context[:config].inventoryfile
|
@@ -689,60 +756,34 @@ module BoltServer
|
|
689
756
|
raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
|
690
757
|
content_type :json
|
691
758
|
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
files = {}
|
698
|
-
|
699
|
-
# Bolt also sets plugin_modulepath to user modulepath so do it here too for
|
700
|
-
# consistency
|
701
|
-
plugin_modulepath = context[:pal].user_modulepath
|
702
|
-
Puppet.lookup(:current_environment).override_with(modulepath: plugin_modulepath).modules.each do |mod|
|
703
|
-
search_dirs = []
|
704
|
-
search_dirs << mod.plugins if mod.plugins?
|
705
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
706
|
-
|
707
|
-
files[mod] ||= []
|
708
|
-
Find.find(*search_dirs).each do |file|
|
709
|
-
files[mod] << file if File.file?(file)
|
710
|
-
end
|
711
|
-
end
|
712
|
-
|
713
|
-
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
|
714
764
|
end
|
715
765
|
|
716
|
-
|
717
|
-
|
718
|
-
begin
|
719
|
-
output = Minitar::Output.new(Zlib::GzipWriter.new(sio))
|
720
|
-
|
721
|
-
plugin_files.each do |mod, files|
|
722
|
-
tar_dir = Pathname.new(mod.name)
|
723
|
-
mod_dir = Pathname.new(mod.path)
|
766
|
+
[200, plugins_tarball.to_json]
|
767
|
+
end
|
724
768
|
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
data: content,
|
732
|
-
size: content.size,
|
733
|
-
mode: stat.mode & 0o777,
|
734
|
-
mtime: stat.mtime
|
735
|
-
)
|
736
|
-
end
|
737
|
-
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
|
738
775
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
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
|
743
784
|
end
|
744
785
|
|
745
|
-
[200,
|
786
|
+
[200, plugins_tarball.to_json]
|
746
787
|
end
|
747
788
|
|
748
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
|