cem_acpt 0.4.0-universal-java-17 → 0.5.0-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: 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: