bolt 2.35.0 → 2.40.2

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.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
  4. data/lib/bolt/analytics.rb +27 -8
  5. data/lib/bolt/apply_result.rb +3 -3
  6. data/lib/bolt/bolt_option_parser.rb +45 -18
  7. data/lib/bolt/cli.rb +92 -110
  8. data/lib/bolt/config.rb +184 -80
  9. data/lib/bolt/config/options.rb +144 -83
  10. data/lib/bolt/config/transport/base.rb +10 -19
  11. data/lib/bolt/config/transport/local.rb +1 -7
  12. data/lib/bolt/config/transport/options.rb +11 -68
  13. data/lib/bolt/config/transport/ssh.rb +8 -19
  14. data/lib/bolt/executor.rb +5 -17
  15. data/lib/bolt/inventory.rb +25 -0
  16. data/lib/bolt/inventory/group.rb +0 -8
  17. data/lib/bolt/inventory/options.rb +130 -0
  18. data/lib/bolt/inventory/target.rb +10 -11
  19. data/lib/bolt/module_installer.rb +21 -13
  20. data/lib/bolt/module_installer/resolver.rb +1 -1
  21. data/lib/bolt/outputter.rb +19 -5
  22. data/lib/bolt/outputter/human.rb +20 -1
  23. data/lib/bolt/outputter/json.rb +1 -1
  24. data/lib/bolt/outputter/logger.rb +1 -1
  25. data/lib/bolt/outputter/rainbow.rb +13 -2
  26. data/lib/bolt/plugin.rb +41 -12
  27. data/lib/bolt/plugin/cache.rb +76 -0
  28. data/lib/bolt/plugin/module.rb +4 -4
  29. data/lib/bolt/plugin/puppetdb.rb +1 -1
  30. data/lib/bolt/project.rb +59 -40
  31. data/lib/bolt/project_manager.rb +201 -0
  32. data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +49 -4
  33. data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +3 -3
  34. data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
  35. data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
  36. data/lib/bolt/puppetdb/client.rb +8 -0
  37. data/lib/bolt/puppetdb/config.rb +1 -2
  38. data/lib/bolt/rerun.rb +1 -5
  39. data/lib/bolt/shell/bash.rb +8 -2
  40. data/lib/bolt/shell/powershell.rb +21 -3
  41. data/lib/bolt/target.rb +4 -0
  42. data/lib/bolt/task/run.rb +1 -1
  43. data/lib/bolt/transport/local.rb +13 -0
  44. data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
  45. data/lib/bolt/util.rb +36 -7
  46. data/lib/bolt/validator.rb +227 -0
  47. data/lib/bolt/version.rb +1 -1
  48. data/lib/bolt_server/base_config.rb +3 -1
  49. data/lib/bolt_server/config.rb +3 -1
  50. data/lib/bolt_server/plugin.rb +13 -0
  51. data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
  52. data/lib/bolt_server/schemas/connect-data.json +22 -0
  53. data/lib/bolt_server/schemas/partials/task.json +3 -3
  54. data/lib/bolt_server/transport_app.rb +68 -40
  55. data/libexec/apply_catalog.rb +1 -1
  56. data/libexec/custom_facts.rb +1 -1
  57. data/libexec/query_resources.rb +1 -1
  58. metadata +23 -17
  59. data/lib/bolt/project_migrator.rb +0 -80
@@ -7,6 +7,7 @@ require 'bolt/project'
7
7
  require 'bolt/logger'
8
8
  require 'bolt/util'
9
9
  require 'bolt/config/options'
10
+ require 'bolt/validator'
10
11
 
11
12
  module Bolt
12
13
  class UnknownTransportError < Bolt::Error
@@ -32,53 +33,110 @@ module Bolt
32
33
  end
33
34
 
34
35
  def self.from_project(project, overrides = {})
35
- logs = []
36
+ logs = []
37
+ deprecations = []
38
+
36
39
  conf = if project.project_file == project.config_file
37
40
  project.data
38
41
  else
39
42
  c = Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
43
+
44
+ # Validate the config against the schema. This will raise a single error
45
+ # with all validation errors.
46
+ Bolt::Validator.new.tap do |validator|
47
+ validator.validate(c, bolt_schema, project.config_file.to_s)
48
+
49
+ validator.warnings.each { |warning| logs << { warn: warning } }
50
+
51
+ validator.deprecations.each do |dep|
52
+ deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
53
+ end
54
+ end
55
+
40
56
  logs << { debug: "Loaded configuration from #{project.config_file}" } if File.exist?(project.config_file)
41
57
  c
42
58
  end
43
-
44
59
  data = load_defaults(project).push(
45
- filepath: project.config_file,
46
- data: conf,
47
- logs: logs,
48
- deprecations: []
60
+ filepath: project.config_file,
61
+ data: conf,
62
+ logs: logs,
63
+ deprecations: deprecations
49
64
  )
50
65
 
