cem_acpt 0.3.4-universal-java-17 → 0.3.5-universal-java-17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c289fcb52a1115630ffece04c465e5a8d945bdefdcc99fbf73a800398d2f2bc6
4
- data.tar.gz: aca0f41efe831bf5bb8dc4399cd1a2d771f7bb72fb1ee13eeccb79d59bd45183
3
+ metadata.gz: b62e59f800597758c9a69bc1ede3d3f97a9a68c6c38f0d4705201ac2613feab9
4
+ data.tar.gz: 4d4a0ad559702df7cff661726a962b0bbc278d2cb0ecba48241a67a55345dac6
5
5
  SHA512:
6
- metadata.gz: 65dd7b8c5023304f0c5f9fd371a64cfb157567004d6f93ebdadc76bbf99f50f7483fab575f73fb9c07256cbb0db688ad4d8c10a6b644a8f7670a3441a54e866e
7
- data.tar.gz: 2504bc3dc1ef1ac1a09c16d00c3cd99b08127ccd31f2b9da134f31ffb2dc19730eda580ec893def0a9b183bd4198c5aa8263ea735f50c0b2c21156a5f8f7124d
6
+ metadata.gz: ee72270cae3121c03597e12f91c3fd6134da4ab40ef342cc3f694ea0a5ee2dcaf37363e9430b2610539120e0cf5ea733caf191d916a926e2d29dc17ac8bc7cfb
7
+ data.tar.gz: 79a40b4e74935a7842bffd7b9252aa63d277cc31e49c0a3a10200f647117a1c3e51d4183a3e2e16aa9cc0e1fd185d0ee375177fdc876ca4097e8828884b55f36
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.3.4-universal-java-17)
4
+ cem_acpt (0.3.5-universal-java-17)
5
5
  concurrent-ruby (>= 1.1, < 2.0)
6
6
  deep_merge (>= 1.2, < 2.0)
7
7
  ed25519 (>= 1.2, < 2.0)
@@ -113,6 +113,9 @@ module CemAcpt::Platform::Gcp
113
113
 
114
114
  # Deletes a GCP VM instance.
115
115
  def delete_instance(instance_name)
116
+ ssh_all_known_hosts.each do |kh_file|
117
+ CemAcpt::Platform::Utils::Linux::SSHelper.remove_known_host(vm_alias(instance_name), kh_file)
118
+ end
116
119
  local_exec("compute instances delete #{instance_name} --quiet")
117
120
  rescue StandardError
118
121
  # Ignore errors when deleting instances.
@@ -318,6 +321,14 @@ module CemAcpt::Platform::Gcp
318
321
  "compute.#{vm_describe(vm)['id']}"
319
322
  end
320
323
 
324
+ def ssh_all_known_hosts
325
+ [
326
+ File.join(ENV['HOME'], '.ssh', 'acpt_test_known_hosts'),
327
+ File.join(ENV['HOME'], '.ssh', 'known_hosts'),
328
+ File.join(ENV['HOME'], '.ssh', 'google_compute_known_hosts'),
329
+ ]
330
+ end
331
+
321
332
  # Default options for Net::SSH
322
333
  def default_ssh_opts
