cem_acpt 0.4.0-universal-java-17 → 0.5.0-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: bd740df7227095812b1fa29a9267fdb3d9470567d5990a56799de780cf45d220
4
- data.tar.gz: 1b53e57a9c26200cdabde23784bbfc0a06cacfd0141636682c9c590669fd697f
3
+ metadata.gz: cff2cb41ee31e39ce4c021c989f95d8c86ba029a4d20dd996a271dc3818894e8
4
+ data.tar.gz: 1717991fb2f8f883002a8a505e2a5500a366d45387cbbf99360254bd73509db9
5
5
  SHA512:
6
- metadata.gz: a7932858ea4758458609cced16b9049d8ebec80304a0cb13a8aa00ff4787435686129d21c5ef60a13fabc52a4e9e945cf6b1a756a822bd70792e2b34f2df493e
7
- data.tar.gz: 0dac3c3ce8acee13005e767fcaaaae93b07507f9e969c9faa87d629fc95c242f979ebff79e1249465ad37bd75eb5015b1c722c6bbf68c775738791003767af6c
6
+ metadata.gz: 7bf62d8d01003f4867855916651b6c29219ec95851600c78e34737df42d3e5db883807142a22b26ee71092a4a37c1b3f4a6b4da69e5a8bf91f1372e0e1277d49
7
+ data.tar.gz: 977f72603efb1f9e51af9b0ac8e635684581922fdfa771998a020afea3ab4107439ac1540d0ba06aa56a75e889c251a50f42c554409fba02343fbe4d30867bd7
data/Gemfile.lock CHANGED
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.4.0-universal-java-17)
4
+ cem_acpt (0.5.0-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)
8
- net-ssh (>= 7.0, < 7.1)
8
+ net-ssh (>= 7.1, < 7.2)
9
9
  puppet-modulebuilder (>= 0.0.1)
10
10
  serverspec-cem-acpt
11
11
 
@@ -25,7 +25,7 @@ GEM
25
25
  multi_json (1.15.0)
26
26
  net-scp (4.0.0)
27
27
  net-ssh (>= 2.6.5, < 8.0.0)
28
- net-ssh (7.0.1)
28
+ net-ssh (7.1.0)
29
29
  net-telnet (0.1.1)
30
30
  parallel (1.22.1)
31
31
  parser (3.2.1.1)
data/cem_acpt.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_runtime_dependency 'concurrent-ruby', '>= 1.1', '< 2.0'
31
31
  spec.add_runtime_dependency 'deep_merge', '>= 1.2', '< 2.0'
32
32
  spec.add_runtime_dependency 'ed25519', '>= 1.2', '< 2.0'
33
- spec.add_runtime_dependency 'net-ssh', '>= 7.0', '< 7.1'
33
+ spec.add_runtime_dependency 'net-ssh', '>= 7.1', '< 7.2'
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'
@@ -19,8 +19,7 @@ module CemAcpt
19
19
  KH_PATH = File.join([ENV['HOME'], '.ssh', 'acpt_test_known_hosts']).freeze
20
20
 
21
21
  def log(msg, level = :info)
22
- real_msg = "CONTEXT: #{msg}"
23
- logger.send(level, real_msg)
22
+ logger.send(level, 'CONTEXT') { msg }
24
23
  end
25
24
 
26
25
  # Builds the Puppet module package
@@ -41,13 +41,8 @@ module CemAcpt
41
41
  # @param test_data [Hash] The test data to use to build the image name.
42
42
  # @return [String] The image name.
43
43
  def build(test_data)
44
- logger.debug 'Building image name...'
45
- logger.debug "Using config: #{@config.to_h}"
46
- logger.debug "Test data: #{test_data}"
47
44
  parts = resolve_parts(test_data)
48
- logger.debug "Resolved parts: #{parts}"
49
45
  image_name = create_image_name(parts)
50
- logger.debug "Created image name: #{image_name}"
51
46
  final_image_name = character_substitutions(image_name)
52
47
  logger.debug "Final image name: #{final_image_name}"
53
48
  final_image_name
@@ -61,10 +56,8 @@ module CemAcpt
61
56
  # @return [Array] The parts array with variables resolved.
62
57
  def resolve_parts(test_data)
63
58
  @config[:parts].each_with_object([]) do |part, ary|
64
- logger.debug "Resolving part: #{part}"
65
59
  if part.start_with?('$')
66
60
  var_path = part[1..-1]
67
- logger.debug "Resolving variable path: #{var_path}"
68
61
  ary << test_data.dot_dig(var_path)
69
62
  else
70
63
  ary << part
@@ -77,10 +70,8 @@ module CemAcpt
77
70
  # @return [String] The image name.
78
71
  def create_image_name(parts)
79
72
  image_name = @config[:join_with] ? parts.join(@config[:join_with]) : parts.join
80
- logger.debug("Final image name: #{image_name}")
81
73
 
82
74
  if @config[:validation_pattern]
83
- logger.debug "Validating image name: #{image_name}..."
84
75
  return image_name if image_name.match?(@config[:validation_pattern])
85
76
 
86
77
  raise "Image name #{image_name} does not match validation pattern #{@config[:validation_pattern]}"
@@ -41,51 +41,103 @@ module CemAcpt
41
41
  # correctly formats the log output when using the standard
42
42
  # Ruby Logger class methods such as #info, #debug, etc.
43
43
  class Logger < ::Logger
