bolt 2.15.0 → 2.20.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
- data/lib/bolt/applicator.rb +21 -15
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +55 -20
- data/lib/bolt/catalog.rb +3 -2
- data/lib/bolt/cli.rb +116 -47
- data/lib/bolt/config.rb +48 -148
- data/lib/bolt/config/options.rb +488 -0
- data/lib/bolt/config/transport/base.rb +16 -16
- data/lib/bolt/config/transport/docker.rb +9 -23
- data/lib/bolt/config/transport/local.rb +6 -44
- data/lib/bolt/config/transport/options.rb +460 -0
- data/lib/bolt/config/transport/orch.rb +9 -18
- data/lib/bolt/config/transport/remote.rb +3 -6
- data/lib/bolt/config/transport/ssh.rb +74 -154
- data/lib/bolt/config/transport/winrm.rb +18 -47
- data/lib/bolt/executor.rb +15 -0
- data/lib/bolt/inventory/group.rb +4 -3
- data/lib/bolt/inventory/inventory.rb +4 -17
- data/lib/bolt/inventory/target.rb +18 -5
- data/lib/bolt/logger.rb +24 -1
- data/lib/bolt/outputter.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +14 -3
- data/lib/bolt/pal.rb +31 -11
- data/lib/bolt/pal/yaml_plan/evaluator.rb +19 -2
- data/lib/bolt/pal/yaml_plan/step.rb +11 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/plugin/module.rb +2 -4
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +41 -44
- data/lib/bolt/puppetdb/client.rb +2 -0
- data/lib/bolt/puppetdb/config.rb +16 -0
- data/lib/bolt/result.rb +7 -0
- data/lib/bolt/shell/bash.rb +53 -45
- data/lib/bolt/shell/powershell.rb +23 -12
- data/lib/bolt/shell/powershell/snippets.rb +15 -6
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +17 -5
- data/lib/bolt/transport/docker/connection.rb +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +20 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +23 -2
- data/lib/bolt/transport/winrm/connection.rb +109 -8
- data/lib/bolt/util.rb +26 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +3 -2
- data/lib/bolt_spec/bolt_context.rb +7 -2
- data/lib/bolt_spec/plans.rb +15 -2
- data/lib/bolt_spec/plans/action_stubs.rb +2 -1
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/mock_executor.rb +14 -1
- data/lib/bolt_spec/run.rb +22 -0
- data/libexec/bolt_catalog +3 -2
- metadata +20 -29
@@ -33,6 +33,7 @@ Puppet::Functions.create_function(:wait_until_available) do
|
|
33
33
|
executor = Puppet.lookup(:bolt_executor)
|
34
34
|
inventory = Puppet.lookup(:bolt_inventory)
|
35
35
|
|
36
|
+
# Send Analytics Report
|
36
37
|
executor.report_function_call(self.class.name)
|
37
38
|
|
38
39
|
# Ensure that given targets are all Target instances
|
@@ -20,7 +20,9 @@ Puppet::Functions.create_function(:'ctrl::do_until') do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def do_until(options = { 'limit' => 0 })
|
23
|
+
# Send Analytics Report
|
23
24
|
Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
|
25
|
+
|
24
26
|
limit = options['limit']
|
25
27
|
i = 0
|
26
28
|
until (x = yield)
|
@@ -17,6 +17,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def exists(scope, filename)
|
20
|
+
# Send Analytics Report
|
20
21
|
Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
|
21
22
|
found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
|
22
23
|
found ? Puppet::FileSystem.exist?(found) : false
|
@@ -16,7 +16,9 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def read(scope, filename)
|
19
|
+
# Send Analytics Report
|
19
20
|
Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
|
21
|
+
|
20
22
|
found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
|
21
23
|
unless found && Puppet::FileSystem.exist?(found)
|
22
24
|
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
@@ -17,7 +17,9 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def readable(scope, filename)
|
20
|
+
# Send Analytics Report
|
20
21
|
Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
|
22
|
+
|
21
23
|
found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
|
22
24
|
found ? File.readable?(found) : false
|
23
25
|
end
|
@@ -23,7 +23,9 @@ Puppet::Functions.create_function(:'out::message') do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
executor = Puppet.lookup(:bolt_executor)
|
26
|
+
# Send Analytics Report
|
26
27
|
executor.report_function_call(self.class.name)
|
28
|
+
|
27
29
|
executor.publish_event(type: :message, message: message)
|
28
30
|
|
29
31
|
nil
|
@@ -30,6 +30,7 @@ Puppet::Functions.create_function(:prompt) do
|
|
30
30
|
options = options.transform_keys(&:to_sym)
|
31
31
|
|
32
32
|
executor = Puppet.lookup(:bolt_executor)
|
33
|
+
# Send analytics report
|
33
34
|
executor.report_function_call(self.class.name)
|
34
35
|
|
35
36
|
response = executor.prompt(prompt, options)
|
data/lib/bolt/applicator.rb
CHANGED
@@ -18,7 +18,6 @@ module Bolt
|
|
18
18
|
pdb_client, hiera_config, max_compiles, apply_settings)
|
19
19
|
# lazy-load expensive gem code
|
20
20
|
require 'concurrent'
|
21
|
-
|
22
21
|
@inventory = inventory
|
23
22
|
@executor = executor
|
24
23
|
@modulepath = modulepath || []
|
@@ -30,17 +29,6 @@ module Bolt
|
|
30
29
|
|
31
30
|
@pool = Concurrent::ThreadPoolExecutor.new(max_threads: max_compiles)
|
32
31
|
@logger = Logging.logger[self]
|
33
|
-
@plugin_tarball = Concurrent::Delay.new do
|
34
|
-
build_plugin_tarball do |mod|
|
35
|
-
search_dirs = []
|
36
|
-
search_dirs << mod.plugins if mod.plugins?
|
37
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
38
|
-
search_dirs << mod.files if mod.files?
|
39
|
-
type_files = "#{mod.path}/types"
|
40
|
-
search_dirs << type_files if File.exist?(type_files)
|
41
|
-
search_dirs
|
42
|
-
end
|
43
|
-
end
|
44
32
|
end
|
45
33
|
|
46
34
|
private def libexec
|
@@ -188,7 +176,6 @@ module Bolt
|
|
188
176
|
|
189
177
|
def apply_ast(raw_ast, targets, options, plan_vars = {})
|
190
178
|
ast = Puppet::Pops::Serialization::ToDataConverter.convert(raw_ast, rich_data: true, symbol_to_string: true)
|
191
|
-
|
192
179
|
# Serialize as pcore for *Result* objects
|
193
180
|
plan_vars = Puppet::Pops::Serialization::ToDataConverter.convert(plan_vars,
|
194
181
|
rich_data: true,
|
@@ -196,19 +183,37 @@ module Bolt
|
|
196
183
|
type_by_reference: true,
|
197
184
|
local_reference: true)
|
198
185
|
|
186
|
+
bolt_project = @project if @project&.name
|
199
187
|
scope = {
|
200
188
|
code_ast: ast,
|
201
189
|
modulepath: @modulepath,
|
202
|
-
project:
|
190
|
+
project: bolt_project.to_h,
|
203
191
|
pdb_config: @pdb_client.config.to_hash,
|
204
192
|
hiera_config: @hiera_config,
|
205
193
|
plan_vars: plan_vars,
|
206
194
|
# This data isn't available on the target config hash
|
207
195
|
config: @inventory.transport_data_get
|
208
196
|
}
|
209
|
-
|
210
197
|
description = options[:description] || 'apply catalog'
|
211
198
|
|
199
|
+
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
200
|
+
if required_modules&.any?
|
201
|
+
@logger.debug("Syncing only required modules: #{required_modules.join(',')}.")
|
202
|
+
end
|
203
|
+
|
204
|
+
@plugin_tarball = Concurrent::Delay.new do
|
205
|
+
build_plugin_tarball do |mod|
|
206
|
+
next unless required_modules.nil? || required_modules.include?(mod.name)
|
207
|
+
search_dirs = []
|
208
|
+
search_dirs << mod.plugins if mod.plugins?
|
209
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
210
|
+
search_dirs << mod.files if mod.files?
|
211
|
+
type_files = "#{mod.path}/types"
|
212
|
+
search_dirs << type_files if File.exist?(type_files)
|
213
|
+
search_dirs
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
212
217
|
r = @executor.log_action(description, targets) do
|
213
218
|
futures = targets.map do |target|
|
214
219
|
Concurrent::Future.execute(executor: @pool) do
|
@@ -235,6 +240,7 @@ module Bolt
|
|
235
240
|
result
|
236
241
|
end
|
237
242
|
else
|
243
|
+
|
238
244
|
arguments = {
|
239
245
|
'catalog' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(catalog),
|
240
246
|
'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins),
|
data/lib/bolt/apply_result.rb
CHANGED
@@ -20,7 +20,7 @@ module Bolt
|
|
20
20
|
error_hash['msg'] =~ /The term 'ruby.exe' is not recognized as the name of a cmdlet/)
|
21
21
|
# Windows does not have Ruby present
|
22
22
|
{
|
23
|
-
'msg' => "Puppet
|
23
|
+
'msg' => "Puppet was not found on the target or in $env:ProgramFiles, please install it to enable 'apply'",
|
24
24
|
'kind' => 'bolt/apply-error'
|
25
25
|
}
|
26
26
|
elsif exit_code == 1 && error_hash['msg'] =~ /cannot load such file -- puppet \(LoadError\)/
|
@@ -10,10 +10,10 @@ module Bolt
|
|
10
10
|
authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
|
11
11
|
escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
|
12
12
|
run_context: %w[concurrency inventoryfile save-rerun cleanup],
|
13
|
-
global_config_setters: %w[modulepath
|
14
|
-
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
13
|
+
global_config_setters: %w[modulepath project configfile],
|
14
|
+
transports: %w[transport connect-timeout tty native-ssh ssh-command copy-command],
|
15
15
|
display: %w[format color verbose trace],
|
16
|
-
global: %w[help version debug] }.freeze
|
16
|
+
global: %w[help version debug log-level] }.freeze
|
17
17
|
|
18
18
|
ACTION_OPTS = OPTIONS.values.flatten.freeze
|
19
19
|
|
@@ -36,6 +36,9 @@ module Bolt
|
|
36
36
|
when 'upload'
|
37
37
|
{ flags: ACTION_OPTS + %w[tmpdir],
|
38
38
|
banner: FILE_UPLOAD_HELP }
|
39
|
+
when 'download'
|
40
|
+
{ flags: ACTION_OPTS,
|
41
|
+
banner: FILE_DOWNLOAD_HELP }
|
39
42
|
else
|
40
43
|
{ flags: OPTIONS[:global],
|
41
44
|
banner: FILE_HELP }
|
@@ -218,10 +221,30 @@ module Bolt
|
|
218
221
|
bolt file <action> [options]
|
219
222
|
|
220
223
|
DESCRIPTION
|
221
|
-
|
224
|
+
Copy files and directories between the controller and targets
|
222
225
|
|
223
226
|
ACTIONS
|
224
|
-
|
227
|
+
download Download a file or directory to the controller
|
228
|
+
upload Upload a local file or directory from the controller
|
229
|
+
HELP
|
230
|
+
|
231
|
+
FILE_DOWNLOAD_HELP = <<~HELP
|
232
|
+
NAME
|
233
|
+
download
|
234
|
+
|
235
|
+
USAGE
|
236
|
+
bolt file download <src> <dest> [options]
|
237
|
+
|
238
|
+
DESCRIPTION
|
239
|
+
Download a file or directory from one or more targets.
|
240
|
+
|
241
|
+
Downloaded files and directories are saved to the a subdirectory
|
242
|
+
matching the target's name under the destination directory. The
|
243
|
+
destination directory is expanded relative to the downloads
|
244
|
+
subdirectory of the project directory.
|
245
|
+
|
246
|
+
EXAMPLES
|
247
|
+
bolt file download /etc/ssh_config ssh_config -t all
|
225
248
|
HELP
|
226
249
|
|
227
250
|
FILE_UPLOAD_HELP = <<~HELP
|
@@ -422,7 +445,7 @@ module Bolt
|
|
422
445
|
|
423
446
|
ACTIONS
|
424
447
|
generate-types Generate type references to register in plans
|
425
|
-
install Install modules from a Puppetfile into a
|
448
|
+
install Install modules from a Puppetfile into a project
|
426
449
|
show-modules List modules available to the Bolt project
|
427
450
|
HELP
|
428
451
|
|
@@ -445,7 +468,7 @@ module Bolt
|
|
445
468
|
bolt puppetfile install [options]
|
446
469
|
|
447
470
|
DESCRIPTION
|
448
|
-
Install modules from a Puppetfile into a
|
471
|
+
Install modules from a Puppetfile into a project
|
449
472
|
HELP
|
450
473
|
|
451
474
|
PUPPETFILE_SHOWMODULES_HELP = <<~HELP
|
@@ -594,13 +617,13 @@ module Bolt
|
|
594
617
|
bolt task show canary
|
595
618
|
HELP
|
596
619
|
|
597
|
-
attr_reader :
|
620
|
+
attr_reader :deprecations
|
598
621
|
|
599
622
|
def initialize(options)
|
600
623
|
super()
|
601
624
|
|
602
625
|
@options = options
|
603
|
-
@
|
626
|
+
@deprecations = []
|
604
627
|
|
605
628
|
separator "\nINVENTORY OPTIONS"
|
606
629
|
define('-t', '--targets TARGETS',
|
@@ -709,29 +732,29 @@ module Bolt
|
|
709
732
|
File.expand_path(moduledir)
|
710
733
|
end
|
711
734
|
end
|
712
|
-
define('--boltdir
|
713
|
-
'Specify what
|
735
|
+
define('--project PATH', '--boltdir PATH',
|
736
|
+
'Specify what project to load config from (default: autodiscovered from current working dir)') do |path|
|
714
737
|
@options[:boltdir] = path
|
715
738
|
end
|
716
|
-
define('--configfile
|
739
|
+
define('--configfile PATH',
|
717
740
|
'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml).',
|
718
|
-
'Directory containing bolt.yaml will be used as the
|
741
|
+
'Directory containing bolt.yaml will be used as the project directory.') do |path|
|
719
742
|
@options[:configfile] = path
|
720
743
|
end
|
721
|
-
define('--hiera-config
|
744
|
+
define('--hiera-config PATH',
|
722
745
|
'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
|
723
746
|
@options[:'hiera-config'] = File.expand_path(path)
|
724
747
|
end
|
725
|
-
define('-i', '--inventoryfile
|
748
|
+
define('-i', '--inventoryfile PATH',
|
726
749
|
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
727
750
|
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
728
751
|
raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
|
729
752
|
end
|
730
753
|
@options[:inventoryfile] = Pathname.new(File.expand_path(path))
|
731
754
|
end
|
732
|
-
define('--puppetfile
|
755
|
+
define('--puppetfile PATH',
|
733
756
|
'Specify a Puppetfile to use when installing modules. (default: ~/.puppetlabs/bolt/Puppetfile)',
|
734
|
-
'Modules are installed in the current
|
757
|
+
'Modules are installed in the current project.') do |path|
|
735
758
|
@options[:puppetfile_path] = Pathname.new(File.expand_path(path))
|
736
759
|
end
|
737
760
|
define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
|
@@ -743,11 +766,15 @@ module Bolt
|
|
743
766
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
744
767
|
@options[:transport] = t
|
745
768
|
end
|
746
|
-
define('--ssh
|
769
|
+
define('--[no-]native-ssh', 'Whether to shell out to native SSH or use the net-ssh Ruby library.',
|
770
|
+
'This option is experimental') do |bool|
|
771
|
+
@options[:'native-ssh'] = bool
|
772
|
+
end
|
773
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh Ruby library. ",
|
747
774
|
"This option is experimental.") do |exec|
|
748
775
|
@options[:'ssh-command'] = exec
|
749
776
|
end
|
750
|
-
define('--copy-command EXEC', "Command to copy files to remote hosts if using
|
777
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using native SSH. ",
|
751
778
|
"This option is experimental.") do |exec|
|
752
779
|
@options[:'copy-command'] = exec
|
753
780
|
end
|
@@ -803,8 +830,16 @@ module Bolt
|
|
803
830
|
end
|
804
831
|
define('--debug', 'Display debug logging') do |_|
|
805
832
|
@options[:debug] = true
|
833
|
+
# We don't actually set '--log-level debug' here, but once the options are evaluated by
|
834
|
+
# the config class the end result is the same.
|
835
|
+
msg = "Command line option '--debug' is deprecated, set '--log-level debug' instead."
|
836
|
+
@deprecations << { type: 'Using --debug instead of --log-level debug', msg: msg }
|
837
|
+
end
|
838
|
+
define('--log-level LEVEL',
|
839
|
+
"Set the log level for the console. Available options are",
|
840
|
+
"debug, info, notice, warn, error, fatal, any.") do |level|
|
841
|
+
@options[:log] = { 'console' => { 'level' => level } }
|
806
842
|
end
|
807
|
-
|
808
843
|
define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
|
809
844
|
@options[:plugin] = plug
|
810
845
|
end
|
data/lib/bolt/catalog.rb
CHANGED
@@ -138,9 +138,10 @@ module Bolt
|
|
138
138
|
# That means the apply body either a) consists of just a
|
139
139
|
# NodeDefinition, b) consists of a BlockExpression which may
|
140
140
|
# contain NodeDefinitions, or c) doesn't contain NodeDefinitions.
|
141
|
-
definitions =
|
141
|
+
definitions = case ast
|
142
|
+
when Puppet::Pops::Model::BlockExpression
|
142
143
|
ast.statements.select { |st| st.is_a?(Puppet::Pops::Model::NodeDefinition) }
|
143
|
-
|
144
|
+
when Puppet::Pops::Model::NodeDefinition
|
144
145
|
[ast]
|
145
146
|
else
|
146
147
|
[]
|
data/lib/bolt/cli.rb
CHANGED
@@ -32,7 +32,7 @@ module Bolt
|
|
32
32
|
'script' => %w[run],
|
33
33
|
'task' => %w[show run],
|
34
34
|
'plan' => %w[show run convert],
|
35
|
-
'file' => %w[upload],
|
35
|
+
'file' => %w[download upload],
|
36
36
|
'puppetfile' => %w[install show-modules generate-types],
|
37
37
|
'secret' => %w[encrypt decrypt createkeys],
|
38
38
|
'inventory' => %w[show],
|
@@ -46,7 +46,6 @@ module Bolt
|
|
46
46
|
Bolt::Logger.initialize_logging
|
47
47
|
@logger = Logging.logger[self]
|
48
48
|
@argv = argv
|
49
|
-
@config = Bolt::Config.default
|
50
49
|
@options = {}
|
51
50
|
end
|
52
51
|
|
@@ -76,7 +75,23 @@ module Bolt
|
|
76
75
|
end
|
77
76
|
private :help?
|
78
77
|
|
78
|
+
# Wrapper method that is called by the Bolt executable. Parses the command and
|
79
|
+
# then loads the project and config. Once config is loaded, it completes the
|
80
|
+
# setup process by configuring Bolt and issuing warnings.
|
81
|
+
#
|
82
|
+
# This separation is needed since the Bolt::Outputter class that normally handles
|
83
|
+
# printing errors relies on config being loaded. All setup that happens before
|
84
|
+
# config is loaded will have errors printed directly to stdout, while all errors
|
85
|
+
# raised after config is loaded are handled by the outputter.
|
79
86
|
def parse
|
87
|
+
parse_command
|
88
|
+
load_config
|
89
|
+
finalize_setup
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parses the command and validates options. All errors that are raised here
|
93
|
+
# are not handled by the outputter, as it relies on config being loaded.
|
94
|
+
def parse_command
|
80
95
|
parser = BoltOptionParser.new(options)
|
81
96
|
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
82
97
|
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
@@ -109,51 +124,66 @@ module Bolt
|
|
109
124
|
end
|
110
125
|
options[:leftovers] = remaining
|
111
126
|
|
127
|
+
# Default to verbose for everything except plans
|
128
|
+
unless options.key?(:verbose)
|
129
|
+
options[:verbose] = options[:subcommand] != 'plan'
|
130
|
+
end
|
131
|
+
|
112
132
|
validate(options)
|
113
133
|
|
114
|
-
|
134
|
+
# Deprecation warnings can't be issued until after config is loaded, so
|
135
|
+
# store them for later.
|
136
|
+
@parser_deprecations = parser.deprecations
|
137
|
+
rescue Bolt::Error => e
|
138
|
+
fatal_error(e)
|
139
|
+
raise e
|
140
|
+
end
|
141
|
+
|
142
|
+
# Loads the project and configuration. All errors that are raised here are not
|
143
|
+
# handled by the outputter, as it relies on config being loaded.
|
144
|
+
def load_config
|
145
|
+
@config = if ENV['BOLT_PROJECT']
|
146
|
+
project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
147
|
+
Bolt::Config.from_project(project, options)
|
148
|
+
elsif options[:configfile]
|
115
149
|
Bolt::Config.from_file(options[:configfile], options)
|
116
150
|
else
|
117
151
|
project = if options[:boltdir]
|
118
|
-
|
152
|
+
dir = Pathname.new(options[:boltdir])
|
153
|
+
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
154
|
+
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
155
|
+
else
|
156
|
+
Bolt::Project.create_project(dir)
|
157
|
+
end
|
119
158
|
else
|
120
159
|
Bolt::Project.find_boltdir(Dir.pwd)
|
121
160
|
end
|
122
161
|
Bolt::Config.from_project(project, options)
|
123
162
|
end
|
163
|
+
rescue Bolt::Error => e
|
164
|
+
fatal_error(e)
|
165
|
+
raise e
|
166
|
+
end
|
124
167
|
|
168
|
+
# Completes the setup process by configuring Bolt and issuing warnings
|
169
|
+
def finalize_setup
|
125
170
|
Bolt::Logger.configure(config.log, config.color)
|
171
|
+
Bolt::Logger.analytics = analytics
|
126
172
|
|
127
173
|
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
128
|
-
|
129
|
-
|
174
|
+
config.check_path_case('modulepath', config.modulepath)
|
175
|
+
config.project.check_deprecated_file
|
130
176
|
|
131
177
|
# Log the file paths for loaded config files
|
132
178
|
config_loaded
|
133
179
|
|
134
180
|
# Display warnings created during parser and config initialization
|
135
|
-
parser.warnings.each { |warning| @logger.warn(warning[:msg]) }
|
136
181
|
config.warnings.each { |warning| @logger.warn(warning[:msg]) }
|
137
|
-
|
138
|
-
|
139
|
-
# After this step
|
140
|
-
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
141
|
-
# options[:targets] will contain a resolved set of Target objects
|
142
|
-
unless options[:subcommand] == 'puppetfile' ||
|
143
|
-
options[:subcommand] == 'secret' ||
|
144
|
-
options[:subcommand] == 'project' ||
|
145
|
-
options[:action] == 'show' ||
|
146
|
-
options[:action] == 'convert'
|
147
|
-
|
148
|
-
update_targets(options)
|
149
|
-
end
|
150
|
-
|
151
|
-
unless options.key?(:verbose)
|
152
|
-
# Default to verbose for everything except plans
|
153
|
-
options[:verbose] = options[:subcommand] != 'plan'
|
154
|
-
end
|
182
|
+
@parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
183
|
+
config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
155
184
|
|
156
185
|
warn_inventory_overrides_cli(options)
|
186
|
+
|
157
187
|
options
|
158
188
|
rescue Bolt::Error => e
|
159
189
|
outputter.fatal_error(e)
|
@@ -219,7 +249,7 @@ module Bolt
|
|
219
249
|
end
|
220
250
|
|
221
251
|
if options[:boltdir] && options[:configfile]
|
222
|
-
raise Bolt::CLIError, "Only one of '--boltdir' or '--configfile' may be specified"
|
252
|
+
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
223
253
|
end
|
224
254
|
|
225
255
|
if options[:noop] &&
|
@@ -245,6 +275,10 @@ module Bolt
|
|
245
275
|
!options[:object]
|
246
276
|
raise Bolt::CLIError, "Must specify a value to #{options[:action]}"
|
247
277
|
end
|
278
|
+
|
279
|
+
if options.key?(:debug) && options.key?(:log)
|
280
|
+
raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
|
281
|
+
end
|
248
282
|
end
|
249
283
|
|
250
284
|
def handle_parser_errors
|
@@ -272,12 +306,12 @@ module Bolt
|
|
272
306
|
def warn_inventory_overrides_cli(opts)
|
273
307
|
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
274
308
|
Bolt::Inventory::ENVIRONMENT_VAR
|
275
|
-
elsif
|
276
|
-
|
309
|
+
elsif config.inventoryfile && Bolt::Util.file_stat(config.inventoryfile)
|
310
|
+
config.inventoryfile
|
277
311
|
else
|
278
312
|
begin
|
279
|
-
Bolt::Util.file_stat(
|
280
|
-
|
313
|
+
Bolt::Util.file_stat(config.default_inventoryfile)
|
314
|
+
config.default_inventoryfile
|
281
315
|
rescue Errno::ENOENT
|
282
316
|
nil
|
283
317
|
end
|
@@ -306,6 +340,17 @@ module Bolt
|
|
306
340
|
exit!
|
307
341
|
end
|
308
342
|
|
343
|
+
# Initialize inventory and targets. Errors here are better to catch early.
|
344
|
+
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
345
|
+
# options[:targets] will contain a resolved set of Target objects
|
346
|
+
unless options[:subcommand] == 'puppetfile' ||
|
347
|
+
options[:subcommand] == 'secret' ||
|
348
|
+
options[:subcommand] == 'project' ||
|
349
|
+
options[:action] == 'show' ||
|
350
|
+
options[:action] == 'convert'
|
351
|
+
update_targets(options)
|
352
|
+
end
|
353
|
+
|
309
354
|
if options[:action] == 'convert'
|
310
355
|
convert_plan(options[:object])
|
311
356
|
return 0
|
@@ -334,30 +379,32 @@ module Bolt
|
|
334
379
|
|
335
380
|
analytics.screen_view(screen, screen_view_fields)
|
336
381
|
|
337
|
-
|
338
|
-
|
382
|
+
case options[:action]
|
383
|
+
when 'show'
|
384
|
+
case options[:subcommand]
|
385
|
+
when 'task'
|
339
386
|
if options[:object]
|
340
387
|
show_task(options[:object])
|
341
388
|
else
|
342
389
|
list_tasks
|
343
390
|
end
|
344
|
-
|
391
|
+
when 'plan'
|
345
392
|
if options[:object]
|
346
393
|
show_plan(options[:object])
|
347
394
|
else
|
348
395
|
list_plans
|
349
396
|
end
|
350
|
-
|
397
|
+
when 'inventory'
|
351
398
|
if options[:detail]
|
352
399
|
show_targets
|
353
400
|
else
|
354
401
|
list_targets
|
355
402
|
end
|
356
|
-
|
403
|
+
when 'group'
|
357
404
|
list_groups
|
358
405
|
end
|
359
406
|
return 0
|
360
|
-
|
407
|
+
when 'show-modules'
|
361
408
|
list_modules
|
362
409
|
return 0
|
363
410
|
end
|
@@ -370,18 +417,20 @@ module Bolt
|
|
370
417
|
|
371
418
|
case options[:subcommand]
|
372
419
|
when 'project'
|
373
|
-
|
420
|
+
case options[:action]
|
421
|
+
when 'init'
|
374
422
|
code = initialize_project
|
375
|
-
|
423
|
+
when 'migrate'
|
376
424
|
code = migrate_project
|
377
425
|
end
|
378
426
|
when 'plan'
|
379
427
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
380
428
|
when 'puppetfile'
|
381
|
-
|
429
|
+
case options[:action]
|
430
|
+
when 'generate-types'
|
382
431
|
code = generate_types
|
383
|
-
|
384
|
-
code = install_puppetfile(
|
432
|
+
when 'install'
|
433
|
+
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
385
434
|
end
|
386
435
|
when 'secret'
|
387
436
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -422,11 +471,22 @@ module Bolt
|
|
422
471
|
src = options[:object]
|
423
472
|
dest = options[:leftovers].first
|
424
473
|
|
474
|
+
if src.nil?
|
475
|
+
raise Bolt::CLIError, "A source path must be specified"
|
476
|
+
end
|
477
|
+
|
425
478
|
if dest.nil?
|
426
479
|
raise Bolt::CLIError, "A destination path must be specified"
|
427
480
|
end
|
428
|
-
|
429
|
-
|
481
|
+
|
482
|
+
case options[:action]
|
483
|
+
when 'download'
|
484
|
+
dest = File.expand_path(dest, Dir.pwd)
|
485
|
+
executor.download_file(targets, src, dest, executor_opts)
|
486
|
+
when 'upload'
|
487
|
+
validate_file('source file', src, true)
|
488
|
+
executor.upload_file(targets, src, dest, executor_opts)
|
489
|
+
end
|
430
490
|
end
|
431
491
|
end
|
432
492
|
|
@@ -513,7 +573,7 @@ module Bolt
|
|
513
573
|
plan_context[:description] = options[:description] if options[:description]
|
514
574
|
|
515
575
|
executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
|
516
|
-
if options.fetch(:format, 'human')
|
576
|
+
if %w[human rainbow].include?(options.fetch(:format, 'human'))
|
517
577
|
executor.subscribe(outputter)
|
518
578
|
else
|
519
579
|
# Only subscribe to out::message events for JSON outputter
|
@@ -771,14 +831,13 @@ module Bolt
|
|
771
831
|
end
|
772
832
|
|
773
833
|
def pal
|
774
|
-
project = config.project.project_file? ? config.project : nil
|
775
834
|
@pal ||= Bolt::PAL.new(config.modulepath,
|
776
835
|
config.hiera_config,
|
777
836
|
config.project.resource_types,
|
778
837
|
config.compile_concurrency,
|
779
838
|
config.trusted_external,
|
780
839
|
config.apply_settings,
|
781
|
-
project)
|
840
|
+
config.project)
|
782
841
|
end
|
783
842
|
|
784
843
|
def convert_plan(plan)
|
@@ -794,7 +853,7 @@ module Bolt
|
|
794
853
|
end
|
795
854
|
|
796
855
|
def rerun
|
797
|
-
@rerun ||= Bolt::Rerun.new(
|
856
|
+
@rerun ||= Bolt::Rerun.new(config.rerunfile, config.save_rerun)
|
798
857
|
end
|
799
858
|
|
800
859
|
def outputter
|
@@ -857,5 +916,15 @@ module Bolt
|
|
857
916
|
def incomplete_install?
|
858
917
|
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact]).empty?
|
859
918
|
end
|
919
|
+
|
920
|
+
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|
921
|
+
# errors prior to config being loaded, as the outputter relies on config being loaded.
|
922
|
+
def fatal_error(error)
|
923
|
+
if $stdout.isatty
|
924
|
+
$stdout.puts("\033[31m#{error.message}\033[0m")
|
925
|
+
else
|
926
|
+
$stdout.puts(error.message)
|
927
|
+
end
|
928
|
+
end
|
860
929
|
end
|
861
930
|
end
|