bolt 2.40.2 → 3.1.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.

Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +19 -17
  3. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +25 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
  5. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +7 -3
  6. data/lib/bolt/analytics.rb +3 -2
  7. data/lib/bolt/applicator.rb +11 -1
  8. data/lib/bolt/bolt_option_parser.rb +3 -113
  9. data/lib/bolt/catalog.rb +10 -29
  10. data/lib/bolt/cli.rb +54 -155
  11. data/lib/bolt/config.rb +62 -239
  12. data/lib/bolt/config/options.rb +58 -97
  13. data/lib/bolt/config/transport/local.rb +1 -0
  14. data/lib/bolt/config/transport/options.rb +8 -1
  15. data/lib/bolt/config/transport/orch.rb +1 -0
  16. data/lib/bolt/executor.rb +15 -5
  17. data/lib/bolt/inventory.rb +3 -2
  18. data/lib/bolt/inventory/group.rb +35 -4
  19. data/lib/bolt/inventory/inventory.rb +1 -1
  20. data/lib/bolt/logger.rb +115 -11
  21. data/lib/bolt/module.rb +10 -2
  22. data/lib/bolt/module_installer.rb +4 -2
  23. data/lib/bolt/module_installer/resolver.rb +65 -12
  24. data/lib/bolt/module_installer/specs/forge_spec.rb +8 -2
  25. data/lib/bolt/module_installer/specs/git_spec.rb +17 -2
  26. data/lib/bolt/outputter/human.rb +9 -5
  27. data/lib/bolt/outputter/json.rb +16 -16
  28. data/lib/bolt/outputter/rainbow.rb +3 -3
  29. data/lib/bolt/pal.rb +94 -14
  30. data/lib/bolt/pal/yaml_plan.rb +8 -2
  31. data/lib/bolt/pal/yaml_plan/evaluator.rb +7 -19
  32. data/lib/bolt/pal/yaml_plan/step.rb +3 -24
  33. data/lib/bolt/pal/yaml_plan/step/upload.rb +2 -2
  34. data/lib/bolt/pal/yaml_plan/transpiler.rb +6 -1
  35. data/lib/bolt/plugin.rb +3 -3
  36. data/lib/bolt/plugin/cache.rb +7 -7
  37. data/lib/bolt/plugin/module.rb +0 -23
  38. data/lib/bolt/plugin/puppet_connect_data.rb +77 -0
  39. data/lib/bolt/plugin/puppetdb.rb +1 -1
  40. data/lib/bolt/project.rb +54 -81
  41. data/lib/bolt/project_manager.rb +4 -3
  42. data/lib/bolt/project_manager/module_migrator.rb +6 -5
  43. data/lib/bolt/rerun.rb +1 -1
  44. data/lib/bolt/result.rb +6 -1
  45. data/lib/bolt/shell/bash.rb +9 -4
  46. data/lib/bolt/shell/bash/tmpdir.rb +4 -1
  47. data/lib/bolt/shell/powershell.rb +9 -5
  48. data/lib/bolt/shell/powershell/snippets.rb +37 -150
  49. data/lib/bolt/task.rb +1 -1
  50. data/lib/bolt/transport/base.rb +0 -9
  51. data/lib/bolt/transport/docker.rb +1 -125
  52. data/lib/bolt/transport/docker/connection.rb +86 -161
  53. data/lib/bolt/transport/local.rb +1 -9
  54. data/lib/bolt/transport/orch/connection.rb +1 -1
  55. data/lib/bolt/transport/ssh.rb +1 -2
  56. data/lib/bolt/transport/ssh/connection.rb +1 -1
  57. data/lib/bolt/validator.rb +2 -2
  58. data/lib/bolt/version.rb +1 -1
  59. data/lib/bolt_server/config.rb +1 -1
  60. data/lib/bolt_server/transport_app.rb +48 -31
  61. data/lib/bolt_spec/bolt_context.rb +9 -4
  62. data/lib/bolt_spec/plans.rb +1 -109
  63. data/libexec/bolt_catalog +1 -1
  64. data/modules/aggregate/plans/count.pp +21 -0
  65. data/modules/aggregate/plans/targets.pp +21 -0
  66. data/modules/puppet_connect/plans/test_input_data.pp +67 -0
  67. data/modules/puppetdb_fact/plans/init.pp +10 -0
  68. metadata +28 -19
  69. data/modules/aggregate/plans/nodes.pp +0 -36
data/lib/bolt/cli.rb CHANGED
@@ -33,19 +33,18 @@ module Bolt
33
33
 
34
34
  class CLI