51
66
  new(project, data, overrides)
52
67
  end
53
68
 
54
69
  def self.from_file(configfile, overrides = {})
55
- project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
56
- logs = []
70
+ project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
71
+ logs = []
72
+ deprecations = []
57
73
 
58
74
  conf = if project.project_file == project.config_file
59
75
  project.data
60
76
  else
61
77
  c = Bolt::Util.read_yaml_hash(configfile, 'config')
78
+
79
+ # Validate the config against the schema. This will raise a single error
80
+ # with all validation errors.
81
+ Bolt::Validator.new.tap do |validator|
82
+ validator.validate(c, bolt_schema, project.config_file.to_s)
83
+
84
+ validator.warnings.each { |warning| logs << { warn: warning } }
85
+
86
+ validator.deprecations.each do |dep|
87
+ deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
88
+ end
89
+ end
90
+
62
91
  logs << { debug: "Loaded configuration from #{configfile}" }
63
92
  c
64
93
  end
65
94
 
66
95
  data = load_defaults(project).push(
67
- filepath: configfile,
68
- data: conf,
69
- logs: logs,
70
- deprecations: []
96
+ filepath: configfile,
97
+ data: conf,
98
+ logs: logs,
99
+ deprecations: deprecations
71
100
  )
72
101
 
73
102
  new(project, data, overrides)
74
103
  end
75
104
 
76
- def self.system_path
77
- # Lazy-load expensive gem code
78
- require 'win32/dir' if Bolt::Util.windows?
105
+ # Builds a hash of definitions for transport configuration.
106
+ #
107
+ def self.transport_definitions
108
+ INVENTORY_OPTIONS.each_with_object({}) do |(option, definition), acc|
109
+ acc[option] = TRANSPORT_CONFIG.key?(option) ? definition.merge(TRANSPORT_CONFIG[option].schema) : definition
110
+ end
111
+ end
112
+
113
+ # Builds the schema for bolt-defaults.yaml used by the validator.
114
+ #
115
+ def self.defaults_schema
116
+ schema = {
117
+ type: Hash,
118
+ properties: BOLT_DEFAULTS_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
119
+ definitions: OPTIONS.merge(transport_definitions)
120
+ }
121
+
122
+ schema[:definitions]['inventory-config'][:properties] = transport_definitions
123
+
124
+ schema
125
+ end
79
126
 
127
+ # Builds the schema for bolt.yaml used by the validator.
128
+ #
129
+ def self.bolt_schema
130
+ {
131
+ type: Hash,
132
+ properties: (BOLT_OPTIONS + INVENTORY_OPTIONS.keys).map { |opt| [opt, _ref: opt] }.to_h,
133
+ definitions: OPTIONS.merge(transport_definitions)
134
+ }
135
+ end
136
+
137
+ def self.system_path
80
138
  if Bolt::Util.windows?
81
- Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc'))
139
+ Pathname.new(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs', 'bolt', 'etc'))
82
140
  else
83
141
  Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
84
142
  end
@@ -94,9 +152,10 @@ module Bolt
94
152
  # projects. This file does not allow project-specific configuration such as 'hiera-config' and
95
153
  # 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
96
154
  def self.load_bolt_defaults_yaml(dir)
97
- filepath = dir + BOLT_DEFAULTS_NAME
98
- data = Bolt::Util.read_yaml_hash(filepath, 'config')
99
- logs = [{ debug: "Loaded configuration from #{filepath}" }]
155
+ filepath = dir + BOLT_DEFAULTS_NAME
156
+ data = Bolt::Util.read_yaml_hash(filepath, 'config')
157
+ logs = [{ debug: "Loaded configuration from #{filepath}" }]
158
+ deprecations = []
100
159
 
101
160
  # Warn if 'bolt.yaml' detected in same directory.
102
161
  if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
@@ -106,6 +165,18 @@ module Bolt
106
165
  )
107
166
  end
108
167
 
168
+ # Validate the config against the schema. This will raise a single error
169
+ # with all validation errors.
170
+ Bolt::Validator.new.tap do |validator|
171
+ validator.validate(data, defaults_schema, filepath)
172
+
173
+ validator.warnings.each { |warning| logs << { warn: warning } }
174
+
175
+ validator.deprecations.each do |dep|
176
+ deprecations << { type: "#{BOLT_DEFAULTS_NAME} #{dep[:option]}", msg: dep[:message] }
177
+ end
178
+ end
179
+
109
180
  # Remove project-specific config such as hiera-config, etc.
110
181
  project_config = data.slice(*(BOLT_PROJECT_OPTIONS - BOLT_DEFAULTS_OPTIONS))
111
182
 
@@ -148,18 +219,30 @@ module Bolt
148
219
  data = data.merge(data.delete('inventory-config'))
149
220
  end
150
221
 
151
- { filepath: filepath, data: data, logs: logs, deprecations: [] }
222
+ { filepath: filepath, data: data, logs: logs, deprecations: deprecations }
152
223
  end
