bolt 3.0.1 → 3.6.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 +13 -11
- data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
- 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_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
- data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
- data/lib/bolt/analytics.rb +4 -8
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +6 -3
- data/lib/bolt/cli.rb +123 -37
- data/lib/bolt/config.rb +15 -7
- data/lib/bolt/config/options.rb +62 -12
- data/lib/bolt/config/transport/lxd.rb +23 -0
- data/lib/bolt/config/transport/options.rb +8 -1
- 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 +37 -18
- data/lib/bolt/inventory/options.rb +9 -0
- data/lib/bolt/inventory/target.rb +16 -0
- data/lib/bolt/logger.rb +8 -0
- data/lib/bolt/module_installer.rb +2 -2
- data/lib/bolt/module_installer/puppetfile.rb +2 -2
- data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
- data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
- data/lib/bolt/node/output.rb +14 -4
- data/lib/bolt/outputter/human.rb +259 -90
- data/lib/bolt/outputter/json.rb +3 -1
- data/lib/bolt/outputter/logger.rb +17 -0
- data/lib/bolt/pal.rb +25 -4
- data/lib/bolt/pal/yaml_plan.rb +1 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
- data/lib/bolt/pal/yaml_plan/step.rb +91 -31
- data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
- data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
- data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
- data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
- data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
- data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
- data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
- data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
- data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
- data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
- data/lib/bolt/plan_creator.rb +1 -1
- data/lib/bolt/plugin.rb +13 -11
- data/lib/bolt/project_manager.rb +1 -1
- data/lib/bolt/project_manager/module_migrator.rb +1 -1
- data/lib/bolt/result.rb +11 -15
- data/lib/bolt/shell.rb +16 -0
- data/lib/bolt/shell/bash.rb +61 -31
- data/lib/bolt/shell/bash/tmpdir.rb +2 -2
- data/lib/bolt/shell/powershell.rb +34 -12
- data/lib/bolt/shell/powershell/snippets.rb +30 -3
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/base.rb +0 -9
- data/lib/bolt/transport/docker.rb +2 -126
- data/lib/bolt/transport/docker/connection.rb +81 -167
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/lxd/connection.rb +99 -0
- 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/transport/ssh/connection.rb +1 -1
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/util.rb +42 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +64 -33
- data/lib/bolt_spec/bolt_context.rb +9 -4
- data/lib/bolt_spec/plans.rb +1 -109
- data/lib/bolt_spec/plans/action_stubs.rb +1 -1
- 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 -7
- data/modules/puppet_connect/plans/test_input_data.pp +65 -7
- metadata +12 -2
@@ -5,30 +5,38 @@ module Bolt
|
|
5
5
|
class YamlPlan
|
6
6
|
class Step
|
7
7
|
class Command < Step
|
8
|
-
def self.
|
9
|
-
|
8
|
+
def self.option_keys
|
9
|
+
Set['catch_errors', 'env_vars', 'run_as']
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.required_keys
|
13
|
-
Set['targets']
|
13
|
+
Set['command', 'targets']
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def self.validate_step_keys(body, number)
|
17
17
|
super
|
18
|
-
|
18
|
+
|
19
|
+
if body.key?('env_vars') && ![Hash, String].include?(body['env_vars'].class)
|
20
|
+
raise StepError.new('env_vars key must be a hash or evaluable string', body['name'], number)
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
# Returns an array of arguments to pass to the step's function call
|
25
|
+
#
|
26
|
+
private def format_args(body)
|
27
|
+
opts = format_options(body)
|
24
28
|
|
25
|
-
|
26
|
-
args
|
27
|
-
args <<
|
29
|
+
args = [body['command'], body['targets']]
|
30
|
+
args << body['description'] if body['description']
|
31
|
+
args << opts if opts.any?
|
28
32
|
|
29
|
-
|
33
|
+
args
|
34
|
+
end
|
30
35
|
|
31
|
-
|
36
|
+
# Returns the function corresponding to the step
|
37
|
+
#
|
38
|
+
private def function
|
39
|
+
'run_command'
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
@@ -5,31 +5,30 @@ module Bolt
|
|
5
5
|
class YamlPlan
|
6
6
|
class Step
|
7
7
|
class Download < Step
|
8
|
-
def self.
|
9
|
-
|
8
|
+
def self.option_keys
|
9
|
+
Set['catch_errors', 'run_as']
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.required_keys
|
13
13
|
Set['download', 'destination', 'targets']
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def transpile
|
23
|
-
code = String.new(" ")
|
24
|
-
code << "$#{@name} = " if @name
|
16
|
+
# Returns an array of arguments to pass to the step's function call
|
17
|
+
#
|
18
|
+
private def format_args(body)
|
19
|
+
opts = format_options(body)
|
25
20
|
|
26
|
-
|
27
|
-
args
|
28
|
-
args <<
|
21
|
+
args = [body['download'], body['destination'], body['targets']]
|
22
|
+
args << body['description'] if body['description']
|
23
|
+
args << opts if opts.any?
|
29
24
|
|
30
|
-
|
25
|
+
args
|
26
|
+
end
|
31
27
|
|
32
|
-
|
28
|
+
# Returns the function corresponding to the step
|
29
|
+
#
|
30
|
+
private def function
|
31
|
+
'download_file'
|
33
32
|
end
|
34
33
|
end
|
35
34
|
end
|
@@ -5,28 +5,28 @@ module Bolt
|
|
5
5
|
class YamlPlan
|
6
6
|
class Step
|
7
7
|
class Eval < Step
|
8
|
-
def self.allowed_keys
|
9
|
-
super + Set['eval']
|
10
|
-
end
|
11
|
-
|
12
8
|
def self.required_keys
|
13
|
-
Set
|
9
|
+
Set['eval']
|
14
10
|
end
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
# Evaluates the step
|
13
|
+
#
|
14
|
+
def evaluate(scope, evaluator)
|
15
|
+
evaluated = evaluator.evaluate_code_blocks(scope, body)
|
16
|
+
evaluated['eval']
|
19
17
|
end
|
20
18
|
|
19
|
+
# Transpiles the step into the plan language
|
20
|
+
#
|
21
21
|
def transpile
|
22
22
|
code = String.new(" ")
|
23
|
-
code << "$#{
|
23
|
+
code << "$#{body['name']} = " if body['name']
|
24
24
|
|
25
|
-
code_body = Bolt::Util.to_code(
|
25
|
+
code_body = Bolt::Util.to_code(body['eval']) || 'undef'
|
26
26
|
|
27
27
|
# If we're trying to assign the result of a multi-line eval to a name
|
28
28
|
# variable, we need to wrap it in `with()`.
|
29
|
-
if
|
29
|
+
if body['name'] && code_body.lines.count > 1
|
30
30
|
indented = code_body.gsub(/\n/, "\n ").chomp(" ")
|
31
31
|
code << "with() || {\n #{indented}}"
|
32
32
|
else
|
@@ -13,14 +13,23 @@ module Bolt
|
|
13
13
|
Set['message']
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
# Returns an array of arguments to pass to the step's function call
|
17
|
+
#
|
18
|
+
private def format_args(body)
|
19
|
+
[body['message']]
|
19
20
|
end
|
20
21
|
|
22
|
+
# Returns the function corresponding to the step
|
23
|
+
#
|
24
|
+
private def function
|
25
|
+
'out::message'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Transpiles the step into the plan language
|
29
|
+
#
|
21
30
|
def transpile
|
22
31
|
code = String.new(" ")
|
23
|
-
code << function_call(
|
32
|
+
code << function_call(function, format_args(body))
|
24
33
|
code << "\n"
|
25
34
|
end
|
26
35
|
end
|
@@ -6,30 +6,34 @@ module Bolt
|
|
6
6
|
class Step
|
7
7
|
class Plan < Step
|
8
8
|
def self.allowed_keys
|
9
|
-
super + Set['
|
9
|
+
super + Set['parameters']
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.
|
13
|
-
Set
|
12
|
+
def self.option_keys
|
13
|
+
Set['catch_errors', 'run_as']
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
@plan = step_body['plan']
|
19
|
-
@parameters = step_body.fetch('parameters', {})
|
16
|
+
def self.required_keys
|
17
|
+
Set['plan']
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
# Returns an array of arguments to pass to the step's function call
|
21
|
+
#
|
22
|
+
private def format_args(body)
|
23
|
+
opts = format_options(body)
|
24
|
+
params = (body['parameters'] || {}).merge(opts)
|
25
25
|
|
26
|
-
|
27
|
-
args
|
28
|
-
args <<
|
26
|
+
args = [body['plan']]
|
27
|
+
args << body['targets'] if body['targets']
|
28
|
+
args << params if params.any?
|
29
29
|
|
30
|
-
|
30
|
+
args
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
+
# Returns the function corresponding to the step
|
34
|
+
#
|
35
|
+
private def function
|
36
|
+
'run_plan'
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -5,18 +5,81 @@ module Bolt
|
|
5
5
|
class YamlPlan
|
6
6
|
class Step
|
7
7
|
class Resources < Step
|
8
|
-
def self.
|
9
|
-
|
8
|
+
def self.option_keys
|
9
|
+
Set['catch_errors', 'description', 'noop', 'run_as']
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.required_keys
|
13
|
-
Set['targets']
|
13
|
+
Set['resources', 'targets']
|
14
14
|
end
|
15
15
|
|
16
|
-
def initialize(
|
16
|
+
def initialize(body)
|
17
17
|
super
|
18
|
-
@resources =
|
19
|
-
|
18
|
+
@body['resources'] = normalize_resources(@body['resources'])
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns an array of arguments to pass to the apply function call
|
22
|
+
#
|
23
|
+
private def format_args(body)
|
24
|
+
opts = format_options(body)
|
25
|
+
|
26
|
+
args = [body['targets']]
|
27
|
+
args << opts if opts.any?
|
28
|
+
|
29
|
+
args
|
30
|
+
end
|
31
|
+
|
32
|
+
def evaluate(scope, evaluator)
|
33
|
+
evaluated = evaluator.evaluate_code_blocks(scope, body)
|
34
|
+
|
35
|
+
scope.call_function('apply_prep', evaluated['targets'])
|
36
|
+
|
37
|
+
apply_args = format_args(evaluated)
|
38
|
+
manifest = generate_manifest(evaluated['resources'])
|
39
|
+
apply_manifest(scope, apply_args, manifest)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generates a manifest from the resources
|
43
|
+
#
|
44
|
+
private def generate_manifest(resources)
|
45
|
+
# inspect returns the Ruby representation of the resource hashes,
|
46
|
+
# which happens to be the same as the Puppet representation
|
47
|
+
puppet_resources = resources.inspect
|
48
|
+
|
49
|
+
# Because the :tasks setting globally controls which mode the parser
|
50
|
+
# is in, we need to make this snippet of non-tasks manifest code
|
51
|
+
# parseable in tasks mode. The way to do that is by putting it in an
|
52
|
+
# apply statement and taking the body.
|
53
|
+
<<~MANIFEST
|
54
|
+
apply('placeholder') {
|
55
|
+
$resources = #{puppet_resources}
|
56
|
+
$resources.each |$res| {
|
57
|
+
Resource[$res['type']] { $res['title']:
|
58
|
+
* => $res['parameters'],
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
# Add relationships if there is more than one resource
|
63
|
+
if $resources.length > 1 {
|
64
|
+
($resources.length - 1).each |$index| {
|
65
|
+
$lhs = $resources[$index]
|
66
|
+
$rhs = $resources[$index+1]
|
67
|
+
$lhs_resource = Resource[$lhs['type'] , $lhs['title']]
|
68
|
+
$rhs_resource = Resource[$rhs['type'] , $rhs['title']]
|
69
|
+
$lhs_resource -> $rhs_resource
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
MANIFEST
|
74
|
+
end
|
75
|
+
|
76
|
+
# Applies the manifest block on the targets
|
77
|
+
#
|
78
|
+
private def apply_manifest(scope, args, manifest)
|
79
|
+
ast = self.class.parse_code_string(manifest)
|
80
|
+
apply_block = ast.body.body
|
81
|
+
applicator = Puppet.lookup(:apply_executor)
|
82
|
+
applicator.apply(args, apply_block, scope)
|
20
83
|
end
|
21
84
|
|
22
85
|
def self.validate(body, step_number)
|
@@ -26,26 +89,28 @@ module Bolt
|
|
26
89
|
if resource['type'] || resource['title']
|
27
90
|
if !resource['type']
|
28
91
|
err = "Resource declaration must include type key if title key is set"
|
29
|
-
raise
|
92
|
+
raise StepError.new(err, body['name'], step_number)
|
30
93
|
elsif !resource['title']
|
31
94
|
err = "Resource declaration must include title key if type key is set"
|
32
|
-
raise
|
95
|
+
raise StepError.new(err, body['name'], step_number)
|
33
96
|
end
|
34
97
|
else
|
35
98
|
type_keys = (resource.keys - ['parameters'])
|
36
99
|
if type_keys.empty?
|
37
100
|
err = "Resource declaration is missing a type"
|
38
|
-
raise
|
101
|
+
raise StepError.new(err, body['name'], step_number)
|
39
102
|
elsif type_keys.length > 1
|
40
103
|
err = "Resource declaration has ambiguous type: could be #{type_keys.join(' or ')}"
|
41
|
-
raise
|
104
|
+
raise StepError.new(err, body['name'], step_number)
|
42
105
|
end
|
43
106
|
end
|
44
107
|
end
|
45
108
|
end
|
46
109
|
|
110
|
+
# Normalizes the resources so they are in a format compatible with apply blocks
|
47
111
|
# What if this comes from a code block?
|
48
|
-
|
112
|
+
#
|
113
|
+
private def normalize_resources(resources)
|
49
114
|
resources.map do |resource|
|
50
115
|
if resource['type'] && resource['title']
|
51
116
|
type = resource['type']
|
@@ -59,25 +124,21 @@ module Bolt
|
|
59
124
|
end
|
60
125
|
end
|
61
126
|
|
62
|
-
def body
|
63
|
-
@body.merge('resources' => @normalized_resources)
|
64
|
-
end
|
65
|
-
|
66
127
|
def transpile
|
67
128
|
code = StringIO.new
|
68
129
|
|
69
130
|
code.print " "
|
70
|
-
|
71
|
-
args = [@targets]
|
72
|
-
code << function_call(fn, args)
|
131
|
+
code << function_call('apply_prep', [body['targets']])
|
73
132
|
code.print "\n"
|
74
133
|
|
75
134
|
code.print " "
|
76
|
-
code.print "$#{
|
135
|
+
code.print "$#{body['name']} = " if body['name']
|
136
|
+
|
137
|
+
code << function_call('apply', format_args(body))
|
77
138
|
|
78
|
-
code.
|
139
|
+
code.print " {\n"
|
79
140
|
|
80
|
-
declarations =
|
141
|
+
declarations = body['resources'].map do |resource|
|
81
142
|
type = resource['type'].is_a?(EvaluableString) ? resource['type'].value : resource['type']
|
82
143
|
title = Bolt::Util.to_code(resource['title'])
|
83
144
|
parameters = resource['parameters'].transform_values do |val|
|
@@ -6,35 +6,54 @@ module Bolt
|
|
6
6
|
class Step
|
7
7
|
class Script < Step
|
8
8
|
def self.allowed_keys
|
9
|
-
super + Set['
|
9
|
+
super + Set['arguments', 'pwsh_params']
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.option_keys
|
13
|
+
Set['catch_errors', 'env_vars', 'run_as']
|
10
14
|
end
|
11
15
|
|
12
16
|
def self.required_keys
|
13
|
-
Set['targets']
|
17
|
+
Set['script', 'targets']
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
20
|
+
def self.validate_step_keys(body, number)
|
17
21
|
super
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
|
23
|
+
if body.key?('arguments') && !body['arguments'].nil? && !body['arguments'].is_a?(Array)
|
24
|
+
raise StepError.new('arguments key must be an array', body['name'], number)
|
25
|
+
end
|
26
|
+
|
27
|
+
if body.key?('pwsh_params') && !body['pwsh_params'].nil? && !body['pwsh_params'].is_a?(Hash)
|
28
|
+
raise StepError.new('pwsh_params key must be a hash', body['name'], number)
|
29
|
+
end
|
30
|
+
|
31
|
+
if body.key?('env_vars') && ![Hash, String].include?(body['env_vars'].class)
|
32
|
+
raise StepError.new('env_vars key must be a hash or evaluable string', body['name'], number)
|
33
|
+
end
|
21
34
|
end
|
22
35
|
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
# Returns an array of arguments to pass to the step's function call
|
37
|
+
#
|
38
|
+
private def format_args(body)
|
39
|
+
args = body['arguments'] || []
|
40
|
+
pwsh_params = body['pwsh_params'] || {}
|
26
41
|
|
27
|
-
|
28
|
-
|
42
|
+
opts = format_options(body)
|
43
|
+
opts = opts.merge('arguments' => args) if args.any?
|
44
|
+
opts = opts.merge('pwsh_params' => pwsh_params) if pwsh_params.any?
|
29
45
|
|
30
|
-
|
31
|
-
args
|
32
|
-
args <<
|
33
|
-
args << options unless options.empty?
|
46
|
+
args = [body['script'], body['targets']]
|
47
|
+
args << body['description'] if body['description']
|
48
|
+
args << opts if opts.any?
|
34
49
|
|
35
|
-
|
50
|
+
args
|
51
|
+
end
|
36
52
|
|
37
|
-
|
53
|
+
# Returns the function corresponding to the step
|
54
|
+
#
|
55
|
+
private def function
|
56
|
+
'run_script'
|
38
57
|
end
|
39
58
|
end
|
40
59
|
end
|
@@ -6,31 +6,34 @@ module Bolt
|
|
6
6
|
class Step
|
7
7
|
class Task < Step
|
8
8
|
def self.allowed_keys
|
9
|
-
super + Set['
|
9
|
+
super + Set['parameters']
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.
|
13
|
-
Set['
|
12
|
+
def self.option_keys
|
13
|
+
Set['catch_errors', 'noop', 'run_as']
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
@task = step_body['task']
|
19
|
-
@parameters = step_body.fetch('parameters', {})
|
16
|
+
def self.required_keys
|
17
|
+
Set['targets', 'task']
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
# Returns an array of arguments to pass to the step's function call
|
21
|
+
#
|
22
|
+
private def format_args(body)
|
23
|
+
opts = format_options(body)
|
24
|
+
params = (body['parameters'] || {}).merge(opts)
|
25
25
|
|
26
|
-
|
27
|
-
args
|
28
|
-
args <<
|
29
|
-
args << @parameters unless @parameters.empty?
|
26
|
+
args = [body['task'], body['targets']]
|
27
|
+
args << body['description'] if body['description']
|
28
|
+
args << params if params.any?
|
30
29
|
|
31
|
-
|
30
|
+
args
|
31
|
+
end
|
32
32
|
|
33
|
-
|
33
|
+
# Returns the function corresponding to the step
|
34
|
+
#
|
35
|
+
private def function
|
36
|
+
'run_task'
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|