bolt 1.35.0 → 1.36.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 +40 -0
- data/lib/bolt/applicator.rb +3 -1
- data/lib/bolt/bolt_option_parser.rb +50 -17
- data/lib/bolt/cli.rb +67 -3
- data/lib/bolt/config.rb +14 -3
- data/lib/bolt/inventory.rb +10 -1
- data/lib/bolt/inventory/group2.rb +23 -15
- data/lib/bolt/inventory/inventory2.rb +19 -0
- data/lib/bolt/inventory/target.rb +4 -4
- data/lib/bolt/outputter/human.rb +12 -5
- data/lib/bolt/outputter/json.rb +12 -5
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/plugin/prompt.rb +5 -2
- data/lib/bolt/puppetdb/config.rb +12 -4
- data/lib/bolt/target.rb +42 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_spec/plans.rb +17 -1
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +4 -1
- data/lib/bolt_spec/plans/mock_executor.rb +6 -5
- data/lib/bolt_spec/run.rb +19 -8
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc29b4aca58aa7208e448120f59687cb5fbdd0cbab78af55861b629420a9fb9e
|
4
|
+
data.tar.gz: 46ec4cca8088b69d9196f3c251084bdeadaee9b676aa4c33617fd0e40a711d20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a0fbaff3412cc6cc7b92922fae45daf150b950d9f60a2eed1d8a97d32ffd404d7c7577167efe90fecf9425444e4acca5794ad8a49ca6248f0a91b6cb6598dec
|
7
|
+
data.tar.gz: da32512009bcbe140b70374ea331a0edbe95696df347311b0b0de07f8740818088c9c2fbfc136572c20c9008c953f205c5e855e048141c9e0a933ce1c77f6d09
|
data/Puppetfile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
forge "http://forge.puppetlabs.com"
|
4
|
+
|
5
|
+
moduledir File.join(File.dirname(__FILE__), 'modules')
|
6
|
+
|
7
|
+
# Core modules used by 'apply'
|
8
|
+
mod 'puppetlabs-service', '1.1.0'
|
9
|
+
mod 'puppetlabs-facts', '0.6.0'
|
10
|
+
mod 'puppetlabs-puppet_agent', '2.2.1'
|
11
|
+
|
12
|
+
# Core types and providers for Puppet 6
|
13
|
+
mod 'puppetlabs-augeas_core', '1.0.5'
|
14
|
+
mod 'puppetlabs-host_core', '1.0.3'
|
15
|
+
mod 'puppetlabs-scheduled_task', '2.0.0'
|
16
|
+
mod 'puppetlabs-sshkeys_core', '1.0.3'
|
17
|
+
mod 'puppetlabs-zfs_core', '1.0.4'
|
18
|
+
mod 'puppetlabs-cron_core', '1.0.3'
|
19
|
+
mod 'puppetlabs-mount_core', '1.0.4'
|
20
|
+
mod 'puppetlabs-selinux_core', '1.0.4'
|
21
|
+
mod 'puppetlabs-yumrepo_core', '1.0.4'
|
22
|
+
mod 'puppetlabs-zone_core', '1.0.3'
|
23
|
+
|
24
|
+
# Useful additional modules
|
25
|
+
mod 'puppetlabs-package', '0.7.0'
|
26
|
+
mod 'puppetlabs-puppet_conf', '0.4.0'
|
27
|
+
mod 'puppetlabs-python_task_helper', '0.3.0'
|
28
|
+
mod 'puppetlabs-reboot', '2.2.0'
|
29
|
+
mod 'puppetlabs-ruby_task_helper', '0.4.0'
|
30
|
+
|
31
|
+
# Plugin modules
|
32
|
+
mod 'puppetlabs-azure_inventory', '0.2.0'
|
33
|
+
mod 'puppetlabs-terraform', '0.2.0'
|
34
|
+
mod 'puppetlabs-vault', '0.2.2'
|
35
|
+
mod 'puppetlabs-aws_inventory', '0.2.0'
|
36
|
+
|
37
|
+
# If we don't list these modules explicitly, r10k will purge them
|
38
|
+
mod 'canary', local: true
|
39
|
+
mod 'aggregate', local: true
|
40
|
+
mod 'puppetdb_fact', local: true
|
data/lib/bolt/applicator.rb
CHANGED
@@ -132,7 +132,9 @@ module Bolt
|
|
132
132
|
def validate_hiera_config(hiera_config)
|
133
133
|
if File.exist?(File.path(hiera_config))
|
134
134
|
data = File.open(File.path(hiera_config), "r:UTF-8") { |f| YAML.safe_load(f.read, [Symbol]) }
|
135
|
-
|
135
|
+
if data.nil?
|
136
|
+
return nil
|
137
|
+
elsif data['version'] != 5
|
136
138
|
raise Bolt::ParseError, "Hiera v5 is required, found v#{data['version'] || 3} in #{hiera_config}"
|
137
139
|
end
|
138
140
|
hiera_config
|
@@ -29,7 +29,7 @@ module Bolt
|
|
29
29
|
{ flags: ACTION_OPTS + %w[tmpdir],
|
30
30
|
banner: FILE_HELP }
|
31
31
|
when 'inventory'
|
32
|
-
{ flags: OPTIONS[:inventory] + OPTIONS[:global] + %w[format inventoryfile boltdir configfile],
|
32
|
+
{ flags: OPTIONS[:inventory] + OPTIONS[:global] + %w[format inventoryfile boltdir configfile detail],
|
33
33
|
banner: INVENTORY_HELP }
|
34
34
|
when 'group'
|
35
35
|
{ flags: OPTIONS[:global] + %w[format inventoryfile boltdir configfile],
|
@@ -49,6 +49,15 @@ module Bolt
|
|
49
49
|
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir],
|
50
50
|
banner: PLAN_HELP }
|
51
51
|
end
|
52
|
+
when 'project'
|
53
|
+
case action
|
54
|
+
when 'init'
|
55
|
+
{ flags: OPTIONS[:global],
|
56
|
+
banner: PROJECT_INIT_HELP }
|
57
|
+
else
|
58
|
+
{ flags: OPTIONS[:global],
|
59
|
+
banner: PROJECT_HELP }
|
60
|
+
end
|
52
61
|
when 'puppetfile'
|
53
62
|
case action
|
54
63
|
when 'install'
|
@@ -121,6 +130,7 @@ module Bolt
|
|
121
130
|
bolt secret decrypt <encrypted> Decrypt a value
|
122
131
|
bolt inventory show Show the list of targets an action would run on
|
123
132
|
bolt group show Show the list of groups in the inventory
|
133
|
+
bolt project init Create a new Bolt project
|
124
134
|
|
125
135
|
Run `bolt <subcommand> --help` to view specific examples.
|
126
136
|
|
@@ -310,13 +320,32 @@ module Bolt
|
|
310
320
|
Available options are:
|
311
321
|
GROUP_HELP
|
312
322
|
|
323
|
+
PROJECT_HELP = <<~PROJECT_HELP
|
324
|
+
Usage: bolt project <action>
|
325
|
+
|
326
|
+
Available actions are:
|
327
|
+
init Create a new Bolt project
|
328
|
+
|
329
|
+
Available options are:
|
330
|
+
PROJECT_HELP
|
331
|
+
|
332
|
+
PROJECT_INIT_HELP = <<~PROJECT_INIT_HELP
|
333
|
+
Usage: bolt project init [directory]
|
334
|
+
|
335
|
+
Create a new Bolt project.
|
336
|
+
Specify a directory to create the Bolt project in. Defaults to the current working directory.
|
337
|
+
|
338
|
+
Available options are:
|
339
|
+
PROJECT_INIT_HELP
|
340
|
+
|
313
341
|
def initialize(options)
|
314
342
|
super()
|
315
343
|
|
316
344
|
@options = options
|
317
345
|
|
318
346
|
define('-n', '--nodes NODES',
|
319
|
-
'Alias for --targets'
|
347
|
+
'Alias for --targets',
|
348
|
+
'Deprecated in favor of --targets') do |nodes|
|
320
349
|
@options [:nodes] ||= []
|
321
350
|
@options[:nodes] << get_arg_input(nodes)
|
322
351
|
end
|
@@ -357,20 +386,23 @@ module Bolt
|
|
357
386
|
"Puppet manifest code to apply to the targets") do |code|
|
358
387
|
@options[:code] = code
|
359
388
|
end
|
389
|
+
define('--detail', 'Show resolved configuration for the targets') do |detail|
|
390
|
+
@options[:detail] = detail
|
391
|
+
end
|
360
392
|
|
361
393
|
separator "\nAuthentication:"
|
362
394
|
define('-u', '--user USER', 'User to authenticate as') do |user|
|
363
395
|
@options[:user] = user
|
364
396
|
end
|
365
397
|
define('-p', '--password [PASSWORD]',
|
366
|
-
'Password to authenticate with
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
@options[:password] = password
|
398
|
+
'Password to authenticate with') do |password|
|
399
|
+
# TODO: Remove deprecation message
|
400
|
+
unless password
|
401
|
+
msg = "Optional parameter for --password is deprecated and no longer prompts for password. " \
|
402
|
+
"Use the prompt plugin instead to prompt for passwords."
|
403
|
+
raise Bolt::CLIError, msg
|
373
404
|
end
|
405
|
+
@options[:password] = password
|
374
406
|
end
|
375
407
|
define('--private-key KEY', 'Private ssh key to authenticate with') do |key|
|
376
408
|
@options[:'private-key'] = key
|
@@ -390,14 +422,14 @@ module Bolt
|
|
390
422
|
@options[:'run-as'] = user
|
391
423
|
end
|
392
424
|
define('--sudo-password [PASSWORD]',
|
393
|
-
'Password for privilege escalation
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
@options[:'sudo-password'] = password
|
425
|
+
'Password for privilege escalation') do |password|
|
426
|
+
# TODO: Remove deprecation message
|
427
|
+
unless password
|
428
|
+
msg = "Optional parameter for --sudo-password is deprecated and no longer prompts for password. " \
|
429
|
+
"Use the prompt plugin instead to prompt for passwords."
|
430
|
+
raise Bolt::CLIError, msg
|
400
431
|
end
|
432
|
+
@options[:'sudo-password'] = password
|
401
433
|
end
|
402
434
|
|
403
435
|
separator "\nRun context:"
|
@@ -422,7 +454,8 @@ module Bolt
|
|
422
454
|
@options[:boltdir] = path
|
423
455
|
end
|
424
456
|
define('--configfile FILEPATH',
|
425
|
-
'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml)'
|
457
|
+
'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml). ' \
|
458
|
+
'Directory containing bolt.yaml will be used as the Boltdir.') do |path|
|
426
459
|
@options[:configfile] = path
|
427
460
|
end
|
428
461
|
define('-i', '--inventoryfile FILEPATH',
|
data/lib/bolt/cli.rb
CHANGED
@@ -37,6 +37,7 @@ module Bolt
|
|
37
37
|
'secret' => %w[encrypt decrypt createkeys],
|
38
38
|
'inventory' => %w[show],
|
39
39
|
'group' => %w[show],
|
40
|
+
'project' => %w[init],
|
40
41
|
'apply' => %w[] }.freeze
|
41
42
|
|
42
43
|
attr_reader :config, :options
|
@@ -134,6 +135,7 @@ module Bolt
|
|
134
135
|
# options[:targets] will contain a resolved set of Target objects
|
135
136
|
unless options[:subcommand] == 'puppetfile' ||
|
136
137
|
options[:subcommand] == 'secret' ||
|
138
|
+
options[:subcommand] == 'project' ||
|
137
139
|
options[:action] == 'show' ||
|
138
140
|
options[:action] == 'convert'
|
139
141
|
|
@@ -145,6 +147,13 @@ module Bolt
|
|
145
147
|
options[:verbose] = options[:subcommand] != 'plan'
|
146
148
|
end
|
147
149
|
|
150
|
+
# TODO: Remove deprecation warning
|
151
|
+
if options[:nodes]
|
152
|
+
@logger.warn("Deprecation Warning: The --nodes command line option has been " \
|
153
|
+
"deprecated in favor of --targets.")
|
154
|
+
end
|
155
|
+
|
156
|
+
warn_inventory_overrides_cli(options)
|
148
157
|
options
|
149
158
|
rescue Bolt::Error => e
|
150
159
|
outputter.fatal_error(e)
|
@@ -244,7 +253,7 @@ module Bolt
|
|
244
253
|
|
245
254
|
def puppetdb_client
|
246
255
|
return @puppetdb_client if @puppetdb_client
|
247
|
-
puppetdb_config = Bolt::PuppetDB::Config.load_config(nil, config.puppetdb)
|
256
|
+
puppetdb_config = Bolt::PuppetDB::Config.load_config(nil, config.puppetdb, config.boltdir.path)
|
248
257
|
@puppetdb_client = Bolt::PuppetDB::Client.new(puppetdb_config)
|
249
258
|
end
|
250
259
|
|
@@ -256,6 +265,35 @@ module Bolt
|
|
256
265
|
puppetdb_client.query_certnames(query)
|
257
266
|
end
|
258
267
|
|
268
|
+
def warn_inventory_overrides_cli(opts)
|
269
|
+
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
270
|
+
Bolt::Inventory::ENVIRONMENT_VAR
|
271
|
+
elsif @config.inventoryfile && Bolt::Util.file_stat(@config.inventoryfile)
|
272
|
+
@config.inventoryfile
|
273
|
+
elsif (inventory_file = @config.default_inventoryfile.find do |file|
|
274
|
+
begin
|
275
|
+
Bolt::Util.file_stat(file)
|
276
|
+
rescue Errno::ENOENT
|
277
|
+
false
|
278
|
+
end
|
279
|
+
end
|
280
|
+
)
|
281
|
+
inventory_file
|
282
|
+
end
|
283
|
+
|
284
|
+
inventory_cli_opts = %i[authentication escalation transports].each_with_object([]) do |key, acc|
|
285
|
+
acc.concat(Bolt::BoltOptionParser::OPTIONS[key])
|
286
|
+
end
|
287
|
+
|
288
|
+
inventory_cli_opts.concat(%w[no-host-key-check no-ssl no-ssl-verify no-tty])
|
289
|
+
|
290
|
+
conflicting_options = Set.new(opts.keys.map(&:to_s)).intersection(inventory_cli_opts)
|
291
|
+
|
292
|
+
if inventory_source && conflicting_options.any?
|
293
|
+
@logger.warn("CLI arguments #{conflicting_options.to_a} may be overridden by Inventory: #{inventory_source}")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
259
297
|
def execute(options)
|
260
298
|
message = nil
|
261
299
|
|
@@ -307,7 +345,11 @@ module Bolt
|
|
307
345
|
list_plans
|
308
346
|
end
|
309
347
|
elsif options[:subcommand] == 'inventory'
|
310
|
-
|
348
|
+
if options[:detail]
|
349
|
+
show_targets
|
350
|
+
else
|
351
|
+
list_targets
|
352
|
+
end
|
311
353
|
elsif options[:subcommand] == 'group'
|
312
354
|
list_groups
|
313
355
|
end
|
@@ -324,6 +366,8 @@ module Bolt
|
|
324
366
|
end
|
325
367
|
|
326
368
|
case options[:subcommand]
|
369
|
+
when 'project'
|
370
|
+
code = initialize_project
|
327
371
|
when 'plan'
|
328
372
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
329
373
|
when 'puppetfile'
|
@@ -413,7 +457,12 @@ module Bolt
|
|
413
457
|
|
414
458
|
def list_targets
|
415
459
|
update_targets(options)
|
416
|
-
outputter.print_targets(options)
|
460
|
+
outputter.print_targets(options[:targets])
|
461
|
+
end
|
462
|
+
|
463
|
+
def show_targets
|
464
|
+
update_targets(options)
|
465
|
+
outputter.print_target_info(options[:targets])
|
417
466
|
end
|
418
467
|
|
419
468
|
def list_groups
|
@@ -495,6 +544,21 @@ module Bolt
|
|
495
544
|
0
|
496
545
|
end
|
497
546
|
|
547
|
+
def initialize_project
|
548
|
+
path = File.expand_path(options[:object] || Dir.pwd)
|
549
|
+
FileUtils.mkdir_p(path)
|
550
|
+
ok = FileUtils.touch(File.join(path, 'bolt.yaml'))
|
551
|
+
|
552
|
+
result = if ok
|
553
|
+
"Successfully created Bolt project directory at #{path}"
|
554
|
+
else
|
555
|
+
"Could not create Bolt project directory at #{path}"
|
556
|
+
end
|
557
|
+
outputter.print_message result
|
558
|
+
|
559
|
+
ok ? 0 : 1
|
560
|
+
end
|
561
|
+
|
498
562
|
def install_puppetfile(config, puppetfile, modulepath)
|
499
563
|
require 'r10k/cli'
|
500
564
|
require 'bolt/r10k_log_proxy'
|
data/lib/bolt/config.rb
CHANGED
@@ -110,7 +110,11 @@ module Bolt
|
|
110
110
|
def normalize_log(target)
|
111
111
|
return target if target == 'console'
|
112
112
|
target = target[5..-1] if target.start_with?('file:')
|
113
|
-
|
113
|
+
if @future
|
114
|
+
'file:' + File.expand_path(target, @boltdir.path)
|
115
|
+
else
|
116
|
+
'file:' + File.expand_path(target)
|
117
|
+
end
|
114
118
|
end
|
115
119
|
|
116
120
|
def update_logs(logs)
|
@@ -132,6 +136,8 @@ module Bolt
|
|
132
136
|
end
|
133
137
|
|
134
138
|
def update_from_file(data)
|
139
|
+
@future = data['future'] == true
|
140
|
+
|
135
141
|
if data['log'].is_a?(Hash)
|
136
142
|
update_logs(data['log'])
|
137
143
|
end
|
@@ -164,8 +170,6 @@ module Bolt
|
|
164
170
|
@plugins = data['plugins'] if data.key?('plugins')
|
165
171
|
@plugin_hooks.merge!(data['plugin_hooks']) if data.key?('plugin_hooks')
|
166
172
|
|
167
|
-
@future = data['future'] == true
|
168
|
-
|
169
173
|
%w[concurrency format puppetdb color transport].each do |key|
|
170
174
|
send("#{key}=", data[key]) if data.key?(key)
|
171
175
|
end
|
@@ -173,6 +177,13 @@ module Bolt
|
|
173
177
|
TRANSPORTS.each do |key, impl|
|
174
178
|
if data[key.to_s]
|
175
179
|
selected = impl.filter_options(data[key.to_s])
|
180
|
+
if @future
|
181
|
+
to_expand = %w[private-key cacert token-file] & selected.keys
|
182
|
+
to_expand.each do |opt|
|
183
|
+
selected[opt] = File.expand_path(selected[opt], @boltdir.path) if opt.is_a?(String)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
176
187
|
@transports[key] = Bolt::Util.deep_merge(@transports[key], selected)
|
177
188
|
end
|
178
189
|
if @transports[key]['interpreters']
|
data/lib/bolt/inventory.rb
CHANGED
@@ -47,6 +47,7 @@ module Bolt
|
|
47
47
|
if ENV.include?(ENVIRONMENT_VAR)
|
48
48
|
begin
|
49
49
|
data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
|
50
|
+
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
|
50
51
|
rescue Psych::Exception
|
51
52
|
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
|
52
53
|
end
|
@@ -67,7 +68,7 @@ module Bolt
|
|
67
68
|
when 2
|
68
69
|
Bolt::Inventory::Inventory2.new(data, config, plugins: plugins)
|
69
70
|
else
|
70
|
-
raise ValidationError
|
71
|
+
raise ValidationError.new("Unsupported version #{version} specified in inventory", nil)
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
@@ -172,6 +173,14 @@ module Bolt
|
|
172
173
|
@target_features[target.name] || Set.new
|
173
174
|
end
|
174
175
|
|
176
|
+
def target_alias(target)
|
177
|
+
@groups.node_aliases.each_with_object([]) do |(alia, name), acc|
|
178
|
+
if target.name == name
|
179
|
+
acc << alia
|
180
|
+
end
|
181
|
+
end.uniq
|
182
|
+
end
|
183
|
+
|
175
184
|
def data_hash
|
176
185
|
{
|
177
186
|
data: @data,
|
@@ -9,7 +9,6 @@ module Bolt
|
|
9
9
|
class Group2
|
10
10
|
attr_accessor :name, :groups
|
11
11
|
|
12
|
-
# THESE are duplicates with the old groups for now.
|
13
12
|
# Regex used to validate group names and target aliases.
|
14
13
|
NAME_REGEX = /\A[a-z0-9_][a-z0-9_-]*\Z/.freeze
|
15
14
|
|
@@ -41,8 +40,7 @@ module Bolt
|
|
41
40
|
|
42
41
|
@unresolved_targets = {}
|
43
42
|
@resolved_targets = {}
|
44
|
-
|
45
|
-
# @target_objects = {}
|
43
|
+
|
46
44
|
@aliases = {}
|
47
45
|
@string_targets = []
|
48
46
|
|
@@ -57,7 +55,7 @@ module Bolt
|
|
57
55
|
elsif target.is_a?(Hash)
|
58
56
|
add_target_definition(target)
|
59
57
|
else
|
60
|
-
raise ValidationError.new("
|
58
|
+
raise ValidationError.new("Target entry must be a String or Hash, not #{target.class}", @name)
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -150,6 +148,7 @@ module Bolt
|
|
150
148
|
resolved_data = resolve_data_keys(target, target_name).merge(
|
151
149
|
'name' => target['name'],
|
152
150
|
'uri' => target['uri'],
|
151
|
+
'alias' => target['alias'],
|
153
152
|
# groups come from group_data
|
154
153
|
'groups' => []
|
155
154
|
)
|
@@ -159,12 +158,16 @@ module Bolt
|
|
159
158
|
end
|
160
159
|
end
|
161
160
|
|
161
|
+
def all_target_names
|
162
|
+
@unresolved_targets.keys + @resolved_targets.keys
|
163
|
+
end
|
164
|
+
|
162
165
|
def add_target_definition(target)
|
163
166
|
# This check ensures target lookup plugins do not returns bare strings.
|
164
|
-
# Remove it if we decide to allows task plugins to return string
|
167
|
+
# Remove it if we decide to allows task plugins to return string Target
|
165
168
|
# names.
|
166
169
|
unless target.is_a?(Hash)
|
167
|
-
raise ValidationError.new("
|
170
|
+
raise ValidationError.new("Target entry must be a Hash, not #{target.class}", @name)
|
168
171
|
end
|
169
172
|
|
170
173
|
target['name'] = resolve_references(target['name']) if target.key?('name')
|
@@ -201,14 +204,7 @@ module Bolt
|
|
201
204
|
raise ValidationError.new(msg, @name)
|
202
205
|
end
|
203
206
|
|
204
|
-
aliases
|
205
|
-
raise ValidationError.new("Invalid alias #{alia}", @name) unless alia =~ NAME_REGEX
|
206
|
-
|
207
|
-
if (found = @aliases[alia])
|
208
|
-
raise ValidationError.new(alias_conflict(alia, found, t_name), @name)
|
209
|
-
end
|
210
|
-
@aliases[alia] = t_name
|
211
|
-
end
|
207
|
+
insert_alia(t_name, aliases)
|
212
208
|
end
|
213
209
|
|
214
210
|
@unresolved_targets[t_name] = target
|
@@ -218,6 +214,17 @@ module Bolt
|
|
218
214
|
@resolved_targets[target.name] = { 'name' => target.name }
|
219
215
|
end
|
220
216
|
|
217
|
+
def insert_alia(target_name, aliases)
|
218
|
+
aliases.each do |alia|
|
219
|
+
raise ValidationError.new("Invalid alias #{alia}", @name) unless alia =~ NAME_REGEX
|
220
|
+
|
221
|
+
if (found = @aliases[alia])
|
222
|
+
raise ValidationError.new(alias_conflict(alia, found, target_name), @name)
|
223
|
+
end
|
224
|
+
@aliases[alia] = target_name
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
221
228
|
def data_merge(data1, data2)
|
222
229
|
if data2.nil? || data1.nil?
|
223
230
|
return data2 || data1
|
@@ -227,6 +234,7 @@ module Bolt
|
|
227
234
|
'config' => Bolt::Util.deep_merge(data1['config'], data2['config']),
|
228
235
|
'name' => data1['name'] || data2['name'],
|
229
236
|
'uri' => data1['uri'] || data2['uri'],
|
237
|
+
'alias' => data1['alias'] || data2['alias'],
|
230
238
|
# Shallow merge instead of deep merge so that vars with a hash value
|
231
239
|
# are assigned a new hash, rather than merging the existing value
|
232
240
|
# with the value meant to replace it
|
@@ -277,7 +285,7 @@ module Bolt
|
|
277
285
|
end
|
278
286
|
|
279
287
|
private def alias_target_conflict(name)
|
280
|
-
"
|
288
|
+
"Target name #{name} conflicts with alias of the same name"
|
281
289
|
end
|
282
290
|
|
283
291
|
def validate_group_input(input)
|
@@ -176,10 +176,29 @@ module Bolt
|
|
176
176
|
unless existing_target
|
177
177
|
add_to_group([new_target], 'all')
|
178
178
|
end
|
179
|
+
# Insert target alias into groups that contain the target
|
180
|
+
if (aliases = new_target.target_alias)
|
181
|
+
aliases = [aliases] if aliases.is_a?(String)
|
182
|
+
unless aliases.is_a?(Array)
|
183
|
+
msg = "Alias entry on #{t_name} must be a String or Array, not #{aliases.class}"
|
184
|
+
raise ValidationError.new(msg, @name)
|
185
|
+
end
|
186
|
+
|
187
|
+
insert_alias_into_group(@groups, new_target.name, aliases)
|
188
|
+
end
|
179
189
|
|
180
190
|
new_target
|
181
191
|
end
|
182
192
|
|
193
|
+
def insert_alias_into_group(group, target_name, aliases)
|
194
|
+
if group.all_target_names.include?(target_name)
|
195
|
+
group.insert_alia(target_name, aliases)
|
196
|
+
end
|
197
|
+
group.groups.each do |grp|
|
198
|
+
insert_alias_into_group(grp, target_name, aliases)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
183
202
|
def add_to_group(targets, desired_group)
|
184
203
|
if group_names.include?(desired_group)
|
185
204
|
targets.each do |target|
|
@@ -4,7 +4,7 @@ module Bolt
|
|
4
4
|
class Inventory
|
5
5
|
# This class represents the active state of a target within the inventory.
|
6
6
|
class Target
|
7
|
-
attr_reader :name, :uri, :safe_name
|
7
|
+
attr_reader :name, :uri, :safe_name, :target_alias
|
8
8
|
|
9
9
|
def initialize(target_data, inventory)
|
10
10
|
unless target_data['name'] || target_data['uri']
|
@@ -34,7 +34,9 @@ module Bolt
|
|
34
34
|
@features = target_data['features'] || Set.new
|
35
35
|
@options = target_data['options'] || {}
|
36
36
|
@plugin_hooks = target_data['plugin_hooks'] || {}
|
37
|
-
|
37
|
+
# When alias is specified in a plan, the key will be `target_alias`, when
|
38
|
+
# alias is specified in inventory the key will be `alias`.
|
39
|
+
@target_alias = target_data['target_alias'] || target_data['alias'] || []
|
38
40
|
|
39
41
|
@inventory = inventory
|
40
42
|
|
@@ -42,7 +44,6 @@ module Bolt
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def vars
|
45
|
-
# XXX Return vars from the cache
|
46
47
|
group_cache['vars'].merge(@vars)
|
47
48
|
end
|
48
49
|
|
@@ -55,7 +56,6 @@ module Bolt
|
|
55
56
|
# rubocop:enable Naming/AccessorMethodName
|
56
57
|
|
57
58
|
def facts
|
58
|
-
# XXX Return facts from the cache
|
59
59
|
Bolt::Util.deep_merge(group_cache['facts'], @facts)
|
60
60
|
end
|
61
61
|
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -135,7 +135,7 @@ module Bolt
|
|
135
135
|
target_str = if targets.length > 5
|
136
136
|
"#{targets.count} targets"
|
137
137
|
else
|
138
|
-
targets.map(&:
|
138
|
+
targets.map(&:safe_name).join(', ')
|
139
139
|
end
|
140
140
|
@stream.puts(colorize(:green, "Starting: #{description} on #{target_str}"))
|
141
141
|
end
|
@@ -219,7 +219,7 @@ module Bolt
|
|
219
219
|
# Building lots of strings...
|
220
220
|
pretty_params = +""
|
221
221
|
task_info = +""
|
222
|
-
usage = +"bolt task run --
|
222
|
+
usage = +"bolt task run --targets <node-name> #{task['name']}"
|
223
223
|
|
224
224
|
task['metadata']['parameters']&.each do |k, v|
|
225
225
|
pretty_params << "- #{k}: #{v['type'] || 'Any'}\n"
|
@@ -311,10 +311,17 @@ module Bolt
|
|
311
311
|
end
|
312
312
|
end
|
313
313
|
|
314
|
-
def print_targets(
|
315
|
-
|
314
|
+
def print_targets(targets)
|
315
|
+
count = "#{targets.count} target#{'s' unless targets.count == 1}"
|
316
|
+
@stream.puts targets.map(&:name).join("\n")
|
317
|
+
@stream.puts colorize(:green, count)
|
318
|
+
end
|
319
|
+
|
320
|
+
def print_target_info(targets)
|
321
|
+
@stream.puts ::JSON.pretty_generate(
|
322
|
+
"targets": targets.map(&:detail)
|
323
|
+
)
|
316
324
|
count = "#{targets.count} target#{'s' unless targets.count == 1}"
|
317
|
-
@stream.puts targets.join("\n")
|
318
325
|
@stream.puts colorize(:green, count)
|
319
326
|
end
|
320
327
|
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -89,11 +89,18 @@ module Bolt
|
|
89
89
|
"moduledir": moduledir }.to_json)
|
90
90
|
end
|
91
91
|
|
92
|
-
def print_targets(
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
92
|
+
def print_targets(targets)
|
93
|
+
@stream.puts ::JSON.pretty_generate(
|
94
|
+
"targets": targets.map(&:name),
|
95
|
+
"count": targets.count
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def print_target_info(targets)
|
100
|
+
@stream.puts ::JSON.pretty_generate(
|
101
|
+
"targets": targets.map(&:detail),
|
102
|
+
"count": targets.count
|
103
|
+
)
|
97
104
|
end
|
98
105
|
|
99
106
|
def print_groups(groups)
|
data/lib/bolt/plugin/prompt.rb
CHANGED
@@ -19,9 +19,12 @@ module Bolt
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def resolve_reference(opts)
|
22
|
-
|
22
|
+
# rubocop:disable Style/GlobalVars
|
23
|
+
$future ? STDERR.print("#{opts['message']}:") : STDOUT.print("#{opts['message']}:")
|
23
24
|
value = STDIN.noecho(&:gets).chomp
|
24
|
-
STDOUT.puts
|
25
|
+
$future ? STDERR.puts : STDOUT.puts
|
26
|
+
# rubocop:enable Style/GlobalVars
|
27
|
+
|
25
28
|
value
|
26
29
|
end
|
27
30
|
end
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -19,7 +19,7 @@ module Bolt
|
|
19
19
|
|
20
20
|
end
|
21
21
|
|
22
|
-
def self.load_config(filename, options)
|
22
|
+
def self.load_config(filename, options, boltdir_path = nil)
|
23
23
|
config = {}
|
24
24
|
global_path = Bolt::Util.windows? ? DEFAULT_CONFIG[:win_global] : DEFAULT_CONFIG[:global]
|
25
25
|
if filename
|
@@ -43,11 +43,12 @@ module Bolt
|
|
43
43
|
end
|
44
44
|
|
45
45
|
config = config.fetch('puppetdb', {})
|
46
|
-
new(config.merge(options))
|
46
|
+
new(config.merge(options), boltdir_path)
|
47
47
|
end
|
48
48
|
|
49
|
-
def initialize(settings)
|
49
|
+
def initialize(settings, boltdir_path = nil)
|
50
50
|
@settings = settings
|
51
|
+
@boltdir_path = boltdir_path
|
51
52
|
expand_paths
|
52
53
|
end
|
53
54
|
|
@@ -66,7 +67,14 @@ module Bolt
|
|
66
67
|
|
67
68
|
def expand_paths
|
68
69
|
%w[cacert cert key token].each do |file|
|
69
|
-
|
70
|
+
next unless @settings[file]
|
71
|
+
# rubocop:disable Style/GlobalVars
|
72
|
+
@settings[file] = if $future && @boltdir_path
|
73
|
+
File.expand_path(@settings[file], @boltdir_path)
|
74
|
+
else
|
75
|
+
File.expand_path(@settings[file])
|
76
|
+
end
|
77
|
+
# rubocop:enable Style/GlobalVars
|
70
78
|
end
|
71
79
|
end
|
72
80
|
|
data/lib/bolt/target.rb
CHANGED
@@ -14,6 +14,7 @@ module Bolt
|
|
14
14
|
new(target.name, inventory)
|
15
15
|
end
|
16
16
|
|
17
|
+
# TODO: Disallow any positional argument other than URI.
|
17
18
|
# Target.new from a plan with just a uri. Puppet requires the arguments to
|
18
19
|
# this method to match (by name) the attributes defined on the datatype.
|
19
20
|
# rubocop:disable Lint/UnusedMethodArgument
|
@@ -27,12 +28,12 @@ module Bolt
|
|
27
28
|
plugin_hooks = nil)
|
28
29
|
from_asserted_hash('uri' => uri)
|
29
30
|
end
|
31
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
30
32
|
|
31
33
|
def initialize(name, inventory = nil)
|
32
34
|
@name = name
|
33
35
|
@inventory = inventory
|
34
36
|
end
|
35
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
36
37
|
|
37
38
|
# features returns an array to be compatible with plans
|
38
39
|
def features
|
@@ -80,6 +81,19 @@ module Bolt
|
|
80
81
|
)
|
81
82
|
end
|
82
83
|
|
84
|
+
def detail
|
85
|
+
{
|
86
|
+
'name' => name,
|
87
|
+
'uri' => uri,
|
88
|
+
'alias' => target_alias,
|
89
|
+
'config' => Bolt::Util.deep_merge(config, 'transport' => transport, transport => options),
|
90
|
+
'vars' => vars,
|
91
|
+
'features' => features,
|
92
|
+
'facts' => facts,
|
93
|
+
'plugin_hooks' => plugin_hooks
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
83
97
|
def inventory_target
|
84
98
|
@inventory.targets[@name]
|
85
99
|
end
|
@@ -226,6 +240,18 @@ module Bolt
|
|
226
240
|
end
|
227
241
|
end
|
228
242
|
|
243
|
+
def vars
|
244
|
+
@inventory.vars(self)
|
245
|
+
end
|
246
|
+
|
247
|
+
def facts
|
248
|
+
@inventory.facts(self)
|
249
|
+
end
|
250
|
+
|
251
|
+
def target_alias
|
252
|
+
@inventory.target_alias(self)
|
253
|
+
end
|
254
|
+
|
229
255
|
# TODO: WHAT does equality mean here?
|
230
256
|
# should we just compare names? is there something else that is meaninful?
|
231
257
|
def eql?(other)
|
@@ -258,6 +284,21 @@ module Bolt
|
|
258
284
|
)
|
259
285
|
end
|
260
286
|
|
287
|
+
def detail
|
288
|
+
{
|
289
|
+
'name' => name,
|
290
|
+
'alias' => target_alias,
|
291
|
+
'config' => {
|
292
|
+
'transport' => transport,
|
293
|
+
transport => options
|
294
|
+
},
|
295
|
+
'vars' => vars,
|
296
|
+
'facts' => facts,
|
297
|
+
'features' => features.to_a,
|
298
|
+
'plugin_hooks' => plugin_hooks
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
261
302
|
def host
|
262
303
|
@uri_obj&.hostname || @host
|
263
304
|
end
|
data/lib/bolt/version.rb
CHANGED
data/lib/bolt_spec/plans.rb
CHANGED
@@ -4,6 +4,7 @@ require 'bolt_spec/plans/mock_executor'
|
|
4
4
|
require 'bolt/config'
|
5
5
|
require 'bolt/inventory'
|
6
6
|
require 'bolt/pal'
|
7
|
+
require 'bolt/plugin'
|
7
8
|
|
8
9
|
# These helpers are intended to be used for plan unit testing without calling
|
9
10
|
# out to target nodes. It accomplishes this by replacing bolt's executor with a
|
@@ -158,6 +159,13 @@ module BoltSpec
|
|
158
159
|
raise "RSpec.configuration.module_path not defined set up rspec puppet or define modulepath for this test"
|
159
160
|
end
|
160
161
|
|
162
|
+
def plugins
|
163
|
+
@plugins ||= Bolt::Plugin.setup(config,
|
164
|
+
pal,
|
165
|
+
puppetdb_client,
|
166
|
+
Bolt::Analytics::NoopClient.new)
|
167
|
+
end
|
168
|
+
|
161
169
|
# Override in your tests
|
162
170
|
def config
|
163
171
|
@config ||= begin
|
@@ -168,8 +176,12 @@ module BoltSpec
|
|
168
176
|
end
|
169
177
|
|
170
178
|
# Override in your tests
|
179
|
+
def inventory_data
|
180
|
+
{}
|
181
|
+
end
|
182
|
+
|
171
183
|
def inventory
|
172
|
-
@inventory ||= Bolt::Inventory.
|
184
|
+
@inventory ||= Bolt::Inventory.create_version(inventory_data, config, plugins)
|
173
185
|
end
|
174
186
|
|
175
187
|
# Provided as a class so expectations can be placed on it.
|
@@ -179,6 +191,10 @@ module BoltSpec
|
|
179
191
|
@puppetdb_client ||= MockPuppetDBClient.new
|
180
192
|
end
|
181
193
|
|
194
|
+
def pal
|
195
|
+
@pal ||= Bolt::PAL.new(config.modulepath, config.hiera_config, config.boltdir.resource_types)
|
196
|
+
end
|
197
|
+
|
182
198
|
def run_plan(name, params)
|
183
199
|
pal = Bolt::PAL.new(config.modulepath, config.hiera_config, config.boltdir.resource_types)
|
184
200
|
result = pal.run_plan(name, params, executor, inventory, puppetdb_client)
|
@@ -23,7 +23,10 @@ module BoltSpec
|
|
23
23
|
@calls += 1
|
24
24
|
if @return_block
|
25
25
|
# Merge arguments and options into params to match puppet function signature.
|
26
|
-
|
26
|
+
params = options.map { |k, v| ["_#{k}", v] }.to_h
|
27
|
+
params = params.merge(arguments)
|
28
|
+
|
29
|
+
check_resultset(@return_block.call(targets: targets, task: task, params: params), task)
|
27
30
|
else
|
28
31
|
Bolt::ResultSet.new(targets.map { |target| @data[target.name] || default_for(target) })
|
29
32
|
end
|
@@ -17,7 +17,7 @@ module BoltSpec
|
|
17
17
|
# Nothing on the executor is 'public'
|
18
18
|
class MockExecutor
|
19
19
|
attr_reader :noop, :error_message
|
20
|
-
attr_accessor :run_as
|
20
|
+
attr_accessor :run_as, :transport_features
|
21
21
|
|
22
22
|
def initialize(modulepath)
|
23
23
|
@noop = false
|
@@ -27,6 +27,7 @@ module BoltSpec
|
|
27
27
|
@modulepath = [modulepath].flatten.map { |path| File.absolute_path(path) }
|
28
28
|
MOCKED_ACTIONS.each { |action| instance_variable_set(:"@#{action}_doubles", {}) }
|
29
29
|
@stub_out_message = nil
|
30
|
+
@transport_features = ['puppet-agent']
|
30
31
|
end
|
31
32
|
|
32
33
|
def module_file_id(file)
|
@@ -168,12 +169,12 @@ module BoltSpec
|
|
168
169
|
|
169
170
|
# Mocked for apply_prep
|
170
171
|
def transport(_protocol)
|
171
|
-
# Always return a transport that includes the puppet-agent feature so version/install are skipped.
|
172
172
|
Class.new do
|
173
|
-
|
174
|
-
|
173
|
+
attr_reader :provided_features
|
174
|
+
def initialize(features)
|
175
|
+
@provided_features = features
|
175
176
|
end
|
176
|
-
end.new
|
177
|
+
end.new(transport_features)
|
177
178
|
end
|
178
179
|
# End apply_prep mocking
|
179
180
|
end
|
data/lib/bolt_spec/run.rb
CHANGED
@@ -5,6 +5,7 @@ require 'bolt/config'
|
|
5
5
|
require 'bolt/executor'
|
6
6
|
require 'bolt/inventory'
|
7
7
|
require 'bolt/pal'
|
8
|
+
require 'bolt/plugin'
|
8
9
|
require 'bolt/puppetdb'
|
9
10
|
require 'bolt/util'
|
10
11
|
|
@@ -130,20 +131,30 @@ module BoltSpec
|
|
130
131
|
# still be loaded
|
131
132
|
def self.with_runner(config_data, inventory_data)
|
132
133
|
Dir.mktmpdir do |boltdir_path|
|
133
|
-
|
134
|
-
|
135
|
-
yield new(config, inventory)
|
134
|
+
runner = new(config_data, inventory_data, boltdir_path)
|
135
|
+
yield runner
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@
|
143
|
-
@inventory = inventory
|
139
|
+
def initialize(config_data, inventory_data, boltdir_path)
|
140
|
+
@config_data = config_data || {}
|
141
|
+
@inventory_data = inventory_data || {}
|
142
|
+
@boltdir_path = boltdir_path
|
144
143
|
@analytics = Bolt::Analytics::NoopClient.new
|
145
144
|
end
|
146
145
|
|
146
|
+
def config
|
147
|
+
@config ||= Bolt::Config.new(Bolt::Boltdir.new(@boltdir_path), @config_data)
|
148
|
+
end
|
149
|
+
|
150
|
+
def inventory
|
151
|
+
@inventory ||= Bolt::Inventory.create_version(@inventory_data, config, plugins)
|
152
|
+
end
|
153
|
+
|
154
|
+
def plugins
|
155
|
+
@plugins ||= Bolt::Plugin.setup(config, pal, puppetdb_client, @analytics)
|
156
|
+
end
|
157
|
+
|
147
158
|
def puppetdb_client
|
148
159
|
@puppetdb_client ||= begin
|
149
160
|
puppetdb_config = Bolt::PuppetDB::Config.load_config(nil, config.puppetdb)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.36.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -319,6 +319,7 @@ executables:
|
|
319
319
|
extensions: []
|
320
320
|
extra_rdoc_files: []
|
321
321
|
files:
|
322
|
+
- Puppetfile
|
322
323
|
- bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb
|
323
324
|
- bolt-modules/boltlib/lib/puppet/datatypes/result.rb
|
324
325
|
- bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb
|