153
224
 
154
225
  # Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
155
226
  # here since Bolt::Config will just ignore any invalid keys.
156
227
  def self.load_bolt_yaml(dir)
157
- filepath = dir + BOLT_CONFIG_NAME
158
- data = Bolt::Util.read_yaml_hash(filepath, 'config')
159
- logs = [{ debug: "Loaded configuration from #{filepath}" }]
228
+ filepath = dir + BOLT_CONFIG_NAME
229
+ data = Bolt::Util.read_yaml_hash(filepath, 'config')
230
+ logs = [{ debug: "Loaded configuration from #{filepath}" }]
160
231
  deprecations = [{ type: 'Using bolt.yaml for system configuration',
161
- msg: "Configuration file #{filepath} is deprecated and will be removed in a future version "\
162
- "of Bolt. Use '#{dir + BOLT_DEFAULTS_NAME}' instead." }]
232
+ msg: "Configuration file #{filepath} is deprecated and will be removed in Bolt 3.0. "\
233
+ "See https://pup.pt/update-bolt-config for how to update to the latest Bolt practices." }]
234
+
235
+ # Validate the config against the schema. This will raise a single error
236
+ # with all validation errors.
237
+ Bolt::Validator.new.tap do |validator|
238
+ validator.validate(data, bolt_schema, filepath)
239
+
240
+ validator.warnings.each { |warning| logs << { warn: warning } }
241
+
242
+ validator.deprecations.each do |dep|
243
+ deprecations << { type: "#{BOLT_CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
244
+ end
245
+ end
163
246
 
164
247
  { filepath: filepath, data: data, logs: logs, deprecations: deprecations }
165
248
  end
@@ -206,17 +289,21 @@ module Bolt
206
289
  @config_files = []
207
290
 
208
291
  default_data = {
292
+ 'apply-settings' => {},
209
293
  'apply_settings' => {},
210
294
  'color' => true,
211
295
  'compile-concurrency' => Etc.nprocessors,
212
296
  'concurrency' => default_concurrency,
213
297
  'format' => 'human',
214
298
  'log' => { 'console' => {} },
299
+ 'module-install' => {},
300
+ 'plugin-hooks' => {},
215
301
  'plugin_hooks' => {},
216
302
  'plugins' => {},
217
303
  'puppetdb' => {},
218
304
  'puppetfile' => {},
219
305
  'save-rerun' => true,
306
+ 'spinner' => true,
220
307
  'transport' => 'ssh'
221
308
  }
222
309
 
@@ -271,7 +358,7 @@ module Bolt
271
358
 
272
359
  # Set console log to debug if in debug mode
273
360
  if options[:debug]
274
- overrides['log'] = { 'console' => { 'level' => :debug } }
361
+ overrides['log'] = { 'console' => { 'level' => 'debug' } }
275
362
  end
276
363
 
277
364
  if options[:puppetfile_path]
@@ -280,6 +367,9 @@ module Bolt
280
367
 
281
368
  overrides['trace'] = opts['trace'] if opts.key?('trace')
282
369
 
370
+ # Validate the overrides
371
+ Bolt::Validator.new.validate(overrides, self.class.bolt_schema, 'command line')
372
+
283
373
  overrides
284
374
  end
285
375
 
@@ -296,7 +386,7 @@ module Bolt
296
386
  when *TRANSPORT_CONFIG.keys
297
387
  Bolt::Util.deep_merge(val1, val2)
298
388
  # Hash values are shallow merged
299
- when 'puppetdb', 'plugin_hooks', 'apply_settings', 'log'
389
+ when 'puppetdb', 'plugin-hooks', 'plugin_hooks', 'apply-settings', 'apply_settings', 'log'
300
390
  val1.merge(val2)
301
391
  # All other values are overwritten
302
392
  else
@@ -333,8 +423,9 @@ module Bolt
333
423
  end
334
424
 
335
425
  # Filter hashes to only include valid options
336
- @data['apply_settings'] = @data['apply_settings'].slice(*OPTIONS['apply_settings'][:properties].keys)
337
- @data['puppetfile'] = @data['puppetfile'].slice(*OPTIONS['puppetfile'][:properties].keys)
426
+ %w[apply-settings apply_settings module-install puppetfile].each do |opt|
427
+ @data[opt] = @data[opt].slice(*OPTIONS.dig(opt, :properties).keys)
428
+ end
338
429
  end
339
430
 
340
431
  private def normalize_log(target)
@@ -357,31 +448,14 @@ module Bolt
357
448
  next if val == 'disable'
358
449
 
359
450
  name = normalize_log(key)
451
+ acc[name] = val.slice('append', 'level').transform_keys(&:to_sym)
360
452
 
361
- # But otherwise it has to be a Hash
362
- unless val.is_a?(Hash)
363
- raise Bolt::ValidationError,
364
- "config of log #{name} must be a Hash, received #{val.class} #{val.inspect}"
365
- end
366
-
367
- acc[name] = val.slice('append', 'level')
368
- .transform_keys(&:to_sym)
453
+ next unless acc[name][:level] == 'notice'
369
454
 
370
- if (v = acc[name][:level])
371
- unless v.is_a?(String) || v.is_a?(Symbol)
372
- raise Bolt::ValidationError,
373
- "level of log #{name} must be a String or Symbol, received #{v.class} #{v.inspect}"
374
- end
375
- unless Bolt::Logger.valid_level?(v)
376
- raise Bolt::ValidationError,
377
- "level of log #{name} must be one of #{Bolt::Logger.levels.join(', ')}; received #{v}"
378
- end
379
- end
380
-
381
- if (v = acc[name][:append]) && v != true && v != false
382
- raise Bolt::ValidationError,
383
- "append flag of log #{name} must be a Boolean, received #{v.class} #{v.inspect}"
384
- end
455
+ @deprecations << {
456
+ type: 'notice log level',
457
+ msg: "Log level 'notice' is deprecated and will be removed in Bolt 3.0. Use 'info' instead."
458
+ }
385
459
  end
386
460
  end
387
461
 
@@ -397,40 +471,36 @@ module Bolt
397
471
  "is automatically appended to the modulepath and cannot be configured."
398
472
  end
399
473
 
400
- keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
401
- keys.each do |key|
402
- next unless Bolt::Util.references?(@data[key])
403
- valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks puppetdb]
404
- raise Bolt::ValidationError,
405
- "Found unsupported key _plugin in config setting #{key}. Plugins are only available in "\
406
- "#{valid_keys.join(', ')}."
407
- end
408
-
409
- unless concurrency.is_a?(Integer) && concurrency > 0
410
- raise Bolt::ValidationError,
411
- "Concurrency must be a positive Integer, received #{concurrency.class} #{concurrency}"
412
- end
413
-
414
- unless compile_concurrency.is_a?(Integer) && compile_concurrency > 0
415
- raise Bolt::ValidationError,
416
- "Compile concurrency must be a positive Integer, received #{compile_concurrency.class} "\
417
- "#{compile_concurrency}"
418
- end
419
-
420
474
  compile_limit = 2 * Etc.nprocessors