44
+ attr_accessor :trap_context
45
+
44
46
  def initialize(logdev, shift_age = 0, shift_size = 1_048_576, **kwargs)
45
47
  super(logdev, shift_age, shift_size, **kwargs)
46
- if kwargs[:formatter].is_a?(Formatter::GithubActionFormatter) || ENV['GITHUB_ACTIONS'] || ENV['CI']
47
- @ci_mode = true
48
- end
48
+ @ci_mode = (kwargs[:formatter].is_a?(Formatter::GithubActionFormatter) || !ENV['GITHUB_ACTIONS'].nil? || !ENV['CI'].nil?)
49
+ @raw_logdev = logdev # Used for logging from trap context
50
+ @trap_context = false # If true, will not use the standard Logger methods and will write directly to the logdev
49
51
  end
50
52
 
51
53
  def debug(progname = nil, &block)
52
- unless log_ci_mode('debug', progname, &block)
53
- super
54
- end
54
+ return if log_trap_context('debug', progname, &block)
55
+ return if log_ci_mode('debug', progname, &block)
56
+
57
+ super
55
58
  end
56
59
 
57
60
  def info(progname = nil, &block)
58
- unless log_ci_mode('info', progname, &block)
59
- super
60
- end
61
+ return if log_trap_context('info', progname, &block)
62
+ return if log_ci_mode('info', progname, &block)
63
+
64
+ super
61
65
  end
62
66
 
63
67
  def warn(progname = nil, &block)
64
- unless log_ci_mode('warn', progname, &block)
65
- super
66
- end
68
+ return if log_trap_context('warn', progname, &block)
69
+ return if log_ci_mode('warn', progname, &block)
70
+
71
+ super
67
72
  end
68
73
 
69
74
  def error(progname = nil, &block)
70
- unless log_ci_mode('error', progname, &block)
71
- super
72
- end
75
+ return if log_trap_context('error', progname, &block)
76
+ return if log_ci_mode('error', progname, &block)
77
+
78
+ super
73
79
  end
74
80
 
75
81
  def fatal(progname = nil, &block)
76
- unless log_ci_mode('fatal', progname, &block)
77
- super
78
- end
82
+ return if log_trap_context('fatal', progname, &block)
83
+ return if log_ci_mode('fatal', progname, &block)
84
+
85
+ super
79
86
  end
80
87
 
81
88
  private
82
89
 
90
+ # Can't use the standard Logger methods when in trap context
91
+ # because they don't work. So we have to write directly to the
92
+ # raw logdev. Unlike the standard Logger methods, there is no
93
+ # Mutex lock, so messages are not guaranteed to be in order.
94
+ # This is necessary in the trap context because using a Mutex
95
+ # lock in a trap context will cause a deadlock.
96
+ def log_trap_context(severity, progname = nil, &block)
97
+ return false unless @trap_context
98
+
99
+ if @raw_logdev.respond_to?(:puts)
100
+ @raw_logdev.puts(ci_format(severity, false, progname, &block))
101
+ elsif @raw_logdev.respond_to?(:write)
102
+ @raw_logdev.write(ci_format(severity, true, progname, &block))
103
+ else
104
+ # Default to logging to STDERR
105
+ $stderr.puts(ci_format(severity, false, progname, &block))
106
+ end
107
+ true
108
+ end
109
+
110
+ # CI mode uses << instead of the standard Logger methods
111
+ # because Github Actions wants messages to be echoed. This
112
+ # is a way to ensure that, in the event of using a multi-logger
113
+ # with a standard Logger, the CI mode messages are still
114
+ # formatted correctly.
83
115
  def log_ci_mode(severity, progname = nil, &block)
84
116
  return false unless @ci_mode
85
117
 
86
- self.<<("::#{severity}::#{progname || (block && block.call) || ''}")
118
+ self.<<(ci_format(severity, true, progname, &block))
87
119
  true
88
120
  end
121
+
122
+ # Formats the log message for CI mode, translating the severity
123
+ # level to the correct format for Github Actions and adding
124
+ # the colon separators.
125
+ def ci_format(severity, newline = true, progname = nil, &block)
126
+ sev = if severity == 'fatal'
127
+ 'error'
128
+ elsif severity == 'warn'
129
+ 'warning'
130
+ elsif severity == 'info'
131
+ 'notice'
132
+ else
133
+ severity
134
+ end
135
+ block_message = block_given? ? block.call : nil
136
+ base = [progname, block_message].compact.join(': ')
137
+ return if base.empty?
138
+
139
+ newline ? "::#{sev}::#{base}\n" : "::#{sev}::#{base}"
140
+ end
89
141
  end
90
142
 
91
143
  class << self
@@ -72,7 +72,7 @@ module CemAcpt::Platform::Gcp
72
72
  elsif File.exist?(File.join([ENV['HOME'], '.ssh', 'acpt_test_key']))
73
73
  @ssh_key = File.join([ENV['HOME'], '.ssh', 'acpt_test_key'])
74
74
  else
75
- logger.debug("Test SSH key not found at #{File.join([ENV['HOME'], '.ssh', 'acpt_test_key'])}, using default")
75
+ logger.debug("Using default gcloud SSH key: #{File.join([ENV['HOME'], '.ssh', 'google_compute_engine'])}")
76
76
  @ssh_key = File.join([ENV['HOME'], '.ssh', 'google_compute_engine'])
77
77
  end
78
78
  @ssh_key
