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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +0 -3
  3. data/Gemfile.lock +9 -1
  4. data/README.md +95 -13
  5. data/cem_acpt.gemspec +2 -1
  6. data/lib/cem_acpt/action_result.rb +8 -2
  7. data/lib/cem_acpt/actions.rb +153 -0
  8. data/lib/cem_acpt/bolt/cmd/base.rb +174 -0
  9. data/lib/cem_acpt/bolt/cmd/output.rb +315 -0
  10. data/lib/cem_acpt/bolt/cmd/task.rb +59 -0
  11. data/lib/cem_acpt/bolt/cmd.rb +22 -0
  12. data/lib/cem_acpt/bolt/errors.rb +49 -0
  13. data/lib/cem_acpt/bolt/helpers.rb +52 -0
  14. data/lib/cem_acpt/bolt/inventory.rb +62 -0
  15. data/lib/cem_acpt/bolt/project.rb +38 -0
  16. data/lib/cem_acpt/bolt/summary_results.rb +96 -0
  17. data/lib/cem_acpt/bolt/tasks.rb +181 -0
  18. data/lib/cem_acpt/bolt/tests.rb +415 -0
  19. data/lib/cem_acpt/bolt/yaml_file.rb +74 -0
  20. data/lib/cem_acpt/bolt.rb +142 -0
  21. data/lib/cem_acpt/cli.rb +6 -0
  22. data/lib/cem_acpt/config/base.rb +4 -0
  23. data/lib/cem_acpt/config/cem_acpt.rb +7 -1
  24. data/lib/cem_acpt/core_ext.rb +25 -0
  25. data/lib/cem_acpt/goss/api/action_response.rb +4 -0
  26. data/lib/cem_acpt/goss/api.rb +23 -25
  27. data/lib/cem_acpt/logging/formatter.rb +3 -3
  28. data/lib/cem_acpt/logging.rb +17 -1
  29. data/lib/cem_acpt/test_data.rb +2 -0
  30. data/lib/cem_acpt/test_runner/log_formatter/base.rb +73 -0
  31. data/lib/cem_acpt/test_runner/log_formatter/bolt_error_formatter.rb +65 -0
  32. data/lib/cem_acpt/test_runner/log_formatter/bolt_output_formatter.rb +54 -0
  33. data/lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb +64 -0
  34. data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +17 -30
  35. data/lib/cem_acpt/test_runner/log_formatter/goss_error_formatter.rb +31 -0
  36. data/lib/cem_acpt/test_runner/log_formatter/standard_error_formatter.rb +35 -0
  37. data/lib/cem_acpt/test_runner/log_formatter.rb +17 -5
  38. data/lib/cem_acpt/test_runner/test_results.rb +150 -0
  39. data/lib/cem_acpt/test_runner.rb +153 -53
  40. data/lib/cem_acpt/utils/files.rb +189 -0
  41. data/lib/cem_acpt/utils/finalizer_queue.rb +73 -0
  42. data/lib/cem_acpt/utils/shell.rb +13 -4
  43. data/lib/cem_acpt/version.rb +1 -1
  44. data/sample_config.yaml +13 -0
  45. metadata +41 -5
  46. 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
- INDENT = ' '
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 inspect
16
- to_s
17
- end
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
- new_results_message(response.results)
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
- name_from_ip(response.host)
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
- test_from_ip(response.host)
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/error_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, **kwargs)
11
- if result.error?
12
- ErrorFormatter.new
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
- GossActionResponse.new(*args)
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
@@ -2,7 +2,8 @@
2
2
 
3
3
  require 'fileutils'
4
4
  require 'securerandom'
5
- require_relative 'action_result'
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
- @results = []
31
- @http_statuses = []
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
- @run_data[:private_key], @run_data[:public_key], @run_data[:known_hosts] = new_ephemeral_ssh_keys
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
- @results = run_tests(@instance_names_ips.map { |_, v| v['ip'] },
83
- config.get('actions.only'),
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 << ActionResult.new(e)
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
- CemAcpt::Utils.package_win_module(config.get('module_dir'))
124
- else
125
- CemAcpt::Utils::Puppet.build_module_package(config.get('module_dir'))
126
- end
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(hosts, only_actions, except_actions)
198
- logger.info('CemAcpt::TestRunner') { 'Running tests...' }
199
- logger.verbose('CemAcpt::TestRunner') { "Hosts: #{hosts}" }
200
- logger.verbose('CemAcpt::TestRunner') { "Only actions: #{only_actions}" }
201
- logger.verbose('CemAcpt::TestRunner') { "Except actions: #{except_actions}" }
202
- api_results = CemAcpt::Goss::Api.run_actions_async(hosts,
203
- only: only_actions || [],
204
- except: except_actions || [])
205
- res = []
206
- api_results.close unless api_results.closed?
207
- while (r = api_results.pop)
208
- res << ActionResult.new(r)
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
- res
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
- @http_statuses << result.http_status
314
+ @statuses << result.status
222
315
  log_test_result(result)
223
316
  end
224
- if @http_statuses.empty?
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 = (@http_statuses.any? { |s| s.to_i != 200 }) ? 1 : 0
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
- result_log_formatter = LogFormatter.new_formatter(result, config, @instance_names_ips)
237
- logger.start_ci_group("Test results for #{result_log_formatter.test_name(result)}")
238
- return log_error_test_result(result_log_formatter, result) if result.error?
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
- logger.info(result_log_formatter.summary(result))
241
- formatted_results = result_log_formatter.results(result)
242
- formatted_results.each do |r|
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(formatter, result)
256
- logger.fatal { formatter.results(result).join("\n") }
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