bolt 2.33.2 → 2.34.0

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

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 956382104b8c11e17fdafa4349b50ec3d7c5ce6b03eaa57ce429bbfad768f8d3
4
- data.tar.gz: dd838e10c07c8f0f87266ff5edf3af4f5595fab45e6aab0ebf82e381226d3897
3
+ metadata.gz: 5d33993b3430c33f173a3a2d07220d175d4545c1992e9bed3bd75a7be7bae9be
4
+ data.tar.gz: 68fe674fd2d8ec196149938668d68b3e4c2becd3a97639c49ee85a637bb2e5a8
5
5
  SHA512:
6
- metadata.gz: 6c2b97a1c268d0fd93f1086efe623c740b74d9d1318596b9a74720e0e944455e244bfc2be2d02ba18634ed543a6ad87f6f27b88dc19189b22cd8fa495b75de02
7
- data.tar.gz: cff4bdac98aad6247866f989f9f3814dd11497fd73d90b08ecf15824ef0ee2b36c73fffeb296ecb8a6cae19eb2e71ec7b834dfe461fdbadd06d46b05b560a758
6
+ metadata.gz: 89e8fd86df6d1427fa61df4e54b0a1973ec607bcd46744a5fa4f0316f217ae02fa0b72428527a6d3ef15303f9fae371c62954d5b53a600d4d71589a3b2108656
7
+ data.tar.gz: e40ee91802e4dcb6a5d542f131800b28f4916ad2245837b34f26bcef128d99485c3d96ab02e498033476fe01fe6e5740be74b985370fd390ded7b8b0683b0e04
@@ -88,7 +88,7 @@ module Bolt
88
88
  { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
89
89
  banner: PLAN_CONVERT_HELP }
90
90
  when 'new'