@@ -95,7 +95,13 @@ module CemAcpt::Platform::Gcp
95
95
  def local_exec(command, out_format: 'json', out_filter: nil, project_flag: true)
96
96
  final_command = format_gcloud_command(command, out_format: out_format, out_filter: out_filter, project_flag: project_flag)
97
97
  stdout, stderr, status = Open3.capture3(env, final_command)
98
- raise "gcloud command '#{final_command}' failed: #{stderr}" unless status.success?
98
+ unless status.success?
99
+ if ENV['CEM_ACPT_VERBOSE'] && (final_command.start_with?('gcloud compute ssh') || final_command.start_with?('gcloud compute scp'))
100
+ sout, serr, stat = Open3.capture3(env, "#{final_command} --dry-run")
101
+ stat.success? ? logger.debug("Real command: #{sout}") : logger.debug("Failed to get real command: #{serr}")
102
+ end
103
+ raise "gcloud command '#{final_command}' failed: #{stderr}"
104
+ end
99
105
 
100
106
  if format(out_format) == 'json'
101
107
  begin
@@ -135,7 +141,6 @@ module CemAcpt::Platform::Gcp
135
141
  def ssh(instance_name, cmd, ignore_command_errors: false, use_proxy_command: true, opts: {})
136
142
  Net::SSH.start(instance_name, user_name,
137
143
  ssh_opts(instance_name: instance_name, use_proxy_command: use_proxy_command, opts: opts)) do |ssh|
138
- logger.debug("Running SSH command '#{cmd}' on instance '#{instance_name}'")
139
144
  result = ssh.exec!(cmd)
140
145
  if result.exitstatus != 0 && !ignore_command_errors
141
146
  abridged_cmd = cmd.length > 100 ? "#{cmd[0..100]}..." : cmd
@@ -152,11 +157,20 @@ module CemAcpt::Platform::Gcp
152
157
  cmd = [
153
158
  'compute',
154
159
  'scp',
160
+ # "--ssh-key-file=#{ssh_key}",
161
+ # "--strict-host-key-checking=no",
162
+ '--plain', # This suppresses ALL gcloud added SCP flags aside from ProxyCommand
163
+ "--scp-flag='-o IdentityFile=#{ssh_key}'", # -i is bugged
164
+ "--scp-flag='-o CheckHostIP=no'",
165
+ "--scp-flag='-o HashKnownHosts=no'",
166
+ "--scp-flag='-o HostKeyAlias=#{vm_alias(instance_name)}'",
167
+ "--scp-flag='-o IdentitiesOnly=yes'",
168
+ "--scp-flag='-o StrictHostKeyChecking=no'",
169
+ "--scp-flag='-o UserKnownHostsFile=#{ssh_opts[:user_known_hosts_file]}'",
155
170
  ]
156
- cmd << '--strict-host-key-checking=no'
157
171
  cmd << "--tunnel-through-iap" if use_proxy_command
158
172
  cmd << "--recurse" if scp_opts[:recurse]
159
- cmd << "#{local} #{instance_name}:#{remote}"
173
+ cmd << "#{local} #{ssh_opts[:user]}@#{instance_name}:#{remote}"
160
174
  local_exec(cmd.join(' '), out_format: 'json')
161
175
  end
162
176
 
@@ -167,24 +181,32 @@ module CemAcpt::Platform::Gcp
167
181
  cmd = [
168
182
  'compute',
169
183
  'scp',
184
+ # "--ssh-key-file=#{ssh_key}",
185
+ # "--strict-host-key-checking=no",
186
+ '--plain', # This suppresses ALL gcloud added SCP flags aside from ProxyCommand
187
+ "--scp-flag='-o IdentityFile=#{ssh_key}'",
188
+ "--scp-flag='-o CheckHostIP=no'",
189
+ "--scp-flag='-o HashKnownHosts=no'",
190
+ "--scp-flag='-o HostKeyAlias=#{vm_alias(instance_name)}'",
191
+ "--scp-flag='-o IdentitiesOnly=yes'",
192
+ "--scp-flag='-o StrictHostKeyChecking=no'",
193
+ "--scp-flag='-o UserKnownHostsFile=#{ssh_opts[:user_known_hosts_file]}'",
170
194
  ]
171
- cmd << '--strict-host-key-checking=no'
172
195
  cmd << '--tunnel-through-iap' if use_proxy_command
173
196
  cmd << '--recurse' if scp_opts[:recurse]
174
- cmd << "#{instance_name}:#{remote} #{local}"
197
+ cmd << "#{ssh_opts[:user]}@#{instance_name}:#{remote} #{local}"
175
198
  local_exec(cmd.join(' '), out_format: 'json')
176
199
  end
177
200
 
178
201
  def ssh_ready?(instance_name, opts: {})
179
- ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
180
- logger.debug("Testing SSH connection to #{instance_name} with options #{ssh_options}")
202
+ # ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
203
+ # logger.debug("Testing SSH connection to #{instance_name} with options #{ssh_options}")
181
204
  gcloud_ssh(instance_name, 'echo "SSH is ready"')
