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,958 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../bolt/container_result'
|
4
|
+
require_relative '../../bolt/pal'
|
5
|
+
require_relative '../../bolt/util/format'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class Outputter
|
9
|
+
class Human < Bolt::Outputter
|
10
|
+
COLORS = {
|
11
|
+
dim: "2", # Dim, the other color of the rainbow
|
12
|
+
red: "31",
|
13
|
+
green: "32",
|
14
|
+
yellow: "33",
|
15
|
+
cyan: "36"
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def print_head; end
|
19
|
+
|
20
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
21
|
+
super
|
22
|
+
# Plans and without_default_logging() calls can both be nested, so we
|
23
|
+
# track each of them with a "stack" consisting of an integer.
|
24
|
+
@plan_depth = 0
|
25
|
+
@disable_depth = 0
|
26
|
+
@pinwheel = %w[- \\ | /]
|
27
|
+
end
|
28
|
+
|
29
|
+
def colorize(color, string)
|
30
|
+
if @color && @stream.isatty
|
31
|
+
"\033[#{COLORS[color]}m#{string}\033[0m"
|
32
|
+
else
|
33
|
+
string
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_spin
|
38
|
+
return unless @spin && @stream.isatty && !@spinning
|
39
|
+
@spinning = true
|
40
|
+
@spin_thread = Thread.new do
|
41
|
+
loop do
|
42
|
+
sleep(0.1)
|
43
|
+
@stream.print(colorize(:cyan, @pinwheel.rotate!.first + "\b"))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stop_spin
|
49
|
+
return unless @spin && @stream.isatty && @spinning
|
50
|
+
@spinning = false
|
51
|
+
@spin_thread.terminate
|
52
|
+
@stream.print("\b")
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_trail(string)
|
56
|
+
string.sub(/\s\z/, '')
|
57
|
+
end
|
58
|
+
|
59
|
+
# Wraps a string to the specified width. Lines only wrap
|
60
|
+
# at whitespace.
|
61
|
+
#
|
62
|
+
def wrap(string, width = 80)
|
63
|
+
return string unless string.is_a?(String)
|
64
|
+
string.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Trims a string to a specified width, adding an ellipsis if it's longer.
|
68
|
+
#
|
69
|
+
def truncate(string, width = 80)
|
70
|
+
return string unless string.is_a?(String) && string.length > width
|
71
|
+
string.lines.first[0...width].gsub(/\s\w+\s*$/, '...')
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_event(event)
|
75
|
+
case event[:type]
|
76
|
+
when :enable_default_output
|
77
|
+
@disable_depth -= 1
|
78
|
+
when :disable_default_output
|
79
|
+
@disable_depth += 1
|
80
|
+
when :message
|
81
|
+
print_message(event[:message])
|
82
|
+
when :verbose
|
83
|
+
print_message(event[:message]) if @verbose
|
84
|
+
end
|
85
|
+
|
86
|
+
if enabled?
|
87
|
+
case event[:type]
|
88
|
+
when :node_start
|
89
|
+
print_start(event[:target]) if @verbose
|
90
|
+
when :node_result
|
91
|
+
print_result(event[:result]) if @verbose
|
92
|
+
when :step_start
|
93
|
+
print_step_start(**event) if plan_logging?
|
94
|
+
when :step_finish
|
95
|
+
print_step_finish(**event) if plan_logging?
|
96
|
+
when :plan_start
|
97
|
+
print_plan_start(event)
|
98
|
+
when :plan_finish
|
99
|
+
print_plan_finish(event)
|
100
|
+
when :container_start
|
101
|
+
print_container_start(event) if plan_logging?
|
102
|
+
when :container_finish
|
103
|
+
print_container_finish(event) if plan_logging?
|
104
|
+
when :start_spin
|
105
|
+
start_spin
|
106
|
+
when :stop_spin
|
107
|
+
stop_spin
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def enabled?
|
113
|
+
@disable_depth == 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def plan_logging?
|
117
|
+
@plan_depth > 0
|
118
|
+
end
|
119
|
+
|
120
|
+
def print_start(target)
|
121
|
+
@stream.puts(colorize(:green, "Started on #{target.safe_name}..."))
|
122
|
+
end
|
123
|
+
|
124
|
+
def print_container_result(result)
|
125
|
+
if result.success?
|
126
|
+
@stream.puts(colorize(:green, "Finished running container #{result.object}:"))
|
127
|
+
else
|
128
|
+
@stream.puts(colorize(:red, "Failed running container #{result.object}:"))
|
129
|
+
end
|
130
|
+
|
131
|
+
if result.error_hash
|
132
|
+
@stream.puts(colorize(:red, remove_trail(indent(2, result.error_hash['msg']))))
|
133
|
+
return 0
|
134
|
+
end
|
135
|
+
|
136
|
+
# Only print results if there's something other than empty string and hash
|
137
|
+
safe_value = result.safe_value
|
138
|
+
if safe_value['stdout'].strip.empty? && safe_value['stderr'].strip.empty?
|
139
|
+
@stream.puts(indent(2, "Running container #{result.object} completed successfully with no result"))
|
140
|
+
else
|
141
|
+
unless safe_value['stdout'].strip && safe_value['stdout'].strip.empty?
|
142
|
+
@stream.puts(indent(2, "STDOUT:"))
|
143
|
+
@stream.puts(indent(4, safe_value['stdout']))
|
144
|
+
end
|
145
|
+
unless safe_value['stderr'].strip.empty?
|
146
|
+
@stream.puts(indent(2, "STDERR:"))
|
147
|
+
@stream.puts(indent(4, safe_value['stderr']))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def print_result(result)
|
153
|
+
if result.success?
|
154
|
+
@stream.puts(colorize(:green, "Finished on #{result.target.safe_name}:"))
|
155
|
+
else
|
156
|
+
@stream.puts(colorize(:red, "Failed on #{result.target.safe_name}:"))
|
157
|
+
end
|
158
|
+
|
159
|
+
if result.error_hash
|
160
|
+
@stream.puts(colorize(:red, remove_trail(indent(2, result.error_hash['msg']))))
|
161
|
+
end
|
162
|
+
|
163
|
+
if result.is_a?(Bolt::ApplyResult) && @verbose
|
164
|
+
result.resource_logs.each do |log|
|
165
|
+
# Omit low-level info/debug messages
|
166
|
+
next if %w[info debug].include?(log['level'])
|
167
|
+
message = format_log(log)
|
168
|
+
@stream.puts(indent(2, message))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Only print results if there's something other than empty string and hash
|
173
|
+
if result.value.empty? || (result.value.keys == ['_output'] && !result.message?)
|
174
|
+
@stream.puts(indent(2, "#{result.action.capitalize} completed successfully with no result"))
|
175
|
+
else
|
176
|
+
# Only print messages that have something other than whitespace
|
177
|
+
if result.message?
|
178
|
+
@stream.puts(remove_trail(indent(2, result.message)))
|
179
|
+
end
|
180
|
+
case result.action
|
181
|
+
when 'command', 'script'
|
182
|
+
safe_value = result.safe_value
|
183
|
+
if safe_value["merged_output"]
|
184
|
+
@stream.puts(indent(2, safe_value['merged_output'])) unless safe_value['merged_output'].strip.empty?
|
185
|
+
|
186
|
+
else # output stdout or stderr
|
187
|
+
unless safe_value['stdout'].nil? || safe_value['stdout'].strip.empty?
|
188
|
+
@stream.puts(indent(2, "STDOUT:"))
|
189
|
+
@stream.puts(indent(4, safe_value['stdout']))
|
190
|
+
end
|
191
|
+
unless safe_value['stderr'].nil? || safe_value['stderr'].strip.empty?
|
192
|
+
@stream.puts(indent(2, "STDERR:"))
|
193
|
+
@stream.puts(indent(4, safe_value['stderr']))
|
194
|
+
end
|
195
|
+
end
|
196
|
+
when 'lookup'
|
197
|
+
@stream.puts(indent(2, Bolt::Util::Format.stringify(result['value'])))
|
198
|
+
else
|
199
|
+
if result.generic_value.any?
|
200
|
+
@stream.puts(indent(2, ::JSON.pretty_generate(result.generic_value)))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def format_log(log)
|
207
|
+
color = case log['level']
|
208
|
+
when 'warn'
|
209
|
+
:yellow
|
210
|
+
when 'err'
|
211
|
+
:red
|
212
|
+
end
|
213
|
+
source = "#{log['source']}: " if log['source']
|
214
|
+
message = "#{log['level'].capitalize}: #{source}#{log['message']}"
|
215
|
+
message = colorize(color, message) if color
|
216
|
+
message
|
217
|
+
end
|
218
|
+
|
219
|
+
def print_step_start(description:, targets:, **_kwargs)
|
220
|
+
target_str = if targets.length > 5
|
221
|
+
"#{targets.count} targets"
|
222
|
+
else
|
223
|
+
targets.map(&:safe_name).join(', ')
|
224
|
+
end
|
225
|
+
@stream.puts(colorize(:green, "Starting: #{description} on #{target_str}"))
|
226
|
+
end
|
227
|
+
|
228
|
+
def print_step_finish(description:, result:, duration:, **_kwargs)
|
229
|
+
failures = result.error_set.length
|
230
|
+
plural = failures == 1 ? '' : 's'
|
231
|
+
message = "Finished: #{description} with #{failures} failure#{plural} in #{duration.round(2)} sec"
|
232
|
+
@stream.puts(colorize(:green, message))
|
233
|
+
end
|
234
|
+
|
235
|
+
def print_container_start(image:, **_kwargs)
|
236
|
+
@stream.puts(colorize(:green, "Starting: run container '#{image}'"))
|
237
|
+
end
|
238
|
+
|
239
|
+
def print_container_finish(event)
|
240
|
+
result = if event[:result].is_a?(Bolt::ContainerFailure)
|
241
|
+
event[:result].result
|
242
|
+
else
|
243
|
+
event[:result]
|
244
|
+
end
|
245
|
+
|
246
|
+
if result.success?
|
247
|
+
@stream.puts(colorize(:green, "Finished: run container '#{result.object}' succeeded."))
|
248
|
+
else
|
249
|
+
@stream.puts(colorize(:red, "Finished: run container '#{result.object}' failed."))
|
250
|
+
end
|
251
|
+
print_container_result(result) if @verbose
|
252
|
+
end
|
253
|
+
|
254
|
+
def print_plan_start(event)
|
255
|
+
@plan_depth += 1
|
256
|
+
# We use this event to both mark the start of a plan _and_ to enable
|
257
|
+
# plan logging for `apply`, so only log the message if we were called
|
258
|
+
# with a plan
|
259
|
+
if event[:plan]
|
260
|
+
@stream.puts(colorize(:green, "Starting: plan #{event[:plan]}"))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def print_plan_finish(event)
|
265
|
+
@plan_depth -= 1
|
266
|
+
plan = event[:plan]
|
267
|
+
duration = event[:duration]
|
268
|
+
@stream.puts(colorize(:green, "Finished: plan #{plan} in #{duration_to_string(duration)}"))
|
269
|
+
end
|
270
|
+
|
271
|
+
def print_summary(results, elapsed_time = nil)
|
272
|
+
ok_set = results.ok_set
|
273
|
+
unless ok_set.empty?
|
274
|
+
@stream.puts format('Successful on %<size>d target%<plural>s: %<names>s',
|
275
|
+
size: ok_set.size,
|
276
|
+
plural: ok_set.size == 1 ? '' : 's',
|
277
|
+
names: ok_set.targets.map(&:safe_name).join(','))
|
278
|
+
end
|
279
|
+
|
280
|
+
error_set = results.error_set
|
281
|
+
unless error_set.empty?
|
282
|
+
@stream.puts colorize(:red,
|
283
|
+
format('Failed on %<size>d target%<plural>s: %<names>s',
|
284
|
+
size: error_set.size,
|
285
|
+
plural: error_set.size == 1 ? '' : 's',
|
286
|
+
names: error_set.targets.map(&:safe_name).join(',')))
|
287
|
+
end
|
288
|
+
|
289
|
+
total_msg = format('Ran on %<size>d target%<plural>s',
|
290
|
+
size: results.size,
|
291
|
+
plural: results.size == 1 ? '' : 's')
|
292
|
+
total_msg << " in #{duration_to_string(elapsed_time)}" unless elapsed_time.nil?
|
293
|
+
@stream.puts total_msg
|
294
|
+
end
|
295
|
+
|
296
|
+
def format_table(results, padding_left = 0, padding_right = 3)
|
297
|
+
# lazy-load expensive gem code
|
298
|
+
require 'terminal-table'
|
299
|
+
|
300
|
+
Terminal::Table.new(
|
301
|
+
rows: results,
|
302
|
+
style: {
|
303
|
+
border_x: '',
|
304
|
+
border_y: '',
|
305
|
+
border_i: '',
|
306
|
+
padding_left: padding_left,
|
307
|
+
padding_right: padding_right,
|
308
|
+
border_top: false,
|
309
|
+
border_bottom: false
|
310
|
+
}
|
311
|
+
)
|
312
|
+
end
|
313
|
+
|
314
|
+
# List available tasks.
|
315
|
+
#
|
316
|
+
# @param tasks [Array] A list of task names and descriptions.
|
317
|
+
# @param modulepath [Array] The modulepath.
|
318
|
+
#
|
319
|
+
def print_tasks(tasks:, modulepath:)
|
320
|
+
command = Bolt::Util.powershell? ? 'Get-BoltTask -Name <TASK NAME>' : 'bolt task show <TASK NAME>'
|
321
|
+
|
322
|
+
tasks = tasks.map do |name, description|
|
323
|
+
description = truncate(description, 72)
|
324
|
+
[name, description]
|
325
|
+
end
|
326
|
+
|
327
|
+
@stream.puts colorize(:cyan, 'Tasks')
|
328
|
+
@stream.puts tasks.any? ? format_table(tasks, 2) : indent(2, 'No available tasks')
|
329
|
+
@stream.puts
|
330
|
+
|
331
|
+
@stream.puts colorize(:cyan, 'Modulepath')
|
332
|
+
@stream.puts indent(2, modulepath.join(File::PATH_SEPARATOR))
|
333
|
+
@stream.puts
|
334
|
+
|
335
|
+
@stream.puts colorize(:cyan, 'Additional information')
|
336
|
+
@stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific task.")
|
337
|
+
end
|
338
|
+
|
339
|
+
# Print information about a task.
|
340
|
+
#
|
341
|
+
# @param task [Bolt::Task] The task information.
|
342
|
+
#
|
343
|
+
def print_task_info(task:)
|
344
|
+
params = (task.parameters || []).sort
|
345
|
+
|
346
|
+
info = +''
|
347
|
+
|
348
|
+
# Add task name and description
|
349
|
+
info << colorize(:cyan, "#{task.name}\n")
|
350
|
+
info << if task.description
|
351
|
+
indent(2, task.description.chomp)
|
352
|
+
else
|
353
|
+
indent(2, 'No description available.')
|
354
|
+
end
|
355
|
+
info << "\n\n"
|
356
|
+
|
357
|
+
# Build usage string
|
358
|
+
usage = +''
|
359
|
+
usage << if Bolt::Util.powershell?
|
360
|
+
"Invoke-BoltTask -Name #{task.name} -Targets <targets>"
|
361
|
+
else
|
362
|
+
"bolt task run #{task.name} --targets <targets>"
|
363
|
+
end
|
364
|
+
usage << (Bolt::Util.powershell? ? ' [-Noop]' : ' [--noop]') if task.supports_noop
|
365
|
+
params.each do |name, data|
|
366
|
+
usage << if data['type']&.start_with?('Optional') || data.key?('default')
|
367
|
+
" [#{name}=<value>]"
|
368
|
+
else
|
369
|
+
" #{name}=<value>"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Add usage
|
374
|
+
info << colorize(:cyan, "Usage\n")
|
375
|
+
info << indent(2, wrap(usage))
|
376
|
+
info << "\n"
|
377
|
+
|
378
|
+
# Add parameters, if any
|
379
|
+
if params.any?
|
380
|
+
info << colorize(:cyan, "Parameters\n")
|
381
|
+
params.each do |name, data|
|
382
|
+
info << indent(2, "#{colorize(:yellow, name)} #{colorize(:dim, data['type'] || 'Any')}\n")
|
383
|
+
info << indent(4, "#{wrap(data['description']).chomp}\n") if data['description']
|
384
|
+
info << indent(4, "Default: #{data['default'].inspect}\n") if data.key?('default')
|
385
|
+
info << "\n"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Add module location
|
390
|
+
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
391
|
+
info << colorize(:cyan, "Module\n")
|
392
|
+
info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
393
|
+
indent(2, 'built-in module')
|
394
|
+
else
|
395
|
+
indent(2, path)
|
396
|
+
end
|
397
|
+
|
398
|
+
@stream.puts info
|
399
|
+
end
|
400
|
+
|
401
|
+
# @param [Hash] plan A hash representing the plan
|
402
|
+
def print_plan_info(plan)
|
403
|
+
params = plan['parameters'].sort
|
404
|
+
|
405
|
+
info = +''
|
406
|
+
|
407
|
+
# Add plan name and description
|
408
|
+
info << colorize(:cyan, "#{plan['name']}\n")
|
409
|
+
|
410
|
+
description = +''
|
411
|
+
description << "#{plan['summary']}\n\n" if plan['summary']
|
412
|
+
description << plan['docstring'] if plan['docstring']
|
413
|
+
|
414
|
+
info << if description.empty?
|
415
|
+
indent(2, 'No description available.')
|
416
|
+
else
|
417
|
+
indent(2, description.strip)
|
418
|
+
end
|
419
|
+
info << "\n\n"
|
420
|
+
|
421
|
+
# Build the usage string
|
422
|
+
usage = +''
|
423
|
+
usage << if Bolt::Util.powershell?
|
424
|
+
"Invoke-BoltPlan -Name #{plan['name']}"
|
425
|
+
else
|
426
|
+
"bolt plan run #{plan['name']}"
|
427
|
+
end
|
428
|
+
params.each do |name, data|
|
429
|
+
usage << (data.include?('default_value') ? " [#{name}=<value>]" : " #{name}=<value>")
|
430
|
+
end
|
431
|
+
|
432
|
+
# Add usage
|
433
|
+
info << colorize(:cyan, "Usage\n")
|
434
|
+
info << indent(2, wrap(usage))
|
435
|
+
info << "\n"
|
436
|
+
|
437
|
+
# Add parameters, if any
|
438
|
+
if params.any?
|
439
|
+
info << colorize(:cyan, "Parameters\n")
|
440
|
+
|
441
|
+
params.each do |name, data|
|
442
|
+
info << indent(2, "#{colorize(:yellow, name)} #{colorize(:dim, data['type'])}\n")
|
443
|
+
info << indent(4, "#{wrap(data['description']).chomp}\n") if data['description']
|
444
|
+
info << indent(4, "Default: #{data['default_value']}\n") unless data['default_value'].nil?
|
445
|
+
info << "\n"
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
# Add module location
|
450
|
+
info << colorize(:cyan, "Module\n")
|
451
|
+
info << if plan['module'].start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
452
|
+
indent(2, 'built-in module')
|
453
|
+
else
|
454
|
+
indent(2, plan['module'])
|
455
|
+
end
|
456
|
+
|
457
|
+
@stream.puts info
|
458
|
+
end
|
459
|
+
|
460
|
+
def print_plans(plans:, modulepath:)
|
461
|
+
command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
|
462
|
+
|
463
|
+
plans = plans.map do |name, description|
|
464
|
+
description = truncate(description, 72)
|
465
|
+
[name, description]
|
466
|
+
end
|
467
|
+
|
468
|
+
@stream.puts colorize(:cyan, 'Plans')
|
469
|
+
@stream.puts plans.any? ? format_table(plans, 2) : indent(2, 'No available plans')
|
470
|
+
@stream.puts
|
471
|
+
|
472
|
+
@stream.puts colorize(:cyan, 'Modulepath')
|
473
|
+
@stream.puts indent(2, modulepath.join(File::PATH_SEPARATOR))
|
474
|
+
@stream.puts
|
475
|
+
|
476
|
+
@stream.puts colorize(:cyan, 'Additional information')
|
477
|
+
@stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific plan.")
|
478
|
+
end
|
479
|
+
|
480
|
+
# Print available guide topics.
|
481
|
+
#
|
482
|
+
# @param topics [Array] The available topics.
|
483
|
+
#
|
484
|
+
def print_topics(topics:, **_kwargs)
|
485
|
+
info = +"#{colorize(:cyan, 'Topics')}\n"
|
486
|
+
info << indent(2, topics.join("\n"))
|
487
|
+
info << "\n\n#{colorize(:cyan, 'Additional information')}\n"
|
488
|
+
info << indent(2, "Use 'bolt guide <TOPIC>' to view a specific guide.")
|
489
|
+
@stream.puts info
|
490
|
+
end
|
491
|
+
|
492
|
+
# Print the guide for the specified topic.
|
493
|
+
#
|
494
|
+
# @param guide [String] The guide.
|
495
|
+
#
|
496
|
+
def print_guide(topic:, guide:, documentation: nil, **_kwargs)
|
497
|
+
info = +"#{colorize(:cyan, topic)}\n"
|
498
|
+
info << indent(2, guide)
|
499
|
+
|
500
|
+
if documentation
|
501
|
+
info << "\n#{colorize(:cyan, 'Documentation')}\n"
|
502
|
+
info << indent(2, documentation.join("\n"))
|
503
|
+
end
|
504
|
+
|
505
|
+
@stream.puts info
|
506
|
+
end
|
507
|
+
|
508
|
+
def print_plan_lookup(value)
|
509
|
+
@stream.puts(value)
|
510
|
+
end
|
511
|
+
|
512
|
+
def print_module_list(module_list)
|
513
|
+
info = +''
|
514
|
+
|
515
|
+
module_list.each do |path, modules|
|
516
|
+
info << if (mod = modules.find { |m| m[:internal_module_group] })
|
517
|
+
colorize(:cyan, mod[:internal_module_group])
|
518
|
+
else
|
519
|
+
colorize(:cyan, path)
|
520
|
+
end
|
521
|
+
|
522
|
+
info << "\n"
|
523
|
+
|
524
|
+
if modules.empty?
|
525
|
+
info << '(no modules installed)'
|
526
|
+
else
|
527
|
+
module_info = modules.map do |m|
|
528
|
+
version = if m[:version].nil?
|
529
|
+
m[:internal_module_group].nil? ? '(no metadata)' : '(built-in)'
|
530
|
+
else
|
531
|
+
m[:version]
|
532
|
+
end
|
533
|
+
|
534
|
+
[m[:name], version]
|
535
|
+
end
|
536
|
+
|
537
|
+
info << format_table(module_info, 2, 1).to_s
|
538
|
+
end
|
539
|
+
|
540
|
+
info << "\n\n"
|
541
|
+
end
|
542
|
+
|
543
|
+
command = Bolt::Util.powershell? ? 'Get-BoltModule -Name <MODULE>' : 'bolt module show <MODULE>'
|
544
|
+
info << colorize(:cyan, "Additional information\n")
|
545
|
+
info << indent(2, "Use '#{command}' to view details for a specific module.")
|
546
|
+
|
547
|
+
@stream.puts info
|
548
|
+
end
|
549
|
+
|
550
|
+
# Prints detailed module information.
|
551
|
+
#
|
552
|
+
# @param name [String] The module's short name.
|
553
|
+
# @param metadata [Hash] The module's metadata.
|
554
|
+
# @param path [String] The path to the module.
|
555
|
+
# @param plans [Array] The module's plans.
|
556
|
+
# @param tasks [Array] The module's tasks.
|
557
|
+
#
|
558
|
+
def print_module_info(name:, metadata:, path:, plans:, tasks:, **_kwargs)
|
559
|
+
info = +''
|
560
|
+
|
561
|
+
info << colorize(:cyan, name)
|
562
|
+
|
563
|
+
info << colorize(:dim, " [#{metadata['version']}]") if metadata['version']
|
564
|
+
info << "\n"
|
565
|
+
|
566
|
+
info << if metadata['summary']
|
567
|
+
indent(2, wrap(metadata['summary'].strip, 76))
|
568
|
+
else
|
569
|
+
indent(2, "No description available.\n")
|
570
|
+
end
|
571
|
+
info << "\n"
|
572
|
+
|
573
|
+
if tasks.any?
|
574
|
+
length = tasks.map(&:first).map(&:length).max
|
575
|
+
data = tasks.map { |task, desc| [task, truncate(desc, 76 - length)] }
|
576
|
+
info << colorize(:cyan, "Tasks\n")
|
577
|
+
info << format_table(data, 2).to_s
|
578
|
+
info << "\n\n"
|
579
|
+
end
|
580
|
+
|
581
|
+
if plans.any?
|
582
|
+
length = plans.map(&:first).map(&:length).max
|
583
|
+
data = plans.map { |plan, desc| [plan, truncate(desc, 76 - length)] }
|
584
|
+
info << colorize(:cyan, "Plans\n")
|
585
|
+
info << format_table(data, 2).to_s
|
586
|
+
info << "\n\n"
|
587
|
+
end
|
588
|
+
|
589
|
+
if metadata['operatingsystem_support']&.any?
|
590
|
+
supported = metadata['operatingsystem_support'].map do |os|
|
591
|
+
[os['operatingsystem'], os['operatingsystemrelease']&.join(', ')]
|
592
|
+
end
|
593
|
+
|
594
|
+
info << colorize(:cyan, "Operating system support\n")
|
595
|
+
info << format_table(supported, 2).to_s
|
596
|
+
info << "\n\n"
|
597
|
+
end
|
598
|
+
|
599
|
+
if metadata['dependencies']&.any?
|
600
|
+
dependencies = metadata['dependencies'].map do |dep|
|
601
|
+
[dep['name'], dep['version_requirement']]
|
602
|
+
end
|
603
|
+
|
604
|
+
info << colorize(:cyan, "Dependencies\n")
|
605
|
+
info << format_table(dependencies, 2).to_s
|
606
|
+
info << "\n\n"
|
607
|
+
end
|
608
|
+
|
609
|
+
info << colorize(:cyan, "Path\n")
|
610
|
+
info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH) ||
|
611
|
+
path.start_with?(Bolt::Config::Modulepath::BOLTLIB_PATH)
|
612
|
+
indent(2, 'built-in module')
|
613
|
+
else
|
614
|
+
indent(2, path)
|
615
|
+
end
|
616
|
+
|
617
|
+
@stream.puts info
|
618
|
+
end
|
619
|
+
|
620
|
+
def print_plugin_list(plugins:, modulepath:)
|
621
|
+
info = +''
|
622
|
+
length = plugins.values.map(&:keys).flatten.map(&:length).max + 4
|
623
|
+
|
624
|
+
plugins.each do |hook, plugin|
|
625
|
+
next if plugin.empty?
|
626
|
+
next if hook == :validate_resolve_reference
|
627
|
+
|
628
|
+
info << colorize(:cyan, "#{hook}\n")
|
629
|
+
|
630
|
+
plugin.each do |name, description|
|
631
|
+
info << indent(2, name.ljust(length))
|
632
|
+
info << truncate(description, 80 - length) if description
|
633
|
+
info << "\n"
|
634
|
+
end
|
635
|
+
|
636
|
+
info << "\n"
|
637
|
+
end
|
638
|
+
|
639
|
+
info << colorize(:cyan, "Modulepath\n")
|
640
|
+
info << indent(2, "#{modulepath.join(File::PATH_SEPARATOR)}\n\n")
|
641
|
+
|
642
|
+
info << colorize(:cyan, "Additional information\n")
|
643
|
+
info << indent(2, "For more information about using plugins see https://pup.pt/bolt-plugins")
|
644
|
+
|
645
|
+
@stream.puts info.chomp
|
646
|
+
end
|
647
|
+
|
648
|
+
def print_new_plan(name:, path:)
|
649
|
+
if Bolt::Util.powershell?
|
650
|
+
show_command = 'Get-BoltPlan -Name '
|
651
|
+
run_command = 'Invoke-BoltPlan -Name '
|
652
|
+
else
|
653
|
+
show_command = 'bolt plan show'
|
654
|
+
run_command = 'bolt plan run'
|
655
|
+
end
|
656
|
+
|
657
|
+
print_message(<<~OUTPUT)
|
658
|
+
Created plan '#{name}' at '#{path}'
|
659
|
+
|
660
|
+
Show this plan with:
|
661
|
+
#{show_command} #{name}
|
662
|
+
Run this plan with:
|
663
|
+
#{run_command} #{name}
|
664
|
+
OUTPUT
|
665
|
+
end
|
666
|
+
|
667
|
+
def print_new_policy(name:, path:)
|
668
|
+
if Bolt::Util.powershell?
|
669
|
+
apply_command = "Invoke-BoltPolicy -Name #{name} -Targets <TARGETS>"
|
670
|
+
show_command = 'Get-BoltPolicy'
|
671
|
+
else
|
672
|
+
apply_command = "bolt policy apply #{name} --targets <TARGETS>"
|
673
|
+
show_command = 'bolt policy show'
|
674
|
+
end
|
675
|
+
|
676
|
+
print_message(<<~OUTPUT)
|
677
|
+
Created policy '#{name}' at '#{path}'
|
678
|
+
|
679
|
+
Apply this policy with:
|
680
|
+
#{apply_command}
|
681
|
+
Show available policies with:
|
682
|
+
#{show_command}
|
683
|
+
OUTPUT
|
684
|
+
end
|
685
|
+
|
686
|
+
# Print policies and the modulepath they are loaded from.
|
687
|
+
#
|
688
|
+
# @param policies [Array] The list of available policies.
|
689
|
+
# @param modulepath [Array] The project's modulepath.
|
690
|
+
#
|
691
|
+
def print_policy_list(policies:, modulepath:)
|
692
|
+
info = +''
|
693
|
+
|
694
|
+
info << colorize(:cyan, "Policies\n")
|
695
|
+
|
696
|
+
if policies.any?
|
697
|
+
policies.sort.each { |policy| info << indent(2, "#{policy}\n") }
|
698
|
+
else
|
699
|
+
info << indent(2, "No available policies\n")
|
700
|
+
end
|
701
|
+
|
702
|
+
info << "\n"
|
703
|
+
|
704
|
+
info << colorize(:cyan, "Modulepath\n")
|
705
|
+
info << indent(2, modulepath.join(File::PATH_SEPARATOR).to_s)
|
706
|
+
|
707
|
+
@stream.puts info.chomp
|
708
|
+
end
|
709
|
+
|
710
|
+
# Print target names and where they came from.
|
711
|
+
#
|
712
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
713
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
714
|
+
# @param flag [Boolean] Whether a targeting command-line option was used.
|
715
|
+
#
|
716
|
+
def print_targets(adhoc:, inventory:, flag:, **_kwargs)
|
717
|
+
adhoc_text = colorize(:yellow, "(Not found in inventory file)")
|
718
|
+
|
719
|
+
targets = []
|
720
|
+
targets += inventory[:targets].map { |target| [target['name'], nil] }
|
721
|
+
targets += adhoc[:targets].map { |target| [target['name'], adhoc_text] }
|
722
|
+
|
723
|
+
info = +''
|
724
|
+
|
725
|
+
# Add target list
|
726
|
+
info << colorize(:cyan, "Targets\n")
|
727
|
+
info << if targets.any?
|
728
|
+
format_table(targets, 2, 2).to_s
|
729
|
+
else
|
730
|
+
indent(2, 'No targets')
|
731
|
+
end
|
732
|
+
info << "\n\n"
|
733
|
+
|
734
|
+
info << format_inventory_source(inventory[:file], inventory[:default])
|
735
|
+
info << format_target_summary(inventory[:count], adhoc[:count], flag, false)
|
736
|
+
|
737
|
+
@stream.puts info
|
738
|
+
end
|
739
|
+
|
740
|
+
# Print detailed target information.
|
741
|
+
#
|
742
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
743
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
744
|
+
# @param flag [Boolean] Whether a targeting command-line option was used.
|
745
|
+
#
|
746
|
+
def print_target_info(adhoc:, inventory:, flag:, **_kwargs)
|
747
|
+
targets = (adhoc[:targets] + inventory[:targets]).sort_by { |t| t['name'] }
|
748
|
+
|
749
|
+
info = +''
|
750
|
+
|
751
|
+
if targets.any?
|
752
|
+
adhoc_text = colorize(:yellow, " (Not found in inventory file)")
|
753
|
+
|
754
|
+
targets.each do |target|
|
755
|
+
info << colorize(:cyan, target['name'])
|
756
|
+
info << adhoc_text if adhoc[:targets].include?(target)
|
757
|
+
info << "\n"
|
758
|
+
info << indent(2, target.to_yaml.lines.drop(1).join)
|
759
|
+
info << "\n"
|
760
|
+
end
|
761
|
+
else
|
762
|
+
info << colorize(:cyan, "Targets\n")
|
763
|
+
info << indent(2, "No targets\n\n")
|
764
|
+
end
|
765
|
+
|
766
|
+
info << format_inventory_source(inventory[:file], inventory[:default])
|
767
|
+
info << format_target_summary(inventory[:count], adhoc[:count], flag, true)
|
768
|
+
|
769
|
+
@stream.puts info
|
770
|
+
end
|
771
|
+
|
772
|
+
private def format_inventory_source(inventory_source, default_inventory)
|
773
|
+
info = +''
|
774
|
+
|
775
|
+
# Add inventory file source
|
776
|
+
info << colorize(:cyan, "Inventory source\n")
|
777
|
+
info << if inventory_source
|
778
|
+
indent(2, "#{inventory_source}\n")
|
779
|
+
else
|
780
|
+
indent(2, wrap("Tried to load inventory from #{default_inventory}, but the file does not exist\n"))
|
781
|
+
end
|
782
|
+
info << "\n"
|
783
|
+
end
|
784
|
+
|
785
|
+
private def format_target_summary(inventory_count, adhoc_count, target_flag, detail_flag)
|
786
|
+
info = +''
|
787
|
+
|
788
|
+
# Add target count summary
|
789
|
+
count = "#{inventory_count + adhoc_count} total, "\
|
790
|
+
"#{inventory_count} from inventory, "\
|
791
|
+
"#{adhoc_count} adhoc"
|
792
|
+
info << colorize(:cyan, "Target count\n")
|
793
|
+
info << indent(2, count)
|
794
|
+
|
795
|
+
# Add filtering information
|
796
|
+
unless target_flag && detail_flag
|
797
|
+
info << colorize(:cyan, "\n\nAdditional information\n")
|
798
|
+
|
799
|
+
unless target_flag
|
800
|
+
opt = Bolt::Util.windows? ? "'-Targets', '-Query', or '-Rerun'" : "'--targets', '--query', or '--rerun'"
|
801
|
+
info << indent(2, "Use the #{opt} option to view specific targets\n")
|
802
|
+
end
|
803
|
+
|
804
|
+
unless detail_flag
|
805
|
+
opt = Bolt::Util.windows? ? '-Detail' : '--detail'
|
806
|
+
info << indent(2, "Use the '#{opt}' option to view target configuration and data")
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
info
|
811
|
+
end
|
812
|
+
|
813
|
+
# Print inventory group information.
|
814
|
+
#
|
815
|
+
# @param count [Integer] Number of groups in the inventory.
|
816
|
+
# @param groups [Array] Names of groups in the inventory.
|
817
|
+
# @param inventory [Hash] Where the inventory was loaded from.
|
818
|
+
#
|
819
|
+
def print_groups(count:, groups:, inventory:)
|
820
|
+
info = +''
|
821
|
+
|
822
|
+
# Add group list
|
823
|
+
info << colorize(:cyan, "Groups\n")
|
824
|
+
info << indent(2, groups.join("\n"))
|
825
|
+
info << "\n\n"
|
826
|
+
|
827
|
+
# Add inventory file source
|
828
|
+
info << format_inventory_source(inventory[:source], inventory[:default])
|
829
|
+
|
830
|
+
# Add group count summary
|
831
|
+
info << colorize(:cyan, "Group count\n")
|
832
|
+
info << indent(2, "#{count} total")
|
833
|
+
|
834
|
+
@stream.puts info
|
835
|
+
end
|
836
|
+
|
837
|
+
# @param [Bolt::ResultSet] apply_result A ResultSet object representing the result of a `bolt apply`
|
838
|
+
def print_apply_result(apply_result)
|
839
|
+
print_summary(apply_result, apply_result.elapsed_time)
|
840
|
+
end
|
841
|
+
|
842
|
+
# @param [Bolt::PlanResult] plan_result A PlanResult object
|
843
|
+
def print_plan_result(plan_result)
|
844
|
+
value = plan_result.value
|
845
|
+
case value
|
846
|
+
when nil
|
847
|
+
@stream.puts("Plan completed successfully with no result")
|
848
|
+
when Bolt::ApplyFailure, Bolt::RunFailure
|
849
|
+
print_result_set(value.result_set)
|
850
|
+
when Bolt::ContainerResult
|
851
|
+
print_container_result(value)
|
852
|
+
when Bolt::ContainerFailure
|
853
|
+
print_container_result(value.result)
|
854
|
+
when Bolt::ResultSet
|
855
|
+
print_result_set(value)
|
856
|
+
when Bolt::Result
|
857
|
+
print_result(value)
|
858
|
+
when Bolt::ApplyResult
|
859
|
+
print_apply_result(value)
|
860
|
+
when Bolt::Error
|
861
|
+
print_bolt_error(**value.to_h.transform_keys(&:to_sym))
|
862
|
+
else
|
863
|
+
@stream.puts(::JSON.pretty_generate(plan_result, quirks_mode: true))
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
def print_result_set(result_set)
|
868
|
+
result_set.each { |result| print_result(result) }
|
869
|
+
print_summary(result_set)
|
870
|
+
end
|
871
|
+
|
872
|
+
def print_puppetfile_result(success, puppetfile, moduledir)
|
873
|
+
if success
|
874
|
+
@stream.puts("Successfully synced modules from #{puppetfile} to #{moduledir}")
|
875
|
+
else
|
876
|
+
@stream.puts(colorize(:red, "Failed to sync modules from #{puppetfile} to #{moduledir}"))
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
def fatal_error(err)
|
881
|
+
@stream.puts(colorize(:red, err.message))
|
882
|
+
if err.is_a? Bolt::RunFailure
|
883
|
+
@stream.puts ::JSON.pretty_generate(err.result_set)
|
884
|
+
end
|
885
|
+
|
886
|
+
if @trace && err.backtrace
|
887
|
+
err.backtrace.each do |line|
|
888
|
+
@stream.puts(colorize(:red, "\t#{line}"))
|
889
|
+
end
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
def print_message(message)
|
894
|
+
@stream.puts(Bolt::Util::Format.stringify(message))
|
895
|
+
end
|
896
|
+
|
897
|
+
def print_error(message)
|
898
|
+
@stream.puts(colorize(:red, message))
|
899
|
+
end
|
900
|
+
|
901
|
+
def print_bolt_error(msg:, details:, **_kwargs)
|
902
|
+
err = msg
|
903
|
+
if (f = details[:file])
|
904
|
+
err += "\n (file: #{f}"
|
905
|
+
err += ", line: #{details[:line]}" if details[:line]
|
906
|
+
err += ", column: #{details[:column]}" if details[:column]
|
907
|
+
err += ")"
|
908
|
+
end
|
909
|
+
@stream.puts(colorize(:red, err))
|
910
|
+
end
|
911
|
+
|
912
|
+
def print_prompt(prompt)
|
913
|
+
@stream.print(colorize(:cyan, indent(4, prompt)))
|
914
|
+
end
|
915
|
+
|
916
|
+
def print_prompt_error(message)
|
917
|
+
@stream.puts(colorize(:red, indent(4, message)))
|
918
|
+
end
|
919
|
+
|
920
|
+
def print_action_step(step)
|
921
|
+
first, *remaining = wrap(step, 76).lines
|
922
|
+
|
923
|
+
first = indent(2, "→ #{first}")
|
924
|
+
remaining = remaining.map { |line| indent(4, line) }
|
925
|
+
step = [first, *remaining, "\n"].join
|
926
|
+
|
927
|
+
@stream.puts(step)
|
928
|
+
end
|
929
|
+
|
930
|
+
def print_action_error(error)
|
931
|
+
# Running everything through 'wrap' messes with newlines. Separating
|
932
|
+
# into lines and wrapping each individually ensures separate errors are
|
933
|
+
# distinguishable.
|
934
|
+
first, *remaining = error.lines
|
935
|
+
first = colorize(:red, indent(2, "→ #{wrap(first, 76)}"))
|
936
|
+
wrapped = remaining.map { |l| wrap(l) }
|
937
|
+
to_print = wrapped.map { |line| colorize(:red, indent(4, line)) }
|
938
|
+
step = [first, *to_print, "\n"].join
|
939
|
+
|
940
|
+
@stream.puts(step)
|
941
|
+
end
|
942
|
+
|
943
|
+
def duration_to_string(duration)
|
944
|
+
hrs = (duration / 3600).floor
|
945
|
+
mins = ((duration % 3600) / 60).floor
|
946
|
+
secs = (duration % 60)
|
947
|
+
if hrs > 0
|
948
|
+
"#{hrs} hr, #{mins} min, #{secs.round} sec"
|
949
|
+
elsif mins > 0
|
950
|
+
"#{mins} min, #{secs.round} sec"
|
951
|
+
else
|
952
|
+
# Include 2 decimal places if the duration is under a minute
|
953
|
+
"#{secs.round(2)} sec"
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
end
|
958
|
+
end
|