cem_acpt 0.8.8 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) 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/image_builder/provision_commands.rb +43 -0
  28. data/lib/cem_acpt/logging/formatter.rb +3 -3
  29. data/lib/cem_acpt/logging.rb +17 -1
  30. data/lib/cem_acpt/provision/terraform/linux.rb +2 -2
  31. data/lib/cem_acpt/test_data.rb +2 -0
  32. data/lib/cem_acpt/test_runner/log_formatter/base.rb +73 -0
  33. data/lib/cem_acpt/test_runner/log_formatter/bolt_error_formatter.rb +65 -0
  34. data/lib/cem_acpt/test_runner/log_formatter/bolt_output_formatter.rb +54 -0
  35. data/lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb +64 -0
  36. data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +17 -30
  37. data/lib/cem_acpt/test_runner/log_formatter/goss_error_formatter.rb +31 -0
  38. data/lib/cem_acpt/test_runner/log_formatter/standard_error_formatter.rb +35 -0
  39. data/lib/cem_acpt/test_runner/log_formatter.rb +17 -5
  40. data/lib/cem_acpt/test_runner/test_results.rb +150 -0
  41. data/lib/cem_acpt/test_runner.rb +153 -53
  42. data/lib/cem_acpt/utils/files.rb +189 -0
  43. data/lib/cem_acpt/utils/finalizer_queue.rb +73 -0
  44. data/lib/cem_acpt/utils/shell.rb +13 -4
  45. data/lib/cem_acpt/version.rb +1 -1
  46. data/sample_config.yaml +13 -0
  47. metadata +41 -5
  48. data/lib/cem_acpt/test_runner/log_formatter/error_formatter.rb +0 -33
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'base'
5
+
6
+ module CemAcpt
7
+ module TestRunner
8
+ module LogFormatter
9
+ # Formats the results of a Bolt::SummaryResults object
10
+ class BoltSummaryResultsFormatter < Base
11
+ def initialize(config, instance_names_ips, subject: nil)
12
+ super(subject)
13
+ @config = config
14
+ @instance_names_ips = instance_names_ips
15
+ end
16
+
17
+ def summary(response = nil)
18
+ super(response)
19
+ [
20
+ "SUMMARY: #{test_name(log_subject)}:",
21
+ normalize_whitespace(log_subject.summary),
22
+ ].join(' ')
23
+ end
24
+
25
+ def results(response = nil)
26
+ super(response)
27
+ log_subject.each_with_object([]) do |res, ary|
28
+ res.results.each do |r|
29
+ header = [
30
+ "#{success_str(r.success?).capitalize}: #{r.name}",
31
+ "action: #{r.action}",
32
+ "target: #{name_from_ip(r.target)}",
33
+ "object: #{r.object}",
34
+ "status: #{r.status}",
35
+ ]
36
+ parts = [
37
+ header.join(', '),
38
+ "validation results:\n#{JSON.pretty_generate(r.failed_validation_results)}",
39
+ ]
40
+ if CemAcpt::Logging.verbose?
41
+ parts << "command result:\n#{JSON.pretty_generate(r.command_result.to_h)}"
42
+ end
43
+ parts << r.error if r.error?
44
+ if r.respond_to?(:details) && !r.details&.empty?
45
+ parts << "details:\n#{JSON.pretty_generate(r.details)}\n"
46
+ end
47
+ ary << parts.join("\n")
48
+ end
49
+ end
50
+ end
51
+
52
+ def host_name(response = nil)
53
+ super(response)
54
+ log_subject.items.map { |i| name_from_ip(i.target) }.compact.uniq.join(', ')
55
+ end
56
+
57
+ def test_name(response = nil)
58
+ super(response)
59
+ 'Bolt tests'
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,53 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'base'
4
+
3
5
  module CemAcpt
4
6
  module TestRunner
5
7
  module LogFormatter
6
8
  # Formats the results of a Goss action
7
- class GossActionResponse
8
- 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