421
475
  unless compile_concurrency < compile_limit
422
476
  raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
423
477
  end
424
478
 
425
- unless %w[human json rainbow].include? format
426
- raise Bolt::ValidationError, "Unsupported format: '#{format}'"
479
+ %w[hiera-config trusted-external-command inventoryfile].each do |opt|
480
+ Bolt::Util.validate_file(opt, @data[opt]) if @data[opt]
427
481
  end
428
482
 
429
- Bolt::Util.validate_file('hiera-config', @data['hiera-config']) if @data['hiera-config']
430
- Bolt::Util.validate_file('trusted-external-command', trusted_external) if trusted_external
483
+ if File.exist?(default_inventoryfile)
484
+ Bolt::Util.validate_file('inventory file', default_inventoryfile)
485
+ end
431
486
 
432
- unless TRANSPORT_CONFIG.include?(transport)
433
- raise UnknownTransportError, transport
487
+ # Warn the user how they should be using the 'puppetfile' or
488
+ # 'module-install' config options. We don't error here since these
489
+ # settings can be set at the user or system level.
490
+ if @project.modules && puppetfile_config.any? && module_install.empty?
491
+ command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
492
+ @logs << { warn: "Detected configuration for 'puppetfile'. This setting is not "\
493
+ "used when 'modules' is configured. Use 'module-install' instead. "\
494
+ "To automatically update your project configuration, run '#{command}'." }
495
+ elsif @project.modules.nil? && puppetfile_config.empty? && module_install.any?
496
+ @logs << { warn: "Detected configuration for 'module-install'. This setting is not "\
497
+ "used when 'modules' is not configured. Use 'puppetfile' instead." }
498
+ elsif @project.modules && puppetfile_config.any? && module_install.any?
499
+ @logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
500
+ "configuration for 'module-install' because 'modules' is also configured." }
501
+ elsif @project.modules.nil? && puppetfile_config.any? && module_install.any?
502
+ @logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
503
+ "configuration for 'puppetfile' because 'modules' is not configured." }
434
504
  end
435
505
  end
436
506
 
@@ -464,6 +534,10 @@ module Bolt
464
534
  @data['modulepath'] = value
465
535
  end
466
536
 
537
+ def plugin_cache
538
+ @project.plugin_cache || @data['plugin-cache'] || {}
539
+ end
540
+
467
541
  def concurrency
468
542
  @data['concurrency']
469
543
  end
@@ -496,6 +570,10 @@ module Bolt
496
570
  @data['save-rerun']
497
571
  end
498
572
 
573
+ def spinner
574
+ @data['spinner']
575
+ end
576
+
499
577
  def inventoryfile
