bolt 1.23.0 → 1.24.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/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +26 -0
- data/lib/bolt/bolt_option_parser.rb +207 -85
- data/lib/bolt/cli.rb +15 -10
- data/lib/bolt/config.rb +5 -1
- data/lib/bolt/inventory/group2.rb +4 -1
- data/lib/bolt/inventory/inventory2.rb +2 -1
- data/lib/bolt/plugin.rb +3 -1
- data/lib/bolt/plugin/pkcs7.rb +100 -0
- data/lib/bolt/plugin/prompt.rb +9 -9
- data/lib/bolt/secret.rb +19 -0
- data/lib/bolt/secret/base.rb +41 -0
- data/lib/bolt/version.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc4db252a26ca2d1f05bddfa9b4affaece04baf3d2324e37b29e7f3579301640
|
4
|
+
data.tar.gz: 963c29e99b6389113a6ed9e8bc1bbe5209d5b7f9a1d0492f0d56af5a88d25f17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11d6586b4ee6b03471749c629fc1ac2c342025b707b5bf443044f7f01ee930d1ab1c678655e827452c800df149e998d0b256e4f11e5bae194cd88eba6eb5847b
|
7
|
+
data.tar.gz: 9775e66c5a65810e72e129787976f98d7725f64a293ccf80237372c90b8db149ca94d991afbc0779d26dab228e3c9a72ac884ad32b1d180c33a03582c57aeab0
|
@@ -6,6 +6,7 @@ require 'bolt/error'
|
|
6
6
|
#
|
7
7
|
# **NOTE:** Not available in apply block
|
8
8
|
Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction) do
|
9
|
+
# Run a plan
|
9
10
|
# @param plan_name The plan to run.
|
10
11
|
# @param named_args Arguments to the plan. Can also include additional options: '_catch_errors', '_run_as'.
|
11
12
|
# @return [PlanResult] The result of running the plan. Undef if plan does not explicitly return results.
|
@@ -18,6 +19,31 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
18
19
|
return_type 'Boltlib::PlanResult'
|
19
20
|
end
|
20
21
|
|
22
|
+
# Run a plan, specifying $nodes as a positional argument.
|
23
|
+
# @param plan_name The plan to run.
|
24
|
+
# @param named_args Arguments to the plan. Can also include additional options: '_catch_errors', '_run_as'.
|
25
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
26
|
+
# @return [PlanResult] The result of running the plan. Undef if plan does not explicitly return results.
|
27
|
+
# @example Run a plan
|
28
|
+
# run_plan('canary', $nodes, 'command' => 'false')
|
29
|
+
dispatch :run_plan_with_targetspec do
|
30
|
+
scope_param
|
31
|
+
param 'String', :plan_name
|
32
|
+
param 'Boltlib::TargetSpec', :targets
|
33
|
+
optional_param 'Hash', :named_args
|
34
|
+
return_type 'Boltlib::PlanResult'
|
35
|
+
end
|
36
|
+
|
37
|
+
def run_plan_with_targetspec(scope, plan_name, targets, named_args = {})
|
38
|
+
unless named_args['nodes'].nil?
|
39
|
+
raise ArgumentError,
|
40
|
+
"A plan's 'nodes' parameter may be specified as the second positional argument to " \
|
41
|
+
"run_plan(), but in that case 'nodes' must not be specified in the named arguments " \
|
42
|
+
"hash."
|
43
|
+
end
|
44
|
+
run_plan(scope, plan_name, named_args.merge('nodes' => targets))
|
45
|
+
end
|
46
|
+
|
21
47
|
def run_plan(scope, plan_name, named_args = {})
|
22
48
|
unless Puppet[:tasks]
|
23
49
|
raise Puppet::ParseErrorWithIssue
|
@@ -6,6 +6,79 @@ require 'optparse'
|
|
6
6
|
|
7
7
|
module Bolt
|
8
8
|
class BoltOptionParser < OptionParser
|
9
|
+
OPTIONS = { inventory: %w[nodes targets query rerun description],
|
10
|
+
authentication: %w[user password private-key host-key-check ssl ssl-verify],
|
11
|
+
escalation: %w[run-as sudo-password],
|
12
|
+
run_context: %w[concurrency inventoryfile save-rerun],
|
13
|
+
global_config_setters: %w[modulepath boltdir configfile],
|
14
|
+
transports: %w[transport connect-timeout tty],
|
15
|
+
display: %w[format color verbose trace],
|
16
|
+
global: %w[help version debug] }.freeze
|
17
|
+
|
18
|
+
ACTION_OPTS = OPTIONS.values.flatten.freeze
|
19
|
+
|
20
|
+
def get_help_text(subcommand, action = nil)
|
21
|
+
case subcommand
|
22
|
+
when 'apply'
|
23
|
+
{ flags: ACTION_OPTS + %w[noop execute compile-concurrency],
|
24
|
+
banner: APPLY_HELP }
|
25
|
+
when 'command'
|
26
|
+
{ flags: ACTION_OPTS,
|
27
|
+
banner: COMMAND_HELP }
|
28
|
+
when 'file'
|
29
|
+
{ flags: ACTION_OPTS + %w[tmpdir],
|
30
|
+
banner: FILE_HELP }
|
31
|
+
when 'plan'
|
32
|
+
case action
|
33
|
+
when 'convert'
|
34
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
35
|
+
banner: PLAN_CONVERT_HELP }
|
36
|
+
when 'show'
|
37
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
38
|
+
banner: PLAN_SHOW_HELP }
|
39
|
+
when 'run'
|
40
|
+
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir],
|
41
|
+
banner: PLAN_RUN_HELP }
|
42
|
+
else
|
43
|
+
{ flags: ACTION_OPTS + %w[params compile-concurrency tmpdir],
|
44
|
+
banner: PLAN_HELP }
|
45
|
+
end
|
46
|
+
when 'puppetfile'
|
47
|
+
case action
|
48
|
+
when 'install'
|
49
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
50
|
+
banner: PUPPETFILE_INSTALL_HELP }
|
51
|
+
when 'show-modules'
|
52
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
53
|
+
banner: PUPPETFILE_SHOWMODULES_HELP }
|
54
|
+
else
|
55
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
56
|
+
banner: PUPPETFILE_HELP }
|
57
|
+
end
|
58
|
+
when 'script'
|
59
|
+
{ flags: ACTION_OPTS + %w[tmpdir],
|
60
|
+
banner: SCRIPT_HELP }
|
61
|
+
when 'secret'
|
62
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
63
|
+
banner: SECRET_HELP }
|
64
|
+
when 'task'
|
65
|
+
case action
|
66
|
+
when 'show'
|
67
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
68
|
+
banner: TASK_SHOW_HELP }
|
69
|
+
when 'run'
|
70
|
+
{ flags: ACTION_OPTS + %w[params tmpdir],
|
71
|
+
banner: TASK_RUN_HELP }
|
72
|
+
else
|
73
|
+
{ flags: ACTION_OPTS + %w[params tmpdir],
|
74
|
+
banner: TASK_HELP }
|
75
|
+
end
|
76
|
+
else
|
77
|
+
{ flags: OPTIONS[:global],
|
78
|
+
banner: BANNER }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
9
82
|
def self.examples(cmd, desc)
|
10
83
|
<<-EXAMP
|
11
84
|
#{desc} a Windows host via WinRM, providing for the password
|
@@ -18,7 +91,7 @@ EXAMP
|
|
18
91
|
end
|
19
92
|
|
20
93
|
BANNER = <<-HELP
|
21
|
-
Usage: bolt <subcommand> <action>
|
94
|
+
Usage: bolt <subcommand> <action>
|
22
95
|
|
23
96
|
Available subcommands:
|
24
97
|
bolt command run <command> Run a command remotely
|
@@ -34,19 +107,41 @@ Available subcommands:
|
|
34
107
|
bolt apply <manifest> Apply Puppet manifest code
|
35
108
|
bolt puppetfile install Install modules from a Puppetfile into a Boltdir
|
36
109
|
bolt puppetfile show-modules List modules available to Bolt
|
110
|
+
bolt secret createkeys Create new encryption keys
|
111
|
+
bolt secret encrypt <plaintext> Encrypt a value
|
112
|
+
bolt secret decrypt <encrypted> Decrypt a value
|
37
113
|
|
38
114
|
Run `bolt <subcommand> --help` to view specific examples.
|
39
115
|
|
40
|
-
|
116
|
+
Available options are:
|
41
117
|
HELP
|
42
118
|
|
43
119
|
TASK_HELP = <<-HELP
|
44
|
-
Usage: bolt task <action> <task> [
|
120
|
+
Usage: bolt task <action> <task> [parameters]
|
45
121
|
|
46
122
|
Available actions are:
|
47
123
|
show Show list of available tasks
|
48
124
|
show <task> Show documentation for task
|
49
|
-
run
|
125
|
+
run <task> Run a Puppet task
|
126
|
+
|
127
|
+
Parameters are of the form <parameter>=<value>.
|
128
|
+
|
129
|
+
#{examples('task run facts', 'run facter on')}
|
130
|
+
Available options are:
|
131
|
+
HELP
|
132
|
+
|
133
|
+
TASK_SHOW_HELP = <<-HELP
|
134
|
+
Usage: bolt task show <task>
|
135
|
+
|
136
|
+
Available actions are:
|
137
|
+
show Show list of available tasks
|
138
|
+
show <task> Show documentation for task
|
139
|
+
|
140
|
+
Available options are:
|
141
|
+
HELP
|
142
|
+
|
143
|
+
TASK_RUN_HELP = <<-HELP
|
144
|
+
Usage: bolt task run <task> [parameters]
|
50
145
|
|
51
146
|
Parameters are of the form <parameter>=<value>.
|
52
147
|
|
@@ -55,7 +150,7 @@ Available options are:
|
|
55
150
|
HELP
|
56
151
|
|
57
152
|
COMMAND_HELP = <<-HELP
|
58
|
-
Usage: bolt command <action> <command>
|
153
|
+
Usage: bolt command <action> <command>
|
59
154
|
|
60
155
|
Available actions are:
|
61
156
|
run Run a command remotely
|
@@ -65,7 +160,7 @@ Available options are:
|
|
65
160
|
HELP
|
66
161
|
|
67
162
|
SCRIPT_HELP = <<-HELP
|
68
|
-
Usage: bolt script <action> <script> [[arg1] ... [argN]]
|
163
|
+
Usage: bolt script <action> <script> [[arg1] ... [argN]]
|
69
164
|
|
70
165
|
Available actions are:
|
71
166
|
run Upload a local script and run it remotely
|
@@ -75,7 +170,7 @@ Available options are:
|
|
75
170
|
HELP
|
76
171
|
|
77
172
|
PLAN_HELP = <<-HELP
|
78
|
-
Usage: bolt plan <action> <plan> [
|
173
|
+
Usage: bolt plan <action> <plan> [parameters]
|
79
174
|
|
80
175
|
Available actions are:
|
81
176
|
convert <plan_path> Convert a YAML plan to a Puppet plan
|
@@ -85,12 +180,37 @@ Available actions are:
|
|
85
180
|
|
86
181
|
Parameters are of the form <parameter>=<value>.
|
87
182
|
|
183
|
+
#{examples('plan run canary command=hostname', 'run the canary plan on')}
|
184
|
+
Available options are:
|
185
|
+
HELP
|
186
|
+
|
187
|
+
PLAN_CONVERT_HELP = <<-HELP
|
188
|
+
Usage: bolt plan convert <plan_path>
|
189
|
+
|
190
|
+
Available options are:
|
191
|
+
HELP
|
192
|
+
|
193
|
+
PLAN_SHOW_HELP = <<-HELP
|
194
|
+
Usage: bolt plan show <plan>
|
195
|
+
|
196
|
+
Available actions are:
|
197
|
+
show Show list of available plans
|
198
|
+
show <plan> Show details for plan
|
199
|
+
|
200
|
+
Available options are:
|
201
|
+
HELP
|
202
|
+
|
203
|
+
PLAN_RUN_HELP = <<-HELP
|
204
|
+
Usage: bolt plan run <plan> [parameters]
|
205
|
+
|
206
|
+
Parameters are of the form <parameter>=<value>.
|
207
|
+
|
88
208
|
#{examples('plan run canary command=hostname', 'run the canary plan on')}
|
89
209
|
Available options are:
|
90
210
|
HELP
|
91
211
|
|
92
212
|
FILE_HELP = <<-HELP
|
93
|
-
Usage: bolt file <action>
|
213
|
+
Usage: bolt file <action>
|
94
214
|
|
95
215
|
Available actions are:
|
96
216
|
upload <src> <dest> Upload local file or directory <src> to <dest> on each node
|
@@ -100,7 +220,7 @@ Available options are:
|
|
100
220
|
HELP
|
101
221
|
|
102
222
|
PUPPETFILE_HELP = <<-HELP
|
103
|
-
Usage: bolt puppetfile <action>
|
223
|
+
Usage: bolt puppetfile <action>
|
104
224
|
|
105
225
|
Available actions are:
|
106
226
|
install Install modules from a Puppetfile into a Boltdir
|
@@ -109,60 +229,75 @@ Available actions are:
|
|
109
229
|
Install modules into the local Boltdir
|
110
230
|
bolt puppetfile install
|
111
231
|
|
232
|
+
Available options are:
|
233
|
+
HELP
|
234
|
+
|
235
|
+
PUPPETFILE_INSTALL_HELP = <<-HELP
|
236
|
+
Usage: bolt puppetfile install
|
237
|
+
|
238
|
+
Install modules into the local Boltdir
|
239
|
+
bolt puppetfile install
|
240
|
+
|
241
|
+
Available options are:
|
242
|
+
HELP
|
243
|
+
|
244
|
+
PUPPETFILE_SHOWMODULES_HELP = <<-HELP
|
245
|
+
Usage: bolt puppetfile show-modules
|
246
|
+
|
112
247
|
Available options are:
|
113
248
|
HELP
|
114
249
|
|
115
250
|
APPLY_HELP = <<-HELP
|
116
|
-
Usage: bolt apply <manifest.pp>
|
251
|
+
Usage: bolt apply <manifest.pp>
|
117
252
|
|
118
253
|
#{examples('apply site.pp', 'apply a manifest on')}
|
119
254
|
bolt apply site.pp --nodes foo.example.com,bar.example.com
|
255
|
+
|
256
|
+
Available options are:
|
120
257
|
HELP
|
121
258
|
|
122
|
-
|
123
|
-
|
124
|
-
# the OptionParser#help method on demand.
|
125
|
-
module SwitchHider
|
126
|
-
attr_accessor :hide
|
259
|
+
SECRET_HELP = <<~SECRET_HELP
|
260
|
+
Manage secrets for inventory and hiera data.
|
127
261
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
262
|
+
Available actions are:
|
263
|
+
createkeys Create new encryption keys
|
264
|
+
encrypt Encrypt a value
|
265
|
+
decrypt Decrypt a value
|
266
|
+
Available options are:
|
267
|
+
SECRET_HELP
|
133
268
|
|
134
269
|
def initialize(options)
|
135
270
|
super()
|
136
271
|
|
137
272
|
@options = options
|
138
273
|
|
139
|
-
|
140
|
-
|
274
|
+
define('-n', '--nodes NODES',
|
275
|
+
'Alias for --targets') do |nodes|
|
141
276
|
@options [:nodes] ||= []
|
142
277
|
@options[:nodes] << get_arg_input(nodes)
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
278
|
+
end
|
279
|
+
define('-t', '--targets TARGETS',
|
280
|
+
'Identifies the targets of command.',
|
281
|
+
'Enter a comma-separated list of target URIs or group names.',
|
282
|
+
"Or read a target list from an input file '@<file>' or stdin '-'.",
|
283
|
+
'Example: --targets localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com',
|
284
|
+
'URI format is [protocol://]host[:port]',
|
285
|
+
"SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}",
|
286
|
+
'For Windows targets, specify the winrm:// protocol if it has not be configured',
|
287
|
+
'For SSH, port defaults to `22`',
|
288
|
+
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets|
|
154
289
|
@options[:targets] ||= []
|
155
290
|
@options[:targets] << get_arg_input(targets)
|
156
|
-
end
|
157
|
-
|
291
|
+
end
|
292
|
+
define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
158
293
|
@options[:query] = query
|
159
|
-
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
294
|
+
end
|
295
|
+
define('--rerun FILTER', 'Retry on nodes from the last run',
|
296
|
+
"'all' all nodes that were part of the last run.",
|
297
|
+
"'failure' nodes that failed in the last run.",
|
298
|
+
"'success' nodes that succeeded in the last run.") do |rerun|
|
164
299
|
@options[:rerun] = rerun
|
165
|
-
end
|
300
|
+
end
|
166
301
|
define('--noop', 'Execute a task that supports it in noop mode') do |_|
|
167
302
|
@options[:noop] = true
|
168
303
|
end
|
@@ -174,12 +309,12 @@ Usage: bolt apply <manifest.pp> [options]
|
|
174
309
|
"Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'") do |params|
|
175
310
|
@options[:task_options] = parse_params(params)
|
176
311
|
end
|
177
|
-
|
178
|
-
|
312
|
+
define('-e', '--execute CODE',
|
313
|
+
"Puppet manifest code to apply to the targets") do |code|
|
179
314
|
@options[:code] = code
|
180
|
-
end
|
315
|
+
end
|
181
316
|
|
182
|
-
separator
|
317
|
+
separator "\nAuthentication:"
|
183
318
|
define('-u', '--user USER', 'User to authenticate as') do |user|
|
184
319
|
@options[:user] = user
|
185
320
|
end
|
@@ -206,7 +341,7 @@ Usage: bolt apply <manifest.pp> [options]
|
|
206
341
|
@options[:'ssl-verify'] = ssl_verify
|
207
342
|
end
|
208
343
|
|
209
|
-
separator
|
344
|
+
separator "\nEscalation:"
|
210
345
|
define('--run-as USER', 'User to run as using privilege escalation') do |user|
|
211
346
|
@options[:'run-as'] = user
|
212
347
|
end
|
@@ -221,7 +356,7 @@ Usage: bolt apply <manifest.pp> [options]
|
|
221
356
|
end
|
222
357
|
end
|
223
358
|
|
224
|
-
separator
|
359
|
+
separator "\nRun context:"
|
225
360
|
define('-c', '--concurrency CONCURRENCY', Integer,
|
226
361
|
'Maximum number of simultaneous connections (default: 100)') do |concurrency|
|
227
362
|
@options[:concurrency] = concurrency
|
@@ -256,7 +391,7 @@ Usage: bolt apply <manifest.pp> [options]
|
|
256
391
|
@options[:'save-rerun'] = save
|
257
392
|
end
|
258
393
|
|
259
|
-
separator
|
394
|
+
separator "\nTransports:"
|
260
395
|
define('--transport TRANSPORT', TRANSPORTS.keys.map(&:to_s),
|
261
396
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
262
397
|
@options[:transport] = t
|
@@ -271,65 +406,52 @@ Usage: bolt apply <manifest.pp> [options]
|
|
271
406
|
@options[:tmpdir] = tmpdir
|
272
407
|
end
|
273
408
|
|
274
|
-
separator
|
409
|
+
separator "\nDisplay:"
|
275
410
|
define('--format FORMAT', 'Output format to use: human or json') do |format|
|
276
411
|
@options[:format] = format
|
277
412
|
end
|
278
413
|
define('--[no-]color', 'Whether to show output in color') do |color|
|
279
414
|
@options[:color] = color
|
280
415
|
end
|
281
|
-
define('-h', '--help', 'Display help') do |_|
|
282
|
-
@options[:help] = true
|
283
|
-
end
|
284
416
|
define('-v', '--[no-]verbose', 'Display verbose logging') do |value|
|
285
417
|
@options[:verbose] = value
|
286
418
|
end
|
287
|
-
define('--debug', 'Display debug logging') do |_|
|
288
|
-
@options[:debug] = true
|
289
|
-
end
|
290
419
|
define('--trace', 'Display error stack traces') do |_|
|
291
420
|
@options[:trace] = true
|
292
421
|
end
|
422
|
+
|
423
|
+
separator "\nGlobal:"
|
424
|
+
define('-h', '--help', 'Display help') do |_|
|
425
|
+
@options[:help] = true
|
426
|
+
end
|
293
427
|
define('--version', 'Display the version') do |_|
|
294
428
|
puts Bolt::VERSION
|
295
429
|
raise Bolt::CLIExit
|
296
430
|
end
|
297
|
-
|
298
|
-
|
431
|
+
define('--debug', 'Display debug logging') do |_|
|
432
|
+
@options[:debug] = true
|
433
|
+
end
|
299
434
|
end
|
300
435
|
|
301
|
-
def
|
302
|
-
|
436
|
+
def remove_excluded_opts(option_list)
|
437
|
+
# Remove any options that are not available for the specified subcommand
|
438
|
+
top.list.delete_if do |opt|
|
439
|
+
opt.respond_to?(:switch_name) && !option_list.include?(opt.switch_name)
|
440
|
+
end
|
441
|
+
# Remove any separators if all options of that type have been removed
|
442
|
+
top.list.delete_if do |opt|
|
443
|
+
i = top.list.index(opt)
|
444
|
+
opt.is_a?(String) && top.list[i + 1].is_a?(String)
|
445
|
+
end
|
303
446
|
end
|
304
447
|
|
305
448
|
def update
|
306
|
-
|
307
|
-
hide_target_opts(false)
|
308
|
-
# Don't show the --execute switch except for `apply`
|
309
|
-
@execute.hide = true
|
310
|
-
|
449
|
+
help_text = get_help_text(@options[:subcommand], @options[:action])
|
311
450
|
# Update the banner according to the subcommand
|
312
|
-
self.banner =
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
COMMAND_HELP
|
317
|
-
when 'script'
|
318
|
-
SCRIPT_HELP
|
319
|
-
when 'task'
|
320
|
-
TASK_HELP
|
321
|
-
when 'file'
|
322
|
-
FILE_HELP
|
323
|
-
when 'puppetfile'
|
324
|
-
# Don't show targeting options for puppetfile
|
325
|
-
hide_target_opts
|
326
|
-
PUPPETFILE_HELP
|
327
|
-
when 'apply'
|
328
|
-
@execute.hide = false
|
329
|
-
APPLY_HELP
|
330
|
-
else
|
331
|
-
BANNER
|
332
|
-
end
|
451
|
+
self.banner = help_text[:banner]
|
452
|
+
# Builds the option list for the specified subcommand and removes all excluded
|
453
|
+
# options from the help text
|
454
|
+
remove_excluded_opts(help_text[:flags])
|
333
455
|
end
|
334
456
|
|
335
457
|
def parse_params(params)
|
data/lib/bolt/cli.rb
CHANGED
@@ -23,6 +23,7 @@ require 'bolt/plugin'
|
|
23
23
|
require 'bolt/pal'
|
24
24
|
require 'bolt/target'
|
25
25
|
require 'bolt/version'
|
26
|
+
require 'bolt/secret'
|
26
27
|
|
27
28
|
module Bolt
|
28
29
|
class CLIExit < StandardError; end
|
@@ -33,6 +34,7 @@ module Bolt
|
|
33
34
|
'plan' => %w[show run convert],
|
34
35
|
'file' => %w[upload],
|
35
36
|
'puppetfile' => %w[install show-modules],
|
37
|
+
'secret' => %w[encrypt decrypt createkeys],
|
36
38
|
'apply' => %w[] }.freeze
|
37
39
|
|
38
40
|
attr_reader :config, :options
|
@@ -51,7 +53,7 @@ module Bolt
|
|
51
53
|
end
|
52
54
|
private :inventory
|
53
55
|
|
54
|
-
def help?(
|
56
|
+
def help?(remaining)
|
55
57
|
# Set the subcommand
|
56
58
|
options[:subcommand] = remaining.shift
|
57
59
|
|
@@ -60,8 +62,12 @@ module Bolt
|
|
60
62
|
options[:subcommand] = remaining.shift
|
61
63
|
end
|
62
64
|
|
63
|
-
#
|
64
|
-
|
65
|
+
# This section handles parsing non-flag options which are
|
66
|
+
# subcommand specific rather then part of the config
|
67
|
+
actions = COMMANDS[options[:subcommand]]
|
68
|
+
if actions && !actions.empty?
|
69
|
+
options[:action] = remaining.shift
|
70
|
+
end
|
65
71
|
|
66
72
|
options[:help]
|
67
73
|
end
|
@@ -72,17 +78,13 @@ module Bolt
|
|
72
78
|
|
73
79
|
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
74
80
|
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
75
|
-
if @argv.empty? || help?(
|
81
|
+
if @argv.empty? || help?(remaining)
|
82
|
+
# Update the parser for the subcommand (or lack thereof)
|
83
|
+
parser.update
|
76
84
|
puts parser.help
|
77
85
|
raise Bolt::CLIExit
|
78
86
|
end
|
79
87
|
|
80
|
-
# This section handles parsing non-flag options which are
|
81
|
-
# subcommand specific rather then part of the config
|
82
|
-
actions = COMMANDS[options[:subcommand]]
|
83
|
-
if actions && !actions.empty?
|
84
|
-
options[:action] = remaining.shift
|
85
|
-
end
|
86
88
|
options[:object] = remaining.shift
|
87
89
|
|
88
90
|
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
@@ -120,6 +122,7 @@ module Bolt
|
|
120
122
|
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
121
123
|
# options[:targets] will contain a resolved set of Target objects
|
122
124
|
unless options[:subcommand] == 'puppetfile' ||
|
125
|
+
options[:subcommand] == 'secret' ||
|
123
126
|
options[:action] == 'show' ||
|
124
127
|
options[:action] == 'convert'
|
125
128
|
|
@@ -311,6 +314,8 @@ module Bolt
|
|
311
314
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
312
315
|
when 'puppetfile'
|
313
316
|
code = install_puppetfile(@config.puppetfile_config, @config.puppetfile, @config.modulepath)
|
317
|
+
when 'secret'
|
318
|
+
code = Bolt::Secret.execute(plugins, outputter, options)
|
314
319
|
when 'apply'
|
315
320
|
if options[:object]
|
316
321
|
validate_file('manifest', options[:object])
|
data/lib/bolt/config.rb
CHANGED
@@ -33,7 +33,7 @@ module Bolt
|
|
33
33
|
class Config
|
34
34
|
attr_accessor :concurrency, :format, :trace, :log, :puppetdb, :color, :save_rerun,
|
35
35
|
:transport, :transports, :inventoryfile, :compile_concurrency, :boltdir,
|
36
|
-
:puppetfile_config
|
36
|
+
:puppetfile_config, :plugins
|
37
37
|
attr_writer :modulepath
|
38
38
|
|
39
39
|
TRANSPORT_OPTIONS = %i[password run-as sudo-password extensions
|
@@ -70,6 +70,7 @@ module Bolt
|
|
70
70
|
@color = true
|
71
71
|
@save_rerun = true
|
72
72
|
@puppetfile_config = {}
|
73
|
+
@plugins = {}
|
73
74
|
|
74
75
|
# add an entry for the default console logger
|
75
76
|
@log = { 'console' => {} }
|
@@ -158,6 +159,9 @@ module Bolt
|
|
158
159
|
|
159
160
|
@save_rerun = data['save-rerun'] if data.key?('save-rerun')
|
160
161
|
|
162
|
+
# Plugins are only settable from config not inventory so we can overwrite
|
163
|
+
@plugins = data['plugins'] if data.key?('plugins')
|
164
|
+
|
161
165
|
%w[concurrency format puppetdb color transport].each do |key|
|
162
166
|
send("#{key}=", data[key]) if data.key?(key)
|
163
167
|
end
|
@@ -93,7 +93,10 @@ module Bolt
|
|
93
93
|
unless (plugin = @plugins.by_name(value['_plugin']))
|
94
94
|
raise ValidationError.new("unkown plugin: #{value['_plugin'].inspect}", nil)
|
95
95
|
end
|
96
|
-
plugin.
|
96
|
+
plugin.validate_inventory_config_lookup(value) if plugin.respond_to?(:validate_inventory_config_lookup)
|
97
|
+
Concurrent::Delay.new do
|
98
|
+
plugin.inventory_config_lookup(value)
|
99
|
+
end
|
97
100
|
else
|
98
101
|
value
|
99
102
|
end
|
data/lib/bolt/plugin.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'bolt/plugin/puppetdb'
|
4
4
|
require 'bolt/plugin/terraform'
|
5
|
+
require 'bolt/plugin/pkcs7'
|
5
6
|
require 'bolt/plugin/prompt'
|
6
7
|
|
7
8
|
module Bolt
|
@@ -10,7 +11,8 @@ module Bolt
|
|
10
11
|
plugins = new(config)
|
11
12
|
plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
|
12
13
|
plugins.add_plugin(Bolt::Plugin::Terraform.new)
|
13
|
-
plugins.add_plugin(Bolt::Plugin::Prompt)
|
14
|
+
plugins.add_plugin(Bolt::Plugin::Prompt.new)
|
15
|
+
plugins.add_plugin(Bolt::Plugin::Pkcs7.new(config.boltdir.path, config.plugins['pkcs7'] || {}))
|
14
16
|
plugins
|
15
17
|
end
|
16
18
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/secret/base'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class Plugin
|
8
|
+
class Pkcs7 < Bolt::Secret::Base
|
9
|
+
def self.validate_config(config)
|
10
|
+
known_keys = ['private-key', 'public-key', 'keysize']
|
11
|
+
known_keys.each do |key|
|
12
|
+
unless key.is_a? String
|
13
|
+
raise Bolt::ValidationError, "Invalid config for pkcs7 plugin: '#{key}' is not a String"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
config.keys.each do |key|
|
18
|
+
unless known_keys.include?(key)
|
19
|
+
raise Bolt::ValidationError, "Unpexpected key in pkcs7 plugin config: #{key}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def name
|
25
|
+
'pkcs7'
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(boltdir, options)
|
29
|
+
self.class.validate_config(options)
|
30
|
+
require 'openssl'
|
31
|
+
@boltdir = boltdir
|
32
|
+
@options = options || {}
|
33
|
+
@logger = Logging.logger[self]
|
34
|
+
end
|
35
|
+
|
36
|
+
def private_key_path
|
37
|
+
path = @options['private-key'] || 'keys/private_key.pkcs7.pem'
|
38
|
+
path = File.absolute_path(path, @boltdir)
|
39
|
+
@logger.debug("Using private-key: #{path}")
|
40
|
+
path
|
41
|
+
end
|
42
|
+
|
43
|
+
def private_key
|
44
|
+
@private_key ||= OpenSSL::PKey::RSA.new(File.read(private_key_path))
|
45
|
+
end
|
46
|
+
|
47
|
+
def public_key_path
|
48
|
+
path = @options['public-key'] || 'keys/public_key.pkcs7.pem'
|
49
|
+
path = File.absolute_path(path, @boltdir)
|
50
|
+
@logger.debug("Using public-key: #{path}")
|
51
|
+
path
|
52
|
+
end
|
53
|
+
|
54
|
+
def public_key
|
55
|
+
@public_key ||= OpenSSL::X509::Certificate.new(File.read(public_key_path))
|
56
|
+
end
|
57
|
+
|
58
|
+
def keysize
|
59
|
+
@options['keysize'] || 2048
|
60
|
+
end
|
61
|
+
|
62
|
+
# The following implementations are intended to be compatible with hiera-eyaml
|
63
|
+
def encrypt_value(plaintext)
|
64
|
+
cipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
65
|
+
OpenSSL::PKCS7.encrypt([public_key], plaintext, cipher, OpenSSL::PKCS7::BINARY).to_der
|
66
|
+
end
|
67
|
+
|
68
|
+
def decrypt_value(ciphertext)
|
69
|
+
pkcs7 = OpenSSL::PKCS7.new(ciphertext)
|
70
|
+
pkcs7.decrypt(private_key, public_key)
|
71
|
+
end
|
72
|
+
|
73
|
+
def secret_createkeys
|
74
|
+
key = OpenSSL::PKey::RSA.new(keysize)
|
75
|
+
|
76
|
+
cert = OpenSSL::X509::Certificate.new
|
77
|
+
cert.subject = OpenSSL::X509::Name.parse('/')
|
78
|
+
cert.serial = 1
|
79
|
+
cert.version = 2
|
80
|
+
cert.not_before = Time.now
|
81
|
+
cert.not_after = Time.now + 50 * 365 * 24 * 60 * 60
|
82
|
+
cert.public_key = key.public_key
|
83
|
+
cert.sign(key, OpenSSL::Digest.new('SHA512'))
|
84
|
+
|
85
|
+
@logger.warn("Overwriting private-key '#{private_key_path}'") if File.exist?(private_key_path)
|
86
|
+
@logger.warn("Overwriting public-key '#{public_key_path}'") if File.exist?(public_key_path)
|
87
|
+
|
88
|
+
private_keydir = File.dirname(private_key_path)
|
89
|
+
FileUtils.mkdir_p(private_keydir) unless File.exist?(private_keydir)
|
90
|
+
FileUtils.touch(private_key_path)
|
91
|
+
File.chmod(0o600, private_key_path)
|
92
|
+
File.write(private_key_path, key.to_pem)
|
93
|
+
|
94
|
+
public_keydir = File.dirname(public_key_path)
|
95
|
+
FileUtils.mkdir_p(public_keydir) unless File.exist?(public_keydir)
|
96
|
+
File.write(public_key_path, cert.to_pem)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/bolt/plugin/prompt.rb
CHANGED
@@ -9,7 +9,7 @@ module Bolt
|
|
9
9
|
@logger = Logging.logger[self]
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def name
|
13
13
|
'prompt'
|
14
14
|
end
|
15
15
|
|
@@ -17,15 +17,15 @@ module Bolt
|
|
17
17
|
['inventory_config_lookup']
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def validate_inventory_config_lookup(opts)
|
21
21
|
raise Bolt::ValidationError, "Prompt requires a 'message'" unless opts['message']
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
end
|
23
|
+
|
24
|
+
def inventory_config_lookup(opts)
|
25
|
+
STDOUT.print "#{opts['message']}:"
|
26
|
+
value = STDIN.noecho(&:gets).chomp
|
27
|
+
STDOUT.puts
|
28
|
+
value
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/lib/bolt/secret.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class Secret
|
5
|
+
def self.execute(plugins, outputter, options)
|
6
|
+
enc = plugins.by_name('pkcs7')
|
7
|
+
case options[:action]
|
8
|
+
when 'createkeys'
|
9
|
+
enc.secret_createkeys
|
10
|
+
when 'encrypt'
|
11
|
+
outputter.print_message(enc.secret_encrypt('plaintext-value' => options[:object]))
|
12
|
+
when 'decrypt'
|
13
|
+
outputter.print_message(enc.secret_decrypt('encrypted-value' => options[:object]))
|
14
|
+
end
|
15
|
+
|
16
|
+
0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class Secret
|
5
|
+
class Base
|
6
|
+
def hooks
|
7
|
+
%w[inventory_config_lookup encrypt decrypt create_keys]
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode(raw)
|
11
|
+
coded = Base64.encode64(raw).strip
|
12
|
+
"ENC[#{name.upcase},#{coded}]"
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode(code)
|
16
|
+
format = %r{\AENC\[(?<plugin>\w+),(?<encoded>[\w\s+-=/]+)\]\s*\z}
|
17
|
+
match = format.match(code)
|
18
|
+
|
19
|
+
raise Bolt::ValidationError, "Could not parse as an encrypted value: #{code}" unless match
|
20
|
+
|
21
|
+
raw = Base64.decode64(match[:encoded])
|
22
|
+
[raw, match[:plugin]]
|
23
|
+
end
|
24
|
+
|
25
|
+
def secret_encrypt(opts)
|
26
|
+
encrypted = encrypt_value(opts['plaintext-value'])
|
27
|
+
encode(encrypted)
|
28
|
+
end
|
29
|
+
|
30
|
+
def secret_decrypt(opts)
|
31
|
+
raw, _plugin = decode(opts['encrypted-value'])
|
32
|
+
decrypt_value(raw)
|
33
|
+
end
|
34
|
+
alias inventory_config_lookup secret_decrypt
|
35
|
+
|
36
|
+
def validate_inventory_config_lookup(opts)
|
37
|
+
decode(opts['encrypted-value'])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/bolt/version.rb
CHANGED
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: 1.
|
4
|
+
version: 1.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -274,28 +274,28 @@ dependencies:
|
|
274
274
|
requirements:
|
275
275
|
- - "~>"
|
276
276
|
- !ruby/object:Gem::Version
|
277
|
-
version: '2.
|
277
|
+
version: '2.7'
|
278
278
|
type: :development
|
279
279
|
prerelease: false
|
280
280
|
version_requirements: !ruby/object:Gem::Requirement
|
281
281
|
requirements:
|
282
282
|
- - "~>"
|
283
283
|
- !ruby/object:Gem::Version
|
284
|
-
version: '2.
|
284
|
+
version: '2.7'
|
285
285
|
- !ruby/object:Gem::Dependency
|
286
286
|
name: rake
|
287
287
|
requirement: !ruby/object:Gem::Requirement
|
288
288
|
requirements:
|
289
289
|
- - "~>"
|
290
290
|
- !ruby/object:Gem::Version
|
291
|
-
version: '
|
291
|
+
version: '12.0'
|
292
292
|
type: :development
|
293
293
|
prerelease: false
|
294
294
|
version_requirements: !ruby/object:Gem::Requirement
|
295
295
|
requirements:
|
296
296
|
- - "~>"
|
297
297
|
- !ruby/object:Gem::Version
|
298
|
-
version: '
|
298
|
+
version: '12.0'
|
299
299
|
- !ruby/object:Gem::Dependency
|
300
300
|
name: rspec
|
301
301
|
requirement: !ruby/object:Gem::Requirement
|
@@ -389,6 +389,7 @@ files:
|
|
389
389
|
- lib/bolt/pal/yaml_plan/transpiler.rb
|
390
390
|
- lib/bolt/plan_result.rb
|
391
391
|
- lib/bolt/plugin.rb
|
392
|
+
- lib/bolt/plugin/pkcs7.rb
|
392
393
|
- lib/bolt/plugin/prompt.rb
|
393
394
|
- lib/bolt/plugin/puppetdb.rb
|
394
395
|
- lib/bolt/plugin/terraform.rb
|
@@ -399,6 +400,8 @@ files:
|
|
399
400
|
- lib/bolt/rerun.rb
|
400
401
|
- lib/bolt/result.rb
|
401
402
|
- lib/bolt/result_set.rb
|
403
|
+
- lib/bolt/secret.rb
|
404
|
+
- lib/bolt/secret/base.rb
|
402
405
|
- lib/bolt/target.rb
|
403
406
|
- lib/bolt/task.rb
|
404
407
|
- lib/bolt/task/puppet_server.rb
|