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
data/lib/bolt/cli.rb
ADDED
@@ -0,0 +1,949 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Avoid requiring the CLI from other files. It has side-effects - such as loading r10k -
|
4
|
+
# that are undesirable when using Bolt as a library.
|
5
|
+
|
6
|
+
require 'uri'
|
7
|
+
require 'benchmark'
|
8
|
+
require 'json'
|
9
|
+
require 'io/console'
|
10
|
+
require 'logging'
|
11
|
+
require 'optparse'
|
12
|
+
require_relative '../bolt/analytics'
|
13
|
+
require_relative '../bolt/application'
|
14
|
+
require_relative '../bolt/bolt_option_parser'
|
15
|
+
require_relative '../bolt/config'
|
16
|
+
require_relative '../bolt/error'
|
17
|
+
require_relative '../bolt/executor'
|
18
|
+
require_relative '../bolt/inventory'
|
19
|
+
require_relative '../bolt/logger'
|
20
|
+
require_relative '../bolt/module_installer'
|
21
|
+
require_relative '../bolt/outputter'
|
22
|
+
require_relative '../bolt/pal'
|
23
|
+
require_relative '../bolt/plugin'
|
24
|
+
require_relative '../bolt/project_manager'
|
25
|
+
require_relative '../bolt/puppetdb'
|
26
|
+
require_relative '../bolt/rerun'
|
27
|
+
require_relative '../bolt/target'
|
28
|
+
require_relative '../bolt/version'
|
29
|
+
|
30
|
+
module Bolt
|
31
|
+
class CLIExit < StandardError; end
|
32
|
+
|
33
|
+
class CLI
|
34
|
+
attr_reader :outputter, :rerun
|
35
|
+
|
36
|
+
COMMANDS = {
|
37
|
+
'apply' => %w[],
|
38
|
+
'command' => %w[run],
|
39
|
+
'file' => %w[download upload],
|
40
|
+
'group' => %w[show],
|
41
|
+
'guide' => %w[],
|
42
|
+
'inventory' => %w[show],
|
43
|
+
'lookup' => %w[],
|
44
|
+
'module' => %w[add generate-types install show],
|
45
|
+
'plan' => %w[show run convert new],
|
46
|
+
'plugin' => %w[show],
|
47
|
+
'policy' => %w[apply new show],
|
48
|
+
'project' => %w[init migrate],
|
49
|
+
'script' => %w[run],
|
50
|
+
'secret' => %w[encrypt decrypt createkeys],
|
51
|
+
'task' => %w[show run]
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
TARGETING_OPTIONS = %i[query rerun targets].freeze
|
55
|
+
|
56
|
+
SUCCESS = 0
|
57
|
+
FAILURE = 1
|
58
|
+
|
59
|
+
def initialize(argv)
|
60
|
+
Bolt::Logger.initialize_logging
|
61
|
+
@logger = Bolt::Logger.logger(self)
|
62
|
+
@argv = argv
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO: Move this to the parser.
|
66
|
+
#
|
67
|
+
# Query whether the help text needs to be displayed.
|
68
|
+
#
|
69
|
+
# @param remaining [Array] Remaining arguments after parsing the command.
|
70
|
+
# @param options [Hash] The CLI options.
|
71
|
+
#
|
72
|
+
private def help?(options, remaining)
|
73
|
+
# Set the subcommand
|
74
|
+
options[:subcommand] = remaining.shift
|
75
|
+
|
76
|
+
if options[:subcommand] == 'help'
|
77
|
+
options[:help] = true
|
78
|
+
options[:subcommand] = remaining.shift
|
79
|
+
end
|
80
|
+
|
81
|
+
# This section handles parsing non-flag options which are
|
82
|
+
# subcommand specific rather then part of the config
|
83
|
+
actions = COMMANDS[options[:subcommand]]
|
84
|
+
if actions && !actions.empty?
|
85
|
+
options[:action] = remaining.shift
|
86
|
+
end
|
87
|
+
|
88
|
+
options[:help]
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO: Move most of this to the parser.
|
92
|
+
#
|
93
|
+
# Parse the command and validate options. All errors that are raised here
|
94
|
+
# are not handled by the outputter, as it relies on config being loaded.
|
95
|
+
#
|
96
|
+
def parse
|
97
|
+
with_error_handling do
|
98
|
+
options = {}
|
99
|
+
parser = BoltOptionParser.new(options)
|
100
|
+
|
101
|
+
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
102
|
+
remaining = parser.permute(@argv) unless @argv.empty?
|
103
|
+
|
104
|
+
if @argv.empty? || help?(options, remaining)
|
105
|
+
# If the subcommand is not enabled, display the default
|
106
|
+
# help text
|
107
|
+
options[:subcommand] = nil unless COMMANDS.include?(options[:subcommand])
|
108
|
+
|
109
|
+
if Bolt::Util.first_run?
|
110
|
+
FileUtils.touch(Bolt::Util.first_runs_free)
|
111
|
+
|
112
|
+
if options[:subcommand].nil? && $stdout.isatty
|
113
|
+
welcome_message
|
114
|
+
raise Bolt::CLIExit
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Update the parser for the subcommand (or lack thereof)
|
119
|
+
parser.update
|
120
|
+
puts parser.help
|
121
|
+
raise Bolt::CLIExit
|
122
|
+
end
|
123
|
+
|
124
|
+
if options[:version]
|
125
|
+
puts Bolt::VERSION
|
126
|
+
raise Bolt::CLIExit
|
127
|
+
end
|
128
|
+
|
129
|
+
options[:object] = remaining.shift
|
130
|
+
|
131
|
+
# Handle reading a command from a file
|
132
|
+
if options[:subcommand] == 'command' && options[:object]
|
133
|
+
options[:object] = Bolt::Util.get_arg_input(options[:object])
|
134
|
+
end
|
135
|
+
|
136
|
+
# Only parse params for task or plan
|
137
|
+
if %w[task plan].include?(options[:subcommand])
|
138
|
+
params, remaining = remaining.partition { |s| s =~ /.+=/ }
|
139
|
+
if options[:params]
|
140
|
+
unless params.empty?
|
141
|
+
raise Bolt::CLIError,
|
142
|
+
"Parameters must be specified through either the --params " \
|
143
|
+
"option or param=value pairs, not both"
|
144
|
+
end
|
145
|
+
options[:params_parsed] = true
|
146
|
+
elsif params.any?
|
147
|
+
options[:params_parsed] = false
|
148
|
+
options[:params] = Hash[params.map { |a| a.split('=', 2) }]
|
149
|
+
else
|
150
|
+
options[:params_parsed] = true
|
151
|
+
options[:params] = {}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
options[:leftovers] = remaining
|
155
|
+
|
156
|
+
# Default to verbose for everything except plans
|
157
|
+
unless options.key?(:verbose)
|
158
|
+
options[:verbose] = options[:subcommand] != 'plan'
|
159
|
+
end
|
160
|
+
|
161
|
+
validate(options)
|
162
|
+
validate_ps_version
|
163
|
+
|
164
|
+
options
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# TODO: Move this to the parser.
|
169
|
+
#
|
170
|
+
# Print a welcome message when users first install Bolt and run `bolt`,
|
171
|
+
# `bolt help` or `bolt --help`.
|
172
|
+
#
|
173
|
+
private def welcome_message
|
174
|
+
bolt = <<~BOLT
|
175
|
+
`.::-`
|
176
|
+
`.-:///////-.`
|
177
|
+
`-:////:. `-:///:- /ooo. .ooo/
|
178
|
+
`.-:///::///:-` `-//: ymmm- :mmmy .---.
|
179
|
+
:///:-. `.:////. -//: ymmm- :mmmy +mmm+
|
180
|
+
://. ///. -//: ymmm--/++/- `-/++/:` :mmmy-:smmms::-
|
181
|
+
://. ://. .://: ymmmdmmmmmmdo` .smmmmmmmmh: :mmmysmmmmmmmms
|
182
|
+
://. ://:///:-. ymmmh/--/hmmmy -mmmd/-.:hmmm+:mmmy.-smmms--.
|
183
|
+
://:.` .-////:-` ymmm- ymmm:hmmm- `dmmm/mmmy +mmm+
|
184
|
+
`-:///:-..:///:-.` ymmm- ommm/dmmm` hmmm+mmmy +mmm+
|
185
|
+
`.-:////:-` ymmm+ /mmmm.ommms` /mmmh:mmmy +mmmo
|
186
|
+
`-.` ymmmmmhhmmmmd: ommmmhydmmmy`:mmmy -mmmmdhd
|
187
|
+
oyyy+shddhs/` .+shddhy+- -yyyo .ohddhs
|
188
|
+
|
189
|
+
|
190
|
+
BOLT
|
191
|
+
example_cmd = if Bolt::Util.windows?
|
192
|
+
"Invoke-BoltCommand -Command 'hostname' -Targets localhost"
|
193
|
+
else
|
194
|
+
"bolt command run 'hostname' --target localhost"
|
195
|
+
end
|
196
|
+
prev_cmd = String.new("bolt")
|
197
|
+
prev_cmd << " #{@argv[0]}" unless @argv.empty?
|
198
|
+
|
199
|
+
message = <<~MSG
|
200
|
+
🎉 Welcome to Bolt #{VERSION}
|
201
|
+
😌 We're here to help bring order to the chaos
|
202
|
+
📖 Find our documentation at https://bolt.guide
|
203
|
+
🙋 Ask a question in #bolt on https://slack.puppet.com/
|
204
|
+
🔩 Contribute at https://github.com/puppetlabs/bolt/
|
205
|
+
💡 Not sure where to start? Try "#{example_cmd}"
|
206
|
+
|
207
|
+
We only print this message once. Run "#{prev_cmd}" again for help text.
|
208
|
+
MSG
|
209
|
+
|
210
|
+
$stdout.print "\033[36m#{bolt}\033[0m"
|
211
|
+
$stdout.print message
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO: Move this to the parser.
|
215
|
+
#
|
216
|
+
# Validate the command. Ensure that the subcommand and action are
|
217
|
+
# recognized, all required arguments are specified, and only supported
|
218
|
+
# command-line options are used.
|
219
|
+
#
|
220
|
+
# @param options [Hash] The CLI options.
|
221
|
+
#
|
222
|
+
private def validate(options)
|
223
|
+
unless COMMANDS.include?(options[:subcommand])
|
224
|
+
command = Bolt::Util.powershell? ? 'Get-Command -Module PuppetBolt' : 'bolt help'
|
225
|
+
raise Bolt::CLIError,
|
226
|
+
"'#{options[:subcommand]}' is not a Bolt command. See '#{command}'."
|
227
|
+
end
|
228
|
+
|
229
|
+
actions = COMMANDS[options[:subcommand]]
|
230
|
+
if actions.any?
|
231
|
+
if options[:action].nil?
|
232
|
+
raise Bolt::CLIError,
|
233
|
+
"Expected an action of the form 'bolt #{options[:subcommand]} <action>'"
|
234
|
+
end
|
235
|
+
|
236
|
+
unless actions.include?(options[:action])
|
237
|
+
raise Bolt::CLIError,
|
238
|
+
"Expected action '#{options[:action]}' to be one of " \
|
239
|
+
"#{actions.join(', ')}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
if %w[task plan script].include?(options[:subcommand]) && options[:action] == 'run'
|
244
|
+
if options[:object].nil?
|
245
|
+
raise Bolt::CLIError, "Must specify a #{options[:subcommand]} to run"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# This may mean that we parsed a parameter as the object
|
250
|
+
if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
|
251
|
+
unless options[:object] =~ /\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/
|
252
|
+
raise Bolt::CLIError,
|
253
|
+
"Invalid #{options[:subcommand]} '#{options[:object]}'"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
if options[:subcommand] == 'apply' && (options[:object] && options[:code])
|
258
|
+
raise Bolt::CLIError, "--execute is unsupported when specifying a manifest file"
|
259
|
+
end
|
260
|
+
|
261
|
+
if options[:subcommand] == 'apply' && (!options[:object] && !options[:code])
|
262
|
+
raise Bolt::CLIError, "a manifest file or --execute is required"
|
263
|
+
end
|
264
|
+
|
265
|
+
if options[:subcommand] == 'lookup' && !options[:object]
|
266
|
+
raise Bolt::CLIError, "Must specify a key to look up"
|
267
|
+
end
|
268
|
+
|
269
|
+
if options[:subcommand] == 'command' && (!options[:object] || options[:object].empty?)
|
270
|
+
raise Bolt::CLIError, "Must specify a command to run"
|
271
|
+
end
|
272
|
+
|
273
|
+
if options[:subcommand] == 'secret' &&
|
274
|
+
(options[:action] == 'decrypt' || options[:action] == 'encrypt') &&
|
275
|
+
!options[:object]
|
276
|
+
raise Bolt::CLIError, "Must specify a value to #{options[:action]}"
|
277
|
+
end
|
278
|
+
|
279
|
+
if options[:subcommand] == 'plan' && options[:action] == 'new' && !options[:object]
|
280
|
+
raise Bolt::CLIError, "Must specify a plan name."
|
281
|
+
end
|
282
|
+
|
283
|
+
if options[:subcommand] == 'module' && options[:action] == 'add' && !options[:object]
|
284
|
+
raise Bolt::CLIError, "Must specify a module name."
|
285
|
+
end
|
286
|
+
|
287
|
+
if options[:action] == 'convert' && !options[:object]
|
288
|
+
raise Bolt::CLIError, "Must specify a plan."
|
289
|
+
end
|
290
|
+
|
291
|
+
if options[:subcommand] == 'policy'
|
292
|
+
if options[:action] == 'apply' && !options[:object]
|
293
|
+
raise Bolt::CLIError, "Must specify one or more policies to apply."
|
294
|
+
end
|
295
|
+
|
296
|
+
if options[:action] == 'apply' && options[:leftovers].any?
|
297
|
+
raise Bolt::CLIError, "Unknown argument(s) #{options[:leftovers].join(', ')}. "\
|
298
|
+
"To apply multiple policies, provide a comma-separated list of "\
|
299
|
+
"policy names."
|
300
|
+
end
|
301
|
+
|
302
|
+
if options[:action] == 'new' && !options[:object]
|
303
|
+
raise Bolt::CLIError, "Must specify a name for the new policy."
|
304
|
+
end
|
305
|
+
|
306
|
+
if options[:action] == 'show' && options[:object]
|
307
|
+
raise Bolt::CLIError, "Unknown argument #{options[:object]}."
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
|
312
|
+
command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
|
313
|
+
raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
|
314
|
+
"the project, run '#{command} #{options[:object]}'."
|
315
|
+
end
|
316
|
+
|
317
|
+
if %w[download upload].include?(options[:action])
|
318
|
+
raise Bolt::CLIError, "Must specify a source" unless options[:object]
|
319
|
+
|
320
|
+
if options[:leftovers].empty?
|
321
|
+
raise Bolt::CLIError, "Must specify a destination"
|
322
|
+
elsif options[:leftovers].size > 1
|
323
|
+
raise Bolt::CLIError, "Unknown arguments #{options[:leftovers].drop(1).join(', ')}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
if options[:subcommand] == 'group' && options[:object]
|
328
|
+
raise Bolt::CLIError, "Unknown argument #{options[:object]}"
|
329
|
+
end
|
330
|
+
|
331
|
+
if options[:action] == 'generate-types' && options[:object]
|
332
|
+
raise Bolt::CLIError, "Unknown argument #{options[:object]}"
|
333
|
+
end
|
334
|
+
|
335
|
+
if !%w[file script lookup].include?(options[:subcommand]) &&
|
336
|
+
!options[:leftovers].empty?
|
337
|
+
raise Bolt::CLIError,
|
338
|
+
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
339
|
+
end
|
340
|
+
|
341
|
+
target_opts = options.keys.select { |opt| TARGETING_OPTIONS.include?(opt) }
|
342
|
+
if options[:subcommand] == 'lookup' &&
|
343
|
+
target_opts.any? && options[:plan_hierarchy]
|
344
|
+
raise Bolt::CLIError, "The 'lookup' command accepts either targeting option OR --plan-hierarchy."
|
345
|
+
end
|
346
|
+
|
347
|
+
if options[:noop] &&
|
348
|
+
!(options[:subcommand] == 'task' && options[:action] == 'run') &&
|
349
|
+
options[:subcommand] != 'apply' &&
|
350
|
+
options[:action] != 'apply'
|
351
|
+
raise Bolt::CLIError,
|
352
|
+
"Option '--noop' can only be specified when running a task or applying manifest code"
|
353
|
+
end
|
354
|
+
|
355
|
+
if options[:env_vars]
|
356
|
+
unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
|
357
|
+
raise Bolt::CLIError,
|
358
|
+
"Option '--env-var' can only be specified when running a command or script"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
validate_targeting_options(options)
|
363
|
+
end
|
364
|
+
|
365
|
+
# Validates that only one targeting option is provided and that commands
|
366
|
+
# requiring a targeting option received one.
|
367
|
+
#
|
368
|
+
# @param options [Hash] The CLI options.
|
369
|
+
#
|
370
|
+
private def validate_targeting_options(options)
|
371
|
+
target_opts = options.slice(*TARGETING_OPTIONS)
|
372
|
+
target_string = "'--targets', '--rerun', or '--query'"
|
373
|
+
|
374
|
+
if target_opts.length > 1
|
375
|
+
raise Bolt::CLIError, "Only one targeting option can be specified: #{target_string}"
|
376
|
+
end
|
377
|
+
|
378
|
+
return if %w[guide module plan project secret].include?(options[:subcommand]) ||
|
379
|
+
%w[convert new show].include?(options[:action]) ||
|
380
|
+
options[:plan_hierarchy]
|
381
|
+
|
382
|
+
if target_opts.empty?
|
383
|
+
raise Bolt::CLIError, "Command requires a targeting option: #{target_string}"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Execute a Bolt command. The +options+ hash includes the subcommand and
|
388
|
+
# action to be run, as well as any additional arguments and options for the
|
389
|
+
# command.
|
390
|
+
#
|
391
|
+
# @param options [Hash] The CLI options.
|
392
|
+
#
|
393
|
+
def execute(options)
|
394
|
+
with_signal_handling do
|
395
|
+
with_error_handling do
|
396
|
+
# TODO: Separate from options hash and pass as own args.
|
397
|
+
command = options[:subcommand]
|
398
|
+
action = options[:action]
|
399
|
+
|
400
|
+
#
|
401
|
+
# INITIALIZE CORE CLASSES
|
402
|
+
#
|
403
|
+
|
404
|
+
project = if ENV['BOLT_PROJECT']
|
405
|
+
Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
406
|
+
elsif options[:project]
|
407
|
+
dir = Pathname.new(options[:project])
|
408
|
+
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
409
|
+
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
410
|
+
else
|
411
|
+
Bolt::Project.create_project(dir)
|
412
|
+
end
|
413
|
+
else
|
414
|
+
Bolt::Project.find_boltdir(Dir.pwd)
|
415
|
+
end
|
416
|
+
|
417
|
+
config = Bolt::Config.from_project(project, options)
|
418
|
+
|
419
|
+
@outputter = Bolt::Outputter.for_format(
|
420
|
+
config.format,
|
421
|
+
config.color,
|
422
|
+
options[:verbose],
|
423
|
+
config.trace,
|
424
|
+
config.spinner
|
425
|
+
)
|
426
|
+
|
427
|
+
@rerun = Bolt::Rerun.new(config.rerunfile, config.save_rerun)
|
428
|
+
|
429
|
+
# TODO: Subscribe this to the executor.
|
430
|
+
analytics = begin
|
431
|
+
client = Bolt::Analytics.build_client(config.analytics)
|
432
|
+
client.bundled_content = bundled_content(options)
|
433
|
+
client
|
434
|
+
end
|
435
|
+
|
436
|
+
Bolt::Logger.configure(config.log, config.color, config.disable_warnings)
|
437
|
+
Bolt::Logger.stream = config.stream
|
438
|
+
Bolt::Logger.analytics = analytics
|
439
|
+
Bolt::Logger.flush_queue
|
440
|
+
|
441
|
+
executor = Bolt::Executor.new(
|
442
|
+
config.concurrency,
|
443
|
+
analytics,
|
444
|
+
options[:noop],
|
445
|
+
config.modified_concurrency,
|
446
|
+
config.future
|
447
|
+
)
|
448
|
+
|
449
|
+
pal = Bolt::PAL.new(
|
450
|
+
Bolt::Config::Modulepath.new(config.modulepath),
|
451
|
+
config.hiera_config,
|
452
|
+
config.project.resource_types,
|
453
|
+
config.compile_concurrency,
|
454
|
+
config.trusted_external,
|
455
|
+
config.apply_settings,
|
456
|
+
config.project
|
457
|
+
)
|
458
|
+
|
459
|
+
plugins = Bolt::Plugin.new(config, pal, analytics)
|
460
|
+
|
461
|
+
inventory = Bolt::Inventory.from_config(config, plugins)
|
462
|
+
|
463
|
+
log_outputter = Bolt::Outputter::Logger.new(options[:verbose], config.trace)
|
464
|
+
|
465
|
+
#
|
466
|
+
# FINALIZING SETUP
|
467
|
+
#
|
468
|
+
|
469
|
+
check_gem_install
|
470
|
+
warn_inventory_overrides_cli(config, options)
|
471
|
+
submit_screen_view(analytics, config, inventory, options)
|
472
|
+
options[:targets] = process_target_list(plugins, @rerun, options)
|
473
|
+
|
474
|
+
# TODO: Fix casing issue in Windows.
|
475
|
+
config.check_path_case('modulepath', config.modulepath)
|
476
|
+
|
477
|
+
if options[:clear_cache]
|
478
|
+
FileUtils.rm(config.project.plugin_cache_file) if File.exist?(config.project.plugin_cache_file)
|
479
|
+
FileUtils.rm(config.project.task_cache_file) if File.exist?(config.project.task_cache_file)
|
480
|
+
FileUtils.rm(config.project.plan_cache_file) if File.exist?(config.project.plan_cache_file)
|
481
|
+
end
|
482
|
+
|
483
|
+
case command
|
484
|
+
when 'apply', 'lookup'
|
485
|
+
if %w[human rainbow].include?(config.format)
|
486
|
+
executor.subscribe(outputter)
|
487
|
+
end
|
488
|
+
when 'plan'
|
489
|
+
if %w[human rainbow].include?(config.format)
|
490
|
+
executor.subscribe(outputter)
|
491
|
+
else
|
492
|
+
executor.subscribe(outputter, %i[message verbose])
|
493
|
+
end
|
494
|
+
else
|
495
|
+
executor.subscribe(outputter)
|
496
|
+
end
|
497
|
+
|
498
|
+
executor.subscribe(log_outputter)
|
499
|
+
|
500
|
+
# TODO: Figure out where this should really go. It doesn't seem to
|
501
|
+
# make sense in the application, since the params should already
|
502
|
+
# be data when they reach that point.
|
503
|
+
if %w[plan task].include?(command) && action == 'run'
|
504
|
+
options[:params] = parse_params(
|
505
|
+
command,
|
506
|
+
options[:object],
|
507
|
+
pal,
|
508
|
+
**options.slice(:params, :params_parsed)
|
509
|
+
)
|
510
|
+
end
|
511
|
+
|
512
|
+
application = Bolt::Application.new(
|
513
|
+
analytics: analytics,
|
514
|
+
config: config,
|
515
|
+
executor: executor,
|
516
|
+
inventory: inventory,
|
517
|
+
pal: pal,
|
518
|
+
plugins: plugins
|
519
|
+
)
|
520
|
+
|
521
|
+
process_command(application, command, action, options)
|
522
|
+
ensure
|
523
|
+
analytics&.finish
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
# Process the command.
|
529
|
+
#
|
530
|
+
# @param app [Bolt::Application] The application.
|
531
|
+
# @param command [String] The command.
|
532
|
+
# @param action [String, NilClass] The action.
|
533
|
+
# @param options [Hash] The CLI options.
|
534
|
+
#
|
535
|
+
private def process_command(app, command, action, options)
|
536
|
+
case command
|
537
|
+
when 'apply'
|
538
|
+
results = outputter.spin do
|
539
|
+
app.apply(options[:object], options[:targets], **options.slice(:code, :noop))
|
540
|
+
end
|
541
|
+
rerun.update(results)
|
542
|
+
app.shutdown
|
543
|
+
outputter.print_apply_result(results)
|
544
|
+
results.ok? ? SUCCESS : FAILURE
|
545
|
+
|
546
|
+
when 'command'
|
547
|
+
outputter.print_head
|
548
|
+
results = app.run_command(options[:object], options[:targets], **options.slice(:env_vars))
|
549
|
+
rerun.update(results)
|
550
|
+
app.shutdown
|
551
|
+
outputter.print_summary(results, results.elapsed_time)
|
552
|
+
results.ok? ? SUCCESS : FAILURE
|
553
|
+
|
554
|
+
when 'file'
|
555
|
+
case action
|
556
|
+
when 'download'
|
557
|
+
outputter.print_head
|
558
|
+
results = app.download_file(options[:object], options[:leftovers].first, options[:targets])
|
559
|
+
rerun.update(results)
|
560
|
+
app.shutdown
|
561
|
+
outputter.print_summary(results, results.elapsed_time)
|
562
|
+
results.ok? ? SUCCESS : FAILURE
|
563
|
+
when 'upload'
|
564
|
+
outputter.print_head
|
565
|
+
results = app.upload_file(options[:object], options[:leftovers].first, options[:targets])
|
566
|
+
rerun.update(results)
|
567
|
+
app.shutdown
|
568
|
+
outputter.print_summary(results, results.elapsed_time)
|
569
|
+
results.ok? ? SUCCESS : FAILURE
|
570
|
+
end
|
571
|
+
|
572
|
+
when 'group'
|
573
|
+
outputter.print_groups(**app.list_groups)
|
574
|
+
SUCCESS
|
575
|
+
|
576
|
+
when 'guide'
|
577
|
+
if options[:object]
|
578
|
+
outputter.print_guide(**app.show_guide(options[:object]))
|
579
|
+
else
|
580
|
+
outputter.print_topics(**app.list_guides)
|
581
|
+
end
|
582
|
+
SUCCESS
|
583
|
+
|
584
|
+
when 'inventory'
|
585
|
+
targets = app.show_inventory(options[:targets])
|
586
|
+
.merge(flag: !options[:targets].nil?)
|
587
|
+
if options[:detail]
|
588
|
+
outputter.print_target_info(**targets)
|
589
|
+
else
|
590
|
+
outputter.print_targets(**targets)
|
591
|
+
end
|
592
|
+
SUCCESS
|
593
|
+
|
594
|
+
when 'lookup'
|
595
|
+
options[:vars] = parse_vars(options[:leftovers])
|
596
|
+
if options[:plan_hierarchy]
|
597
|
+
outputter.print_plan_lookup(app.plan_lookup(options[:object], **options.slice(:vars)))
|
598
|
+
SUCCESS
|
599
|
+
else
|
600
|
+
results = outputter.spin do
|
601
|
+
app.lookup(options[:object], options[:targets], **options.slice(:vars))
|
602
|
+
end
|
603
|
+
rerun.update(results)
|
604
|
+
app.shutdown
|
605
|
+
outputter.print_result_set(results)
|
606
|
+
results.ok? ? SUCCESS : FAILURE
|
607
|
+
end
|
608
|
+
|
609
|
+
when 'module'
|
610
|
+
case action
|
611
|
+
when 'add'
|
612
|
+
ok = outputter.spin { app.add_module(options[:object], outputter) }
|
613
|
+
ok ? SUCCESS : FAILURE
|
614
|
+
when 'generate-types'
|
615
|
+
app.generate_types
|
616
|
+
SUCCESS
|
617
|
+
when 'install'
|
618
|
+
ok = outputter.spin { app.install_modules(outputter, **options.slice(:force, :resolve)) }
|
619
|
+
ok ? SUCCESS : FAILURE
|
620
|
+
when 'show'
|
621
|
+
if options[:object]
|
622
|
+
outputter.print_module_info(**app.show_module(options[:object]))
|
623
|
+
else
|
624
|
+
outputter.print_module_list(app.list_modules)
|
625
|
+
end
|
626
|
+
SUCCESS
|
627
|
+
end
|
628
|
+
|
629
|
+
when 'plan'
|
630
|
+
case action
|
631
|
+
when 'convert'
|
632
|
+
app.convert_plan(options[:object])
|
633
|
+
SUCCESS
|
634
|
+
when 'new'
|
635
|
+
result = app.new_plan(options[:object], **options.slice(:puppet, :plan_script))
|
636
|
+
outputter.print_new_plan(**result)
|
637
|
+
SUCCESS
|
638
|
+
when 'run'
|
639
|
+
result = app.run_plan(options[:object], options[:targets], **options.slice(:params))
|
640
|
+
rerun.update(result)
|
641
|
+
app.shutdown
|
642
|
+
outputter.print_plan_result(result)
|
643
|
+
result.ok? ? SUCCESS : FAILURE
|
644
|
+
when 'show'
|
645
|
+
if options[:object]
|
646
|
+
outputter.print_plan_info(app.show_plan(options[:object]))
|
647
|
+
else
|
648
|
+
outputter.print_plans(**app.list_plans(**options.slice(:filter)))
|
649
|
+
end
|
650
|
+
SUCCESS
|
651
|
+
end
|
652
|
+
|
653
|
+
when 'plugin'
|
654
|
+
outputter.print_plugin_list(**app.list_plugins)
|
655
|
+
SUCCESS
|
656
|
+
|
657
|
+
when 'policy'
|
658
|
+
Bolt::Logger.warn('policy_command', 'This command is experimental and is subject to change.')
|
659
|
+
case action
|
660
|
+
when 'apply'
|
661
|
+
results = outputter.spin do
|
662
|
+
app.apply_policies(options[:object], options[:targets], **options.slice(:noop))
|
663
|
+
end
|
664
|
+
rerun.update(results)
|
665
|
+
app.shutdown
|
666
|
+
outputter.print_apply_result(results)
|
667
|
+
results.ok? ? SUCCESS : FAILURE
|
668
|
+
when 'new'
|
669
|
+
result = app.new_policy(options[:object])
|
670
|
+
outputter.print_new_policy(**result)
|
671
|
+
SUCCESS
|
672
|
+
when 'show'
|
673
|
+
outputter.print_policy_list(**app.list_policies)
|
674
|
+
SUCCESS
|
675
|
+
end
|
676
|
+
|
677
|
+
when 'project'
|
678
|
+
case action
|
679
|
+
when 'init'
|
680
|
+
app.create_project(options[:object], outputter, **options.slice(:modules))
|
681
|
+
SUCCESS
|
682
|
+
when 'migrate'
|
683
|
+
app.migrate_project(outputter)
|
684
|
+
SUCCESS
|
685
|
+
end
|
686
|
+
|
687
|
+
when 'script'
|
688
|
+
outputter.print_head
|
689
|
+
opts = options.slice(:env_vars).merge(arguments: options[:leftovers])
|
690
|
+
results = app.run_script(options[:object], options[:targets], **opts)
|
691
|
+
rerun.update(results)
|
692
|
+
app.shutdown
|
693
|
+
outputter.print_summary(results, results.elapsed_time)
|
694
|
+
results.ok? ? SUCCESS : FAILURE
|
695
|
+
|
696
|
+
when 'secret'
|
697
|
+
case action
|
698
|
+
when 'createkeys'
|
699
|
+
result = app.create_secret_keys(**options.slice(:force, :plugin))
|
700
|
+
outputter.print_message(result)
|
701
|
+
SUCCESS
|
702
|
+
when 'decrypt'
|
703
|
+
result = app.decrypt_secret(options[:object], **options.slice(:plugin))
|
704
|
+
outputter.print_message(result)
|
705
|
+
SUCCESS
|
706
|
+
when 'encrypt'
|
707
|
+
result = app.encrypt_secret(options[:object], **options.slice(:plugin))
|
708
|
+
outputter.print_message(result)
|
709
|
+
SUCCESS
|
710
|
+
end
|
711
|
+
|
712
|
+
when 'task'
|
713
|
+
case action
|
714
|
+
when 'run'
|
715
|
+
outputter.print_head
|
716
|
+
results = app.run_task(options[:object], options[:targets], **options.slice(:params))
|
717
|
+
rerun.update(results)
|
718
|
+
app.shutdown
|
719
|
+
outputter.print_summary(results, results.elapsed_time)
|
720
|
+
results.ok? ? SUCCESS : FAILURE
|
721
|
+
when 'show'
|
722
|
+
if options[:object]
|
723
|
+
outputter.print_task_info(**app.show_task(options[:object]))
|
724
|
+
else
|
725
|
+
outputter.print_tasks(**app.list_tasks(**options.slice(:filter)))
|
726
|
+
end
|
727
|
+
SUCCESS
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
|
732
|
+
# Process the target list by turning a PuppetDB query or rerun mode into a
|
733
|
+
# list of target names.
|
734
|
+
#
|
735
|
+
# @param plugins [Bolt::Plugin] The Plugin instance.
|
736
|
+
# @param rerun [Bolt::Rerun] The Rerun instance.
|
737
|
+
# @param options [Hash] The CLI options.
|
738
|
+
# @return [Hash] The target list.
|
739
|
+
#
|
740
|
+
private def process_target_list(plugins, rerun, options)
|
741
|
+
if options[:query]
|
742
|
+
plugins.puppetdb_client.query_certnames(options[:query])
|
743
|
+
elsif options[:rerun]
|
744
|
+
rerun.get_targets(options[:rerun])
|
745
|
+
elsif options[:targets]
|
746
|
+
options[:targets]
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
750
|
+
# List content that ships with Bolt.
|
751
|
+
#
|
752
|
+
# @param options [Hash] The CLI options.
|
753
|
+
#
|
754
|
+
private def bundled_content(options)
|
755
|
+
# We only need to enumerate bundled content when running a task or plan
|
756
|
+
content = { 'Plan' => [],
|
757
|
+
'Task' => [],
|
758
|
+
'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
|
759
|
+
if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
|
760
|
+
default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
|
761
|
+
content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
|
762
|
+
col << iter&.first
|
763
|
+
end
|
764
|
+
content['Task'] = default_content.list_tasks.each_with_object([]) do |iter, col|
|
765
|
+
col << iter&.first
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
content
|
770
|
+
end
|
771
|
+
|
772
|
+
# Check and warn if Bolt is installed as a gem.
|
773
|
+
#
|
774
|
+
private def check_gem_install
|
775
|
+
if ENV['BOLT_GEM'].nil? && incomplete_install?
|
776
|
+
msg = <<~MSG.chomp
|
777
|
+
Bolt might be installed as a gem. To use Bolt reliably and with all of its
|
778
|
+
dependencies, uninstall the 'bolt' gem and install Bolt as a package:
|
779
|
+
https://puppet.com/docs/bolt/latest/bolt_installing.html
|
780
|
+
|
781
|
+
If you meant to install Bolt as a gem and want to disable this warning,
|
782
|
+
set the BOLT_GEM environment variable.
|
783
|
+
MSG
|
784
|
+
|
785
|
+
Bolt::Logger.warn("gem_install", msg)
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
# Print a fatal error. Print using the outputter if it's configured.
|
790
|
+
# Otherwise, mock the output by printing directly to stdout.
|
791
|
+
#
|
792
|
+
# @param error [StandardError] The error to print.
|
793
|
+
#
|
794
|
+
private def fatal_error(error)
|
795
|
+
if @outputter
|
796
|
+
@outputter.fatal_error(error)
|
797
|
+
elsif $stdout.isatty
|
798
|
+
$stdout.puts("\033[31m#{error.message}\033[0m")
|
799
|
+
else
|
800
|
+
$stdout.puts(error.message)
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
# Query whether Bolt is installed as a gem or package by checking if all
|
805
|
+
# built-in modules are installed.
|
806
|
+
#
|
807
|
+
private def incomplete_install?
|
808
|
+
builtin_module_list = %w[aggregate canary puppetdb_fact secure_env_vars puppet_connect]
|
809
|
+
(Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - builtin_module_list).empty?
|
810
|
+
end
|
811
|
+
|
812
|
+
# Parse parameters for tasks and plans.
|
813
|
+
#
|
814
|
+
# @param options [Hash] Options from the calling method.
|
815
|
+
#
|
816
|
+
private def parse_params(command, object, pal, params: nil, params_parsed: nil)
|
817
|
+
if params
|
818
|
+
params_parsed ? params : pal.parse_params(command, object, params)
|
819
|
+
else
|
820
|
+
{}
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
# Parse variables for lookups.
|
825
|
+
#
|
826
|
+
# @param vars [Array, NilClass] Unparsed variables.
|
827
|
+
#
|
828
|
+
private def parse_vars(vars)
|
829
|
+
return unless vars
|
830
|
+
Hash[vars.map { |a| a.split('=', 2) }]
|
831
|
+
end
|
832
|
+
|
833
|
+
# TODO: See if this can be moved to Bolt::Analytics.
|
834
|
+
#
|
835
|
+
# Submit a screen view to the analytics client.
|
836
|
+
#
|
837
|
+
# @param analytics [Bolt::Analytics] The analytics client.
|
838
|
+
# @param config [Bolt::Config] The config.
|
839
|
+
# @param inventory [Bolt::Inventory] The inventory.
|
840
|
+
# @param options [Hash] The CLI options.
|
841
|
+
#
|
842
|
+
private def submit_screen_view(analytics, config, inventory, options)
|
843
|
+
screen = "#{options[:subcommand]}_#{options[:action]}"
|
844
|
+
|
845
|
+
if options[:action] == 'show' && options[:object]
|
846
|
+
screen += '_object'
|
847
|
+
end
|
848
|
+
|
849
|
+
pp_count, yaml_count = if File.exist?(config.project.plans_path)
|
850
|
+
%w[pp yaml].map do |extension|
|
851
|
+
Find.find(config.project.plans_path.to_s)
|
852
|
+
.grep(/.*\.#{extension}/)
|
853
|
+
.length
|
854
|
+
end
|
855
|
+
else
|
856
|
+
[0, 0]
|
857
|
+
end
|
858
|
+
|
859
|
+
screen_view_fields = {
|
860
|
+
output_format: config.format,
|
861
|
+
boltdir_type: config.project.type,
|
862
|
+
puppet_plan_count: pp_count,
|
863
|
+
yaml_plan_count: yaml_count
|
864
|
+
}
|
865
|
+
|
866
|
+
if options.key?(:targets)
|
867
|
+
screen_view_fields.merge!(
|
868
|
+
target_nodes: options[:targets].count,
|
869
|
+
inventory_nodes: inventory.node_names.count,
|
870
|
+
inventory_groups: inventory.group_names.count,
|
871
|
+
inventory_version: inventory.version
|
872
|
+
)
|
873
|
+
end
|
874
|
+
|
875
|
+
analytics.screen_view(screen, **screen_view_fields)
|
876
|
+
end
|
877
|
+
|
878
|
+
# Issue a deprecation warning if the user is running an unsupported version
|
879
|
+
# of PowerShell on the controller.
|
880
|
+
#
|
881
|
+
private def validate_ps_version
|
882
|
+
if Bolt::Util.powershell?
|
883
|
+
command = "powershell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy "\
|
884
|
+
"Bypass -Command $PSVersionTable.PSVersion.Major"
|
885
|
+
stdout, _stderr, _status = Open3.capture3(command)
|
886
|
+
|
887
|
+
return unless !stdout.empty? && stdout.to_i < 3
|
888
|
+
|
889
|
+
msg = "Detected PowerShell 2 on controller. PowerShell 2 is unsupported."
|
890
|
+
Bolt::Logger.deprecation_warning("powershell_2_controller", msg)
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Warn the user that transport configuration options set from the command
|
895
|
+
# line may be overridden by transport configuration set in the inventory.
|
896
|
+
#
|
897
|
+
# @param opts [Hash] The CLI options.
|
898
|
+
#
|
899
|
+
private def warn_inventory_overrides_cli(config, opts)
|
900
|
+
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
901
|
+
Bolt::Inventory::ENVIRONMENT_VAR
|
902
|
+
elsif config.inventoryfile
|
903
|
+
config.inventoryfile
|
904
|
+
elsif File.exist?(config.default_inventoryfile)
|
905
|
+
config.default_inventoryfile
|
906
|
+
end
|
907
|
+
|
908
|
+
inventory_cli_opts = %i[authentication escalation transports].each_with_object([]) do |key, acc|
|
909
|
+
acc.concat(Bolt::BoltOptionParser::OPTIONS[key])
|
910
|
+
end
|
911
|
+
|
912
|
+
inventory_cli_opts.concat(%w[no-host-key-check no-ssl no-ssl-verify no-tty])
|
913
|
+
|
914
|
+
conflicting_options = Set.new(opts.keys.map(&:to_s)).intersection(inventory_cli_opts)
|
915
|
+
|
916
|
+
if inventory_source && conflicting_options.any?
|
917
|
+
Bolt::Logger.warn(
|
918
|
+
"cli_overrides",
|
919
|
+
"CLI arguments #{conflicting_options.to_a} might be overridden by Inventory: #{inventory_source}"
|
920
|
+
)
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
# Handle and print errors.
|
925
|
+
#
|
926
|
+
private def with_error_handling
|
927
|
+
yield
|
928
|
+
rescue Bolt::Error => e
|
929
|
+
fatal_error(e)
|
930
|
+
raise e
|
931
|
+
end
|
932
|
+
|
933
|
+
# Handle signals.
|
934
|
+
#
|
935
|
+
private def with_signal_handling
|
936
|
+
handler = Signal.trap :INT do |signo|
|
937
|
+
Bolt::Logger.logger(self).info(
|
938
|
+
"Exiting after receiving SIG#{Signal.signame(signo)} signal. "\
|
939
|
+
"There might be processes left executing on some targets."
|
940
|
+
)
|
941
|
+
exit!
|
942
|
+
end
|
943
|
+
|
944
|
+
yield
|
945
|
+
ensure
|
946
|
+
Signal.trap :INT, handler if handler
|
947
|
+
end
|
948
|
+
end
|
949
|
+
end
|