500
578
  @data['inventoryfile']
501
579
  end
@@ -513,7 +591,18 @@ module Bolt
513
591
  end
514
592
 
515
593
  def plugin_hooks
516
- @data['plugin_hooks']
594
+ if @data['plugin-hooks'].any? && @data['plugin_hooks'].any?
595
+ Bolt::Logger.warn_once(
596
+ "plugin-hooks and plugin_hooks set",
597
+ "Detected configuration for 'plugin-hooks' and 'plugin_hooks'. Bolt will ignore 'plugin_hooks'."
598
+ )
599
+
600
+ @data['plugin-hooks']
601
+ elsif @data['plugin-hooks'].any?
602
+ @data['plugin-hooks']
603
+ else
604
+ @data['plugin_hooks']
605
+ end
517
606
  end
518
607
 
519
608
  def trusted_external
@@ -521,13 +610,28 @@ module Bolt
521
610
  end
522
611
 
523
612
  def apply_settings
524
- @data['apply_settings']
613
+ if @data['apply-settings'].any? && @data['apply_settings'].any?
614
+ Bolt::Logger.warn_once(
615
+ "apply-settings and apply_settings set",
616
+ "Detected configuration for 'apply-settings' and 'apply_settings'. Bolt will ignore 'apply_settings'."
617
+ )
618
+
619
+ @data['apply-settings']
620
+ elsif @data['apply-settings'].any?
621
+ @data['apply-settings']
622
+ else
623
+ @data['apply_settings']
624
+ end
525
625
  end
526
626
 
527
627
  def transport
528
628
  @data['transport']
529
629
  end
530
630
 
631
+ def module_install
632
+ @project.module_install || @data['module-install']
633
+ end
634
+
531
635
  # Check if there is a case-insensitive match to the path
532
636
  def check_path_case(type, paths)
533
637
  return if paths.nil?
@@ -33,77 +33,42 @@ module Bolt
33
33
  "_plugin" => {
34
34
  description: "The name of the plugin.",
35
35
  type: "string"
36
+ },
37
+ "_cache" => {
38
+ description: "This feature is experimental. Enable plugin caching and set a time-to-live.",
39
+ type: "object",
40
+ required: ["ttl"],
41
+ properties: {
42
+ "ttl" => {
43
+ description: "Time in seconds to keep the plugin cache.",
44
+ type: "integer",
45
+ minimum: 0
46
+ }
47
+ }
36
48
  }
37
49
  }
38
50
  }
39
51
  }.freeze
40
52
 
