bolt 3.3.0 → 3.7.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 +5 -5
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +66 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +19 -2
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/guides/targets.txt +31 -0
- data/lib/bolt/analytics.rb +4 -8
- data/lib/bolt/bolt_option_parser.rb +35 -17
- data/lib/bolt/cli.rb +109 -28
- data/lib/bolt/config.rb +11 -7
- data/lib/bolt/config/options.rb +41 -9
- data/lib/bolt/config/transport/lxd.rb +3 -1
- data/lib/bolt/config/transport/options.rb +7 -0
- data/lib/bolt/config/transport/podman.rb +33 -0
- data/lib/bolt/container_result.rb +105 -0
- data/lib/bolt/error.rb +15 -0
- data/lib/bolt/executor.rb +27 -15
- data/lib/bolt/inventory.rb +5 -4
- data/lib/bolt/inventory/inventory.rb +3 -2
- data/lib/bolt/inventory/options.rb +9 -0
- data/lib/bolt/inventory/target.rb +16 -0
- data/lib/bolt/node/output.rb +14 -4
- data/lib/bolt/outputter/human.rb +243 -84
- data/lib/bolt/outputter/json.rb +6 -4
- data/lib/bolt/outputter/logger.rb +17 -0
- data/lib/bolt/pal.rb +22 -2
- data/lib/bolt/pal/yaml_plan/step.rb +4 -2
- data/lib/bolt/pal/yaml_plan/step/command.rb +8 -0
- data/lib/bolt/pal/yaml_plan/step/script.rb +4 -0
- data/lib/bolt/pal/yaml_plan/transpiler.rb +2 -2
- data/lib/bolt/plan_creator.rb +2 -2
- data/lib/bolt/plugin.rb +13 -11
- data/lib/bolt/puppetdb/client.rb +54 -0
- data/lib/bolt/result.rb +5 -14
- data/lib/bolt/shell/bash.rb +33 -22
- data/lib/bolt/shell/powershell.rb +6 -8
- data/lib/bolt/transport/docker.rb +1 -1
- data/lib/bolt/transport/docker/connection.rb +21 -32
- data/lib/bolt/transport/lxd/connection.rb +5 -5
- data/lib/bolt/transport/orch.rb +13 -5
- data/lib/bolt/transport/podman.rb +19 -0
- data/lib/bolt/transport/podman/connection.rb +98 -0
- data/lib/bolt/util.rb +42 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +3 -0
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
- data/lib/bolt_spec/plans/mock_executor.rb +91 -11
- data/modules/puppet_connect/plans/test_input_data.pp +22 -0
- metadata +11 -2
data/lib/bolt/config.rb
CHANGED
@@ -169,6 +169,7 @@ module Bolt
|
|
169
169
|
@config_files = []
|
170
170
|
|
171
171
|
default_data = {
|
172
|
+
'analytics' => true,
|
172
173
|
'apply-settings' => {},
|
173
174
|
'color' => true,
|
174
175
|
'compile-concurrency' => Etc.nprocessors,
|
@@ -263,6 +264,8 @@ module Bolt
|
|
263
264
|
# Disabled warnings are concatenated
|
264
265
|
when 'disable-warnings'
|
265
266
|
val1.concat(val2)
|
267
|
+
when 'analytics'
|
268
|
+
val1 && val2
|
266
269
|
# All other values are overwritten
|
267
270
|
else
|
268
271
|
val2
|
@@ -328,13 +331,6 @@ module Bolt
|
|
328
331
|
end
|
329
332
|
|
330
333
|
def validate
|
331
|
-
if @data['future']
|
332
|
-
Bolt::Logger.warn(
|
333
|
-
"future_option",
|
334
|
-
"Configuration option 'future' no longer exposes future behavior."
|
335
|
-
)
|
336
|
-
end
|
337
|
-
|
338
334
|
if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
|
339
335
|
raise Bolt::ValidationError,
|
340
336
|
"Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
|
@@ -395,6 +391,10 @@ module Bolt
|
|
395
391
|
@data['format'] = value
|
396
392
|
end
|
397
393
|
|
394
|
+
def future
|
395
|
+
@data['future']
|
396
|
+
end
|
397
|
+
|
398
398
|
def trace
|
399
399
|
@data['trace']
|
400
400
|
end
|
@@ -459,6 +459,10 @@ module Bolt
|
|
459
459
|
Set.new(@project.disable_warnings + @data['disable-warnings'])
|
460
460
|
end
|
461
461
|
|
462
|
+
def analytics
|
463
|
+
@data['analytics']
|
464
|
+
end
|
465
|
+
|
462
466
|
# Check if there is a case-insensitive match to the path
|
463
467
|
def check_path_case(type, paths)
|
464
468
|
return if paths.nil?
|
data/lib/bolt/config/options.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'bolt/config/transport/
|
4
|
-
require 'bolt/config/transport/winrm'
|
5
|
-
require 'bolt/config/transport/orch'
|
3
|
+
require 'bolt/config/transport/docker'
|
6
4
|
require 'bolt/config/transport/local'
|
7
5
|
require 'bolt/config/transport/lxd'
|
8
|
-
require 'bolt/config/transport/
|
6
|
+
require 'bolt/config/transport/orch'
|
7
|
+
require 'bolt/config/transport/podman'
|
9
8
|
require 'bolt/config/transport/remote'
|
9
|
+
require 'bolt/config/transport/ssh'
|
10
|
+
require 'bolt/config/transport/winrm'
|
10
11
|
|
11
12
|
module Bolt
|
12
13
|
class Config
|
@@ -14,13 +15,14 @@ module Bolt
|
|
14
15
|
# Transport config classes. Used to load default transport config which
|
15
16
|
# gets passed along to the inventory.
|
16
17
|
TRANSPORT_CONFIG = {
|
17
|
-
'
|
18
|
-
'winrm' => Bolt::Config::Transport::WinRM,
|
19
|
-
'pcp' => Bolt::Config::Transport::Orch,
|
18
|
+
'docker' => Bolt::Config::Transport::Docker,
|
20
19
|
'local' => Bolt::Config::Transport::Local,
|
21
20
|
'lxd' => Bolt::Config::Transport::LXD,
|
22
|
-
'
|
23
|
-
'
|
21
|
+
'pcp' => Bolt::Config::Transport::Orch,
|
22
|
+
'podman' => Bolt::Config::Transport::Podman,
|
23
|
+
'remote' => Bolt::Config::Transport::Remote,
|
24
|
+
'ssh' => Bolt::Config::Transport::SSH,
|
25
|
+
'winrm' => Bolt::Config::Transport::WinRM
|
24
26
|
}.freeze
|
25
27
|
|
26
28
|
# Plugin definition. This is used by the JSON schemas to indicate that an option
|
@@ -55,6 +57,13 @@ module Bolt
|
|
55
57
|
# Definitions used to validate config options.
|
56
58
|
# https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
|
57
59
|
OPTIONS = {
|
60
|
+
"analytics" => {
|
61
|
+
description: "Whether to disable analytics. Setting this option to 'false' in the system-wide "\
|
62
|
+
"or user-level configuration will disable analytics for all projects, even if this "\
|
63
|
+
"option is set to 'true' at the project level.",
|
64
|
+
type: [TrueClass, FalseClass],
|
65
|
+
_example: false
|
66
|
+
},
|
58
67
|
"apply-settings" => {
|
59
68
|
description: "A map of Puppet settings to use when applying Puppet code using the `apply` "\
|
60
69
|
"plan function or the `bolt apply` command.",
|
@@ -133,6 +142,20 @@ module Bolt
|
|
133
142
|
_example: "json",
|
134
143
|
_default: "human"
|
135
144
|
},
|
145
|
+
"future" => {
|
146
|
+
description: "Enable new Bolt features that may include breaking changes.",
|
147
|
+
type: Hash,
|
148
|
+
properties: {
|
149
|
+
"file_paths" => {
|
150
|
+
description: "Load scripts from the `scripts/` directory of a module",
|
151
|
+
type: [TrueClass, FalseClass],
|
152
|
+
_example: true,
|
153
|
+
_default: false
|
154
|
+
}
|
155
|
+
},
|
156
|
+
_plugin: false,
|
157
|
+
_example: { 'file_paths' => true }
|
158
|
+
},
|
136
159
|
"hiera-config" => {
|
137
160
|
description: "The path to the Hiera configuration file.",
|
138
161
|
type: String,
|
@@ -497,6 +520,12 @@ module Bolt
|
|
497
520
|
_plugin: true,
|
498
521
|
_example: { "job-poll-interval" => 15, "job-poll-timeout" => 30 }
|
499
522
|
},
|
523
|
+
"podman" => {
|
524
|
+
description: "A map of configuration options for the podman transport.",
|
525
|
+
type: Hash,
|
526
|
+
_plugin: true,
|
527
|
+
_example: { "cleanup" => false, "tmpdir" => "/mount/tmp" }
|
528
|
+
},
|
500
529
|
"remote" => {
|
501
530
|
description: "A map of configuration options for the remote transport.",
|
502
531
|
type: Hash,
|
@@ -532,6 +561,7 @@ module Bolt
|
|
532
561
|
|
533
562
|
# Options that are available in a bolt-defaults.yaml file
|
534
563
|
DEFAULTS_OPTIONS = %w[
|
564
|
+
analytics
|
535
565
|
color
|
536
566
|
compile-concurrency
|
537
567
|
concurrency
|
@@ -551,12 +581,14 @@ module Bolt
|
|
551
581
|
|
552
582
|
# Options that are available in a bolt-project.yaml file
|
553
583
|
PROJECT_OPTIONS = %w[
|
584
|
+
analytics
|
554
585
|
apply-settings
|
555
586
|
color
|
556
587
|
compile-concurrency
|
557
588
|
concurrency
|
558
589
|
disable-warnings
|
559
590
|
format
|
591
|
+
future
|
560
592
|
hiera-config
|
561
593
|
log
|
562
594
|
modulepath
|
@@ -266,6 +266,13 @@ module Bolt
|
|
266
266
|
_plugin: true,
|
267
267
|
_example: "BOLT.PRODUCTION"
|
268
268
|
},
|
269
|
+
"remote" => {
|
270
|
+
type: String,
|
271
|
+
description: "The LXD remote host to use.",
|
272
|
+
_default: "local",
|
273
|
+
_plugin: false,
|
274
|
+
_example: 'myremote'
|
275
|
+
},
|
269
276
|
"run-as" => {
|
270
277
|
type: String,
|
271
278
|
description: "The user to run commands as after login. The run-as user must be different than the "\
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/config/transport/base'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class Config
|
8
|
+
module Transport
|
9
|
+
class Podman < Base
|
10
|
+
OPTIONS = %w[
|
11
|
+
cleanup
|
12
|
+
host
|
13
|
+
interpreters
|
14
|
+
shell-command
|
15
|
+
tmpdir
|
16
|
+
tty
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
DEFAULTS = {
|
20
|
+
'cleanup' => true
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
private def validate
|
24
|
+
super
|
25
|
+
|
26
|
+
if @config['interpreters']
|
27
|
+
@config['interpreters'] = normalize_interpreters(@config['interpreters'])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'bolt/error'
|
5
|
+
require 'bolt/result'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class ContainerResult
|
9
|
+
attr_reader :value, :object
|
10
|
+
|
11
|
+
def self.from_exception(exception, exit_code, image, position: [])
|
12
|
+
details = Bolt::Result.create_details(position)
|
13
|
+
error = {
|
14
|
+
'kind' => 'puppetlabs.tasks/container-error',
|
15
|
+
'issue_code' => 'CONTAINER_ERROR',
|
16
|
+
'msg' => "Error running container '#{image}': #{exception}",
|
17
|
+
'details' => details
|
18
|
+
}
|
19
|
+
error['details']['exit_code'] = exit_code
|
20
|
+
ContainerResult.new({ '_error' => error }, object: image)
|
21
|
+
end
|
22
|
+
|
23
|
+
def _pcore_init_hash
|
24
|
+
{ 'value' => @value,
|
25
|
+
'object' => @image }
|
26
|
+
end
|
27
|
+
|
28
|
+
# First argument can't be named given the way that Puppet deserializes variables
|
29
|
+
def initialize(value = nil, object: nil)
|
30
|
+
@value = value || {}
|
31
|
+
@object = object
|
32
|
+
end
|
33
|
+
|
34
|
+
def eql?(other)
|
35
|
+
self.class == other.class &&
|
36
|
+
value == other.value
|
37
|
+
end
|
38
|
+
alias == eql?
|
39
|
+
|
40
|
+
def [](key)
|
41
|
+
value[key]
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_json(opts = nil)
|
45
|
+
to_data.to_json(opts)
|
46
|
+
end
|
47
|
+
alias to_s to_json
|
48
|
+
|
49
|
+
# This is the value with all non-UTF-8 characters removed, suitable for
|
50
|
+
# printing or converting to JSON. It *should* only be possible to have
|
51
|
+
# non-UTF-8 characters in stdout/stderr keys as they are not allowed from
|
52
|
+
# tasks but we scrub the whole thing just in case.
|
53
|
+
def safe_value
|
54
|
+
Bolt::Util.walk_vals(value) do |val|
|
55
|
+
if val.is_a?(String)
|
56
|
+
# Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
|
57
|
+
val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
|
58
|
+
else
|
59
|
+
val
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def stdout
|
65
|
+
value['stdout']
|
66
|
+
end
|
67
|
+
|
68
|
+
def stderr
|
69
|
+
value['stderr']
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_data
|
73
|
+
{
|
74
|
+
"object" => object,
|
75
|
+
"status" => status,
|
76
|
+
"value" => safe_value
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def status
|
81
|
+
ok? ? 'success' : 'failure'
|
82
|
+
end
|
83
|
+
|
84
|
+
def ok?
|
85
|
+
error_hash.nil?
|
86
|
+
end
|
87
|
+
alias ok ok?
|
88
|
+
alias success? ok?
|
89
|
+
|
90
|
+
# This allows access to errors outside puppet compilation
|
91
|
+
# it should be prefered over error in bolt code
|
92
|
+
def error_hash
|
93
|
+
value['_error']
|
94
|
+
end
|
95
|
+
|
96
|
+
# Warning: This will fail outside of a compilation.
|
97
|
+
# Use error_hash inside bolt.
|
98
|
+
# Is it crazy for this to behave differently outside a compiler?
|
99
|
+
def error
|
100
|
+
if error_hash
|
101
|
+
Puppet::DataTypes::Error.from_asserted_hash(error_hash)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/bolt/error.rb
CHANGED
@@ -61,6 +61,21 @@ module Bolt
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
class ContainerFailure < Bolt::Error
|
65
|
+
attr_reader :result
|
66
|
+
|
67
|
+
def initialize(result)
|
68
|
+
details = {
|
69
|
+
'value' => result.value,
|
70
|
+
'object' => result.object
|
71
|
+
}
|
72
|
+
message = "Plan aborted: Running container '#{result.object}' failed."
|
73
|
+
super(message, 'bolt/container-failure', details)
|
74
|
+
@result = result
|
75
|
+
@error_code = 2
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
64
79
|
class RunFailure < Bolt::Error
|
65
80
|
attr_reader :result_set
|
66
81
|
|
data/lib/bolt/executor.rb
CHANGED
@@ -12,34 +12,37 @@ require 'bolt/config'
|
|
12
12
|
require 'bolt/result_set'
|
13
13
|
require 'bolt/puppetdb'
|
14
14
|
# Load transports
|
15
|
-
require 'bolt/transport/
|
16
|
-
require 'bolt/transport/winrm'
|
17
|
-
require 'bolt/transport/orch'
|
15
|
+
require 'bolt/transport/docker'
|
18
16
|
require 'bolt/transport/local'
|
19
17
|
require 'bolt/transport/lxd'
|
20
|
-
require 'bolt/transport/
|
18
|
+
require 'bolt/transport/orch'
|
19
|
+
require 'bolt/transport/podman'
|
21
20
|
require 'bolt/transport/remote'
|
21
|
+
require 'bolt/transport/ssh'
|
22
|
+
require 'bolt/transport/winrm'
|
22
23
|
require 'bolt/yarn'
|
23
24
|
|
24
25
|
module Bolt
|
25
26
|
TRANSPORTS = {
|
26
|
-
|
27
|
-
winrm: Bolt::Transport::WinRM,
|
28
|
-
pcp: Bolt::Transport::Orch,
|
27
|
+
docker: Bolt::Transport::Docker,
|
29
28
|
local: Bolt::Transport::Local,
|
30
29
|
lxd: Bolt::Transport::LXD,
|
31
|
-
|
32
|
-
|
30
|
+
pcp: Bolt::Transport::Orch,
|
31
|
+
podman: Bolt::Transport::Podman,
|
32
|
+
remote: Bolt::Transport::Remote,
|
33
|
+
ssh: Bolt::Transport::SSH,
|
34
|
+
winrm: Bolt::Transport::WinRM
|
33
35
|
}.freeze
|
34
36
|
|
35
37
|
class Executor
|
36
|
-
attr_reader :noop, :transports, :in_parallel
|
38
|
+
attr_reader :noop, :transports, :in_parallel, :future
|
37
39
|
attr_accessor :run_as
|
38
40
|
|
39
41
|
def initialize(concurrency = 1,
|
40
42
|
analytics = Bolt::Analytics::NoopClient.new,
|
41
43
|
noop = false,
|
42
|
-
modified_concurrency = false
|
44
|
+
modified_concurrency = false,
|
45
|
+
future = {})
|
43
46
|
# lazy-load expensive gem code
|
44
47
|
require 'concurrent'
|
45
48
|
@analytics = analytics
|
@@ -64,6 +67,7 @@ module Bolt
|
|
64
67
|
@noop = noop
|
65
68
|
@run_as = nil
|
66
69
|
@in_parallel = false
|
70
|
+
@future = future
|
67
71
|
@pool = if concurrency > 0
|
68
72
|
Concurrent::ThreadPoolExecutor.new(name: 'exec', max_threads: concurrency)
|
69
73
|
else
|
@@ -217,7 +221,7 @@ module Bolt
|
|
217
221
|
results
|
218
222
|
end
|
219
223
|
|
220
|
-
def report_transport(transport, count)
|
224
|
+
private def report_transport(transport, count)
|
221
225
|
name = transport.class.name.split('::').last.downcase
|
222
226
|
unless @reported_transports.include?(name)
|
223
227
|
@analytics&.event('Transport', 'initialize', label: name, value: count)
|
@@ -475,7 +479,7 @@ module Bolt
|
|
475
479
|
Time.now
|
476
480
|
end
|
477
481
|
|
478
|
-
def wait_until(timeout, retry_interval)
|
482
|
+
private def wait_until(timeout, retry_interval)
|
479
483
|
start = wait_now
|
480
484
|
until yield
|
481
485
|
raise(TimeoutError, 'Timed out waiting for target') if (wait_now - start).to_i >= timeout
|
@@ -484,22 +488,30 @@ module Bolt
|
|
484
488
|
end
|
485
489
|
|
486
490
|
def prompt(prompt, options)
|
487
|
-
@prompting = true
|
488
491
|
unless $stdin.tty?
|
492
|
+
return options[:default] if options[:default]
|
489
493
|
raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
|
490
494
|
end
|
491
495
|
|
492
|
-
|
496
|
+
@prompting = true
|
497
|
+
|
498
|
+
if options[:default] && !options[:sensitive]
|
499
|
+
$stderr.print("#{prompt} [#{options[:default]}]: ")
|
500
|
+
else
|
501
|
+
$stderr.print("#{prompt}: ")
|
502
|
+
end
|
493
503
|
|
494
504
|
value = if options[:sensitive]
|
495
505
|
$stdin.noecho(&:gets).to_s.chomp
|
496
506
|
else
|
497
507
|
$stdin.gets.to_s.chomp
|
498
508
|
end
|
509
|
+
|
499
510
|
@prompting = false
|
500
511
|
|
501
512
|
$stderr.puts if options[:sensitive]
|
502
513
|
|
514
|
+
value = options[:default] if value.empty?
|
503
515
|
value
|
504
516
|
end
|
505
517
|
|