182
- logger.debug('Removing ecdsa & ed25519 host keys from config due to bug in jruby-openssl')
183
- gcloud_ssh(instance_name, 'sudo sed -E -i "s;HostKey /etc/ssh/ssh_host_(ecdsa|ed25519)_key;;g" /etc/ssh/sshd_config')
184
- logger.debug('Restarting SSH service')
185
- gcloud_ssh(instance_name, 'sudo systemctl restart sshd', ignore_command_errors: true)
205
+ # logger.debug('Removing ecdsa & ed25519 host keys from config due to bug in jruby-openssl')
206
+ # gcloud_ssh(instance_name, 'sudo sed -E -i "s;HostKey /etc/ssh/ssh_host_(ecdsa|ed25519)_key;;g" /etc/ssh/sshd_config')
207
+ # logger.debug('Restarting SSH service')
208
+ # gcloud_ssh(instance_name, 'sudo systemctl restart sshd', ignore_command_errors: true)
186
209
  logger.info("SSH connection to #{instance_name} is ready")
187
- logger.debug('Getting OS release')
188
210
  osr = gcloud_ssh(instance_name, 'sudo cat /etc/os-release')
189
211
  @os_release = CemAcpt::Platform::Utils::Linux::OSRelease.new(osr)
190
212
  logger.debug("OS release: #{@os_release.to_h}")
@@ -195,8 +217,8 @@ module CemAcpt::Platform::Gcp
195
217
  end
196
218
 
197
219
  def dnf_automatic_success?(instance_name, opts: {})
198
- ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
199
- logger.debug("Checking dnf-automatic success on #{instance_name} with options #{ssh_options}")
220
+ # ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
221
+ # logger.debug("Checking dnf-automatic success on #{instance_name} with options #{ssh_options}")
200
222
  gcloud_ssh(instance_name, 'sudo systemctl restart dnf-automatic.timer')
201
223
  true
202
224
  rescue StandardError => e
@@ -205,8 +227,8 @@ module CemAcpt::Platform::Gcp
205
227
  end
206
228
 
207
229
  def rpm_db_check_success?(instance_name, pkgmgr, opts: {})
208
- ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
209
- logger.debug("Checking #{pkgmgr} rpm db on #{instance_name} with options #{ssh_options}")
230
+ # ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
231
+ # logger.debug("Checking #{pkgmgr} rpm db on #{instance_name} with options #{ssh_options}")
210
232
  gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y rpm glibc")
211
233
  gcloud_ssh(instance_name, "sudo rm -f /var/lib/rpm/.rpm.lock")
212
234
  gcloud_ssh(instance_name, "sudo #{pkgmgr} upgrade -y #{pkgmgr}")
@@ -318,11 +340,16 @@ module CemAcpt::Platform::Gcp
318
340
  cmd = [
319
341
  'compute',
320
342
  'ssh',
321
- '--ssh-key-file',
322
- ssh_key,
323
343
  '--tunnel-through-iap',
344
+ '--plain', # This suppresses ALL gcloud added SSH flags aside from ProxyCommand
345
+ "--ssh-flag='-i #{ssh_key}'",
346
+ "--ssh-flag='-o CheckHostIP=no'",
347
+ "--ssh-flag='-o HashKnownHosts=no'",
348
+ "--ssh-flag='-o HostKeyAlias=#{vm_alias(instance_name)}'",
349
+ "--ssh-flag='-o IdentitiesOnly=yes'",
350
+ "--ssh-flag='-o StrictHostKeyChecking=no'",
324
351
  "--ssh-flag='-o UserKnownHostsFile=#{ssh_opts[:user_known_hosts_file]}'",
325
- instance_name,
352
+ "#{ssh_opts[:user]}@#{instance_name}",
326
353
  "--command='#{command}'",
327
354
  ].join(' ')
328
355
  local_exec(cmd)
@@ -352,21 +379,24 @@ module CemAcpt::Platform::Gcp
352
379
  dopts = {
353
380
  auth_methods: ['publickey'],
354
381
  check_host_ip: false,
355
- compression: true,
356
382
  config: false,
383
+ global_known_hosts_file: '/dev/null',
384
+ user_known_hosts_file: '/dev/null',
357
385
  keys: [ssh_key],
358
386
  keys_only: true,
359
387
  kex: ['diffie-hellman-group-exchange-sha256'], # ecdh algos cause jruby to shit the bed
388
+ max_pkt_size: 0x10000,
360
389
  non_interactive: true,
361
390
  port: 22,
362
391
  user: user_name,
363
392
  verify_host_key: :never,
364
393
  }
365
- if CemAcpt::Platform::Utils::Linux::SSHelper.env_ssh_known_hosts?
366
- dopts[:user_known_hosts_file] = CemAcpt::Platform::Utils::Linux::SSHelper.env_ssh_known_hosts
367
- else
368
- dopts[:user_known_hosts_file] = ssh_all_known_hosts.find { |f| File.exist?(File.expand_path(f)) unless f.nil? }
369
- end
394
+ dopts[:logger] = logger if ENV['CEM_ACPT_VERBOSE']
395
+ # if CemAcpt::Platform::Utils::Linux::SSHelper.env_ssh_known_hosts?
396
+ # dopts[:user_known_hosts_file] = CemAcpt::Platform::Utils::Linux::SSHelper.env_ssh_known_hosts
397
+ # else
398
+ # dopts[:user_known_hosts_file] = ssh_all_known_hosts.find { |f| File.exist?(File.expand_path(f)) unless f.nil? }
399
+ # end
370
400
  dopts
371
401
  end
372
402
 
@@ -229,7 +229,6 @@ module CemAcpt::Platform::Gcp
229
229
 
230
230
  def info
231
231
  describe_cmd = "compute instances describe #{name}"