323
334
  {
@@ -4,6 +4,43 @@ module CemAcpt
4
4
  module Platform
5
5
  module Utils
6
6
  module Linux
7
+ # Module provides SSH helper methods for Linux platforms
8
+ class SSHelper
9
+ def self.ssh_keygen?
10
+ system('command -v ssh-keygen 2>&1 > /dev/null')
11
+ end
12
+
13
+ def self.remove_known_host(host, file = File.join([ENV['HOME'], '.ssh', 'known_hosts']))
14
+ if File.exist?(file)
15
+ if ssh_keygen?
16
+ system("ssh-keygen -R #{host} -f #{file} 2>&1 > /dev/null")
17
+ else
18
+ remove_known_host_no_keygen(host, file)
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.remove_known_host_no_keygen(host, file = File.join([ENV['HOME'], '.ssh', 'known_hosts']))
24
+ require 'fileutils'
25
+ found = false
26
+ File.open("#{file}.cem_acpt_new", 'w') do |f|
27
+ File.readlines(file).each do |line|
28
+ if line.include?(host)
29
+ found = true
30
+ else
31
+ f.puts(line)
32
+ end
33
+ end
34
+ end
35
+ if found
36
+ FileUtils.mv("#{file}.cem_acpt_new", file)
37
+ else
38
+ FileUtils.rm("#{file}.cem_acpt_new")
39
+ end
40
+ end
41
+ end
42
+
43
+ # Class to parse the /etc/os-release file
7
44
  class OSRelease
8
45
  attr_reader :properties
9
46
 
@@ -21,7 +58,7 @@ module CemAcpt
21
58
 
22
59
  def parse_string(os_release)
23
60
  os_release = os_release.split("\n")
24
- os_release = os_release.map { |line| line.split('=', 2) }.reject { |l| l.length != 2 }
61
+ os_release = os_release.map { |line| line.split('=', 2) }.select { |l| l.length == 2 }
25
62
  os_release.map! { |k, v| [k.downcase, v.delete_prefix('"').delete_suffix('"')] }
26
63
  os_release.to_h
27
64
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../logging'
4
+
5
+ module CemAcpt
6
+ module TestRunner
7
+ # Namespace for TestRunner logging wrapper
8
+ module Logging
9
+ def use_logger(logger, **prefix_parts)
10
+ if logger.nil? || !logger.is_a?(CemAcpt::TestRunner::Logging::Logger)
11
+ CemAcpt::TestRunner::Logging::Logger.new(**prefix_parts)
12
+ else
13
+ this_prefix_parts = logger.prefix_parts.dup.merge(prefix_parts)
14
+ CemAcpt::TestRunner::Logging::Logger.new(**this_prefix_parts)
15
+ end
16
+ end
17
+
18
+ # Class to handle logging for Runner objects
19
+ class Logger
20
+ include CemAcpt::Logging
21
+
22
+ attr_reader :prefix_parts
23
+
24
+ def initialize(**prefix_parts)
25
+ @prefix_parts = prefix_parts
26
+ end
27
+
28
+ def log_prefix
29
+ parts = []
30
+ parts << @prefix_parts[:test] if @prefix_parts.key?(:test)
31
+ parts << @prefix_parts[:node] if @prefix_parts.key?(:node)
32
+ parts << @prefix_parts[:stage] if @prefix_parts.key?(:stage)
33
+ parts << @prefix_parts[:step] if @prefix_parts.key?(:step)
34
+ parts.join(': ')
35
+ end
36
+
37
+ def add_prefix_parts(**parts)
38
+ @prefix_parts.merge!(parts)
39
+ end
40
+
41
+ def debug(msg, no_prefix: false)
42
+ logger.debug(format_msg(msg, no_prefix))
43
+ end
44
+
45
+ def info(msg, no_prefix: false)
46
+ logger.info(format_msg(msg, no_prefix))
47
+ end
48
+
49
+ def warn(msg, no_prefix: false)
50
+ logger.warn(format_msg(msg, no_prefix))
51
+ end
52
+
53
+ def error(msg, no_prefix: false)
54
+ logger.error(format_msg(msg, no_prefix))
55
+ end
56
+
57
+ def fatal(msg, no_prefix: false)
58
+ logger.fatal(format_msg(msg, no_prefix))
59
+ end
60
+
61
+ def <<(msg)
62
+ logger << msg
63
+ end
64
+
65
+ private
66
+
67
+ def format_msg(msg, no_prefix)
68
+ if no_prefix
69
+ msg
70
+ else
71
+ "#{log_prefix}: #{msg}"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -3,8 +3,8 @@
3
3
  require 'concurrent-ruby'
4
4
  require 'open3'
5
5
  require_relative '../context'
6
- require_relative '../logging'
7
6
  require_relative '../puppet_helpers'
7
+ require_relative 'logging'
8
8
  require_relative 'runner'
9
9
 
10
10
  module CemAcpt
@@ -19,7 +19,7 @@ module CemAcpt
19
19
  # creating Runner objects, handling input and output, and exception
20
20
  # handling.
21
21
  class RunHandler
22
- include CemAcpt::Logging
22
+ include CemAcpt::TestRunner::Logging
23
23
 
24
24
  attr_accessor :run_context
25
25
 
@@ -29,6 +29,7 @@ module CemAcpt
29
29
  @module_pkg_path = Concurrent::IVar.new
30
30
  @runners = Concurrent::Array.new
31
31
  @thread_pool = Concurrent::SimpleExecutorService.new
32
+ @logger = use_logger(nil, stage: 'RunHandler')
32
33
  end
33
34
 
34
35
  # Gets the overall exit code for all runners
@@ -38,14 +39,14 @@ module CemAcpt
38
39
 
39
40
  # Runs the acceptance test suites.
40
41
  def run
41
- logger.info('RUN HANDLER: Creating and starting test runners...')
42
+ @logger.info('creating and starting test runners...')
42
43
  create_runners
43
- logger.info("RUN HANDLER: Created #{@runners.length} runners...")
44
+ @logger.info("created #{@runners.length} runners...")
44
45
  @runners.map { |r| @thread_pool.post { r.start } }
45
46
  @thread_pool.shutdown
46
47
  @thread_pool.wait_for_termination
47
48
  @thread_pool = Concurrent::SimpleExecutorService.new
48
- logger.info('Test runners have all exited...')
49
+ @logger.info('test runners have all exited...')
49
50
  rescue StandardError => e
50
51
  handle_fatal_error(e)
51
52
  ensure
@@ -70,17 +71,17 @@ module CemAcpt
70
71
  end
71
72
 
72
73
  def create_runner(node, platform)
73
- logger.info("RUN HANDLER: Creating runner for #{node.test_data[:test_name]} on node #{node.node_name}...")
74
+ @logger.info("creating runner for #{node.test_data[:test_name]} on node #{node.node_name}...")
74
75
  runner = CemAcpt::TestRunner::Runner.new(node, @run_context, platform)
75
76
  runner || runner_creation_error(node)
76
77
  end
77
78
 
78
79
  def runner_creation_error(node)
79
80
  msg = [
80
- "Failed to create runner object for node #{node.node_name}.",
81
+ "failed to create runner object for node #{node.node_name}!",
81
82
  "Cannot run test #{node.test_data[:test_name]}",
82
83
  ].join(' ')
83
- logger.error(msg)
84
+ @logger.error(msg)
84
85
  nil
85
86
  end
86
87
 
@@ -88,22 +89,22 @@ module CemAcpt
88
89
  def handle_test_results
89
90
  @runners.each do |runner|
90
91
  result = runner.run_result.to_h
91
- logger << "::group::{Results for #{runner.node.test_data[:test_name]}}\n"
92
+ @logger << "::group::{Results for #{runner.node.test_data[:test_name]}}\n"
92
93
  if result.key?('summary_line')
93
- logger.info("SUMMARY: #{result['summary_line']} for test #{runner.node.test_data[:test_name]}")
94
+ @logger.info("SUMMARY: #{result['summary_line']} for test #{runner.node.test_data[:test_name]}", no_prefix: true)
94
95
  else
95
96
  handle_runner_error_results(runner)
96
- logger << "::endgroup::\n"
97
+ @logger << "::endgroup::\n"
97
98
  next
98
99
  end
99
100
  unless runner.test_failures?
100
- logger << "::endgroup::\n"
101
+ @logger << "::endgroup::\n"
101
102
  next
102
103
  end
103
104
 
104
105
  if result.key?('examples') # Log errors outside of examples
105
106
  if result['examples'].empty? && !result['messages'].empty?
106
- logger.error(result['messages'].join("\n"))
107
+ @logger.error(result['messages'].join("\n"), no_prefix: true)
107
108
  else
108
109
  handle_example_error_pending_skipped(runner.node.node_name, result['examples'])
109
110
  end
@@ -111,7 +112,7 @@ module CemAcpt
111
112
  handle_runner_error_results(runner)
112
113
  end
113
114
  #debug_test_results(runner) if logger.debug?
114
- logger << "::endgroup::\n"
115
+ @logger << "::endgroup::\n"
115
116
  end
116
117
  end
117
118
 
@@ -121,18 +122,18 @@ module CemAcpt
121
122
  next if e['status'] == 'passed'
122
123
 
123
124
  if e['status'] == 'pending'
124
- logger.info(test_pending_msg(node_name, e))
125
+ @logger.info(test_pending_msg(node_name, e), no_prefix: true)
125
126
  else
126
- logger.error(test_error_msg(node_name, e))
127
+ @logger.error(test_error_msg(node_name, e), no_prefix: true)
127
128
  end
128
129
  end
129
130
  end
130
131
 
131
132
  # Handles logging the results of the runners that errored.
132
133
  def handle_runner_error_results(runner)
133
- logger.error("SUMMARY: Encountered an error with test #{runner.node.test_data[:test_name]} on node #{runner.node.node_name}")
134
+ @logger.error("SUMMARY: Encountered an error with test #{runner.node.test_data[:test_name]} on node #{runner.node.node_name}", no_prefix: true)
134
135
  runner.run_result.to_h.each do |k, v|
135
- logger.error("#{k.upcase}: #{v}")
136
+ @logger.error("#{k.upcase}: #{v}", no_prefix: true)
136
137
  end
137
138
  end
138
139
 
@@ -202,8 +203,8 @@ module CemAcpt
202
203
  # Gracefully handles a fatal error and exits the program.
203
204
  # @param err [StandardError, Exception] the error that caused the fatal error
204
205
  def handle_fatal_error(err)
205
- logger.fatal("RUN HANDLER: Fatal error: #{err.message}")
206
- logger.debug(err.backtrace.join("\n"))
206
+ @logger.fatal("fatal error: #{err.message}")
207
+ @logger.debug(err.backtrace.join("\n"), no_prefix: true)
207
208
  end
208
209
  end
209
210
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'concurrent-ruby'
4
4
  require 'English'
5
- require_relative '../logging'
6
5
  require_relative '../rspec_utils'
6
+ require_relative 'logging'
7
7
  require_relative 'runner_result'
8
8
  require_relative 'runner_workflow_builder'
9
9
  #require_relative 'workflow'
@@ -25,19 +25,15 @@ module CemAcpt
25
25
 
26
26
  class RunnerProvisionError < RunnerStepError; end
27
27
 
28
-
29
-
30
28
  # Runner is a class that runs a single acceptance test suite on a single node.
31
29
  # It is responsible for managing the lifecycle of the test suite and
32
30
  # reporting the results back to the main thread. Runner objects are created
33
31
  # by the RunHandler and then, when started, execute their logic in a thread.
34
32
  class Runner
35
- include CemAcpt::LoggingAsync
33
+ include CemAcpt::TestRunner::Logging
36
34
 
37
35
  attr_reader :node, :node_exists, :run_result
38
36
 
39
- MAX_PROVISION_ATTEMPTS = 3
40
-
41
37
  # @param node [String] the name of the node to run the acceptance test suite on
42
38
  # @param ctx [CemAcpt::RunnerCtx] a cem_acpt Ctx (context) object
43
39
  # @param module_pkg_path [Concurrent::IVar] the path to the module package
@@ -52,18 +48,19 @@ module CemAcpt
52
48
  @provision_start_time = nil
53
49
  @node_exists = false
54
50
  @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
51
+ @logger = use_logger(nil, stage: 'Runner', node: @node.node_name, test: @node.test_data[:test_name])
55
52
  validate!
56
53
  end
57
54
 
58
55
  # Executes test suite steps
59
56
  def start
60
- async_info("Starting test suite for #{@node.node_name}", log_prefix('RUNNER'))
57
+ @logger.info('Starting test suite workflow...')
61
58
  @workflow = new_workflow
62
59
  @workflow.run
63
60
  if @workflow.success?
64
61
  @run_result = @workflow.last_result
65
62
  @workflow.completed_steps.each do |s|
66
- async_info("Step '#{s.name}' completed successfully", log_prefix('RUNNER'))
63
+ @logger.info("Step '#{s.name}' completed successfully")
67
64
  end
68
65
  true
69
66
  else
@@ -72,7 +69,7 @@ module CemAcpt
72
69
  false
73
70
  end
74
71
  rescue StandardError => e
75
- step_error_logging(e)
72
+ step_error_logging(e, :fatal)
76
73
  @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
77
74
  @run_result.from_error(e)
78
75
  end
@@ -89,7 +86,7 @@ module CemAcpt
89
86
  # Builds a new workflow for the runner
90
87
  # @return [CemAcpt::TestRunner::Workflow::Manager] the new workflow
91
88
  def new_workflow
92
- builder = RunnerWorkflowBuilder.new(@node, config: @context.config)
89
+ builder = RunnerWorkflowBuilder.new(@node, @context.config, @logger)
93
90
  builder.add_provision
94
91
  builder.add_sleep(time: 30)
95
92
  builder.add_wait_for_node_ssh
@@ -104,13 +101,15 @@ module CemAcpt
104
101
  builder.workflow
105
102
  end
106
103
 
107
- def step_error_logging(err)
108
- prefix = err.respond_to?(:step) ? log_prefix(err.step.capitalize) : log_prefix('RUNNER')
109
- fatal_msg = ["runner failed: #{err.message}"]
110
- async_fatal(fatal_msg, prefix)
111
- async_debug("Completed steps: #{@completed_steps}", prefix)
112
- async_debug("Failed runner backtrace:\n#{err.backtrace.join("\n")}", prefix)
113
- async_debug("Failed runner test data: #{@node.test_data}", prefix)
104
+ def step_error_logging(err, kind = :error)
105
+ msg = if err.respond_to?(:step)
106
+ "runner failed on step '#{err.step}': #{err.message}"
107
+ else
108
+ "runner failed: #{err.message}"
109
+ end
110
+ @logger.send(kind, msg)
111
+ @logger.debug("failed runner backtrace:\n#{err.backtrace.join("\n")}")
112
+ @logger.debug("failed runner test data: #{@node.test_data}")
114
113
  end
115
114
 
116
115
  def log_prefix(prefix)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../logging'
3
+ require_relative 'logging'
4
4
  require_relative 'workflow'
5
5
 
6
6
  module CemAcpt
@@ -9,15 +9,17 @@ module CemAcpt
9
9
  # @!attribute [r] workflow
10
10
  # @return [CemAcpt::TestRunner::Workflow::Manager] Workflow object
11
11
  class RunnerWorkflowBuilder
12
- include CemAcpt::LoggingAsync
12
+ include CemAcpt::TestRunner::Logging
13
13
 
14
14
  attr_reader :workflow
15
15
 
16
16
  # @param node [CemAcpt::Platform::Base] Initialized node object
17
17
  # @param config [Hash] Context config hash
18
- def initialize(node, config)
18
+ # @param logger [CemAcpt::TestRunner::Logging::Logger] Logger object
19
+ def initialize(node, config, logger = nil)
19
20
  @node = node
20
- @workflow = CemAcpt::TestRunner::Workflow::Manager.new(workflow_manager_opts(config))
21
+ @logger = use_logger(logger, stage: 'Workflow')
22
+ @workflow = CemAcpt::TestRunner::Workflow::Manager.new(workflow_manager_opts(config, logger))
21
23
  @config = config
22
24
  end
23
25
 
@@ -28,7 +30,7 @@ module CemAcpt
28
30
  retryable: false,
29
31
  }
