cem_acpt 0.3.3-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: 50d9f549fee6436b9f857dbdf313dcab94b223e988c2cbfeadd8999d86f436f0
4
- data.tar.gz: 62a556754fe568b9d3bbc28b7381b3657c7aa0db872e5405bb392a1b9cef061d
3
+ metadata.gz: b62e59f800597758c9a69bc1ede3d3f97a9a68c6c38f0d4705201ac2613feab9
4
+ data.tar.gz: 4d4a0ad559702df7cff661726a962b0bbc278d2cb0ecba48241a67a55345dac6
5
5
  SHA512:
6
- metadata.gz: bf027699bcf399a68a6b845ef4191b4b912736f6eb52708e35a0393e78fce74f3c0299ee627bbfe80ec06a8ef9be0e5b030db6a1ab99a678943c8ebdf21112f6
7
- data.tar.gz: eb7448394e6be3719a735cf30adedae0aefb0ea8a6144523300c2e57da61bdbb3dfbf76a394b3c2014cc78a8283a6c565f2f311481951d09f1211454f22add74
6
+ metadata.gz: ee72270cae3121c03597e12f91c3fd6134da4ab40ef342cc3f694ea0a5ee2dcaf37363e9430b2610539120e0cf5ea733caf191d916a926e2d29dc17ac8bc7cfb
7
+ data.tar.gz: 79a40b4e74935a7842bffd7b9252aa63d277cc31e49c0a3a10200f647117a1c3e51d4183a3e2e16aa9cc0e1fd185d0ee375177fdc876ca4097e8828884b55f36
@@ -0,0 +1,36 @@
1
+ name: Unit Tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ pull_request:
6
+ types:
7
+ - opened
8
+ - synchronize
9
+ branches:
10
+ - main
11
+ tags:
12
+ - v.*
13
+
14
+ jobs:
15
+ tests:
16
+ name: RSpec tests
17
+ runs-on: ubuntu-20.04
18
+ steps:
19
+ - name: Checkout Source
20
+ uses: actions/checkout@v3
21
+
22
+ - name: Set up Java 17
23
+ uses: actions/setup-java@v3
24
+ with:
25
+ distribution: 'adopt-hotspot'
26
+ java-version: '17'
27
+
28
+ - name: Set up JRuby
29
+ uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: jruby-9.3.3.0
32
+ bundler-cache: true
33
+
34
+ - name: Run RSpec
35
+ run: |
36
+ bundle exec rake spec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.3.3-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)
@@ -13,10 +13,13 @@ GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
15
  ast (2.4.2)
16
+ coderay (1.1.3)
16
17
  concurrent-ruby (1.1.10)
17
18
  deep_merge (1.2.2)
18
19
  diff-lcs (1.5.0)
19
20
  ed25519 (1.3.0-java)
21
+ ffi (1.15.5-java)
22
+ method_source (1.0.0)
20
23
  minitar (0.9)
21
24
  multi_json (1.15.0)
22
25
  net-scp (4.0.0)
@@ -27,6 +30,10 @@ GEM
27
30
  parser (3.1.2.0)
28
31
  ast (~> 2.4.1)
29
32
  pathspec (1.0.0)
33
+ pry (0.14.2-java)
34
+ coderay (~> 1.1)
35
+ method_source (~> 1.0)
36
+ spoon (~> 0.0)
30
37
  puppet-modulebuilder (0.3.0)
31
38
  minitar (~> 0.9)
32
39
  pathspec (>= 0.2.1, < 2.0.0)
@@ -73,6 +80,8 @@ GEM
73
80
  net-ssh (>= 2.7)
74
81
  net-telnet (= 0.1.1)
75
82
  sfl
83
+ spoon (0.0.6)
84
+ ffi
76
85
  unicode-display_width (2.2.0)
77
86
 
78
87
  PLATFORMS
@@ -80,9 +89,10 @@ PLATFORMS
80
89
 
81
90
  DEPENDENCIES
82
91
  cem_acpt!
92
+ pry
83
93
  rake (>= 12.0)
84
94
  rspec (>= 3.0)
85
95
  rubocop
86
96
 
87
97
  BUNDLED WITH
88
- 2.3.25
98
+ 2.3.15
data/cem_acpt.gemspec CHANGED
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency 'puppet-modulebuilder', '>= 0.0.1'
35
35
  spec.add_runtime_dependency 'serverspec-cem-acpt'
36
36
  spec.add_development_dependency 'rubocop'
37
+ spec.add_development_dependency 'pry'
37
38
  end
@@ -4,6 +4,7 @@ module CemAcpt::Platform::Gcp
4
4
  require 'json'
5
5
  require 'open3'
6
6
  require_relative File.join(__dir__, '..', 'base', 'cmd.rb')
7
+ require_relative File.join(__dir__, '..', 'utils', 'linux.rb')
7
8
 
8
9
  # This class provides methods to run gcloud commands. It allows for default values to be
9
10
  # set for the project, zone, and user and can also find these values from the local config.
@@ -59,6 +60,10 @@ module CemAcpt::Platform::Gcp
59
60
  @local_port ||= rand(49_512..65_535)
60
61
  end
61
62
 
63
+ def os_release
64
+ @os_release || :not_set
65
+ end
66
+
62
67
  def ssh_key
63
68
  return @ssh_key unless @ssh_key.nil?
64
69
 
@@ -108,6 +113,9 @@ module CemAcpt::Platform::Gcp
108
113
 
109
114
  # Deletes a GCP VM instance.
110
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
111
119
  local_exec("compute instances delete #{instance_name} --quiet")
112
120
  rescue StandardError
113
121
  # Ignore errors when deleting instances.