41
- # The following constants define the various configuration options available to Bolt.
42
- # Each constant is a hash where keys are the configuration option and values are the
43
- # option's definition. These options are used in multiple locations:
44
- #
45
- # - Automatic type validation when loading and setting configuration
46
- # - Generating reference documentation for configuration files
47
- # - Generating JSON schemas for configuration files
48
- #
49
- # Data includes keys defined by JSON Schema Draft 07 as well as some metadata used
50
- # by Bolt to generate documentation. The following keys are used:
51
- #
52
- # :description String A detailed description of the option and what it does. This
53
- # field is used in both documentation and the JSON schemas,
54
- # and should provide as much detail as possible, including
55
- # links to relevant documentation.
56
- #
57
- # :type Class The expected type of a value. These should be Ruby classes,
58
- # as this field is used to perform automatic type validation.
59
- # If an option can accept more than one type, this should be
60
- # an array of types. Boolean values should set :type to
61
- # [TrueClass, FalseClass], as Ruby does not have a single
62
- # Boolean class.
63
- #
64
- # :items Hash A definition hash for items in an array. Similar to values
65
- # for top-level options, items can have a :description, :type,
66
- # or any other key in this list.
67
- #
68
- # :uniqueItems Boolean Whether or not an array should contain only unique items.
69
- #
70
- # :properties Hash A hash where keys are sub-options and values are definitions
71
- # for the sub-option. Similar to values for top-level options,
72
- # properties can have a :description, :type, or any other key
73
- # in this list.
74
- #
75
- # :additionalProperties A variation of the :properties key, where the hash is a
76
- # Hash definition for any properties not specified in :properties.
77
- # This can be used to permit arbitrary sub-options, such as
78
- # logs for the 'log' option.
79
- #
80
- # :required Array An array of properties that are required for options that
81
- # accept Hash values.
82
- #
83
- # :minimum Integer The minimum integer value for an option.
84
- #
85
- # :enum Array An array of values that the option recognizes.
86
- #
87
- # :pattern String A JSON regex pattern that the option's vaue should match.
88
- #
89
- # :format String Requires that a string value matches a format defined by the
90
- # JSON Schema draft.
91
- #
92
- # :_plugin Boolean Whether the option accepts a plugin reference. This is used
93
- # when generating the JSON schemas to determine whether or not
94
- # to include a reference to the _plugin definition. If :_plugin
95
- # is set to true, the script that generates JSON schemas will
96
- # automatically recurse through the :items and :properties keys
97
- # and add plugin references if applicable.
98
- #
99
- # :_example Any An example value for the option. This is used to generate
100
- # reference documentation for configuration files.
101
- #
102
- # :_default Any The documented default value for the option. This is only
103
- # used to generate reference documentation for configuration
104
- # files and is not used by Bolt to actually set default values.
53
+ # Definitions used to validate config options.
54
+ # https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
105
55
  OPTIONS = {
106
56
  "apply_settings" => {
57
+ description: "A map of Puppet settings to use when applying Puppet code using the `apply` "\
58
+ "plan function or the `bolt apply` command.",
59
+ type: Hash,
60
+ properties: {
61
+ "show_diff" => {
62
+ description: "Whether to log and report a contextual diff.",
63
+ type: [TrueClass, FalseClass],
64
+ _example: true,
65
+ _default: false
66
+ }
67
+ },
68
+ _plugin: false,
69
+ _deprecation: "This option will be removed in Bolt 3.0. Use `apply-settings` instead."
70
+ },
71
+ "apply-settings" => {
107
72
  description: "A map of Puppet settings to use when applying Puppet code using the `apply` "\
108
73
  "plan function or the `bolt apply` command.",
109
74
  type: Hash,
@@ -169,9 +134,26 @@ module Bolt
169
134
  "files](inventory_file_v2.md).",
170
135
  type: String,
171
136
  _plugin: false,
137
+ _deprecation: "This option will be removed in Bolt 3.0. Use the `--inventoryfile` command-line option "\
138
+ "to use a non-default inventory file or move the file contents to `inventory.yaml` in the "\
139
+ "project directory.",
172
140
  _example: "~/.puppetlabs/bolt/inventory.yaml",
173
141
  _default: "project/inventory.yaml"
174
142
  },
143
+ "plugin-cache" => {
144
+ description: "This feature is experimental. Enable plugin caching and set the time-to-live.",
145
+ type: Hash,
146
+ required: ["ttl"],
147
+ properties: {
148
+ "ttl" => {
149
+ description: "Time in seconds to keep the plugin cache.",
150
+ type: Integer,
151
+ minimum: 0
152
+ }
153
+ },
154
+ _plugin: false,
155
+ _example: { "ttl" => 3600 }
156
+ },
175
157
  "log" => {
176
158
  description: "A map of configuration for the logfile output. Under `log`, you can configure log options "\
177
159
  "for `console` and add configuration for individual log files, such as "\
@@ -183,12 +165,13 @@ module Bolt
183
165
  properties: {
184
166
  "console" => {
185
167
  description: "Configuration for logs output to the console.",
186
- type: Hash,
168
+ type: [String, Hash],
169
+ enum: ['disable'],
187
170
  properties: {
188
171
  "level" => {
189
172
  description: "The type of information to log.",
190
173
  type: String,
191
- enum: %w[trace debug error info warn fatal any],
174
+ enum: %w[trace debug error info notice warn fatal any],
192
175
  _default: "warn"
193
176
  }
194
177
  }
@@ -207,7 +190,7 @@ module Bolt
207
190
  "level" => {
208
191
  description: "The type of information to log.",
209
192
  type: String,
210
- enum: %w[trace debug error info warn fatal any],
193
+ enum: %w[trace debug error info notice warn fatal any],
211
194
  _default: "warn"
212
195
  }
213
196
  }
@@ -227,6 +210,41 @@ module Bolt
227
210
  _example: ["~/.puppetlabs/bolt/modules", "~/.puppetlabs/bolt/site-modules"],
228
211
  _default: ["project/modules", "project/site-modules", "project/site"]
229
212
  },
213
+ "module-install" => {
214
+ description: "Options that configure where Bolt downloads modules from. This option is only used when "\
215
+ "installing modules using the `bolt module add|install` commands and "\
216
+ "`Add|Install-BoltModule` cmdlets.",
217
+ type: Hash,
218
+ properties: {
219
+ "forge" => {
220
+ description: "A subsection that can have its own `proxy` setting to set an HTTP proxy for Forge "\
221
+ "operations only, and a `baseurl` setting to specify a different Forge host.",
222
+ type: Hash,
223
+ properties: {
224
+ "baseurl" => {
225
+ description: "The URL to the Forge host.",
226
+ type: String,
227
+ format: "uri",
228
+ _example: "https://forge.example.com"
229
+ },
230
+ "proxy" => {
231
+ description: "The HTTP proxy to use for Forge operations.",
232
+ type: String,
233
+ format: "uri",
234
+ _example: "https://my-forge-proxy.com:8080"
235
+ }
236
+ },
237
+ _example: { "baseurl" => "https://forge.example.com", "proxy" => "https://my-forge-proxy.com:8080" }
238
+ },
239
+ "proxy" => {
240
+ description: "The HTTP proxy to use for Git and Forge operations.",
241
+ type: String,
242
+ format: "uri",
243
+ _example: "https://my-proxy.com:8080"
244
+ }
245
+ },
246
+ _plugin: false
247
+ },
230
248
  "modules" => {
231
249
  description: "A list of module dependencies for the project. Each dependency is a map of data specifying "\
232
250
  "the module to install. To install the project's module dependencies, run the `bolt module "\
@@ -276,8 +294,8 @@ module Bolt
276
294
  },
277
295
  "name" => {
278
296
  description: "The name of the Bolt project. When this option is configured, the project is considered a "\
279
- "[Bolt project](experimental_features.md#bolt-projects), allowing Bolt to load content from "\
280
- "the project directory as though it were a module.",
297
+ "[Bolt project](projects.md), allowing Bolt to load content from the project directory "\
298
+ "as though it were a module.",
281
299
  type: String,
282
300
  _plugin: false,
283
301
  _example: "myproject"
@@ -294,6 +312,16 @@ module Bolt
294
312
  _example: ["myproject", "myproject::foo", "myproject::bar", "myproject::deploy::*"]
295
313
  },
296
314
  "plugin_hooks" => {
315
+ description: "A map of [plugin hooks](writing_plugins.md#hooks) and which plugins a hook should use. "\
316
+ "The only configurable plugin hook is `puppet_library`, which can use two possible plugins: "\
317
+ "[`puppet_agent`](https://github.com/puppetlabs/puppetlabs-puppet_agent#puppet_agentinstall) "\
318
+ "and [`task`](using_plugins.md#task).",
319
+ type: Hash,
320
+ _plugin: true,
321
+ _example: { "puppet_library" => { "plugin" => "puppet_agent", "version" => "6.15.0", "_run_as" => "root" } },
322
+ _deprecation: "This option will be removed in Bolt 3.0. Use `plugin-hooks` instead."
323
+ },
324
+ "plugin-hooks" => {
297
325
  description: "A map of [plugin hooks](writing_plugins.md#hooks) and which plugins a hook should use. "\
298
326
  "The only configurable plugin hook is `puppet_library`, which can use two possible plugins: "\
299
327
  "[`puppet_agent`](https://github.com/puppetlabs/puppetlabs-puppet_agent#puppet_agentinstall) "\
@@ -307,7 +335,11 @@ module Bolt
307
335
  "its value is a map of configuration data. Configurable options are specified by the plugin. "\
308
336
  "Read more about configuring plugins in [Using plugins](using_plugins.md#configuring-plugins).",
309
337
  type: Hash,
310
- _plugin: true,
338
+ additionalProperties: {
339
+ type: Hash,
340
+ _plugin: true
341
+ },
342
+ _plugin: false,
311
343
  _example: { "pkcs7" => { "keysize" => 1024 } }
312
344
  },
313
345
  "puppetdb" => {
@@ -318,49 +350,57 @@ module Bolt
318
350
  "cacert" => {
319
351
  description: "The path to the ca certificate for PuppetDB.",
320
352
  type: String,
321
- _example: "/etc/puppetlabs/puppet/ssl/certs/ca.pem"
353
+ _example: "/etc/puppetlabs/puppet/ssl/certs/ca.pem",
354
+ _plugin: true
322
355
  },
323
356
  "cert" => {
324
357
  description: "The path to the client certificate file to use for authentication.",
325
358
  type: String,
326
- _example: "/etc/puppetlabs/puppet/ssl/certs/my-host.example.com.pem"
359
+ _example: "/etc/puppetlabs/puppet/ssl/certs/my-host.example.com.pem",
360
+ _plugin: true
327
361
  },
328
362
  "connect_timeout" => {
329
363
  description: "How long to wait in seconds when establishing connections with PuppetDB.",
330
364
  type: Integer,
331
365
  minimum: 1,
332
366
  _default: 60,
333
- _example: 120
367
+ _example: 120,
368
+ _plugin: true
334
369
  },
335
370
  "key" => {
336
371
  description: "The private key for the certificate.",
337
372
  type: String,
338
- _example: "/etc/puppetlabs/puppet/ssl/private_keys/my-host.example.com.pem"
373
+ _example: "/etc/puppetlabs/puppet/ssl/private_keys/my-host.example.com.pem",
374
+ _plugin: true
339
375
  },
340
376
  "read_timeout" => {
341
377
  description: "How long to wait in seconds for a response from PuppetDB.",
342
378
  type: Integer,
343
379
  minimum: 1,
344
380
  _default: 60,
345
- _example: 120
381
+ _example: 120,
382
+ _plugin: true
346
383
  },
347
384
  "server_urls" => {
348
385
  description: "An array containing the PuppetDB host to connect to. Include the protocol `https` "\
349
386
  "and the port, which is usually `8081`. For example, "\
350
387
  "`https://my-puppetdb-server.com:8081`.",
351
388
  type: Array,
352
- _example: ["https://puppet.example.com:8081"]
389
+ _example: ["https://puppet.example.com:8081"],
390
+ _plugin: true
353
391
  },
354
392
  "token" => {
355
393
  description: "The path to the PE RBAC Token.",
356
394
  type: String,
357
- _example: "~/.puppetlabs/token"
395
+ _example: "~/.puppetlabs/token",
396
+ _plugin: true
358
397
  }
359
398
  },
360
399
  _plugin: true
361
400
  },
362
401
  "puppetfile" => {
363
- description: "A map containing options for the `bolt puppetfile install` command.",
402
+ description: "A map containing options for the `bolt puppetfile install` command and "\
403
+ "`Install-BoltPuppetfile` cmdlet.",
364
404
  type: Hash,
365
405
  properties: {
366
406
  "forge" => {
@@ -375,19 +415,19 @@ module Bolt
375
415
  _example: "https://forge.example.com"
376
416
  },
377
417
  "proxy" => {
378
- description: "The HTTP proxy to use for Git and Forge operations.",
418
+ description: "The HTTP proxy to use for Forge operations.",
379
419
  type: String,
380
420
  format: "uri",
381
- _example: "https://forgeapi.example.com"
421
+ _example: "https://my-forge-proxy.com:8080"
382
422
  }
383
423
  },
384
- _example: { "baseurl" => "https://forge.example.com", "proxy" => "https://forgeapi.example.com" }
424
+ _example: { "baseurl" => "https://forge.example.com", "proxy" => "https://my-forge-proxy.com:8080" }
385
425
  },
386
426
  "proxy" => {
387
427
  description: "The HTTP proxy to use for Git and Forge operations.",
388
428
  type: String,
389
429
  format: "uri",
390
- _example: "https://forgeapi.example.com"
430
+ _example: "https://my-proxy.com:8080"
391
431
  }
392
432
  },