30
32
  @workflow.add_step(:sleep, **opts) do |s|
31
- log_info("sleeping for #{s.opts[:time]} seconds", s)
33
+ s.logger.info("sleeping for #{s.opts[:time]} seconds")
32
34
  sleep(s.opts[:time])
33
35
  end
34
36
  end
@@ -104,7 +106,7 @@ module CemAcpt
104
106
  }.merge(s.opts[:node].node)
105
107
  s.opts[:node_inventory].add(s.opts[:node].node_name, node_desc)
106
108
  s.opts[:node_inventory].save
107
- log_info("node #{s.opts[:node_name]} saved to inventory", s)
109
+ s.logger.info("node #{s.opts[:node_name]} saved to inventory")
108
110
  s.opts[:node_inventory]
109
111
  end
110
112
  end
@@ -169,11 +171,11 @@ module CemAcpt
169
171
  rspec_cmd.execute(pty: false, log_prefix: "RSPEC: #{@node.test_data[:test_name]}")
170
172
  run_result.from_json_file(cmd_opts.format[:json])
171
173
  rescue Errno::EIO => e
172
- log_error("failed to run rspec: #{@node.test_data[:test_name]}: #{$ERROR_INFO}", s)
174
+ s.logger.error("failed to run rspec: #{@node.test_data[:test_name]}: #{$ERROR_INFO}")
173
175
  run_result.from_error(e)
