cem_acpt 0.8.8 → 0.9.0
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/logging/formatter.rb +3 -3
- data/lib/cem_acpt/logging.rb +17 -1
- 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
@@ -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
|
data/lib/cem_acpt/test_runner.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'securerandom'
|
5
|
-
require_relative '
|
5
|
+
require_relative 'actions'
|
6
|
+
require_relative 'bolt'
|
6
7
|
require_relative 'goss'
|
7
8
|
require_relative 'logging'
|
8
9
|
require_relative 'platform'
|
@@ -11,6 +12,7 @@ require_relative 'test_data'
|
|
11
12
|
require_relative 'utils'
|
12
13
|
require_relative 'version'
|
13
14
|
require_relative 'test_runner/log_formatter'
|
15
|
+
require_relative 'test_runner/test_results'
|
14
16
|
|
15
17
|
module CemAcpt
|
16
18
|
# Namespace for all Runner-related classes and modules
|
@@ -19,6 +21,8 @@ module CemAcpt
|
|
19
21
|
class Runner
|
20
22
|
include CemAcpt::Logging
|
21
23
|
|
24
|
+
SUCCESS_STATUS = [200, 0].freeze
|
25
|
+
|
22
26
|
attr_reader :duration, :exit_code
|
23
27
|
attr_accessor :run_data # This is opened up mainly for windows use.
|
24
28
|
|
@@ -27,8 +31,9 @@ module CemAcpt
|
|
27
31
|
@run_data = {}
|
28
32
|
@duration = 0
|
29
33
|
@exit_code = 0
|
30
|
-
@
|
31
|
-
@
|
34
|
+
@bolt_test_runner = nil
|
35
|
+
@results = CemAcpt::TestRunner::TestResults.new
|
36
|
+
@statuses = []
|
32
37
|
@provisioned = false
|
33
38
|
@destroyed = false
|
34
39
|
end
|
@@ -42,23 +47,14 @@ module CemAcpt
|
|
42
47
|
end
|
43
48
|
|
44
49
|
def run
|
45
|
-
@run_data = {}
|
46
50
|
@start_time = Time.now
|
47
51
|
module_dir = config.get('module_dir')
|
48
52
|
@old_dir = Dir.pwd
|
49
53
|
Dir.chdir(module_dir)
|
54
|
+
configure_actions
|
50
55
|
logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run started at #{@start_time}")
|
51
56
|
logger.info('CemAcpt::TestRunner') { "Using module directory: #{module_dir}..." }
|
52
|
-
|
53
|
-
logger.info('CemAcpt::TestRunner') { 'Created ephemeral SSH key pair...' }
|
54
|
-
@run_data[:module_package_path] = build_module_package
|
55
|
-
logger.info('CemAcpt::TestRunner') { "Created module package: #{@run_data[:module_package_path]}..." }
|
56
|
-
@run_data[:test_data] = new_test_data
|
57
|
-
logger.info('CemAcpt::TestRunner') { 'Created test data...' }
|
58
|
-
logger.verbose('CemAcpt::TestRunner') { "Test data: #{@run_data[:test_data]}" }
|
59
|
-
@run_data[:nodes] = new_node_data
|
60
|
-
logger.info('CemAcpt::TestRunner') { 'Created node data...' }
|
61
|
-
logger.verbose('CemAcpt::TestRunner') { "Node data: #{@run_data[:nodes]}" }
|
57
|
+
pre_provision_test_nodes
|
62
58
|
provision_test_nodes
|
63
59
|
@instance_names_ips = provisioner_output
|
64
60
|
logger.info('CemAcpt::TestRunner') { "Instance names and IPs class: #{@instance_names_ips.class}" }
|
@@ -79,18 +75,17 @@ module CemAcpt
|
|
79
75
|
win_node.run
|
80
76
|
end
|
81
77
|
end
|
82
|
-
@
|
83
|
-
|
84
|
-
config.get('actions.except'))
|
78
|
+
@hosts = @instance_names_ips.map { |_, v| v['ip'] }
|
79
|
+
run_tests
|
85
80
|
rescue StandardError => e
|
86
81
|
logger.error('CemAcpt::TestRunner') { 'Run failed due to error...' }
|
87
|
-
@results <<
|
82
|
+
@results << e
|
88
83
|
ensure
|
89
84
|
logger.end_ci_group
|
90
85
|
clean_up
|
91
86
|
process_test_results
|
92
87
|
Dir.chdir(@old_dir) if @old_dir
|
93
|
-
@results
|
88
|
+
@results.to_a
|
94
89
|
end
|
95
90
|
|
96
91
|
def clean_up(_trap_context = false)
|
@@ -117,13 +112,36 @@ module CemAcpt
|
|
117
112
|
|
118
113
|
attr_reader :config
|
119
114
|
|
115
|
+
# Configures the actions to run based on the config
|
116
|
+
def configure_actions
|
117
|
+
logger.info('CemAcpt::TestRunner') { 'Configuring and registering actions...' }
|
118
|
+
goss_actions = CemAcpt::Goss::Api::ACTIONS.keys
|
119
|
+
CemAcpt::Actions.configure(config) do |c|
|
120
|
+
c.register_group(:goss, order: 0).register_action(goss_actions.first) do |context|
|
121
|
+
run_goss_tests(context)
|
122
|
+
end
|
123
|
+
goss_actions[1..-1].each { |a| c[:goss].register_action(a) }
|
124
|
+
c.register_group(:bolt, order: 1).register_action(:bolt) do |context|
|
125
|
+
run_bolt_tests(context)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
logger.debug('CemAcpt::TestRunner') { "All actions #{CemAcpt::Actions.config.action_names}" }
|
129
|
+
logger.debug('CemAcpt::TestRunner') { "Only actions: #{CemAcpt::Actions.config.only}" }
|
130
|
+
logger.debug('CemAcpt::TestRunner') { "Except actions: #{CemAcpt::Actions.config.except}" }
|
131
|
+
logger.info('CemAcpt::TestRunner') do
|
132
|
+
"Configured and registered actions, will run actions: #{CemAcpt::Actions.config.action_names.join(', ')}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
120
136
|
# @return [String] The path to the module package
|
121
137
|
def build_module_package
|
122
|
-
if config.get('tests').first.include? 'windows'
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
138
|
+
pkg_path = if config.get('tests').first.include? 'windows'
|
139
|
+
CemAcpt::Utils.package_win_module(config.get('module_dir'))
|
140
|
+
else
|
141
|
+
CemAcpt::Utils::Puppet.build_module_package(config.get('module_dir'))
|
142
|
+
end
|
143
|
+
logger.info('CemAcpt::TestRunner') { "Created module package: #{pkg_path}..." }
|
144
|
+
pkg_path
|
127
145
|
end
|
128
146
|
|
129
147
|
# @return [Array<String>] The paths to the ssh private key, public key, and known hosts file
|
@@ -131,7 +149,9 @@ module CemAcpt
|
|
131
149
|
return [nil, nil, nil] if config.get('no_ephemeral_ssh_key')
|
132
150
|
|
133
151
|
logger.info('CemAcpt::TestRunner') { 'Creating ephemeral SSH keys...' }
|
134
|
-
CemAcpt::Utils::SSH::Ephemeral.create
|
152
|
+
pri, pub, kh = CemAcpt::Utils::SSH::Ephemeral.create
|
153
|
+
logger.info('CemAcpt::TestRunner') { 'Created ephemeral SSH key pair...' }
|
154
|
+
[pri, pub, kh]
|
135
155
|
end
|
136
156
|
|
137
157
|
def clean_ephemeral_ssh_keys
|
@@ -143,12 +163,45 @@ module CemAcpt
|
|
143
163
|
|
144
164
|
def new_test_data
|
145
165
|
logger.debug('CemAcpt::TestRunner') { 'Creating new test data...' }
|
146
|
-
CemAcpt::TestData.acceptance_test_data(config)
|
166
|
+
tdata = CemAcpt::TestData.acceptance_test_data(config)
|
167
|
+
logger.info('CemAcpt::TestRunner') { 'Created test data...' }
|
168
|
+
logger.verbose('CemAcpt::TestRunner') { "Test data:\n#{tdata}" }
|
169
|
+
tdata
|
147
170
|
end
|
148
171
|
|
149
172
|
def new_node_data
|
150
173
|
logger.debug('CemAcpt::TestRunner') { 'Creating new node data...' }
|
151
|
-
CemAcpt::Platform.use(config.get('platform.name'), config, @run_data)
|
174
|
+
ndata = CemAcpt::Platform.use(config.get('platform.name'), config, @run_data)
|
175
|
+
logger.info('CemAcpt::TestRunner') { 'Created node data...' }
|
176
|
+
logger.verbose('CemAcpt::TestRunner') { "Node data:\n#{ndata}" }
|
177
|
+
ndata
|
178
|
+
end
|
179
|
+
|
180
|
+
# Runs all methods that are needed to prep data and environment for the provision_test_nodes method
|
181
|
+
def pre_provision_test_nodes
|
182
|
+
logger.info('CemAcpt::TestRunner') { 'Pre-provisioning test nodes...' }
|
183
|
+
logger.info('CemAcpt::TestRunner') { 'Creating initial run data...' }
|
184
|
+
@run_data = {}
|
185
|
+
@run_data[:private_key], @run_data[:public_key], @run_data[:known_hosts] = new_ephemeral_ssh_keys
|
186
|
+
@run_data[:module_package_path] = build_module_package
|
187
|
+
@run_data[:test_data] = new_test_data
|
188
|
+
@run_data[:nodes] = new_node_data
|
189
|
+
logger.verbose('CemAcpt::TestRunner') { "Initial run data:\n#{@run_data}" }
|
190
|
+
logger.info('CemAcpt::TestRunner') { 'Created initial run data...' }
|
191
|
+
setup_bolt
|
192
|
+
end
|
193
|
+
|
194
|
+
def setup_bolt
|
195
|
+
logger.info('CemAcpt::TestRunner') { 'Setting up Bolt...' }
|
196
|
+
@bolt_test_runner = CemAcpt::Bolt::TestRunner.new(config, run_data: @run_data)
|
197
|
+
@bolt_test_runner.setup!
|
198
|
+
return unless @bolt_test_runner.tests.to_a.empty?
|
199
|
+
|
200
|
+
if !only_actions.empty? && only_actions.include?('bolt')
|
201
|
+
raise 'No Bolt tests to run and only bolt action was specified'
|
202
|
+
end
|
203
|
+
|
204
|
+
logger.warn('CemAcpt::TestRunner') { 'No Bolt tests to run' }
|
152
205
|
end
|
153
206
|
|
154
207
|
def provision_test_nodes
|
@@ -194,20 +247,60 @@ module CemAcpt
|
|
194
247
|
logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
|
195
248
|
end
|
196
249
|
|
197
|
-
def run_tests
|
198
|
-
logger.info('CemAcpt::TestRunner') { '
|
199
|
-
logger.verbose('CemAcpt::TestRunner') { "Hosts: #{hosts}" }
|
200
|
-
logger.verbose('CemAcpt::TestRunner') { "Only actions: #{
|
201
|
-
logger.verbose('CemAcpt::TestRunner') { "Except actions: #{
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
250
|
+
def run_tests
|
251
|
+
logger.info('CemAcpt::TestRunner') { 'Preparing to run tests...' }
|
252
|
+
logger.verbose('CemAcpt::TestRunner') { "Hosts: #{@hosts}" }
|
253
|
+
logger.verbose('CemAcpt::TestRunner') { "Only actions: #{CemAcpt::Actions.only}" }
|
254
|
+
logger.verbose('CemAcpt::TestRunner') { "Except actions: #{CemAcpt::Actions.except}" }
|
255
|
+
@results = CemAcpt::TestRunner::TestResults.new(config, @instance_names_ips)
|
256
|
+
CemAcpt::Actions.execute
|
257
|
+
end
|
258
|
+
|
259
|
+
def run_goss_tests(context = {})
|
260
|
+
logger.info('CemAcpt::TestRunner') { 'Running goss tests...' }
|
261
|
+
context[:hosts] = @hosts
|
262
|
+
context[:results] = @results
|
263
|
+
CemAcpt::Goss::Api.run_actions_async(context)
|
264
|
+
end
|
265
|
+
|
266
|
+
def run_bolt_tests(_context = {})
|
267
|
+
logger.info('CemAcpt::TestRunner') { 'Running Bolt tests...' }
|
268
|
+
# If the Bolt config has tests:only or tests:ignore lists, we need to filter the hosts
|
269
|
+
# based on their associated tests.
|
270
|
+
@bolt_test_runner.hosts = filtered_bolt_hosts
|
271
|
+
@bolt_test_runner.run
|
272
|
+
@results << @bolt_test_runner.results
|
273
|
+
end
|
274
|
+
|
275
|
+
def filtered_bolt_hosts
|
276
|
+
tests_only = config.get('bolt.tests.only')
|
277
|
+
tests_only_unset = tests_only.nil? || tests_only.empty?
|
278
|
+
logger.debug('CemAcpt::TestRunner') { "Bolt tests only: #{tests_only}" } unless tests_only_unset
|
279
|
+
tests_ignore = config.get('bolt.tests.ignore')
|
280
|
+
tests_ignore_unset = tests_ignore.nil? || tests_ignore.empty?
|
281
|
+
logger.debug('CemAcpt::TestRunner') { "Bolt tests ignore: #{tests_ignore}" } unless tests_ignore_unset
|
282
|
+
return @instance_names_ips.map { |_, v| v['ip'] } if tests_only_unset && tests_ignore_unset
|
283
|
+
|
284
|
+
logger.debug('CemAcpt::TestRunner') { 'Filtering Bolt hosts...' }
|
285
|
+
filtered_hosts = []
|
286
|
+
@instance_names_ips.each do |_, v|
|
287
|
+
host = v['ip']
|
288
|
+
test_name = v['test_name']
|
289
|
+
in_only = !tests_only_unset && tests_only.include?(test_name)
|
290
|
+
in_ignore = !tests_ignore_unset && tests_ignore.include?(test_name)
|
291
|
+
if in_only || !in_ignore
|
292
|
+
filtered_hosts << host
|
293
|
+
logger.debug('CemAcpt::TestRunner') { "Added host #{host} to filtered hosts" }
|
294
|
+
else
|
295
|
+
logger.debug('CemAcpt::TestRunner') do
|
296
|
+
"Not adding host #{host} to filtered hosts. In only? #{in_only}; In ignore? #{in_ignore}"
|
297
|
+
end
|
298
|
+
end
|
209
299
|
end
|
210
|
-
|
300
|
+
filtered_hosts.compact!
|
301
|
+
filtered_hosts.uniq!
|
302
|
+
logger.debug('CemAcpt::TestRunner') { "Filtered hosts: #{filtered_hosts}" }
|
303
|
+
filtered_hosts
|
211
304
|
end
|
212
305
|
|
213
306
|
def process_test_results
|
@@ -218,14 +311,14 @@ module CemAcpt
|
|
218
311
|
logger.info('CemAcpt::TestRunner') { "Processing #{@results.size} test result(s)..." }
|
219
312
|
until @results.empty?
|
220
313
|
result = @results.pop
|
221
|
-
@
|
314
|
+
@statuses << result.status
|
222
315
|
log_test_result(result)
|
223
316
|
end
|
224
|
-
if @
|
317
|
+
if @statuses.empty?
|
225
318
|
logger.error('CemAcpt::TestRunner') { 'No test results to process' }
|
226
319
|
@exit_code = 1
|
227
320
|
else
|
228
|
-
@exit_code = (@
|
321
|
+
@exit_code = (@statuses.any? { |s| SUCCESS_STATUS.include?(s.to_i) }) ? 1 : 0
|
229
322
|
end
|
230
323
|
end
|
231
324
|
@duration = Time.now - @start_time
|
@@ -233,13 +326,22 @@ module CemAcpt
|
|
233
326
|
end
|
234
327
|
|
235
328
|
def log_test_result(result)
|
236
|
-
|
237
|
-
|
238
|
-
|
329
|
+
logger.start_ci_group("Test results for #{result.log_formatter.test_name}")
|
330
|
+
case result
|
331
|
+
when CemAcpt::TestRunner::TestResults::TestErrorActionResult
|
332
|
+
log_error_test_result(result)
|
333
|
+
when CemAcpt::TestRunner::TestResults::TestActionResult
|
334
|
+
log_action_test_result(result)
|
335
|
+
else
|
336
|
+
raise ArgumentError, "result must be a CemAcpt::TestRunner::TestResults::TestActionResult or CemAcpt::TestRunner::TestResults::TestErrorActionResult, got #{result.class}"
|
337
|
+
end
|
338
|
+
ensure
|
339
|
+
logger.end_ci_group
|
340
|
+
end
|
239
341
|
|
240
|
-
|
241
|
-
|
242
|
-
|
342
|
+
def log_action_test_result(result)
|
343
|
+
logger.info { result.log_formatter.summary }
|
344
|
+
result.log_formatter.results.each do |r|
|
243
345
|
if r.start_with?('Passed:')
|
244
346
|
logger.verbose { r }
|
245
347
|
elsif r.start_with?('Skipped:')
|
@@ -248,12 +350,10 @@ module CemAcpt
|
|
248
350
|
logger.error { r }
|
249
351
|
end
|
250
352
|
end
|
251
|
-
ensure
|
252
|
-
logger.end_ci_group
|
253
353
|
end
|
254
354
|
|
255
|
-
def log_error_test_result(
|
256
|
-
logger.fatal {
|
355
|
+
def log_error_test_result(result)
|
356
|
+
logger.fatal { result.log_formatter.results.join("\n") }
|
257
357
|
end
|
258
358
|
|
259
359
|
# Upload the cem_windows module to the bucket if we're testing the cem_windows module
|