bolt 2.27.0 → 2.32.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/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/applicator.rb +14 -14
- data/lib/bolt/bolt_option_parser.rb +74 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +178 -127
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +38 -9
- 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 +197 -0
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -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/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
- data/lib/bolt/outputter.rb +2 -45
- data/lib/bolt/outputter/human.rb +78 -18
- data/lib/bolt/outputter/json.rb +22 -7
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +29 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +32 -22
- 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 +200 -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/file_cache.rb +11 -11
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +93 -13
- 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 +30 -11
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt/puppetfile.rb +0 -160
- data/lib/bolt/puppetfile/module.rb +0 -66
- 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: 5b010e9146d3269d88005be58db6b788f57ea3046f96f28756641b0c7266eec2
|
4
|
+
data.tar.gz: ce8d15031143acabc664a7025ef004c69211c25316f9691783388ba74e61dec9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b45657eb2b985f8e97c59ff4603ad049fd17ee6585292463a57bde3b5c7ca55451883b5e88cb0f61e55c05f1d5415cbde3874320ae446124d646a3d3e8ffa812
|
7
|
+
data.tar.gz: 673e3f3310bf4f22602153bf8300851fa3a832db8e73abc9352874bea7c0b1fd50a5baa42d18474885caea7ec9fad4aa50c25f44aa27974097e10ea03b31e07e
|
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.2.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
|
@@ -26,8 +26,51 @@ Puppet::Functions.create_function(:'out::message') do
|
|
26
26
|
# Send Analytics Report
|
27
27
|
executor.report_function_call(self.class.name)
|
28
28
|
|
29
|
-
executor.publish_event(type: :message, message: message)
|
29
|
+
executor.publish_event(type: :message, message: stringify(message))
|
30
30
|
|
31
31
|
nil
|
32
32
|
end
|
33
|
+
|
34
|
+
def stringify(message)
|
35
|
+
formatted = format_message(message)
|
36
|
+
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
37
|
+
::JSON.pretty_generate(formatted)
|
38
|
+
else
|
39
|
+
formatted
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_message(message)
|
44
|
+
case message
|
45
|
+
when Array
|
46
|
+
message.map { |item| format_message(item) }
|
47
|
+
when Bolt::ApplyResult
|
48
|
+
format_apply_result(message)
|
49
|
+
when Bolt::Result, Bolt::ResultSet
|
50
|
+
# This is equivalent to to_s, but formattable
|
51
|
+
message.to_data
|
52
|
+
when Bolt::RunFailure
|
53
|
+
formatted_resultset = message.result_set.to_data
|
54
|
+
message.to_h.merge('result_set' => formatted_resultset)
|
55
|
+
when Hash
|
56
|
+
message.each_with_object({}) do |(k, v), h|
|
57
|
+
h[format_message(k)] = format_message(v)
|
58
|
+
end
|
59
|
+
when Integer, Float, NilClass
|
60
|
+
message
|
61
|
+
else
|
62
|
+
message.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def format_apply_result(result)
|
67
|
+
logs = result.resource_logs&.map do |log|
|
68
|
+
# Omit low-level info/debug messages
|
69
|
+
next if %w[info debug].include?(log['level'])
|
70
|
+
indent(2, format_log(log))
|
71
|
+
end
|
72
|
+
hash = result.to_data
|
73
|
+
hash['logs'] = logs unless logs.empty?
|
74
|
+
hash
|
75
|
+
end
|
33
76
|
end
|
@@ -9,11 +9,14 @@ Puppet::Functions.create_function(:prompt) do
|
|
9
9
|
# @param prompt The prompt to display.
|
10
10
|
# @param options A hash of additional options.
|
11
11
|
# @option options [Boolean] sensitive Disable echo back and mark the response as sensitive.
|
12
|
+
# The returned value will be wrapped by the `Sensitive` data type. To access the raw
|
13
|
+
# value, use the `unwrap` function (i.e. `$sensitive_value.unwrap`).
|
12
14
|
# @return The response to the prompt.
|
13
15
|
# @example Prompt the user if plan execution should continue
|
14
16
|
# $response = prompt('Continue executing plan? [Y\N]')
|
15
17
|
# @example Prompt the user for sensitive information
|
16
18
|
# $password = prompt('Enter your password', 'sensitive' => true)
|
19
|
+
# out::message("Password is: ${password.unwrap}")
|
17
20
|
dispatch :prompt do
|
18
21
|
param 'String', :prompt
|
19
22
|
optional_param 'Hash[String[1], Any]', :options
|
data/guides/module.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
TOPIC
|
2
|
+
module
|
3
|
+
|
4
|
+
DESCRIPTION
|
5
|
+
Modules are shareable, reusable packages of Puppet content. They can include
|
6
|
+
tasks, plans, functions, and other types of content that you can use in your
|
7
|
+
project. You can download and install modules to your project from the
|
8
|
+
Puppet Forge or write your own modules. Bolt also ships with several helpful
|
9
|
+
modules pre-installed that are available to all of your projects.
|
10
|
+
|
11
|
+
Bolt makes it easy to manage the modules that your project depends on. You
|
12
|
+
can use Bolt commands to install a project's modules, add new modules to a
|
13
|
+
project, and view the modules that are available to the project.
|
14
|
+
|
15
|
+
To learn more about managing modules in a project, see the documentation.
|
16
|
+
To learn how modules are loaded by Bolt, see the 'modulepath' guide.
|
17
|
+
|
18
|
+
DOCUMENTATION
|
19
|
+
https://pup.pt/bolt-modules
|
@@ -0,0 +1,25 @@
|
|
1
|
+
TOPIC
|
2
|
+
modulepath
|
3
|
+
|
4
|
+
DESCRIPTION
|
5
|
+
The modulepath is an ordered list of directories that Bolt loads modules
|
6
|
+
from. When Bolt runs a command, it automatically loads modules from the
|
7
|
+
modulepath.
|
8
|
+
|
9
|
+
While Bolt has a default modulepath, you can also configure your own
|
10
|
+
modulepath, which can include directories within the project or directories
|
11
|
+
elsewhere on your system. Regardless of whether your project uses a default
|
12
|
+
or configured modulepath, Bolt automatically adds directories to the
|
13
|
+
modulepath. This includes modules containing core Bolt content, which is
|
14
|
+
added to the beginning of the modulepath, and bundled content, which is
|
15
|
+
added to the end of the modulepath.
|
16
|
+
|
17
|
+
Modules loaded from a directory listed earlier in the modulepath take
|
18
|
+
precedence over modules with the same name loaded from a directory later in
|
19
|
+
the modulepath. Bolt will not warn or error when two modules share a name
|
20
|
+
and instead will ignore modules with a lower precedence.
|
21
|
+
|
22
|
+
To learn more about modules, see the 'module' guide.
|
23
|
+
|
24
|
+
DOCUMENTATION
|
25
|
+
https://pup.pt/bolt-project-reference#modulepath
|
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
|
@@ -99,11 +99,11 @@ module Bolt
|
|
99
99
|
# see what happened
|
100
100
|
print_logs = stat.success?
|
101
101
|
result = begin
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
JSON.parse(out)
|
103
|
+
rescue JSON::ParserError
|
104
|
+
print_logs = true
|
105
|
+
{ 'message' => "Something's gone terribly wrong! STDERR is logged." }
|
106
|
+
end
|
107
107
|
|
108
108
|
# Any messages logged by Puppet will be on stderr as JSON hashes, so we
|
109
109
|
# parse those and store them here. Any message on stderr that is not
|
@@ -66,9 +66,18 @@ module Bolt
|
|
66
66
|
banner: GUIDE_HELP }
|
67
67
|
when 'module'
|
68
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 }
|
69
75
|
when 'install'
|
70
|
-
{ flags: OPTIONS[:global] + %w[configfile force project],
|
76
|
+
{ flags: OPTIONS[:global] + %w[configfile force project resolve],
|
71
77
|
banner: MODULE_INSTALL_HELP }
|
78
|
+
when 'show'
|
79
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
80
|
+
banner: MODULE_SHOW_HELP }
|
72
81
|
else
|
73
82
|
{ flags: OPTIONS[:global],
|
74
83
|
banner: MODULE_HELP }
|
@@ -178,6 +187,7 @@ module Bolt
|
|
178
187
|
group Show the list of groups in the inventory
|
179
188
|
guide View guides for Bolt concepts and features
|
180
189
|
inventory Show the list of targets an action would run on
|
190
|
+
module Manage Bolt project modules
|
181
191
|
plan Convert, create, show, and run Bolt plans
|
182
192
|
project Create and migrate Bolt projects
|
183
193
|
puppetfile Install and list modules and generate type references
|
@@ -358,10 +368,48 @@ module Bolt
|
|
358
368
|
bolt module <action> [options]
|
359
369
|
|
360
370
|
DESCRIPTION
|
361
|
-
|
371
|
+
Manage Bolt project modules
|
372
|
+
|
373
|
+
The module command is only supported when a project is configured
|
374
|
+
with the 'modules' key.
|
362
375
|
|
363
376
|
ACTIONS
|
364
|
-
|
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.
|
365
413
|
HELP
|
366
414
|
|
367
415
|
MODULE_INSTALL_HELP = <<~HELP
|
@@ -379,6 +427,20 @@ module Bolt
|
|
379
427
|
generate a Puppetfile, and install the modules.
|
380
428
|
HELP
|
381
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
|
+
|
382
444
|
PLAN_HELP = <<~HELP
|
383
445
|
NAME
|
384
446
|
plan
|
@@ -716,7 +778,7 @@ module Bolt
|
|
716
778
|
'For SSH, port defaults to `22`',
|
717
779
|
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets|
|
718
780
|
@options[:targets] ||= []
|
719
|
-
@options[:targets] << get_arg_input(targets)
|
781
|
+
@options[:targets] << Bolt::Util.get_arg_input(targets)
|
720
782
|
end
|
721
783
|
define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
722
784
|
@options[:query] = query
|
@@ -876,6 +938,13 @@ module Bolt
|
|
876
938
|
@options[:tmpdir] = tmpdir
|
877
939
|
end
|
878
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
|
+
|
879
948
|
separator "\nDISPLAY OPTIONS"
|
880
949
|
define('--filter FILTER', 'Filter tasks and plans by a matching substring') do |filter|
|
881
950
|
unless /^[a-z0-9_:]+$/.match(filter)
|
@@ -955,27 +1024,10 @@ module Bolt
|
|
955
1024
|
end
|
956
1025
|
|
957
1026
|
def parse_params(params)
|
958
|
-
json = get_arg_input(params)
|
1027
|
+
json = Bolt::Util.get_arg_input(params)
|
959
1028
|
JSON.parse(json)
|
960
1029
|
rescue JSON::ParserError => e
|
961
1030
|
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{e}"
|
962
1031
|
end
|
963
|
-
|
964
|
-
def get_arg_input(value)
|
965
|
-
if value.start_with?('@')
|
966
|
-
file = value.sub(/^@/, '')
|
967
|
-
read_arg_file(file)
|
968
|
-
elsif value == '-'
|
969
|
-
$stdin.read
|
970
|
-
else
|
971
|
-
value
|
972
|
-
end
|
973
|
-
end
|
974
|
-
|
975
|
-
def read_arg_file(file)
|
976
|
-
File.read(File.expand_path(file))
|
977
|
-
rescue StandardError => e
|
978
|
-
raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
|
979
|
-
end
|
980
1032
|
end
|
981
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,28 +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
|
-
|
44
|
-
|
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
|
45
48
|
|
46
49
|
attr_reader :config, :options
|
47
50
|
|
@@ -99,6 +102,10 @@ module Bolt
|
|
99
102
|
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
100
103
|
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
101
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
|
+
|
102
109
|
# Update the parser for the subcommand (or lack thereof)
|
103
110
|
parser.update
|
104
111
|
puts parser.help
|
@@ -107,6 +114,11 @@ module Bolt
|
|
107
114
|
|
108
115
|
options[:object] = remaining.shift
|
109
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
|
+
|
110
122
|
# Only parse task_options for task or plan
|
111
123
|
if %w[task plan].include?(options[:subcommand])
|
112
124
|
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
@@ -184,6 +196,10 @@ module Bolt
|
|
184
196
|
|
185
197
|
warn_inventory_overrides_cli(options)
|
186
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
|
+
|
187
203
|
options
|
188
204
|
rescue Bolt::Error => e
|
189
205
|
outputter.fatal_error(e)
|
@@ -211,14 +227,10 @@ module Bolt
|
|
211
227
|
end
|
212
228
|
|
213
229
|
def validate(options)
|
214
|
-
|
215
|
-
commands = COMMANDS.dup
|
216
|
-
commands.delete('module') unless ENV['BOLT_MODULE_FEATURE']
|
217
|
-
|
218
|
-
unless commands.include?(options[:subcommand])
|
230
|
+
unless COMMANDS.include?(options[:subcommand])
|
219
231
|
raise Bolt::CLIError,
|
220
232
|
"Expected subcommand '#{options[:subcommand]}' to be one of " \
|
221
|
-
"#{
|
233
|
+
"#{COMMANDS.keys.join(', ')}"
|
222
234
|
end
|
223
235
|
|
224
236
|
actions = COMMANDS[options[:subcommand]]
|
@@ -235,12 +247,6 @@ module Bolt
|
|
235
247
|
end
|
236
248
|
end
|
237
249
|
|
238
|
-
if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
|
239
|
-
!options[:leftovers].empty?
|
240
|
-
raise Bolt::CLIError,
|
241
|
-
"Unknown argument(s) #{options[:leftovers].join(', ')}"
|
242
|
-
end
|
243
|
-
|
244
250
|
if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
|
245
251
|
if options[:object].nil?
|
246
252
|
raise Bolt::CLIError, "Must specify a #{options[:subcommand]} to run"
|
@@ -252,23 +258,6 @@ module Bolt
|
|
252
258
|
end
|
253
259
|
end
|
254
260
|
|
255
|
-
if options[:boltdir] && options[:configfile]
|
256
|
-
raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
|
257
|
-
end
|
258
|
-
|
259
|
-
if options[:noop] &&
|
260
|
-
!(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
|
261
|
-
raise Bolt::CLIError,
|
262
|
-
"Option '--noop' may only be specified when running a task or applying manifest code"
|
263
|
-
end
|
264
|
-
|
265
|
-
if options[:env_vars]
|
266
|
-
unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
|
267
|
-
raise Bolt::CLIError,
|
268
|
-
"Option '--env-var' may only be specified when running a command or script"
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
261
|
if options[:subcommand] == 'apply' && (options[:object] && options[:code])
|
273
262
|
raise Bolt::CLIError, "--execute is unsupported when specifying a manifest file"
|
274
263
|
end
|
@@ -291,6 +280,38 @@ module Bolt
|
|
291
280
|
raise Bolt::CLIError, "Must specify a plan name."
|
292
281
|
end
|
293
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
|
+
|
294
315
|
if options.key?(:debug) && options.key?(:log)
|
295
316
|
raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
|
296
317
|
end
|
@@ -384,7 +405,7 @@ module Bolt
|
|
384
405
|
inventory_version: inventory.version)
|
385
406
|
end
|
386
407
|
|
387
|
-
analytics.screen_view(screen, screen_view_fields)
|
408
|
+
analytics.screen_view(screen, **screen_view_fields)
|
388
409
|
|
389
410
|
case options[:action]
|
390
411
|
when 'show'
|
@@ -409,6 +430,8 @@ module Bolt
|
|
409
430
|
end
|
410
431
|
when 'group'
|
411
432
|
list_groups
|
433
|
+
when 'module'
|
434
|
+
list_modules
|
412
435
|
end
|
413
436
|
return 0
|
414
437
|
when 'show-modules'
|
@@ -437,9 +460,7 @@ module Bolt
|
|
437
460
|
when 'init'
|
438
461
|
code = initialize_project
|
439
462
|
when 'migrate'
|
440
|
-
|
441
|
-
path = config.project.path
|
442
|
-
code = Bolt::ProjectMigrate.new(path, outputter, inv).migrate_project
|
463
|
+
code = Bolt::ProjectMigrator.new(config, outputter).migrate
|
443
464
|
end
|
444
465
|
when 'plan'
|
445
466
|
case options[:action]
|
@@ -450,15 +471,23 @@ module Bolt
|
|
450
471
|
end
|
451
472
|
when 'module'
|
452
473
|
case options[:action]
|
474
|
+
when 'add'
|
475
|
+
code = add_project_module(options[:object], config.project)
|
453
476
|
when 'install'
|
454
|
-
code = install_project_modules
|
477
|
+
code = install_project_modules(config.project, options[:force], options[:resolve])
|
478
|
+
when 'generate-types'
|
479
|
+
code = generate_types
|
455
480
|
end
|
456
481
|
when 'puppetfile'
|
457
482
|
case options[:action]
|
458
483
|
when 'generate-types'
|
459
484
|
code = generate_types
|
460
485
|
when 'install'
|
461
|
-
code = install_puppetfile(
|
486
|
+
code = install_puppetfile(
|
487
|
+
config.puppetfile_config,
|
488
|
+
config.puppetfile,
|
489
|
+
config.modulepath.first
|
490
|
+
)
|
462
491
|
end
|
463
492
|
when 'secret'
|
464
493
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -558,8 +587,24 @@ module Bolt
|
|
558
587
|
end
|
559
588
|
|
560
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
|
+
|
561
596
|
update_targets(options)
|
562
|
-
|
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)
|
563
608
|
end
|
564
609
|
|
565
610
|
def show_targets
|
@@ -588,10 +633,10 @@ module Bolt
|
|
588
633
|
message = <<~MESSAGE.chomp
|
589
634
|
Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
|
590
635
|
separated by double colons '::'.
|
591
|
-
|
636
|
+
|
592
637
|
Each name segment must begin with a lowercase letter, and may only include lowercase
|
593
638
|
letters, digits, and underscores.
|
594
|
-
|
639
|
+
|
595
640
|
Examples of valid plan names:
|
596
641
|
- #{config.project.name}
|
597
642
|
- #{config.project.name}::my_plan
|
@@ -833,7 +878,8 @@ module Bolt
|
|
833
878
|
"project with modules."
|
834
879
|
end
|
835
880
|
|
836
|
-
|
881
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
882
|
+
installer.install(options[:modules], puppetfile, moduledir)
|
837
883
|
end
|
838
884
|
|
839
885
|
# If either bolt.yaml or bolt-project.yaml exist, the user has already
|
@@ -854,81 +900,86 @@ module Bolt
|
|
854
900
|
|
855
901
|
# Installs modules declared in the project configuration file.
|
856
902
|
#
|
857
|
-
def install_project_modules
|
858
|
-
|
859
|
-
|
860
|
-
|
903
|
+
def install_project_modules(project, force, resolve)
|
904
|
+
assert_project_file(project)
|
905
|
+
|
906
|
+
unless project.modules
|
907
|
+
outputter.print_message "Project configuration file #{project.project_file} does not "\
|
908
|
+
"specify any module dependencies. Nothing to do."
|
861
909
|
return 0
|
862
910
|
end
|
863
911
|
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
912
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
913
|
+
|
914
|
+
ok = installer.install(project.modules,
|
915
|
+
project.puppetfile,
|
916
|
+
project.managed_moduledir,
|
917
|
+
force: force,
|
918
|
+
resolve: resolve)
|
919
|
+
ok ? 0 : 1
|
870
920
|
end
|
871
921
|
|
872
|
-
#
|
922
|
+
# Adds a single module to the project.
|
873
923
|
#
|
874
|
-
def
|
875
|
-
|
876
|
-
require 'bolt/puppetfile/installer'
|
877
|
-
|
878
|
-
puppetfile = Bolt::Puppetfile.new(modules)
|
879
|
-
|
880
|
-
# If the Puppetfile exists, check if it includes specs for each declared
|
881
|
-
# module, erroring if there are any missing. Otherwise, resolve the
|
882
|
-
# module dependencies and write a new Puppetfile. Users can forcibly
|
883
|
-
# overwrite an existing Puppetfile with the '--force' option.
|
884
|
-
if puppetfile_path.exist? && !options[:force]
|
885
|
-
outputter.print_message "Parsing existing Puppetfile at #{puppetfile_path}"
|
886
|
-
existing = Bolt::Puppetfile.parse(puppetfile_path)
|
887
|
-
|
888
|
-
unless existing.modules.superset? puppetfile.modules
|
889
|
-
missing_modules = puppetfile.modules - existing.modules
|
890
|
-
|
891
|
-
raise Bolt::Error.new(
|
892
|
-
"Puppetfile #{puppetfile_path} is missing specifications for modules: "\
|
893
|
-
"#{missing_modules.map(&:title).join(', ')}. This may not be a Puppetfile "\
|
894
|
-
"managed by Bolt. To forcibly overwrite the Puppetfile, run with the "\
|
895
|
-
"'--force' option.",
|
896
|
-
'bolt/missing-module-specs'
|
897
|
-
)
|
898
|
-
end
|
899
|
-
else
|
900
|
-
outputter.print_message "Resolving module dependencies, this may take a moment"
|
901
|
-
puppetfile.resolve
|
902
|
-
outputter.print_message "Writing Puppetfile at #{puppetfile_path}"
|
903
|
-
puppetfile.write(puppetfile_path, force: true)
|
904
|
-
end
|
905
|
-
|
906
|
-
outputter.print_message "Syncing modules from #{puppetfile_path} to #{moduledir}"
|
907
|
-
ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile_path, moduledir)
|
924
|
+
def add_project_module(name, project)
|
925
|
+
assert_project_file(project)
|
908
926
|
|
909
|
-
|
910
|
-
pal
|
927
|
+
modules = project.modules || []
|
928
|
+
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
911
929
|
|
912
|
-
|
930
|
+
ok = installer.add(name,
|
931
|
+
modules,
|
932
|
+
project.puppetfile,
|
933
|
+
project.managed_moduledir,
|
934
|
+
project.project_file)
|
913
935
|
ok ? 0 : 1
|
914
936
|
end
|
915
937
|
|
916
|
-
#
|
938
|
+
# Asserts that there is a project configuration file.
|
917
939
|
#
|
918
|
-
def
|
919
|
-
|
920
|
-
|
921
|
-
|
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
|
922
949
|
|
923
|
-
|
924
|
-
|
950
|
+
raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
|
951
|
+
end
|
952
|
+
end
|
925
953
|
|
926
|
-
|
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)
|
927
960
|
ok ? 0 : 1
|
928
961
|
end
|
929
962
|
|
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."
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
930
981
|
def pal
|
931
|
-
@pal ||= Bolt::PAL.new(config.modulepath,
|
982
|
+
@pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
|
932
983
|
config.hiera_config,
|
933
984
|
config.project.resource_types,
|
934
985
|
config.compile_concurrency,
|
@@ -940,17 +991,17 @@ module Bolt
|
|
940
991
|
# Collects the list of Bolt guides and maps them to their topics.
|
941
992
|
def guides
|
942
993
|
@guides ||= begin
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
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
|
954
1005
|
end
|
955
1006
|
|
956
1007
|
# Display the list of available Bolt guides.
|
@@ -1001,10 +1052,10 @@ module Bolt
|
|
1001
1052
|
|
1002
1053
|
def analytics
|
1003
1054
|
@analytics ||= begin
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1055
|
+
client = Bolt::Analytics.build_client
|
1056
|
+
client.bundled_content = bundled_content
|
1057
|
+
client
|
1058
|
+
end
|
1008
1059
|
end
|
1009
1060
|
|
1010
1061
|
def bundled_content
|
@@ -1027,7 +1078,7 @@ module Bolt
|
|
1027
1078
|
'Task' => [],
|
1028
1079
|
'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
|
1029
1080
|
if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
|
1030
|
-
default_content = Bolt::PAL.new([], nil, nil)
|
1081
|
+
default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
|
1031
1082
|
content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
|
1032
1083
|
col << iter&.first
|
1033
1084
|
end
|
@@ -1042,7 +1093,7 @@ module Bolt
|
|
1042
1093
|
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
1043
1094
|
# package installs include modules listed in the Bolt repo Puppetfile
|
1044
1095
|
def incomplete_install?
|
1045
|
-
(Dir.children(Bolt::
|
1096
|
+
(Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
|
1046
1097
|
end
|
1047
1098
|
|
1048
1099
|
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|