174
176
  rescue StandardError => e
175
- log_error("failed to run rspec: #{@node.test_data[:test_name]}: #{e.message}", s)
176
- log_debug(e.backtrace.join("\n"), s)
177
+ s.logger.error("failed to run rspec: #{@node.test_data[:test_name]}: #{e.message}")
178
+ s.logger.debug(e.backtrace.join("\n"))
177
179
  run_result.from_error(e)
178
180
  end
179
181
  end
@@ -184,53 +186,30 @@ module CemAcpt
184
186
  def add_clean_up(force: false, **kwargs)
185
187
  opts = {
186
188
  node: @node,
187
- config: @config[:config],
189
+ config: @config,
188
190
  force: force,
189
191
  retryable: kwargs[:retryable] || false,
190
192
  }
191
193
  @workflow.add_step(:clean_up, **opts) do |s|
192
194
  if !force && s.opts[:config].get('no_destroy_nodes')
193
- log_info("not destroying node #{s.opts[:node].node_name} because 'no_destroy_nodes' is set to true", s)
195
+ s.logger.info("not destroying node #{s.opts[:node].node_name} because 'no_destroy_nodes' is set to true")
194
196
  else
195
- log_info("destroying node #{s.opts[:node].node_name}", s)
197
+ s.logger.info("destroying node #{s.opts[:node].node_name}")
196
198
  s.opts[:node].destroy
