openbolt 5.6.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Puppetfile +35 -35
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +27 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +14 -22
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +14 -26
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +2 -2
- data/bolt-modules/file/lib/puppet/functions/file/delete.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +1 -1
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +1 -1
- data/guides/debugging.yaml +1 -1
- data/guides/inventory.yaml +2 -2
- data/guides/links.yaml +18 -9
- data/guides/logging.yaml +1 -1
- data/guides/module.yaml +1 -1
- data/guides/modulepath.yaml +1 -1
- data/guides/project.yaml +2 -2
- data/guides/targets.yaml +3 -1
- data/guides/transports.yaml +4 -3
- data/lib/bolt/analytics.rb +2 -10
- data/lib/bolt/bolt_option_parser.rb +66 -66
- data/lib/bolt/cli.rb +21 -2
- data/lib/bolt/config/options.rb +18 -18
- data/lib/bolt/config/transport/choria.rb +3 -0
- data/lib/bolt/config/transport/options.rb +34 -45
- data/lib/bolt/executor.rb +4 -0
- data/lib/bolt/fiber_executor.rb +46 -2
- data/lib/bolt/inventory/options.rb +1 -1
- data/lib/bolt/inventory/target.rb +1 -1
- data/lib/bolt/module_installer/puppetfile.rb +1 -1
- data/lib/bolt/module_installer/specs.rb +1 -1
- data/lib/bolt/outputter/human.rb +1 -2
- data/lib/bolt/plan_creator.rb +4 -4
- data/lib/bolt/plan_future.rb +5 -1
- data/lib/bolt/project_manager/inventory_migrator.rb +1 -1
- data/lib/bolt/project_manager/module_migrator.rb +1 -1
- data/lib/bolt/project_manager.rb +1 -1
- data/lib/bolt/shell/bash.rb +7 -4
- data/lib/bolt/shell/powershell/snippets.rb +20 -1
- data/lib/bolt/shell/powershell.rb +2 -2
- data/lib/bolt/transport/base.rb +9 -4
- data/lib/bolt/transport/choria/agent_discovery.rb +32 -14
- data/lib/bolt/transport/choria/bolt_tasks.rb +64 -40
- data/lib/bolt/transport/choria/client.rb +66 -49
- data/lib/bolt/transport/choria/command_builders.rb +144 -24
- data/lib/bolt/transport/choria/file_transfer.rb +134 -0
- data/lib/bolt/transport/choria/helpers.rb +25 -20
- data/lib/bolt/transport/choria/shell.rb +255 -148
- data/lib/bolt/transport/choria.rb +96 -27
- data/lib/bolt/transport/ssh/connection.rb +2 -1
- data/lib/bolt/util.rb +13 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +4 -0
- data/libexec/apply_catalog.rb +3 -0
- metadata +22 -7
|
@@ -6,6 +6,8 @@ require 'digest/sha2'
|
|
|
6
6
|
require 'json'
|
|
7
7
|
require 'securerandom'
|
|
8
8
|
require 'shellwords'
|
|
9
|
+
require_relative '../../bolt/node/errors'
|
|
10
|
+
require_relative '../../bolt/shell'
|
|
9
11
|
require_relative '../../bolt/transport/base'
|
|
10
12
|
|
|
11
13
|
module Bolt
|
|
@@ -21,15 +23,19 @@ module Bolt
|
|
|
21
23
|
#
|
|
22
24
|
# bolt_tasks agent only: Only run_task works, via the bolt_tasks agent
|
|
23
25
|
# which downloads task files from an OpenVox/Puppet Server and executes
|
|
24
|
-
# them via task_wrapper. All other operations fail with an
|
|
25
|
-
#
|
|
26
|
+
# them via task_wrapper. All other operations fail with an error that
|
|
27
|
+
# names the agent they need.
|
|
26
28
|
#
|
|
27
29
|
# shell agent installed (>= 1.2.1): run_command, run_script, and
|
|
28
30
|
# run_task work. run_task uses the bolt_tasks agent by default.
|
|
29
31
|
# To run local tasks via the shell agent, set task-agent to 'shell'
|
|
30
32
|
# in project config or specify --choria-task-agent shell.
|
|
31
33
|
#
|
|
32
|
-
#
|
|
34
|
+
# file_transfer agent installed (>= 1.0.0): upload and download work.
|
|
35
|
+
# The controller needs the agent module's client files, which carry
|
|
36
|
+
# the client library the transport uses.
|
|
37
|
+
#
|
|
38
|
+
# Plans are not yet supported.
|
|
33
39
|
class Choria < Base
|
|
34
40
|
def initialize
|
|
35
41
|
super
|
|
@@ -45,10 +51,9 @@ module Bolt
|
|
|
45
51
|
@default_collective = nil
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# on the target's detected OS.
|
|
54
|
+
# Task selection does not use this list, since select_implementation
|
|
55
|
+
# below overrides the Base method that reads it. The list is kept for
|
|
56
|
+
# apply_prep, which reads a transport's provided features.
|
|
52
57
|
def provided_features
|
|
53
58
|
%w[shell powershell]
|
|
54
59
|
end
|
|
@@ -69,6 +74,29 @@ module Bolt
|
|
|
69
74
|
impl
|
|
70
75
|
end
|
|
71
76
|
|
|
77
|
+
# Groups the targets by the implementation each selects. A target
|
|
78
|
+
# without a suitable implementation gets its error result instead of
|
|
79
|
+
# failing the whole batch.
|
|
80
|
+
#
|
|
81
|
+
# @param targets [Array<Bolt::Target>] Targets to run the task on
|
|
82
|
+
# @param task [Bolt::Task] Task with the implementations to select from
|
|
83
|
+
# @param result_opts [Hash] Options passed through to emit_results (:action, :name, :position)
|
|
84
|
+
# @param callback [Proc] Called with :node_result events
|
|
85
|
+
# @return [Array] 2-element array:
|
|
86
|
+
# - [Hash{Hash => Array<Bolt::Target>}] Each implementation and the targets that selected it
|
|
87
|
+
# - [Array<Bolt::Result>] Error results for the targets without an implementation
|
|
88
|
+
def group_by_implementation(targets, task, result_opts, &callback)
|
|
89
|
+
selected = {}
|
|
90
|
+
failures = {}
|
|
91
|
+
targets.each do |target|
|
|
92
|
+
selected[target] = select_implementation(target, task)
|
|
93
|
+
rescue Bolt::NoImplementationError => e
|
|
94
|
+
logger.debug { "No suitable implementation of #{task.name} for #{target.safe_name}" }
|
|
95
|
+
failures[target] = error_output(e.message, e.kind)
|
|
96
|
+
end
|
|
97
|
+
[selected.keys.group_by { |target| selected[target] }, emit_results(failures, **result_opts, &callback)]
|
|
98
|
+
end
|
|
99
|
+
|
|
72
100
|
# Group targets by collective so each batch uses a single RPC client
|
|
73
101
|
# scope. MCollective RPC calls are published to a collective-specific
|
|
74
102
|
# NATS subject, so targets in different collectives must be in separate
|
|
@@ -107,7 +135,6 @@ module Bolt
|
|
|
107
135
|
logger.debug { "Task #{task.name} routing: agent: #{chosen_agent}, #{capable.size} capable / #{targets.size - capable.size} incapable" }
|
|
108
136
|
|
|
109
137
|
unless capable.empty?
|
|
110
|
-
capable.each { |target| callback&.call(type: :node_start, target: target) }
|
|
111
138
|
arguments = unwrap_sensitive_args(arguments)
|
|
112
139
|
|
|
113
140
|
results += case chosen_agent
|
|
@@ -148,12 +175,18 @@ module Bolt
|
|
|
148
175
|
# @param callback [Proc] Called with :node_start and :node_result events
|
|
149
176
|
# @return [Array<Bolt::Result>] Results for all targets
|
|
150
177
|
def batch_task_with(targets, task, target_mapping, options = {}, position = [], &callback)
|
|
151
|
-
# Pre-warm the agent cache so individual batch_task calls are cache hits
|
|
178
|
+
# Pre-warm the agent cache so individual batch_task calls are cache hits.
|
|
179
|
+
# A target that did not answer has no cache entry, and batch_task would
|
|
180
|
+
# discover it again and wait out the timeout again, so it gets its
|
|
181
|
+
# error here instead.
|
|
152
182
|
configure_client(targets.first)
|
|
153
|
-
discover_agents(targets)
|
|
183
|
+
discovery_failures = discover_agents(targets)
|
|
184
|
+
discovered, silent = targets.partition { |target| discovered?(target) }
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
186
|
+
silent_errors = silent.to_h { |target| [target, discovery_failures[target] || undiscovered_error(target)] }
|
|
187
|
+
silent.each { |target| callback&.call(type: :node_start, target: target) }
|
|
188
|
+
results = emit_results(silent_errors, action: 'task', name: task.name, position: position, &callback)
|
|
189
|
+
discovered.each do |target|
|
|
157
190
|
results += batch_task([target], task, target_mapping[target], options, position, &callback)
|
|
158
191
|
end
|
|
159
192
|
results
|
|
@@ -169,29 +202,59 @@ module Bolt
|
|
|
169
202
|
first_target = targets.first
|
|
170
203
|
configure_client(first_target)
|
|
171
204
|
|
|
172
|
-
|
|
205
|
+
# A target that denies the caller still answers, so it counts as connected.
|
|
206
|
+
response = rpc_request('rpcutil', targets, 'rpcutil.ping', accept_aborted: true) do |client|
|
|
173
207
|
client.ping
|
|
174
208
|
end
|
|
175
209
|
response[:responded].length == targets.length
|
|
176
|
-
|
|
177
|
-
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# The single-target methods of the transport API. The remote transport
|
|
213
|
+
# calls run_task for its run-on target.
|
|
214
|
+
|
|
215
|
+
def run_command(target, command, options = {}, position = [])
|
|
216
|
+
batch_command([target], command, options, position).first
|
|
217
|
+
end
|
|
178
218
|
|
|
179
|
-
|
|
180
|
-
|
|
219
|
+
def run_script(target, script, arguments, options = {}, position = [])
|
|
220
|
+
raise_file_error(batch_script([target], script, arguments, options, position).first)
|
|
181
221
|
end
|
|
182
222
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
223
|
+
# Raises when no implementation of the task suits the target, like the
|
|
224
|
+
# other transports do. A batch reports that as the target's result.
|
|
225
|
+
def run_task(target, task, arguments, options = {}, position = [])
|
|
226
|
+
result = batch_task([target], task, arguments, options, position).first
|
|
227
|
+
# A result with an object is the task's own, whatever error the task
|
|
228
|
+
# printed. A failure the transport reports has none.
|
|
229
|
+
return result if result.object
|
|
230
|
+
|
|
231
|
+
raise Bolt::NoImplementationError.new(target, task) if result.error_hash&.dig('kind') == 'bolt/no-implementation'
|
|
232
|
+
|
|
233
|
+
raise_file_error(result)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def upload(target, source, destination, options = {})
|
|
237
|
+
batch_upload([target], source, destination, options).first
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Downloads into the destination itself. A batch gives each target a
|
|
241
|
+
# directory of its own under the destination.
|
|
242
|
+
def download(target, source, destination, _options = {})
|
|
243
|
+
download_into({ target => destination }, source, []).first
|
|
188
244
|
end
|
|
189
245
|
|
|
190
|
-
def
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
246
|
+
def connected?(target)
|
|
247
|
+
batch_connected?([target])
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Raises when a preparation step failed on the node, like the other
|
|
251
|
+
# transports do, and returns the result otherwise. A batch reports
|
|
252
|
+
# that failure as the target's result.
|
|
253
|
+
def raise_file_error(result)
|
|
254
|
+
error = result.error_hash
|
|
255
|
+
raise Bolt::Node::FileError, error['msg'] if error && error['kind'] == 'puppetlabs.tasks/task_file_error'
|
|
256
|
+
|
|
257
|
+
result
|
|
195
258
|
end
|
|
196
259
|
|
|
197
260
|
# Returns the Choria node identity for a target. Uses the transport
|
|
@@ -201,6 +264,11 @@ module Bolt
|
|
|
201
264
|
target.options['host'] || target.host
|
|
202
265
|
end
|
|
203
266
|
|
|
267
|
+
# The distinct identities behind the targets, in target order.
|
|
268
|
+
def identities_of(targets)
|
|
269
|
+
targets.map { |target| choria_identity(target) }.uniq
|
|
270
|
+
end
|
|
271
|
+
|
|
204
272
|
# Returns the collective for a target, used by batches() to group
|
|
205
273
|
# targets. Falls back to the default collective from the loaded config.
|
|
206
274
|
def collective_for(target)
|
|
@@ -214,5 +282,6 @@ require_relative 'choria/agent_discovery'
|
|
|
214
282
|
require_relative 'choria/bolt_tasks'
|
|
215
283
|
require_relative 'choria/client'
|
|
216
284
|
require_relative 'choria/command_builders'
|
|
285
|
+
require_relative 'choria/file_transfer'
|
|
217
286
|
require_relative 'choria/helpers'
|
|
218
287
|
require_relative 'choria/shell'
|
|
@@ -139,7 +139,8 @@ module Bolt
|
|
|
139
139
|
"Host key verification failed for #{target.safe_name}: #{e.message}",
|
|
140
140
|
'HOST_KEY_ERROR'
|
|
141
141
|
)
|
|
142
|
-
|
|
142
|
+
# Socket.tcp raises IO::TimeoutError for a connect timeout since Ruby 4.0
|
|
143
|
+
rescue Net::SSH::ConnectionTimeout, IO::TimeoutError
|
|
143
144
|
raise Bolt::Node::ConnectError.new(
|
|
144
145
|
"Timeout after #{target.options['connect-timeout']} seconds connecting to #{target.safe_name}",
|
|
145
146
|
'CONNECT_ERROR'
|
data/lib/bolt/util.rb
CHANGED
|
@@ -422,6 +422,19 @@ module Bolt
|
|
|
422
422
|
path.split(%r{[/\\]}).last
|
|
423
423
|
end
|
|
424
424
|
|
|
425
|
+
# Escapes a string for use inside a PowerShell single-quoted string by
|
|
426
|
+
# doubling every character PowerShell reads as a single quote, which is
|
|
427
|
+
# the apostrophe and the curly single quotes U+2018 to U+201B. The
|
|
428
|
+
# string is converted to UTF-8 first, so a quote in another encoding,
|
|
429
|
+
# such as 0x92 in Windows-1252, is doubled too. A binary string, such
|
|
430
|
+
# as a command-line argument under the C locale, can't be converted, so
|
|
431
|
+
# its bytes are read as UTF-8 and the result stays binary.
|
|
432
|
+
def escape_powershell_single_quotes(str)
|
|
433
|
+
return str.gsub(/'|\xE2\x80[\x98-\x9B]/n) { |quote| quote * 2 } if str.encoding == Encoding::BINARY
|
|
434
|
+
|
|
435
|
+
str.encode('UTF-8').gsub(/['\u{2018}-\u{201B}]/) { |quote| quote * 2 }
|
|
436
|
+
end
|
|
437
|
+
|
|
425
438
|
# Prompts yes or no, returning true for yes and false for no.
|
|
426
439
|
#
|
|
427
440
|
def prompt_yes_no(prompt, outputter)
|
data/lib/bolt/version.rb
CHANGED
data/libexec/apply_catalog.rb
CHANGED
|
@@ -106,6 +106,9 @@ begin
|
|
|
106
106
|
|
|
107
107
|
Puppet.override(overrides) do
|
|
108
108
|
catalog = Puppet::Resource::Catalog.from_data_hash(args['catalog'])
|
|
109
|
+
# The catalog is named after the Bolt target, and OpenVox 9 logs an error when that
|
|
110
|
+
# name differs from the node name the agent reports under
|
|
111
|
+
Puppet[:node_name_value] = catalog.name
|
|
109
112
|
catalog.environment = env.name.to_s
|
|
110
113
|
catalog.environment_instance = env
|
|
111
114
|
if defined?(Puppet::Pops::Evaluator::DeferredResolver)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openbolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 9.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenVox Project
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: bindata
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "<"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "<"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: CFPropertyList
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,14 +71,14 @@ dependencies:
|
|
|
57
71
|
requirements:
|
|
58
72
|
- - "~>"
|
|
59
73
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '2.
|
|
74
|
+
version: '2.28'
|
|
61
75
|
type: :runtime
|
|
62
76
|
prerelease: false
|
|
63
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
78
|
requirements:
|
|
65
79
|
- - "~>"
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '2.
|
|
81
|
+
version: '2.28'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: concurrent-ruby
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -247,14 +261,14 @@ dependencies:
|
|
|
247
261
|
requirements:
|
|
248
262
|
- - "~>"
|
|
249
263
|
- !ruby/object:Gem::Version
|
|
250
|
-
version: '
|
|
264
|
+
version: '9.0'
|
|
251
265
|
type: :runtime
|
|
252
266
|
prerelease: false
|
|
253
267
|
version_requirements: !ruby/object:Gem::Requirement
|
|
254
268
|
requirements:
|
|
255
269
|
- - "~>"
|
|
256
270
|
- !ruby/object:Gem::Version
|
|
257
|
-
version: '
|
|
271
|
+
version: '9.0'
|
|
258
272
|
- !ruby/object:Gem::Dependency
|
|
259
273
|
name: openvox-strings
|
|
260
274
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -671,6 +685,7 @@ files:
|
|
|
671
685
|
- lib/bolt/transport/choria/bolt_tasks.rb
|
|
672
686
|
- lib/bolt/transport/choria/client.rb
|
|
673
687
|
- lib/bolt/transport/choria/command_builders.rb
|
|
688
|
+
- lib/bolt/transport/choria/file_transfer.rb
|
|
674
689
|
- lib/bolt/transport/choria/helpers.rb
|
|
675
690
|
- lib/bolt/transport/choria/shell.rb
|
|
676
691
|
- lib/bolt/transport/docker.rb
|
|
@@ -735,14 +750,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
735
750
|
requirements:
|
|
736
751
|
- - ">="
|
|
737
752
|
- !ruby/object:Gem::Version
|
|
738
|
-
version: '3.
|
|
753
|
+
version: '3.4'
|
|
739
754
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
740
755
|
requirements:
|
|
741
756
|
- - ">="
|
|
742
757
|
- !ruby/object:Gem::Version
|
|
743
758
|
version: '0'
|
|
744
759
|
requirements: []
|
|
745
|
-
rubygems_version: 4.0.
|
|
760
|
+
rubygems_version: 4.0.16
|
|
746
761
|
specification_version: 4
|
|
747
762
|
summary: Execute commands remotely over SSH and WinRM
|
|
748
763
|
test_files: []
|