cem_acpt 0.3.2-universal-java-17 → 0.3.4-universal-java-17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1403d7aad1f43c9ab46a2ec09e10e6e6971a895f864f908da36d1aa4f459ccb
4
- data.tar.gz: 7c24f47c835e7422311e21fb3b398d871ba88fa8ba295a6b403c0b5006cfe890
3
+ metadata.gz: c289fcb52a1115630ffece04c465e5a8d945bdefdcc99fbf73a800398d2f2bc6
4
+ data.tar.gz: aca0f41efe831bf5bb8dc4399cd1a2d771f7bb72fb1ee13eeccb79d59bd45183
5
5
  SHA512:
6
- metadata.gz: d828aa44f6879dacbd6b5dabd5682dad73e7f4150436a0bffb51c73707345f61bd1b45527d102dfe67cb060acf0cec639c24c89df1a11b6c3f8598e48c939621
7
- data.tar.gz: fa6b986ce60edafa468ee41a7dda019aad8fe09590918e93ec41325e9158a9bd96712a26b6575b1f08d7f239e66d14665ca2a3cbd90dcf08e0d6ce95347ceb73
6
+ metadata.gz: 65dd7b8c5023304f0c5f9fd371a64cfb157567004d6f93ebdadc76bbf99f50f7483fab575f73fb9c07256cbb0db688ad4d8c10a6b644a8f7670a3441a54e866e
7
+ data.tar.gz: 2504bc3dc1ef1ac1a09c16d00c3cd99b08127ccd31f2b9da134f31ffb2dc19730eda580ec893def0a9b183bd4198c5aa8263ea735f50c0b2c21156a5f8f7124d
@@ -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.2-universal-java-17)
4
+ cem_acpt (0.3.4-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
 
@@ -173,12 +178,38 @@ module CemAcpt::Platform::Gcp
173
178
  logger.debug('Restarting SSH service')
174
179
  gcloud_ssh(instance_name, 'sudo systemctl restart sshd', ignore_command_errors: true)
175
180
  logger.info("SSH connection to #{instance_name} is ready")
181
+ logger.debug('Getting OS release')
182
+ osr = gcloud_ssh(instance_name, 'sudo cat /etc/os-release')
183
+ @os_release = CemAcpt::Platform::Utils::Linux::OSRelease.new(osr)
184
+ logger.debug("OS release: #{@os_release.to_h}")
176
185
  true
177
186
  rescue StandardError => e
178
187
  logger.debug("SSH connection to #{instance_name} failed: #{e}")
179
188
  false
180
189
  end
181
190
 
191
+ def dnf_automatic_success?(instance_name, opts: {})
192
+ ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
193
+ logger.debug("Checking dnf-automatic success on #{instance_name} with options #{ssh_options}")
194
+ gcloud_ssh(instance_name, 'sudo systemctl restart dnf-automatic.timer')
195
+ true
196
+ rescue StandardError => e
197
+ logger.error("DNF automatic updates on #{instance_name} failed: #{e}")
198
+ false
199
+ end
200
+
201
+ def rpm_db_check_success?(instance_name, pkgmgr, opts: {})
202
+ ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
203
+ logger.debug("Checking #{pkgmgr} rpm db on #{instance_name} with options #{ssh_options}")
204
+ gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y rpm glibc")
205
+ gcloud_ssh(instance_name, "sudo rm -f /var/lib/rpm/.rpm.lock")
206
+ gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y #{pkgmgr}")
207
+ true
208
+ rescue StandardError => e
209
+ logger.error("#{pkgmgr} rpm db check on #{instance_name} failed: #{e}")
210
+ false
211
+ end
212
+
182
213
  # This function spawns a background thread to run a GCP IAP tunnel, run the given
183
214
  # code block, then kill the thread. The code block will be yielded ssh_opts that
184
215
  # are used to configure SSH connections over the IAP tunnel. The IAP tunnel is
@@ -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
@@ -12,9 +12,9 @@ module Platform
12
12
 
13
13
  # Provision a GCP instance
14
14
  def provision
15
- creation_params = config.dup
16
- creation_params[:disk][:image_name] = image_name.dup
17
- creation_params[:local_port] = local_port.dup
15
+ creation_params = Marshal.load(Marshal.dump(config))
16
+ creation_params[:disk][:image_name] = image_name
17
+ creation_params[:local_port] = local_port
18
18
  @instance = CemAcpt::Platform::Gcp::VM.new(
19
19
  node_name.dup,
20
20
  components: creation_params,
@@ -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)
@@ -64,7 +76,7 @@ module Platform
64
76
  CemAcpt::Platform::Gcp::Cmd.new(out_format: 'json')
65
77
  end
66
78
 
67
- # Apllies the given Puppet manifest on the given instance
79
+ # Applies the given Puppet manifest on the given instance
68
80
  # @param instance_name [String] the name of the instance to apply the manifest to
69
81
  # @param manifest [String] the Puppet manifest to apply
70
82
  # @param opts [Hash] options to pass to the apply command
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ module Platform
5
+ module Utils
6
+ module Linux
7
+ class OSRelease
8
+ attr_reader :properties
9
+
10
+ def initialize(os_release)
11
+ @os_release = parse_string(os_release)
12
+ @properties = []
13
+ define_properties(@os_release)
14
+ end
15
+
16
+ def to_h
17
+ @os_release
18
+ end
19
+
20
+ private
21
+
22
+ def parse_string(os_release)
23
+ os_release = os_release.split("\n")
24
+ os_release = os_release.map { |line| line.split('=', 2) }.reject { |l| l.length != 2 }
25
+ os_release.map! { |k, v| [k.downcase, v.delete_prefix('"').delete_suffix('"')] }
26
+ os_release.to_h
27
+ end
28
+
29
+ def define_properties(hsh)
30
+ hsh.each do |k, v|
31
+ @properties << k
32
+ self.class.send(:define_method, k) { v }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -59,12 +59,13 @@ module CemAcpt::Platform
59
59
  # @param platform [String] the name of the platform.
60
60
  # @return [Class] the platform-specific Object.
61
61
  def platform_class(platform)
62
+ class_name = platform.capitalize
62
63
  # We require the platform base class here so that we can use it as
63
64
  # a parent class for the platform-specific class.
64
65
  require_relative 'platform/base'
65
66
  # If the class has already been defined, we can just use it.
66
- if Object.const_defined?(platform.capitalize)
67
- klass = Object.const_get(platform.capitalize)
67
+ if Object.const_defined?(class_name)
68
+ klass = Object.const_get(class_name)
68
69
  else
69
70
  # Otherwise, we need to create the class. We do this by setting
70
71
  # a new constant with the name of the platform capitalized, and
@@ -74,7 +75,7 @@ module CemAcpt::Platform
74
75
  # include Logging and Concurrent::Async, and finally call the
75
76
  # initialize method on the class.
76
77
  klass = Object.const_set(
77
- platform.capitalize,
78
+ class_name,
78
79
  Class.new(CemAcpt::Platform::Base) do
79
80
  require_relative "platform/#{platform}"
80
81
  include Platform
@@ -5,6 +5,8 @@ require 'English'
5
5
  require_relative '../logging'
6
6
  require_relative '../rspec_utils'
7
7
  require_relative 'runner_result'
8
+ require_relative 'runner_workflow_builder'
9
+ #require_relative 'workflow'
8
10
 
9
11
  module CemAcpt
10
12
  module TestRunner
@@ -23,6 +25,8 @@ module CemAcpt
23
25
 
24
26
  class RunnerProvisionError < RunnerStepError; end
25
27
 
28
+
29
+
26
30
  # Runner is a class that runs a single acceptance test suite on a single node.
27
31
  # It is responsible for managing the lifecycle of the test suite and
28
32
  # reporting the results back to the main thread. Runner objects are created
@@ -32,6 +36,8 @@ module CemAcpt
32
36
 
33
37
  attr_reader :node, :node_exists, :run_result
34
38
 
39
+ MAX_PROVISION_ATTEMPTS = 3
40
+
35
41
  # @param node [String] the name of the node to run the acceptance test suite on
36
42
  # @param ctx [CemAcpt::RunnerCtx] a cem_acpt Ctx (context) object
37
43
  # @param module_pkg_path [Concurrent::IVar] the path to the module package
@@ -42,34 +48,33 @@ module CemAcpt
42
48
  @debug_mode = @context.config.debug_mode?
43
49
  @node_inventory = @context.node_inventory
44
50
  @module_pkg_path = @context.module_package_path
51
+ @provision_attempts = 0
52
+ @provision_start_time = nil
45
53
  @node_exists = false
46
54
  @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
47
- @completed_steps = []
48
55
  validate!
49
56
  end
50
57
 
51
- def run_step(step_sym)
52
- send(step_sym)
53
- @completed_steps << step_sym
54
- rescue StandardError => e
55
- err = CemAcpt::TestRunner::RunnerStepError.new(step_sym, e)
56
- step_error_logging(err)
57
- @run_result.from_error(err)
58
- destroy unless step_sym == :destroy
59
- end
60
-
61
58
  # Executes test suite steps
62
59
  def start
63
60
  async_info("Starting test suite for #{@node.node_name}", log_prefix('RUNNER'))
64
- run_step(:provision)
65
- run_step(:bootstrap)
66
- run_step(:run_tests)
67
- run_step(:destroy)
68
- true
61
+ @workflow = new_workflow
62
+ @workflow.run
63
+ if @workflow.success?
64
+ @run_result = @workflow.last_result
65
+ @workflow.completed_steps.each do |s|
66
+ async_info("Step '#{s.name}' completed successfully", log_prefix('RUNNER'))
67
+ end
68
+ true
69
+ else
70
+ @run_result = @workflow.last_error
71
+ step_error_logging(@workflow.last_error)
72
+ false
73
+ end
69
74
  rescue StandardError => e
70
75
  step_error_logging(e)
76
+ @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
71
77
  @run_result.from_error(e)
72
- destroy
73
78
  end
74
79
 
75
80
  # Checks for failures in the test results.
@@ -81,6 +86,24 @@ module CemAcpt
81
86
 
82
87
  private
83
88
 
89
+ # Builds a new workflow for the runner
90
+ # @return [CemAcpt::TestRunner::Workflow::Manager] the new workflow
91
+ def new_workflow
92
+ builder = RunnerWorkflowBuilder.new(@node, config: @context.config)
93
+ builder.add_provision
94
+ builder.add_sleep(time: 30)
95
+ builder.add_wait_for_node_ssh
96
+ builder.add_check_dnf_automatic
97
+ builder.add_check_rpm_db
98
+ builder.add_save_node_to_inventory(node_inventory: @node_inventory, platform: @platform)
99
+ builder.add_check_for_module_package_path(module_pkg_path: @module_pkg_path)
100
+ builder.add_install_module(module_pkg_path: @module_pkg_path)
101
+ builder.add_check_node_inventory_file(node_inventory: @node_inventory)
102
+ builder.add_run_tests(rspec_opts: rspec_opts, rspec_cmd: CemAcpt::RSpecUtils::Command, run_result: @run_result)
103
+ builder.add_clean_up
104
+ builder.workflow
105
+ end
106
+
84
107
  def step_error_logging(err)
85
108
  prefix = err.respond_to?(:step) ? log_prefix(err.step.capitalize) : log_prefix('RUNNER')
86
109
  fatal_msg = ["runner failed: #{err.message}"]
@@ -94,93 +117,6 @@ module CemAcpt
94
117
  "#{prefix}: #{@node.test_data[:test_name]}:"
95
118
  end
96
119
 
97
- # Provisions the node for the acceptance test suite.
98
- def provision
99
- async_info("Provisioning #{@node.node_name}...", log_prefix('PROVISION'))
100
- start_time = Time.now
101
- @node.provision
102
- @node_exists = true
103
- max_retries = 60 # equals 300 seconds because we check every five seconds
104
- until @node.ready?
105
- if max_retries <= 0
106
- async_fatal("Node #{@node.node_name} failed to provision", log_prefix('PROVISION'))
107
- raise CemAcpt::TestRunner::RunnerProvisionError, "Provisioning timed out for node #{@node.node_name}"
108
- end
109
-
110
- async_info("Waiting for #{@node.node_name} to be ready for remote connections...", log_prefix('PROVISION'))
111
- max_retries -= 1
112
- sleep(5)
113
- end
114
- async_info("Node #{@node.node_name} is ready...", log_prefix('PROVISION'))
115
- node_desc = {
116
- test_data: @node.test_data,
117
- platform: @platform,
118
- local_port: @node.local_port,
119
- }.merge(@node.node)
120
- @node_inventory.add(@node.node_name, node_desc)
121
- @node_inventory.save
122
- async_info("Node #{@node.node_name} provisioned in #{Time.now - start_time} seconds", log_prefix('PROVISION'))
123
- end
124
-
125
- # Bootstraps the node for the acceptance test suite. Currently, this
126
- # just uploads and installs the module package.
127
- def bootstrap
128
- async_info("Bootstrapping #{@node.node_name}...", log_prefix('BOOTSTRAP'))
129
- until File.exist?(@module_pkg_path)
130
- async_debug("Waiting for module package #{@module_pkg_path} to exist...", log_prefix('BOOTSTRAP'))
131
- sleep(1)
132
- end
133
- async_info("Installing module package #{@module_pkg_path}...", log_prefix('BOOTSTRAP'))
134
- @node.install_puppet_module_package(@module_pkg_path)
135
- end
136
-
137
- # Runs the acceptance test suite via rspec.
138
- def run_tests
139
- attempts = 0
140
- until File.exist?(@node_inventory.save_file_path)
141
- raise 'Node inventory file not found' if (attempts += 1) > 3
142
-
143
- sleep(1)
144
- end
145
- async_info("Running test #{@node.test_data[:test_name]} on node #{@node.node_name}...", log_prefix('RSPEC'))
146
- @node.run_tests do |cmd_env|
147
- cmd_opts = rspec_opts
148
- cmd_opts.env = cmd_opts.env.merge(cmd_env) if cmd_env
149
- # Documentation format gets logged in real time, JSON file is read after the fact
150
- begin
151
- @rspec_cmd = CemAcpt::RSpecUtils::Command.new(cmd_opts)
152
- @rspec_cmd.execute(pty: false, log_prefix: log_prefix('RSPEC'))
153
- @run_result.from_json_file(cmd_opts.format[:json])
154
- rescue Errno::EIO => e
155
- async_error("failed to run rspec: #{@node.test_data[:test_name]}: #{$ERROR_INFO}", log_prefix('RSPEC'))
156
- @run_result.from_error(e)
157
- rescue StandardError => e
158
- async_error("failed to run rspec: #{@node.test_data[:test_name]}: #{e.message}", log_prefix('RSPEC'))
159
- async_debug("Backtrace:\n#{e.backtrace}", log_prefix('RSPEC'))
160
- @run_result.from_error(e)
161
- end
162
- end
163
- async_info("Tests completed with exit code: #{@run_result.exit_status}", log_prefix('RSPEC'))
164
- end
165
-
166
- # Destroys the node for the acceptance test suite.
167
- def destroy
168
- kill_spec_pty_if_exists
169
- if @context.config.get('no_destroy_nodes')
170
- async_info("Not destroying node #{@node.node_name} because 'no_destroy_nodes' is set to true",
171
- log_prefix('DESTROY'))
172
- else
173
- async_info("Destroying #{@node.node_name}...", log_prefix('DESTROY'))
174
- @node.destroy
175
- @node_exists = false
176
- async_info("Node #{@node.node_name} destroyed successfully", log_prefix('DESTROY'))
177
- end
178
- end
179
-
180
- def kill_spec_pty_if_exists
181
- @rspec_cmd&.kill_pty
182
- end
183
-
184
120
  # Validates the runner configuration.
185
121
  def validate!
186
122
  raise 'No node provided' unless @node