bolt 2.40.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bolt might be problematic. Click here for more details.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +17 -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 +58 -157
  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 +93 -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/shell/bash.rb +1 -1
  45. data/lib/bolt/shell/bash/tmpdir.rb +4 -1
  46. data/lib/bolt/shell/powershell.rb +3 -4
  47. data/lib/bolt/shell/powershell/snippets.rb +9 -149
  48. data/lib/bolt/task.rb +1 -1
  49. data/lib/bolt/transport/docker/connection.rb +2 -2
  50. data/lib/bolt/transport/local.rb +1 -9
  51. data/lib/bolt/transport/orch/connection.rb +1 -1
  52. data/lib/bolt/transport/ssh.rb +1 -2
  53. data/lib/bolt/transport/ssh/connection.rb +1 -1
  54. data/lib/bolt/validator.rb +2 -2
  55. data/lib/bolt/version.rb +1 -1
  56. data/lib/bolt_server/config.rb +1 -1
  57. data/lib/bolt_server/schemas/partials/task.json +1 -1
  58. data/lib/bolt_server/transport_app.rb +3 -2
  59. data/libexec/bolt_catalog +1 -1
  60. data/modules/aggregate/plans/count.pp +21 -0
  61. data/modules/aggregate/plans/targets.pp +21 -0
  62. data/modules/puppet_connect/plans/test_input_data.pp +31 -0
  63. data/modules/puppetdb_fact/plans/init.pp +10 -0
  64. metadata +27 -18
  65. 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)
@@ -212,12 +197,14 @@ module Bolt
212
197
 
213
198
  private def validate_ps_version
214
199
  if Bolt::Util.powershell?
215
- target = inventory.get_target('localhost')
216
- Bolt::Transport::Local.new.with_connection(target) do |conn|
217
- # This will automatically validate the powershell version on the Bolt
218
- # controller
219
- Bolt::Shell::Powershell.new(target, conn)
220
- end
200
+ command = "powershell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy "\
201
+ "Bypass -Command $PSVersionTable.PSVersion.Major"
202
+ stdout, _stderr, _status = Open3.capture3(command)
203
+
204
+ return unless !stdout.empty? && stdout.to_i < 3
205
+
206
+ msg = "Detected PowerShell 2 on controller. PowerShell 2 is unsupported."
207
+ Bolt::Logger.deprecation_warning("powershell_2_controller", msg)
221
208
  end
222
209
  end
223
210
 
@@ -311,10 +298,6 @@ module Bolt
311
298
  "Unknown argument(s) #{options[:leftovers].join(', ')}"
312
299
  end
313
300
 
314
- if options.slice(:boltdir, :configfile, :project).length > 1
315
- raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
316
- end
317
-
318
301
  if options[:noop] &&
319
302
  !(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
320
303
  raise Bolt::CLIError,
@@ -327,10 +310,6 @@ module Bolt
327
310
  "Option '--env-var' may only be specified when running a command or script"
328
311
  end
329
312
  end
330
-
331
- if options.key?(:debug) && options.key?(:log)
332
- raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
333
- end
334
313
  end
335
314
 
336
315
  def handle_parser_errors
@@ -373,7 +352,10 @@ module Bolt
373
352
  conflicting_options = Set.new(opts.keys.map(&:to_s)).intersection(inventory_cli_opts)
374
353
 
375
354
  if inventory_source && conflicting_options.any?
376
- @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
+ )
377
359
  end
378
360
  end
379
361
 
@@ -390,7 +372,7 @@ module Bolt
390
372
  # Initialize inventory and targets. Errors here are better to catch early.
391
373
  # options[:target_args] will contain a string/array version of the targetting options this is passed to plans
392
374
  # options[:targets] will contain a resolved set of Target objects
393
- unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
375
+ unless %w[guide module project secret].include?(options[:subcommand]) ||
394
376
  %w[convert new show].include?(options[:action])
395
377
  update_targets(options)
396
378
  end
@@ -445,9 +427,6 @@ module Bolt
445
427
  list_modules
446
428
  end
447
429
  return 0
448
- when 'show-modules'
449
- list_modules
450
- return 0
451
430
  when 'convert'
452
431
  pal.convert_plan(options[:object])
453
432
  return 0
@@ -497,17 +476,6 @@ module Bolt
497
476
  when 'generate-types'
