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 +4 -4
- data/lib/bolt/bolt_option_parser.rb +6 -1
- data/lib/bolt/cli.rb +61 -138
- data/lib/bolt/error.rb +9 -3
- data/lib/bolt/executor.rb +12 -0
- data/lib/bolt/inventory/group.rb +2 -1
- data/lib/bolt/outputter/human.rb +19 -7
- data/lib/bolt/pal.rb +1 -1
- data/lib/bolt/plan_creator.rb +160 -0
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/project.rb +2 -2
- data/lib/bolt/project_migrator/config.rb +2 -1
- data/lib/bolt/project_migrator/inventory.rb +2 -2
- data/lib/bolt/puppetdb/config.rb +5 -5
- data/lib/bolt/shell/powershell.rb +1 -1
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/orch.rb +3 -3
- data/lib/bolt/transport/remote.rb +1 -1
- data/lib/bolt/util.rb +5 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d33993b3430c33f173a3a2d07220d175d4545c1992e9bed3bd75a7be7bae9be
|
4
|
+
data.tar.gz: 68fe674fd2d8ec196149938668d68b3e4c2becd3a97639c49ee85a637bb2e5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
data/lib/bolt/cli.rb
CHANGED
@@ -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/
|
21
|
+
require 'bolt/pal'
|
22
|
+
require 'bolt/plan_creator'
|
22
23
|
require 'bolt/plugin'
|
23
24
|
require 'bolt/project_migrator'
|
24
|
-
require 'bolt/
|
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
|
-
"
|
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 '
|
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
|
-
|
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 '
|
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 '
|
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 '
|
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(
|
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,
|
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 '
|
970
|
-
"
|
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
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
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
|
|
data/lib/bolt/error.rb
CHANGED
@@ -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
|
-
|
41
|
-
|
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
|
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
|
data/lib/bolt/executor.rb
CHANGED
@@ -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}")
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -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
|
-
'
|
248
|
+
'#{command}'.
|
248
249
|
MSG
|
249
250
|
raise ValidationError.new(msg, nil)
|
250
251
|
end
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -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
|
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 =
|
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'].
|
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
|
-
|
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 =
|
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
|
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
|
311
|
+
print_message("\nUse 'bolt guide <TOPIC>' to view a specific guide.")
|
300
312
|
end
|
301
313
|
|
302
314
|
def print_guide(guide, _topic)
|
data/lib/bolt/pal.rb
CHANGED
@@ -390,7 +390,7 @@ module Bolt
|
|
390
390
|
plan.docstring
|
391
391
|
end
|
392
392
|
|
393
|
-
defaults = plan.parameters.
|
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
|
data/lib/bolt/plugin.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -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
|
-
"'
|
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
|
-
|
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
|
14
|
+
private def inventory1to2(inventory_file, backup_dir)
|
15
15
|
unless File.exist?(inventory_file)
|
16
16
|
return true
|
17
17
|
end
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -6,14 +6,14 @@ require 'bolt/util'
|
|
6
6
|
module Bolt
|
7
7
|
module PuppetDB
|
8
8
|
class Config
|
9
|
-
if
|
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]
|
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
|
data/lib/bolt/task/run.rb
CHANGED
@@ -42,7 +42,7 @@ module Bolt
|
|
42
42
|
if targets.empty?
|
43
43
|
Bolt::ResultSet.new([])
|
44
44
|
else
|
45
|
-
result = executor.
|
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)
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -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
|
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.
|
32
|
+
arguments = arguments.merge('_target' => target.to_h.compact)
|
33
33
|
|
34
34
|
remote_task = task.remote_instance
|
35
35
|
|
data/lib/bolt/util.rb
CHANGED
@@ -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 }
|
data/lib/bolt/version.rb
CHANGED
@@ -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.
|
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-
|
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
|