bolt 3.13.0 → 3.16.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +137 -104
- data/bolt-modules/boltlib/lib/puppet/functions/background.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +5 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +13 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +47 -7
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +4 -2
- data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +4 -2
- data/guides/{debugging.txt → debugging.yaml} +5 -6
- data/guides/{inventory.txt → inventory.yaml} +6 -7
- data/guides/{links.txt → links.yaml} +3 -4
- data/guides/{logging.txt → logging.yaml} +5 -6
- data/guides/{module.txt → module.yaml} +5 -6
- data/guides/{modulepath.txt → modulepath.yaml} +5 -6
- data/guides/{project.txt → project.yaml} +6 -7
- data/guides/{targets.txt → targets.yaml} +5 -6
- data/guides/{transports.txt → transports.yaml} +6 -7
- data/lib/bolt/analytics.rb +3 -20
- data/lib/bolt/application.rb +620 -0
- data/lib/bolt/bolt_option_parser.rb +17 -5
- data/lib/bolt/cli.rb +592 -772
- data/lib/bolt/config/transport/options.rb +12 -0
- data/lib/bolt/config/transport/ssh.rb +7 -0
- data/lib/bolt/executor.rb +12 -4
- data/lib/bolt/fiber_executor.rb +63 -14
- data/lib/bolt/module_installer/puppetfile.rb +24 -10
- data/lib/bolt/outputter/human.rb +199 -43
- data/lib/bolt/outputter/json.rb +66 -43
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/pal.rb +67 -14
- data/lib/bolt/pal/yaml_plan/step.rb +2 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +0 -8
- data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -1
- data/lib/bolt/plan_creator.rb +2 -20
- data/lib/bolt/plan_future.rb +23 -3
- data/lib/bolt/plan_result.rb +1 -1
- data/lib/bolt/plugin/task.rb +1 -1
- data/lib/bolt/project.rb +0 -7
- data/lib/bolt/result_set.rb +2 -1
- data/lib/bolt/transport/local/connection.rb +17 -1
- data/lib/bolt/transport/orch/connection.rb +13 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +3 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +12 -0
- data/lib/bolt_server/schemas/action-apply.json +32 -0
- data/lib/bolt_server/schemas/action-apply_prep.json +19 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +4 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +4 -0
- data/lib/bolt_server/transport_app.rb +180 -60
- data/lib/bolt_spec/plans/mock_executor.rb +16 -6
- metadata +23 -15
- data/guides/guide.txt +0 -17
- data/lib/bolt/secret.rb +0 -37
data/lib/bolt/outputter/json.rb
CHANGED
@@ -47,8 +47,13 @@ module Bolt
|
|
47
47
|
@stream.puts results.to_json
|
48
48
|
end
|
49
49
|
alias print_module_list print_table
|
50
|
+
alias print_module_info print_table
|
50
51
|
|
51
|
-
|
52
|
+
# Print information about a task.
|
53
|
+
#
|
54
|
+
# @param task [Bolt::Task] The task information.
|
55
|
+
#
|
56
|
+
def print_task_info(task:)
|
52
57
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
53
58
|
module_dir = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
54
59
|
"built-in module"
|
@@ -58,11 +63,16 @@ module Bolt
|
|
58
63
|
@stream.puts task.to_h.merge(module_dir: module_dir).to_json
|
59
64
|
end
|
60
65
|
|
61
|
-
|
62
|
-
|
66
|
+
# List available tasks.
|
67
|
+
#
|
68
|
+
# @param tasks [Array] A list of task names and descriptions.
|
69
|
+
# @param modulepath [Array] The modulepath.
|
70
|
+
#
|
71
|
+
def print_tasks(**kwargs)
|
72
|
+
print_table(**kwargs)
|
63
73
|
end
|
64
74
|
|
65
|
-
def print_plugin_list(plugins
|
75
|
+
def print_plugin_list(plugins:, modulepath:)
|
66
76
|
plugins.delete(:validate_resolve_reference)
|
67
77
|
print_table('plugins' => plugins, 'modulepath' => modulepath)
|
68
78
|
end
|
@@ -77,11 +87,15 @@ module Bolt
|
|
77
87
|
@stream.puts plan.to_json
|
78
88
|
end
|
79
89
|
|
80
|
-
def print_plans(
|
81
|
-
print_table(
|
90
|
+
def print_plans(**kwargs)
|
91
|
+
print_table(**kwargs)
|
82
92
|
end
|
83
93
|
|
84
|
-
def
|
94
|
+
def print_new_plan(**kwargs)
|
95
|
+
print_table(**kwargs)
|
96
|
+
end
|
97
|
+
|
98
|
+
def print_apply_result(apply_result)
|
85
99
|
@stream.puts apply_result.to_json
|
86
100
|
end
|
87
101
|
|
@@ -94,15 +108,21 @@ module Bolt
|
|
94
108
|
@stream.puts result_set.to_json
|
95
109
|
end
|
96
110
|
|
97
|
-
|
98
|
-
|
111
|
+
# Print available guide topics.
|
112
|
+
#
|
113
|
+
# @param topics [Array] The available topics.
|
114
|
+
#
|
115
|
+
def print_topics(**kwargs)
|
116
|
+
print_table(kwargs)
|
99
117
|
end
|
100
118
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
119
|
+
# Print the guide for the specified topic.
|
120
|
+
#
|
121
|
+
# @param guide [String] The guide.
|
122
|
+
# @param topic [String] The topic.
|
123
|
+
#
|
124
|
+
def print_guide(**kwargs)
|
125
|
+
@stream.puts(kwargs.to_json)
|
106
126
|
end
|
107
127
|
|
108
128
|
def print_plan_lookup(value)
|
@@ -115,35 +135,38 @@ module Bolt
|
|
115
135
|
moduledir: moduledir.to_s }.to_json)
|
116
136
|
end
|
117
137
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
)
|
141
|
-
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
138
|
+
# Print target names and where they came from.
|
139
|
+
#
|
140
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
141
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
142
|
+
# @param targets [Array] All targets.
|
143
|
+
# @param count [Integer] Number of targets.
|
144
|
+
#
|
145
|
+
def print_targets(adhoc:, inventory:, targets:, count:, **_kwargs)
|
146
|
+
adhoc[:targets] = adhoc[:targets].map { |t| t['name'] }
|
147
|
+
inventory[:targets] = inventory[:targets].map { |t| t['name'] }
|
148
|
+
targets = targets.map { |t| t['name'] }
|
149
|
+
@stream.puts({ adhoc: adhoc, inventory: inventory, targets: targets, count: count }.to_json)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Print target names and where they came from.
|
153
|
+
#
|
154
|
+
# @param adhoc [Hash] Adhoc targets provided on the command line.
|
155
|
+
# @param inventory [Hash] Targets provided from the inventory.
|
156
|
+
# @param targets [Array] All targets.
|
157
|
+
# @param count [Integer] Number of targets.
|
158
|
+
#
|
159
|
+
def print_target_info(adhoc:, inventory:, targets:, count:, **_kwargs)
|
160
|
+
@stream.puts({ adhoc: adhoc, inventory: inventory, targets: targets, count: count }.to_json)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Print inventory group information.
|
164
|
+
#
|
165
|
+
# @param count [Integer] Number of groups in the inventory.
|
166
|
+
# @param groups [Array] Names of groups in the inventory.
|
167
|
+
#
|
168
|
+
def print_groups(count:, groups:, **_kwargs)
|
169
|
+
@stream.puts({ count: count, groups: groups }.to_json)
|
147
170
|
end
|
148
171
|
|
149
172
|
def fatal_error(err)
|
data/lib/bolt/pal.rb
CHANGED
@@ -339,7 +339,7 @@ module Bolt
|
|
339
339
|
end
|
340
340
|
|
341
341
|
# Write the cache if any entries were updated
|
342
|
-
File.write(@project.task_cache_file, task_cache.to_json) if updated
|
342
|
+
File.write(@project.task_cache_file, task_cache.to_json) if updated && @project
|
343
343
|
filter_content ? filter_content(task_list, @project&.tasks) : task_list
|
344
344
|
end
|
345
345
|
|
@@ -435,7 +435,7 @@ module Bolt
|
|
435
435
|
list << [plan_name, data['description']] unless data['private']
|
436
436
|
end
|
437
437
|
|
438
|
-
File.write(@project.plan_cache_file, plan_cache.to_json) if updated
|
438
|
+
File.write(@project.plan_cache_file, plan_cache.to_json) if updated && @project
|
439
439
|
|
440
440
|
filter_content ? filter_content(plan_list, @project&.plans) : plan_list
|
441
441
|
end
|
@@ -447,7 +447,7 @@ module Bolt
|
|
447
447
|
# @param mtime [String] The last time the file was modified.
|
448
448
|
#
|
449
449
|
private def file_modified?(path, mtime)
|
450
|
-
path && !(File.exist?(path) && File.mtime(path).to_s == mtime)
|
450
|
+
path && !(File.exist?(path) && File.mtime(path).to_s == mtime.to_s)
|
451
451
|
end
|
452
452
|
|
453
453
|
def list_plans(filter_content: false)
|
@@ -512,19 +512,14 @@ module Bolt
|
|
512
512
|
end
|
513
513
|
end
|
514
514
|
|
515
|
-
privie = plan.tag(:private)&.text
|
516
|
-
unless privie.nil? || %w[true false].include?(privie.downcase)
|
517
|
-
msg = "Plan #{plan_name} key 'private' must be a boolean, received: #{privie}"
|
518
|
-
raise Bolt::Error.new(msg, 'bolt/invalid-plan')
|
519
|
-
end
|
520
|
-
|
521
515
|
pp_info = {
|
522
516
|
'name' => plan_name,
|
523
517
|
'description' => description,
|
524
518
|
'parameters' => parameters,
|
525
|
-
'module' => mod
|
519
|
+
'module' => mod,
|
520
|
+
'private' => private_plan?(plan)
|
526
521
|
}
|
527
|
-
|
522
|
+
|
528
523
|
pp_info.merge!(get_plan_mtime(plan.file)) if with_mtime
|
529
524
|
pp_info
|
530
525
|
|
@@ -554,14 +549,39 @@ module Bolt
|
|
554
549
|
'name' => plan_name,
|
555
550
|
'description' => plan.description,
|
556
551
|
'parameters' => parameters,
|
557
|
-
'module' => mod
|
552
|
+
'module' => mod,
|
553
|
+
'private' => !!plan.private
|
558
554
|
}
|
559
|
-
|
555
|
+
|
560
556
|
yaml_info.merge!(get_plan_mtime(yaml_path)) if with_mtime
|
561
557
|
yaml_info
|
562
558
|
end
|
563
559
|
end
|
564
560
|
|
561
|
+
# Returns true if the plan is private, false otherwise.
|
562
|
+
#
|
563
|
+
# @param plan [PuppetStrings::Yard::CodeObjects::Plan] The puppet-strings plan documentation.
|
564
|
+
# @return [Boolean]
|
565
|
+
#
|
566
|
+
private def private_plan?(plan)
|
567
|
+
if plan.tag(:private)
|
568
|
+
value = plan.tag(:private).text
|
569
|
+
api_value = value.downcase == 'true' ? 'private' : 'public'
|
570
|
+
|
571
|
+
Bolt::Logger.deprecate(
|
572
|
+
'plan_private_tag',
|
573
|
+
"Tag '@private #{value}' in plan '#{plan.name}' is deprecated, use '@api #{api_value}' instead"
|
574
|
+
)
|
575
|
+
|
576
|
+
unless %w[true false].include?(plan.tag(:private).text.downcase)
|
577
|
+
msg = "Value for '@private' tag in plan '#{plan.name}' must be a boolean, received: #{value}"
|
578
|
+
raise Bolt::Error.new(msg, 'bolt/invalid-plan')
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
plan.tag(:api).text == 'private' || plan.tag(:private)&.text&.downcase == 'true'
|
583
|
+
end
|
584
|
+
|
565
585
|
def get_plan_mtime(path)
|
566
586
|
# If the plan is from the project modules/ directory, or is in the
|
567
587
|
# project itself, include the last mtime of the file so we can compare
|
@@ -635,6 +655,36 @@ module Bolt
|
|
635
655
|
end
|
636
656
|
end
|
637
657
|
|
658
|
+
# Return information about a module.
|
659
|
+
#
|
660
|
+
# @param name [String] The name of the module.
|
661
|
+
# @return [Hash]
|
662
|
+
#
|
663
|
+
def show_module(name)
|
664
|
+
name = name.tr('-', '/')
|
665
|
+
|
666
|
+
data = in_bolt_compiler do |_compiler|
|
667
|
+
mod = Puppet.lookup(:current_environment).module(name.split(%r{[/-]}, 2).last)
|
668
|
+
|
669
|
+
unless mod && (mod.forge_name == name || mod.name == name)
|
670
|
+
raise Bolt::Error.new("Could not find module '#{name}' on the modulepath.", 'bolt/unknown-module')
|
671
|
+
end
|
672
|
+
|
673
|
+
{
|
674
|
+
name: mod.forge_name || mod.name,
|
675
|
+
metadata: mod.metadata,
|
676
|
+
path: mod.path,
|
677
|
+
plans: mod.plans.map(&:name).sort,
|
678
|
+
tasks: mod.tasks.map(&:name).sort
|
679
|
+
}
|
680
|
+
end
|
681
|
+
|
682
|
+
data[:plans] = list_plans_with_cache.to_h.slice(*data[:plans]).to_a
|
683
|
+
data[:tasks] = list_tasks_with_cache.to_h.slice(*data[:tasks]).to_a
|
684
|
+
|
685
|
+
data
|
686
|
+
end
|
687
|
+
|
638
688
|
def generate_types(cache: false)
|
639
689
|
require 'puppet/face/generate'
|
640
690
|
in_bolt_compiler do
|
@@ -683,7 +733,10 @@ module Bolt
|
|
683
733
|
# Create a Fiber for the main plan. This will be run along with any
|
684
734
|
# other Fibers created during the plan run in the round_robin, with the
|
685
735
|
# main plan always taking precedence in being resumed.
|
686
|
-
|
736
|
+
#
|
737
|
+
# Every future except for the main plan needs to have a plan id in
|
738
|
+
# order to be tracked for the `wait()` function with no arguments.
|
739
|
+
future = executor.create_future(name: plan_name, plan_id: 1) do |_scope|
|
687
740
|
r = compiler.call_function('run_plan', plan_name, params.merge('_bolt_api_call' => true))
|
688
741
|
Bolt::PlanResult.from_pcore(r, 'success')
|
689
742
|
rescue Bolt::Error => e
|
@@ -13,6 +13,7 @@ module Bolt
|
|
13
13
|
download
|
14
14
|
eval
|
15
15
|
message
|
16
|
+
verbose
|
16
17
|
plan
|
17
18
|
resources
|
18
19
|
script
|
@@ -219,3 +220,4 @@ require 'bolt/pal/yaml_plan/step/task'
|
|
219
220
|
require 'bolt/pal/yaml_plan/step/upload'
|
220
221
|
require 'bolt/pal/yaml_plan/step/download'
|
221
222
|
require 'bolt/pal/yaml_plan/step/message'
|
223
|
+
require 'bolt/pal/yaml_plan/step/verbose'
|
@@ -24,14 +24,6 @@ module Bolt
|
|
24
24
|
private def function
|
25
25
|
'out::message'
|
26
26
|
end
|
27
|
-
|
28
|
-
# Transpiles the step into the plan language
|
29
|
-
#
|
30
|
-
def transpile
|
31
|
-
code = String.new(" ")
|
32
|
-
code << function_call(function, format_args(body))
|
33
|
-
code << "\n"
|
34
|
-
end
|
35
27
|
end
|
36
28
|
end
|
37
29
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class PAL
|
5
|
+
class YamlPlan
|
6
|
+
class Step
|
7
|
+
class Verbose < Step
|
8
|
+
def self.allowed_keys
|
9
|
+
super + Set['verbose']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_keys
|
13
|
+
Set['verbose']
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns an array of arguments to pass to the step's function call
|
17
|
+
#
|
18
|
+
private def format_args(body)
|
19
|
+
[body['verbose']]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the function corresponding to the step
|
23
|
+
#
|
24
|
+
private def function
|
25
|
+
'out::verbose'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -30,7 +30,7 @@ module Bolt
|
|
30
30
|
plan_string = String.new('')
|
31
31
|
plan_string << "# #{plan_object.description}\n" if plan_object.description
|
32
32
|
plan_string << "# WARNING: This is an autogenerated plan. It might not behave as expected.\n"
|
33
|
-
plan_string << "# @
|
33
|
+
plan_string << "# @api #{plan_object.private ? 'private' : 'public'}\n" unless plan_object.private.nil?
|
34
34
|
plan_string << "#{param_descriptions}\n" unless param_descriptions.empty?
|
35
35
|
|
36
36
|
plan_string << "plan #{plan_object.name}("
|
data/lib/bolt/plan_creator.rb
CHANGED
@@ -51,7 +51,7 @@ module Bolt
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def self.create_plan(plans_path, plan_name,
|
54
|
+
def self.create_plan(plans_path, plan_name, is_puppet)
|
55
55
|
_, name_segments, basename = segment_plan_name(plan_name)
|
56
56
|
dir_path = plans_path.join(*name_segments)
|
57
57
|
|
@@ -77,25 +77,7 @@ module Bolt
|
|
77
77
|
)
|
78
78
|
end
|
79
79
|
|
80
|
-
|
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
|
80
|
+
{ name: plan_name, path: plan_path }
|
99
81
|
end
|
100
82
|
|
101
83
|
def self.segment_plan_name(plan_name)
|
data/lib/bolt/plan_future.rb
CHANGED
@@ -4,14 +4,34 @@ require 'fiber'
|
|
4
4
|
|
5
5
|
module Bolt
|
6
6
|
class PlanFuture
|
7
|
-
attr_reader :fiber, :id
|
8
|
-
attr_accessor :value
|
7
|
+
attr_reader :fiber, :id, :scope
|
8
|
+
attr_accessor :value, :plan_stack
|
9
9
|
|
10
|
-
def initialize(fiber, id, name
|
10
|
+
def initialize(fiber, id, plan_id:, name: nil, scope: nil)
|
11
11
|
@fiber = fiber
|
12
12
|
@id = id
|
13
13
|
@name = name
|
14
14
|
@value = nil
|
15
|
+
|
16
|
+
# Default to Puppet's current global_scope, otherwise things will
|
17
|
+
# blow up when the Fiber Executor tries to override the global_scope.
|
18
|
+
@scope = scope || Puppet.lookup(:global_scope) { nil }
|
19
|
+
|
20
|
+
# The plan invocation ID when the Future is created may be
|
21
|
+
# different from the plan ID of the Future when we switch to it if a new
|
22
|
+
# plan was run inside the Future, so keep track of the plans that a
|
23
|
+
# Future is executing in as a stack. When one plan finishes, pop it off
|
24
|
+
# since now we're in the calling plan. These IDs are unique to each plan
|
25
|
+
# invocation, not just plan names.
|
26
|
+
@plan_stack = [plan_id]
|
27
|
+
end
|
28
|
+
|
29
|
+
def original_plan
|
30
|
+
@plan_stack.last
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_plan
|
34
|
+
@plan_stack.first
|
15
35
|
end
|
16
36
|
|
17
37
|
def name
|