bolt 2.34.0 → 2.40.1
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 +1 -1
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
- data/lib/bolt/analytics.rb +27 -8
- data/lib/bolt/apply_result.rb +3 -3
- data/lib/bolt/bolt_option_parser.rb +45 -18
- data/lib/bolt/cli.rb +98 -116
- data/lib/bolt/config.rb +184 -80
- data/lib/bolt/config/options.rb +148 -87
- data/lib/bolt/config/transport/base.rb +10 -19
- data/lib/bolt/config/transport/local.rb +1 -7
- data/lib/bolt/config/transport/options.rb +12 -69
- data/lib/bolt/config/transport/ssh.rb +8 -19
- data/lib/bolt/error.rb +24 -0
- data/lib/bolt/executor.rb +92 -18
- data/lib/bolt/inventory.rb +25 -0
- data/lib/bolt/inventory/group.rb +0 -8
- data/lib/bolt/inventory/options.rb +130 -0
- data/lib/bolt/inventory/target.rb +10 -11
- data/lib/bolt/module_installer.rb +21 -13
- data/lib/bolt/module_installer/resolver.rb +1 -1
- data/lib/bolt/outputter.rb +19 -5
- data/lib/bolt/outputter/human.rb +22 -3
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +13 -2
- data/lib/bolt/pal.rb +18 -6
- data/lib/bolt/pal/yaml_plan.rb +7 -0
- data/lib/bolt/plugin.rb +41 -12
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/module.rb +4 -4
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +59 -40
- data/lib/bolt/project_manager.rb +201 -0
- data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +49 -4
- data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +3 -3
- data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
- data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
- data/lib/bolt/puppetdb/client.rb +11 -2
- data/lib/bolt/puppetdb/config.rb +4 -3
- data/lib/bolt/rerun.rb +1 -5
- data/lib/bolt/shell/bash.rb +8 -2
- data/lib/bolt/shell/powershell.rb +21 -3
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/local.rb +13 -0
- data/lib/bolt/transport/orch.rb +0 -5
- data/lib/bolt/transport/orch/connection.rb +10 -3
- data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
- data/lib/bolt/util.rb +36 -7
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt/yarn.rb +23 -0
- data/lib/bolt_server/base_config.rb +3 -1
- data/lib/bolt_server/config.rb +3 -1
- data/lib/bolt_server/plugin.rb +13 -0
- data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
- data/lib/bolt_server/schemas/connect-data.json +22 -0
- data/lib/bolt_server/schemas/partials/task.json +2 -2
- data/lib/bolt_server/transport_app.rb +82 -23
- data/lib/bolt_spec/plans/mock_executor.rb +4 -1
- data/libexec/apply_catalog.rb +1 -1
- data/libexec/custom_facts.rb +1 -1
- data/libexec/query_resources.rb +1 -1
- metadata +22 -14
- data/lib/bolt/project_migrator.rb +0 -80
data/lib/bolt/inventory/group.rb
CHANGED
@@ -254,14 +254,6 @@ module Bolt
|
|
254
254
|
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in group #{@name}"
|
255
255
|
@logger.warn(msg)
|
256
256
|
end
|
257
|
-
|
258
|
-
Bolt::Util.walk_keys(input) do |key|
|
259
|
-
if @plugins.reference?(key)
|
260
|
-
raise ValidationError.new("Group keys cannot be specified as _plugin references", @name)
|
261
|
-
else
|
262
|
-
key
|
263
|
-
end
|
264
|
-
end
|
265
257
|
end
|
266
258
|
|
267
259
|
def validate(used_group_names = Set.new, used_target_names = Set.new, used_aliases = {})
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/config/options'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class Inventory
|
7
|
+
module Options
|
8
|
+
# Top-level options available in the inventory.
|
9
|
+
OPTIONS = %w[
|
10
|
+
config
|
11
|
+
facts
|
12
|
+
features
|
13
|
+
groups
|
14
|
+
targets
|
15
|
+
vars
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
# Definitions used to validate the data.
|
19
|
+
# https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
|
20
|
+
DEFINITIONS = {
|
21
|
+
"alias" => {
|
22
|
+
description: "A unique alias to refer to the target. Aliases cannot conflict "\
|
23
|
+
"with the name of a group, the name of a target, or another alias.",
|
24
|
+
type: [String, Array],
|
25
|
+
uniqueItems: true,
|
26
|
+
items: {
|
27
|
+
type: String,
|
28
|
+
_plugin: true
|
29
|
+
},
|
30
|
+
_plugin: true
|
31
|
+
},
|
32
|
+
"config" => {
|
33
|
+
description: "A map of configuration options.",
|
34
|
+
type: Hash,
|
35
|
+
# These properties are populated as part of Bolt::Inventory.schema
|
36
|
+
properties: {},
|
37
|
+
_plugin: true
|
38
|
+
},
|
39
|
+
"facts" => {
|
40
|
+
description: "A map of system information, also known as facts, for the target.",
|
41
|
+
type: Hash,
|
42
|
+
_plugin: true
|
43
|
+
},
|
44
|
+
"features" => {
|
45
|
+
description: "A list of available features for the target.",
|
46
|
+
type: Array,
|
47
|
+
uniqueItems: true,
|
48
|
+
items: {
|
49
|
+
type: String,
|
50
|
+
_plugin: true
|
51
|
+
},
|
52
|
+
_plugin: true
|
53
|
+
},
|
54
|
+
"groups" => {
|
55
|
+
description: "A list of groups and their associated configuration.",
|
56
|
+
type: Array,
|
57
|
+
items: {
|
58
|
+
type: Hash,
|
59
|
+
required: ["name"],
|
60
|
+
properties: {
|
61
|
+
"config" => { _ref: "config" },
|
62
|
+
"facts" => { _ref: "facts" },
|
63
|
+
"features" => { _ref: "features" },
|
64
|
+
"groups" => { _ref: "groups" },
|
65
|
+
"name" => { _ref: "name" },
|
66
|
+
"plugin_hooks" => { _ref: "plugin_hooks" },
|
67
|
+
"targets" => { _ref: "targets" },
|
68
|
+
"vars" => { _ref: "vars" }
|
69
|
+
},
|
70
|
+
_plugin: true
|
71
|
+
},
|
72
|
+
_plugin: true
|
73
|
+
},
|
74
|
+
"name" => {
|
75
|
+
description: "A human-readable name to refer to the group or target. Names "\
|
76
|
+
"cannot conflict with the name of a group, the name of a target, "\
|
77
|
+
"or the alias of a target. A name is required for a group and is "\
|
78
|
+
"required for a target unless the uri option is set.",
|
79
|
+
type: String,
|
80
|
+
_plugin: true
|
81
|
+
},
|
82
|
+
"plugin_hooks" => {
|
83
|
+
description: "Configuration for the Puppet library plugin used to install the "\
|
84
|
+
"Puppet agent on the target. For more information, see "\
|
85
|
+
"https://pup.pt/bolt-plugin-hooks",
|
86
|
+
type: Hash,
|
87
|
+
properties: {
|
88
|
+
"puppet_library" => {
|
89
|
+
description: "Configuration for the Puppet library plugin.",
|
90
|
+
type: Hash,
|
91
|
+
_plugin: true
|
92
|
+
}
|
93
|
+
},
|
94
|
+
_plugin: true
|
95
|
+
},
|
96
|
+
"targets" => {
|
97
|
+
description: "A list of targets and their associated configuration.",
|
98
|
+
type: Array,
|
99
|
+
items: {
|
100
|
+
type: [String, Hash],
|
101
|
+
properties: {
|
102
|
+
"alias" => { _ref: "alias" },
|
103
|
+
"config" => { _ref: "config" },
|
104
|
+
"facts" => { _ref: "facts" },
|
105
|
+
"features" => { _ref: "features" },
|
106
|
+
"name" => { _ref: "name" },
|
107
|
+
"plugin_hooks" => { _ref: "plugin_hooks" },
|
108
|
+
"uri" => { _ref: "uri" },
|
109
|
+
"vars" => { _ref: "vars" }
|
110
|
+
},
|
111
|
+
_plugin: true
|
112
|
+
},
|
113
|
+
_plugin: true
|
114
|
+
},
|
115
|
+
"uri" => {
|
116
|
+
description: "The URI of the target. This option is required unless the name "\
|
117
|
+
"option is set.",
|
118
|
+
type: String,
|
119
|
+
format: "uri",
|
120
|
+
_plugin: true
|
121
|
+
},
|
122
|
+
"vars" => {
|
123
|
+
description: "A map of variables for the group or target.",
|
124
|
+
type: Hash,
|
125
|
+
_plugin: true
|
126
|
+
}
|
127
|
+
}.freeze
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -31,7 +31,8 @@ module Bolt
|
|
31
31
|
end
|
32
32
|
|
33
33
|
if @name == 'localhost'
|
34
|
-
|
34
|
+
default = { 'config' => { 'transport' => 'local' } }
|
35
|
+
target_data = Bolt::Util.deep_merge(default, target_data)
|
35
36
|
end
|
36
37
|
|
37
38
|
@config = target_data['config'] || {}
|
@@ -49,18 +50,16 @@ module Bolt
|
|
49
50
|
validate
|
50
51
|
end
|
51
52
|
|
52
|
-
def
|
53
|
+
def set_local_defaults
|
54
|
+
return if @set_local_default
|
53
55
|
defaults = {
|
54
|
-
'
|
55
|
-
'transport' => 'local',
|
56
|
-
'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
|
57
|
-
},
|
58
|
-
'features' => ['puppet-agent']
|
56
|
+
'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
|
59
57
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
old_config = @config
|
59
|
+
@config = Bolt::Util.deep_merge(defaults, @config)
|
60
|
+
invalidate_config_cache! if old_config != @config
|
61
|
+
set_feature('puppet-agent')
|
62
|
+
@set_local_default = true
|
64
63
|
end
|
65
64
|
|
66
65
|
# rubocop:disable Naming/AccessorMethodName
|
@@ -17,14 +17,14 @@ module Bolt
|
|
17
17
|
|
18
18
|
# Adds a single module to the project.
|
19
19
|
#
|
20
|
-
def add(name, specs, puppetfile_path, moduledir,
|
20
|
+
def add(name, specs, puppetfile_path, moduledir, project_file, config)
|
21
21
|
project_specs = Specs.new(specs)
|
22
22
|
|
23
23
|
# Exit early if project config already includes a spec with this name.
|
24
24
|
if project_specs.include?(name)
|
25
25
|
@outputter.print_message(
|
26
|
-
"Project configuration file #{
|
27
|
-
"#{name}. Nothing to do."
|
26
|
+
"Project configuration file #{project_file} already includes specification "\
|
27
|
+
"with name #{name}. Nothing to do."
|
28
28
|
)
|
29
29
|
return true
|
30
30
|
end
|
@@ -47,30 +47,32 @@ module Bolt
|
|
47
47
|
# a version conflict.
|
48
48
|
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
49
49
|
|
50
|
+
@outputter.start_spin
|
50
51
|
begin
|
51
52
|
resolve_specs.add_specs('name' => name)
|
52
|
-
puppetfile = Resolver.new.resolve(resolve_specs)
|
53
|
+
puppetfile = Resolver.new.resolve(resolve_specs, config)
|
53
54
|
rescue Bolt::Error
|
54
55
|
project_specs.add_specs('name' => name)
|
55
|
-
puppetfile = Resolver.new.resolve(project_specs)
|
56
|
+
puppetfile = Resolver.new.resolve(project_specs, config)
|
56
57
|
end
|
58
|
+
@outputter.stop_spin
|
57
59
|
|
58
60
|
# Display the diff between the existing Puppetfile and the new Puppetfile.
|
59
61
|
print_puppetfile_diff(existing_puppetfile, puppetfile)
|
60
62
|
|
61
63
|
# Add the module to the project configuration.
|
62
|
-
@outputter.print_action_step("Updating project configuration file at #{
|
64
|
+
@outputter.print_action_step("Updating project configuration file at #{project_file}")
|
63
65
|
|
64
|
-
data = Bolt::Util.read_yaml_hash(
|
66
|
+
data = Bolt::Util.read_yaml_hash(project_file, 'project')
|
65
67
|
data['modules'] ||= []
|
66
68
|
data['modules'] << name.tr('-', '/')
|
67
69
|
|
68
70
|
begin
|
69
|
-
File.write(
|
71
|
+
File.write(project_file, data.to_yaml)
|
70
72
|
rescue SystemCallError => e
|
71
73
|
raise Bolt::FileError.new(
|
72
74
|
"Unable to update project configuration file: #{e.message}",
|
73
|
-
|
75
|
+
project_file
|
74
76
|
)
|
75
77
|
end
|
76
78
|
|
@@ -79,7 +81,7 @@ module Bolt
|
|
79
81
|
puppetfile.write(puppetfile_path, moduledir)
|
80
82
|
|
81
83
|
# Install the modules.
|
82
|
-
install_puppetfile(puppetfile_path, moduledir)
|
84
|
+
install_puppetfile(puppetfile_path, moduledir, config)
|
83
85
|
end
|
84
86
|
|
85
87
|
# Outputs a diff of an old Puppetfile and a new Puppetfile.
|
@@ -145,7 +147,7 @@ module Bolt
|
|
145
147
|
|
146
148
|
# Installs a project's module dependencies.
|
147
149
|
#
|
148
|
-
def install(specs, path, moduledir, force: false, resolve: true)
|
150
|
+
def install(specs, path, moduledir, config = {}, force: false, resolve: true)
|
149
151
|
@outputter.print_message("Installing project modules\n\n")
|
150
152
|
|
151
153
|
if resolve != false
|
@@ -155,7 +157,11 @@ module Bolt
|
|
155
157
|
# and write a Puppetfile.
|
156
158
|
if force || !path.exist?
|
157
159
|
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
158
|
-
|
160
|
+
|
161
|
+
# This doesn't use the block as it's more testable to just mock *_spin
|
162
|
+
@outputter.start_spin
|
163
|
+
puppetfile = Resolver.new.resolve(specs, config)
|
164
|
+
@outputter.stop_spin
|
159
165
|
|
160
166
|
# We get here either through 'bolt module install' which uses the
|
161
167
|
# managed modulepath (which isn't configurable) or through bolt
|
@@ -177,14 +183,16 @@ module Bolt
|
|
177
183
|
end
|
178
184
|
|
179
185
|
# Install the modules.
|
180
|
-
install_puppetfile(path, moduledir)
|
186
|
+
install_puppetfile(path, moduledir, config)
|
181
187
|
end
|
182
188
|
|
183
189
|
# Installs the Puppetfile and generates types.
|
184
190
|
#
|
185
191
|
def install_puppetfile(path, moduledir, config = {})
|
186
192
|
@outputter.print_action_step("Syncing modules from #{path} to #{moduledir}")
|
193
|
+
@outputter.start_spin
|
187
194
|
ok = Installer.new(config).install(path, moduledir)
|
195
|
+
@outputter.stop_spin
|
188
196
|
|
189
197
|
# Automatically generate types after installing modules
|
190
198
|
@outputter.print_action_step("Generating type references")
|
data/lib/bolt/outputter.rb
CHANGED
@@ -2,24 +2,25 @@
|
|
2
2
|
|
3
3
|
module Bolt
|
4
4
|
class Outputter
|
5
|
-
def self.for_format(format, color, verbose, trace)
|
5
|
+
def self.for_format(format, color, verbose, trace, spin)
|
6
6
|
case format
|
7
7
|
when 'human'
|
8
|
-
Bolt::Outputter::Human.new(color, verbose, trace)
|
8
|
+
Bolt::Outputter::Human.new(color, verbose, trace, spin)
|
9
9
|
when 'json'
|
10
|
-
Bolt::Outputter::JSON.new(color, verbose, trace)
|
10
|
+
Bolt::Outputter::JSON.new(color, verbose, trace, false)
|
11
11
|
when 'rainbow'
|
12
|
-
Bolt::Outputter::Rainbow.new(color, verbose, trace)
|
12
|
+
Bolt::Outputter::Rainbow.new(color, verbose, trace, spin)
|
13
13
|
when nil
|
14
14
|
raise "Cannot use outputter before parsing."
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
18
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
19
19
|
@color = color
|
20
20
|
@verbose = verbose
|
21
21
|
@trace = trace
|
22
22
|
@stream = stream
|
23
|
+
@spin = spin
|
23
24
|
end
|
24
25
|
|
25
26
|
def indent(indent, string)
|
@@ -34,6 +35,19 @@ module Bolt
|
|
34
35
|
def print_error
|
35
36
|
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
36
37
|
end
|
38
|
+
|
39
|
+
def start_spin; end
|
40
|
+
|
41
|
+
def stop_spin; end
|
42
|
+
|
43
|
+
def spin
|
44
|
+
start_spin
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
stop_spin
|
49
|
+
end
|
50
|
+
end
|
37
51
|
end
|
38
52
|
end
|
39
53
|
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -14,12 +14,13 @@ module Bolt
|
|
14
14
|
|
15
15
|
def print_head; end
|
16
16
|
|
17
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
17
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
18
18
|
super
|
19
19
|
# Plans and without_default_logging() calls can both be nested, so we
|
20
20
|
# track each of them with a "stack" consisting of an integer.
|
21
21
|
@plan_depth = 0
|
22
22
|
@disable_depth = 0
|
23
|
+
@pinwheel = %w[- \\ | /]
|
23
24
|
end
|
24
25
|
|
25
26
|
def colorize(color, string)
|
@@ -30,6 +31,24 @@ module Bolt
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
def start_spin
|
35
|
+
return unless @spin
|
36
|
+
@spin = true
|
37
|
+
@spin_thread = Thread.new do
|
38
|
+
loop do
|
39
|
+
sleep(0.1)
|
40
|
+
@stream.print(colorize(:cyan, @pinwheel.rotate!.first + "\b"))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_spin
|
46
|
+
return unless @spin
|
47
|
+
@spin_thread.terminate
|
48
|
+
@spin = false
|
49
|
+
@stream.print("\b")
|
50
|
+
end
|
51
|
+
|
33
52
|
def remove_trail(string)
|
34
53
|
string.sub(/\s\z/, '')
|
35
54
|
end
|
@@ -215,7 +234,7 @@ module Bolt
|
|
215
234
|
|
216
235
|
def print_tasks(tasks, modulepath)
|
217
236
|
command = Bolt::Util.powershell? ? 'Get-BoltTask -Task <TASK NAME>' : 'bolt task show <TASK NAME>'
|
218
|
-
print_table(tasks)
|
237
|
+
tasks.any? ? print_table(tasks) : print_message('No available tasks')
|
219
238
|
print_message("\nMODULEPATH:\n#{modulepath.join(File::PATH_SEPARATOR)}\n"\
|
220
239
|
"\nUse '#{command}' to view "\
|
221
240
|
"details and parameters for a specific task.")
|
@@ -299,7 +318,7 @@ module Bolt
|
|
299
318
|
|
300
319
|
def print_plans(plans, modulepath)
|
301
320
|
command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
|
302
|
-
print_table(plans)
|
321
|
+
plans.any? ? print_table(plans) : print_message('No available plans')
|
303
322
|
print_message("\nMODULEPATH:\n#{modulepath.join(File::PATH_SEPARATOR)}\n"\
|
304
323
|
"\nUse '#{command}' to view "\
|
305
324
|
"details and parameters for a specific plan.")
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -5,7 +5,7 @@ require 'bolt/pal'
|
|
5
5
|
module Bolt
|
6
6
|
class Outputter
|
7
7
|
class Rainbow < Bolt::Outputter::Human
|
8
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
8
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
9
9
|
begin
|
10
10
|
require 'paint'
|
11
11
|
if Bolt::Util.windows?
|
@@ -53,7 +53,7 @@ module Bolt
|
|
53
53
|
@state = :normal if c == 'm'
|
54
54
|
end
|
55
55
|
end
|
56
|
-
a.join
|
56
|
+
a.join
|
57
57
|
else
|
58
58
|
"\033[#{COLORS[color]}m#{string}\033[0m"
|
59
59
|
end
|
@@ -62,6 +62,17 @@ module Bolt
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
def start_spin
|
66
|
+
return unless @spin
|
67
|
+
@spin = true
|
68
|
+
@spin_thread = Thread.new do
|
69
|
+
loop do
|
70
|
+
@stream.print(colorize(:rainbow, @pinwheel.rotate!.first + "\b"))
|
71
|
+
sleep(0.1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
65
76
|
def print_summary(results, elapsed_time = nil)
|
66
77
|
ok_set = results.ok_set
|
67
78
|
unless ok_set.empty?
|