bolt 2.38.0 → 3.0.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 (66) 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 +54 -155
  11. data/lib/bolt/config.rb +63 -269
  12. data/lib/bolt/config/options.rb +59 -97
  13. data/lib/bolt/config/transport/local.rb +1 -0
  14. data/lib/bolt/config/transport/options.rb +10 -2
  15. data/lib/bolt/config/transport/orch.rb +1 -0
  16. data/lib/bolt/config/transport/ssh.rb +0 -5
  17. data/lib/bolt/executor.rb +15 -5
  18. data/lib/bolt/inventory.rb +3 -2
  19. data/lib/bolt/inventory/group.rb +35 -12
  20. data/lib/bolt/inventory/inventory.rb +1 -1
  21. data/lib/bolt/logger.rb +115 -11
  22. data/lib/bolt/module.rb +10 -2
  23. data/lib/bolt/module_installer.rb +4 -2
  24. data/lib/bolt/module_installer/resolver.rb +65 -12
  25. data/lib/bolt/module_installer/specs/forge_spec.rb +8 -2
  26. data/lib/bolt/module_installer/specs/git_spec.rb +17 -2
  27. data/lib/bolt/outputter/human.rb +9 -5
  28. data/lib/bolt/outputter/json.rb +16 -16
  29. data/lib/bolt/outputter/rainbow.rb +3 -3
  30. data/lib/bolt/pal.rb +93 -14
  31. data/lib/bolt/pal/yaml_plan.rb +8 -2
  32. data/lib/bolt/pal/yaml_plan/evaluator.rb +7 -19
  33. data/lib/bolt/pal/yaml_plan/step.rb +3 -24
  34. data/lib/bolt/pal/yaml_plan/step/upload.rb +2 -2
  35. data/lib/bolt/pal/yaml_plan/transpiler.rb +6 -1
  36. data/lib/bolt/plugin.rb +3 -3
  37. data/lib/bolt/plugin/cache.rb +8 -8
  38. data/lib/bolt/plugin/module.rb +0 -23
  39. data/lib/bolt/plugin/puppet_connect_data.rb +77 -0
  40. data/lib/bolt/plugin/puppetdb.rb +1 -1
  41. data/lib/bolt/project.rb +54 -81
  42. data/lib/bolt/project_manager.rb +4 -3
  43. data/lib/bolt/project_manager/module_migrator.rb +6 -5
  44. data/lib/bolt/rerun.rb +1 -1
  45. data/lib/bolt/shell/bash.rb +1 -1
  46. data/lib/bolt/shell/bash/tmpdir.rb +4 -1
  47. data/lib/bolt/shell/powershell.rb +3 -4
  48. data/lib/bolt/shell/powershell/snippets.rb +9 -149
  49. data/lib/bolt/task.rb +1 -1
  50. data/lib/bolt/transport/docker/connection.rb +2 -2
  51. data/lib/bolt/transport/local.rb +1 -9
  52. data/lib/bolt/transport/orch/connection.rb +1 -1
  53. data/lib/bolt/transport/ssh.rb +1 -2
  54. data/lib/bolt/transport/ssh/connection.rb +1 -1
  55. data/lib/bolt/validator.rb +16 -15
  56. data/lib/bolt/version.rb +1 -1
  57. data/lib/bolt_server/config.rb +1 -1
  58. data/lib/bolt_server/schemas/partials/task.json +1 -1
  59. data/lib/bolt_server/transport_app.rb +3 -2
  60. data/libexec/bolt_catalog +1 -1
  61. data/modules/aggregate/plans/count.pp +21 -0
  62. data/modules/aggregate/plans/targets.pp +21 -0
  63. data/modules/puppet_connect/plans/test_input_data.pp +31 -0
  64. data/modules/puppetdb_fact/plans/init.pp +10 -0
  65. metadata +26 -17
  66. 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,17 @@ 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
712
+ unless project.modules.any?
756
713
  outputter.print_message "Project configuration file #{project.project_file} does not "\
757
714
  "specify any module dependencies. Nothing to do."
758
715
  return 0
759
716
  end
760
717
 
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"
766
- )
767
- end
768
-
769
- modules = project.modules || []
770
718
  installer = Bolt::ModuleInstaller.new(outputter, pal)
771
719
 
772
720
  ok = outputter.spin do