498
477
  code = generate_types
499
478
  end
500
- when 'puppetfile'
501
- case options[:action]
502
- when 'generate-types'
503
- code = generate_types
504
- when 'install'
505
- code = install_puppetfile(
506
- config.puppetfile_config,
507
- config.puppetfile,
508
- config.modulepath.first
509
- )
510
- end
511
479
  when 'secret'
512
480
  code = Bolt::Secret.execute(plugins, outputter, options)
513
481
  when 'apply'
@@ -525,7 +493,6 @@ module Bolt
525
493
 
526
494
  elapsed_time = Benchmark.realtime do
527
495
  executor_opts = {}
528
- executor_opts[:description] = options[:description] if options.key?(:description)
529
496
  executor_opts[:env_vars] = options[:env_vars] if options.key?(:env_vars)
530
497
  executor.subscribe(outputter)
531
498
  executor.subscribe(log_outputter)
@@ -538,15 +505,11 @@ module Bolt
538
505
  validate_file('script', script)
539
506
  executor.run_script(targets, script, options[:leftovers], executor_opts)
540
507
  when 'task'
541
- r = outputter.spin do
542
- pal.run_task(options[:object],
543
- targets,
544
- options[:task_options],
545
- executor,
546
- inventory,
547
- options[:description])
548
- end
549
- r
508
+ pal.run_task(options[:object],
509
+ targets,
510
+ options[:task_options],
511
+ executor,
512
+ inventory)
550
513
  when 'file'
551
514
  src = options[:object]
552
515
  dest = options[:leftovers].first
@@ -607,7 +570,7 @@ module Bolt
607
570
  end
608
571
 
609
572
  def list_plans
610
- 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])
611
574
  outputter.print_plans(plans, pal.user_modulepath)
612
575
  end
613
576
 
@@ -659,7 +622,7 @@ module Bolt
659
622
  if node_param && target_param
660
623
  msg = "Plan parameters include both 'nodes' and 'targets' with type 'TargetSpec', " \
661
624
  "neither will populated with the value for --nodes or --targets."
662
- @logger.warn(msg)
625
+ Bolt::Logger.warn("nodes_targets_parameters", msg)
663
626
  elsif node_param
664
627
  plan_arguments['nodes'] = nodes.join(',')
665
628
  elsif target_param
@@ -669,7 +632,6 @@ module Bolt
669
632
 
670
633
  plan_context = { plan_name: plan_name,
671
634
  params: plan_arguments }
672
- plan_context[:description] = options[:description] if options[:description]
673
635
 
674
636
  executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
675
637
  if %w[human rainbow].include?(options.fetch(:format, 'human'))
@@ -681,9 +643,7 @@ module Bolt
681
643
 
682
644
  executor.subscribe(log_outputter)
683
645
  executor.start_plan(plan_context)
684
- result = outputter.spin do
685
- pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
686
- end
646
+ result = pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
687
647
 
688
648
  # If a non-bolt exception bubbles up the plan won't get finished
689
649
  executor.finish_plan(result)
@@ -706,7 +666,7 @@ module Bolt
706
666
  "about defining and declaring classes and types in the Puppet documentation at "\
707
667
  "https://puppet.com/docs/puppet/latest/lang_classes.html and "\
708
668
  "https://puppet.com/docs/puppet/latest/lang_defined_types.html"
709
- @logger.warn(message)
669
+ Bolt::Logger.warn("empty_manifest", message)
710
670
  end
711
671
 
712
672
  executor = Bolt::Executor.new(config.concurrency, analytics, noop, config.modified_concurrency)
@@ -735,14 +695,12 @@ module Bolt
735
695
  end
736
696
 
737
697
  def list_modules
738
- assert_puppetfile_or_module_command(config.project.modules)
739
698
  outputter.print_module_list(pal.list_modules)
740
699
  end
741
700
 
742
701
  def generate_types
743
- assert_puppetfile_or_module_command(config.project.modules)
744
702
  # generate_types will surface a nice error with helpful message if it fails
745
- pal.generate_types
703
+ pal.generate_types(cache: true)
746
704
  0
747
705
  end
748
706
 
@@ -750,27 +708,19 @@ module Bolt
750
708
  #
751
709
  def install_project_modules(project, config, force, resolve)