197
- log_info("node #{s.opts[:node].node_name} destroyed", s)
199
+ s.logger.info("node #{s.opts[:node].node_name} destroyed")
198
200
  end
199
201
  end
200
202
  end
201
203
 
202
204
  private
203
205
 
204
- def log_msg(msg, step)
205
- "Step '#{step.name}' on #{@node.node_name}: #{msg}"
206
- end
207
-
208
- def log_debug(msg, step)
209
- async_debug(log_msg(msg, step))
210
- end
211
-
212
- def log_info(msg, step)
213
- async_info(log_msg(msg, step))
214
- end
215
-
216
- def log_warn(msg, step)
217
- async_warn(log_msg(msg, step))
218
- end
219
-
220
- def log_error(msg, step)
221
- async_error(log_msg(msg, step))
222
- end
223
-
224
- def log_fatal(msg, step)
225
- async_fatal(log_msg(msg, step))
226
- end
227
-
228
- def workflow_manager_opts(config)
206
+ def workflow_manager_opts(config, logger = nil)
229
207
  {
230
- retry_max: config[:workflow_retry_max] || 3,
231
- retry_delay: config[:workflow_retry_delay] || 0,
232
- ignore_failures: config[:workflow_ignore_failures] || false,
233
- raise_on_fail: config[:workflow_raise_on_fail] || true,
208
+ retry_max: config.get('workflow.retry_max') || 3,
209
+ retry_delay: config.get('workflow.retry_delay') || 0,
210
+ ignore_failures: config.get('workflow.ignore_failures') || false,
211
+ raise_on_fail: config.get('workflow.raise_on_fail') || true,
212
+ logger: logger,
234
213
  }
235
214
  end
236
215
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../logging'
3
+ require_relative '../logging'
4
4
  require_relative 'step'
5
5
 
6
6
  module CemAcpt
@@ -20,7 +20,7 @@ module CemAcpt
20
20
  # @!attribute [r] steps
21
21
  # @return [Array<Step>] The steps that are part of this workflow
22
22
  class Manager
23
- include CemAcpt::LoggingAsync
23
+ include CemAcpt::TestRunner::Logging
24
24
 
25
25
  attr_reader :completed_steps, :last_error, :last_result, :retry_max, :retry_delay, :steps
26
26
 
@@ -34,6 +34,7 @@ module CemAcpt
34
34
  @retry_delay = opts[:retry_delay] || 0
35
35
  @ignore_failures = opts[:ignore_failures] || false
36
36
  @raise_on_fail = opts[:raise_on_fail] || true
37
+ @logger = use_logger(opts[:logger], stage: 'Workflow')
37
38
  @steps = []
38
39
  @workflow_runs = 0
39
40
  @last_error = nil
@@ -57,8 +58,8 @@ module CemAcpt
57
58
  else
58
59
  name
59
60
  end
60
- step = Step.new(step_name, **kwargs, &block)
61
- @steps << StepState.new(step, @steps.length, **kwargs)
61
+ step = Step.new(step_name, **kwargs.merge(logger: @logger), &block)
62
+ @steps << StepState.new(step, @steps.length, **kwargs.merge(logger: @logger))
62
63
  end
63
64
 
64
65
  # Run the workflow
@@ -92,6 +93,43 @@ module CemAcpt
92
93
 
93
94
  private
94
95
 
