cem_acpt 0.8.8 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +0 -3
- data/Gemfile.lock +9 -1
- data/README.md +95 -13
- data/cem_acpt.gemspec +2 -1
- data/lib/cem_acpt/action_result.rb +8 -2
- data/lib/cem_acpt/actions.rb +153 -0
- data/lib/cem_acpt/bolt/cmd/base.rb +174 -0
- data/lib/cem_acpt/bolt/cmd/output.rb +315 -0
- data/lib/cem_acpt/bolt/cmd/task.rb +59 -0
- data/lib/cem_acpt/bolt/cmd.rb +22 -0
- data/lib/cem_acpt/bolt/errors.rb +49 -0
- data/lib/cem_acpt/bolt/helpers.rb +52 -0
- data/lib/cem_acpt/bolt/inventory.rb +62 -0
- data/lib/cem_acpt/bolt/project.rb +38 -0
- data/lib/cem_acpt/bolt/summary_results.rb +96 -0
- data/lib/cem_acpt/bolt/tasks.rb +181 -0
- data/lib/cem_acpt/bolt/tests.rb +415 -0
- data/lib/cem_acpt/bolt/yaml_file.rb +74 -0
- data/lib/cem_acpt/bolt.rb +142 -0
- data/lib/cem_acpt/cli.rb +6 -0
- data/lib/cem_acpt/config/base.rb +4 -0
- data/lib/cem_acpt/config/cem_acpt.rb +7 -1
- data/lib/cem_acpt/core_ext.rb +25 -0
- data/lib/cem_acpt/goss/api/action_response.rb +4 -0
- data/lib/cem_acpt/goss/api.rb +23 -25
- data/lib/cem_acpt/image_builder/provision_commands.rb +43 -0
- data/lib/cem_acpt/logging/formatter.rb +3 -3
- data/lib/cem_acpt/logging.rb +17 -1
- data/lib/cem_acpt/provision/terraform/linux.rb +2 -2
- data/lib/cem_acpt/test_data.rb +2 -0
- data/lib/cem_acpt/test_runner/log_formatter/base.rb +73 -0
- data/lib/cem_acpt/test_runner/log_formatter/bolt_error_formatter.rb +65 -0
- data/lib/cem_acpt/test_runner/log_formatter/bolt_output_formatter.rb +54 -0
- data/lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb +64 -0
- data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +17 -30
- data/lib/cem_acpt/test_runner/log_formatter/goss_error_formatter.rb +31 -0
- data/lib/cem_acpt/test_runner/log_formatter/standard_error_formatter.rb +35 -0
- data/lib/cem_acpt/test_runner/log_formatter.rb +17 -5
- data/lib/cem_acpt/test_runner/test_results.rb +150 -0
- data/lib/cem_acpt/test_runner.rb +153 -53
- data/lib/cem_acpt/utils/files.rb +189 -0
- data/lib/cem_acpt/utils/finalizer_queue.rb +73 -0
- data/lib/cem_acpt/utils/shell.rb +13 -4
- data/lib/cem_acpt/version.rb +1 -1
- data/sample_config.yaml +13 -0
- metadata +41 -5
- data/lib/cem_acpt/test_runner/log_formatter/error_formatter.rb +0 -33
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative 'base'
|
5
|
+
|
6
|
+
module CemAcpt
|
7
|
+
module TestRunner
|
8
|
+
module LogFormatter
|
9
|
+
# Formats the results of a Bolt::SummaryResults object
|
10
|
+
class BoltSummaryResultsFormatter < Base
|
11
|
+
def initialize(config, instance_names_ips, subject: nil)
|
12
|
+
super(subject)
|
13
|
+
@config = config
|
14
|
+
@instance_names_ips = instance_names_ips
|
15
|
+
end
|
16
|
+
|
17
|
+
def summary(response = nil)
|
18
|
+
super(response)
|
19
|
+
[
|
20
|
+
"SUMMARY: #{test_name(log_subject)}:",
|
21
|
+
normalize_whitespace(log_subject.summary),
|
22
|
+
].join(' ')
|
23
|
+
end
|
24
|
+
|
25
|
+
def results(response = nil)
|
26
|
+
super(response)
|
27
|
+
log_subject.each_with_object([]) do |res, ary|
|
28
|
+
res.results.each do |r|
|
29
|
+
header = [
|
30
|
+
"#{success_str(r.success?).capitalize}: #{r.name}",
|
31
|
+
"action: #{r.action}",
|
32
|
+
"target: #{name_from_ip(r.target)}",
|
33
|
+
"object: #{r.object}",
|
34
|
+
"status: #{r.status}",
|
35
|
+
]
|
36
|
+
parts = [
|
37
|
+
header.join(', '),
|
38
|
+
"validation results:\n#{JSON.pretty_generate(r.failed_validation_results)}",
|
39
|
+
]
|
40
|
+
if CemAcpt::Logging.verbose?
|
41
|
+
parts << "command result:\n#{JSON.pretty_generate(r.command_result.to_h)}"
|
42
|
+
end
|
43
|
+
parts << r.error if r.error?
|
44
|
+
if r.respond_to?(:details) && !r.details&.empty?
|
45
|
+
parts << "details:\n#{JSON.pretty_generate(r.details)}\n"
|
46
|
+
end
|
47
|
+
ary << parts.join("\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def host_name(response = nil)
|
53
|
+
super(response)
|
54
|
+
log_subject.items.map { |i| name_from_ip(i.target) }.compact.uniq.join(', ')
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_name(response = nil)
|
58
|
+
super(response)
|
59
|
+
'Bolt tests'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,53 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'base'
|
4
|
+
|
3
5
|
module CemAcpt
|
4
6
|
module TestRunner
|
5
7
|
module LogFormatter
|
6
8
|
# Formats the results of a Goss action
|
7
|
-
class GossActionResponse
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(config, instance_names_ips)
|
9
|
+
class GossActionResponse < Base
|
10
|
+
def initialize(config, instance_names_ips, subject: nil)
|
11
|
+
super(subject)
|
11
12
|
@config = config
|
12
13
|
@instance_names_ips = instance_names_ips
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def to_s
|
20
|
-
"#<#{self.class.name}:0x#{object_id.to_s(16)}>"
|
21
|
-
end
|
22
|
-
|
23
|
-
def summary(response)
|
24
|
-
new_summary_message(response)
|
16
|
+
def summary(response = nil)
|
17
|
+
super(response)
|
18
|
+
new_summary_message(log_subject)
|
25
19
|
end
|
26
20
|
|
27
|
-
def results(response)
|
28
|
-
|
21
|
+
def results(response = nil)
|
22
|
+
super(response)
|
23
|
+
new_results_message(log_subject.results)
|
29
24
|
end
|
30
25
|
|
31
|
-
def host_name(response)
|
32
|
-
|
26
|
+
def host_name(response = nil)
|
27
|
+
super(response)
|
28
|
+
name_from_ip(log_subject.host)
|
33
29
|
end
|
34
30
|
|
35
|
-
def test_name(response)
|
36
|
-
|
31
|
+
def test_name(response = nil)
|
32
|
+
super(response)
|
33
|
+
test_from_ip(log_subject.host)
|
37
34
|
end
|
38
35
|
|
39
36
|
private
|
40
37
|
|
41
|
-
def normalize_whitespace(str)
|
42
|
-
raise ArgumentError, 'str must be a String' unless str.is_a?(String)
|
43
|
-
|
44
|
-
str.gsub(%r{(\n|\r|\t)}, ' ').gsub(%r{\s{2,}}, ' ').strip
|
45
|
-
end
|
46
|
-
|
47
|
-
def success_str(success)
|
48
|
-
success ? 'passed' : 'failed'
|
49
|
-
end
|
50
|
-
|
51
38
|
def name_from_ip(ip)
|
52
39
|
@instance_names_ips.each do |name, val|
|
53
40
|
return name if val['ip'] == ip
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module CemAcpt
|
6
|
+
module TestRunner
|
7
|
+
module LogFormatter
|
8
|
+
class GossErrorFormatter < Base
|
9
|
+
def summary(subject = nil)
|
10
|
+
super(subject)
|
11
|
+
"Error: #{log_subject.summary}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def results(response = nil)
|
15
|
+
super(response)
|
16
|
+
[log_subject.summary, log_subject.results.join("\n")]
|
17
|
+
end
|
18
|
+
|
19
|
+
def host_name(response = nil)
|
20
|
+
super(response)
|
21
|
+
"Error: #{log_subject.error.class.name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_name(response = nil)
|
25
|
+
super(response)
|
26
|
+
"Error: #{log_subject.error.class.name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module CemAcpt
|
6
|
+
module TestRunner
|
7
|
+
module LogFormatter
|
8
|
+
class StandardErrorFormatter < Base
|
9
|
+
def summary(subject = nil)
|
10
|
+
super(subject)
|
11
|
+
"Error: #{log_subject.detailed_message}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def results(response = nil)
|
15
|
+
super(response)
|
16
|
+
[
|
17
|
+
"Error: #{log_subject.class.name}",
|
18
|
+
log_subject.detailed_message,
|
19
|
+
log_subject.backtrace.join("\n"),
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
def host_name(response = nil)
|
24
|
+
super(response)
|
25
|
+
"Error: #{log_subject.class.name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_name(response = nil)
|
29
|
+
super(response)
|
30
|
+
"Error: #{log_subject.class.name}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,17 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'log_formatter/bolt_summary_results_formatter'
|
4
|
+
require_relative 'log_formatter/bolt_output_formatter'
|
3
5
|
require_relative 'log_formatter/goss_action_response'
|
4
|
-
require_relative 'log_formatter/
|
6
|
+
require_relative 'log_formatter/goss_error_formatter'
|
7
|
+
require_relative 'log_formatter/standard_error_formatter'
|
5
8
|
|
6
9
|
module CemAcpt
|
7
10
|
module TestRunner
|
8
11
|
# Holds classes for formatting test runner results
|
9
12
|
module LogFormatter
|
10
|
-
def self.new_formatter(result, *args, **
|
11
|
-
|
12
|
-
|
13
|
+
def self.new_formatter(result, *args, **_kwargs)
|
14
|
+
case result
|
15
|
+
when CemAcpt::Goss::Api::ActionResponse
|
16
|
+
if result.error?
|
17
|
+
GossErrorFormatter.new(result)
|
18
|
+
else
|
19
|
+
GossActionResponse.new(*args, subject: result)
|
20
|
+
end
|
21
|
+
when CemAcpt::Bolt::SummaryResults
|
22
|
+
BoltSummaryResultsFormatter.new(*args, subject: result)
|
23
|
+
when StandardError
|
24
|
+
StandardErrorFormatter.new(result)
|
13
25
|
else
|
14
|
-
|
26
|
+
raise ArgumentError, "result must be a CemAcpt::Goss::Api::ActionResponse, CemAcpt::Bolt::SummaryResults, or StandardError, got #{result.class}"
|
15
27
|
end
|
16
28
|
end
|
17
29
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'log_formatter'
|
4
|
+
|
5
|
+
module CemAcpt
|
6
|
+
module TestRunner
|
7
|
+
# Namespace for all test result classes and methods
|
8
|
+
module TestResults
|
9
|
+
def self.new(config = nil, instance_names_ips = nil)
|
10
|
+
Results.new(config, instance_names_ips)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Class that manages test results
|
14
|
+
class Results
|
15
|
+
attr_accessor :config, :instance_names_ips
|
16
|
+
|
17
|
+
def initialize(config = nil, instance_names_ips = nil)
|
18
|
+
@results_queue = Queue.new
|
19
|
+
@config = config
|
20
|
+
@instance_names_ips = instance_names_ips
|
21
|
+
end
|
22
|
+
|
23
|
+
def combine(other)
|
24
|
+
new_res_queue_data = (to_a + other.to_a).compact.uniq
|
25
|
+
@results_queue = Queue.new(new_res_queue_data)
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def <<(result)
|
31
|
+
case result
|
32
|
+
when CemAcpt::Goss::Api::ActionResponse, CemAcpt::Bolt::SummaryResults
|
33
|
+
@results_queue << TestActionResult.new(result, new_formatter(result))
|
34
|
+
when StandardError
|
35
|
+
@results_queue << TestErrorActionResult.new(result, new_formatter(result))
|
36
|
+
else
|
37
|
+
raise ArgumentError, "result must be a CemAcpt::Goss::Api::ActionResponse, CemAcpt::Bolt::SummaryResults, or StandardError, got #{result.class}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_a
|
42
|
+
results = []
|
43
|
+
@results_queue.close unless @results_queue.closed?
|
44
|
+
while (r = @results_queue.pop)
|
45
|
+
results << r
|
46
|
+
end
|
47
|
+
@results_queue = Queue.new(results)
|
48
|
+
results
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
52
|
+
@results_queue.send(method_name, *args, **kwargs, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def respond_to_missing?(method_name, include_private = false)
|
56
|
+
@results_queue.respond_to?(method_name, include_private) || super
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def new_formatter(result)
|
62
|
+
CemAcpt::TestRunner::LogFormatter.new_formatter(result, @config, @instance_names_ips)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Wrapper class for the result of an action. Provides a common interface for
|
67
|
+
# getting reportable data from the result.
|
68
|
+
class TestActionResult
|
69
|
+
attr_reader :result, :log_formatter
|
70
|
+
|
71
|
+
def initialize(result, log_formatter = nil)
|
72
|
+
@result = result
|
73
|
+
@log_formatter = log_formatter
|
74
|
+
end
|
75
|
+
|
76
|
+
def error?
|
77
|
+
(@result.respond_to?(:error?) && @result.error?) || false
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
81
|
+
if @result.respond_to?(method_name)
|
82
|
+
@result.send(method_name, *args, **kwargs, &block)
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def respond_to_missing?(method_name, include_private = false)
|
89
|
+
@result.respond_to?(method_name, include_private) || super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Wrapper class for an error raised during an action.
|
94
|
+
class TestErrorActionResult
|
95
|
+
attr_reader :error, :log_formatter
|
96
|
+
|
97
|
+
def initialize(error, log_formatter = nil)
|
98
|
+
@error = error
|
99
|
+
@log_formatter = log_formatter
|
100
|
+
end
|
101
|
+
alias result error
|
102
|
+
|
103
|
+
def error?
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def success?
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
"#<#{@error.class.name}:0x#{@error.object_id.to_s(16)}>"
|
113
|
+
end
|
114
|
+
|
115
|
+
def inspect
|
116
|
+
to_s
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_h
|
120
|
+
{
|
121
|
+
class: @error.class.name,
|
122
|
+
message: @error.message,
|
123
|
+
backtrace: @error.backtrace,
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def status
|
128
|
+
1
|
129
|
+
end
|
130
|
+
alias http_status status
|
131
|
+
|
132
|
+
def results
|
133
|
+
[summary, "Class: #{@error.class.name}", @error.backtrace.join("\n")]
|
134
|
+
end
|
135
|
+
|
136
|
+
def results?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
def summary
|
141
|
+
@error.message
|
142
|
+
end
|
143
|
+
|
144
|
+
def summary?
|
145
|
+
true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|