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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/cem_acpt/platform/gcp/cmd.rb +11 -0
- data/lib/cem_acpt/platform/utils/linux.rb +38 -1
- data/lib/cem_acpt/test_runner/logging.rb +77 -0
- data/lib/cem_acpt/test_runner/run_handler.rb +21 -20
- data/lib/cem_acpt/test_runner/runner.rb +16 -17
- data/lib/cem_acpt/test_runner/runner_workflow_builder.rb +21 -42
- data/lib/cem_acpt/test_runner/workflow/manager.rb +55 -17
- data/lib/cem_acpt/test_runner/workflow/step.rb +14 -20
- data/lib/cem_acpt/test_runner/workflow.rb +0 -204
- data/lib/cem_acpt/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b62e59f800597758c9a69bc1ede3d3f97a9a68c6c38f0d4705201ac2613feab9
|
4
|
+
data.tar.gz: 4d4a0ad559702df7cff661726a962b0bbc278d2cb0ecba48241a67a55345dac6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee72270cae3121c03597e12f91c3fd6134da4ab40ef342cc3f694ea0a5ee2dcaf37363e9430b2610539120e0cf5ea733caf191d916a926e2d29dc17ac8bc7cfb
|
7
|
+
data.tar.gz: 79a40b4e74935a7842bffd7b9252aa63d277cc31e49c0a3a10200f647117a1c3e51d4183a3e2e16aa9cc0e1fd185d0ee375177fdc876ca4097e8828884b55f36
|
data/Gemfile.lock
CHANGED
@@ -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) }.
|
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('
|
42
|
+
@logger.info('creating and starting test runners...')
|
42
43
|
create_runners
|
43
|
-
logger.info("
|
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('
|
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("
|
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
|
-
"
|
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("
|
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::
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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 '
|
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::
|
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
|
-
|
18
|
+
# @param logger [CemAcpt::TestRunner::Logging::Logger] Logger object
|
19
|
+
def initialize(node, config, logger = nil)
|
19
20
|
@node = node
|
20
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
176
|
-
|
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
|
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
|
-
|
195
|
+
s.logger.info("not destroying node #{s.opts[:node].node_name} because 'no_destroy_nodes' is set to true")
|
194
196
|
else
|
195
|
-
|
197
|
+
s.logger.info("destroying node #{s.opts[:node].node_name}")
|
196
198
|
s.opts[:node].destroy
|
197
|
-
|
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
|
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
|
231
|
-
retry_delay: config
|
232
|
-
ignore_failures: config
|
233
|
-
raise_on_fail: config
|
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 '
|
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::
|
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
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
152
|
+
log_warn("ignoring failure of step '#{step.name}'")
|
115
153
|
@completed_steps << step
|
116
154
|
else
|
117
|
-
|
155
|
+
log_error("failed with error: #{@last_error.message}")
|
118
156
|
raise @last_error
|
119
157
|
end
|
120
158
|
else
|
121
|
-
|
122
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 '
|
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::
|
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
|
-
@
|
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
|
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(
|
57
|
-
|
56
|
+
def run(start_log = 'starting step')
|
57
|
+
@logger.info(start_log)
|
58
58
|
@result = @block.call(self)
|
59
|
-
|
59
|
+
@logger.debug('SUCCESS')
|
60
60
|
@result
|
61
61
|
rescue StandardError => e
|
62
|
-
|
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
|
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(
|
143
|
+
def run(start_log = 'starting step')
|
150
144
|
@run_count += 1
|
151
|
-
@last_result = @step.run(
|
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
|
182
|
-
run("
|
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
|
data/lib/cem_acpt/version.rb
CHANGED
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
|
+
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
|
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
|