@@ -173,12 +181,38 @@ module CemAcpt::Platform::Gcp
173
181
  logger.debug('Restarting SSH service')
174
182
  gcloud_ssh(instance_name, 'sudo systemctl restart sshd', ignore_command_errors: true)
175
183
  logger.info("SSH connection to #{instance_name} is ready")
184
+ logger.debug('Getting OS release')
185
+ osr = gcloud_ssh(instance_name, 'sudo cat /etc/os-release')
186
+ @os_release = CemAcpt::Platform::Utils::Linux::OSRelease.new(osr)
187
+ logger.debug("OS release: #{@os_release.to_h}")
176
188
  true
177
189
  rescue StandardError => e
178
190
  logger.debug("SSH connection to #{instance_name} failed: #{e}")
179
191
  false
180
192
  end
181
193
 
194
+ def dnf_automatic_success?(instance_name, opts: {})
195
+ ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
196
+ logger.debug("Checking dnf-automatic success on #{instance_name} with options #{ssh_options}")
197
+ gcloud_ssh(instance_name, 'sudo systemctl restart dnf-automatic.timer')
198
+ true
199
+ rescue StandardError => e
200
+ logger.error("DNF automatic updates on #{instance_name} failed: #{e}")
201
+ false
202
+ end
203
+
204
+ def rpm_db_check_success?(instance_name, pkgmgr, opts: {})
205
+ ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
206
+ logger.debug("Checking #{pkgmgr} rpm db on #{instance_name} with options #{ssh_options}")
207
+ gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y rpm glibc")
208
+ gcloud_ssh(instance_name, "sudo rm -f /var/lib/rpm/.rpm.lock")
209
+ gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y #{pkgmgr}")
210
+ true
211
+ rescue StandardError => e
212
+ logger.error("#{pkgmgr} rpm db check on #{instance_name} failed: #{e}")
213
+ false
214
+ end
215
+
182
216
  # This function spawns a background thread to run a GCP IAP tunnel, run the given
183
217
  # code block, then kill the thread. The code block will be yielded ssh_opts that
184
218
  # are used to configure SSH connections over the IAP tunnel. The IAP tunnel is
@@ -287,6 +321,14 @@ module CemAcpt::Platform::Gcp
287
321
  "compute.#{vm_describe(vm)['id']}"
288
322
  end
289
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
+
290
332
  # Default options for Net::SSH
291
333
  def default_ssh_opts
292
334
  {
@@ -236,6 +236,7 @@ module CemAcpt::Platform::Gcp
236
236
  node_data: data,
237
237
  transport: :ssh,
238
238
  ssh_opts: opts,
239
+ os_release: os_release.to_h,
239
240
  }
240
241
  end
241
242
 
@@ -260,6 +261,29 @@ module CemAcpt::Platform::Gcp
260
261
  false
261
262
  end
262
263
 
264
+ def dnf_automatic_success?
265
+ if os_release.name.match?(%r{^Red Hat.*}) && os_release.version.match?(%r{^(8|9).*})
266
+ logger.debug("Checking dnf-automatic on #{name} with platform #{info[:os_release][:platform]}")
267
+ @cmd.dnf_automatic_success?(name)
268
+ else
269
+ true
270
+ end
271
+ end
272
+
273
+ def rpm_db_check_success?
274
+ if os_release.name.match?(%r{^Red Hat.*})
275
+ logger.debug("Checking rpmdb on #{name} with platform #{info[:os_release][:platform]}")
276
+ pkgmgr = os_release.version.match?(%r{^(8|9).*}) ? 'dnf' : 'yum'
277
+ @cmd.rpm_db_check_success?(name, pkgmgr)
278
+ else
279
+ true
280
+ end
281
+ end
282
+
283
+ def os_release
284
+ @cmd.os_release
285
+ end
286
+
263
287
  def destroy
264
288
  @cmd.delete_instance(name)
265
289
  end
@@ -35,6 +35,18 @@ module Platform
35
35
  @instance.ready?
36
36
  end
37
37
 
38
+ # Returns true if dnf_automatic ran successfully on the GCP instance
39
+ def dnf_automatic_success?
40
+ logger.debug("Checking if dnf_automatic ran successfully on #{node_name}...")
41
+ @instance.dnf_automatic_success?
42
+ end
43
+
44
+ # Returns true if rpm_db_check ran successfully on the GCP instance
45
+ def rpm_db_check_success?
46
+ logger.debug("Checking if rpm_db_check ran successfully on #{node_name}...")
47
+ @instance.rpm_db_check_success?
48
+ end
49
+
38
50
  # Runs the test suite against the GCP instance. Must be given a block.
39
51
  # If necessary, can pass information into the block to be used in the test suite.
40
52
  def run_tests(&block)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ module Platform
5
+ module Utils
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
44
+ class OSRelease
45
+ attr_reader :properties
46
+
47
+ def initialize(os_release)
48
+ @os_release = parse_string(os_release)
49
+ @properties = []
50
+ define_properties(@os_release)
51
+ end
52
+
53
+ def to_h
54
+ @os_release
55
+ end
56
+
57
+ private
58
+
59
+ def parse_string(os_release)
60
+ os_release = os_release.split("\n")
61
+ os_release = os_release.map { |line| line.split('=', 2) }.select { |l| l.length == 2 }
62
+ os_release.map! { |k, v| [k.downcase, v.delete_prefix('"').delete_suffix('"')] }
63
+ os_release.to_h
64
+ end
65
+
66
+ def define_properties(hsh)
67
+ hsh.each do |k, v|
68
+ @properties << k
69
+ self.class.send(:define_method, k) { v }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ 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