openbolt 5.0.0.rc1
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.
- checksums.yaml +7 -0
- data/Puppetfile +52 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
- data/bolt-modules/boltlib/types/planresult.pp +18 -0
- data/bolt-modules/boltlib/types/targetspec.pp +7 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
- 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 +36 -0
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
- data/exe/bolt +17 -0
- data/guides/debugging.yaml +27 -0
- data/guides/inventory.yaml +23 -0
- data/guides/links.yaml +12 -0
- data/guides/logging.yaml +17 -0
- data/guides/module.yaml +18 -0
- data/guides/modulepath.yaml +24 -0
- data/guides/project.yaml +21 -0
- data/guides/targets.yaml +28 -0
- data/guides/transports.yaml +22 -0
- data/lib/bolt/analytics.rb +233 -0
- data/lib/bolt/application.rb +806 -0
- data/lib/bolt/applicator.rb +368 -0
- data/lib/bolt/apply_inventory.rb +93 -0
- data/lib/bolt/apply_result.rb +154 -0
- data/lib/bolt/apply_target.rb +90 -0
- data/lib/bolt/bolt_option_parser.rb +1226 -0
- data/lib/bolt/catalog/logging.rb +15 -0
- data/lib/bolt/catalog.rb +144 -0
- data/lib/bolt/cli.rb +949 -0
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +673 -0
- data/lib/bolt/config/transport/base.rb +133 -0
- data/lib/bolt/config/transport/docker.rb +34 -0
- data/lib/bolt/config/transport/jail.rb +33 -0
- data/lib/bolt/config/transport/local.rb +39 -0
- data/lib/bolt/config/transport/lxd.rb +34 -0
- data/lib/bolt/config/transport/options.rb +431 -0
- data/lib/bolt/config/transport/orch.rb +41 -0
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/config/transport/remote.rb +24 -0
- data/lib/bolt/config/transport/ssh.rb +138 -0
- data/lib/bolt/config/transport/winrm.rb +63 -0
- data/lib/bolt/config.rb +515 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +194 -0
- data/lib/bolt/executor.rb +539 -0
- data/lib/bolt/fiber_executor.rb +190 -0
- data/lib/bolt/inventory/group.rb +446 -0
- data/lib/bolt/inventory/inventory.rb +391 -0
- data/lib/bolt/inventory/options.rb +139 -0
- data/lib/bolt/inventory/target.rb +293 -0
- data/lib/bolt/inventory.rb +120 -0
- data/lib/bolt/logger.rb +252 -0
- data/lib/bolt/module.rb +54 -0
- data/lib/bolt/module_installer/installer.rb +44 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/puppetfile.rb +131 -0
- data/lib/bolt/module_installer/resolver.rb +129 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
- data/lib/bolt/module_installer/specs/id/base.rb +116 -0
- data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
- data/lib/bolt/module_installer/specs/id/github.rb +90 -0
- data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
- data/lib/bolt/module_installer/specs.rb +95 -0
- data/lib/bolt/module_installer.rb +208 -0
- data/lib/bolt/node/errors.rb +55 -0
- data/lib/bolt/node/output.rb +29 -0
- data/lib/bolt/outputter/human.rb +958 -0
- data/lib/bolt/outputter/json.rb +205 -0
- data/lib/bolt/outputter/logger.rb +76 -0
- data/lib/bolt/outputter/rainbow.rb +118 -0
- data/lib/bolt/outputter.rb +57 -0
- data/lib/bolt/pal/issues.rb +19 -0
- data/lib/bolt/pal/logging.rb +17 -0
- data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
- data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
- data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
- data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
- data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
- data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
- data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/step.rb +223 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
- data/lib/bolt/pal/yaml_plan.rb +172 -0
- data/lib/bolt/pal.rb +847 -0
- data/lib/bolt/plan_creator.rb +219 -0
- data/lib/bolt/plan_future.rb +86 -0
- data/lib/bolt/plan_result.rb +44 -0
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/env_var.rb +54 -0
- data/lib/bolt/plugin/module.rb +276 -0
- data/lib/bolt/plugin/prompt.rb +36 -0
- data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
- data/lib/bolt/plugin/puppetdb.rb +124 -0
- data/lib/bolt/plugin/task.rb +72 -0
- data/lib/bolt/plugin.rb +380 -0
- data/lib/bolt/project.rb +219 -0
- data/lib/bolt/project_manager/config_migrator.rb +113 -0
- data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
- data/lib/bolt/project_manager/migrator.rb +39 -0
- data/lib/bolt/project_manager/module_migrator.rb +203 -0
- data/lib/bolt/project_manager.rb +221 -0
- data/lib/bolt/puppetdb/client.rb +153 -0
- data/lib/bolt/puppetdb/config.rb +176 -0
- data/lib/bolt/puppetdb/instance.rb +146 -0
- data/lib/bolt/puppetdb.rb +15 -0
- data/lib/bolt/r10k_log_proxy.rb +30 -0
- data/lib/bolt/rerun.rb +55 -0
- data/lib/bolt/resource_instance.rb +133 -0
- data/lib/bolt/result.rb +247 -0
- data/lib/bolt/result_set.rb +128 -0
- data/lib/bolt/shell/bash/tmpdir.rb +62 -0
- data/lib/bolt/shell/bash.rb +516 -0
- data/lib/bolt/shell/powershell/snippets.rb +181 -0
- data/lib/bolt/shell/powershell.rb +365 -0
- data/lib/bolt/shell.rb +105 -0
- data/lib/bolt/target.rb +174 -0
- data/lib/bolt/task/puppet_server.rb +27 -0
- data/lib/bolt/task/run.rb +55 -0
- data/lib/bolt/task.rb +163 -0
- data/lib/bolt/transport/base.rb +252 -0
- data/lib/bolt/transport/docker/connection.rb +150 -0
- data/lib/bolt/transport/docker.rb +23 -0
- data/lib/bolt/transport/jail/connection.rb +81 -0
- data/lib/bolt/transport/jail.rb +21 -0
- data/lib/bolt/transport/local/connection.rb +106 -0
- data/lib/bolt/transport/local.rb +20 -0
- data/lib/bolt/transport/lxd/connection.rb +115 -0
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/orch/connection.rb +111 -0
- data/lib/bolt/transport/orch.rb +271 -0
- data/lib/bolt/transport/podman/connection.rb +102 -0
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/remote.rb +41 -0
- data/lib/bolt/transport/simple.rb +54 -0
- data/lib/bolt/transport/ssh/connection.rb +321 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
- data/lib/bolt/transport/ssh.rb +48 -0
- data/lib/bolt/transport/winrm/connection.rb +378 -0
- data/lib/bolt/transport/winrm.rb +33 -0
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/util/puppet_log_level.rb +21 -0
- data/lib/bolt/util.rb +465 -0
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +5 -0
- data/lib/bolt.rb +8 -0
- data/lib/bolt_server/acl.rb +39 -0
- data/lib/bolt_server/base_config.rb +112 -0
- data/lib/bolt_server/config.rb +64 -0
- data/lib/bolt_server/file_cache.rb +200 -0
- data/lib/bolt_server/request_error.rb +11 -0
- data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
- data/lib/bolt_server/schemas/action-run_command.json +12 -0
- data/lib/bolt_server/schemas/action-run_script.json +47 -0
- data/lib/bolt_server/schemas/action-run_task.json +20 -0
- data/lib/bolt_server/schemas/action-upload_file.json +47 -0
- data/lib/bolt_server/schemas/partials/target-any.json +10 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
- data/lib/bolt_server/schemas/partials/task.json +94 -0
- data/lib/bolt_server/schemas/transport-ssh.json +25 -0
- data/lib/bolt_server/schemas/transport-winrm.json +19 -0
- data/lib/bolt_server/transport_app.rb +554 -0
- data/lib/bolt_spec/bolt_context.rb +226 -0
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
- data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
- data/lib/bolt_spec/plans/action_stubs.rb +196 -0
- data/lib/bolt_spec/plans/mock_executor.rb +361 -0
- data/lib/bolt_spec/plans/publish_stub.rb +49 -0
- data/lib/bolt_spec/plans.rb +190 -0
- data/lib/bolt_spec/run.rb +246 -0
- data/lib/logging_extensions/logging.rb +13 -0
- data/libexec/apply_catalog.rb +130 -0
- data/libexec/bolt_catalog +68 -0
- data/libexec/custom_facts.rb +63 -0
- data/libexec/query_resources.rb +75 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
- data/modules/aggregate/plans/count.pp +56 -0
- data/modules/aggregate/plans/targets.pp +56 -0
- data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
- data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
- data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
- data/modules/canary/plans/init.pp +100 -0
- data/modules/puppet_connect/plans/test_input_data.pp +94 -0
- data/modules/puppetdb_fact/plans/init.pp +20 -0
- data/resources/bolt_bash_completion.sh +214 -0
- metadata +735 -0
@@ -0,0 +1,539 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Used for $ERROR_INFO. This *must* be capitalized!
|
4
|
+
require 'English'
|
5
|
+
require 'json'
|
6
|
+
require 'logging'
|
7
|
+
require 'pathname'
|
8
|
+
require 'set'
|
9
|
+
require_relative '../bolt/analytics'
|
10
|
+
require_relative '../bolt/config'
|
11
|
+
require_relative '../bolt/fiber_executor'
|
12
|
+
require_relative '../bolt/puppetdb'
|
13
|
+
require_relative '../bolt/result'
|
14
|
+
require_relative '../bolt/result_set'
|
15
|
+
# Load transports
|
16
|
+
require_relative '../bolt/transport/docker'
|
17
|
+
require_relative '../bolt/transport/jail'
|
18
|
+
require_relative '../bolt/transport/local'
|
19
|
+
require_relative '../bolt/transport/lxd'
|
20
|
+
require_relative '../bolt/transport/orch'
|
21
|
+
require_relative '../bolt/transport/podman'
|
22
|
+
require_relative '../bolt/transport/remote'
|
23
|
+
require_relative '../bolt/transport/ssh'
|
24
|
+
require_relative '../bolt/transport/winrm'
|
25
|
+
|
26
|
+
module Bolt
|
27
|
+
TRANSPORTS = {
|
28
|
+
docker: Bolt::Transport::Docker,
|
29
|
+
jail: Bolt::Transport::Jail,
|
30
|
+
local: Bolt::Transport::Local,
|
31
|
+
lxd: Bolt::Transport::LXD,
|
32
|
+
pcp: Bolt::Transport::Orch,
|
33
|
+
podman: Bolt::Transport::Podman,
|
34
|
+
remote: Bolt::Transport::Remote,
|
35
|
+
ssh: Bolt::Transport::SSH,
|
36
|
+
winrm: Bolt::Transport::WinRM
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
class Executor
|
40
|
+
attr_reader :noop, :transports, :future
|
41
|
+
attr_accessor :run_as
|
42
|
+
|
43
|
+
def initialize(concurrency = 1,
|
44
|
+
analytics = Bolt::Analytics::NoopClient.new,
|
45
|
+
noop = false,
|
46
|
+
modified_concurrency = false,
|
47
|
+
future = {})
|
48
|
+
# lazy-load expensive gem code
|
49
|
+
require 'concurrent'
|
50
|
+
@analytics = analytics
|
51
|
+
@logger = Bolt::Logger.logger(self)
|
52
|
+
|
53
|
+
@transports = Bolt::TRANSPORTS.each_with_object({}) do |(key, val), coll|
|
54
|
+
coll[key.to_s] = if key == :remote
|
55
|
+
Concurrent::Delay.new do
|
56
|
+
val.new(self)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
Concurrent::Delay.new do
|
60
|
+
val.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@reported_transports = Set.new
|
65
|
+
@subscribers = {}
|
66
|
+
@publisher = Concurrent::SingleThreadExecutor.new
|
67
|
+
@publisher.post { Thread.current[:name] = 'event-publisher' }
|
68
|
+
|
69
|
+
@noop = noop
|
70
|
+
@run_as = nil
|
71
|
+
@future = future
|
72
|
+
@pool = if concurrency > 0
|
73
|
+
Concurrent::ThreadPoolExecutor.new(name: 'exec', max_threads: concurrency)
|
74
|
+
else
|
75
|
+
Concurrent.global_immediate_executor
|
76
|
+
end
|
77
|
+
@logger.debug { "Started with #{concurrency} max thread(s)" }
|
78
|
+
|
79
|
+
@concurrency = concurrency
|
80
|
+
@warn_concurrency = modified_concurrency
|
81
|
+
@fiber_executor = Bolt::FiberExecutor.new
|
82
|
+
end
|
83
|
+
|
84
|
+
def transport(transport)
|
85
|
+
impl = @transports[transport || 'ssh']
|
86
|
+
raise(Bolt::UnknownTransportError, transport) unless impl
|
87
|
+
# If there was an error creating the transport, ensure it gets thrown
|
88
|
+
impl.no_error!
|
89
|
+
impl.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def subscribe(subscriber, types = nil)
|
93
|
+
@subscribers[subscriber] = types
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def unsubscribe(subscriber, types = nil)
|
98
|
+
if types.nil? || types.sort == @subscribers[subscriber]&.sort
|
99
|
+
@subscribers.delete(subscriber)
|
100
|
+
elsif @subscribers[subscriber].is_a?(Array)
|
101
|
+
@subscribers[subscriber] = @subscribers[subscriber] - types
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def publish_event(event)
|
106
|
+
@subscribers.each do |subscriber, types|
|
107
|
+
# If types isn't set or if the subscriber is subscribed to
|
108
|
+
# that type of event, publish the event
|
109
|
+
next unless types.nil? || types.include?(event[:type])
|
110
|
+
@publisher.post(subscriber) do |sub|
|
111
|
+
# Wait for user to input to prompt before printing anything
|
112
|
+
sleep(0.1) while @prompting
|
113
|
+
sub.handle_event(event)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def shutdown
|
119
|
+
@publisher.shutdown
|
120
|
+
@publisher.wait_for_termination
|
121
|
+
end
|
122
|
+
|
123
|
+
# Starts executing the given block on a list of nodes in parallel, one thread per "batch".
|
124
|
+
#
|
125
|
+
# This is the main driver of execution on a list of targets. It first
|
126
|
+
# groups targets by transport, then divides each group into batches as
|
127
|
+
# defined by the transport. Yields each batch, along with the corresponding
|
128
|
+
# transport, to the block in turn and returns an array of result promises.
|
129
|
+
def queue_execute(targets)
|
130
|
+
if @warn_concurrency && targets.length > @concurrency
|
131
|
+
@warn_concurrency = false
|
132
|
+
msg = "The ulimit is low, which might cause file limit issues. Default concurrency has been set to "\
|
133
|
+
"'#{@concurrency}' to mitigate those issues, which might cause Bolt to run slow. "\
|
134
|
+
"Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
|
135
|
+
"configuration, or by configuring Bolt's concurrency. "\
|
136
|
+
"See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
|
137
|
+
Bolt::Logger.warn("low_ulimit", msg)
|
138
|
+
end
|
139
|
+
|
140
|
+
targets.group_by(&:transport).flat_map do |protocol, protocol_targets|
|
141
|
+
transport = transport(protocol)
|
142
|
+
report_transport(transport, protocol_targets.count)
|
143
|
+
transport.batches(protocol_targets).flat_map do |batch|
|
144
|
+
batch_promises = Array(batch).each_with_object({}) do |target, h|
|
145
|
+
h[target] = Concurrent::Promise.new(executor: :immediate)
|
146
|
+
end
|
147
|
+
# Pass this argument through to avoid retaining a reference to a
|
148
|
+
# local variable that will change on the next iteration of the loop.
|
149
|
+
@pool.post(batch_promises) do |result_promises|
|
150
|
+
Thread.current[:name] ||= Thread.current.name
|
151
|
+
results = yield transport, batch
|
152
|
+
Array(results).each do |result|
|
153
|
+
result_promises[result.target].set(result)
|
154
|
+
end
|
155
|
+
# NotImplementedError can be thrown if the transport is not implemented improperly
|
156
|
+
rescue StandardError, NotImplementedError => e
|
157
|
+
result_promises.each do |target, promise|
|
158
|
+
# If an exception happens while running, the result won't be logged
|
159
|
+
# by the CLI. Log a warning, as this is probably a problem with the transport.
|
160
|
+
# If batch_* commands are used from the Base transport, then exceptions
|
161
|
+
# normally shouldn't reach here.
|
162
|
+
@logger.warn(e)
|
163
|
+
promise.set(Bolt::Result.from_exception(target, e))
|
164
|
+
end
|
165
|
+
ensure
|
166
|
+
# Make absolutely sure every promise gets a result to avoid a
|
167
|
+
# deadlock. Use whatever exception is causing this block to
|
168
|
+
# execute, or generate one if we somehow got here without an
|
169
|
+
# exception and some promise is still missing a result.
|
170
|
+
result_promises.each do |target, promise|
|
171
|
+
next if promise.fulfilled?
|
172
|
+
error = $ERROR_INFO || Bolt::Error.new("No result was returned for #{target.uri}",
|
173
|
+
"puppetlabs.bolt/missing-result-error")
|
174
|
+
promise.set(Bolt::Result.from_exception(target, error))
|
175
|
+
end
|
176
|
+
end
|
177
|
+
batch_promises.values
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Create a ResultSet from the results of all promises.
|
183
|
+
def await_results(promises)
|
184
|
+
ResultSet.new(promises.map(&:value))
|
185
|
+
end
|
186
|
+
|
187
|
+
# Execute the given block on a list of nodes in parallel, one thread per "batch".
|
188
|
+
#
|
189
|
+
# This is the main driver of execution on a list of targets. It first
|
190
|
+
# groups targets by transport, then divides each group into batches as
|
191
|
+
# defined by the transport. Each batch, along with the corresponding
|
192
|
+
# transport, is yielded to the block in turn and the results all collected
|
193
|
+
# into a single ResultSet.
|
194
|
+
def batch_execute(targets, &block)
|
195
|
+
promises = queue_execute(targets, &block)
|
196
|
+
await_results(promises)
|
197
|
+
end
|
198
|
+
|
199
|
+
def log_action(description, targets)
|
200
|
+
publish_event(type: :step_start, description: description, targets: targets)
|
201
|
+
|
202
|
+
start_time = Time.now
|
203
|
+
results = yield
|
204
|
+
duration = Time.now - start_time
|
205
|
+
|
206
|
+
publish_event(type: :step_finish, description: description, result: results, duration: duration)
|
207
|
+
|
208
|
+
results
|
209
|
+
end
|
210
|
+
|
211
|
+
def log_plan(plan_name)
|
212
|
+
publish_event(type: :plan_start, plan: plan_name)
|
213
|
+
start_time = Time.now
|
214
|
+
|
215
|
+
results = nil
|
216
|
+
begin
|
217
|
+
results = yield
|
218
|
+
ensure
|
219
|
+
duration = Time.now - start_time
|
220
|
+
publish_event(type: :plan_finish, plan: plan_name, duration: duration)
|
221
|
+
end
|
222
|
+
|
223
|
+
results
|
224
|
+
end
|
225
|
+
|
226
|
+
private def report_transport(transport, count)
|
227
|
+
name = transport.class.name.split('::').last.downcase
|
228
|
+
unless @reported_transports.include?(name)
|
229
|
+
@analytics&.event('Transport', 'initialize', label: name, value: count)
|
230
|
+
end
|
231
|
+
@reported_transports.add(name)
|
232
|
+
end
|
233
|
+
|
234
|
+
def report_function_call(function)
|
235
|
+
@analytics&.event('Plan', 'call_function', label: function)
|
236
|
+
end
|
237
|
+
|
238
|
+
def report_bundled_content(mode, name)
|
239
|
+
@analytics.report_bundled_content(mode, name)
|
240
|
+
end
|
241
|
+
|
242
|
+
def report_file_source(plan_function, source)
|
243
|
+
label = Pathname.new(source).absolute? ? 'absolute' : 'module'
|
244
|
+
@analytics&.event('Plan', plan_function, label: label)
|
245
|
+
end
|
246
|
+
|
247
|
+
def report_noop_mode(noop)
|
248
|
+
@analytics&.event('Task', 'noop', label: (!!noop).to_s)
|
249
|
+
end
|
250
|
+
|
251
|
+
def report_apply(statement_count, resource_counts)
|
252
|
+
data = { statement_count: statement_count }
|
253
|
+
|
254
|
+
unless resource_counts.empty?
|
255
|
+
sum = resource_counts.inject(0) { |accum, i| accum + i }
|
256
|
+
# Intentionally rounded to an integer. High precision isn't useful.
|
257
|
+
data[:resource_mean] = sum / resource_counts.length
|
258
|
+
end
|
259
|
+
|
260
|
+
@analytics&.event('Apply', 'ast', **data)
|
261
|
+
end
|
262
|
+
|
263
|
+
def report_yaml_plan(plan)
|
264
|
+
steps = plan.steps.count
|
265
|
+
return_type = case plan.return
|
266
|
+
when Bolt::PAL::YamlPlan::EvaluableString
|
267
|
+
'expression'
|
268
|
+
when nil
|
269
|
+
nil
|
270
|
+
else
|
271
|
+
'value'
|
272
|
+
end
|
273
|
+
|
274
|
+
@analytics&.event('Plan', 'yaml', plan_steps: steps, return_type: return_type)
|
275
|
+
rescue StandardError => e
|
276
|
+
@logger.trace { "Failed to submit analytics event: #{e.message}" }
|
277
|
+
end
|
278
|
+
|
279
|
+
def with_node_logging(description, batch, log_level = :info)
|
280
|
+
@logger.send(log_level, "#{description} on #{batch.map(&:safe_name)}")
|
281
|
+
publish_event(type: :start_spin)
|
282
|
+
result = yield
|
283
|
+
publish_event(type: :stop_spin)
|
284
|
+
@logger.send(log_level, result.to_json)
|
285
|
+
result
|
286
|
+
end
|
287
|
+
|
288
|
+
def run_command(targets, command, options = {}, position = [])
|
289
|
+
description = options.fetch(:description, "command '#{command}'")
|
290
|
+
log_action(description, targets) do
|
291
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
292
|
+
|
293
|
+
batch_execute(targets) do |transport, batch|
|
294
|
+
with_node_logging("Running command '#{command}'", batch) do
|
295
|
+
transport.batch_command(batch, command, options, position, &method(:publish_event))
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def run_script(targets, script, arguments, options = {}, position = [])
|
302
|
+
description = options.fetch(:description, "script #{script}")
|
303
|
+
log_action(description, targets) do
|
304
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
305
|
+
options[:script_interpreter] = (future || {}).fetch('script_interpreter', false)
|
306
|
+
|
307
|
+
@analytics&.event('Future', 'script_interpreter', label: options[:script_interpreter].to_s)
|
308
|
+
|
309
|
+
batch_execute(targets) do |transport, batch|
|
310
|
+
with_node_logging("Running script #{script} with '#{arguments.to_json}'", batch) do
|
311
|
+
transport.batch_script(batch, script, arguments, options, position, &method(:publish_event))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def run_task(targets, task, arguments, options = {}, position = [], log_level = :info)
|
318
|
+
description = options.fetch(:description, "task #{task.name}")
|
319
|
+
log_action(description, targets) do
|
320
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
321
|
+
arguments['_task'] = task.name
|
322
|
+
|
323
|
+
batch_execute(targets) do |transport, batch|
|
324
|
+
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch, log_level) do
|
325
|
+
transport.batch_task(batch, task, arguments, options, position, &method(:publish_event))
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def run_task_with(target_mapping, task, options = {}, position = [])
|
332
|
+
targets = target_mapping.keys
|
333
|
+
description = options.fetch(:description, "task #{task.name}")
|
334
|
+
|
335
|
+
log_action(description, targets) do
|
336
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
337
|
+
target_mapping.each_value { |arguments| arguments['_task'] = task.name }
|
338
|
+
|
339
|
+
batch_execute(targets) do |transport, batch|
|
340
|
+
with_node_logging("Running task #{task.name}'", batch) do
|
341
|
+
transport.batch_task_with(batch, task, target_mapping, options, position, &method(:publish_event))
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def upload_file(targets, source, destination, options = {}, position = [])
|
348
|
+
description = options.fetch(:description, "file upload from #{source} to #{destination}")
|
349
|
+
log_action(description, targets) do
|
350
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
351
|
+
|
352
|
+
batch_execute(targets) do |transport, batch|
|
353
|
+
with_node_logging("Uploading file #{source} to #{destination}", batch) do
|
354
|
+
transport.batch_upload(batch, source, destination, options, position, &method(:publish_event))
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def download_file(targets, source, destination, options = {}, position = [])
|
361
|
+
description = options.fetch(:description, "file download from #{source} to #{destination}")
|
362
|
+
|
363
|
+
begin
|
364
|
+
FileUtils.mkdir_p(destination)
|
365
|
+
rescue Errno::EEXIST => e
|
366
|
+
message = "#{e.message}; unable to create destination directory #{destination}"
|
367
|
+
raise Bolt::Error.new(message, 'bolt/file-exist-error')
|
368
|
+
end
|
369
|
+
|
370
|
+
log_action(description, targets) do
|
371
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
372
|
+
|
373
|
+
batch_execute(targets) do |transport, batch|
|
374
|
+
with_node_logging("Downloading file #{source} to #{destination}", batch) do
|
375
|
+
transport.batch_download(batch, source, destination, options, position, &method(:publish_event))
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def run_plan(scope, plan, params)
|
382
|
+
plan.call_by_name_with_scope(scope, params, true)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Call into FiberExecutor to avoid this class getting
|
386
|
+
# overloaded while also minimizing the Puppet lookups needed from plan
|
387
|
+
# functions
|
388
|
+
#
|
389
|
+
def create_future(plan_id:, scope: nil, name: nil, &block)
|
390
|
+
@fiber_executor.create_future(scope: scope, name: name, plan_id: plan_id, &block)
|
391
|
+
end
|
392
|
+
|
393
|
+
def get_current_future(fiber:)
|
394
|
+
@fiber_executor.get_current_future(fiber: fiber)
|
395
|
+
end
|
396
|
+
|
397
|
+
def get_current_plan_id(fiber:)
|
398
|
+
@fiber_executor.get_current_plan_id(fiber: fiber)
|
399
|
+
end
|
400
|
+
|
401
|
+
def plan_complete?
|
402
|
+
@fiber_executor.plan_complete?
|
403
|
+
end
|
404
|
+
|
405
|
+
def round_robin
|
406
|
+
@fiber_executor.round_robin
|
407
|
+
end
|
408
|
+
|
409
|
+
def in_parallel?
|
410
|
+
@fiber_executor.in_parallel?
|
411
|
+
end
|
412
|
+
|
413
|
+
def wait(futures, **opts)
|
414
|
+
@fiber_executor.wait(futures, **opts)
|
415
|
+
end
|
416
|
+
|
417
|
+
def get_futures_for_plan(plan_id:)
|
418
|
+
@fiber_executor.get_futures_for_plan(plan_id: plan_id)
|
419
|
+
end
|
420
|
+
|
421
|
+
# Execute a plan function concurrently. This function accepts the executor
|
422
|
+
# function to be run and the parameters to pass to it, and returns the
|
423
|
+
# result of running the executor function.
|
424
|
+
#
|
425
|
+
def run_in_thread
|
426
|
+
require 'concurrent'
|
427
|
+
require 'fiber'
|
428
|
+
future = Concurrent::Future.execute do
|
429
|
+
yield
|
430
|
+
end
|
431
|
+
|
432
|
+
# Used to track how often we resume the same executor function
|
433
|
+
still_running = 0
|
434
|
+
# While the thread is still running
|
435
|
+
while future.incomplete?
|
436
|
+
# If the Fiber gets resumed, increment the resume tracker. This means
|
437
|
+
# the tracker starts at 1 since it needs to increment before yielding,
|
438
|
+
# since it can't yield then increment.
|
439
|
+
still_running += 1
|
440
|
+
# If the Fiber has been resumed before, still_running will be 2 or
|
441
|
+
# more. Yield different values for when the same Fiber is resumed
|
442
|
+
# multiple times and when it's resumed the first time in order to know
|
443
|
+
# if progress was made in the plan.
|
444
|
+
Fiber.yield(still_running < 2 ? :something_happened : :returned_immediately)
|
445
|
+
end
|
446
|
+
|
447
|
+
# Once the thread completes, return the result.
|
448
|
+
future.value || future.reason
|
449
|
+
end
|
450
|
+
|
451
|
+
class TimeoutError < RuntimeError; end
|
452
|
+
|
453
|
+
def wait_until_available(targets,
|
454
|
+
description: 'wait until available',
|
455
|
+
wait_time: 120,
|
456
|
+
retry_interval: 1)
|
457
|
+
log_action(description, targets) do
|
458
|
+
batch_execute(targets) do |transport, batch|
|
459
|
+
with_node_logging('Waiting until available', batch) do
|
460
|
+
wait_until(wait_time, retry_interval) { transport.batch_connected?(batch) }
|
461
|
+
batch.map { |target| Result.new(target, action: 'wait_until_available', object: description) }
|
462
|
+
rescue TimeoutError => e
|
463
|
+
available, unavailable = batch.partition { |target| transport.batch_connected?([target]) }
|
464
|
+
(
|
465
|
+
available.map { |target| Result.new(target, action: 'wait_until_available', object: description) } +
|
466
|
+
unavailable.map { |target| Result.from_exception(target, e, action: 'wait_until_available') }
|
467
|
+
)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Used to simplify unit testing, to avoid having to mock other calls to Time.now.
|
474
|
+
private def wait_now
|
475
|
+
Time.now
|
476
|
+
end
|
477
|
+
|
478
|
+
private def wait_until(timeout, retry_interval)
|
479
|
+
start = wait_now
|
480
|
+
until yield
|
481
|
+
raise(TimeoutError, 'Timed out waiting for target') if (wait_now - start).to_i >= timeout
|
482
|
+
sleep(retry_interval)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def prompt(prompt, options)
|
487
|
+
unless $stdin.tty?
|
488
|
+
return options[:default] if options[:default]
|
489
|
+
raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
|
490
|
+
end
|
491
|
+
|
492
|
+
@prompting = true
|
493
|
+
|
494
|
+
if options[:default] && !options[:sensitive]
|
495
|
+
$stderr.print("#{prompt} [#{options[:default]}]: ")
|
496
|
+
else
|
497
|
+
$stderr.print("#{prompt}: ")
|
498
|
+
end
|
499
|
+
|
500
|
+
value = if options[:sensitive]
|
501
|
+
$stdin.noecho(&:gets).to_s.chomp
|
502
|
+
else
|
503
|
+
$stdin.gets.to_s.chomp
|
504
|
+
end
|
505
|
+
|
506
|
+
@prompting = false
|
507
|
+
|
508
|
+
$stderr.puts if options[:sensitive]
|
509
|
+
|
510
|
+
value = options[:default] if value.empty?
|
511
|
+
value
|
512
|
+
end
|
513
|
+
|
514
|
+
# Plan context doesn't make sense for most transports but it is tightly
|
515
|
+
# coupled with the orchestrator transport since the transport behaves
|
516
|
+
# differently when a plan is running. In order to limit how much this
|
517
|
+
# pollutes the transport API we only handle the orchestrator transport here.
|
518
|
+
# Since we call this function without resolving targets this will result
|
519
|
+
# in the orchestrator transport always being initialized during plan runs.
|
520
|
+
# For now that's ok.
|
521
|
+
#
|
522
|
+
# In the future if other transports need this or if we want a plan stack
|
523
|
+
# we'll need to refactor.
|
524
|
+
def start_plan(plan_context)
|
525
|
+
transport('pcp').plan_context = plan_context
|
526
|
+
end
|
527
|
+
|
528
|
+
def finish_plan(plan_result)
|
529
|
+
transport('pcp').finish_plan(plan_result)
|
530
|
+
end
|
531
|
+
|
532
|
+
def without_default_logging
|
533
|
+
publish_event(type: :disable_default_output)
|
534
|
+
yield
|
535
|
+
ensure
|
536
|
+
publish_event(type: :enable_default_output)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|