752
710
  assert_project_file(project)
753
- assert_puppetfile_or_module_command(project.modules)
754
711
 
755
- unless project.modules
756
- outputter.print_message "Project configuration file #{project.project_file} does not "\
757
- "specify any module dependencies. Nothing to do."
758
- return 0
759
- end
760
-
761
- if resolve != false && config.any?
762
- @logger.warn(
763
- "Detected configuration for 'module-install'. This configuration is currently "\
764
- "only supported when installing modules, not when resolving module dependencies. "\
765
- "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."
766
716
  )
717
+ return 0
767
718
  end
768
719
 
769
- modules = project.modules || []
770
720
  installer = Bolt::ModuleInstaller.new(outputter, pal)
771
721
 
772
722
  ok = outputter.spin do
773
- installer.install(modules,
723
+ installer.install(project.modules,
774
724
  project.puppetfile,
775
725
  project.managed_moduledir,
776
726
  config,
@@ -785,22 +735,12 @@ module Bolt
785
735
  #
786
736
  def add_project_module(name, project, config)
787
737
  assert_project_file(project)
788
- assert_puppetfile_or_module_command(project.modules)
789
-
790
- if config.any?
791
- @logger.warn(
792
- "Detected configuration for 'module-install'. This configuration is currently "\
793
- "only supported when installing modules, not when resolving module dependencies. "\
794
- "For more information, see https://pup.pt/bolt-module-install"
795
- )
796
- end
797
738
 
798
- modules = project.modules || []
799
739
  installer = Bolt::ModuleInstaller.new(outputter, pal)
800
740
 
801
741
  ok = outputter.spin do
802
742
  installer.add(name,
803
- modules,
743
+ project.modules,
804
744
  project.puppetfile,
805
745
  project.managed_moduledir,
806
746
  project.project_file,
@@ -831,8 +771,6 @@ module Bolt
831
771
  # Loads a Puppetfile and installs its modules.
832
772
  #
833
773
  def install_puppetfile(puppetfile_config, puppetfile, moduledir)
834
- assert_puppetfile_or_module_command(config.project.modules)
835
-
836
774
  outputter.print_message("Installing modules from Puppetfile")
837
775
  installer = Bolt::ModuleInstaller.new(outputter, pal)
838
776
  ok = outputter.spin do
@@ -842,43 +780,6 @@ module Bolt
842
780
  ok ? 0 : 1
843
781
  end
844
782
 
845
- # Raises an error if the 'puppetfile install' command is deprecated due to
846
- # modules being configured.
847
- #
848
- def assert_puppetfile_or_module_command(modules)
849
- if Bolt::Util.powershell?
850
- case options[:action]
851
- when 'generate-types'
852
- old_command = 'Register-BoltPuppetfileTypes'
853
- new_command = 'Register-BoltModuleTypes'
854
- when 'install'
855
- old_command = 'Install-BoltPuppetfile'
856
- new_command = 'Install-BoltModule'
857
- when 'show', 'show-modules'
858
- old_command = 'Get-BoltPuppetfileModules'
859
- new_command = 'Get-BoltModule'
860
- end
861
- else
862
- old_command = "bolt puppetfile #{options[:action]}"
863
- new_command = if options[:action] == 'show-modules'
864
- 'bolt module show'
865
- else
866
- "bolt module #{options[:action]}"
867
- end
868
- end
869
-
870
- if modules && options[:subcommand] == 'puppetfile'
871
- raise Bolt::CLIError,
872
- "Unable to use command '#{old_command}' when 'modules' is configured in "\
873
- "bolt-project.yaml. Use '#{new_command}' instead."
874
- elsif modules.nil? && options[:subcommand] == 'module'
875
- msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
876
- "bolt-project.yaml. "
877
- msg += "Use '#{old_command}' instead." if options[:action] != 'add'
878
- raise Bolt::CLIError, msg
879
- end
880
- end
881
-
882
783
  def pal
883
784
  @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
884
785
  config.hiera_config,
@@ -975,7 +876,7 @@ module Bolt
975
876
  set the BOLT_GEM environment variable.
976
877
  MSG
977
878
 
978
- @logger.warn(msg)
879
+ Bolt::Logger.warn("gem_install", msg)
979
880
  end
980
881
 
981
882
  # We only need to enumerate bundled content when running a task or plan
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