bolt 3.6.1 → 3.9.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 +3 -3
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +26 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +27 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +43 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +34 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +55 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +10 -6
- 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 +29 -13
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +66 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +5 -15
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +10 -18
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +5 -17
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +5 -15
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +10 -18
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +91 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +9 -3
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +6 -2
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +8 -3
- data/guides/guide.txt +17 -0
- data/guides/inventory.txt +5 -0
- data/guides/links.txt +13 -0
- data/guides/targets.txt +29 -0
- data/guides/transports.txt +23 -0
- data/lib/bolt/applicator.rb +4 -3
- data/lib/bolt/bolt_option_parser.rb +353 -227
- data/lib/bolt/catalog.rb +2 -1
- data/lib/bolt/cli.rb +94 -36
- data/lib/bolt/config/options.rb +2 -1
- data/lib/bolt/config/transport/docker.rb +5 -1
- data/lib/bolt/config/transport/lxd.rb +1 -1
- data/lib/bolt/config/transport/options.rb +2 -1
- data/lib/bolt/config/transport/podman.rb +5 -1
- data/lib/bolt/error.rb +11 -1
- data/lib/bolt/executor.rb +51 -72
- data/lib/bolt/fiber_executor.rb +141 -0
- data/lib/bolt/inventory.rb +5 -4
- data/lib/bolt/inventory/inventory.rb +3 -2
- data/lib/bolt/logger.rb +1 -1
- data/lib/bolt/module_installer/specs.rb +1 -1
- data/lib/bolt/module_installer/specs/git_spec.rb +10 -6
- data/lib/bolt/outputter/human.rb +59 -29
- data/lib/bolt/outputter/json.rb +8 -4
- data/lib/bolt/pal.rb +64 -3
- data/lib/bolt/pal/yaml_plan/step.rb +4 -2
- data/lib/bolt/plan_creator.rb +2 -2
- data/lib/bolt/plan_future.rb +66 -0
- data/lib/bolt/puppetdb/client.rb +54 -0
- data/lib/bolt/result.rb +5 -0
- data/lib/bolt/transport/docker/connection.rb +7 -4
- data/lib/bolt/transport/lxd/connection.rb +4 -0
- data/lib/bolt/transport/podman/connection.rb +4 -0
- data/lib/bolt/transport/ssh/connection.rb +3 -6
- data/lib/bolt/util.rb +73 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +42 -45
- metadata +12 -3
- data/lib/bolt/yarn.rb +0 -23
data/lib/bolt/pal.rb
CHANGED
@@ -607,13 +607,74 @@ module Bolt
|
|
607
607
|
end
|
608
608
|
end
|
609
609
|
|
610
|
-
def run_plan(plan_name, params, executor
|
610
|
+
def run_plan(plan_name, params, executor, inventory = nil, pdb_client = nil, applicator = nil)
|
611
|
+
# Start the round robin inside the plan compiler, so that
|
612
|
+
# backgrounded tasks can finish once the main plan exits
|
611
613
|
in_plan_compiler(executor, inventory, pdb_client, applicator) do |compiler|
|
612
|
-
|
613
|
-
|
614
|
+
# Create a Fiber for the main plan. This will be run along with any
|
615
|
+
# other Fibers created during the plan run in the round_robin, with the
|
616
|
+
# main plan always taking precedence in being resumed.
|
617
|
+
future = executor.create_future(name: plan_name) do |_scope|
|
618
|
+
r = compiler.call_function('run_plan', plan_name, params.merge('_bolt_api_call' => true))
|
619
|
+
Bolt::PlanResult.from_pcore(r, 'success')
|
620
|
+
rescue Bolt::Error => e
|
621
|
+
Bolt::PlanResult.new(e, 'failure')
|
622
|
+
end
|
623
|
+
|
624
|
+
# Round robin until all Fibers, including the main plan, have finished.
|
625
|
+
# This will stay alive until backgrounded tasks have finished.
|
626
|
+
executor.round_robin until executor.plan_complete?
|
627
|
+
|
628
|
+
# Return the result from the main plan
|
629
|
+
future.value
|
614
630
|
end
|
615
631
|
rescue Bolt::Error => e
|
616
632
|
Bolt::PlanResult.new(e, 'failure')
|
617
633
|
end
|
634
|
+
|
635
|
+
def lookup(key, targets, inventory, executor, _concurrency)
|
636
|
+
# Install the puppet-agent package and collect facts. Facts are
|
637
|
+
# automatically added to the targets.
|
638
|
+
in_plan_compiler(executor, inventory, nil) do |compiler|
|
639
|
+
compiler.call_function('apply_prep', targets)
|
640
|
+
end
|
641
|
+
|
642
|
+
overrides = {
|
643
|
+
bolt_inventory: inventory,
|
644
|
+
bolt_project: @project
|
645
|
+
}
|
646
|
+
|
647
|
+
# Do a lookup with a catalog compiler, which uses the 'hierarchy' key in
|
648
|
+
# Hiera config.
|
649
|
+
results = targets.map do |target|
|
650
|
+
node = Puppet::Node.from_data_hash(
|
651
|
+
'name' => target.name,
|
652
|
+
'parameters' => { 'clientcert' => target.name }
|
653
|
+
)
|
654
|
+
|
655
|
+
trusted = Puppet::Context::TrustedInformation.local(node).to_h
|
656
|
+
|
657
|
+
env_conf = {
|
658
|
+
modulepath: @modulepath.full_modulepath,
|
659
|
+
facts: target.facts,
|
660
|
+
variables: target.vars
|
661
|
+
}
|
662
|
+
|
663
|
+
with_puppet_settings do
|
664
|
+
Puppet::Pal.in_tmp_environment(target.name, **env_conf) do |pal|
|
665
|
+
Puppet.override(overrides) do
|
666
|
+
Puppet.lookup(:pal_current_node).trusted_data = trusted
|
667
|
+
pal.with_catalog_compiler do |compiler|
|
668
|
+
Bolt::Result.for_lookup(target, key, compiler.call_function('lookup', key))
|
669
|
+
rescue StandardError => e
|
670
|
+
Bolt::Result.from_exception(target, e)
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
Bolt::ResultSet.new(results)
|
678
|
+
end
|
618
679
|
end
|
619
680
|
end
|
@@ -122,9 +122,11 @@ module Bolt
|
|
122
122
|
raise StepError.new("Parameters key must be a hash", body['name'], step_number)
|
123
123
|
end
|
124
124
|
|
125
|
-
metaparams =
|
125
|
+
metaparams = body['parameters'].keys
|
126
|
+
.select { |key| key.start_with?('_') }
|
127
|
+
.map { |key| key.sub(/^_/, '') }
|
126
128
|
|
127
|
-
if (dups = body
|
129
|
+
if (dups = body.keys & metaparams).any?
|
128
130
|
raise StepError.new(
|
129
131
|
"Cannot specify metaparameters when using top-level keys with same name: #{dups.join(', ')}",
|
130
132
|
body['name'],
|
data/lib/bolt/plan_creator.rb
CHANGED
@@ -36,8 +36,8 @@ module Bolt
|
|
36
36
|
prefix, _, basename = segment_plan_name(plan_name)
|
37
37
|
|
38
38
|
unless prefix == project.name
|
39
|
-
message = "
|
40
|
-
|
39
|
+
message = "Incomplete plan name: A plan name must be prefixed with the name of the "\
|
40
|
+
"project or module. Did you mean '#{project.name}::#{plan_name}'?"
|
41
41
|
|
42
42
|
raise Bolt::ValidationError, message
|
43
43
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiber'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class PlanFuture
|
7
|
+
attr_reader :fiber, :id
|
8
|
+
attr_accessor :value
|
9
|
+
|
10
|
+
def initialize(fiber, id, name = nil)
|
11
|
+
@fiber = fiber
|
12
|
+
@id = id
|
13
|
+
@name = name
|
14
|
+
@value = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@name || @id
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"Future '#{name}'"
|
23
|
+
end
|
24
|
+
|
25
|
+
def alive?
|
26
|
+
fiber.alive?
|
27
|
+
end
|
28
|
+
|
29
|
+
def raise(exception)
|
30
|
+
# Make sure the value gets set
|
31
|
+
@value = exception
|
32
|
+
# This was introduced in Ruby 2.7
|
33
|
+
begin
|
34
|
+
# Raise an exception to kill the Fiber. If the Fiber has not been
|
35
|
+
# resumed yet, or is already terminated this will raise a FiberError.
|
36
|
+
# We don't especially care about the FiberError, as long as the Fiber
|
37
|
+
# doesn't report itself as alive.
|
38
|
+
fiber.raise(exception)
|
39
|
+
rescue FiberError
|
40
|
+
# If the Fiber is still alive, resume it with a block to raise the
|
41
|
+
# exception which will terminate it.
|
42
|
+
if fiber.alive?
|
43
|
+
fiber.resume { raise(exception) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def resume
|
49
|
+
if fiber.alive?
|
50
|
+
@value = fiber.resume
|
51
|
+
else
|
52
|
+
@value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def state
|
57
|
+
if fiber.alive?
|
58
|
+
"running"
|
59
|
+
elsif value.is_a?(Exception)
|
60
|
+
"error"
|
61
|
+
else
|
62
|
+
"done"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/bolt/puppetdb/client.rb
CHANGED
@@ -95,6 +95,60 @@ module Bolt
|
|
95
95
|
make_query(query, path)
|
96
96
|
end
|
97
97
|
|
98
|
+
# Sends a command to PuppetDB using version 1 of the commands API.
|
99
|
+
# https://puppet.com/docs/puppetdb/latest/api/command/v1/commands.html
|
100
|
+
#
|
101
|
+
# @param command [String] The command to invoke.
|
102
|
+
# @param version [Integer] The version of the command to invoke.
|
103
|
+
# @param payload [Hash] The payload to send with the command.
|
104
|
+
# @return A UUID identifying the submitted command.
|
105
|
+
#
|
106
|
+
def send_command(command, version, payload)
|
107
|
+
command = command.dup.force_encoding('utf-8')
|
108
|
+
body = JSON.generate(payload)
|
109
|
+
|
110
|
+
# PDB requires the following query parameters to the POST request.
|
111
|
+
# Error early if there's no certname, as PDB does not return a
|
112
|
+
# message indicating it's required.
|
113
|
+
unless payload['certname']
|
114
|
+
raise Bolt::Error.new(
|
115
|
+
"Payload must include 'certname', unable to invoke command.",
|
116
|
+
'bolt/pdb-command'
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
url = uri.tap do |u|
|
121
|
+
u.path = 'pdb/cmd/v1'
|
122
|
+
u.query_values = { 'command' => command,
|
123
|
+
'version' => version,
|
124
|
+
'certname' => payload['certname'] }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Send the command to PDB
|
128
|
+
begin
|
129
|
+
@logger.debug("Sending PuppetDB command '#{command}' to #{url}")
|
130
|
+
response = http_client.post(url.to_s, body: body, header: headers)
|
131
|
+
rescue StandardError => e
|
132
|
+
raise Bolt::PuppetDBFailoverError, "Failed to invoke PuppetDB command: #{e}"
|
133
|
+
end
|
134
|
+
|
135
|
+
@logger.debug("Got response code #{response.code} from PuppetDB")
|
136
|
+
if response.code != 200
|
137
|
+
raise Bolt::PuppetDBError, "Failed to invoke PuppetDB command: #{response.body}"
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the UUID string from the response body
|
141
|
+
begin
|
142
|
+
JSON.parse(response.body).fetch('uuid', nil)
|
143
|
+
rescue JSON::ParserError
|
144
|
+
raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
|
145
|
+
end
|
146
|
+
rescue Bolt::PuppetDBFailoverError => e
|
147
|
+
@logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
|
148
|
+
reject_url
|
149
|
+
send_command(command, version, payload)
|
150
|
+
end
|
151
|
+
|
98
152
|
def http_client
|
99
153
|
return @http if @http
|
100
154
|
# lazy-load expensive gem code
|
data/lib/bolt/result.rb
CHANGED
@@ -28,6 +28,11 @@ module Bolt
|
|
28
28
|
%w[file line].zip(position).to_h.compact
|
29
29
|
end
|
30
30
|
|
31
|
+
def self.for_lookup(target, key, value)
|
32
|
+
val = { 'value' => value }
|
33
|
+
new(target, value: val, action: 'lookup', object: key)
|
34
|
+
end
|
35
|
+
|
31
36
|
def self.for_command(target, value, action, command, position)
|
32
37
|
details = create_details(position)
|
33
38
|
unless value['exit_code'] == 0
|
@@ -27,6 +27,10 @@ module Bolt
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def reset_cwd?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
30
34
|
# The full ID of the target container
|
31
35
|
#
|
32
36
|
# @return [String] The full ID of the target container
|
@@ -46,8 +50,8 @@ module Bolt
|
|
46
50
|
def connect
|
47
51
|
# We don't actually have a connection, but we do need to
|
48
52
|
# check that the container exists and is running.
|
49
|
-
output = execute_local_json_command('ps')
|
50
|
-
index = output.find_index { |item| item["ID"]
|
53
|
+
output = execute_local_json_command('ps', ['--no-trunc'])
|
54
|
+
index = output.find_index { |item| item["ID"].start_with?(target.host) || item["Names"] == target.host }
|
51
55
|
raise "Could not find a container with name or ID matching '#{target.host}'" if index.nil?
|
52
56
|
# Now find the indepth container information
|
53
57
|
output = execute_local_json_command('inspect', [output[index]["ID"]])
|
@@ -74,7 +78,6 @@ module Bolt
|
|
74
78
|
# CODEREVIEW: Is it always safe to pass --interactive?
|
75
79
|
args += %w[--interactive]
|
76
80
|
args += %w[--tty] if target.options['tty']
|
77
|
-
args += %W[--env DOCKER_HOST=#{@docker_host}] if @docker_host
|
78
81
|
args += @env_vars if @env_vars
|
79
82
|
|
80
83
|
if target.options['shell-command'] && !target.options['shell-command'].empty?
|
@@ -86,7 +89,7 @@ module Bolt
|
|
86
89
|
docker_command = %w[docker exec] + args + [container_id] + Shellwords.split(command)
|
87
90
|
@logger.trace { "Executing: #{docker_command.join(' ')}" }
|
88
91
|
|
89
|
-
Open3.popen3(*docker_command)
|
92
|
+
Open3.popen3(env_hash, *docker_command)
|
90
93
|
rescue StandardError
|
91
94
|
@logger.trace { "Command aborted" }
|
92
95
|
raise
|
@@ -39,8 +39,6 @@ module Bolt
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
PAGEANT_NAME = "Pageant\0".encode(Encoding::UTF_16LE)
|
43
|
-
|
44
42
|
def connect
|
45
43
|
options = {
|
46
44
|
logger: @transport_logger,
|
@@ -115,10 +113,9 @@ module Bolt
|
|
115
113
|
options[:use_agent] = false
|
116
114
|
end
|
117
115
|
elsif Bolt::Util.windows?
|
118
|
-
|
119
|
-
#
|
120
|
-
|
121
|
-
if @find_window.call(nil, PAGEANT_NAME).to_i == 0
|
116
|
+
pageant = Net::SSH::Authentication::Pageant::Win.FindWindow("Pageant", "Pageant")
|
117
|
+
# If pageant is not running
|
118
|
+
if pageant.to_i == 0
|
122
119
|
@logger.debug { "Disabling use_agent in net-ssh: pageant process not running" }
|
123
120
|
options[:use_agent] = false
|
124
121
|
end
|
data/lib/bolt/util.rb
CHANGED
@@ -78,11 +78,83 @@ module Bolt
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def first_runs_free
|
81
|
+
# If this fails, use the system path instead
|
82
|
+
FileUtils.mkdir_p(Bolt::Config.user_path)
|
81
83
|
Bolt::Config.user_path + '.first_runs_free'
|
84
|
+
rescue StandardError
|
85
|
+
begin
|
86
|
+
# If using the system path fails, then don't bother with the welcome
|
87
|
+
# message
|
88
|
+
FileUtils.mkdir_p(Bolt::Config.system_path)
|
89
|
+
Bolt::Config.system_path + '.first_runs_free'
|
90
|
+
rescue StandardError
|
91
|
+
nil
|
92
|
+
end
|
82
93
|
end
|
83
94
|
|
84
95
|
def first_run?
|
85
|
-
|
96
|
+
!first_runs_free.nil? &&
|
97
|
+
!File.exist?(first_runs_free)
|
98
|
+
end
|
99
|
+
|
100
|
+
# If Puppet is loaded, we aleady have the path to the module and should
|
101
|
+
# just get it. This takes the path to a file provided by the user and a
|
102
|
+
# Puppet Parser scope object and tries to find the file, either as an
|
103
|
+
# absolute path or Puppet module syntax lookup. Returns the path to the
|
104
|
+
# file if found, or nil.
|
105
|
+
#
|
106
|
+
def find_file_from_scope(file, scope, fallback = false)
|
107
|
+
# If we got an absolute path, just return that.
|
108
|
+
return file if Pathname.new(file).absolute?
|
109
|
+
|
110
|
+
module_name, file_pattern = Bolt::Util.split_path(file)
|
111
|
+
# Get the absolute path to the module root from the scope
|
112
|
+
mod_path = scope.compiler.environment.module(module_name)&.path
|
113
|
+
|
114
|
+
# Search the module for the file, falling back to new-style paths if enabled.
|
115
|
+
find_file_in_module(mod_path, file_pattern, fallback) if mod_path
|
116
|
+
end
|
117
|
+
|
118
|
+
# This method is used by Bolt to find files when provided a
|
119
|
+
# module-style path without loading Puppet. It takes the absolute path to
|
120
|
+
# the module root and the module-style path minus the module name and
|
121
|
+
# searches subdirectories in the module in order of precedence.
|
122
|
+
#
|
123
|
+
def find_file_in_module(module_path, module_file, fallback = false)
|
124
|
+
# If the first part of the path is 'scripts' or 'files', the path may
|
125
|
+
# be a new-style file location and should fall back to the new path.
|
126
|
+
subdir_or_file = split_path(module_file).first
|
127
|
+
case subdir_or_file
|
128
|
+
# For any subdirs that may indicate the user passed a new-style path,
|
129
|
+
# first look in 'mymod/files/<relative_path>' (old-style) then fall
|
130
|
+
# back to 'mymod/<relative_path>' (new-style) if enabled.
|
131
|
+
when 'scripts', 'files'
|
132
|
+
search_module(module_path, module_file, fallback)
|
133
|
+
else
|
134
|
+
# If the path definitely isn't new-style, only look in the 'files/'
|
135
|
+
# directory.
|
136
|
+
search_module(module_path, module_file)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# This searches a module for files under 'files/' or 'scripts/',
|
141
|
+
# optionally falling back to the new style of file loading. It takes the
|
142
|
+
# absolute path to the module root, the relative path provided by the
|
143
|
+
# user, and whether to fall back to the new-style script loading if the
|
144
|
+
# file isn't found in 'files/'.
|
145
|
+
#
|
146
|
+
private def search_module(module_path, module_file, fallback = false)
|
147
|
+
if File.exist?(File.join(module_path, 'files', module_file))
|
148
|
+
File.join(module_path, 'files', module_file)
|
149
|
+
elsif File.exist?(File.join(module_path, module_file)) && fallback
|
150
|
+
File.join(module_path, module_file)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Copied directly from puppet/lib/puppet/parser/files.rb
|
155
|
+
#
|
156
|
+
def split_path(path)
|
157
|
+
path.split(File::SEPARATOR, 2)
|
86
158
|
end
|
87
159
|
|
88
160
|
# Accepts a path with either 'plans' or 'tasks' in it and determines
|
data/lib/bolt/version.rb
CHANGED
@@ -17,13 +17,12 @@ module BoltSpec
|
|
17
17
|
|
18
18
|
# Nothing on the executor is 'public'
|
19
19
|
class MockExecutor
|
20
|
-
attr_reader :noop, :error_message, :
|
20
|
+
attr_reader :noop, :error_message, :transports, :future
|
21
21
|
attr_accessor :run_as, :transport_features, :execute_any_plan
|
22
22
|
|
23
23
|
def initialize(modulepath)
|
24
24
|
@noop = false
|
25
25
|
@run_as = nil
|
26
|
-
@in_parallel = false
|
27
26
|
@future = {}
|
28
27
|
@error_message = nil
|
29
28
|
@allow_apply = false
|
@@ -38,6 +37,7 @@ module BoltSpec
|
|
38
37
|
@execute_any_plan = true
|
39
38
|
# plans that are allowed to be executed by the @executor_real
|
40
39
|
@allowed_exec_plans = {}
|
40
|
+
@id = 0
|
41
41
|
end
|
42
42
|
|
43
43
|
def module_file_id(file)
|
@@ -257,58 +257,53 @@ module BoltSpec
|
|
257
257
|
end
|
258
258
|
# End apply_prep mocking
|
259
259
|
|
260
|
-
#
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
# executed for each object sequentially, and this function returns the
|
265
|
-
# result itself.
|
266
|
-
#
|
267
|
-
def create_yarn(scope, block, object, _index)
|
268
|
-
# Create the new scope
|
269
|
-
newscope = Puppet::Parser::Scope.new(scope.compiler)
|
270
|
-
local = Puppet::Parser::Scope::LocalScope.new
|
271
|
-
|
272
|
-
# Compress the current scopes into a single vars hash to add to the new scope
|
273
|
-
current_scope = scope.effective_symtable(true)
|
274
|
-
until current_scope.nil?
|
275
|
-
current_scope.instance_variable_get(:@symbols)&.each_pair { |k, v| local[k] = v }
|
276
|
-
current_scope = current_scope.parent
|
277
|
-
end
|
278
|
-
newscope.push_ephemerals([local])
|
279
|
-
|
280
|
-
begin
|
281
|
-
result = catch(:return) do
|
282
|
-
args = { block.parameters[0][1].to_s => object }
|
283
|
-
block.closure.call_by_name_with_scope(newscope, args, true)
|
284
|
-
end
|
285
|
-
|
286
|
-
# If we got a return from the block, get it's value
|
287
|
-
# Otherwise the result is the last line from the block
|
288
|
-
result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
|
260
|
+
# Parallel function mocking
|
261
|
+
def run_in_thread
|
262
|
+
yield
|
263
|
+
end
|
289
264
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
end
|
265
|
+
def in_parallel?
|
266
|
+
false
|
267
|
+
end
|
294
268
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
269
|
+
def create_future(scope: nil, name: nil)
|
270
|
+
newscope = nil
|
271
|
+
if scope
|
272
|
+
# Create the new scope
|
273
|
+
newscope = Puppet::Parser::Scope.new(scope.compiler)
|
274
|
+
local = Puppet::Parser::Scope::LocalScope.new
|
275
|
+
|
276
|
+
# Compress the current scopes into a single vars hash to add to the new scope
|
277
|
+
current_scope = scope.effective_symtable(true)
|
278
|
+
until current_scope.nil?
|
279
|
+
current_scope.instance_variable_get(:@symbols)&.each_pair { |k, v| local[k] = v }
|
280
|
+
current_scope = current_scope.parent
|
301
281
|
end
|
282
|
+
newscope.push_ephemerals([local])
|
302
283
|
end
|
284
|
+
|
285
|
+
# Execute "futures" serially when running in BoltSpec
|
286
|
+
result = yield newscope
|
287
|
+
@id += 1
|
288
|
+
future = Bolt::PlanFuture.new(nil, @id, name: name)
|
289
|
+
future.value = result
|
290
|
+
future
|
303
291
|
end
|
304
292
|
|
305
|
-
|
306
|
-
# passed to the function, so these results can be returned as-is.
|
307
|
-
#
|
308
|
-
def round_robin(results)
|
293
|
+
def wait(results, _timeout, **_kwargs)
|
309
294
|
results
|
310
295
|
end
|
311
296
|
|
297
|
+
# Since Futures are executed immediately once created, this will always
|
298
|
+
# be true by the time it's called.
|
299
|
+
def plan_complete?
|
300
|
+
true
|
301
|
+
end
|
302
|
+
|
303
|
+
def plan_futures
|
304
|
+
[]
|
305
|
+
end
|
306
|
+
|
312
307
|
# Public methods on Bolt::Executor that need to be mocked so there aren't
|
313
308
|
# "undefined method" errors.
|
314
309
|
|
@@ -337,6 +332,8 @@ module BoltSpec
|
|
337
332
|
def subscribe(_subscriber, _types = nil); end
|
338
333
|
|
339
334
|
def unsubscribe(_subscriber, _types = nil); end
|
335
|
+
|
336
|
+
def round_robin; end
|
340
337
|
end
|
341
338
|
end
|
342
339
|
end
|