bolt 1.19.0 → 1.20.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/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +3 -10
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +3 -10
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +3 -9
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +2 -8
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +3 -8
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +2 -8
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +1 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +7 -33
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +3 -10
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +3 -10
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +2 -8
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +2 -7
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -1
- data/lib/bolt/applicator.rb +7 -3
- data/lib/bolt/apply_result.rb +8 -21
- data/lib/bolt/bolt_option_parser.rb +2 -2
- data/lib/bolt/catalog.rb +0 -1
- data/lib/bolt/cli.rb +51 -29
- data/lib/bolt/config.rb +0 -2
- data/lib/bolt/executor.rb +38 -50
- data/lib/bolt/inventory/group.rb +5 -0
- data/lib/bolt/inventory/group2.rb +5 -0
- data/lib/bolt/logger.rb +7 -3
- data/lib/bolt/outputter.rb +6 -4
- data/lib/bolt/outputter/human.rb +90 -6
- data/lib/bolt/outputter/json.rb +4 -4
- data/lib/bolt/outputter/logger.rb +53 -0
- data/lib/bolt/pal.rb +3 -3
- data/lib/bolt/pal/yaml_plan/step.rb +1 -1
- data/lib/bolt/plugin.rb +2 -0
- data/lib/bolt/plugin/terraform.rb +84 -0
- data/lib/bolt/result.rb +12 -8
- data/lib/bolt/result_set.rb +4 -0
- data/lib/bolt/transport/orch.rb +4 -4
- data/lib/bolt/version.rb +1 -1
- metadata +4 -3
- data/lib/bolt/notifier.rb +0 -23
data/lib/bolt/outputter.rb
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
module Bolt
|
4
4
|
class Outputter
|
5
|
-
def self.for_format(format, color, trace)
|
5
|
+
def self.for_format(format, color, verbose, trace)
|
6
6
|
case format
|
7
7
|
when 'human'
|
8
|
-
Bolt::Outputter::Human.new(color, trace)
|
8
|
+
Bolt::Outputter::Human.new(color, verbose, trace)
|
9
9
|
when 'json'
|
10
|
-
Bolt::Outputter::JSON.new(color, trace)
|
10
|
+
Bolt::Outputter::JSON.new(color, verbose, trace)
|
11
11
|
when nil
|
12
12
|
raise "Cannot use outputter before parsing."
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def initialize(color, trace, stream = $stdout)
|
16
|
+
def initialize(color, verbose, trace, stream = $stdout)
|
17
17
|
@color = color
|
18
|
+
@verbose = verbose
|
18
19
|
@trace = trace
|
19
20
|
@stream = stream
|
20
21
|
end
|
@@ -23,3 +24,4 @@ end
|
|
23
24
|
|
24
25
|
require 'bolt/outputter/human'
|
25
26
|
require 'bolt/outputter/json'
|
27
|
+
require 'bolt/outputter/logger'
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -11,6 +11,14 @@ module Bolt
|
|
11
11
|
|
12
12
|
def print_head; end
|
13
13
|
|
14
|
+
def initialize(color, verbose, trace, stream = $stdout)
|
15
|
+
super
|
16
|
+
# Plans and without_default_logging() calls can both be nested, so we
|
17
|
+
# track each of them with a "stack" consisting of an integer.
|
18
|
+
@plan_depth = 0
|
19
|
+
@disable_depth = 0
|
20
|
+
end
|
21
|
+
|
14
22
|
def colorize(color, string)
|
15
23
|
if @color && @stream.isatty
|
16
24
|
"\033[#{COLORS[color]}m#{string}\033[0m"
|
@@ -28,15 +36,37 @@ module Bolt
|
|
28
36
|
string.sub(/\s\z/, '')
|
29
37
|
end
|
30
38
|
|
31
|
-
def
|
39
|
+
def handle_event(event)
|
40
|
+
return unless enabled? || event[:type] == :enable_default_output
|
41
|
+
|
32
42
|
case event[:type]
|
33
43
|
when :node_start
|
34
|
-
print_start(event[:target])
|
44
|
+
print_start(event[:target]) if @verbose
|
35
45
|
when :node_result
|
36
|
-
print_result(event[:result])
|
46
|
+
print_result(event[:result]) if @verbose
|
47
|
+
when :step_start
|
48
|
+
print_step_start(event) if plan_logging?
|
49
|
+
when :step_finish
|
50
|
+
print_step_finish(event) if plan_logging?
|
51
|
+
when :plan_start
|
52
|
+
print_plan_start(event)
|
53
|
+
when :plan_finish
|
54
|
+
print_plan_finish(event)
|
55
|
+
when :enable_default_output
|
56
|
+
@disable_depth -= 1
|
57
|
+
when :disable_default_output
|
58
|
+
@disable_depth += 1
|
37
59
|
end
|
38
60
|
end
|
39
61
|
|
62
|
+
def enabled?
|
63
|
+
@disable_depth == 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def plan_logging?
|
67
|
+
@plan_depth > 0
|
68
|
+
end
|
69
|
+
|
40
70
|
def print_start(target)
|
41
71
|
@stream.puts(colorize(:green, "Started on #{target.host}..."))
|
42
72
|
end
|
@@ -52,6 +82,15 @@ module Bolt
|
|
52
82
|
@stream.puts(colorize(:red, remove_trail(indent(2, result.error_hash['msg']))))
|
53
83
|
end
|
54
84
|
|
85
|
+
if result.is_a?(Bolt::ApplyResult) && @verbose
|
86
|
+
result.resource_logs.each do |log|
|
87
|
+
# Omit low-level info/debug messages
|
88
|
+
next if %w[info debug].include?(log['level'])
|
89
|
+
message = format_log(log)
|
90
|
+
@stream.puts(indent(2, message))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
55
94
|
if result.message
|
56
95
|
@stream.puts(remove_trail(indent(2, result.message)))
|
57
96
|
end
|
@@ -74,6 +113,52 @@ module Bolt
|
|
74
113
|
end
|
75
114
|
end
|
76
115
|
|
116
|
+
def format_log(log)
|
117
|
+
color = case log['level']
|
118
|
+
when 'warn'
|
119
|
+
:yellow
|
120
|
+
when 'err'
|
121
|
+
:red
|
122
|
+
end
|
123
|
+
source = "#{log['source']}: " if log['source']
|
124
|
+
message = "#{log['level'].capitalize}: #{source}#{log['message']}"
|
125
|
+
message = colorize(color, message) if color
|
126
|
+
message
|
127
|
+
end
|
128
|
+
|
129
|
+
def print_step_start(description:, targets:, **_kwargs)
|
130
|
+
target_str = if targets.length > 5
|
131
|
+
"#{targets.count} targets"
|
132
|
+
else
|
133
|
+
targets.map(&:uri).join(', ')
|
134
|
+
end
|
135
|
+
@stream.puts(colorize(:green, "Starting: #{description} on #{target_str}"))
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_step_finish(description:, result:, duration:, **_kwargs)
|
139
|
+
failures = result.error_set.length
|
140
|
+
plural = failures == 1 ? '' : 's'
|
141
|
+
message = "Finished: #{description} with #{failures} failure#{plural} in #{duration.round(2)} sec"
|
142
|
+
@stream.puts(colorize(:green, message))
|
143
|
+
end
|
144
|
+
|
145
|
+
def print_plan_start(event)
|
146
|
+
@plan_depth += 1
|
147
|
+
# We use this event to both mark the start of a plan _and_ to enable
|
148
|
+
# plan logging for `apply`, so only log the message if we were called
|
149
|
+
# with a plan
|
150
|
+
if event[:plan]
|
151
|
+
@stream.puts(colorize(:green, "Starting: plan #{event[:plan]}"))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def print_plan_finish(event)
|
156
|
+
@plan_depth -= 1
|
157
|
+
plan = event[:plan]
|
158
|
+
duration = event[:duration]
|
159
|
+
@stream.puts(colorize(:green, "Finished: plan #{plan} in #{duration.round(2)} sec"))
|
160
|
+
end
|
161
|
+
|
77
162
|
def print_summary(results, elapsed_time = nil)
|
78
163
|
ok_set = results.ok_set
|
79
164
|
unless ok_set.empty?
|
@@ -222,9 +307,8 @@ module Bolt
|
|
222
307
|
end
|
223
308
|
|
224
309
|
# @param [Bolt::ResultSet] apply_result A ResultSet object representing the result of a `bolt apply`
|
225
|
-
def print_apply_result(apply_result)
|
226
|
-
apply_result
|
227
|
-
print_summary(apply_result)
|
310
|
+
def print_apply_result(apply_result, elapsed_time)
|
311
|
+
print_summary(apply_result, elapsed_time)
|
228
312
|
end
|
229
313
|
|
230
314
|
# @param [Bolt::PlanResult] plan_result A PlanResult object
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
module Bolt
|
4
4
|
class Outputter
|
5
5
|
class JSON < Bolt::Outputter
|
6
|
-
def initialize(color, trace, stream = $stdout)
|
6
|
+
def initialize(color, verbose, trace, stream = $stdout)
|
7
|
+
super
|
7
8
|
@items_open = false
|
8
9
|
@object_open = false
|
9
10
|
@preceding_item = false
|
10
|
-
super(color, trace, stream)
|
11
11
|
end
|
12
12
|
|
13
13
|
def print_head
|
@@ -17,7 +17,7 @@ module Bolt
|
|
17
17
|
@object_open = true
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def handle_event(event)
|
21
21
|
case event[:type]
|
22
22
|
when :node_result
|
23
23
|
print_result(event[:result])
|
@@ -72,7 +72,7 @@ module Bolt
|
|
72
72
|
print_table('plans' => plans, 'modulepath' => modulepath)
|
73
73
|
end
|
74
74
|
|
75
|
-
def print_apply_result(apply_result)
|
75
|
+
def print_apply_result(apply_result, _elapsed_time)
|
76
76
|
@stream.puts apply_result.to_json
|
77
77
|
end
|
78
78
|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/pal'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class Outputter
|
7
|
+
class Logger < Bolt::Outputter
|
8
|
+
def initialize(verbose, trace)
|
9
|
+
super(false, verbose, trace)
|
10
|
+
@logger = Logging.logger[self]
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_event(event)
|
14
|
+
case event[:type]
|
15
|
+
when :step_start
|
16
|
+
log_step_start(event)
|
17
|
+
when :step_finish
|
18
|
+
log_step_finish(event)
|
19
|
+
when :plan_start
|
20
|
+
log_plan_start(event)
|
21
|
+
when :plan_finish
|
22
|
+
log_plan_finish(event)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_step_start(description:, targets:, **_kwargs)
|
27
|
+
target_str = if targets.length > 5
|
28
|
+
"#{targets.count} targets"
|
29
|
+
else
|
30
|
+
targets.map(&:uri).join(', ')
|
31
|
+
end
|
32
|
+
@logger.info("Starting: #{description} on #{target_str}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def log_step_finish(description:, result:, duration:, **_kwargs)
|
36
|
+
failures = result.error_set.length
|
37
|
+
plural = failures == 1 ? '' : 's'
|
38
|
+
@logger.info("Finished: #{description} with #{failures} failure#{plural} in #{duration.round(2)} sec")
|
39
|
+
end
|
40
|
+
|
41
|
+
def log_plan_start(event)
|
42
|
+
plan = event[:plan]
|
43
|
+
@logger.notice("Starting: plan #{plan}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_plan_finish(event)
|
47
|
+
plan = event[:plan]
|
48
|
+
duration = event[:duration]
|
49
|
+
@logger.notice("Finished: plan #{plan} in #{duration.round(2)} sec")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/bolt/pal.rb
CHANGED
@@ -333,10 +333,10 @@ module Bolt
|
|
333
333
|
end
|
334
334
|
end
|
335
335
|
|
336
|
-
def run_task(task_name, targets, params, executor, inventory, description = nil
|
336
|
+
def run_task(task_name, targets, params, executor, inventory, description = nil)
|
337
337
|
in_task_compiler(executor, inventory) do |compiler|
|
338
|
-
params = params.merge('_bolt_api_call' => true)
|
339
|
-
compiler.call_function('run_task', task_name, targets, description, params
|
338
|
+
params = params.merge('_bolt_api_call' => true, '_catch_errors' => true)
|
339
|
+
compiler.call_function('run_task', task_name, targets, description, params)
|
340
340
|
end
|
341
341
|
end
|
342
342
|
|
@@ -77,7 +77,7 @@ module Bolt
|
|
77
77
|
# We have to do a little extra parsing here, since we only need
|
78
78
|
# with() for eval blocks
|
79
79
|
code = Bolt::Util.to_code(body['eval'])
|
80
|
-
if @name && code.
|
80
|
+
if @name && code.lines.count > 1
|
81
81
|
# A little indented niceness
|
82
82
|
indented = code.gsub(/\n/, "\n ").chomp(" ")
|
83
83
|
result << "with() || {\n #{indented}}"
|
data/lib/bolt/plugin.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bolt/plugin/puppetdb'
|
4
|
+
require 'bolt/plugin/terraform'
|
4
5
|
|
5
6
|
module Bolt
|
6
7
|
class Plugin
|
7
8
|
def self.setup(config, pdb_client)
|
8
9
|
plugins = new(config)
|
9
10
|
plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
|
11
|
+
plugins.add_plugin(Bolt::Plugin::Terraform.new)
|
10
12
|
plugins
|
11
13
|
end
|
12
14
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class Plugin
|
7
|
+
class Terraform
|
8
|
+
def initialize
|
9
|
+
@logger = Logging.logger[self]
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
'terraform'
|
14
|
+
end
|
15
|
+
|
16
|
+
def hooks
|
17
|
+
['lookup_targets']
|
18
|
+
end
|
19
|
+
|
20
|
+
def warn_missing_property(name, property)
|
21
|
+
@logger.warn("Could not find property #{property} of terraform resource #{name}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def lookup_targets(opts)
|
25
|
+
state = load_statefile(opts)
|
26
|
+
|
27
|
+
resources = state.fetch('modules', {}).flat_map do |mod|
|
28
|
+
mod.fetch('resources', {}).map do |name, resource|
|
29
|
+
[name, resource.dig('primary', 'attributes')]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
regex = Regexp.new(opts['resource_type'])
|
34
|
+
|
35
|
+
resources.select do |name, _resource|
|
36
|
+
name.match?(regex)
|
37
|
+
end.map do |name, resource|
|
38
|
+
unless resource.key?(opts['uri'])
|
39
|
+
warn_missing_property(name, opts['uri'])
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
43
|
+
target = { 'uri' => resource[opts['uri']] }
|
44
|
+
if opts.key?('name')
|
45
|
+
if resource.key?(opts['name'])
|
46
|
+
target['name'] = resource[opts['name']]
|
47
|
+
else
|
48
|
+
warn_missing_property(name, opts['name'])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
if opts.key?('config')
|
52
|
+
target['config'] = resolve_config(name, resource, opts['config'])
|
53
|
+
end
|
54
|
+
target
|
55
|
+
end.compact
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_statefile(opts)
|
59
|
+
dir = opts['dir']
|
60
|
+
filename = opts.fetch('statefile', 'terraform.tfstate')
|
61
|
+
statefile = File.expand_path(File.join(dir, filename))
|
62
|
+
|
63
|
+
JSON.parse(File.read(statefile))
|
64
|
+
rescue StandardError => e
|
65
|
+
raise Bolt::FileError.new("Could not load Terraform state file #{filename}: #{e}", filename)
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_config(name, resource, config_template)
|
69
|
+
Bolt::Util.walk_vals(config_template) do |value|
|
70
|
+
if value.is_a?(String)
|
71
|
+
if resource.key?(value)
|
72
|
+
resource[value]
|
73
|
+
else
|
74
|
+
warn_missing_property(name, value)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
else
|
78
|
+
value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/bolt/result.rb
CHANGED
@@ -5,7 +5,7 @@ require 'bolt/error'
|
|
5
5
|
|
6
6
|
module Bolt
|
7
7
|
class Result
|
8
|
-
attr_reader :target, :value, :
|
8
|
+
attr_reader :target, :value, :action, :object
|
9
9
|
|
10
10
|
def self.from_exception(target, exception)
|
11
11
|
@exception = exception
|
@@ -23,7 +23,7 @@ module Bolt
|
|
23
23
|
Result.new(target, error: error)
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.for_command(target, stdout, stderr, exit_code,
|
26
|
+
def self.for_command(target, stdout, stderr, exit_code, action, command)
|
27
27
|
value = {
|
28
28
|
'stdout' => stdout,
|
29
29
|
'stderr' => stderr,
|
@@ -37,7 +37,7 @@ module Bolt
|
|
37
37
|
'details' => { 'exit_code' => exit_code }
|
38
38
|
}
|
39
39
|
end
|
40
|
-
new(target, value: value,
|
40
|
+
new(target, value: value, action: action, object: command)
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.for_task(target, stdout, stderr, exit_code, task)
|
@@ -61,11 +61,11 @@ module Bolt
|
|
61
61
|
'msg' => msg,
|
62
62
|
'details' => { 'exit_code' => exit_code } }
|
63
63
|
end
|
64
|
-
new(target, value: value,
|
64
|
+
new(target, value: value, action: 'task', object: task)
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.for_upload(target, source, destination)
|
68
|
-
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'",
|
68
|
+
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
|
69
69
|
end
|
70
70
|
|
71
71
|
# Satisfies the Puppet datatypes API
|
@@ -73,10 +73,10 @@ module Bolt
|
|
73
73
|
new(target, value: value)
|
74
74
|
end
|
75
75
|
|
76
|
-
def initialize(target, error: nil, message: nil, value: nil,
|
76
|
+
def initialize(target, error: nil, message: nil, value: nil, action: nil, object: nil)
|
77
77
|
@target = target
|
78
78
|
@value = value || {}
|
79
|
-
@
|
79
|
+
@action = action
|
80
80
|
@object = object
|
81
81
|
@value_set = !value.nil?
|
82
82
|
if error && !error.is_a?(Hash)
|
@@ -94,7 +94,7 @@ module Bolt
|
|
94
94
|
# DEPRECATION: node in status hashes is deprecated and should be removed in 2.0
|
95
95
|
{ node: @target.name,
|
96
96
|
target: @target.name,
|
97
|
-
|
97
|
+
action: action,
|
98
98
|
object: object,
|
99
99
|
status: ok? ? 'success' : 'failure',
|
100
100
|
result: @value }
|
@@ -128,6 +128,10 @@ module Bolt
|
|
128
128
|
to_json
|
129
129
|
end
|
130
130
|
|
131
|
+
def to_data
|
132
|
+
Bolt::Util.walk_keys(status_hash, &:to_s)
|
133
|
+
end
|
134
|
+
|
131
135
|
def ok?
|
132
136
|
error_hash.nil?
|
133
137
|
end
|