bolt 3.14.1 → 3.17.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/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +137 -104
- data/guides/debugging.yaml +27 -0
- data/guides/inventory.yaml +23 -0
- data/guides/links.yaml +12 -0
- data/guides/logging.yaml +17 -0
- data/guides/module.yaml +18 -0
- data/guides/modulepath.yaml +24 -0
- data/guides/project.yaml +21 -0
- data/guides/targets.yaml +28 -0
- data/guides/transports.yaml +22 -0
- data/lib/bolt/analytics.rb +2 -19
- data/lib/bolt/application.rb +634 -0
- data/lib/bolt/bolt_option_parser.rb +28 -4
- data/lib/bolt/cli.rb +592 -788
- data/lib/bolt/fiber_executor.rb +7 -3
- data/lib/bolt/inventory/inventory.rb +68 -39
- data/lib/bolt/inventory.rb +2 -9
- data/lib/bolt/module_installer/puppetfile.rb +24 -10
- data/lib/bolt/outputter/human.rb +83 -32
- data/lib/bolt/outputter/json.rb +63 -38
- data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -1
- data/lib/bolt/pal.rb +31 -11
- data/lib/bolt/plan_creator.rb +84 -25
- data/lib/bolt/plan_future.rb +11 -6
- data/lib/bolt/plan_result.rb +1 -1
- data/lib/bolt/plugin/task.rb +1 -1
- data/lib/bolt/plugin.rb +11 -17
- 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/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/transport_app.rb +113 -24
- data/lib/bolt_spec/bolt_context.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +14 -3
- data/lib/bolt/secret.rb +0 -37
data/lib/bolt/outputter/json.rb
CHANGED
@@ -49,7 +49,11 @@ module Bolt
|
|
49
49
|
alias print_module_list print_table
|
50
50
|
alias print_module_info print_table
|
51
51
|
|
52
|
-
|
52
|
+
# Print information about a task.
|
53
|
+
#
|
54
|
+
# @param task [Bolt::Task] The task information.
|
55
|
+
#
|
56
|
+
def print_task_info(task:)
|
53
57
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
54
58
|
module_dir = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
55
59
|
"built-in module"
|
@@ -59,11 +63,16 @@ module Bolt
|
|
59
63
|
@stream.puts task.to_h.merge(module_dir: module_dir).to_json
|
60
64
|
end
|
61
65
|
|
62
|
-
|
63
|
-
|
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)
|
64
73
|
end
|
65
74
|
|
66
|
-
def print_plugin_list(plugins
|
75
|
+
def print_plugin_list(plugins:, modulepath:)
|
67
76
|
plugins.delete(:validate_resolve_reference)
|
68
77
|
print_table('plugins' => plugins, 'modulepath' => modulepath)
|
69
78
|
end
|
@@ -78,11 +87,15 @@ module Bolt
|
|
78
87
|
@stream.puts plan.to_json
|
79
88
|
end
|
80
89
|
|
81
|
-
def print_plans(
|
82
|
-
print_table(
|
90
|
+
def print_plans(**kwargs)
|
91
|
+
print_table(**kwargs)
|
83
92
|
end
|
84
93
|
|
85
|
-
def
|
94
|
+
def print_new_plan(**kwargs)
|
95
|
+
print_table(**kwargs)
|
96
|
+
end
|
97
|
+
|
98
|
+
def print_apply_result(apply_result)
|
86
99
|
@stream.puts apply_result.to_json
|
87
100
|
end
|
88
101
|
|
@@ -95,10 +108,19 @@ module Bolt
|
|
95
108
|
@stream.puts result_set.to_json
|
96
109
|
end
|
97
110
|
|
98
|
-
|
99
|
-
|
111
|
+
# Print available guide topics.
|
112
|
+
#
|
113
|
+
# @param topics [Array] The available topics.
|
114
|
+
#
|
115
|
+
def print_topics(**kwargs)
|
116
|
+
print_table(kwargs)
|
100
117
|
end
|
101
118
|
|
119
|
+
# Print the guide for the specified topic.
|
120
|
+
#
|
121
|
+
# @param guide [String] The guide.
|
122
|
+
# @param topic [String] The topic.
|
123
|
+
#
|
102
124
|
def print_guide(**kwargs)
|
103
125
|
@stream.puts(kwargs.to_json)
|
104
126
|
end
|
@@ -113,35 +135,38 @@ module Bolt
|
|
113
135
|
moduledir: moduledir.to_s }.to_json)
|
114
136
|
end
|
115
137
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
)
|
139
|
-
end
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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)
|
145
170
|
end
|
146
171
|
|
147
172
|
def fatal_error(err)
|
@@ -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/pal.rb
CHANGED
@@ -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
|
data/lib/bolt/plan_creator.rb
CHANGED
@@ -7,7 +7,7 @@ require 'bolt/util'
|
|
7
7
|
|
8
8
|
module Bolt
|
9
9
|
module PlanCreator
|
10
|
-
def self.
|
10
|
+
def self.validate_plan_name(project, plan_name)
|
11
11
|
if project.name.nil?
|
12
12
|
raise Bolt::Error.new(
|
13
13
|
"Project directory '#{project.path}' is not a named project. Unable to create "\
|
@@ -51,7 +51,15 @@ module Bolt
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
|
54
|
+
# Create a new plan from the plan templates based on which language the
|
55
|
+
# user configured, and whether the plan wraps a script.
|
56
|
+
#
|
57
|
+
# @param plans_path [string] The path to the new plan
|
58
|
+
# @param plan_name [string] The name of the new plan
|
59
|
+
# @param is_puppet [boolean] Whether to create a Puppet language plan
|
60
|
+
# @param script [string] A reference to a script for the new plan to run
|
61
|
+
#
|
62
|
+
def self.create_plan(plans_path, plan_name, is_puppet: false, script: nil)
|
55
63
|
_, name_segments, basename = segment_plan_name(plan_name)
|
56
64
|
dir_path = plans_path.join(*name_segments)
|
57
65
|
|
@@ -66,8 +74,15 @@ module Bolt
|
|
66
74
|
|
67
75
|
type = is_puppet ? 'pp' : 'yaml'
|
68
76
|
plan_path = dir_path + "#{basename}.#{type}"
|
69
|
-
plan_template = is_puppet
|
70
|
-
|
77
|
+
plan_template = if is_puppet && script
|
78
|
+
puppet_script_plan(plan_name, script)
|
79
|
+
elsif is_puppet
|
80
|
+
puppet_plan(plan_name)
|
81
|
+
elsif script
|
82
|
+
yaml_script_plan(script)
|
83
|
+
else
|
84
|
+
yaml_plan(plan_name)
|
85
|
+
end
|
71
86
|
begin
|
72
87
|
File.write(plan_path, plan_template)
|
73
88
|
rescue Errno::EACCES => e
|
@@ -77,25 +92,7 @@ module Bolt
|
|
77
92
|
)
|
78
93
|
end
|
79
94
|
|
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
|
95
|
+
{ name: plan_name, path: plan_path }
|
99
96
|
end
|
100
97
|
|
101
98
|
def self.segment_plan_name(plan_name)
|
@@ -108,7 +105,11 @@ module Bolt
|
|
108
105
|
[prefix, name_segments, basename]
|
109
106
|
end
|
110
107
|
|
111
|
-
|
108
|
+
# Template for a new simple YAML plan.
|
109
|
+
#
|
110
|
+
# @param plan_name [string] The name of the new plan
|
111
|
+
#
|
112
|
+
private_class_method def self.yaml_plan(plan_name)
|
112
113
|
<<~YAML
|
113
114
|
# This is the structure of a simple plan. To learn more about writing
|
114
115
|
# YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
|
@@ -137,7 +138,42 @@ module Bolt
|
|
137
138
|
YAML
|
138
139
|
end
|
139
140
|
|
140
|
-
|
141
|
+
# Template for a new YAML plan that runs a script.
|
142
|
+
#
|
143
|
+
# @param script [string] A reference to the script to run.
|
144
|
+
#
|
145
|
+
private_class_method def self.yaml_script_plan(script)
|
146
|
+
<<~YAML
|
147
|
+
# This is the structure of a simple plan. To learn more about writing
|
148
|
+
# YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
|
149
|
+
|
150
|
+
# The description sets the description of the plan that will appear
|
151
|
+
# in 'bolt plan show' output.
|
152
|
+
description: A plan created with bolt plan new
|
153
|
+
|
154
|
+
# The parameters key defines the parameters that can be passed to
|
155
|
+
# the plan.
|
156
|
+
parameters:
|
157
|
+
targets:
|
158
|
+
type: TargetSpec
|
159
|
+
description: A list of targets to run actions on
|
160
|
+
|
161
|
+
# The steps key defines the actions the plan will take in order.
|
162
|
+
steps:
|
163
|
+
- name: run_script
|
164
|
+
script: #{script}
|
165
|
+
targets: $targets
|
166
|
+
|
167
|
+
# The return key sets the return value of the plan.
|
168
|
+
return: $run_script
|
169
|
+
YAML
|
170
|
+
end
|
171
|
+
|
172
|
+
# Template for a new simple Puppet plan.
|
173
|
+
#
|
174
|
+
# @param plan_name [string] The name of the new plan
|
175
|
+
#
|
176
|
+
private_class_method def self.puppet_plan(plan_name)
|
141
177
|
<<~PUPPET
|
142
178
|
# This is the structure of a simple plan. To learn more about writing
|
143
179
|
# Puppet plans, see the documentation: http://pup.pt/bolt-puppet-plans
|
@@ -156,5 +192,28 @@ module Bolt
|
|
156
192
|
}
|
157
193
|
PUPPET
|
158
194
|
end
|
195
|
+
|
196
|
+
# Template for a new Puppet plan that only runs a script.
|
197
|
+
#
|
198
|
+
# @param plan_name [string] The name of the new plan
|
199
|
+
# @param script [string] A reference to the script to run
|
200
|
+
#
|
201
|
+
private_class_method def self.puppet_script_plan(plan_name, script)
|
202
|
+
<<~PUPPET
|
203
|
+
# This is the structure of a simple plan. To learn more about writing
|
204
|
+
# Puppet plans, see the documentation: http://pup.pt/bolt-puppet-plans
|
205
|
+
|
206
|
+
# The summary sets the description of the plan that will appear
|
207
|
+
# in 'bolt plan show' output. Bolt uses puppet-strings to parse the
|
208
|
+
# summary and parameters from the plan.
|
209
|
+
# @summary A plan created with bolt plan new.
|
210
|
+
# @param targets The targets to run on.
|
211
|
+
plan #{plan_name} (
|
212
|
+
TargetSpec $targets
|
213
|
+
) {
|
214
|
+
return run_script('#{script}', $targets)
|
215
|
+
}
|
216
|
+
PUPPET
|
217
|
+
end
|
159
218
|
end
|
160
219
|
end
|
data/lib/bolt/plan_future.rb
CHANGED
@@ -4,14 +4,19 @@ require 'fiber'
|
|
4
4
|
|
5
5
|
module Bolt
|
6
6
|
class PlanFuture
|
7
|
-
attr_reader :fiber, :id
|
7
|
+
attr_reader :fiber, :id, :scope
|
8
8
|
attr_accessor :value, :plan_stack
|
9
9
|
|
10
|
-
def initialize(fiber, id, plan_id:, name: nil)
|
11
|
-
@fiber
|
12
|
-
@id
|
13
|
-
@name
|
14
|
-
@value
|
10
|
+
def initialize(fiber, id, plan_id:, name: nil, scope: nil)
|
11
|
+
@fiber = fiber
|
12
|
+
@id = id
|
13
|
+
@name = name
|
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
|
+
|
15
20
|
# The plan invocation ID when the Future is created may be
|
16
21
|
# different from the plan ID of the Future when we switch to it if a new
|
17
22
|
# plan was run inside the Future, so keep track of the plans that a
|
data/lib/bolt/plan_result.rb
CHANGED
data/lib/bolt/plugin/task.rb
CHANGED
@@ -59,7 +59,7 @@ module Bolt
|
|
59
59
|
run_opts = {}
|
60
60
|
run_opts[:run_as] = opts['_run_as'] if opts['_run_as']
|
61
61
|
begin
|
62
|
-
task =
|
62
|
+
task = @context.get_validated_task(opts['task'], params)
|
63
63
|
rescue Bolt::Error => e
|
64
64
|
raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, name, 'puppet_library')
|
65
65
|
end
|
data/lib/bolt/plugin.rb
CHANGED
@@ -127,29 +127,15 @@ module Bolt
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new, **opts)
|
131
|
-
plugins = new(config, pal, analytics, **opts)
|
132
|
-
|
133
|
-
config.plugins.each_key do |plugin|
|
134
|
-
plugins.by_name(plugin)
|
135
|
-
end
|
136
|
-
|
137
|
-
plugins.plugin_hooks.merge!(plugins.resolve_references(config.plugin_hooks))
|
138
|
-
|
139
|
-
plugins
|
140
|
-
end
|
141
|
-
|
142
130
|
RUBY_PLUGINS = %w[task prompt env_var puppetdb puppet_connect_data].freeze
|
143
131
|
BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory
|
144
132
|
yaml env_var gcloud_inventory].freeze
|
145
133
|
DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
|
146
134
|
|
147
135
|
attr_reader :pal, :plugin_context
|
148
|
-
|
136
|
+
attr_writer :plugin_hooks
|
149
137
|
|
150
|
-
|
151
|
-
|
152
|
-
def initialize(config, pal, analytics, load_plugins: true)
|
138
|
+
def initialize(config, pal, analytics = Bolt::Analytics::NoopClient.new, load_plugins: true)
|
153
139
|
@config = config
|
154
140
|
@analytics = analytics
|
155
141
|
@plugin_context = PluginContext.new(config, pal, self)
|
@@ -166,7 +152,15 @@ module Bolt
|
|
166
152
|
raise Bolt::Error.new(msg, 'bolt/plugin-error')
|
167
153
|
end
|
168
154
|
@unresolved_plugin_configs['puppetdb'] = config.puppetdb if config.puppetdb
|
169
|
-
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns a map of configured plugin hooks. Any unresolved plugin references
|
158
|
+
# are resolved.
|
159
|
+
#
|
160
|
+
# @return [Hash[String, Hash]]
|
161
|
+
#
|
162
|
+
def plugin_hooks
|
163
|
+
@plugin_hooks ||= DEFAULT_PLUGIN_HOOKS.merge(resolve_references(@config.plugin_hooks))
|
170
164
|
end
|
171
165
|
|
172
166
|
def modules
|