773
- installer.install(modules,
721
+ installer.install(project.modules,
774
722
  project.puppetfile,
775
723
  project.managed_moduledir,
776
724
  config,
@@ -785,22 +733,12 @@ module Bolt
785
733
  #
786
734
  def add_project_module(name, project, config)
787
735
  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
736
 
798
- modules = project.modules || []
799
737
  installer = Bolt::ModuleInstaller.new(outputter, pal)
800
738
 
801
739
  ok = outputter.spin do
802
740
  installer.add(name,
803
- modules,
741
+ project.modules,
804
742
  project.puppetfile,
805
743
  project.managed_moduledir,
806
744
  project.project_file,
@@ -831,8 +769,6 @@ module Bolt
831
769
  # Loads a Puppetfile and installs its modules.
832
770
  #
833
771
  def install_puppetfile(puppetfile_config, puppetfile, moduledir)
834
- assert_puppetfile_or_module_command(config.project.modules)
835
-
836
772
  outputter.print_message("Installing modules from Puppetfile")
837
773
  installer = Bolt::ModuleInstaller.new(outputter, pal)
838
774
  ok = outputter.spin do
@@ -842,43 +778,6 @@ module Bolt
842
778
  ok ? 0 : 1
843
779
  end
844
780
 
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
781
  def pal
883
782
  @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
884
783
  config.hiera_config,
@@ -975,7 +874,7 @@ module Bolt
975
874
  set the BOLT_GEM environment variable.
976
875
  MSG
977
876
 
978
- @logger.warn(msg)
877
+ Bolt::Logger.warn("gem_install", msg)
979
878
  end
980
879
 
981
880
  # 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
@@ -448,49 +323,19 @@ module Bolt
448
323
  next if val == 'disable'
449
324
 
450
325
  name = normalize_log(key)
451
-
452
- # But otherwise it has to be a Hash
453
- unless val.is_a?(Hash)
454
- raise Bolt::ValidationError,
455
- "config of log #{name} must be a Hash, received #{val.class} #{val.inspect}"
456
- end
457
-
458
- acc[name] = val.slice('append', 'level')
459
- .transform_keys(&:to_sym)
460
-
461
- if (v = acc[name][:level])
462
- unless v.is_a?(String) || v.is_a?(Symbol)
463
- raise Bolt::ValidationError,
464
- "level of log #{name} must be a String or Symbol, received #{v.class} #{v.inspect}"
465
- end
466
-
467
- unless Bolt::Logger.valid_level?(v)
468
- raise Bolt::ValidationError,
469
- "level of log #{name} must be one of #{Bolt::Logger.levels.join(', ')}; received #{v}"
470
- end
471
-
472
- if v == 'notice'
473
- @deprecations << {
474
- type: 'notice log level',
475
- msg: "Log level 'notice' is deprecated and will be removed in Bolt 3.0. Use 'info' instead."
476
- }
477
- end
478
- end
479
-
480
- if (v = acc[name][:append]) && v != true && v != false
481
- raise Bolt::ValidationError,
482
- "append flag of log #{name} must be a Boolean, received #{v.class} #{v.inspect}"
483
- end
326
+ acc[name] = val.slice('append', 'level').transform_keys(&:to_sym)
484
327
  end
485
328
  end
486
329
 
487
330
  def validate
488
331
  if @data['future']
489
- msg = "Configuration option 'future' no longer exposes future behavior."
490
- @logs << { warn: msg }
332
+ Bolt::Logger.warn(
333
+ "future_option",
334
+ "Configuration option 'future' no longer exposes future behavior."
335
+ )
491
336
  end
492
337
 
493
- if @project.modules && @data['modulepath']&.include?(@project.managed_moduledir.to_s)
338
+ if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
494
339
  raise Bolt::ValidationError,
495
340
  "Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
496
341
  "is automatically appended to the modulepath and cannot be configured."
@@ -508,29 +353,6 @@ module Bolt
508
353
  if File.exist?(default_inventoryfile)
509
354
  Bolt::Util.validate_file('inventory file', default_inventoryfile)
510
355
  end
511
-
512
- unless TRANSPORT_CONFIG.include?(transport)
513
- raise UnknownTransportError, transport
514
- end
515
-
516
- # Warn the user how they should be using the 'puppetfile' or
517
- # 'module-install' config options. We don't error here since these
518
- # settings can be set at the user or system level.
519
- if @project.modules && puppetfile_config.any? && module_install.empty?
520
- command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
521
- @logs << { warn: "Detected configuration for 'puppetfile'. This setting is not "\
522
- "used when 'modules' is configured. Use 'module-install' instead. "\
523
- "To automatically update your project configuration, run '#{command}'." }
524
- elsif @project.modules.nil? && puppetfile_config.empty? && module_install.any?
525
- @logs << { warn: "Detected configuration for 'module-install'. This setting is not "\
526
- "used when 'modules' is not configured. Use 'puppetfile' instead." }
527
- elsif @project.modules && puppetfile_config.any? && module_install.any?
528
- @logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
529
- "configuration for 'module-install' because 'modules' is also configured." }
530
- elsif @project.modules.nil? && puppetfile_config.any? && module_install.any?
531
- @logs << { warn: "Detected configuration for 'puppetfile' and 'module-install'. Using "\
532
- "configuration for 'puppetfile' because 'modules' is not configured." }
533
- end
534
356
  end
535
357
 
536
358
  def default_inventoryfile
@@ -546,21 +368,15 @@ module Bolt
546
368
  end
547
369
 
548
370
  def puppetfile
549
- @puppetfile || @project.puppetfile
371
+ @project.puppetfile
550
372
  end
551
373
 
552
374
  def modulepath
553
- path = @data['modulepath'] || @project.modulepath
554
-
555
- if @project.modules
556
- path + [@project.managed_moduledir.to_s]
557
- else
558
- path
559
- end
375
+ (@data['modulepath'] || @project.modulepath) + [@project.managed_moduledir.to_s]
560
376
  end
561
377
 
562
378
  def modulepath=(value)
563
- @data['modulepath'] = value
379
+ @data['modulepath'] = Array(value)
564
380
  end
565
381
 
566
382
  def plugin_cache
@@ -611,27 +427,12 @@ module Bolt
611
427
  @data['compile-concurrency']
612
428
  end
613
429
 
614
- def puppetfile_config
615
- @data['puppetfile']
616
- end
617
-
618
430
  def plugins
619
431
  @data['plugins']
620
432
  end
621
433
 
622
434
  def plugin_hooks
623
- if @data['plugin-hooks'].any? && @data['plugin_hooks'].any?
624
- Bolt::Logger.warn_once(
625
- "plugin-hooks and plugin_hooks set",
626
- "Detected configuration for 'plugin-hooks' and 'plugin_hooks'. Bolt will ignore 'plugin_hooks'."
627
- )
628
-
629
- @data['plugin-hooks']
630
- elsif @data['plugin-hooks'].any?
631
- @data['plugin-hooks']
632
- else
633
- @data['plugin_hooks']
634
- end
435
+ @data['plugin-hooks']
635
436
  end
636
437
 
637
438
  def trusted_external
@@ -639,18 +440,7 @@ module Bolt
639
440
  end
640
441
 
641
442
  def apply_settings
642
- if @data['apply-settings'].any? && @data['apply_settings'].any?
643
- Bolt::Logger.warn_once(
644
- "apply-settings and apply_settings set",
645
- "Detected configuration for 'apply-settings' and 'apply_settings'. Bolt will ignore 'apply_settings'."
646
- )
647
-
648
- @data['apply-settings']
649
- elsif @data['apply-settings'].any?
650
- @data['apply-settings']
651
- else
652
- @data['apply_settings']
653
- end
443
+ @data['apply-settings']
654
444
  end
655
445
 
656
446
  def transport
@@ -661,6 +451,10 @@ module Bolt
661
451
  @project.module_install || @data['module-install']
662
452
  end
663
453
 
454
+ def disable_warnings
455
+ Set.new(@project.disable_warnings + @data['disable-warnings'])
456
+ end
457
+
664
458
  # Check if there is a case-insensitive match to the path
665
459
  def check_path_case(type, paths)
666
460
  return if paths.nil?
@@ -669,7 +463,7 @@ module Bolt
669
463
  if matches.any?
670
464
  msg = "WARNING: Bolt is case sensitive when specifying a #{type}. Did you mean:\n"
671
465
  matches.each { |path| msg += " #{path}\n" }
672
- @logger.warn msg
466
+ Bolt::Logger.warn("path_case", msg)
673
467
  end
674
468
  end
675
469