91
- { flags: OPTIONS[:global] + %w[configfile project],
91
+ { flags: OPTIONS[:global] + %w[configfile project pp],
92
92
  banner: PLAN_NEW_HELP }
93
93
  when 'run'
94
94
  { flags: ACTION_OPTS + %w[params compile-concurrency tmpdir hiera-config],
@@ -945,6 +945,11 @@ module Bolt
945
945
  @options[:resolve] = resolve
946
946
  end
947
947
 
948
+ separator "\nPLAN OPTIONS"
949
+ define('--pp', 'Create a new Puppet language plan.') do |pp|
950
+ @options[:puppet] = pp
951
+ end
952
+
948
953
  separator "\nDISPLAY OPTIONS"
949
954
  define('--filter FILTER', 'Filter tasks and plans by a matching substring') do |filter|
950
955
  unless /^[a-z0-9_:]+$/.match(filter)
@@ -15,17 +15,18 @@ require 'bolt/config'
15
15
  require 'bolt/error'
16
16
  require 'bolt/executor'
17
17
  require 'bolt/inventory'
18
- require 'bolt/rerun'
19
18
  require 'bolt/logger'
19
+ require 'bolt/module_installer'
20
20
  require 'bolt/outputter'
21
- require 'bolt/puppetdb'
21
+ require 'bolt/pal'
22
+ require 'bolt/plan_creator'
22
23
  require 'bolt/plugin'
23
24
  require 'bolt/project_migrator'
24
- require 'bolt/pal'
25
+ require 'bolt/puppetdb'
26
+ require 'bolt/rerun'
27
+ require 'bolt/secret'
25
28
  require 'bolt/target'
26
29
  require 'bolt/version'
27
- require 'bolt/secret'
28
- require 'bolt/module_installer'
29
30
 
30
31
  module Bolt
31
32
  class CLIExit < StandardError; end
@@ -196,10 +197,6 @@ module Bolt
196
197
 
197
198
  warn_inventory_overrides_cli(options)
198
199
 
199
- # Assert whether the puppetfile/module commands are available depending
200
- # on whether 'modules' is configured.
201
- assert_puppetfile_or_module_command(config.project.modules)
202
-
203
200
  options
204
201
  rescue Bolt::Error => e
205
202
  outputter.fatal_error(e)
@@ -228,9 +225,9 @@ module Bolt
228
225
 
229
226
  def validate(options)
230
227
  unless COMMANDS.include?(options[:subcommand])
228
+ command = Bolt::Util.powershell? ? 'Get-Command -Module PuppetBolt' : 'bolt help'
231
229
  raise Bolt::CLIError,
232
- "Expected subcommand '#{options[:subcommand]}' to be one of " \
233
- "#{COMMANDS.keys.join(', ')}"
230
+ "'#{options[:subcommand]}' is not a Bolt command. See '#{command}'."
234
231
  end
235
232
 
236
233
  actions = COMMANDS[options[:subcommand]]
@@ -285,8 +282,9 @@ module Bolt
285
282
  end
286
283
 
287
284
  if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
285
+ command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
288
286
  raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
289
- "the project, run 'bolt module add #{options[:object]}'."
287
+ "the project, run '#{command} #{options[:object]}'."
290
288
  end
291
289
 
292
290
  if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
@@ -465,7 +463,16 @@ module Bolt
465
463
  when 'plan'
466
464
  case options[:action]
467
465
  when 'new'
468
- code = new_plan(options[:object])
466
+ command = Bolt::Util.powershell? ? 'New-BoltPlan' : 'bolt plan new'
467
+ @logger.warn("Command '#{command}' is experimental and subject to changes.")
468
+ plan_name = options[:object]
469
+
470
+ # If this passes validation, it will return the path to the plan to create
471
+ Bolt::PlanCreator.validate_input(config.project, plan_name)
472
+ code = Bolt::PlanCreator.create_plan(config.project.plans_path,
473
+ plan_name,
474
+ outputter,
475
+ options[:puppet])
469
476
  when 'run'
470
477
  code = run_plan(options[:object], options[:task_options], options[:target_args], options)
471
478
  end
@@ -617,118 +624,6 @@ module Bolt
617
624
  outputter.print_groups(groups)
618
625
  end
619
626
 
620
- def new_plan(plan_name)
621
- @logger.warn("Command 'bolt plan new' is experimental and subject to changes.")
622
-
623
- if config.project.name.nil?
624
- raise Bolt::Error.new(
625
- "Project directory '#{config.project.path}' is not a named project. Unable to create "\
626
- "a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
627
- "configuration file.",
628
- "bolt/unnamed-project-error"
629
- )
630
- end
631
-
632
- if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
633
- message = <<~MESSAGE.chomp
634
- Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
635
- separated by double colons '::'.
636
-
637
- Each name segment must begin with a lowercase letter, and may only include lowercase
638
- letters, digits, and underscores.
639
-
640
- Examples of valid plan names:
641
- - #{config.project.name}
642
- - #{config.project.name}::my_plan
643
- MESSAGE
644
-
645
- raise Bolt::ValidationError, message
646
- end
647
-
648
- prefix, *name_segments, basename = plan_name.split('::')
649
-
650
- # If the plan name is just the project name, then create an 'init' plan.
651
- # Otherwise, use the last name segment for the plan's filename.
652
- basename ||= 'init'
653
-
654
- unless prefix == config.project.name
655
- message = "First segment of plan name '#{plan_name}' must match project name '#{config.project.name}'. "\
656
- "Did you mean '#{config.project.name}::#{plan_name}'?"
657
-
658
- raise Bolt::ValidationError, message
659
- end
660
-
661
- dir_path = config.project.plans_path.join(*name_segments)
662
-
663
- %w[pp yaml].each do |ext|
664
- next unless (path = config.project.plans_path + "#{basename}.#{ext}").exist?
665
- raise Bolt::Error.new(
666
- "A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
667
- 'bolt/existing-plan-error'
668
- )
669
- end
670
-
671
- begin
672
- FileUtils.mkdir_p(dir_path)
673
- rescue Errno::EEXIST => e
674
- raise Bolt::Error.new(
675
- "#{e.message}; unable to create plan directory '#{dir_path}'",
676
- 'bolt/existing-file-error'
677
- )
678
- end
679
-
680
- plan_path = dir_path + "#{basename}.yaml"
681
-
682
- plan_template = <<~PLAN
683
- # This is the structure of a simple plan. To learn more about writing
684
- # YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
685
-
686
- # The description sets the description of the plan that will appear
687
- # in 'bolt plan show' output.
688
- description: A plan created with bolt plan new
689
-
690
- # The parameters key defines the parameters that can be passed to
691
- # the plan.
692
- parameters:
693
- targets:
694
- type: TargetSpec
695
- description: A list of targets to run actions on
696
- default: localhost
697
-
698
- # The steps key defines the actions the plan will take in order.
699
- steps:
700
- - message: Hello from #{plan_name}
701
- - name: command_step
702
- command: whoami
703
- targets: $targets
704
-
705
- # The return key sets the return value of the plan.
706
- return: $command_step
707
- PLAN
708
-
709
- begin
710
- File.write(plan_path, plan_template)
711
- rescue Errno::EACCES => e
712
- raise Bolt::FileError.new(
713
- "#{e.message}; unable to create plan",
714
- plan_path
715
- )
716
- end
717
-
718
- output = <<~OUTPUT
719
- Created plan '#{plan_name}' at '#{plan_path}'
720
-
721
- Show this plan with:
722
- bolt plan show #{plan_name}
723
- Run this plan with:
724
- bolt plan run #{plan_name}
725
- OUTPUT
726
-
727
- outputter.print_message(output)
728
-
729
- 0
730
- end
731
-
732
627
  def run_plan(plan_name, plan_arguments, nodes, options)
733
628
  unless nodes.empty?
734
629
  if plan_arguments['nodes'] || plan_arguments['targets']
@@ -820,10 +715,12 @@ module Bolt
820
715
  end
821
716
 
822
717
  def list_modules
718
+ assert_puppetfile_or_module_command(config.project.modules)
823
719
  outputter.print_module_list(pal.list_modules)
824
720
  end
825
721
 
826
722
  def generate_types
723
+ assert_puppetfile_or_module_command(config.project.modules)
827
724
  # generate_types will surface a nice error with helpful message if it fails
828
725
  pal.generate_types
829
726
  0
@@ -844,8 +741,9 @@ module Bolt
844
741
  "project name must begin with a lowercase letter and can include lowercase "\
845
742
  "letters, numbers, and underscores."
846
743
  else
744
+ command = Bolt::Util.powershell? ? 'New-BoltProject -Name <NAME>' : 'bolt project init <NAME>'
847
745
  raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
848
- "project name. Please specify a name using 'bolt project init <name>'."
746
+ "project name. Please specify a name using '#{command}'."
849
747
  end
850
748
  end
851
749
 
@@ -902,6 +800,7 @@ module Bolt
902
800
  #
903
801
  def install_project_modules(project, force, resolve)
904
802
  assert_project_file(project)
803
+ assert_puppetfile_or_module_command(project.modules)
905
804
 
906
805
  unless project.modules
907
806
  outputter.print_message "Project configuration file #{project.project_file} does not "\
@@ -923,6 +822,7 @@ module Bolt
923
822
  #
924
823
  def add_project_module(name, project)
925
824
  assert_project_file(project)
825
+ assert_puppetfile_or_module_command(project.modules)
926
826
 
927
827
  modules = project.modules || []
928
828
  installer = Bolt::ModuleInstaller.new(outputter, pal)
@@ -940,11 +840,13 @@ module Bolt
940
840
  def assert_project_file(project)
941
841
  unless project.project_file?
942
842
  msg = if project.config_file.exist?
843
+ command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
943
844
  "Detected Bolt configuration file #{project.config_file}, unable to install "\
944
- "modules. To update to a project configuration file, run 'bolt project migrate'."
845
+ "modules. To update to a project configuration file, run '#{command}'."
945
846
  else
847
+ command = Bolt::Util.powershell? ? 'New-BoltProject' : 'bolt project init'
946
848
  "Could not find project configuration file #{project.project_file}, unable "\
947
- "to install modules. To create a Bolt project, run 'bolt project init'."
849
+ "to install modules. To create a Bolt project, run '#{command}'."
948
850
  end
949
851
 
950
852
  raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
@@ -953,10 +855,12 @@ module Bolt
953
855
 
954
856
  # Loads a Puppetfile and installs its modules.
955
857
  #
956
- def install_puppetfile(config, puppetfile, moduledir)
858
+ def install_puppetfile(puppetfile_config, puppetfile, moduledir)
859
+ assert_puppetfile_or_module_command(config.project.modules)
860
+
957
861
  outputter.print_message("Installing modules from Puppetfile")
958
862
  installer = Bolt::ModuleInstaller.new(outputter, pal)
959
- ok = installer.install_puppetfile(puppetfile, moduledir, config)
863
+ ok = installer.install_puppetfile(puppetfile, moduledir, puppetfile_config)
960
864
  ok ? 0 : 1
961
865
  end
962
866
 
@@ -964,17 +868,36 @@ module Bolt
964
868
  # modules being configured.
965
869
  #
966
870
  def assert_puppetfile_or_module_command(modules)
871
+ if Bolt::Util.powershell?
872
+ case options[:action]
873
+ when 'generate-types'
874
+ old_command = 'Register-BoltPuppetfileTypes'
875
+ new_command = 'Register-BoltModuleTypes'
876
+ when 'install'
877
+ old_command = 'Install-BoltPuppetfile'
878
+ new_command = 'Install-BoltModule'
879
+ when 'show', 'show-modules'
880
+ old_command = 'Get-BoltPuppetfileModules'
881
+ new_command = 'Get-BoltModule'
882
+ end
883
+ else
884
+ old_command = "bolt puppetfile #{options[:action]}"
885
+ new_command = if options[:action] == 'show-modules'
886
+ 'bolt module show'
887
+ else
888
+ "bolt module #{options[:action]}"
889
+ end
890
+ end
891
+
967
892
  if modules && options[:subcommand] == 'puppetfile'
968
893
  raise Bolt::CLIError,
969
- "Unable to use command 'bolt puppetfile #{options[:action]}' when "\
970
- "'modules' is configured in bolt-project.yaml. Use the 'module' command "\
971
- "instead. For a list of available actions for the 'module' command, run "\
972
- "'bolt module --help'."
894
+ "Unable to use command '#{old_command}' when 'modules' is configured in "\
895
+ "bolt-project.yaml. Use '#{new_command}' instead."
973
896
  elsif modules.nil? && options[:subcommand] == 'module'
974
- raise Bolt::CLIError,
975
- "Unable to use command 'bolt module #{options[:action]}'. To use "\
976
- "this command, update your project configuration to manage module "\
977
- "dependencies."
897
+ msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
898
+ "bolt-project.yaml. "
899
+ msg += "Use '#{old_command}' instead." if options[:action] != 'add'
900
+ raise Bolt::CLIError, msg
978
901
  end
979
902
  end
980
903
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bolt/util'
4
+
3
5
  module Bolt
4
6
  class Error < RuntimeError
5
7
  attr_reader :kind, :details, :issue_code, :error_code
@@ -37,13 +39,17 @@ module Bolt
37
39
  end
38
40
 
39
41
  def self.unknown_task(task)
40
- new("Could not find a task named \"#{task}\". For a list of available tasks, run \"bolt task show\"",
41
- 'bolt/unknown-task')
42
+ command = Bolt::Util.powershell? ? "Get-BoltTask" : "bolt task show"
43
+ new(
44
+ "Could not find a task named '#{task}'. For a list of available tasks, run '#{command}'.",
45
+ 'bolt/unknown-task'
46
+ )
42
47
  end
43
48
 
44
49
  def self.unknown_plan(plan)
50
+ command = Bolt::Util.powershell? ? "Get-BoltPlan" : "bolt plan show"
45
51
  new(
46
- "Could not find a plan named \"#{plan}\". For a list of available plans, run \"bolt plan show\"",
52
+ "Could not find a plan named '#{plan}'. For a list of available plans, run '#{command}'.",
47
53
  'bolt/unknown-plan'
48
54
  )
49
55
  end
@@ -293,6 +293,18 @@ module Bolt
293
293
  end
294
294
  end
295
295
 
296
+ def run_task_with_minimal_logging(targets, task, arguments, options = {})
297
+ description = options.fetch(:description, "task #{task.name}")
298
+ log_action(description, targets) do
299
+ options[:run_as] = run_as if run_as && !options.key?(:run_as)
300
+ arguments['_task'] = task.name
301
+
302
+ batch_execute(targets) do |transport, batch|
303
+ transport.batch_task(batch, task, arguments, options, [], &method(:publish_event))
304
+ end
305
+ end
306
+ end
307
+
296
308
  def run_task_with(target_mapping, task, options = {}, position = [])
297
309
  targets = target_mapping.keys
298
310
  description = options.fetch(:description, "task #{task.name}")
@@ -241,10 +241,11 @@ module Bolt
241
241
  end
242
242
 
243
243
  if input.key?('nodes')
244
+ command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
244
245
  msg = <<~MSG.chomp
245
246
  Found 'nodes' key in group #{@name}. This looks like a v1 inventory file, which is
246
247
  no longer supported by Bolt. Migrate to a v2 inventory file automatically using
247
- 'bolt project migrate'.
248
+ '#{command}'.
248
249
  MSG
249
250
  raise ValidationError.new(msg, nil)
250
251
  end
@@ -214,9 +214,10 @@ module Bolt
214
214
  end
215
215
 
216
216
  def print_tasks(tasks, modulepath)
217
+ command = Bolt::Util.powershell? ? 'Get-BoltTask -Task <TASK NAME>' : 'bolt task show <TASK NAME>'
217
218
  print_table(tasks)
218
219
  print_message("\nMODULEPATH:\n#{modulepath.join(File::PATH_SEPARATOR)}\n"\
219
- "\nUse `bolt task show <task-name>` to view "\
220
+ "\nUse '#{command}' to view "\
220
221
  "details and parameters for a specific task.")
221
222
  end
222
223
 
@@ -225,20 +226,26 @@ module Bolt
225
226
  # Building lots of strings...
226
227
  pretty_params = +""
227
228
  task_info = +""
228
- usage = +"bolt task run --targets <node-name> #{task.name}"
229
+ usage = if Bolt::Util.powershell?
230
+ +"Invoke-BoltTask -Name #{task.name} -Targets <targets>"
231
+ else
232
+ +"bolt task run #{task.name} --targets <targets>"
233
+ end
229
234
 
230
235
  task.parameters&.each do |k, v|
231
236
  pretty_params << "- #{k}: #{v['type'] || 'Any'}\n"
232
237
  pretty_params << " Default: #{v['default'].inspect}\n" if v.key?('default')
233
238
  pretty_params << " #{v['description']}\n" if v['description']
234
- usage << if v['type'].is_a?(Puppet::Pops::Types::POptionalType)
239
+ usage << if v['type'].start_with?("Optional")
235
240
  " [#{k}=<value>]"
236
241
  else
237
242
  " #{k}=<value>"
238
243
  end
239
244
  end
240
245
 
241
- usage << " [--noop]" if task.supports_noop
246
+ if task.supports_noop
247
+ usage << Bolt::Util.powershell? ? '[-Noop]' : '[--noop]'
248
+ end
242
249
 
243
250
  task_info << "\n#{task.name}"
244
251
  task_info << " - #{task.description}" if task.description
@@ -261,7 +268,11 @@ module Bolt
261
268
  # Building lots of strings...
262
269
  pretty_params = +""
263
270
  plan_info = +""
264
- usage = +"bolt plan run #{plan['name']}"
271
+ usage = if Bolt::Util.powershell?
272
+ +"Invoke-BoltPlan -Name #{plan['name']}"
273
+ else
274
+ +"bolt plan run #{plan['name']}"
275
+ end
265
276
 
266
277
  plan['parameters'].each do |name, p|
267
278
  pretty_params << "- #{name}: #{p['type']}\n"
@@ -287,16 +298,17 @@ module Bolt
287
298
  end
288
299
 
289
300
  def print_plans(plans, modulepath)
301
+ command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
290
302
  print_table(plans)
291
303
  print_message("\nMODULEPATH:\n#{modulepath.join(File::PATH_SEPARATOR)}\n"\
292
- "\nUse `bolt plan show <plan-name>` to view "\
304
+ "\nUse '#{command}' to view "\
293
305
  "details and parameters for a specific plan.")
294
306
  end
295
307
 
296
308
  def print_topics(topics)
297
309
  print_message("Available topics are:")
298
310
  print_message(topics.join("\n"))
299
- print_message("\nUse `bolt guide <topic>` to view a specific guide.")
311
+ print_message("\nUse 'bolt guide <TOPIC>' to view a specific guide.")
300
312
  end
301
313
 
302
314
  def print_guide(guide, _topic)
@@ -390,7 +390,7 @@ module Bolt
390
390
  plan.docstring
391
391
  end
392
392
 
393
- defaults = plan.parameters.reject { |_, value| value.nil? }.to_h
393
+ defaults = plan.parameters.to_h.compact
394
394
  signature_params = Set.new(plan.parameters.map(&:first))
395
395
  parameters = plan.tags(:param).each_with_object({}) do |param, params|
396
396
  name = param.name
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+ require 'bolt/logger'
5
+ require 'bolt/module'
6
+ require 'bolt/util'
7
+
8
+ module Bolt
9
+ module PlanCreator
10
+ def self.validate_input(project, plan_name)
11
+ if project.name.nil?
12
+ raise Bolt::Error.new(
13
+ "Project directory '#{project.path}' is not a named project. Unable to create "\
14
+ "a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
15
+ "configuration file.",
16
+ "bolt/unnamed-project-error"
17
+ )
18
+ end
19
+
20
+ if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
21
+ message = <<~MESSAGE.chomp
22
+ Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
23
+ separated by double colons '::'.
24
+
25
+ Each name segment must begin with a lowercase letter, and may only include lowercase
26
+ letters, digits, and underscores.
27
+
28
+ Examples of valid plan names:
29
+ - #{project.name}
30
+ - #{project.name}::my_plan
31
+ MESSAGE
32
+
33
+ raise Bolt::ValidationError, message
34
+ end
35
+
36
+ prefix, _, basename = segment_plan_name(plan_name)
37
+
38
+ unless prefix == project.name
39
+ message = "First segment of plan name '#{plan_name}' must match project name '#{project.name}'. "\
40
+ "Did you mean '#{project.name}::#{plan_name}'?"
41
+
42
+ raise Bolt::ValidationError, message
43
+ end
44
+
45
+ %w[pp yaml].each do |ext|
46
+ next unless (path = project.plans_path + "#{basename}.#{ext}").exist?
47
+ raise Bolt::Error.new(
48
+ "A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
49
+ 'bolt/existing-plan-error'
50
+ )
51
+ end
52
+ end
53
+
54
+ def self.create_plan(plans_path, plan_name, outputter, is_puppet)
55
+ _, name_segments, basename = segment_plan_name(plan_name)
56
+ dir_path = plans_path.join(*name_segments)
57
+
58
+ begin
59
+ FileUtils.mkdir_p(dir_path)
60
+ rescue Errno::EEXIST => e
61
+ raise Bolt::Error.new(
62
+ "#{e.message}; unable to create plan directory '#{dir_path}'",
63
+ 'bolt/existing-file-error'
64
+ )
65
+ end
66
+
67
+ type = is_puppet ? 'pp' : 'yaml'
68
+ plan_path = dir_path + "#{basename}.#{type}"
69
+ plan_template = is_puppet ? puppet_plan(plan_name) : yaml_plan(plan_name)
70
+
71
+ begin
72
+ File.write(plan_path, plan_template)
73
+ rescue Errno::EACCES => e
74
+ raise Bolt::FileError.new(
75
+ "#{e.message}; unable to create plan",
76
+ plan_path
77
+ )
78
+ end
79
+
80
+ if Bolt::Util.powershell?
81
+ show_command = 'Get-BoltPlan -Name '
82
+ run_command = 'Invoke-BoltPlan -Name '
83
+ else
84
+ show_command = 'bolt plan show'
85
+ run_command = 'bolt plan run'
86
+ end
87
+
88
+ output = <<~OUTPUT
89
+ Created plan '#{plan_name}' at '#{plan_path}'
90
+
91
+ Show this plan with:
92
+ #{show_command} #{plan_name}
93
+ Run this plan with:
94
+ #{run_command} #{plan_name}
95
+ OUTPUT
96
+
97
+ outputter.print_message(output)
98
+ 0
99
+ end
100
+
101
+ def self.segment_plan_name(plan_name)
102
+ prefix, *name_segments, basename = plan_name.split('::')
103
+
104
+ # If the plan name is just the project name, then create an 'init' plan.
105
+ # Otherwise, use the last name segment for the plan's filename.
106
+ basename ||= 'init'
107
+
108
+ [prefix, name_segments, basename]
109
+ end
110
+
111
+ def self.yaml_plan(plan_name)
112
+ <<~YAML
113
+ # This is the structure of a simple plan. To learn more about writing
114
+ # YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
115
+
116
+ # The description sets the description of the plan that will appear
117
+ # in 'bolt plan show' output.
118
+ description: A plan created with bolt plan new
119
+
120
+ # The parameters key defines the parameters that can be passed to
121
+ # the plan.
122
+ parameters:
123
+ targets:
124
+ type: TargetSpec
125
+ description: A list of targets to run actions on
126
+ default: localhost
127
+
128
+ # The steps key defines the actions the plan will take in order.
129
+ steps:
130
+ - message: Hello from #{plan_name}
131
+ - name: command_step
132
+ command: whoami
133
+ targets: $targets
134
+
135
+ # The return key sets the return value of the plan.
136
+ return: $command_step
137
+ YAML
138
+ end
139
+
140
+ def self.puppet_plan(plan_name)
141
+ <<~PUPPET
142
+ # This is the structure of a simple plan. To learn more about writing
143
+ # Puppet plans, see the documentation: http://pup.pt/bolt-puppet-plans
144
+
145
+ # The summary sets the description of the plan that will appear
146
+ # in 'bolt plan show' output. Bolt uses puppet-strings to parse the
147
+ # summary and parameters from the plan.
148
+ # @summary A plan created with bolt plan new.
149
+ # @param targets The targets to run on.
150
+ plan #{plan_name} (
151
+ TargetSpec $targets = "localhost"
152
+ ) {
153
+ out::message("Hello from #{plan_name}")
154
+ $command_result = run_command('whoami', $targets)
155
+ return $command_result
156
+ }
157
+ PUPPET
158
+ end
159
+ end
160
+ end
@@ -290,7 +290,7 @@ module Bolt
290
290
  begin
291
291
  validate_proc = get_hook(plugin_name, :validate_resolve_reference)
292
292
  rescue PluginError
293
- validate_proc = proc { |*args| }
293
+ validate_proc = proc { |*args| } # Nothing to do
294
294
  end
295
295
 
296
296
  validate_proc.call(reference)
@@ -11,9 +11,9 @@ module Bolt
11
11
  PROJECT_SETTINGS = {
12
12
  "name" => "The name of the project",
13
13
  "plans" => "An array of plan names to show, if they exist in the project."\
14
- "These plans are included in `bolt plan show` output",
14
+ "These plans are included in `bolt plan show` and `Get-BoltPlan` output",
15
15
  "tasks" => "An array of task names to show, if they exist in the project."\
16
- "These tasks are included in `bolt task show` output"
16
+ "These tasks are included in `bolt task show` and `Get-BoltTask` output"
17
17
  }.freeze
18
18
 
19
19
  attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
@@ -54,10 +54,11 @@ module Bolt
54
54
  @outputter.print_action_step("Renaming bolt.yaml to bolt-project.yaml")
55
55
  FileUtils.mv(config_file, project_file)
56
56
 
57
+ command = Bolt::Util.powershell? ? 'Get-Help about_bolt_project' : 'bolt guide project'
57
58
  @outputter.print_action_step(
58
59
  "Successfully migrated config. Please add a 'name' key to bolt-project.yaml "\
59
60
  "to use project-level tasks and plans. Learn more about projects by running "\
60
- "'bolt guide project'."
61
+ "'#{command}'."
61
62
  )
62
63
 
63
64
  true
@@ -6,12 +6,12 @@ module Bolt
6
6
  class ProjectMigrator
7
7
  class Inventory < Base
8
8
  def migrate(inventory_file, backup_dir)
9
- inventory_1_to_2(inventory_file, backup_dir)
9
+ inventory1to2(inventory_file, backup_dir)
10
10
  end
11
11
 
12
12
  # Migrates an inventory v1 file to inventory v2.
13
13
  #
14
- private def inventory_1_to_2(inventory_file, backup_dir)
14
+ private def inventory1to2(inventory_file, backup_dir)
15
15
  unless File.exist?(inventory_file)
16
16
  return true
17
17
  end
@@ -6,14 +6,14 @@ require 'bolt/util'
6
6
  module Bolt
7
7
  module PuppetDB
8
8
  class Config
9
- if !ENV['HOME'].nil?
10
- DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
11
- DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
12
- global: '/etc/puppetlabs/client-tools/puppetdb.conf' }.freeze
13
- else
9
+ if ENV['HOME'].nil?
14
10
  DEFAULT_TOKEN = Bolt::Util.windows? ? 'nul' : '/dev/null'