35
35
  COMMANDS = {
36
- 'command' => %w[run],
37
- 'script' => %w[run],
38
- 'task' => %w[show run],
39
- 'plan' => %w[show run convert new],
40
- 'file' => %w[download upload],
41
- 'puppetfile' => %w[install show-modules generate-types],
42
- 'secret' => %w[encrypt decrypt createkeys],
43
- 'inventory' => %w[show],
44
- 'group' => %w[show],
45
- 'project' => %w[init migrate],
46
- 'module' => %w[add generate-types install show],
47
- 'apply' => %w[],
48
- 'guide' => %w[]
36
+ 'command' => %w[run],
37
+ 'script' => %w[run],
38
+ 'task' => %w[show run],
39
+ 'plan' => %w[show run convert new],
40
+ 'file' => %w[download upload],
41
+ 'secret' => %w[encrypt decrypt createkeys],
42
+ 'inventory' => %w[show],
43
+ 'group' => %w[show],
44
+ 'project' => %w[init migrate],
45
+ 'module' => %w[add generate-types install show],
46
+ 'apply' => %w[],
47
+ 'guide' => %w[]
49
48
  }.freeze
50
49
 
51
50
  attr_reader :config, :options
@@ -147,10 +146,6 @@ module Bolt
147
146
  end
148
147
 
149
148
  validate(options)
150
-
151
- # Deprecation warnings can't be issued until after config is loaded, so
152
- # store them for later.
153
- @parser_deprecations = parser.deprecations
154
149
  rescue Bolt::Error => e
155
150
  fatal_error(e)
156
151
  raise e
@@ -159,25 +154,19 @@ module Bolt
159
154
  # Loads the project and configuration. All errors that are raised here are not
160
155
  # handled by the outputter, as it relies on config being loaded.
161
156
  def load_config
162
- @config = if ENV['BOLT_PROJECT']
163
- project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
164
- Bolt::Config.from_project(project, options)
165
- elsif options[:configfile]
166
- Bolt::Config.from_file(options[:configfile], options)
157
+ project = if ENV['BOLT_PROJECT']
158
+ Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
159
+ elsif options[:project]
160
+ dir = Pathname.new(options[:project])
161
+ if (dir + Bolt::Project::BOLTDIR_NAME).directory?
162
+ Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
163
+ else
164
+ Bolt::Project.create_project(dir)
165
+ end
167
166
  else
168
- cli_flag = options[:project] || options[:boltdir]
169
- project = if cli_flag
170
- dir = Pathname.new(cli_flag)
171
- if (dir + Bolt::Project::BOLTDIR_NAME).directory?
172
- Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
173
- else
174
- Bolt::Project.create_project(dir)
175
- end
176
- else
177
- Bolt::Project.find_boltdir(Dir.pwd)
178
- end
179
- Bolt::Config.from_project(project, options)
167
+ Bolt::Project.find_boltdir(Dir.pwd)
180
168
  end
169
+ @config = Bolt::Config.from_project(project, options)
181
170
  rescue Bolt::Error => e
182
171
  fatal_error(e)
183
172
  raise e
@@ -185,20 +174,16 @@ module Bolt
185
174
 
186
175
  # Completes the setup process by configuring Bolt and log messages
187
176
  def finalize_setup
188
- Bolt::Logger.configure(config.log, config.color)
177
+ Bolt::Logger.configure(config.log, config.color, config.disable_warnings)
189
178
  Bolt::Logger.analytics = analytics
179
+ Bolt::Logger.flush_queue
190
180
 
191
181
  # Logger must be configured before checking path case and project file, otherwise logs will not display
192
182
  config.check_path_case('modulepath', config.modulepath)
193
183
  config.project.check_deprecated_file
194
184
 
195
- # Log messages created during parser and config initialization
196
- config.logs.each { |log| @logger.send(log.keys[0], log.values[0]) }
197
- @parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
198
- config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
199
-
200
- if options[:clear_cache] && File.exist?(config.project.cache_file)
201
- FileUtils.rm(config.project.cache_file)
185
+ if options[:clear_cache] && File.exist?(config.project.plugin_cache_file)
186
+ FileUtils.rm(config.project.plugin_cache_file)
202
187
  end
203
188
 
204
189
  warn_inventory_overrides_cli(options)
@@ -218,9 +203,8 @@ module Bolt
218
203
 
219
204
  return unless !stdout.empty? && stdout.to_i < 3
220
205
 
221
- msg = "Detected PowerShell 2 on controller. PowerShell 2 is deprecated and "\
222
- "support will be removed in Bolt 3.0."
223
- Bolt::Logger.deprecation_warning("PowerShell 2 controller", msg)
206
+ msg = "Detected PowerShell 2 on controller. PowerShell 2 is unsupported."
207
+ Bolt::Logger.deprecation_warning("powershell_2_controller", msg)
224
208
  end
