bolt 2.26.0 → 2.31.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +13 -12
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/lib/bolt/analytics.rb +4 -0
- data/lib/bolt/applicator.rb +19 -18
- data/lib/bolt/bolt_option_parser.rb +112 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +210 -174
- data/lib/bolt/config.rb +22 -2
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +30 -0
- data/lib/bolt/config/transport/options.rb +1 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/logger.rb +26 -19
- data/lib/bolt/module_installer.rb +242 -0
- data/lib/bolt/outputter.rb +4 -0
- data/lib/bolt/outputter/human.rb +77 -17
- data/lib/bolt/outputter/json.rb +21 -6
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +46 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +62 -12
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +198 -0
- data/lib/bolt/puppetfile.rb +149 -0
- data/lib/bolt/puppetfile/installer.rb +43 -0
- data/lib/bolt/puppetfile/module.rb +93 -0
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +15 -0
- data/lib/bolt/shell/bash.rb +4 -3
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +51 -10
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/file_cache.rb +11 -11
- data/lib/bolt_server/transport_app.rb +206 -27
- data/lib/bolt_spec/bolt_context.rb +8 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +14 -6
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt_server/pe/pal.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b9f29b79c8544029c9a69f6dc76b083857a87e178c9aae0359e49c59ce3cfdb
|
4
|
+
data.tar.gz: 02166a6dda2dc381365b7556f506a419e2717267e88f6015af430f7f725d75d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a282194ca9ccf988e7282afc3a3a0b57b4edc16bb5bf61790fb0eb0ef234e0c7a76c0ea6cd44915047a99e6d423311be1307f435d8000c568d0552268f6afa6
|
7
|
+
data.tar.gz: 957e6be1a72af03bd448fe070e6f9bb0b78aa1bb65e90a8bda251ef0f6c56e5ffaa8ff64aa0c5852963d9fecb87144b61ffd24494fe0aabcbdff43ecde9947a3
|
data/Puppetfile
CHANGED
@@ -7,33 +7,34 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
|
|
7
7
|
# Core modules used by 'apply'
|
8
8
|
mod 'puppetlabs-service', '1.3.0'
|
9
9
|
mod 'puppetlabs-puppet_agent', '4.1.1'
|
10
|
-
mod 'puppetlabs-facts', '1.
|
10
|
+
mod 'puppetlabs-facts', '1.1.0'
|
11
11
|
|
12
12
|
# Core types and providers for Puppet 6
|
13
|
-
mod 'puppetlabs-augeas_core', '1.
|
13
|
+
mod 'puppetlabs-augeas_core', '1.1.1'
|
14
14
|
mod 'puppetlabs-host_core', '1.0.3'
|
15
|
-
mod 'puppetlabs-scheduled_task', '2.
|
16
|
-
mod 'puppetlabs-sshkeys_core', '1.0
|
17
|
-
mod 'puppetlabs-zfs_core', '1.0
|
18
|
-
mod 'puppetlabs-cron_core', '1.0.
|
15
|
+
mod 'puppetlabs-scheduled_task', '2.2.1'
|
16
|
+
mod 'puppetlabs-sshkeys_core', '2.1.0'
|
17
|
+
mod 'puppetlabs-zfs_core', '1.1.0'
|
18
|
+
mod 'puppetlabs-cron_core', '1.0.4'
|
19
19
|
mod 'puppetlabs-mount_core', '1.0.4'
|
20
20
|
mod 'puppetlabs-selinux_core', '1.0.4'
|
21
|
-
mod 'puppetlabs-yumrepo_core', '1.0.
|
21
|
+
mod 'puppetlabs-yumrepo_core', '1.0.7'
|
22
22
|
mod 'puppetlabs-zone_core', '1.0.3'
|
23
23
|
|
24
24
|
# Useful additional modules
|
25
|
-
mod 'puppetlabs-package', '1.
|
25
|
+
mod 'puppetlabs-package', '1.3.0'
|
26
26
|
mod 'puppetlabs-puppet_conf', '0.6.0'
|
27
27
|
mod 'puppetlabs-python_task_helper', '0.4.3'
|
28
28
|
mod 'puppetlabs-reboot', '3.0.0'
|
29
29
|
mod 'puppetlabs-ruby_task_helper', '0.5.1'
|
30
30
|
mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
|
31
|
-
mod 'puppetlabs-stdlib', '6.
|
31
|
+
mod 'puppetlabs-stdlib', '6.5.0'
|
32
32
|
|
33
33
|
# Plugin modules
|
34
|
-
mod 'puppetlabs-aws_inventory', '0.5.
|
35
|
-
mod 'puppetlabs-azure_inventory', '0.
|
36
|
-
mod 'puppetlabs-gcloud_inventory', '0.1.
|
34
|
+
mod 'puppetlabs-aws_inventory', '0.5.2'
|
35
|
+
mod 'puppetlabs-azure_inventory', '0.4.1'
|
36
|
+
mod 'puppetlabs-gcloud_inventory', '0.1.3'
|
37
|
+
mod 'puppetlabs-http_request', '0.1.0'
|
37
38
|
mod 'puppetlabs-pkcs7', '0.1.1'
|
38
39
|
mod 'puppetlabs-terraform', '0.5.0'
|
39
40
|
mod 'puppetlabs-vault', '0.3.0'
|
@@ -6,15 +6,15 @@ require 'tempfile'
|
|
6
6
|
#
|
7
7
|
# > **Note:** Not available in apply block
|
8
8
|
Puppet::Functions.create_function(:write_file) do
|
9
|
-
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
10
9
|
# @param content File content to write.
|
11
10
|
# @param destination An absolute path on the target(s).
|
11
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
12
12
|
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
13
13
|
# @option options [String] _run_as User to run as using privilege escalation.
|
14
14
|
# @return A list of results, one entry per target.
|
15
15
|
# @example Write a file to a target
|
16
16
|
# $content = 'Hello, world!'
|
17
|
-
# write_file($
|
17
|
+
# write_file($content, '/Users/me/hello.txt', $targets)
|
18
18
|
dispatch :write_file do
|
19
19
|
required_param 'String', :content
|
20
20
|
required_param 'String[1]', :destination
|
data/lib/bolt/analytics.rb
CHANGED
@@ -93,6 +93,10 @@ module Bolt
|
|
93
93
|
def self.write_config(filename, config)
|
94
94
|
FileUtils.mkdir_p(File.dirname(filename))
|
95
95
|
File.write(filename, config.to_yaml)
|
96
|
+
rescue StandardError => e
|
97
|
+
Bolt::Logger.warn_once('unwriteable_file', "Could not write analytics configuration to #{filename}.")
|
98
|
+
# This will get caught by build_client and create a NoopClient
|
99
|
+
raise e
|
96
100
|
end
|
97
101
|
|
98
102
|
class Client
|
data/lib/bolt/applicator.rb
CHANGED
@@ -50,15 +50,15 @@ module Bolt
|
|
50
50
|
|
51
51
|
def catalog_apply_task
|
52
52
|
@catalog_apply_task ||= begin
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
path = File.join(libexec, 'apply_catalog.rb')
|
54
|
+
file = { 'name' => 'apply_catalog.rb', 'path' => path }
|
55
|
+
metadata = { 'supports_noop' => true, 'input_method' => 'stdin',
|
56
|
+
'implementations' => [
|
57
|
+
{ 'name' => 'apply_catalog.rb' },
|
58
|
+
{ 'name' => 'apply_catalog.rb', 'remote' => true }
|
59
|
+
] }
|
60
|
+
Bolt::Task.new('apply_helpers::apply_catalog', metadata, [file])
|
61
|
+
end
|
62
62
|
end
|
63
63
|
|
64
64
|
def query_resources_task
|
@@ -75,34 +75,35 @@ module Bolt
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
def compile(target,
|
78
|
+
def compile(target, scope)
|
79
79
|
# This simplified Puppet node object is what .local uses to determine the
|
80
80
|
# certname of the target
|
81
81
|
node = Puppet::Node.from_data_hash('name' => target.name,
|
82
82
|
'parameters' => { 'clientcert' => target.name })
|
83
83
|
trusted = Puppet::Context::TrustedInformation.local(node)
|
84
|
-
|
84
|
+
target_data = {
|
85
85
|
name: target.name,
|
86
86
|
facts: @inventory.facts(target).merge('bolt' => true),
|
87
87
|
variables: @inventory.vars(target),
|
88
88
|
trusted: trusted.to_h
|
89
89
|
}
|
90
|
+
catalog_request = scope.merge(target: target_data)
|
90
91
|
|
91
92
|
bolt_catalog_exe = File.join(libexec, 'bolt_catalog')
|
92
93
|
old_path = ENV['PATH']
|
93
94
|
ENV['PATH'] = "#{RbConfig::CONFIG['bindir']}#{File::PATH_SEPARATOR}#{old_path}"
|
94
|
-
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data:
|
95
|
+
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_request.to_json)
|
95
96
|
ENV['PATH'] = old_path
|
96
97
|
|
97
98
|
# If bolt_catalog does not return valid JSON, we should print stderr to
|
98
99
|
# see what happened
|
99
100
|
print_logs = stat.success?
|
100
101
|
result = begin
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
JSON.parse(out)
|
103
|
+
rescue JSON::ParserError
|
104
|
+
print_logs = true
|
105
|
+
{ 'message' => "Something's gone terribly wrong! STDERR is logged." }
|
106
|
+
end
|
106
107
|
|
107
108
|
# Any messages logged by Puppet will be on stderr as JSON hashes, so we
|
108
109
|
# parse those and store them here. Any message on stderr that is not
|
@@ -192,7 +193,7 @@ module Bolt
|
|
192
193
|
plan_vars: plan_vars,
|
193
194
|
# This data isn't available on the target config hash
|
194
195
|
config: @inventory.transport_data_get
|
195
|
-
}
|
196
|
+
}.freeze
|
196
197
|
description = options[:description] || 'apply catalog'
|
197
198
|
|
198
199
|
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
@@ -64,6 +64,24 @@ module Bolt
|
|
64
64
|
when 'guide'
|
65
65
|
{ flags: OPTIONS[:global] + %w[format],
|
66
66
|
banner: GUIDE_HELP }
|
67
|
+
when 'module'
|
68
|
+
case action
|
69
|
+
when 'add'
|
70
|
+
{ flags: OPTIONS[:global] + %w[configfile project],
|
71
|
+
banner: MODULE_ADD_HELP }
|
72
|
+
when 'generate-types'
|
73
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
74
|
+
banner: MODULE_GENERATETYPES_HELP }
|
75
|
+
when 'install'
|
76
|
+
{ flags: OPTIONS[:global] + %w[configfile force project resolve],
|
77
|
+
banner: MODULE_INSTALL_HELP }
|
78
|
+
when 'show'
|
79
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
80
|
+
banner: MODULE_SHOW_HELP }
|
81
|
+
else
|
82
|
+
{ flags: OPTIONS[:global],
|
83
|
+
banner: MODULE_HELP }
|
84
|
+
end
|
67
85
|
when 'plan'
|
68
86
|
case action
|
69
87
|
when 'convert'
|
@@ -169,6 +187,7 @@ module Bolt
|
|
169
187
|
group Show the list of groups in the inventory
|
170
188
|
guide View guides for Bolt concepts and features
|
171
189
|
inventory Show the list of targets an action would run on
|
190
|
+
module Manage Bolt project modules
|
172
191
|
plan Convert, create, show, and run Bolt plans
|
173
192
|
project Create and migrate Bolt projects
|
174
193
|
puppetfile Install and list modules and generate type references
|
@@ -341,6 +360,87 @@ module Bolt
|
|
341
360
|
Show the list of targets an action would run on.
|
342
361
|
HELP
|
343
362
|
|
363
|
+
MODULE_HELP = <<~HELP
|
364
|
+
NAME
|
365
|
+
module
|
366
|
+
|
367
|
+
USAGE
|
368
|
+
bolt module <action> [options]
|
369
|
+
|
370
|
+
DESCRIPTION
|
371
|
+
Manage Bolt project modules
|
372
|
+
|
373
|
+
The module command is only supported when a project is configured
|
374
|
+
with the 'modules' key.
|
375
|
+
|
376
|
+
ACTIONS
|
377
|
+
add Add a module to the project
|
378
|
+
generate-types Generate type references to register in plans
|
379
|
+
install Install the project's modules
|
380
|
+
show List modules available to the Bolt project
|
381
|
+
HELP
|
382
|
+
|
383
|
+
MODULE_ADD_HELP = <<~HELP
|
384
|
+
NAME
|
385
|
+
add
|
386
|
+
|
387
|
+
USAGE
|
388
|
+
bolt module add <module> [options]
|
389
|
+
|
390
|
+
DESCRIPTION
|
391
|
+
Add a module to the project.
|
392
|
+
|
393
|
+
Module declarations are loaded from the project's configuration
|
394
|
+
file. Bolt will automatically resolve all module dependencies,
|
395
|
+
generate a Puppetfile, and install the modules.
|
396
|
+
|
397
|
+
The module command is only supported when a project is configured
|
398
|
+
with the 'modules' key.
|
399
|
+
HELP
|
400
|
+
|
401
|
+
MODULE_GENERATETYPES_HELP = <<~HELP
|
402
|
+
NAME
|
403
|
+
generate-types
|
404
|
+
|
405
|
+
USAGE
|
406
|
+
bolt module generate-types [options]
|
407
|
+
|
408
|
+
DESCRIPTION
|
409
|
+
Generate type references to register in plans.
|
410
|
+
|
411
|
+
The module command is only supported when a project is configured
|
412
|
+
with the 'modules' key.
|
413
|
+
HELP
|
414
|
+
|
415
|
+
MODULE_INSTALL_HELP = <<~HELP
|
416
|
+
NAME
|
417
|
+
install
|
418
|
+
|
419
|
+
USAGE
|
420
|
+
bolt module install [options]
|
421
|
+
|
422
|
+
DESCRIPTION
|
423
|
+
Install the project's modules.
|
424
|
+
|
425
|
+
Module declarations are loaded from the project's configuration
|
426
|
+
file. Bolt will automatically resolve all module dependencies,
|
427
|
+
generate a Puppetfile, and install the modules.
|
428
|
+
HELP
|
429
|
+
|
430
|
+
MODULE_SHOW_HELP = <<~HELP
|
431
|
+
NAME
|
432
|
+
show
|
433
|
+
|
434
|
+
USAGE
|
435
|
+
bolt module show [options]
|
436
|
+
|
437
|
+
DESCRIPTION
|
438
|
+
List modules available to the Bolt project.
|
439
|
+
|
440
|
+
The module command is only supported when a project is configured
|
441
|
+
with the 'modules' key.
|
442
|
+
HELP
|
443
|
+
|
344
444
|
PLAN_HELP = <<~HELP
|
345
445
|
NAME
|
346
446
|
plan
|
@@ -678,7 +778,7 @@ module Bolt
|
|
678
778
|
'For SSH, port defaults to `22`',
|
679
779
|
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets|
|
680
780
|
@options[:targets] ||= []
|
681
|
-
@options[:targets] << get_arg_input(targets)
|
781
|
+
@options[:targets] << Bolt::Util.get_arg_input(targets)
|
682
782
|
end
|
683
783
|
define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
684
784
|
@options[:query] = query
|
@@ -828,7 +928,7 @@ module Bolt
|
|
828
928
|
"This option is experimental.") do |exec|
|
829
929
|
@options[:'copy-command'] = exec
|
830
930
|
end
|
831
|
-
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
931
|
+
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout in seconds (defaults vary)') do |timeout|
|
832
932
|
@options[:'connect-timeout'] = timeout
|
833
933
|
end
|
834
934
|
define('--[no-]tty', 'Request a pseudo TTY on targets that support it') do |tty|
|
@@ -838,6 +938,13 @@ module Bolt
|
|
838
938
|
@options[:tmpdir] = tmpdir
|
839
939
|
end
|
840
940
|
|
941
|
+
separator "\nMODULE OPTIONS"
|
942
|
+
define('--[no-]resolve',
|
943
|
+
'Use --no-resolve to install modules listed in the Puppetfile without resolving modules configured',
|
944
|
+
'in Bolt project configuration') do |resolve|
|
945
|
+
@options[:resolve] = resolve
|
946
|
+
end
|
947
|
+
|
841
948
|
separator "\nDISPLAY OPTIONS"
|
842
949
|
define('--filter FILTER', 'Filter tasks and plans by a matching substring') do |filter|
|
843
950
|
unless /^[a-z0-9_:]+$/.match(filter)
|
@@ -864,9 +971,9 @@ module Bolt
|
|
864
971
|
define('--modules MODULES',
|
865
972
|
'A comma-separated list of modules to install from the Puppet Forge',
|
866
973
|
'when initializing a project. Resolves and installs all dependencies.') do |modules|
|
867
|
-
@options[:modules] = modules.split(',')
|
974
|
+
@options[:modules] = modules.split(',').map { |mod| { 'name' => mod } }
|
868
975
|
end
|
869
|
-
define('--force', '
|
976
|
+
define('--force', 'Force a destructive action') do |_force|
|
870
977
|
@options[:force] = true
|
871
978
|
end
|
872
979
|
|
@@ -917,27 +1024,10 @@ module Bolt
|
|
917
1024
|
end
|
918
1025
|
|
919
1026
|
def parse_params(params)
|
920
|
-
json = get_arg_input(params)
|
1027
|
+
json = Bolt::Util.get_arg_input(params)
|
921
1028
|
JSON.parse(json)
|
922
1029
|
rescue JSON::ParserError => e
|
923
1030
|
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{e}"
|
924
1031
|
end
|
925
|
-
|
926
|
-
def get_arg_input(value)
|
927
|
-
if value.start_with?('@')
|
928
|
-
file = value.sub(/^@/, '')
|
929
|
-
read_arg_file(file)
|
930
|
-
elsif value == '-'
|
931
|
-
$stdin.read
|
932
|
-
else
|
933
|
-
value
|
934
|
-
end
|
935
|
-
end
|
936
|
-
|
937
|
-
def read_arg_file(file)
|
938
|
-
File.read(File.expand_path(file))
|
939
|
-
rescue StandardError => e
|
940
|
-
raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
|
941
|
-
end
|
942
1032
|
end
|
943
1033
|
end
|
data/lib/bolt/catalog.rb
CHANGED
@@ -97,7 +97,7 @@ module Bolt
|
|
97
97
|
}
|
98
98
|
|
99
99
|
with_puppet_settings(puppet_settings) do
|
100
|
-
Puppet::Pal.in_tmp_environment('bolt_catalog', env_conf) do |pal|
|
100
|
+
Puppet::Pal.in_tmp_environment('bolt_catalog', **env_conf) do |pal|
|
101
101
|
Puppet.override(puppet_overrides) do
|
102
102
|
Puppet.lookup(:pal_current_node).trusted_data = target['trusted']
|
103
103
|
pal.with_catalog_compiler do |compiler|
|
data/lib/bolt/cli.rb
CHANGED
@@ -20,27 +20,31 @@ require 'bolt/logger'
|
|
20
20
|
require 'bolt/outputter'
|
21
21
|
require 'bolt/puppetdb'
|
22
22
|
require 'bolt/plugin'
|
23
|
-
require 'bolt/
|
23
|
+
require 'bolt/project_migrator'
|
24
24
|
require 'bolt/pal'
|
25
25
|
require 'bolt/target'
|
26
26
|
require 'bolt/version'
|
27
27
|
require 'bolt/secret'
|
28
|
+
require 'bolt/module_installer'
|
28
29
|
|
29
30
|
module Bolt
|
30
31
|
class CLIExit < StandardError; end
|
31
32
|
class CLI
|
32
|
-
COMMANDS = {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
COMMANDS = {
|
34
|
+
'command' => %w[run],
|
35
|
+
'script' => %w[run],
|
36
|
+
'task' => %w[show run],
|
37
|
+
'plan' => %w[show run convert new],
|
38
|
+
'file' => %w[download upload],
|
39
|
+
'puppetfile' => %w[install show-modules generate-types],
|
40
|
+
'secret' => %w[encrypt decrypt createkeys],
|
41
|
+
'inventory' => %w[show],
|
42
|
+
'group' => %w[show],
|
43
|
+
'project' => %w[init migrate],
|
44
|
+
'module' => %w[add generate-types install show],
|
45
|
+
'apply' => %w[],
|
46
|
+
'guide' => %w[]
|
47
|
+
}.freeze
|
44
48
|
|
45
49
|
attr_reader :config, :options
|
46
50
|
|
@@ -98,6 +102,10 @@ module Bolt
|
|
98
102
|
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
99
103
|
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
100
104
|
if @argv.empty? || help?(remaining)
|
105
|
+
# If the subcommand is not enabled, display the default
|
106
|
+
# help text
|
107
|
+
options[:subcommand] = nil unless COMMANDS.include?(options[:subcommand])
|
108
|
+
|
101
109
|
# Update the parser for the subcommand (or lack thereof)
|
102
110
|
parser.update
|
103
111
|
puts parser.help
|
@@ -106,6 +114,11 @@ module Bolt
|
|
106
114
|
|
107
115
|
options[:object] = remaining.shift
|
108
116
|
|
117
|
+
# Handle reading a command from a file
|
118
|
+
if options[:subcommand] == 'command' && options[:object]
|
119
|
+
options[:object] = Bolt::Util.get_arg_input(options[:object])
|
120
|
+
end
|
121
|
+
|
109
122
|
# Only parse task_options for task or plan
|
110
123
|
if %w[task plan].include?(options[:subcommand])
|
111
124
|
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
@@ -183,6 +196,10 @@ module Bolt
|
|
183
196
|
|
184
197
|
warn_inventory_overrides_cli(options)
|
185
198
|
|
199
|
+
# Assert whether the puppetfile/module commands are available depending
|
200
|
+
# on whether 'modules' is configured.
|
201
|
+
assert_puppetfile_or_module_command(config.project.modules)
|
202
|
+
|
186
203
|
options
|
187
204
|
rescue Bolt::Error => e
|
188
205
|
outputter.fatal_error(e)
|
@@ -230,12 +247,6 @@ module Bolt
|
|
230
247
|
end
|
231
248
|
end
|
232
249
|
|
233
|
-
if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
|
234
|
-
!options[:leftovers].empty?
|
235
|
-
raise Bolt::CLIError,
|
236
|
-
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
237
|
-
end
|
238
|
-
|
239
250
|
if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
|
240
251
|
if options[:object].nil?
|
241
252
|
raise Bolt::CLIError, "Must specify a #{options[:subcommand]} to run"
|
@@ -247,23 +258,6 @@ module Bolt
|
|
247
258
|
end
|
248
259
|
end
|
249
260
|
|
250
|
-
if options[:boltdir] && options[:configfile]
|
251
|
-
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
252
|
-
end
|
253
|
-
|
254
|
-
if options[:noop] &&
|
255
|
-
!(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
|
256
|
-
raise Bolt::CLIError,
|
257
|
-
"Option '--noop' may only be specified when running a task or applying manifest code"
|
258
|
-
end
|
259
|
-
|
260
|
-
if options[:env_vars]
|
261
|
-
unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
|
262
|
-
raise Bolt::CLIError,
|
263
|
-
"Option '--env-var' may only be specified when running a command or script"
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
261
|
if options[:subcommand] == 'apply' && (options[:object] && options[:code])
|
268
262
|
raise Bolt::CLIError, "--execute is unsupported when specifying a manifest file"
|
269
263
|
end
|
@@ -286,6 +280,38 @@ module Bolt
|
|
286
280
|
raise Bolt::CLIError, "Must specify a plan name."
|
287
281
|
end
|
288
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[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
|
288
|
+
raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
|
289
|
+
"the project, run 'bolt module add #{options[:object]}'."
|
290
|
+
end
|
291
|
+
|
292
|
+
if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
|
293
|
+
!options[:leftovers].empty?
|
294
|
+
raise Bolt::CLIError,
|
295
|
+
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
296
|
+
end
|
297
|
+
|
298
|
+
if options[:boltdir] && options[:configfile]
|
299
|
+
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
300
|
+
end
|
301
|
+
|
302
|
+
if options[:noop] &&
|
303
|
+
!(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
|
304
|
+
raise Bolt::CLIError,
|
305
|
+
"Option '--noop' may only be specified when running a task or applying manifest code"
|
306
|
+
end
|
307
|
+
|
308
|
+
if options[:env_vars]
|
309
|
+
unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
|
310
|
+
raise Bolt::CLIError,
|
311
|
+
"Option '--env-var' may only be specified when running a command or script"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
289
315
|
if options.key?(:debug) && options.key?(:log)
|
290
316
|
raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
|
291
317
|
end
|
@@ -353,7 +379,7 @@ module Bolt
|
|
353
379
|
# Initialize inventory and targets. Errors here are better to catch early.
|
354
380
|
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
355
381
|
# options[:targets] will contain a resolved set of Target objects
|
356
|
-
unless %w[project puppetfile secret
|
382
|
+
unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
|
357
383
|
%w[convert new show].include?(options[:action])
|
358
384
|
update_targets(options)
|
359
385
|
end
|
@@ -379,7 +405,7 @@ module Bolt
|
|
379
405
|
inventory_version: inventory.version)
|
380
406
|
end
|
381
407
|
|
382
|
-
analytics.screen_view(screen, screen_view_fields)
|
408
|
+
analytics.screen_view(screen, **screen_view_fields)
|
383
409
|
|
384
410
|
case options[:action]
|
385
411
|
when 'show'
|
@@ -404,6 +430,8 @@ module Bolt
|
|
404
430
|
end
|
405
431
|
when 'group'
|
406
432
|
list_groups
|
433
|
+
when 'module'
|
434
|
+
list_modules
|
407
435
|
end
|
408
436
|
return 0
|
409
437
|
when 'show-modules'
|
@@ -432,9 +460,7 @@ module Bolt
|
|
432
460
|
when 'init'
|
433
461
|
code = initialize_project
|
434
462
|
when 'migrate'
|
435
|
-
|
436
|
-
path = config.project.path
|
437
|
-
code = Bolt::ProjectMigrate.new(path, outputter, inv).migrate_project
|
463
|
+
code = Bolt::ProjectMigrator.new(config, outputter).migrate
|
438
464
|
end
|
439
465
|
when 'plan'
|
440
466
|
case options[:action]
|
@@ -443,12 +469,25 @@ module Bolt
|
|
443
469
|
when 'run'
|
444
470
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
445
471
|
end
|
472
|
+
when 'module'
|
473
|
+
case options[:action]
|
474
|
+
when 'add'
|
475
|
+
code = add_project_module(options[:object], config.project)
|
476
|
+
when 'install'
|
477
|
+
code = install_project_modules(config.project, options[:force], options[:resolve])
|
478
|
+
when 'generate-types'
|
479
|
+
code = generate_types
|
480
|
+
end
|
446
481
|
when 'puppetfile'
|
447
482
|
case options[:action]
|
448
483
|
when 'generate-types'
|
449
484
|
code = generate_types
|
450
485
|
when 'install'
|
451
|
-
code = install_puppetfile(
|
486
|
+
code = install_puppetfile(
|
487
|
+
config.puppetfile_config,
|
488
|
+
config.puppetfile,
|
489
|
+
config.modulepath.first
|
490
|
+
)
|
452
491
|
end
|
453
492
|
when 'secret'
|
454
493
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -548,8 +587,24 @@ module Bolt
|
|
548
587
|
end
|
549
588
|
|
550
589
|
def list_targets
|
590
|
+
inventoryfile = config.inventoryfile || config.default_inventoryfile
|
591
|
+
|
592
|
+
# Retrieve the known group and target names. This needs to be done before
|
593
|
+
# updating targets, as that will add adhoc targets to the inventory.
|
594
|
+
known_names = inventory.target_names
|
595
|
+
|
551
596
|
update_targets(options)
|
552
|
-
|
597
|
+
|
598
|
+
inventory_targets, adhoc_targets = options[:targets].partition do |target|
|
599
|
+
known_names.include?(target.name)
|
600
|
+
end
|
601
|
+
|
602
|
+
target_list = {
|
603
|
+
inventory: inventory_targets,
|
604
|
+
adhoc: adhoc_targets
|
605
|
+
}
|
606
|
+
|
607
|
+
outputter.print_targets(target_list, inventoryfile)
|
553
608
|
end
|
554
609
|
|
555
610
|
def show_targets
|
@@ -578,10 +633,10 @@ module Bolt
|
|
578
633
|
message = <<~MESSAGE.chomp
|
579
634
|
Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
|
580
635
|
separated by double colons '::'.
|
581
|
-
|
636
|
+
|
582
637
|
Each name segment must begin with a lowercase letter, and may only include lowercase
|
583
638
|
letters, digits, and underscores.
|
584
|
-
|
639
|
+
|
585
640
|
Examples of valid plan names:
|
586
641
|
- #{config.project.name}
|
587
642
|
- #{config.project.name}::my_plan
|
@@ -798,36 +853,39 @@ module Bolt
|
|
798
853
|
old_config = project + 'bolt.yaml'
|
799
854
|
config = project + 'bolt-project.yaml'
|
800
855
|
puppetfile = project + 'Puppetfile'
|
801
|
-
|
856
|
+
moduledir = project + 'modules'
|
802
857
|
|
803
|
-
#
|
804
|
-
#
|
805
|
-
#
|
806
|
-
#
|
807
|
-
#
|
858
|
+
# Warn the user if the project directory already exists. We don't error
|
859
|
+
# here since users might not have installed any modules yet. If both
|
860
|
+
# bolt.yaml and bolt-project.yaml exist, this will just warn about
|
861
|
+
# bolt-project.yaml and subsequent Bolt actions will warn about both files
|
862
|
+
# existing.
|
863
|
+
if config.exist?
|
864
|
+
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
865
|
+
elsif old_config.exist?
|
866
|
+
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
867
|
+
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
868
|
+
end
|
869
|
+
|
870
|
+
# If modules were specified, first check if there is already a Puppetfile
|
871
|
+
# at the project directory, erroring if there is. If there is no
|
872
|
+
# Puppetfile, install the specified modules. The module installer will
|
873
|
+
# resolve dependencies, generate a Puppetfile, and install the modules.
|
808
874
|
if options[:modules]
|
809
875
|
if puppetfile.exist?
|
810
876
|
raise Bolt::CLIError,
|
811
|
-
"Found existing Puppetfile at #{puppetfile}, unable to initialize
|
812
|
-
"
|
813
|
-
else
|
814
|
-
puppetfile_specs = resolve_puppetfile_specs
|
877
|
+
"Found existing Puppetfile at #{puppetfile}, unable to initialize "\
|
878
|
+
"project with modules."
|
815
879
|
end
|
880
|
+
|
881
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
882
|
+
installer.install(options[:modules], puppetfile, moduledir)
|
816
883
|
end
|
817
884
|
|
818
|
-
#
|
819
|
-
#
|
820
|
-
#
|
821
|
-
|
822
|
-
# both files existing
|
823
|
-
if config.exist?
|
824
|
-
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
825
|
-
# This won't get called if bolt-project.yaml exists
|
826
|
-
elsif old_config.exist?
|
827
|
-
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
828
|
-
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
829
|
-
# Bless the project directory as a...wait for it...project
|
830
|
-
else
|
885
|
+
# If either bolt.yaml or bolt-project.yaml exist, the user has already
|
886
|
+
# been warned and we can just finish project creation. Otherwise, create a
|
887
|
+
# bolt-project.yaml with the project name in it.
|
888
|
+
unless config.exist? || old_config.exist?
|
831
889
|
begin
|
832
890
|
content = { 'name' => name }
|
833
891
|
File.write(config.to_path, content.to_yaml)
|
@@ -837,113 +895,91 @@ module Bolt
|
|
837
895
|
end
|
838
896
|
end
|
839
897
|
|
840
|
-
# Write the generated Puppetfile to the fancy new project
|
841
|
-
if puppetfile_specs
|
842
|
-
File.write(puppetfile, puppetfile_specs.join("\n"))
|
843
|
-
outputter.print_message "Successfully created Puppetfile at #{puppetfile}"
|
844
|
-
# Install the modules from our shiny new Puppetfile
|
845
|
-
if install_puppetfile({}, puppetfile, modulepath)
|
846
|
-
outputter.print_message "Successfully installed #{options[:modules].join(', ')}"
|
847
|
-
else
|
848
|
-
raise Bolt::CLIError, "Could not install #{options[:modules].join(', ')}"
|
849
|
-
end
|
850
|
-
end
|
851
|
-
|
852
898
|
0
|
853
899
|
end
|
854
900
|
|
855
|
-
#
|
856
|
-
#
|
857
|
-
def
|
858
|
-
|
859
|
-
|
860
|
-
# Build the document model from the module names, defaulting to the latest version of each module
|
861
|
-
model = PuppetfileResolver::Puppetfile::Document.new('')
|
862
|
-
options[:modules].each do |mod_name|
|
863
|
-
model.add_module(
|
864
|
-
PuppetfileResolver::Puppetfile::ForgeModule.new(mod_name).tap { |mod| mod.version = :latest }
|
865
|
-
)
|
866
|
-
end
|
867
|
-
|
868
|
-
# Make sure the Puppetfile model is valid
|
869
|
-
unless model.valid?
|
870
|
-
raise Bolt::ValidationError,
|
871
|
-
"Unable to resolve dependencies for #{options[:modules].join(', ')}"
|
872
|
-
end
|
873
|
-
|
874
|
-
# Create the resolver using the Puppetfile model. nil disables Puppet version restrictions.
|
875
|
-
resolver = PuppetfileResolver::Resolver.new(model, nil)
|
876
|
-
|
877
|
-
# Configure and resolve the dependency graph
|
878
|
-
result = resolver.resolve(
|
879
|
-
cache: nil,
|
880
|
-
ui: nil,
|
881
|
-
module_paths: [],
|
882
|
-
allow_missing_modules: true
|
883
|
-
)
|
901
|
+
# Installs modules declared in the project configuration file.
|
902
|
+
#
|
903
|
+
def install_project_modules(project, force, resolve)
|
904
|
+
assert_project_file(project)
|
884
905
|
|
885
|
-
|
886
|
-
|
887
|
-
|
906
|
+
unless project.modules
|
907
|
+
outputter.print_message "Project configuration file #{project.project_file} does not "\
|
908
|
+
"specify any module dependencies. Nothing to do."
|
909
|
+
return 0
|
888
910
|
end
|
889
911
|
|
890
|
-
|
891
|
-
titles = model.modules.each_with_object({}) do |mod, acc|
|
892
|
-
acc[mod.name] = mod.title
|
893
|
-
end
|
912
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
894
913
|
|
895
|
-
|
896
|
-
|
914
|
+
ok = installer.install(project.modules,
|
915
|
+
project.puppetfile,
|
916
|
+
project.managed_moduledir,
|
917
|
+
force: force,
|
918
|
+
resolve: resolve)
|
919
|
+
ok ? 0 : 1
|
920
|
+
end
|
897
921
|
|
898
|
-
|
899
|
-
|
900
|
-
|
922
|
+
# Adds a single module to the project.
|
923
|
+
#
|
924
|
+
def add_project_module(name, project)
|
925
|
+
assert_project_file(project)
|
926
|
+
|
927
|
+
modules = project.modules || []
|
928
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
929
|
+
|
930
|
+
ok = installer.add(name,
|
931
|
+
modules,
|
932
|
+
project.puppetfile,
|
933
|
+
project.managed_moduledir,
|
934
|
+
project.project_file)
|
935
|
+
ok ? 0 : 1
|
936
|
+
end
|
901
937
|
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
938
|
+
# Asserts that there is a project configuration file.
|
939
|
+
#
|
940
|
+
def assert_project_file(project)
|
941
|
+
unless project.project_file?
|
942
|
+
msg = if project.config_file.exist?
|
943
|
+
"Detected Bolt configuration file #{project.config_file}, unable to install "\
|
944
|
+
"modules. To update to a project configuration file, run 'bolt project migrate'."
|
945
|
+
else
|
946
|
+
"Could not find project configuration file #{project.project_file}, unable "\
|
947
|
+
"to install modules. To create a Bolt project, run 'bolt project init'."
|
948
|
+
end
|
906
949
|
|
907
|
-
|
908
|
-
spec_graph.values.map do |spec|
|
909
|
-
"mod '#{spec.owner}-#{spec.name}', '#{spec.version}'"
|
950
|
+
raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
|
910
951
|
end
|
911
952
|
end
|
912
953
|
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
puppetfile: puppetfile.to_s,
|
922
|
-
moduledir: moduledir
|
923
|
-
}
|
924
|
-
|
925
|
-
settings = R10K::Settings.global_settings.evaluate(config)
|
926
|
-
R10K::Initializers::GlobalInitializer.new(settings).call
|
927
|
-
install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil)
|
928
|
-
|
929
|
-
# Override the r10k logger with a proxy to our own logger
|
930
|
-
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
931
|
-
|
932
|
-
ok = install_action.call
|
933
|
-
outputter.print_puppetfile_result(ok, puppetfile, moduledir)
|
934
|
-
# Automatically generate types after installing modules
|
935
|
-
pal.generate_types
|
954
|
+
# Loads a Puppetfile and installs its modules.
|
955
|
+
#
|
956
|
+
def install_puppetfile(config, puppetfile, moduledir)
|
957
|
+
outputter.print_message("Installing modules from Puppetfile")
|
958
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
959
|
+
ok = installer.install_puppetfile(puppetfile, moduledir, config)
|
960
|
+
ok ? 0 : 1
|
961
|
+
end
|
936
962
|
|
937
|
-
|
938
|
-
|
939
|
-
|
963
|
+
# Raises an error if the 'puppetfile install' command is deprecated due to
|
964
|
+
# modules being configured.
|
965
|
+
#
|
966
|
+
def assert_puppetfile_or_module_command(modules)
|
967
|
+
if modules && options[:subcommand] == 'puppetfile'
|
968
|
+
raise Bolt::CLIError,
|
969
|
+
"Unable to use command 'bolt puppetfile #{options[:action]}' when "\
|
970
|
+
"'modules' is configured in bolt-project.yaml. Use the 'module' command "\
|
971
|
+
"instead. For a list of available actions for the 'module' command, run "\
|
972
|
+
"'bolt module --help'."
|
973
|
+
elsif modules.nil? && options[:subcommand] == 'module'
|
974
|
+
raise Bolt::CLIError,
|
975
|
+
"Unable to use command 'bolt module #{options[:action]}'. To use "\
|
976
|
+
"this command, update your project configuration to manage module "\
|
977
|
+
"dependencies."
|
940
978
|
end
|
941
|
-
rescue R10K::Error => e
|
942
|
-
raise PuppetfileError, e
|
943
979
|
end
|
944
980
|
|
945
981
|
def pal
|
946
|
-
@pal ||= Bolt::PAL.new(config.modulepath,
|
982
|
+
@pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
|
947
983
|
config.hiera_config,
|
948
984
|
config.project.resource_types,
|
949
985
|
config.compile_concurrency,
|
@@ -955,17 +991,17 @@ module Bolt
|
|
955
991
|
# Collects the list of Bolt guides and maps them to their topics.
|
956
992
|
def guides
|
957
993
|
@guides ||= begin
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
994
|
+
root_path = File.expand_path(File.join(__dir__, '..', '..', 'guides'))
|
995
|
+
files = Dir.children(root_path).sort
|
996
|
+
|
997
|
+
files.each_with_object({}) do |file, guides|
|
998
|
+
next if file !~ /\.txt\z/
|
999
|
+
topic = File.basename(file, '.txt')
|
1000
|
+
guides[topic] = File.join(root_path, file)
|
1001
|
+
end
|
1002
|
+
rescue SystemCallError => e
|
1003
|
+
raise Bolt::FileError.new("#{e.message}: unable to load guides directory", root_path)
|
1004
|
+
end
|
969
1005
|
end
|
970
1006
|
|
971
1007
|
# Display the list of available Bolt guides.
|
@@ -1016,10 +1052,10 @@ module Bolt
|
|
1016
1052
|
|
1017
1053
|
def analytics
|
1018
1054
|
@analytics ||= begin
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1055
|
+
client = Bolt::Analytics.build_client
|
1056
|
+
client.bundled_content = bundled_content
|
1057
|
+
client
|
1058
|
+
end
|
1023
1059
|
end
|
1024
1060
|
|
1025
1061
|
def bundled_content
|
@@ -1042,7 +1078,7 @@ module Bolt
|
|
1042
1078
|
'Task' => [],
|
1043
1079
|
'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
|
1044
1080
|
if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
|
1045
|
-
default_content = Bolt::PAL.new([], nil, nil)
|
1081
|
+
default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
|
1046
1082
|
content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
|
1047
1083
|
col << iter&.first
|
1048
1084
|
end
|
@@ -1057,7 +1093,7 @@ module Bolt
|
|
1057
1093
|
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
1058
1094
|
# package installs include modules listed in the Bolt repo Puppetfile
|
1059
1095
|
def incomplete_install?
|
1060
|
-
(Dir.children(Bolt::
|
1096
|
+
(Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
|
1061
1097
|
end
|
1062
1098
|
|
1063
1099
|
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|