96
+ def new_logger(logger)
97
+ if logger.nil?
98
+ logger
99
+ else
100
+ @logger = logger.dup
101
+ @logger.add_prefix_parts(stage: 'Workflow')
102
+ end
103
+ end
104
+
105
+ def log(level, msg)
106
+ if @logger
107
+ @logger.send(level, msg)
108
+ else
109
+ send("async_#{level}".to_sym, msg)
110
+ end
111
+ end
112
+
113
+ def log_debug(msg)
114
+ log(:debug, msg)
115
+ end
116
+
117
+ def log_info(msg)
118
+ log(:info, msg)
119
+ end
120
+
121
+ def log_warn(msg)
122
+ log(:warn, msg)
123
+ end
124
+
125
+ def log_error(msg)
126
+ log(:error, msg)
127
+ end
128
+
129
+ def log_fatal(msg)
130
+ log(:fatal, msg)
131
+ end
132
+
95
133
  # Handles the result of a step
96
134
  # @param [Step] step The step that was run
97
135
  # @param [Object] result The result of the step
@@ -100,26 +138,26 @@ module CemAcpt
100
138
  @last_result = result unless step.name == :clean_up # Don't overwrite the last result with the clean_up step
101
139
  case result
102
140
  when :retry_workflow
103
- async_warn("Step '#{step.name}' failed and requested a workflow retry")
104
- async_debug("Step '#{step.name}' failed with error: #{step.last_error.message}")
105
- async_debug("Step '#{step.name}' failed with error: #{step.last_error.backtrace.join("\n")}")
141
+ log_warn("step '#{step.name}' failed and requested a workflow retry")
142
+ log_debug("step '#{step.name}' failed with error: #{step.last_error.message}")
143
+ log_debug("step '#{step.name}' failed with error: #{step.last_error.backtrace.join("\n")}")
106
144
  @last_error = step.last_error
107
145
  retry_workflow
108
146
  when :fail
109
- async_warn("Step '#{step.name}' failed")
110
- async_debug(step.last_error.message)
111
- async_debug(step.last_error.backtrace.join("\n"))
147
+ log_warn("step '#{step.name}' failed")
148
+ log_debug(step.last_error.message)
149
+ log_debug(step.last_error.backtrace.join("\n"))
112
150
  @last_error = step.last_error
113
151
  if ignore_failures?
114
- async_warn("Ignoring failure of step '#{step.name}'")
152
+ log_warn("ignoring failure of step '#{step.name}'")
115
153
  @completed_steps << step
116
154
  else
117
- async_error("Workflow failed with error: #{@last_error.message}")
155
+ log_error("failed with error: #{@last_error.message}")
118
156
  raise @last_error
119
157
  end
120
158
  else
121
- async_info("Step '#{step.name}' succeeded")
122
- async_debug("Step '#{step.name}' returned: #{result}")
159
+ log_info("step '#{step.name}' succeeded")
160
+ log_debug("step '#{step.name}' returned: #{result}")
123
161
  @completed_steps << step
124
162
  end
125
163
  end
@@ -128,12 +166,12 @@ module CemAcpt
128
166
  # @raise [StandardError] If the workflow is not retryable or has exceeded the maximum number of retries
129
167
  def retry_workflow
130
168
  if @workflow_runs < @retry_max
131
- async_info("Retrying workflow (attempt #{@workflow_runs + 1} of #{@retry_max})")
169
+ log_info("Retrying workflow (attempt #{@workflow_runs + 1} of #{@retry_max})")
132
170
  sleep @retry_delay if @retry_delay > 0
133
171
  clean_up
134
172
  run
135
173
  else
136
- async_fatal('Workflow is not retryable or has exceeded the maximum number of retries')
174
+ log_fatal('Workflow is not retryable or has exceeded the maximum number of retries')
137
175
  raise @last_error
138
176
  end
139
177
  end
@@ -142,7 +180,7 @@ module CemAcpt
142
180
  # @return [Step] The default clean_up step
143
181
  def default_clean_up
144
182
  add_step(:clean_up) do
145
- async_info('No clean_up step defined, skipping')
183
+ log_info('No clean_up step defined, skipping')
146
184
  true
147
185
  end
148
186
  @steps.last
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../logging'
3
+ require_relative '../logging'
4
4
 
5
5
  module CemAcpt
6
6
  module TestRunner
@@ -28,9 +28,9 @@ module CemAcpt
28
28
  # @!attribute [r] result
29
29
  # @return [Object] The result of the step
30
30
  class Step
31
- include CemAcpt::LoggingAsync
31
+ include CemAcpt::TestRunner::Logging
32
32
 
33
- attr_reader :name, :opts, :result
33
+ attr_reader :logger, :name, :opts, :result
34
34
 
35
35
  # @param name [Symbol] The name of the step
36
36
  # @param opts [Hash] The options passed to the step
@@ -42,7 +42,7 @@ module CemAcpt
42
42
  @block = block
43
43
  @result = :not_run
44
44
  @failed = false
45
- @log_prefix = opts[:log_prefix] || "#{@name.upcase}:"
45
+ @logger = use_logger(opts[:logger], step: name.to_s.capitalize)
46
46
  end
47
47
 
48
48
  # @return [Boolean] True if the step has been run and failed
@@ -51,27 +51,21 @@ module CemAcpt
51
51
  end
52
52
 
