bolt 2.30.0 → 2.34.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 +12 -12
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- 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/logging.txt +18 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/bolt_option_parser.rb +6 -1
- data/lib/bolt/cli.rb +82 -142
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +31 -13
- data/lib/bolt/config/transport/options.rb +2 -2
- data/lib/bolt/error.rb +13 -3
- data/lib/bolt/executor.rb +24 -12
- data/lib/bolt/inventory.rb +10 -9
- data/lib/bolt/inventory/group.rb +2 -1
- data/lib/bolt/module_installer.rb +117 -91
- 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 +85 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +179 -0
- data/lib/bolt/outputter.rb +0 -47
- data/lib/bolt/outputter/human.rb +46 -16
- data/lib/bolt/outputter/json.rb +17 -8
- data/lib/bolt/pal.rb +52 -40
- data/lib/bolt/pal/yaml_plan.rb +4 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
- data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
- data/lib/bolt/plan_creator.rb +160 -0
- data/lib/bolt/plugin.rb +2 -2
- data/lib/bolt/project.rb +6 -11
- data/lib/bolt/project_migrator.rb +1 -1
- data/lib/bolt/project_migrator/base.rb +2 -2
- data/lib/bolt/project_migrator/config.rb +5 -4
- data/lib/bolt/project_migrator/inventory.rb +3 -3
- data/lib/bolt/project_migrator/modules.rb +23 -21
- data/lib/bolt/puppetdb/config.rb +5 -5
- data/lib/bolt/result.rb +23 -11
- data/lib/bolt/shell/bash.rb +14 -8
- data/lib/bolt/shell/powershell.rb +12 -7
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/base.rb +18 -18
- data/lib/bolt/transport/docker.rb +23 -6
- data/lib/bolt/transport/orch.rb +26 -17
- data/lib/bolt/transport/remote.rb +3 -3
- data/lib/bolt/transport/simple.rb +6 -6
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +5 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +2 -0
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +92 -12
- data/lib/bolt_spec/bolt_context.rb +4 -2
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +5 -5
- data/lib/bolt_spec/run.rb +1 -1
- metadata +24 -9
- data/lib/bolt/puppetfile.rb +0 -142
- data/lib/bolt/puppetfile/module.rb +0 -90
- data/lib/bolt_server/pe/pal.rb +0 -67
- data/modules/secure_env_vars/plans/init.pp +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d33993b3430c33f173a3a2d07220d175d4545c1992e9bed3bd75a7be7bae9be
|
4
|
+
data.tar.gz: 68fe674fd2d8ec196149938668d68b3e4c2becd3a97639c49ee85a637bb2e5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89e8fd86df6d1427fa61df4e54b0a1973ec607bcd46744a5fa4f0316f217ae02fa0b72428527a6d3ef15303f9fae371c62954d5b53a600d4d71589a3b2108656
|
7
|
+
data.tar.gz: e40ee91802e4dcb6a5d542f131800b28f4916ad2245837b34f26bcef128d99485c3d96ab02e498033476fe01fe6e5740be74b985370fd390ded7b8b0683b0e04
|
data/Puppetfile
CHANGED
@@ -6,36 +6,37 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
|
|
6
6
|
|
7
7
|
# Core modules used by 'apply'
|
8
8
|
mod 'puppetlabs-service', '1.3.0'
|
9
|
-
mod 'puppetlabs-puppet_agent', '4.
|
10
|
-
mod 'puppetlabs-facts', '1.
|
9
|
+
mod 'puppetlabs-puppet_agent', '4.2.0'
|
10
|
+
mod 'puppetlabs-facts', '1.2.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', '
|
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.2.0'
|
17
|
+
mod 'puppetlabs-zfs_core', '1.2.0'
|
18
|
+
mod 'puppetlabs-cron_core', '1.0.5'
|
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
34
|
mod 'puppetlabs-aws_inventory', '0.5.2'
|
35
35
|
mod 'puppetlabs-azure_inventory', '0.4.1'
|
36
36
|
mod 'puppetlabs-gcloud_inventory', '0.1.3'
|
37
|
-
mod 'puppetlabs-http_request', '0.
|
37
|
+
mod 'puppetlabs-http_request', '0.2.0'
|
38
38
|
mod 'puppetlabs-pkcs7', '0.1.1'
|
39
|
+
mod 'puppetlabs-secure_env_vars', '0.1.0'
|
39
40
|
mod 'puppetlabs-terraform', '0.5.0'
|
40
41
|
mod 'puppetlabs-vault', '0.3.0'
|
41
42
|
mod 'puppetlabs-yaml', '0.2.0'
|
@@ -44,4 +45,3 @@ mod 'puppetlabs-yaml', '0.2.0'
|
|
44
45
|
mod 'canary', local: true
|
45
46
|
mod 'aggregate', local: true
|
46
47
|
mod 'puppetdb_fact', local: true
|
47
|
-
mod 'secure_env_vars', local: true
|
@@ -112,7 +112,7 @@ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFun
|
|
112
112
|
call_function('debug', "Simulating file download of '#{source}' - no targets given - no action taken")
|
113
113
|
r = Bolt::ResultSet.new([])
|
114
114
|
else
|
115
|
-
r = executor.download_file(targets, source, destination, options)
|
115
|
+
r = executor.download_file(targets, source, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
|
116
116
|
end
|
117
117
|
|
118
118
|
if !r.ok && !options[:catch_errors]
|
@@ -3,6 +3,12 @@
|
|
3
3
|
require 'bolt/error'
|
4
4
|
|
5
5
|
# Returns the facts hash for a target.
|
6
|
+
#
|
7
|
+
# Using the `facts` function does not automatically collect facts for a target,
|
8
|
+
# and will only return facts that are currently set in the inventory. To collect
|
9
|
+
# facts from a target and set them in the inventory, run the
|
10
|
+
# [facts](writing_plans.md#collect-facts-from-targets) plan or
|
11
|
+
# [puppetdb_fact](writing_plans.md#collect-facts-from-puppetdb) plan.
|
6
12
|
Puppet::Functions.create_function(:facts) do
|
7
13
|
# @param target A target.
|
8
14
|
# @return The target's facts.
|
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'bolt/error'
|
4
4
|
|
5
|
-
# Makes a query to
|
5
|
+
# Makes a query to [puppetdb](https://puppet.com/docs/puppetdb/latest/index.html)
|
6
6
|
# using Bolt's PuppetDB client.
|
7
7
|
Puppet::Functions.create_function(:puppetdb_query) do
|
8
8
|
# rubocop:disable Layout/LineLength
|
9
9
|
# @param query A PQL query.
|
10
|
-
#
|
10
|
+
# Learn more about [Puppet's query language](https://puppet.com/docs/puppetdb/latest/api/query/tutorial-pql.html), PQL.
|
11
11
|
# @return Results of the PuppetDB query.
|
12
12
|
# @example Request certnames for all nodes
|
13
13
|
# puppetdb_query('nodes[certname] {}')
|
@@ -69,7 +69,7 @@ Puppet::Functions.create_function(:run_command) do
|
|
69
69
|
call_function('debug', "Simulating run_command('#{command}') - no targets given - no action taken")
|
70
70
|
r = Bolt::ResultSet.new([])
|
71
71
|
else
|
72
|
-
r = executor.run_command(targets, command, options)
|
72
|
+
r = executor.run_command(targets, command, options, Puppet::Pops::PuppetStack.top_of_stack)
|
73
73
|
end
|
74
74
|
|
75
75
|
if !r.ok && !options[:catch_errors]
|
@@ -87,7 +87,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
|
|
87
87
|
r = if targets.empty?
|
88
88
|
Bolt::ResultSet.new([])
|
89
89
|
else
|
90
|
-
executor.run_script(targets, found, arguments, options)
|
90
|
+
executor.run_script(targets, found, arguments, options, Puppet::Pops::PuppetStack.top_of_stack)
|
91
91
|
end
|
92
92
|
|
93
93
|
if !r.ok && !options[:catch_errors]
|
@@ -133,7 +133,7 @@ Puppet::Functions.create_function(:run_task) do
|
|
133
133
|
if targets.empty?
|
134
134
|
Bolt::ResultSet.new([])
|
135
135
|
else
|
136
|
-
result = executor.run_task(targets, task, params, options)
|
136
|
+
result = executor.run_task(targets, task, params, options, Puppet::Pops::PuppetStack.top_of_stack)
|
137
137
|
if !result.ok && !options[:catch_errors]
|
138
138
|
raise Bolt::RunFailure.new(result, 'run_task', task_name)
|
139
139
|
end
|
@@ -180,7 +180,7 @@ Puppet::Functions.create_function(:run_task_with) do
|
|
180
180
|
else
|
181
181
|
# Combine the results from the task run with any failing results that were
|
182
182
|
# generated earlier when creating the target mapping
|
183
|
-
task_result = executor.run_task_with(target_mapping, task, options)
|
183
|
+
task_result = executor.run_task_with(target_mapping, task, options, Puppet::Pops::PuppetStack.top_of_stack)
|
184
184
|
result = Bolt::ResultSet.new(task_result.results + error_set)
|
185
185
|
|
186
186
|
if !result.ok && !options[:catch_errors]
|
@@ -83,7 +83,7 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
|
|
83
83
|
call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken")
|
84
84
|
r = Bolt::ResultSet.new([])
|
85
85
|
else
|
86
|
-
r = executor.upload_file(targets, found, destination, options)
|
86
|
+
r = executor.upload_file(targets, found, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
|
87
87
|
end
|
88
88
|
|
89
89
|
if !r.ok && !options[:catch_errors]
|
@@ -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/logging.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
TOPIC
|
2
|
+
logging
|
3
|
+
|
4
|
+
DESCRIPTION
|
5
|
+
Bolt prints messages both to the console and to log files. Messages can
|
6
|
+
either come from Bolt's 'outputter', which logs user-facing messages like
|
7
|
+
progress and results, or from the 'logger', which logs warnings, errors, and
|
8
|
+
log-structured output to log files. Both of these message streams are
|
9
|
+
configurable.
|
10
|
+
|
11
|
+
By default, Bolt logs to the console at 'warn' level and writes a log file to
|
12
|
+
'<project>/bolt-debug.log' at 'debug' level. Unless you are running a plan,
|
13
|
+
Bolt runs in verbose mode by default.
|
14
|
+
|
15
|
+
To learn more about projects, see the 'project' guide.
|
16
|
+
|
17
|
+
DOCUMENTATION
|
18
|
+
https://pup.pt/bolt-logging
|
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
|
@@ -88,7 +88,7 @@ module Bolt
|
|
88
88
|
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
89
89
|
banner: PLAN_CONVERT_HELP }
|
90
90
|
when 'new'
|
91
|
-
{ flags: OPTIONS[:global] + %w[configfile project],
|
91
|
+
{ flags: OPTIONS[:global] + %w[configfile project pp],
|
92
92
|
banner: PLAN_NEW_HELP }
|
93
93
|
when 'run'
|
94
94
|
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir hiera-config],
|
@@ -945,6 +945,11 @@ module Bolt
|
|
945
945
|
@options[:resolve] = resolve
|
946
946
|
end
|
947
947
|
|
948
|
+
separator "\nPLAN OPTIONS"
|
949
|
+
define('--pp', 'Create a new Puppet language plan.') do |pp|
|
950
|
+
@options[:puppet] = pp
|
951
|
+
end
|
952
|
+
|
948
953
|
separator "\nDISPLAY OPTIONS"
|
949
954
|
define('--filter FILTER', 'Filter tasks and plans by a matching substring') do |filter|
|
950
955
|
unless /^[a-z0-9_:]+$/.match(filter)
|
data/lib/bolt/cli.rb
CHANGED
@@ -15,17 +15,18 @@ require 'bolt/config'
|
|
15
15
|
require 'bolt/error'
|
16
16
|
require 'bolt/executor'
|
17
17
|
require 'bolt/inventory'
|
18
|
-
require 'bolt/rerun'
|
19
18
|
require 'bolt/logger'
|
19
|
+
require 'bolt/module_installer'
|
20
20
|
require 'bolt/outputter'
|
21
|
-
require 'bolt/
|
21
|
+
require 'bolt/pal'
|
22
|
+
require 'bolt/plan_creator'
|
22
23
|
require 'bolt/plugin'
|
23
24
|
require 'bolt/project_migrator'
|
24
|
-
require 'bolt/
|
25
|
+
require 'bolt/puppetdb'
|
26
|
+
require 'bolt/rerun'
|
27
|
+
require 'bolt/secret'
|
25
28
|
require 'bolt/target'
|
26
29
|
require 'bolt/version'
|
27
|
-
require 'bolt/secret'
|
28
|
-
require 'bolt/module_installer'
|
29
30
|
|
30
31
|
module Bolt
|
31
32
|
class CLIExit < StandardError; end
|
@@ -196,10 +197,6 @@ module Bolt
|
|
196
197
|
|
197
198
|
warn_inventory_overrides_cli(options)
|
198
199
|
|
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
|
-
|
203
200
|
options
|
204
201
|
rescue Bolt::Error => e
|
205
202
|
outputter.fatal_error(e)
|
@@ -228,9 +225,9 @@ module Bolt
|
|
228
225
|
|
229
226
|
def validate(options)
|
230
227
|
unless COMMANDS.include?(options[:subcommand])
|
228
|
+
command = Bolt::Util.powershell? ? 'Get-Command -Module PuppetBolt' : 'bolt help'
|
231
229
|
raise Bolt::CLIError,
|
232
|
-
"
|
233
|
-
"#{COMMANDS.keys.join(', ')}"
|
230
|
+
"'#{options[:subcommand]}' is not a Bolt command. See '#{command}'."
|
234
231
|
end
|
235
232
|
|
236
233
|
actions = COMMANDS[options[:subcommand]]
|
@@ -285,8 +282,9 @@ module Bolt
|
|
285
282
|
end
|
286
283
|
|
287
284
|
if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
|
285
|
+
command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
|
288
286
|
raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
|
289
|
-
"the project, run '
|
287
|
+
"the project, run '#{command} #{options[:object]}'."
|
290
288
|
end
|
291
289
|
|
292
290
|
if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
|
@@ -465,7 +463,16 @@ module Bolt
|
|
465
463
|
when 'plan'
|
466
464
|
case options[:action]
|
467
465
|
when 'new'
|
468
|
-
|
466
|
+
command = Bolt::Util.powershell? ? 'New-BoltPlan' : 'bolt plan new'
|
467
|
+
@logger.warn("Command '#{command}' is experimental and subject to changes.")
|
468
|
+
plan_name = options[:object]
|
469
|
+
|
470
|
+
# If this passes validation, it will return the path to the plan to create
|
471
|
+
Bolt::PlanCreator.validate_input(config.project, plan_name)
|
472
|
+
code = Bolt::PlanCreator.create_plan(config.project.plans_path,
|
473
|
+
plan_name,
|
474
|
+
outputter,
|
475
|
+
options[:puppet])
|
469
476
|
when 'run'
|
470
477
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
471
478
|
end
|
@@ -587,8 +594,24 @@ module Bolt
|
|
587
594
|
end
|
588
595
|
|
589
596
|
def list_targets
|
597
|
+
inventoryfile = config.inventoryfile || config.default_inventoryfile
|
598
|
+
|
599
|
+
# Retrieve the known group and target names. This needs to be done before
|
600
|
+
# updating targets, as that will add adhoc targets to the inventory.
|
601
|
+
known_names = inventory.target_names
|
602
|
+
|
590
603
|
update_targets(options)
|
591
|
-
|
604
|
+
|
605
|
+
inventory_targets, adhoc_targets = options[:targets].partition do |target|
|
606
|
+
known_names.include?(target.name)
|
607
|
+
end
|
608
|
+
|
609
|
+
target_list = {
|
610
|
+
inventory: inventory_targets,
|
611
|
+
adhoc: adhoc_targets
|
612
|
+
}
|
613
|
+
|
614
|
+
outputter.print_targets(target_list, inventoryfile)
|
592
615
|
end
|
593
616
|
|
594
617
|
def show_targets
|
@@ -601,118 +624,6 @@ module Bolt
|
|
601
624
|
outputter.print_groups(groups)
|
602
625
|
end
|
603
626
|
|
604
|
-
def new_plan(plan_name)
|
605
|
-
@logger.warn("Command 'bolt plan new' is experimental and subject to changes.")
|
606
|
-
|
607
|
-
if config.project.name.nil?
|
608
|
-
raise Bolt::Error.new(
|
609
|
-
"Project directory '#{config.project.path}' is not a named project. Unable to create "\
|
610
|
-
"a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
|
611
|
-
"configuration file.",
|
612
|
-
"bolt/unnamed-project-error"
|
613
|
-
)
|
614
|
-
end
|
615
|
-
|
616
|
-
if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
|
617
|
-
message = <<~MESSAGE.chomp
|
618
|
-
Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
|
619
|
-
separated by double colons '::'.
|
620
|
-
|
621
|
-
Each name segment must begin with a lowercase letter, and may only include lowercase
|
622
|
-
letters, digits, and underscores.
|
623
|
-
|
624
|
-
Examples of valid plan names:
|
625
|
-
- #{config.project.name}
|
626
|
-
- #{config.project.name}::my_plan
|
627
|
-
MESSAGE
|
628
|
-
|
629
|
-
raise Bolt::ValidationError, message
|
630
|
-
end
|
631
|
-
|
632
|
-
prefix, *name_segments, basename = plan_name.split('::')
|
633
|
-
|
634
|
-
# If the plan name is just the project name, then create an 'init' plan.
|
635
|
-
# Otherwise, use the last name segment for the plan's filename.
|
636
|
-
basename ||= 'init'
|
637
|
-
|
638
|
-
unless prefix == config.project.name
|
639
|
-
message = "First segment of plan name '#{plan_name}' must match project name '#{config.project.name}'. "\
|
640
|
-
"Did you mean '#{config.project.name}::#{plan_name}'?"
|
641
|
-
|
642
|
-
raise Bolt::ValidationError, message
|
643
|
-
end
|
644
|
-
|
645
|
-
dir_path = config.project.plans_path.join(*name_segments)
|
646
|
-
|
647
|
-
%w[pp yaml].each do |ext|
|
648
|
-
next unless (path = config.project.plans_path + "#{basename}.#{ext}").exist?
|
649
|
-
raise Bolt::Error.new(
|
650
|
-
"A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
|
651
|
-
'bolt/existing-plan-error'
|
652
|
-
)
|
653
|
-
end
|
654
|
-
|
655
|
-
begin
|
656
|
-
FileUtils.mkdir_p(dir_path)
|
657
|
-
rescue Errno::EEXIST => e
|
658
|
-
raise Bolt::Error.new(
|
659
|
-
"#{e.message}; unable to create plan directory '#{dir_path}'",
|
660
|
-
'bolt/existing-file-error'
|
661
|
-
)
|
662
|
-
end
|
663
|
-
|
664
|
-
plan_path = dir_path + "#{basename}.yaml"
|
665
|
-
|
666
|
-
plan_template = <<~PLAN
|
667
|
-
# This is the structure of a simple plan. To learn more about writing
|
668
|
-
# YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
|
669
|
-
|
670
|
-
# The description sets the description of the plan that will appear
|
671
|
-
# in 'bolt plan show' output.
|
672
|
-
description: A plan created with bolt plan new
|
673
|
-
|
674
|
-
# The parameters key defines the parameters that can be passed to
|
675
|
-
# the plan.
|
676
|
-
parameters:
|
677
|
-
targets:
|
678
|
-
type: TargetSpec
|
679
|
-
description: A list of targets to run actions on
|
680
|
-
default: localhost
|
681
|
-
|
682
|
-
# The steps key defines the actions the plan will take in order.
|
683
|
-
steps:
|
684
|
-
- message: Hello from #{plan_name}
|
685
|
-
- name: command_step
|
686
|
-
command: whoami
|
687
|
-
targets: $targets
|
688
|
-
|
689
|
-
# The return key sets the return value of the plan.
|
690
|
-
return: $command_step
|
691
|
-
PLAN
|
692
|
-
|
693
|
-
begin
|
694
|
-
File.write(plan_path, plan_template)
|
695
|
-
rescue Errno::EACCES => e
|
696
|
-
raise Bolt::FileError.new(
|
697
|
-
"#{e.message}; unable to create plan",
|
698
|
-
plan_path
|
699
|
-
)
|
700
|
-
end
|
701
|
-
|
702
|
-
output = <<~OUTPUT
|
703
|
-
Created plan '#{plan_name}' at '#{plan_path}'
|
704
|
-
|
705
|
-
Show this plan with:
|
706
|
-
bolt plan show #{plan_name}
|
707
|
-
Run this plan with:
|
708
|
-
bolt plan run #{plan_name}
|
709
|
-
OUTPUT
|
710
|
-
|
711
|
-
outputter.print_message(output)
|
712
|
-
|
713
|
-
0
|
714
|
-
end
|
715
|
-
|
716
627
|
def run_plan(plan_name, plan_arguments, nodes, options)
|
717
628
|
unless nodes.empty?
|
718
629
|
if plan_arguments['nodes'] || plan_arguments['targets']
|
@@ -804,10 +715,12 @@ module Bolt
|
|
804
715
|
end
|
805
716
|
|
806
717
|
def list_modules
|
718
|
+
assert_puppetfile_or_module_command(config.project.modules)
|
807
719
|
outputter.print_module_list(pal.list_modules)
|
808
720
|
end
|
809
721
|
|
810
722
|
def generate_types
|
723
|
+
assert_puppetfile_or_module_command(config.project.modules)
|
811
724
|
# generate_types will surface a nice error with helpful message if it fails
|
812
725
|
pal.generate_types
|
813
726
|
0
|
@@ -828,8 +741,9 @@ module Bolt
|
|
828
741
|
"project name must begin with a lowercase letter and can include lowercase "\
|
829
742
|
"letters, numbers, and underscores."
|
830
743
|
else
|
744
|
+
command = Bolt::Util.powershell? ? 'New-BoltProject -Name <NAME>' : 'bolt project init <NAME>'
|
831
745
|
raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
|
832
|
-
"project name. Please specify a name using '
|
746
|
+
"project name. Please specify a name using '#{command}'."
|
833
747
|
end
|
834
748
|
end
|
835
749
|
|
@@ -886,6 +800,7 @@ module Bolt
|
|
886
800
|
#
|
887
801
|
def install_project_modules(project, force, resolve)
|
888
802
|
assert_project_file(project)
|
803
|
+
assert_puppetfile_or_module_command(project.modules)
|
889
804
|
|
890
805
|
unless project.modules
|
891
806
|
outputter.print_message "Project configuration file #{project.project_file} does not "\
|
@@ -907,6 +822,7 @@ module Bolt
|
|
907
822
|
#
|
908
823
|
def add_project_module(name, project)
|
909
824
|
assert_project_file(project)
|
825
|
+
assert_puppetfile_or_module_command(project.modules)
|
910
826
|
|
911
827
|
modules = project.modules || []
|
912
828
|
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
@@ -924,11 +840,13 @@ module Bolt
|
|
924
840
|
def assert_project_file(project)
|
925
841
|
unless project.project_file?
|
926
842
|
msg = if project.config_file.exist?
|
843
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
927
844
|
"Detected Bolt configuration file #{project.config_file}, unable to install "\
|
928
|
-
"modules. To update to a project configuration file, run '
|
845
|
+
"modules. To update to a project configuration file, run '#{command}'."
|
929
846
|
else
|
847
|
+
command = Bolt::Util.powershell? ? 'New-BoltProject' : 'bolt project init'
|
930
848
|
"Could not find project configuration file #{project.project_file}, unable "\
|
931
|
-
"to install modules. To create a Bolt project, run '
|
849
|
+
"to install modules. To create a Bolt project, run '#{command}'."
|
932
850
|
end
|
933
851
|
|
934
852
|
raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
|
@@ -937,9 +855,12 @@ module Bolt
|
|
937
855
|
|
938
856
|
# Loads a Puppetfile and installs its modules.
|
939
857
|
#
|
940
|
-
def install_puppetfile(
|
858
|
+
def install_puppetfile(puppetfile_config, puppetfile, moduledir)
|
859
|
+
assert_puppetfile_or_module_command(config.project.modules)
|
860
|
+
|
861
|
+
outputter.print_message("Installing modules from Puppetfile")
|
941
862
|
installer = Bolt::ModuleInstaller.new(outputter, pal)
|
942
|
-
ok = installer.install_puppetfile(puppetfile, moduledir,
|
863
|
+
ok = installer.install_puppetfile(puppetfile, moduledir, puppetfile_config)
|
943
864
|
ok ? 0 : 1
|
944
865
|
end
|
945
866
|
|
@@ -947,22 +868,41 @@ module Bolt
|
|
947
868
|
# modules being configured.
|
948
869
|
#
|
949
870
|
def assert_puppetfile_or_module_command(modules)
|
871
|
+
if Bolt::Util.powershell?
|
872
|
+
case options[:action]
|
873
|
+
when 'generate-types'
|
874
|
+
old_command = 'Register-BoltPuppetfileTypes'
|
875
|
+
new_command = 'Register-BoltModuleTypes'
|
876
|
+
when 'install'
|
877
|
+
old_command = 'Install-BoltPuppetfile'
|
878
|
+
new_command = 'Install-BoltModule'
|
879
|
+
when 'show', 'show-modules'
|
880
|
+
old_command = 'Get-BoltPuppetfileModules'
|
881
|
+
new_command = 'Get-BoltModule'
|
882
|
+
end
|
883
|
+
else
|
884
|
+
old_command = "bolt puppetfile #{options[:action]}"
|
885
|
+
new_command = if options[:action] == 'show-modules'
|
886
|
+
'bolt module show'
|
887
|
+
else
|
888
|
+
"bolt module #{options[:action]}"
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
950
892
|
if modules && options[:subcommand] == 'puppetfile'
|
951
893
|
raise Bolt::CLIError,
|
952
|
-
"Unable to use command '
|
953
|
-
"
|
954
|
-
"instead. For a list of available actions for the 'module' command, run "\
|
955
|
-
"'bolt module --help'."
|
894
|
+
"Unable to use command '#{old_command}' when 'modules' is configured in "\
|
895
|
+
"bolt-project.yaml. Use '#{new_command}' instead."
|
956
896
|
elsif modules.nil? && options[:subcommand] == 'module'
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
897
|
+
msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
|
898
|
+
"bolt-project.yaml. "
|
899
|
+
msg += "Use '#{old_command}' instead." if options[:action] != 'add'
|
900
|
+
raise Bolt::CLIError, msg
|
961
901
|
end
|
962
902
|
end
|
963
903
|
|
964
904
|
def pal
|
965
|
-
@pal ||= Bolt::PAL.new(config.modulepath,
|
905
|
+
@pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
|
966
906
|
config.hiera_config,
|
967
907
|
config.project.resource_types,
|
968
908
|
config.compile_concurrency,
|
@@ -1061,7 +1001,7 @@ module Bolt
|
|
1061
1001
|
'Task' => [],
|
1062
1002
|
'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
|
1063
1003
|
if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
|
1064
|
-
default_content = Bolt::PAL.new([], nil, nil)
|
1004
|
+
default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
|
1065
1005
|
content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
|
1066
1006
|
col << iter&.first
|
1067
1007
|
end
|
@@ -1076,7 +1016,7 @@ module Bolt
|
|
1076
1016
|
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
1077
1017
|
# package installs include modules listed in the Bolt repo Puppetfile
|
1078
1018
|
def incomplete_install?
|
1079
|
-
(Dir.children(Bolt::
|
1019
|
+
(Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
|
1080
1020
|
end
|
1081
1021
|
|
1082
1022
|
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|