393
433
  _plugin: false
@@ -401,6 +441,14 @@ module Bolt
401
441
  _example: false,
402
442
  _default: true
403
443
  },
444
+ "spinner" => {
445
+ description: "Whether to print a spinner to the console for long-running Bolt operations.",
446
+ type: [TrueClass, FalseClass],
447
+ _plugin: false,
448
+ _example: false,
449
+ _default: true
450
+ },
451
+
404
452
  "tasks" => {
405
453
  description: "A list of task names and glob patterns to filter the project's tasks by. This option is used "\
406
454
  "to limit the visibility of tasks for users of the project. For example, project authors "\
@@ -435,7 +483,7 @@ module Bolt
435
483
  "specified in the URI.",
436
484
  type: String,
437
485
  enum: TRANSPORT_CONFIG.keys,
438
- _plugin: false,
486
+ _plugin: true,
439
487
  _example: "winrm",
440
488
  _default: "ssh"
441
489
  },
@@ -461,6 +509,7 @@ module Bolt
461
509
  "remote" => {
462
510
  description: "A map of configuration options for the remote transport.",
463
511
  type: Hash,
512
+ additionalProperties: true,
464
513
  _plugin: true,
465
514
  _example: { "run-on" => "proxy_target" }
466
515
  },
@@ -480,6 +529,7 @@ module Bolt
480
529
 