53
53
  # Run the step. This calls and executes the block passed to the constructor.
54
- # @param log_prefix [String] The prefix to use when logging the step name
54
+ # @param start_log [String] The message to log when the step starts
55
55
  # @return [Object] The result of the step
56
- def run(_log_prefix = 'Running step')
57
- async_info(log_msg('Starting step'))
56
+ def run(start_log = 'starting step')
57
+ @logger.info(start_log)
58
58
  @result = @block.call(self)
59
- async_debug(log_msg('SUCCESS'))
59
+ @logger.debug('SUCCESS')
60
60
  @result
61
61
  rescue StandardError => e
62
- async_debug(log_msg("FAILED: #{e.message}"))
62
+ @logger.debug("FAILED: #{e.message}")
63
63
  @result = StepError.new(@name, e)
64
64
  @failed = true
65
65
  @result
66
66
  ensure
67
67
  Thread.pass # Be kind to the scheduler
68
68
  end
69
-
70
- private
71
-
72
- def log_msg(msg)
73
- [@log_prefix, msg].join(' ')
74
- end
75
69
  end
76
70
 
77
71
  # StepState is a class that holds the state of a Step.
@@ -144,11 +138,11 @@ module CemAcpt
144
138
  end
145
139
 
146
140
  # Run the step. This wraps the Step#run method and handles updating the state of the step.
147
- # @param log_prefix [String] The prefix to use when logging the step name
141
+ # @param start_log [String] The message to log when the step starts
148
142
  # @return [Object] The result of the step
149
- def run(log_prefix = 'Running step')
143
+ def run(start_log = 'starting step')
150
144
  @run_count += 1
151
- @last_result = @step.run(log_prefix)
145
+ @last_result = @step.run(start_log)
152
146
  @results << @last_result
153
147
  if @last_result.is_a?(StepError)
154
148
  handle_error(@last_result)
@@ -178,8 +172,8 @@ module CemAcpt
178
172
 
179
173
  # Retry running the step
180
174
  def retry_step
181
- sleep @retry_delay if @retry_delay > 0
182
- run("Retrying step (attempt #{@run_count} of #{@retry_max})")
175
+ sleep @retry_delay if @retry_delay.positive?
176
+ run("retrying step (attempt #{@run_count + 1} of #{@retry_max})")
183
177
  end
184
178
  end
185
179
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../logging'
4
3
  require_relative 'workflow/manager'
5
4
  require_relative 'workflow/step'
6
5
 
@@ -10,206 +9,3 @@ module CemAcpt
10
9
  module Workflow; end
11
10
  end
12
11
  end