15
11
  DEFAULT_CONFIG = { user: '/etc/puppetlabs/puppet/puppetdb.conf',
16
12
  global: '/etc/puppetlabs/puppet/puppetdb.conf' }.freeze
13
+ else
14
+ DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
15
+ DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
16
+ global: '/etc/puppetlabs/client-tools/puppetdb.conf' }.freeze
17
17
 
18
18
  end
19
19
 
@@ -11,7 +11,7 @@ module Bolt
11
11
  def initialize(target, conn)
12
12
  super
13
13
 
14
- extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] != '.' ? '.' + ext : ext }
14
+ extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] == '.' ? ext : '.' + ext }
15
15
  extensions += target.options['interpreters'].keys if target.options['interpreters']
16
16
  @extensions = DEFAULT_EXTENSIONS + extensions
17
17
  end
@@ -42,7 +42,7 @@ module Bolt
42
42
  if targets.empty?
43
43
  Bolt::ResultSet.new([])
44
44
  else
45
- result = executor.run_task(targets, task, params, options)
45
+ result = executor.run_task_with_minimal_logging(targets, task, params, options)
46
46
 
47
47
  if !result.ok && !options[:catch_errors]
48
48
  raise Bolt::RunFailure.new(result, 'run_task', task.name)
@@ -10,10 +10,10 @@ require 'bolt/transport/orch/connection'
10
10
  module Bolt