232
- logger.debug("Gathering info for VM #{name} with gcloud command: #{describe_cmd}")
233
232
  data = @cmd.local_exec(describe_cmd, out_format: 'json')
234
233
  opts = @cmd.ssh_opts(instance_name: name)
235
234
  {
@@ -243,7 +242,7 @@ module CemAcpt::Platform::Gcp
243
242
  def create
244
243
  unless @cmd.ssh_key.include?('google_compute_engine')
245
244
  # Add the test ssh key to os-login
246
- logger.debug("Adding test SSH key to os-login for #{name}")
245
+ logger.info("Adding test SSH key #{@cmd.ssh_key}.pub to os-login for #{name}")
247
246
  @cmd.local_exec("compute os-login ssh-keys add --key-file #{@cmd.ssh_key}.pub --project #{project.name} --ttl 1h")
248
247
  end
249
248
  @cmd.local_exec(create_cmd)
@@ -253,12 +252,10 @@ module CemAcpt::Platform::Gcp
253
252
  end
254
253
 
255
254
  def ready?
256
- logger.debug("Checking if VM #{name} is ready")
257
255
  instance_status = @cmd.local_exec('compute instances list', out_filter: "NAME = #{name}").first['status']
258
256
  logger.debug("Instance #{name} status: #{instance_status}")
259
257
  return false unless instance_status == 'RUNNING'
260
258
 
261
- logger.debug("Checking instance #{name} SSH connectivity")
262
259
  @cmd.ssh_ready?(name)
263
260
  rescue StandardError, Exception
264
261
  false
@@ -275,7 +272,6 @@ module CemAcpt::Platform::Gcp
275
272
 
276
273
  def rpm_db_check_success?
277
274
  if os_release.name.match?(%r{^Red Hat.*})
278
- logger.debug("Checking rpmdb on #{name} with platform #{info[:os_release][:platform]}")
279
275
  pkgmgr = os_release.version.match?(%r{^(8|9).*}) ? 'dnf' : 'yum'
280
276
  @cmd.rpm_db_check_success?(name, pkgmgr)
281
277
  else
@@ -20,7 +20,6 @@ module Platform
20
20
  components: creation_params,
21
21
  )
22
22
  @instance.configure!
23
- logger.debug("Creating with command: #{@instance.send(:create_cmd)}")
24
23
  @instance.create
25
24
  end
26
25
 
@@ -31,26 +30,22 @@ module Platform
31
30
 
32
31
  # Returns true if the GCP instance is ready for use in the test suite
33
32
  def ready?
34
- logger.debug("Checking if #{node_name} is ready...")
35
33
  @instance.ready?
36
34
  end
37
35
 
38
36
  # Returns true if dnf_automatic ran successfully on the GCP instance
39
37
  def dnf_automatic_success?
40
- logger.debug("Checking if dnf_automatic ran successfully on #{node_name}...")
41
38
  @instance.dnf_automatic_success?
42
39
  end
43
40
 
44
41
  # Returns true if rpm_db_check ran successfully on the GCP instance
45
42
  def rpm_db_check_success?
46
- logger.debug("Checking if rpm_db_check ran successfully on #{node_name}...")
47
43
  @instance.rpm_db_check_success?
48
44
  end
49
45
 
50
46
  # Runs the test suite against the GCP instance. Must be given a block.
51
47
  # If necessary, can pass information into the block to be used in the test suite.
52
48
  def run_tests(&block)
53
- logger.debug("Running tests for #{node_name}...")
54
49
  block.call @instance.cmd.env
55
50
  end
56
51
 
@@ -58,7 +53,6 @@ module Platform
58
53
  def install_puppet_module_package(module_pkg_path, remote_path = nil, puppet_path = '/opt/puppetlabs/bin/puppet')
59
54
  remote_path = remote_path.nil? ? File.join('/tmp', File.basename(module_pkg_path)) : remote_path
60
55
  logger.info("Uploading module package #{module_pkg_path} to #{remote_path} on #{node_name} and installing it...")
61
- logger.debug("Using puppet path: #{puppet_path}")
62
56
  @instance.install_puppet_module_package(module_pkg_path, remote_path, puppet_path)
63
57
  logger.info("Module package #{module_pkg_path} installed on #{node_name}")
64
58
  end
@@ -28,7 +28,6 @@ module CemAcpt
28
28
 
29
29
  # Validates module metadata by raising exception if invalid
30
30
  _metadata = builder.metadata
31
- logger.debug("Metadata for module #{builder.release_name} is valid")
32
31
 
33
32
  # Builds the module package
34
33
  logger.info("Building module package for #{builder.release_name}")
@@ -14,7 +14,7 @@ module CemAcpt
14
14
 
15
15
  # Holds RSpec options used by Command
16
16
  class Options
17
- OPTIONS = %i[test_path bundle rspec use_bundler bundle_install format debug quiet env].freeze
17
+ OPTIONS = %i[test_path bundle rspec use_bundler bundle_install format debug profile backtrace quiet env].freeze
18
18
 
19
19
  attr_accessor(*OPTIONS)
20
20
 
@@ -65,9 +65,11 @@ module CemAcpt
65
65
  def defaults
66
66
  @defaults ||= { use_bundler: false,
67
67
  bundle_install: false,
68
- format: { documentation: nil },
68
+ format: { progress: nil },
69
69
  debug: false,
70
70
  quiet: false,
71
+ profile: false,
72
+ backtrace: false,
71
73
  env: {} }
