bolt 3.12.0 → 3.16.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +137 -104
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +5 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +13 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +47 -7
- data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
- data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
- data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +9 -49
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
- data/guides/{debugging.txt → debugging.yaml} +5 -6
- data/guides/{inventory.txt → inventory.yaml} +6 -7
- data/guides/{links.txt → links.yaml} +3 -4
- data/guides/{logging.txt → logging.yaml} +5 -6
- data/guides/{module.txt → module.yaml} +5 -6
- data/guides/{modulepath.txt → modulepath.yaml} +5 -6
- data/guides/{project.txt → project.yaml} +6 -7
- data/guides/{targets.txt → targets.yaml} +5 -6
- data/guides/{transports.txt → transports.yaml} +6 -7
- data/lib/bolt/analytics.rb +3 -20
- data/lib/bolt/application.rb +620 -0
- data/lib/bolt/bolt_option_parser.rb +18 -6
- data/lib/bolt/cli.rb +592 -772
- data/lib/bolt/config/options.rb +2 -2
- data/lib/bolt/config/transport/options.rb +12 -0
- data/lib/bolt/config/transport/ssh.rb +7 -0
- data/lib/bolt/executor.rb +12 -4
- data/lib/bolt/fiber_executor.rb +63 -14
- data/lib/bolt/outputter/human.rb +201 -43
- data/lib/bolt/outputter/json.rb +68 -43
- data/lib/bolt/outputter/logger.rb +6 -0
- data/lib/bolt/pal.rb +67 -14
- data/lib/bolt/pal/yaml_plan/step.rb +2 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +0 -8
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -1
- data/lib/bolt/plan_creator.rb +2 -20
- data/lib/bolt/plan_future.rb +23 -3
- data/lib/bolt/plan_result.rb +1 -1
- data/lib/bolt/plugin/task.rb +1 -1
- data/lib/bolt/project.rb +0 -7
- data/lib/bolt/result_set.rb +2 -1
- data/lib/bolt/transport/local/connection.rb +17 -1
- data/lib/bolt/transport/orch/connection.rb +13 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +3 -1
- data/lib/bolt/util/format.rb +68 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/schemas/partials/target-ssh.json +4 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +4 -0
- data/lib/bolt_server/transport_app.rb +92 -50
- data/lib/bolt_spec/bolt_context.rb +9 -0
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +31 -7
- data/lib/bolt_spec/plans/publish_stub.rb +4 -4
- data/resources/bolt_bash_completion.sh +1 -1
- metadata +29 -15
- data/guides/guide.txt +0 -17
- data/lib/bolt/secret.rb +0 -37
data/lib/bolt/config/options.rb
CHANGED
@@ -202,7 +202,7 @@ module Bolt
|
|
202
202
|
"level" => {
|
203
203
|
description: "The type of information to log.",
|
204
204
|
type: String,
|
205
|
-
enum: %w[trace debug error info warn fatal
|
205
|
+
enum: %w[trace debug error info warn fatal],
|
206
206
|
_default: "warn"
|
207
207
|
}
|
208
208
|
}
|
@@ -221,7 +221,7 @@ module Bolt
|
|
221
221
|
"level" => {
|
222
222
|
description: "The type of information to log.",
|
223
223
|
type: String,
|
224
|
-
enum: %w[trace debug error info warn fatal
|
224
|
+
enum: %w[trace debug error info warn fatal],
|
225
225
|
_default: "warn"
|
226
226
|
}
|
227
227
|
}
|
@@ -16,6 +16,18 @@ module Bolt
|
|
16
16
|
_default: false,
|
17
17
|
_example: true
|
18
18
|
},
|
19
|
+
"batch-mode" => {
|
20
|
+
type: [TrueClass, FalseClass],
|
21
|
+
description: "Whether to disable password querying. When set to `false`, SSH will fall back to "\
|
22
|
+
"prompting for a password if key authentication fails. This might cause Bolt to hang. "\
|
23
|
+
"To prevent Bolt from hanging, you can configure `ssh-command` to use an SSH utility "\
|
24
|
+
"such as sshpass that supports providing a password non-interactively. For more "\
|
25
|
+
"information, see [Providing a password non-interactively using "\
|
26
|
+
"`native-ssh`](troubleshooting.md#providing-a-password-non-interactively-using-native-ssh).",
|
27
|
+
_plugin: true,
|
28
|
+
_default: true,
|
29
|
+
_example: false
|
30
|
+
},
|
19
31
|
"bundled-ruby" => {
|
20
32
|
description: "Whether to use the Ruby bundled with Bolt packages for local targets.",
|
21
33
|
type: [TrueClass, FalseClass],
|
@@ -34,6 +34,7 @@ module Bolt
|
|
34
34
|
|
35
35
|
# Options available when using the native ssh transport
|
36
36
|
NATIVE_OPTIONS = %w[
|
37
|
+
batch-mode
|
37
38
|
cleanup
|
38
39
|
copy-command
|
39
40
|
host
|
@@ -49,6 +50,7 @@ module Bolt
|
|
49
50
|
].concat(RUN_AS_OPTIONS).sort.freeze
|
50
51
|
|
51
52
|
DEFAULTS = {
|
53
|
+
"batch-mode" => true,
|
52
54
|
"cleanup" => true,
|
53
55
|
"connect-timeout" => 10,
|
54
56
|
"disconnect-timeout" => 5,
|
@@ -124,6 +126,11 @@ module Bolt
|
|
124
126
|
msg = 'Cannot use native SSH transport with load-config set to false'
|
125
127
|
raise Bolt::ValidationError, msg
|
126
128
|
end
|
129
|
+
|
130
|
+
if !@config['batch-mode'] && !@config['ssh-command']
|
131
|
+
raise Bolt::ValidationError,
|
132
|
+
'Must set ssh-command when batch-mode is set to false'
|
133
|
+
end
|
127
134
|
end
|
128
135
|
end
|
129
136
|
end
|
data/lib/bolt/executor.rb
CHANGED
@@ -381,8 +381,16 @@ module Bolt
|
|
381
381
|
# overloaded while also minimizing the Puppet lookups needed from plan
|
382
382
|
# functions
|
383
383
|
#
|
384
|
-
def create_future(scope: nil, name: nil, &block)
|
385
|
-
@fiber_executor.create_future(scope: scope, name: name, &block)
|
384
|
+
def create_future(plan_id:, scope: nil, name: nil, &block)
|
385
|
+
@fiber_executor.create_future(scope: scope, name: name, plan_id: plan_id, &block)
|
386
|
+
end
|
387
|
+
|
388
|
+
def get_current_future(fiber:)
|
389
|
+
@fiber_executor.get_current_future(fiber: fiber)
|
390
|
+
end
|
391
|
+
|
392
|
+
def get_current_plan_id(fiber:)
|
393
|
+
@fiber_executor.get_current_plan_id(fiber: fiber)
|
386
394
|
end
|
387
395
|
|
388
396
|
def plan_complete?
|
@@ -401,8 +409,8 @@ module Bolt
|
|
401
409
|
@fiber_executor.wait(futures, **opts)
|
402
410
|
end
|
403
411
|
|
404
|
-
def
|
405
|
-
@fiber_executor.
|
412
|
+
def get_futures_for_plan(plan_id:)
|
413
|
+
@fiber_executor.get_futures_for_plan(plan_id: plan_id)
|
406
414
|
end
|
407
415
|
|
408
416
|
# Execute a plan function concurrently. This function accepts the executor
|
data/lib/bolt/fiber_executor.rb
CHANGED
@@ -5,18 +5,19 @@ require 'bolt/plan_future'
|
|
5
5
|
|
6
6
|
module Bolt
|
7
7
|
class FiberExecutor
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :active_futures, :finished_futures
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@logger = Bolt::Logger.logger(self)
|
12
12
|
@id = 0
|
13
|
-
@
|
13
|
+
@active_futures = []
|
14
|
+
@finished_futures = []
|
14
15
|
end
|
15
16
|
|
16
17
|
# Whether there is more than one fiber running in parallel.
|
17
18
|
#
|
18
19
|
def in_parallel?
|
19
|
-
|
20
|
+
active_futures.length > 1
|
20
21
|
end
|
21
22
|
|
22
23
|
# Creates a new Puppet scope from the current Plan scope so that variables
|
@@ -24,7 +25,7 @@ module Bolt
|
|
24
25
|
# Then creates a new Fiber to execute the block, wraps the Fiber in a
|
25
26
|
# Bolt::PlanFuture, and returns the Bolt::PlanFuture.
|
26
27
|
#
|
27
|
-
def create_future(scope: nil, name: nil)
|
28
|
+
def create_future(plan_id:, scope: nil, name: nil)
|
28
29
|
newscope = nil
|
29
30
|
if scope
|
30
31
|
# Save existing variables to the new scope before starting the future
|
@@ -46,13 +47,16 @@ module Bolt
|
|
46
47
|
end
|
47
48
|
|
48
49
|
# PlanFutures are assigned an ID, which is just a global incrementing
|
49
|
-
# integer. The main plan should always have ID 0.
|
50
|
+
# integer. The main plan should always have ID 0. They also have a
|
51
|
+
# plan_id, which identifies which plan spawned them. This is used for
|
52
|
+
# tracking which Futures to wait on when `wait()` is called without
|
53
|
+
# arguments.
|
50
54
|
@id += 1
|
51
|
-
future = Bolt::PlanFuture.new(future, @id, name)
|
55
|
+
future = Bolt::PlanFuture.new(future, @id, name: name, plan_id: plan_id, scope: newscope)
|
52
56
|
@logger.trace("Created future #{future.name}")
|
53
57
|
|
54
58
|
# Register the PlanFuture with the FiberExecutor to be executed
|
55
|
-
|
59
|
+
active_futures << future
|
56
60
|
future
|
57
61
|
end
|
58
62
|
|
@@ -63,12 +67,16 @@ module Bolt
|
|
63
67
|
# the PlanFuture and remove the PlanFuture from the FiberExecutor.
|
64
68
|
#
|
65
69
|
def round_robin
|
66
|
-
|
67
|
-
# If the Fiber is still running and can be resumed, then resume it
|
70
|
+
active_futures.each do |future|
|
71
|
+
# If the Fiber is still running and can be resumed, then resume it.
|
72
|
+
# Override Puppet's global_scope to prevent ephemerals in other scopes
|
73
|
+
# from being popped off in the wrong order due to race conditions.
|
74
|
+
# This primarily happens when running executor functions from custom
|
75
|
+
# Puppet language functions, but may happen elsewhere.
|
68
76
|
@logger.trace("Checking future '#{future.name}'")
|
69
77
|
if future.alive?
|
70
78
|
@logger.trace("Resuming future '#{future.name}'")
|
71
|
-
future.resume
|
79
|
+
Puppet.override(global_scope: future.scope) { future.resume }
|
72
80
|
end
|
73
81
|
|
74
82
|
# Once we've restarted the Fiber, check to see if it's finished again
|
@@ -78,19 +86,19 @@ module Bolt
|
|
78
86
|
|
79
87
|
# If the future errored and the main plan has already exited, log the
|
80
88
|
# error at warn level.
|
81
|
-
unless
|
89
|
+
unless active_futures.map(&:id).include?(0) || future.state == "done"
|
82
90
|
Bolt::Logger.warn('errored_futures', "Error in future '#{future.name}': #{future.value}")
|
83
91
|
end
|
84
92
|
|
85
93
|
# Remove the PlanFuture from the FiberExecutor.
|
86
|
-
|
94
|
+
finished_futures.push(active_futures.delete(future))
|
87
95
|
end
|
88
96
|
|
89
97
|
# If the Fiber immediately returned or if the Fiber is blocking on a
|
90
98
|
# `wait` call, Bolt should pause for long enough that something can
|
91
99
|
# execute before checking again. This mitigates CPU
|
92
100
|
# thrashing.
|
93
|
-
return unless
|
101
|
+
return unless active_futures.all? { |f| %i[returned_immediately unfinished].include?(f.value) }
|
94
102
|
@logger.trace("Nothing can be resumed. Rechecking in 0.5 seconds.")
|
95
103
|
|
96
104
|
sleep(0.5)
|
@@ -101,12 +109,53 @@ module Bolt
|
|
101
109
|
# Bolt can exit.
|
102
110
|
#
|
103
111
|
def plan_complete?
|
104
|
-
|
112
|
+
active_futures.empty?
|
113
|
+
end
|
114
|
+
|
115
|
+
def all_futures
|
116
|
+
active_futures + finished_futures
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get the PlanFuture object that is currently executing
|
120
|
+
#
|
121
|
+
def get_current_future(fiber:)
|
122
|
+
all_futures.select { |f| f.fiber == fiber }.first
|
123
|
+
end
|
124
|
+
|
125
|
+
# Get the plan invocation ID for the PlanFuture that is currently executing
|
126
|
+
#
|
127
|
+
def get_current_plan_id(fiber:)
|
128
|
+
get_current_future(fiber: fiber).current_plan
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get the Future objects associated with a particular plan invocation.
|
132
|
+
#
|
133
|
+
def get_futures_for_plan(plan_id:)
|
134
|
+
all_futures.select { |f| f.original_plan == plan_id }
|
105
135
|
end
|
106
136
|
|
107
137
|
# Block until the provided PlanFuture objects have finished, or the timeout is reached.
|
108
138
|
#
|
109
139
|
def wait(futures, timeout: nil, catch_errors: false, **_kwargs)
|
140
|
+
if futures.nil?
|
141
|
+
results = []
|
142
|
+
plan_id = get_current_plan_id(fiber: Fiber.current)
|
143
|
+
# Recollect the futures for this plan until all of the futures have
|
144
|
+
# finished. This ensures that we include futures created inside of
|
145
|
+
# futures being waited on.
|
146
|
+
until (futures = get_futures_for_plan(plan_id: plan_id)).map(&:alive?).none?
|
147
|
+
if futures.map(&:fiber).include?(Fiber.current)
|
148
|
+
msg = "The wait() function cannot be called with no arguments inside a "\
|
149
|
+
"background block in the same plan."
|
150
|
+
raise Bolt::Error.new(msg, 'bolt/infinite-wait')
|
151
|
+
end
|
152
|
+
# Wait for all the futures we know about so far before recollecting
|
153
|
+
# Futures for the plan and waiting again
|
154
|
+
results = wait(futures, timeout: timeout, catch_errors: catch_errors)
|
155
|
+
end
|
156
|
+
return results
|
157
|
+
end
|
158
|
+
|
110
159
|
if timeout.nil?
|
111
160
|
Fiber.yield(:unfinished) until futures.map(&:alive?).none?
|
112
161
|
else
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -78,6 +78,8 @@ module Bolt
|
|
78
78
|
@disable_depth += 1
|
79
79
|
when :message
|
80
80
|
print_message(event[:message])
|
81
|
+
when :verbose
|
82
|
+
print_message(event[:message]) if @verbose
|
81
83
|
end
|
82
84
|
|
83
85
|
if enabled?
|
@@ -308,7 +310,12 @@ module Bolt
|
|
308
310
|
)
|
309
311
|
end
|
310
312
|
|
311
|
-
|
313
|
+
# List available tasks.
|
314
|
+
#
|
315
|
+
# @param tasks [Array] A list of task names and descriptions.
|
316
|
+
# @param modulepath [Array] The modulepath.
|
317
|
+
#
|
318
|
+
def print_tasks(tasks:, modulepath:)
|
312
319
|
command = Bolt::Util.powershell? ? 'Get-BoltTask -Name <TASK NAME>' : 'bolt task show <TASK NAME>'
|
313
320
|
|
314
321
|
tasks = tasks.map do |name, description|
|
@@ -328,8 +335,11 @@ module Bolt
|
|
328
335
|
@stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific task.")
|
329
336
|
end
|
330
337
|
|
331
|
-
#
|
332
|
-
|
338
|
+
# Print information about a task.
|
339
|
+
#
|
340
|
+
# @param task [Bolt::Task] The task information.
|
341
|
+
#
|
342
|
+
def print_task_info(task:)
|
333
343
|
params = (task.parameters || []).sort
|
334
344
|
|
335
345
|
info = +''
|
@@ -339,7 +349,7 @@ module Bolt
|
|
339
349
|
info << if task.description
|
340
350
|
indent(2, task.description.chomp)
|
341
351
|
else
|
342
|
-
indent(2, 'No description')
|
352
|
+
indent(2, 'No description available.')
|
343
353
|
end
|
344
354
|
info << "\n\n"
|
345
355
|
|
@@ -398,7 +408,7 @@ module Bolt
|
|
398
408
|
info << if plan['description']
|
399
409
|
indent(2, plan['description'].chomp)
|
400
410
|
else
|
401
|
-
indent(2, 'No description')
|
411
|
+
indent(2, 'No description available.')
|
402
412
|
end
|
403
413
|
info << "\n\n"
|
404
414
|
|
@@ -441,7 +451,7 @@ module Bolt
|
|
441
451
|
@stream.puts info
|
442
452
|
end
|
443
453
|
|
444
|
-
def print_plans(plans
|
454
|
+
def print_plans(plans:, modulepath:)
|
445
455
|
command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
|
446
456
|
|
447
457
|
plans = plans.map do |name, description|
|
@@ -461,7 +471,11 @@ module Bolt
|
|
461
471
|
@stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific plan.")
|
462
472
|
end
|
463
473
|
|
464
|
-
|
474
|
+
# Print available guide topics.
|
475
|
+
#
|
476
|
+
# @param topics [Array] The available topics.
|
477
|
+
#
|
478
|
+
def print_topics(topics:, **_kwargs)
|
465
479
|
info = +"#{colorize(:cyan, 'Topics')}\n"
|
466
480
|
info << indent(2, topics.join("\n"))
|
467
481
|
info << "\n\n#{colorize(:cyan, 'Additional information')}\n"
|
@@ -469,8 +483,20 @@ module Bolt
|
|
469
483
|
@stream.puts info
|
470
484
|
end
|
471
485
|
|
472
|
-
|
473
|
-
|
486
|
+
# Print the guide for the specified topic.
|
487
|
+
#
|
488
|
+
# @param guide [String] The guide.
|
489
|
+
#
|
490
|
+
def print_guide(topic:, guide:, documentation: nil, **_kwargs)
|
491
|
+
info = +"#{colorize(:cyan, topic)}\n"
|
492
|
+
info << indent(2, guide)
|
493
|
+
|
494
|
+
if documentation
|
495
|
+
info << "\n#{colorize(:cyan, 'Documentation')}\n"
|
496
|
+
info << indent(2, documentation.join("\n"))
|
497
|
+
end
|
498
|
+
|
499
|
+
@stream.puts info
|
474
500
|
end
|
475
501
|
|
476
502
|
def print_plan_lookup(value)
|
@@ -478,15 +504,19 @@ module Bolt
|
|
478
504
|
end
|
479
505
|
|
480
506
|
def print_module_list(module_list)
|
507
|
+
info = +''
|
508
|
+
|
481
509
|
module_list.each do |path, modules|
|
482
|
-
if (mod = modules.find { |m| m[:internal_module_group] })
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
510
|
+
info << if (mod = modules.find { |m| m[:internal_module_group] })
|
511
|
+
colorize(:cyan, mod[:internal_module_group])
|
512
|
+
else
|
513
|
+
colorize(:cyan, path)
|
514
|
+
end
|
515
|
+
|
516
|
+
info << "\n"
|
487
517
|
|
488
518
|
if modules.empty?
|
489
|
-
|
519
|
+
info << '(no modules installed)'
|
490
520
|
else
|
491
521
|
module_info = modules.map do |m|
|
492
522
|
version = if m[:version].nil?
|
@@ -498,24 +528,100 @@ module Bolt
|
|
498
528
|
[m[:name], version]
|
499
529
|
end
|
500
530
|
|
501
|
-
|
531
|
+
info << format_table(module_info, 2, 1).to_s
|
502
532
|
end
|
503
533
|
|
504
|
-
|
534
|
+
info << "\n\n"
|
505
535
|
end
|
536
|
+
|
537
|
+
command = Bolt::Util.powershell? ? 'Get-BoltModule -Name <MODULE>' : 'bolt module show <MODULE>'
|
538
|
+
info << colorize(:cyan, "Additional information\n")
|
539
|
+
info << indent(2, "Use '#{command}' to view details for a specific module.")
|
540
|
+
|
541
|
+
@stream.puts info
|
506
542
|
end
|
507
543
|
|
508
|
-
|
544
|
+
# Prints detailed module information.
|
545
|
+
#
|
546
|
+
# @param name [String] The module's short name.
|
547
|
+
# @param metadata [Hash] The module's metadata.
|
548
|
+
# @param path [String] The path to the module.
|
549
|
+
# @param plans [Array] The module's plans.
|
550
|
+
# @param tasks [Array] The module's tasks.
|
551
|
+
#
|
552
|
+
def print_module_info(name:, metadata:, path:, plans:, tasks:, **_kwargs)
|
553
|
+
info = +''
|
554
|
+
|
555
|
+
info << colorize(:cyan, name)
|
556
|
+
|
557
|
+
info << colorize(:dim, " [#{metadata['version']}]") if metadata['version']
|
558
|
+
info << "\n"
|
559
|
+
|
560
|
+
info << if metadata['summary']
|
561
|
+
indent(2, wrap(metadata['summary'].strip, 76))
|
562
|
+
else
|
563
|
+
indent(2, "No description available.\n")
|
564
|
+
end
|
565
|
+
info << "\n"
|
566
|
+
|
567
|
+
if tasks.any?
|
568
|
+
length = tasks.map(&:first).map(&:length).max
|
569
|
+
data = tasks.map { |task, desc| [task, truncate(desc, 76 - length)] }
|
570
|
+
info << colorize(:cyan, "Tasks\n")
|
571
|
+
info << format_table(data, 2).to_s
|
572
|
+
info << "\n\n"
|
573
|
+
end
|
574
|
+
|
575
|
+
if plans.any?
|
576
|
+
length = plans.map(&:first).map(&:length).max
|
577
|
+
data = plans.map { |plan, desc| [plan, truncate(desc, 76 - length)] }
|
578
|
+
info << colorize(:cyan, "Plans\n")
|
579
|
+
info << format_table(data, 2).to_s
|
580
|
+
info << "\n\n"
|
581
|
+
end
|
582
|
+
|
583
|
+
if metadata['operatingsystem_support']&.any?
|
584
|
+
supported = metadata['operatingsystem_support'].map do |os|
|
585
|
+
[os['operatingsystem'], os['operatingsystemrelease']&.join(', ')]
|
586
|
+
end
|
587
|
+
|
588
|
+
info << colorize(:cyan, "Operating system support\n")
|
589
|
+
info << format_table(supported, 2).to_s
|
590
|
+
info << "\n\n"
|
591
|
+
end
|
592
|
+
|
593
|
+
if metadata['dependencies']&.any?
|
594
|
+
dependencies = metadata['dependencies'].map do |dep|
|
595
|
+
[dep['name'], dep['version_requirement']]
|
596
|
+
end
|
597
|
+
|
598
|
+
info << colorize(:cyan, "Dependencies\n")
|
599
|
+
info << format_table(dependencies, 2).to_s
|
600
|
+
info << "\n\n"
|
601
|
+
end
|
602
|
+
|
603
|
+
info << colorize(:cyan, "Path\n")
|
604
|
+
info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH) ||
|
605
|
+
path.start_with?(Bolt::Config::Modulepath::BOLTLIB_PATH)
|
606
|
+
indent(2, 'built-in module')
|
607
|
+
else
|
608
|
+
indent(2, path)
|
609
|
+
end
|
610
|
+
|
611
|
+
@stream.puts info
|
612
|
+
end
|
613
|
+
|
614
|
+
def print_plugin_list(plugins:, modulepath:)
|
509
615
|
info = +''
|
510
|
-
length =
|
616
|
+
length = plugins.values.map(&:keys).flatten.map(&:length).max + 4
|
511
617
|
|
512
|
-
|
513
|
-
next if
|
618
|
+
plugins.each do |hook, plugin|
|
619
|
+
next if plugin.empty?
|
514
620
|
next if hook == :validate_resolve_reference
|
515
621
|
|
516
622
|
info << colorize(:cyan, "#{hook}\n")
|
517
623
|
|
518
|
-
|
624
|
+
plugin.each do |name, description|
|
519
625
|
info << indent(2, name.ljust(length))
|
520
626
|
info << truncate(description, 80 - length) if description
|
521
627
|
info << "\n"
|
@@ -533,12 +639,37 @@ module Bolt
|
|
533
639
|
@stream.puts info.chomp
|
534
640
|
end
|
535
641
|
|
536
|
-
def
|
537
|
-
|
642
|
+
def print_new_plan(name:, path:)
|
643
|
+
if Bolt::Util.powershell?
|
644
|
+
show_command = 'Get-BoltPlan -Name '
|
645
|
+
run_command = 'Invoke-BoltPlan -Name '
|
646
|
+
else
|
647
|
+
show_command = 'bolt plan show'
|
648
|
+
run_command = 'bolt plan run'
|
649
|
+
end
|
650
|
+
|
651
|
+
print_message(<<~OUTPUT)
|
652
|
+
Created plan '#{name}' at '#{path}'
|
653
|
+
|
654
|
+
Show this plan with:
|
655
|
+
#{show_command} #{name}
|
656
|
+
Run this plan with:
|
657
|
+
#{run_command} #{name}
|
658
|
+
OUTPUT
|
659
|
+
end
|
660
|
+
|
661
|
+
# Print target names and where they came from.
|
662
|
+
#
|
663
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
664
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
665
|
+
# @param flag [Boolean] Whether a targeting command-line option was used.
|
666
|
+
#
|
667
|
+
def print_targets(adhoc:, inventory:, flag:, **_kwargs)
|
668
|
+
adhoc_text = colorize(:yellow, "(Not found in inventory file)")
|
538
669
|
|
539
670
|
targets = []
|
540
|
-
targets +=
|
541
|
-
targets +=
|
671
|
+
targets += inventory[:targets].map { |target| [target['name'], nil] }
|
672
|
+
targets += adhoc[:targets].map { |target| [target['name'], adhoc_text] }
|
542
673
|
|
543
674
|
info = +''
|
544
675
|
|
@@ -551,27 +682,31 @@ module Bolt
|
|
551
682
|
end
|
552
683
|
info << "\n\n"
|
553
684
|
|
554
|
-
info << format_inventory_source(
|
555
|
-
info << format_target_summary(
|
685
|
+
info << format_inventory_source(inventory[:file], inventory[:default])
|
686
|
+
info << format_target_summary(inventory[:count], adhoc[:count], flag, false)
|
556
687
|
|
557
688
|
@stream.puts info
|
558
689
|
end
|
559
690
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
691
|
+
# Print detailed target information.
|
692
|
+
#
|
693
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
694
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
695
|
+
# @param flag [Boolean] Whether a targeting command-line option was used.
|
696
|
+
#
|
697
|
+
def print_target_info(adhoc:, inventory:, flag:, **_kwargs)
|
698
|
+
targets = (adhoc[:targets] + inventory[:targets]).sort_by { |t| t['name'] }
|
564
699
|
|
565
700
|
info = +''
|
566
701
|
|
567
702
|
if targets.any?
|
568
|
-
|
703
|
+
adhoc_text = colorize(:yellow, " (Not found in inventory file)")
|
569
704
|
|
570
705
|
targets.each do |target|
|
571
|
-
info << colorize(:cyan, target
|
572
|
-
info <<
|
706
|
+
info << colorize(:cyan, target['name'])
|
707
|
+
info << adhoc_text if adhoc[:targets].include?(target)
|
573
708
|
info << "\n"
|
574
|
-
info << indent(2, target.
|
709
|
+
info << indent(2, target.to_yaml.lines.drop(1).join)
|
575
710
|
info << "\n"
|
576
711
|
end
|
577
712
|
else
|
@@ -579,8 +714,8 @@ module Bolt
|
|
579
714
|
info << indent(2, "No targets\n\n")
|
580
715
|
end
|
581
716
|
|
582
|
-
info << format_inventory_source(
|
583
|
-
info << format_target_summary(
|
717
|
+
info << format_inventory_source(inventory[:file], inventory[:default])
|
718
|
+
info << format_target_summary(inventory[:count], adhoc[:count], flag, true)
|
584
719
|
|
585
720
|
@stream.puts info
|
586
721
|
end
|
@@ -626,7 +761,13 @@ module Bolt
|
|
626
761
|
info
|
627
762
|
end
|
628
763
|
|
629
|
-
|
764
|
+
# Print inventory group information.
|
765
|
+
#
|
766
|
+
# @param count [Integer] Number of groups in the inventory.
|
767
|
+
# @param groups [Array] Names of groups in the inventory.
|
768
|
+
# @param inventory [Hash] Where the inventory was loaded from.
|
769
|
+
#
|
770
|
+
def print_groups(count:, groups:, inventory:)
|
630
771
|
info = +''
|
631
772
|
|
632
773
|
# Add group list
|
@@ -635,18 +776,18 @@ module Bolt
|
|
635
776
|
info << "\n\n"
|
636
777
|
|
637
778
|
# Add inventory file source
|
638
|
-
info << format_inventory_source(
|
779
|
+
info << format_inventory_source(inventory[:source], inventory[:default])
|
639
780
|
|
640
781
|
# Add group count summary
|
641
782
|
info << colorize(:cyan, "Group count\n")
|
642
|
-
info << indent(2, "#{
|
783
|
+
info << indent(2, "#{count} total")
|
643
784
|
|
644
785
|
@stream.puts info
|
645
786
|
end
|
646
787
|
|
647
788
|
# @param [Bolt::ResultSet] apply_result A ResultSet object representing the result of a `bolt apply`
|
648
|
-
def print_apply_result(apply_result
|
649
|
-
print_summary(apply_result, elapsed_time)
|
789
|
+
def print_apply_result(apply_result)
|
790
|
+
print_summary(apply_result, apply_result.elapsed_time)
|
650
791
|
end
|
651
792
|
|
652
793
|
# @param [Bolt::PlanResult] plan_result A PlanResult object
|
@@ -663,6 +804,12 @@ module Bolt
|
|
663
804
|
print_container_result(value.result)
|
664
805
|
when Bolt::ResultSet
|
665
806
|
print_result_set(value)
|
807
|
+
when Bolt::Result
|
808
|
+
print_result(value)
|
809
|
+
when Bolt::ApplyResult
|
810
|
+
print_apply_result(value)
|
811
|
+
when Bolt::Error
|
812
|
+
print_bolt_error(**value.to_h.transform_keys(&:to_sym))
|
666
813
|
else
|
667
814
|
@stream.puts(::JSON.pretty_generate(plan_result, quirks_mode: true))
|
668
815
|
end
|
@@ -702,6 +849,17 @@ module Bolt
|
|
702
849
|
@stream.puts(colorize(:red, message))
|
703
850
|
end
|
704
851
|
|
852
|
+
def print_bolt_error(msg:, details:, **_kwargs)
|
853
|
+
err = msg
|
854
|
+
if (f = details[:file])
|
855
|
+
err += "\n (file: #{f}"
|
856
|
+
err += ", line: #{details[:line]}" if details[:line]
|
857
|
+
err += ", column: #{details[:column]}" if details[:column]
|
858
|
+
err += ")"
|
859
|
+
end
|
860
|
+
@stream.puts(colorize(:red, err))
|
861
|
+
end
|
862
|
+
|
705
863
|
def print_prompt(prompt)
|
706
864
|
@stream.print(colorize(:cyan, indent(4, prompt)))
|
707
865
|
end
|