11
11
  module Transport
12
12
  class Orch < Base
13
- CONF_FILE = if !ENV['HOME'].nil?
14
- File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
15
- else
13
+ CONF_FILE = if ENV['HOME'].nil?
16
14
  '/etc/puppetlabs/client-tools/orchestrator.conf'
15
+ else
16
+ File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
17
17
  end
18
18
  BOLT_COMMAND_TASK = Struct.new(:name).new('bolt_shim::command').freeze
19
19
  BOLT_SCRIPT_TASK = Struct.new(:name).new('bolt_shim::script').freeze
@@ -29,7 +29,7 @@ module Bolt
29
29
  def run_task(target, task, arguments, options = {}, position = [])
30
30
  proxy_target = get_proxy(target)
31
31
  transport = @executor.transport(proxy_target.transport)
32
- arguments = arguments.merge('_target' => target.to_h.reject { |_, v| v.nil? })
32
+ arguments = arguments.merge('_target' => target.to_h.compact)
33
33
 
34
34
  remote_task = task.remote_instance
35
35
 
@@ -273,6 +273,11 @@ module Bolt
273
273
  !!File::ALT_SEPARATOR
274
274
  end
275
275
 
276
+ # Returns true if running in PowerShell.
277
+ def powershell?
278
+ !!ENV['PSModulePath']
279
+ end
280
+
276
281
  # Accept hash and return hash with top level keys of type "String" converted to symbols.