225
209
  end
226
210
 
@@ -314,10 +298,6 @@ module Bolt
314
298
  "Unknown argument(s) #{options[:leftovers].join(', ')}"
315
299
  end
316
300
 
317
- if options.slice(:boltdir, :configfile, :project).length > 1
318
- raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
319
- end
320
-
321
301
  if options[:noop] &&
322
302
  !(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
323
303
  raise Bolt::CLIError,
@@ -330,10 +310,6 @@ module Bolt
330
310
  "Option '--env-var' may only be specified when running a command or script"
331
311
  end
332
312
  end
333
-
334
- if options.key?(:debug) && options.key?(:log)
335
- raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
336
- end
337
313
  end
338
314
 
339
315
  def handle_parser_errors
@@ -376,7 +352,10 @@ module Bolt
376
352
  conflicting_options = Set.new(opts.keys.map(&:to_s)).intersection(inventory_cli_opts)
377
353
 
378
354
  if inventory_source && conflicting_options.any?
379
- @logger.warn("CLI arguments #{conflicting_options.to_a} may be overridden by Inventory: #{inventory_source}")
355
+ Bolt::Logger.warn(
356
+ "cli_overrides",
357
+ "CLI arguments #{conflicting_options.to_a} may be overridden by Inventory: #{inventory_source}"
358
+ )
380
359
  end
381
360
  end
382
361
 
@@ -393,7 +372,7 @@ module Bolt
393
372
  # Initialize inventory and targets. Errors here are better to catch early.
394
373
  # options[:target_args] will contain a string/array version of the targetting options this is passed to plans
395
374
  # options[:targets] will contain a resolved set of Target objects
396
- unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
375
+ unless %w[guide module project secret].include?(options[:subcommand]) ||
397
376
  %w[convert new show].include?(options[:action])
398
377
  update_targets(options)
399
378
  end
@@ -448,9 +427,6 @@ module Bolt
448
427
  list_modules
449
428
  end
450
429
  return 0
451
- when 'show-modules'
452
- list_modules
453
- return 0
454
430
  when 'convert'
455
431
  pal.convert_plan(options[:object])
456
432
  return 0
@@ -500,17 +476,6 @@ module Bolt
500
476
  when 'generate-types'
501
477
  code = generate_types
502
478
  end
503
- when 'puppetfile'
504
- case options[:action]
505
- when 'generate-types'
506
- code = generate_types
507
- when 'install'
508
- code = install_puppetfile(
509
- config.puppetfile_config,
510
- config.puppetfile,
511
- config.modulepath.first
512
- )
513
- end
514
479
  when 'secret'
515
480
  code = Bolt::Secret.execute(plugins, outputter, options)
516
481
  when 'apply'
@@ -528,7 +493,6 @@ module Bolt
528
493
 
529
494
  elapsed_time = Benchmark.realtime do
530
495
  executor_opts = {}
531
- executor_opts[:description] = options[:description] if options.key?(:description)
532
496
  executor_opts[:env_vars] = options[:env_vars] if options.key?(:env_vars)
533
497
  executor.subscribe(outputter)
534
498
  executor.subscribe(log_outputter)
@@ -541,15 +505,11 @@ module Bolt
541
505
  validate_file('script', script)
542
506
  executor.run_script(targets, script, options[:leftovers], executor_opts)
543
507
  when 'task'
544
- r = outputter.spin do
545
- pal.run_task(options[:object],
546
- targets,
547
- options[:task_options],
548
- executor,
549
- inventory,
550
- options[:description])
551
- end
552
- r
508
+ pal.run_task(options[:object],
509
+ targets,
510
+ options[:task_options],
511
+ executor,
512
+ inventory)
553
513
  when 'file'
554
514
  src = options[:object]
555
515
  dest = options[:leftovers].first
@@ -610,7 +570,7 @@ module Bolt
610
570
  end
611
571
 
612
572
  def list_plans
613
- plans = filter_content(pal.list_plans(filter_content: true), options[:filter])
573
+ plans = filter_content(pal.list_plans_with_cache(filter_content: true), options[:filter])
614
574
  outputter.print_plans(plans, pal.user_modulepath)
615
575
  end
616
576
 
@@ -662,7 +622,7 @@ module Bolt
662
622
  if node_param && target_param
663
623
  msg = "Plan parameters include both 'nodes' and 'targets' with type 'TargetSpec', " \
664
624
  "neither will populated with the value for --nodes or --targets."
665
- @logger.warn(msg)
625
+ Bolt::Logger.warn("nodes_targets_parameters", msg)
666
626
  elsif node_param
667
627
  plan_arguments['nodes'] = nodes.join(',')
668
628
  elsif target_param
@@ -672,7 +632,6 @@ module Bolt
672
632
 
673
633
  plan_context = { plan_name: plan_name,
674
634
  params: plan_arguments }
675
- plan_context[:description] = options[:description] if options[:description]
676
635
 
677
636
  executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
678
637
  if %w[human rainbow].include?(options.fetch(:format, 'human'))
@@ -684,9 +643,7 @@ module Bolt
684
643
 
685
644
  executor.subscribe(log_outputter)
686
645
  executor.start_plan(plan_context)
687
- result = outputter.spin do
688
- pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
689
- end
646
+ result = pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
690
647
 
691
648
  # If a non-bolt exception bubbles up the plan won't get finished
692
649
  executor.finish_plan(result)
@@ -709,7 +666,7 @@ module Bolt
709
666
  "about defining and declaring classes and types in the Puppet documentation at "\
710
667
  "https://puppet.com/docs/puppet/latest/lang_classes.html and "\
711
668
  "https://puppet.com/docs/puppet/latest/lang_defined_types.html"
712
- @logger.warn(message)
669
+ Bolt::Logger.warn("empty_manifest", message)
713
670
  end
714
671
 
715
672
  executor = Bolt::Executor.new(config.concurrency, analytics, noop, config.modified_concurrency)
@@ -738,14 +695,12 @@ module Bolt
738
695
  end
739
696
 
740
697
  def list_modules
741
- assert_puppetfile_or_module_command(config.project.modules)
742
698
  outputter.print_module_list(pal.list_modules)
743
699
  end
744
700
 
745
701
  def generate_types
746
- assert_puppetfile_or_module_command(config.project.modules)
747
702
  # generate_types will surface a nice error with helpful message if it fails
748
- pal.generate_types
703
+ pal.generate_types(cache: true)
749
704
  0
750
705
  end
751
706
 
@@ -753,27 +708,19 @@ module Bolt
753
708
  #
754
709
  def install_project_modules(project, config, force, resolve)
755
710
  assert_project_file(project)
756
- assert_puppetfile_or_module_command(project.modules)
757
-
758
- unless project.modules
759
- outputter.print_message "Project configuration file #{project.project_file} does not "\
760
- "specify any module dependencies. Nothing to do."
761
- return 0
762
- end
763
711
 
764
- if resolve != false && config.any?
765
- @logger.warn(
766
- "Detected configuration for 'module-install'. This configuration is currently "\
767
- "only supported when installing modules, not when resolving module dependencies. "\
768
- "For more information, see https://pup.pt/bolt-module-install"
712
+ if project.modules.empty? && resolve != false
713
+ outputter.print_message(
714
+ "Project configuration file #{project.project_file} does not "\
715
+ "specify any module dependencies. Nothing to do."
769
716
  )
717
+ return 0
770
718
  end
771
719
 
772
- modules = project.modules || []
773
720
  installer = Bolt::ModuleInstaller.new(outputter, pal)
774
721
 
775
722
  ok = outputter.spin do
776
- installer.install(modules,
723
+ installer.install(project.modules,
777
724
  project.puppetfile,
778
725
  project.managed_moduledir,
779
726
  config,
@@ -788,22 +735,12 @@ module Bolt
788
735
  #
789
736
  def add_project_module(name, project, config)
790
737
  assert_project_file(project)
791
- assert_puppetfile_or_module_command(project.modules)
792
738
 
793
- if config.any?
794
- @logger.warn(
795
- "Detected configuration for 'module-install'. This configuration is currently "\
796
- "only supported when installing modules, not when resolving module dependencies. "\
797
- "For more information, see https://pup.pt/bolt-module-install"
798
- )
799
- end
800
-
801
- modules = project.modules || []
802
739
  installer = Bolt::ModuleInstaller.new(outputter, pal)
803
740
 
804
741
  ok = outputter.spin do
805
742
  installer.add(name,
806
- modules,
743
+ project.modules,
807
744
  project.puppetfile,
808
745
  project.managed_moduledir,
809
746
  project.project_file,
@@ -834,8 +771,6 @@ module Bolt
834
771
  # Loads a Puppetfile and installs its modules.
835
772
  #
836
773
  def install_puppetfile(puppetfile_config, puppetfile, moduledir)
837
- assert_puppetfile_or_module_command(config.project.modules)
838
-
839
774
  outputter.print_message("Installing modules from Puppetfile")
840
775
  installer = Bolt::ModuleInstaller.new(outputter, pal)
841
776
  ok = outputter.spin do
@@ -845,43 +780,6 @@ module Bolt
845
780
  ok ? 0 : 1
846
781
  end
847
782
 
848
- # Raises an error if the 'puppetfile install' command is deprecated due to
849
- # modules being configured.
850
- #
851
- def assert_puppetfile_or_module_command(modules)
852
- if Bolt::Util.powershell?
853
- case options[:action]
854
- when 'generate-types'
855
- old_command = 'Register-BoltPuppetfileTypes'
856
- new_command = 'Register-BoltModuleTypes'
857
- when 'install'
858
- old_command = 'Install-BoltPuppetfile'
859
- new_command = 'Install-BoltModule'
860
- when 'show', 'show-modules'
861
- old_command = 'Get-BoltPuppetfileModules'
862
- new_command = 'Get-BoltModule'
863
- end
864
- else
865
- old_command = "bolt puppetfile #{options[:action]}"
866
- new_command = if options[:action] == 'show-modules'
867
- 'bolt module show'
868
- else
869
- "bolt module #{options[:action]}"
870
- end
871
- end
872
-
873
- if modules && options[:subcommand] == 'puppetfile'
874
- raise Bolt::CLIError,
875
- "Unable to use command '#{old_command}' when 'modules' is configured in "\
876
- "bolt-project.yaml. Use '#{new_command}' instead."
877
- elsif modules.nil? && options[:subcommand] == 'module'
878
- msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
879
- "bolt-project.yaml. "
880
- msg += "Use '#{old_command}' instead." if options[:action] != 'add'
881
- raise Bolt::CLIError, msg
882
- end
883
- end
884
-
885
783
  def pal
886
784
  @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
887
785
  config.hiera_config,
@@ -978,7 +876,7 @@ module Bolt
978
876
  set the BOLT_GEM environment variable.
979
877
  MSG
980
878
 
981
- @logger.warn(msg)
879
+ Bolt::Logger.warn("gem_install", msg)
982
880
  end
983
881
 
984
882
  # We only need to enumerate bundled content when running a task or plan
@@ -1001,7 +899,8 @@ module Bolt
1001
899
  # Gem installs include the aggregate, canary, and puppetdb_fact modules, while
1002
900
  # package installs include modules listed in the Bolt repo Puppetfile
1003
901
  def incomplete_install?
1004
- (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
902
+ builtin_module_list = %w[aggregate canary puppetdb_fact secure_env_vars puppet_connect]
903
+ (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - builtin_module_list).empty?
1005
904
  end
1006
905
 
1007
906
  # Mimicks the output from Outputter::Human#fatal_error. This should be used to print
data/lib/bolt/config.rb CHANGED
@@ -20,10 +20,9 @@ module Bolt
20
20
  class Config
21
21
  include Bolt::Config::Options
22
22
 
23
- attr_reader :config_files, :logs, :data, :transports, :project, :modified_concurrency, :deprecations
23
+ attr_reader :config_files, :data, :transports, :project, :modified_concurrency
24
24
 
25
- BOLT_CONFIG_NAME = 'bolt.yaml'
26
- BOLT_DEFAULTS_NAME = 'bolt-defaults.yaml'
25
+ DEFAULTS_NAME = 'bolt-defaults.yaml'
27
26
 
28
27
  # The default concurrency value that is used when the ulimit is not low (i.e. < 700)
29
28
  DEFAULT_DEFAULT_CONCURRENCY = 100
@@ -33,70 +32,9 @@ module Bolt
33
32
  end
34
33
 
35
34
  def self.from_project(project, overrides = {})
36
- logs = []
37
- deprecations = []
38
-
39
- conf = if project.project_file == project.config_file
40
- project.data
41
- else
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
-
56
- logs << { debug: "Loaded configuration from #{project.config_file}" } if File.exist?(project.config_file)
57
- c
58
- end
59
- data = load_defaults(project).push(
60
- filepath: project.config_file,
61
- data: conf,
62
- logs: logs,
63
- deprecations: deprecations
64
- )
65
-
66
- new(project, data, overrides)
67
- end
68
-
69
- def self.from_file(configfile, overrides = {})
70
- project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
71
- logs = []
72
- deprecations = []
73
-
74
- conf = if project.project_file == project.config_file
75
- project.data
76
- else
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
-
91
- logs << { debug: "Loaded configuration from #{configfile}" }
92
- c
93
- end
94
-
95
- data = load_defaults(project).push(
96
- filepath: configfile,
97
- data: conf,
98
- logs: logs,
99
- deprecations: deprecations
35
+ data = load_defaults.push(
36
+ filepath: project.project_file,
37
+ data: project.data
100
38
  )
101
39
 
102
40
  new(project, data, overrides)
@@ -115,7 +53,7 @@ module Bolt
115
53
  def self.defaults_schema
116
54
  schema = {
117
55
  type: Hash,
118
- properties: BOLT_DEFAULTS_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
56
+ properties: DEFAULTS_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
119
57
  definitions: OPTIONS.merge(transport_definitions)
120
58
  }
121
59
 
@@ -124,16 +62,6 @@ module Bolt
124
62
  schema
125
63
  end
126
64
 
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
65
  def self.system_path
138
66
  if Bolt::Util.windows?
139
67
  Pathname.new(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs', 'bolt', 'etc'))
@@ -149,41 +77,31 @@ module Bolt
149
77
  end
150
78
 
151
79
  # Loads a 'bolt-defaults.yaml' file, which contains default configuration that applies to all
152
- # projects. This file does not allow project-specific configuration such as 'hiera-config' and
153
- # 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
80
+ # projects. This file does not allow project-specific configuration such as 'hiera-config'
81
+ # and nests all default inventory configuration under an 'inventory-config' key.
154
82
  def self.load_bolt_defaults_yaml(dir)
155
- filepath = dir + BOLT_DEFAULTS_NAME
83
+ filepath = dir + DEFAULTS_NAME
156
84
  data = Bolt::Util.read_yaml_hash(filepath, 'config')
157
- logs = [{ debug: "Loaded configuration from #{filepath}" }]
158
- deprecations = []
159
-
160
- # Warn if 'bolt.yaml' detected in same directory.
161
- if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
162
- logs.push(
163
- warn: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
164
- "will be ignored."
165
- )
166
- end
85
+
86
+ Bolt::Logger.debug("Loaded configuration from #{filepath}")
167
87
 
168
88
  # Validate the config against the schema. This will raise a single error
169
89
  # with all validation errors.
170
90
  Bolt::Validator.new.tap do |validator|
171
91
  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
92
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
93
+ validator.deprecations.each { |dep| Bolt::Logger.deprecate(dep[:id], dep[:msg]) }
178
94
  end
179
95
 
180
96
  # Remove project-specific config such as hiera-config, etc.
181
- project_config = data.slice(*(BOLT_PROJECT_OPTIONS - BOLT_DEFAULTS_OPTIONS))
97
+ project_config = data.slice(*(PROJECT_OPTIONS - DEFAULTS_OPTIONS))
182
98
 
183
99
  if project_config.any?
184
100
  data.reject! { |key, _| project_config.include?(key) }
185
- logs.push(
186
- warn: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
101
+
102
+ Bolt::Logger.warn(
103
+ "unsupported_project_config",
104
+ "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
187
105
  "Project configuration should be set in 'bolt-project.yaml'."
188
106
  )
189
107
  end
@@ -193,8 +111,10 @@ module Bolt
193
111
 
194
112
  if transport_config.any?
195
113
  data.reject! { |key, _| transport_config.include?(key) }
196
- logs.push(
197
- warn: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
114
+
115
+ Bolt::Logger.warn(
116
+ "unsupported_inventory_config",
117
+ "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
198
118
  "Transport configuration should be set under the 'inventory-config' option or "\
199
119
  "in 'inventory.yaml'."
200
120
  )
@@ -219,55 +139,20 @@ module Bolt
219
139
  data = data.merge(data.delete('inventory-config'))
220
140
  end
221
141
 
222
- { filepath: filepath, data: data, logs: logs, deprecations: deprecations }
223
- end
224
-
225
- # Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
226
- # here since Bolt::Config will just ignore any invalid keys.
227
- def self.load_bolt_yaml(dir)
228
- filepath = dir + BOLT_CONFIG_NAME
229
- data = Bolt::Util.read_yaml_hash(filepath, 'config')
230
- logs = [{ debug: "Loaded configuration from #{filepath}" }]
231
- deprecations = [{ type: 'Using bolt.yaml for system configuration',
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
246
-
247
- { filepath: filepath, data: data, logs: logs, deprecations: deprecations }
142
+ { filepath: filepath, data: data }
248
143
  end
249
144
 
250
- def self.load_defaults(project)
145
+ def self.load_defaults
251
146
  confs = []
252
147
 
253
- # Load system-level config. Prefer a 'bolt-defaults.yaml' file, but fall back to the
254
- # legacy 'bolt.yaml' file. If the project-level config file is also the system-level
255
- # config file, don't load it a second time.
256
- if File.exist?(system_path + BOLT_DEFAULTS_NAME)
148
+ # Load system-level config.
149
+ if File.exist?(system_path + DEFAULTS_NAME)
257
150
  confs << load_bolt_defaults_yaml(system_path)
258
- elsif File.exist?(system_path + BOLT_CONFIG_NAME) &&
259
- (system_path + BOLT_CONFIG_NAME) != project.config_file
260
- confs << load_bolt_yaml(system_path)
261
151
  end
262
152
 
263
- # Load user-level config if there is a homedir. Prefer a 'bolt-defaults.yaml' file, but
264
- # fall back to the legacy 'bolt.yaml' file.
265
- if user_path
266
- if File.exist?(user_path + BOLT_DEFAULTS_NAME)
267
- confs << load_bolt_defaults_yaml(user_path)
268
- elsif File.exist?(user_path + BOLT_CONFIG_NAME)
269
- confs << load_bolt_yaml(user_path)
270
- end
153
+ # Load user-level config if there is a homedir.
154
+ if user_path && File.exist?(user_path + DEFAULTS_NAME)
155
+ confs << load_bolt_defaults_yaml(user_path)
271
156
  end
272
157
 
273
158
  confs
@@ -275,33 +160,26 @@ module Bolt
275
160
 
276
161
  def initialize(project, config_data, overrides = {})
277
162
  unless config_data.is_a?(Array)
278
- config_data = [{ filepath: project.config_file,
279
- data: config_data,
280
- logs: [],
281
- deprecations: [] }]
163
+ config_data = [{ filepath: project.project_file, data: config_data }]
282
164
  end
283
165
 
284
166
  @logger = Bolt::Logger.logger(self)
285
167
  @project = project
286
- @logs = @project.logs.dup
287
- @deprecations = @project.deprecations.dup
288
168
  @transports = {}
289
169
  @config_files = []
290
170
 
291
171
  default_data = {
292
172
  'apply-settings' => {},
293
- 'apply_settings' => {},
294
173
  'color' => true,
295
174
  'compile-concurrency' => Etc.nprocessors,
296
175
  'concurrency' => default_concurrency,
176
+ 'disable-warnings' => [],
297
177
  'format' => 'human',
298
178
  'log' => { 'console' => {} },
299
179
  'module-install' => {},
300
180
  'plugin-hooks' => {},
301
- 'plugin_hooks' => {},
302
181
  'plugins' => {},
303
182
  'puppetdb' => {},
304
- 'puppetfile' => {},
305
183
  'save-rerun' => true,
306
184
  'spinner' => true,
307
185
  'transport' => 'ssh'
@@ -315,9 +193,6 @@ module Bolt
315
193
  end
316
194
 
317
195
  loaded_data = config_data.each_with_object([]) do |data, acc|
318
- @logs.concat(data[:logs]) if data[:logs].any?
319
- @deprecations.concat(data[:deprecations]) if data[:deprecations].any?
320
-
321
196
  if data[:data].any?
322
197
  @config_files.push(data[:filepath])
323
198
  acc.push(data[:data])
@@ -347,28 +222,25 @@ module Bolt
347
222
  def normalize_overrides(options)
348
223
  opts = options.transform_keys(&:to_s)
349
224
 
350
- # Pull out config options. We need to add 'transport' as it's not part of the
351
- # OPTIONS hash but is a valid option that can be set with the --transport CLI option
352
- overrides = opts.slice(*OPTIONS.keys, 'transport')
225
+ # Pull out config options. We need to add 'transport' and 'inventoryfile' as they're
226
+ # not part of the OPTIONS hash but are valid options that can be set with CLI options
227
+ overrides = opts.slice(*OPTIONS.keys, 'inventoryfile', 'transport')
353
228
 
354
229
  # Pull out transport config options
355
230
  TRANSPORT_CONFIG.each do |transport, config|
356
231
  overrides[transport] = opts.slice(*config.options)
357
232
  end
358
233
 
359
- # Set console log to debug if in debug mode
360
- if options[:debug]
361
- overrides['log'] = { 'console' => { 'level' => 'debug' } }
362
- end
363
-
364
- if options[:puppetfile_path]
365
- @puppetfile = options[:puppetfile_path]
366
- end
367
-
368
234
  overrides['trace'] = opts['trace'] if opts.key?('trace')
369
235
 
370
- # Validate the overrides
371
- Bolt::Validator.new.validate(overrides, self.class.bolt_schema, 'command line')
236
+ # Validate the overrides that can have arbitrary values
237
+ schema = {
238
+ type: Hash,
239
+ properties: CLI_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
240
+ definitions: OPTIONS.merge(INVENTORY_OPTIONS)
241
+ }
242
+
243
+ Bolt::Validator.new.validate(overrides.slice(*CLI_OPTIONS), schema, 'command line')
372
244
 
373
245
  overrides
374
246
  end
@@ -386,8 +258,11 @@ module Bolt
386
258
  when *TRANSPORT_CONFIG.keys
387
259
  Bolt::Util.deep_merge(val1, val2)
388
260
  # Hash values are shallow merged
389
- when 'puppetdb', 'plugin-hooks', 'plugin_hooks', 'apply-settings', 'apply_settings', 'log'
261
+ when 'apply-settings', 'log', 'plugin-hooks', 'puppetdb'
390
262
  val1.merge(val2)
263
+ # Disabled warnings are concatenated
264
+ when 'disable-warnings'
265
+ val1.concat(val2)
391
266
  # All other values are overwritten
392
267
  else
393
268
  val2
@@ -423,7 +298,7 @@ module Bolt
423
298
  end
424
299
 
425
300
  # Filter hashes to only include valid options
426
- %w[apply-settings apply_settings module-install puppetfile].each do |opt|
301
+ %w[apply-settings module-install].each do |opt|
427
302
  @data[opt] = @data[opt].slice(*OPTIONS.dig(opt, :properties).keys)
428
303
  end
429
304
  end
@@ -449,23 +324,18 @@ module Bolt
449
324
 
450
325
  name = normalize_log(key)
451
326
  acc[name] = val.slice('append', 'level').transform_keys(&:to_sym)
452
-
453
- next unless acc[name][:level] == 'notice'
454
-
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
- }
459
327
  end
460
328
  end
461
329
 
462
330
  def validate
463
331
  if @data['future']
464
- msg = "Configuration option 'future' no longer exposes future behavior."
465
- @logs << { warn: msg }
332
+ Bolt::Logger.warn(
333
+ "future_option",
334
+ "Configuration option 'future' no longer exposes future behavior."
335
+ )
466
336
  end
467
337
 
468
- if @project.modules && @data['modulepath']&.include?(@project.managed_moduledir.to_s)
338
+ if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
469
339
  raise Bolt::ValidationError,
470
340
  "Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
471
341
  "is automatically appended to the modulepath and cannot be configured."
@@ -483,25 +353,6 @@ module Bolt
483
353
  if File.exist?(default_inventoryfile)
484
354
  Bolt::Util.validate_file('inventory file', default_inventoryfile)
485
355
  end
486
-
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." }
504
- end
505
356
  end
506
357
 
507
358
  def default_inventoryfile
@@ -517,21 +368,15 @@ module Bolt
517
368
  end
518
369
 
519
370
  def puppetfile
520
- @puppetfile || @project.puppetfile
371
+ @project.puppetfile
521
372
  end
522
373
 
523
374
  def modulepath
524
- path = @data['modulepath'] || @project.modulepath
525
-
526
- if @project.modules
527
- path + [@project.managed_moduledir.to_s]
528
- else
529
- path
530
- end
375
+ (@data['modulepath'] || @project.modulepath) + [@project.managed_moduledir.to_s]
531
376
  end
532
377
 
533
378
  def modulepath=(value)
534
- @data['modulepath'] = value
379
+ @data['modulepath'] = Array(value)
535
380
  end
536
381
 
537
382
  def plugin_cache
@@ -582,27 +427,12 @@ module Bolt
582
427
  @data['compile-concurrency']
583
428
  end
584
429
 
585
- def puppetfile_config
586
- @data['puppetfile']
587
- end
588
-
589
430
  def plugins
590
431
  @data['plugins']
591
432
  end
592
433
 
593
434
  def 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
435
+ @data['plugin-hooks']
606
436
  end
607
437
 
608
438
  def trusted_external
@@ -610,18 +440,7 @@ module Bolt
610
440
  end
611
441
 
612
442
  def 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
443
+ @data['apply-settings']
625
444
  end
626
445
 
627
446
  def transport
@@ -632,6 +451,10 @@ module Bolt
632
451
  @project.module_install || @data['module-install']
633
452
  end
634
453
 
454
+ def disable_warnings
455
+ Set.new(@project.disable_warnings + @data['disable-warnings'])
456
+ end
457
+
635
458
  # Check if there is a case-insensitive match to the path
636
459
  def check_path_case(type, paths)
637
460
  return if paths.nil?
@@ -640,7 +463,7 @@ module Bolt
640
463
  if matches.any?
641
464
  msg = "WARNING: Bolt is case sensitive when specifying a #{type}. Did you mean:\n"
642
465
  matches.each { |path| msg += " #{path}\n" }
643
- @logger.warn msg
466
+ Bolt::Logger.warn("path_case", msg)
644
467
  end
645
468
  end
646
469