72
74
  end
73
75
 
@@ -123,7 +125,11 @@ module CemAcpt
123
125
  # Returns an array representation of the RSpec command
124
126
  def to_a
125
127
  cmd = cmd_base.dup
128
+ cmd << '--no-color'
126
129
  cmd << opts.test_path if opts.test_path
130
+ cmd << '--debug' if opts.debug
131
+ cmd << '--profile' if opts.profile
132
+ cmd << '--backtrace' if opts.backtrace
127
133
  opts.format.each do |fmt, out|
128
134
  cmd += ['--format', fmt.to_s.shellescape]
129
135
  cmd += ['--out', out.to_s.shellescape] if out
@@ -156,10 +162,8 @@ module CemAcpt
156
162
  # the RSpec command.
157
163
  # @return [Integer] The exit code of the RSpec command
158
164
  def execute_pty(log_prefix: 'RSPEC')
159
- async_debug("Executing RSpec command '#{self}' in PTY...", log_prefix)
160
165
  PTY.spawn(opts.env, ENV['SHELL']) do |r, w, pid|
161
166
  @pty_pid = pid
162
- async_debug("Spawned RSpec PTY with PID #{@pty_pid}", log_prefix)
163
167
  export_envs(w)
164
168
  w.puts "#{self}; exit $?"
165
169
  handle_io(r, log_prefix: log_prefix)
@@ -173,14 +177,15 @@ module CemAcpt
173
177
  # the RSpec command.
174
178
  # @return [Integer] The exit code of the RSpec command
175
179
  def execute_no_pty(log_prefix: 'RSPEC')
176
- async_info("Executing RSpec command '#{self}' with Open3.popen2e()...", log_prefix)
177
- exit_status = nil
178
- Open3.popen2e(opts.env, to_s) do |stdin, std_out_err, wait_thr|
179
- stdin.close
180
- handle_io(std_out_err, log_prefix: log_prefix)
181
- exit_status = wait_thr.value
182
- end
183
- exit_status
180
+ _std_out_err, exit_status = Open3.capture2e(opts.env, to_s)
181
+ exit_status.to_i
182
+ # exit_status = nil
183
+ # Open3.popen2e(opts.env, to_s) do |stdin, std_out_err, wait_thr|
184
+ # stdin.close
185
+ # handle_io(std_out_err, log_prefix: log_prefix)
186
+ # exit_status = wait_thr.value
187
+ # end
188
+ # exit_status
184
189
  end
185
190
 
186
191
  # Kills the PTY process with `SIGKILL` if the process exists
@@ -484,7 +484,9 @@ module CemAcpt
484
484
  # total assignable ports are set to all ports in the ephemeral port range.
485
485
  # When a port is allocated, it's removed from the port range.
486
486
  class LocalPortAllocator
487
- EPHEMERAL_PORT_RANGE = (49_152..65_535).to_a.freeze
487
+ # The ephemeral port range is 49,152 to 65,534. We don't use 65,535
488
+ # because MacOS shits the bed when trying to use it.
489
+ EPHEMERAL_PORT_RANGE = (49_152..65_534).to_a.freeze
488
490
 
489
491
  def initialize
490
492
  @port_range = Concurrent::Array.new(EPHEMERAL_PORT_RANGE.dup).shuffle!
@@ -35,7 +35,6 @@ module CemAcpt
35
35
  logger.info 'Gathering acceptance test data...'
36
36
  raise "No 'tests' entry found in config file" unless @config.has?('tests')
37
37
  @config.get('tests').each_with_object([]) do |test_name, a|
38
- logger.debug("Processing test #{test_name}...")
39
38
  test_file = acceptance_tests.find { |f| File.basename(f, '_spec.rb') == test_name }
40
39
  raise "Test file not found for test #{test_name}" unless test_file
41
40
  logger.debug("Test file found for test #{test_name}: #{test_file}")
@@ -28,7 +28,6 @@ module CemAcpt
28
28
  @run_context = run_context
29
29
  @module_pkg_path = Concurrent::IVar.new
30
30
  @runners = Concurrent::Array.new
31
- @thread_pool = Concurrent::SimpleExecutorService.new
32
31
  @logger = use_logger(nil, stage: 'RunHandler')
33
32
  end
34
33
 
@@ -39,13 +38,17 @@ module CemAcpt
39
38
 
40
39
  # Runs the acceptance test suites.
41
40
  def run
42
- @logger.info('creating and starting test runners...')
43
41
  create_runners
44
- @logger.info("created #{@runners.length} runners...")
45
- @runners.map { |r| @thread_pool.post { r.start } }
46
- @thread_pool.shutdown
47
- @thread_pool.wait_for_termination
48
- @thread_pool = Concurrent::SimpleExecutorService.new
42
+ @logger.info("created #{@runners.length} runner(s)...")
43
+ if @runners.length > 1
44
+ @thread_pool = Concurrent::SimpleExecutorService.new
45
+ @runners.map { |r| @thread_pool.post { r.start } }
46
+ @thread_pool.shutdown
47
+ @thread_pool.wait_for_termination
48
+ @thread_pool = Concurrent::SimpleExecutorService.new
49
+ else
50
+ @runners[0].start
51
+ end
49
52
  @logger.info('test runners have all exited...')
50
53
  rescue StandardError => e
51
54
  handle_fatal_error(e)