277
282
  def symbolize_top_level_keys(hsh)
278
283
  hsh.each_with_object({}) { |(k, v), h| k.is_a?(String) ? h[k.to_sym] = v : h[k] = v }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.33.2'
4
+ VERSION = '2.34.0'
5
5
  end
@@ -63,6 +63,7 @@ module BoltServer
63
63
  end
64
64
 
65
65
  def client
66
+ # rubocop:disable Naming/VariableNumber
66
67
  @client ||= begin
67
68
  uri = URI(@config['file-server-uri'])
68
69
  https = Net::HTTP.new(uri.host, uri.port)
@@ -75,6 +76,7 @@ module BoltServer
75
76
  https.open_timeout = @config['file-server-conn-timeout']
76
77
  https
77
78
  end
79
+ # rubocop:enable Naming/VariableNumber
78
80
  end
79
81
 
80
82
  def request_file(path, params, file)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.33.2
4
+ version: 2.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-04 00:00:00.000000000 Z
11
+ date: 2020-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -506,6 +506,7 @@ files:
506
506
  - lib/bolt/pal/yaml_plan/step/task.rb
507
507
  - lib/bolt/pal/yaml_plan/step/upload.rb
508
508
  - lib/bolt/pal/yaml_plan/transpiler.rb
509
+ - lib/bolt/plan_creator.rb
509
510
  - lib/bolt/plan_result.rb
510
511
  - lib/bolt/plugin.rb
511
512
  - lib/bolt/plugin/env_var.rb