13
-
14
- # module CemAcpt
15
- # module TestRunner
16
-
17
-
18
- # # Workflow is a class that manages how steps are executed.
19
- # class Workflow
20
- # include CemAcpt::LoggingAsync
21
-
22
- # attr_reader :steps, :groups, :group_retry_max, :completed_steps
23
-
24
- # def initialize(init_steps: [], **opts)
25
- # raise ArgumentError, 'init_steps must be an Array' unless init_steps.is_a?(Array)
26
- # raise ArgumentError, 'init_steps must be an Array of Step objects' unless init_steps.all? { |step| step.is_a?(Step) }
27
-
28
- # @steps = init_steps
29
- # @groups = @steps.map(&:group).compact.uniq
30
- # @group_retry_max = opts[:group_retry_max] || 3
31
- # @group_tries = {}
32
- # @last_error = nil
33
- # @completed_steps = []
34
- # end
35
-
36
- # def add_step(step, order = nil)
37
- # raise ArgumentError, 'step must be a Step object' unless step.is_a?(Step)
38
-
39
- # unless order.nil?
40
- # @steps.insert(order, step)
41
- # else
42
- # @steps << step
43
- # end
44
- # end
45
-
46
- # def add_step_with(name, order = nil, **kwargs, &block)
47
- # raise ArgumentError, 'order must be an Integer' unless order.is_a?(Integer) || order.nil?
48
- # raise ArgumentError, 'name must be a Symbol' unless name.is_a?(Symbol)
49
-
50
- # unless order.nil?
51
- # @steps.insert(order, Step.new(name, **kwargs, &block))
52
- # else
53
- # @steps << Step.new(name, **kwargs, &block)
54
- # end
55
- # end
56
-
57
- # def run
58
- # current_run_groups = []
59
- # @steps.each do |step|
60
- # next if step.name == :clean_up
61
-
62
- # group_run_increment(step, current_run_groups)
63
- # step.run
64
- # @completed_steps << step
65
- # end
66
- # @completed_steps
67
- # rescue StandardError => e
68
- # async_error("Workflow failed with error: #{e}")
69
- # @last_error = e
70
- # raise e
71
- # ensure
72
- # clean_up
73
- # end
74
-
75
- # def success?
76
- # (@completed_steps.length == @steps.length) && @completed_steps.none? { |step| step.result.is_a?(StepError) }
77
- # end
78
-
79
- # private
80
-
81
- # def group_run_increment(step, current_run_groups = [])
82
- # if step.grouped? && !current_run_groups.include?(step.group)
83
- # current_run_groups << step.group
84
- # if @group_tries[step.group].nil? || @group_tries[step.group].zero?
85
- # @group_tries[step.group] = 1
86
- # elsif @group_tries[step.group] >= @group_retry_max
87
- # raise StepError.new(step, @last_error)
88
- # else
89
- # @group_tries[step.group] += 1
90
- # end
91
- # end
92
- # end
93
-
94
- # def run_step(step)
95
- # result = step.run
96
- # #handle_result(step, result)
97
- # @completed_steps << step
98
- # result
99
- # end
100
-
101
- # def handle_result(step, result)
102
- # if result.is_a?(StepError)
103
- # handle_error(step, result)
104
- # else
105
- # async_info("Step '#{step.name}' completed successfully")
106
- # end
107
- # end
108
-
109
- # def handle_error(step, err)
110
- # if step.retryable? && step.runs < @retry_max
111
- # async_info("Retrying step '#{step.name}' (attempt #{step.runs + 1} of #{@retry_max})")
112
- # sleep step.retry_delay if step.retry_delay > 0
113
- # run_step(step)
114
- # else
115
- # async_debug("Step '#{step.name}' is not retryable or has exceeded the maximum number of retries")
116
- # raise err if step.raise_on_fail?
117
- # end
118
- # end
119
-
120
- # def default_clean_up
121
- # Step.new(:clean_up, retryable: false, raise_on_fail: true) do
122
- # async_info('No clean_up step defined, skipping')
123
- # end
124
- # end
125
-
126
- # def clean_up
127
- # cleanup_step = @steps.find { |step| step.name == :clean_up } || default_clean_up
128
- # unless cleanup_step.nil?
129
- # cleanup_step.retryable = false # clean_up steps should not be retried
130
- # cleanup_step.raise_on_fail = true # clean_up steps should always raise on failure
131
- # run_step(cleanup_step)
132
- # end
133
- # end
134
- # end
135
-
136
- # # Step is a class that defines a single step in a Workflow.
137
- # class Step
138
- # include CemAcpt::LoggingAsync
139
-
140
- # attr_reader :group, :name, :opts, :result, :retry_delay, :retry_max, :runs
141
-
142
- # def initialize(name, **opts, &logic)
143
- # @name = name
144
- # @logic = logic
145
- # @group = opts[:group] || nil
146
- # @retryable = opts[:retryable] || false
147
- # @retry_delay = opts[:retry_delay] || 0
148
- # @retry_max = opts[:retry_max] || 3
149
- # @retry_group_on_fail = opts[:retry_group_on_fail] || false
150
- # @raise_on_fail = opts[:raise_on_fail] || true
151
- # @runs = 0
152
- # @opts = opts
153
- # @result = :not_run
154
- # end
155
-
156
- # def grouped?
157
- # !@group.nil?
158
- # end
159
-
160
- # def retryable?
161
- # @retryable
162
- # end
163
-
164
- # def retry_group_on_fail?
165
- # @retry_group_on_fail
166
- # end
167
-
168
- # def raise_on_fail?
169
- # @raise_on_fail
170
- # end
171
-
172
- # def retryable=(val)
173
- # raise ArgumentError, 'retryable must be a Boolean' unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
174
-
175
- # @retryable = val
176
- # end
177
-
178
- # def retry_delay=(val)
179
- # raise ArgumentError, 'retry_delay must be an Integer' unless val.is_a?(Integer)
180
-
181
- # @retry_delay = val
182
- # end
183
-
184
- # def retry_group_on_fail=(val)
185
- # raise ArgumentError, 'retry_group_on_fail must be a Boolean' unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
186
-
187
- # @retry_group_on_fail = val
188
- # end
189
-
190
- # def raise_on_fail=(val)
191
- # raise ArgumentError, 'raise_on_fail must be a Boolean' unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
192
-
193
- # @raise_on_fail = val
194
- # end
195
-
196
- # def run
197
- # begin
198
- # @runs += 1
199
- # async_info("Running step #{@name}...") if @runs == 1
200
- # @result = @logic.call(@opts)
201
- # rescue StandardError => e
202
- # if retryable? && @runs < @retry_max
203
- # sleep @retry_delay if @retry_delay > 0
204
- # async_info("Retrying step #{@name} (attempt #{@runs + 1} of #{@retry_max})")
205
- # run
206
- # else
207
- # @result = CemAcpt::TestRunner::StepError.new(@name, e)
208
- # raise @result if raise_on_fail?
209
- # end
210
- # end
211
- # @result
212
- # end
213
- # end
214
- # end
215
- # end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemAcpt
4
- VERSION = '0.3.4'
4
+ VERSION = '0.3.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cem_acpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: universal-java-17
6
6
  authors:
7
7
  - puppetlabs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-30 00:00:00.000000000 Z
11
+ date: 2023-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -190,6 +190,7 @@ files:
190
190
  - lib/cem_acpt/spec_helper_acceptance.rb
191
191
  - lib/cem_acpt/test_data.rb
192
192
  - lib/cem_acpt/test_runner.rb
193
+ - lib/cem_acpt/test_runner/logging.rb
193
194
  - lib/cem_acpt/test_runner/run_handler.rb
194
195
  - lib/cem_acpt/test_runner/runner.rb
195
196
  - lib/cem_acpt/test_runner/runner_result.rb