@@ -56,8 +59,14 @@ module CemAcpt
56
59
  def destroy_test_nodes
57
60
  return if @runners.empty? || @runners.none?(&:node_exists)
58
61
 
59
- @runners.each do |r|
60
- r.send(:destroy) if r.node_exists
62
+ if @runners.length > 1
63
+ @thread_pool.kill
64
+ destroy_thread_pool = Concurrent::SimpleExecutorService.new
65
+ @runners.map { |r| destroy_thread_pool.post { r.destroy } if r.node_exists }
66
+ destroy_thread_pool.shutdown
67
+ destroy_thread_pool.wait_for_termination
68
+ else
69
+ @runners[0].destroy if @runners[0].node_exists
61
70
  end
62
71
  end
63
72
 
@@ -98,6 +107,7 @@ module CemAcpt
98
107
  next
99
108
  end
100
109
  unless runner.test_failures?
110
+ debug_test_results(runner)
101
111
  @logger << "::endgroup::\n"
102
112
  next
103
113
  end
@@ -111,7 +121,7 @@ module CemAcpt
111
121
  else
112
122
  handle_runner_error_results(runner)
113
123
  end
114
- #debug_test_results(runner) if logger.debug?
124
+ debug_test_results(runner)
115
125
  @logger << "::endgroup::\n"
116
126
  end
117
127
  end
@@ -138,8 +148,8 @@ module CemAcpt
138
148
  backtrace = v['backtrace']
139
149
  @logger.error("#{k.upcase}: #{msg}", no_prefix: true) unless msg.nil?
140
150
  @logger.error("BACKTRACE:\n#{backtrace.join("\n")}", no_prefix: true) unless backtrace.nil?
141
- elsif v.is_a?(Hash)
142
- v.each do |k2, v2|
151
+ elsif v.respond_to?(:to_h)
152
+ v.to_h.each do |k2, v2|
143
153
  @logger.error("#{k.upcase}: #{k2.upcase}: #{v2}", no_prefix: true)
144
154
  end
145
155
  else
@@ -192,24 +202,16 @@ module CemAcpt
192
202
 
193
203
  # Logs performance data for the acceptance test suites.
194
204
  # This is only logged if the log level is set to debug.
195
- # def debug_test_results(runner)
196
- # examples_by_time = []
197
- # runner.run_result.to_h.each do |node, result|
198
- # next unless result['examples']
199
-
200
- # result['examples'].each do |e|
201
- # examples_by_time << [e['run_time'], e['id'], e['status'], e['line_number'], node]
202
- # end
203
- # end
204
- # if examples_by_time
205
- # logger.debug('Showing test results in order of execution time...')
206
- # examples_by_time.sort_by(&:first).reverse.each do |e|
207
- # logger.debug("RUNTIME: #{e[0]}; ID: #{e[1]}; STATUS: #{e[2]}; LINE: #{e[3]}; HOST: #{e[4]};")
208
- # end
209
- # else
210
- # logger.debug('No debug results to show')
211
- # end
212
- # end
205
+ def debug_test_results(runner)
206
+ @logger.debug("RSPEC VERSION: #{runner.run_result.rspec_version}", no_prefix: true) if runner.run_result.rspec_version
207
+ @logger.debug("TOTAL RSPEC RUN TIME: #{runner.run_result.duration} seconds", no_prefix: true) if runner.run_result.duration
208
+ num_slowest = runner.run_result.slowest_examples.length
209
+ if num_slowest.positive?
210
+ runner.run_result.slowest_examples.each_with_index do |e, idx|
211
+ @logger.debug("SLOWEST TEST(#{idx + 1} of #{num_slowest}): #{e}", no_prefix: true)
212
+ end
213
+ end
214
+ end
213
215
 
214
216
  # Gracefully handles a fatal error and exits the program.
215
217
  # @param err [StandardError, Exception] the error that caused the fatal error
@@ -47,7 +47,10 @@ module CemAcpt
47
47
  @provision_attempts = 0
48
48
  @provision_start_time = nil
49
49
  @node_exists = false
50
- @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
50
+ @run_result = CemAcpt::TestRunner::RunnerResult.new(@node,
51
+ config: @context.config,
52
+ test_data: @context.test_data,
53
+ debug: @debug_mode)
51
54
  @logger = use_logger(nil, stage: 'Runner', node: @node.node_name, test: @node.test_data[:test_name])
52
55
  validate!
53
56
  end
@@ -59,9 +62,9 @@ module CemAcpt
59
62
  @workflow.run
60
63
  if @workflow.success?
61
64
  @run_result = @workflow.last_result
62
- @workflow.completed_steps.each do |s|
63
- @logger.info("Step '#{s.name}' completed successfully")
64
- end
65
+ # @workflow.completed_steps.each do |s|
66
+ # @logger.info("Step '#{s.name}' completed successfully")
67
+ # end
65
68
  true
66
69
  else
67
70
  @run_result = @workflow.last_error
@@ -70,7 +73,10 @@ module CemAcpt
70
73
  end
71
74
  rescue StandardError => e
72
75
  step_error_logging(e, :fatal)
73
- @run_result = CemAcpt::TestRunner::RunnerResult.new(@node, debug: @debug_mode)
76
+ @run_result = CemAcpt::TestRunner::RunnerResult.new(@node,
77
+ config: @context.config,
78
+ test_data: @context.test_data,
79
+ debug: @debug_mode)
74
80
  @run_result.from_error(e)