481
530
  # Options that are available in a bolt.yaml file
482
531
  BOLT_OPTIONS = %w[
532
+ apply-settings
483
533
  apply_settings
484
534
  color
485
535
  compile-concurrency
@@ -489,11 +539,13 @@ module Bolt
489
539
  inventoryfile
490
540
  log
491
541
  modulepath
542
+ plugin-hooks
492
543
  plugin_hooks
493
544
  plugins
494
545
  puppetdb
495
546
  puppetfile
496
547
  save-rerun
548
+ spinner
497
549
  trusted-external-command
498
550
  ].freeze
499
551
 
@@ -505,15 +557,20 @@ module Bolt
505
557
  format
506
558
  inventory-config
507
559
  log
560
+ module-install
561
+ plugin-cache
562
+ plugin-hooks
508
563
  plugin_hooks
509
564
  plugins
510
565
  puppetdb
511
566
  puppetfile
512
567
  save-rerun
568
+ spinner
513
569
  ].freeze
514
570
 
515
571
  # Options that are available in a bolt-project.yaml file
516
572
  BOLT_PROJECT_OPTIONS = %w[
573
+ apply-settings
517
574
  apply_settings
518
575
  color
519
576
  compile-concurrency
@@ -523,14 +580,18 @@ module Bolt
523
580
  inventoryfile
524
581
  log
525
582
  modulepath
583
+ module-install
526
584
  modules
527
585
  name
528
586
  plans
587
+ plugin-cache
588
+ plugin-hooks
529
589
  plugin_hooks
530
590
  plugins
531
591
  puppetdb
532
592
  puppetfile
533
593
  save-rerun
594
+ spinner
534
595
  tasks
535
596
  trusted-external-command
536
597
  ].freeze