75
81
  end
76
82
 
@@ -108,8 +114,8 @@ module CemAcpt
108
114
  "runner failed: #{err.message}"
109
115
  end
110
116
  @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}")
117
+ # @logger.debug("failed runner backtrace:\n#{err.backtrace.join("\n")}")
118
+ # @logger.debug("failed runner test data: #{@node.test_data}")
113
119
  end
114
120
 
115
121
  def log_prefix(prefix)
@@ -16,14 +16,31 @@ module CemAcpt
16
16
  class RunnerResult
17
17
  attr_reader :run_result
18
18
 
19
- def initialize(node, debug: false)
19
+ def initialize(node, config: nil, test_data: nil, debug: false)
20
20
  @node = node
21
- @run_result = {}
21
+ @config = config
22
+ @test_data = test_data
22
23
  @debug = debug
24
+ @run_result = {}
25
+ end
26
+ alias to_h run_result
27
+
28
+ def duration
29
+ return unless run_result['summary']&.key?('duration')
30
+
31
+ @duration ||= run_result['summary']['duration']
32
+ end
33
+
34
+ def slowest_examples
35
+ return [] unless run_result&.key?('examples')
36
+
37
+ @slowest_examples ||= run_result['examples'].sort_by { |e| e['run_time'] }.reverse[0..4].map do |e|
38
+ "#{e['full_description']} (~#{e['run_time'].round(1)} seconds)"
39
+ end
23
40
  end
24
41
 
25
- def to_h
26
- @run_result
42
+ def rspec_version
43
+ run_result['version']
27
44
  end
28
45
 
29
46
  def debug?
@@ -91,8 +108,8 @@ module CemAcpt
91
108
  results.merge(
92
109
  {
93
110
  'debug' => {
94
- 'node' => @node,
95
- 'config' => @config,
111
+ 'node' => @node.node,
112
+ 'config' => @config.to_h,
96
113
  'test_data' => @test_data,
97
114
  }
98
115
  }
@@ -130,7 +130,7 @@ module CemAcpt
130
130
  node: @node,
131
131
  module_pkg_path: module_pkg_path,
132
132
  retryable: kwargs[:retryable] || true,
133
- retry_delay: kwargs[:retry_delay] || 60,
133
+ retry_delay: kwargs[:retry_delay] || 30,
134
134
  }
135
135
  @workflow.add_step(:bootstrap, **opts) do |s|
136
136
  s.opts[:node].install_puppet_module_package(s.opts[:module_pkg_path])
@@ -139,14 +139,14 @@ module CemAcpt
139
139
  case result
140
140
  when :retry_workflow
141
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")}")
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")}")
144
144
  @last_error = step.last_error
145
145
  retry_workflow
146
146
  when :fail
147
147
  log_warn("step '#{step.name}' failed")
148
- log_debug(step.last_error.message)
149
- log_debug(step.last_error.backtrace.join("\n"))
148
+ # log_debug(step.last_error.message)
149
+ # log_debug(step.last_error.backtrace.join("\n"))
150
150
  @last_error = step.last_error
151
151
  if ignore_failures?
152
152
  log_warn("ignoring failure of step '#{step.name}'")
@@ -157,7 +157,7 @@ module CemAcpt
157
157
  end
158
158
  else
159
159
  log_info("step '#{step.name}' succeeded")
160
- log_debug("step '#{step.name}' returned: #{result}")
160
+ # log_debug("step '#{step.name}' returned: #{result}")
161
161
  @completed_steps << step
162
162
  end
163
163
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemAcpt
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/cem_acpt.rb CHANGED
@@ -28,6 +28,20 @@ module CemAcpt
28
28
  level: log_level,
29
29
  formatter: log_formatter,
30
30
  )
31
+ Signal.trap('INT') do
32
+ logger.trap_context = true
33
+ logger.fatal('Signal Handler') { 'Received interrupt signal. Cleaning up test suite...' }
34
+ CemAcpt::Context.clean_up_test_suite(params)
35
+ logger.fatal('Signal Handler') { 'Exiting due to interrupt signal' }
36
+ exit 1
37
+ end
38
+ Signal.trap('TERM') do
39
+ logger.trap_context = true
40
+ logger.fatal('Signal Handler') { 'Received terminate signal. Cleaning up test suite...' }
41
+ CemAcpt::Context.clean_up_test_suite(params)
42
+ logger.fatal('Signal Handler') { 'Exiting due to terminate signal' }
43
+ exit 1
44
+ end
31
45
  exit_code = CemAcpt::Context.with(params, &:run)
32
46
  exit exit_code
33
47
  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.4.0
4
+ version: 0.5.0
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-03-16 00:00:00.000000000 Z
11
+ date: 2023-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -75,10 +75,10 @@ dependencies:
75
75
  requirements:
76
76
  - - ">="
77
77
  - !ruby/object:Gem::Version
78
- version: '7.0'
78
+ version: '7.1'
79
79
  - - "<"
80
80
  - !ruby/object:Gem::Version
81
- version: '7.1'
81
+ version: '7.2'
82
82
  name: net-ssh
83
83
  prerelease: false
84
84
  type: :runtime
@@ -86,10 +86,10 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '7.0'
89
+ version: '7.1'
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
- version: '7.1'
92
+ version: '7.2'
93
93
  - !ruby/object:Gem::Dependency
94
94
  requirement: !ruby/object:Gem::Requirement
95
95
  requirements: