cem_acpt 0.8.0 → 0.8.1

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: 853cc9a89c0feaf798bb5efdfc0c8af47d2664121cddd3794b65a61d5dc80851
4
- data.tar.gz: 6d0d5c67e27918d7897ea567a4134249c8a81e23ea95e6a3850a65585ebdd3e0
3
+ metadata.gz: c7329475f3aa0a02e2b6f65017ed0c24b35769f31eddcff07f306297d99b6558
4
+ data.tar.gz: 346c753606378a5b022f2c0cd5e2f3db37dbc473468828075c1c6b9d1f2bf8d6
5
5
  SHA512:
6
- metadata.gz: 244c701ba966fa878c399efba4953db7be8f5977c906501f9d2da8410583ebf2c2c505c687dc03e8cd97b325f362b28472f68c089065cda21f83d01d16c15d22
7
- data.tar.gz: ae7ca222e4264ed32ed47df2745d76c8347c1b5eab94cd2f0278af581c7c7108fb006232c5bbb4d37b2455559c0826a5630498cd620d4fd85a8948b8a0856c22
6
+ metadata.gz: d3eeb3af0f675509e0e02f658656a21d66e682c2d127196155a44adedade3513f4361dd78bb818070d0bd4bb2e349782d4a475a3d1a7a51b9ab800bcc0d9b1e6
7
+ data.tar.gz: 2ac9dc43f0bd592b22053599060ffce68cccb995b32755686c0c4128cf69c2d125b155738be38a0ad91cbca7e7017109ab0721bfda4adaae4da46ab72e30a00d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.8.0)
4
+ cem_acpt (0.8.1)
5
5
  async-http (>= 0.60, < 0.70)
6
6
  bcrypt_pbkdf (>= 1.0, < 2.0)
7
7
  deep_merge (>= 1.2, < 2.0)
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'open3'
5
5
  require 'stringio'
6
6
  require_relative '../../logging'
7
+ require_relative '../../utils/shell'
7
8
 
8
9
  module CemAcpt
9
10
  module Provision
@@ -81,34 +82,10 @@ module CemAcpt
81
82
  end
82
83
 
83
84
  def run_cmd(cmd, opts = {}, env = {}, suffix: '')
84
- logger.debug('CemAcpt::Provision::TerraformCmd') { "Running command with args: cmd = \"#{cmd}\", opts = \"#{opts}\", env = \"#{env}\", suffix = \"#{suffix}\"" }
85
- val, outerr = execute(format_cmd(cmd, opts, suffix), environment(env))
86
- raise "Error running command: #{cmd}\n#{outerr}" unless val.success?
87
-
88
- outerr
89
- end
90
-
91
- def execute(cmd, env)
92
- logger.debug('CemAcpt::Provision::TerraformCmd') { "Executing command: #{cmd}" }
93
- io_outerr = StringIO.new
94
- val = Open3.popen2e(env, cmd) do |stdin, outerr, wait_thr|
95
- stdin.close
96
- outerr.sync = true
97
- output_thread = Thread.new do
98
- while (line = outerr.readline_nonblock)
99
- logger << line
100
- io_outerr.write(line) unless line.chomp.empty?
101
- end
102
- rescue IO::WaitReadable
103
- retry
104
- rescue EOFError
105
- # Do nothing
106
- end
107
- wait_thr.join
108
- output_thread.exit
109
- wait_thr.value
110
- end
111
- [val, io_outerr.string]
85
+ cmd = format_cmd(cmd, opts, suffix)
86
+ env = environment(env)
87
+ logger.debug('CemAcpt::Provision::TerraformCmd') { "Running command \"#{cmd}\" with environment \"#{env}\"" }
88
+ CemAcpt::Utils::Shell.run_cmd(cmd, env, output: logger)
112
89
  end
113
90
 
114
91
  def chdir(opts = {})
@@ -123,14 +100,10 @@ module CemAcpt
123
100
  end
124
101
 
125
102
  def which_terraform
126
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
127
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
128
- exts.each do |ext|
129
- exe = File.join(path, "terraform#{ext}")
130
- return exe if File.executable?(exe) && !File.directory?(exe)
131
- end
132
- end
133
- raise 'terraform not found in PATH, make sure it is installed'
103
+ path = CemAcpt::Utils::Shell.which('terraform')
104
+ raise 'terraform not found in PATH, make sure it is installed' if path.nil?
105
+
106
+ path
134
107
  end
135
108
 
136
109
  def format_cmd(cmd, opts = {}, suffix = '')
@@ -36,7 +36,13 @@ module CemAcpt
36
36
  terraform_init
37
37
  terraform_plan(formatted_vars, DEFAULT_PLAN_NAME)
38
38
  terraform_apply(DEFAULT_PLAN_NAME)
39
- JSON.parse(terraform_output('instance_name_ip', json: true))
39
+ begin
40
+ output = terraform_output('instance_name_ip', json: true)
41
+ JSON.parse(output)
42
+ rescue JSON::ParserError => e
43
+ logger.error('CemAcpt::Provision::Terraform') { "Error parsing Terraform output: #{output}" }
44
+ raise e
45
+ end
40
46
  end
41
47
 
42
48
  def destroy
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'securerandom'
4
5
  require_relative 'action_result'
5
6
  require_relative 'goss'
@@ -8,7 +9,6 @@ require_relative 'platform'
8
9
  require_relative 'provision'
9
10
  require_relative 'test_data'
10
11
  require_relative 'utils'
11
- require_relative 'utils/winrm_runner'
12
12
  require_relative 'version'
13
13
  require_relative 'test_runner/log_formatter'
14
14
 
@@ -30,6 +30,7 @@ module CemAcpt
30
30
  @results = []
31
31
  @http_statuses = []
32
32
  @provisioned = false
33
+ @destroyed = false
33
34
  end
34
35
 
35
36
  def inspect
@@ -48,7 +49,6 @@ module CemAcpt
48
49
  Dir.chdir(module_dir)
49
50
  logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run started at #{@start_time}")
50
51
  logger.info('CemAcpt::TestRunner') { "Using module directory: #{module_dir}..." }
51
- keep_terminal_alive
52
52
  @run_data[:private_key], @run_data[:public_key], @run_data[:known_hosts] = new_ephemeral_ssh_keys
53
53
  logger.info('CemAcpt::TestRunner') { 'Created ephemeral SSH key pair...' }
54
54
  @run_data[:module_package_path] = build_module_package
@@ -74,7 +74,7 @@ module CemAcpt
74
74
  # instance_names_ips. It contains the username, password, and ip of the
75
75
  # windows node, as well as the test name that will be run on that node.
76
76
  login_info = CemAcpt::Utils.get_windows_login_info(k, v)
77
- win_node = CemAcpt::Utils::WinRMRunner::WinNode.new(login_info, @run_data[:module_package_path].split('/').last)
77
+ win_node = CemAcpt::Utils::WinRMRunner::WinNode.new(login_info, @run_data[:win_remote_module_name])
78
78
  win_node.run
79
79
  end
80
80
  end
@@ -85,43 +85,37 @@ module CemAcpt
85
85
  logger.error('CemAcpt::TestRunner') { 'Run failed due to error...' }
86
86
  @results << ActionResult.new(e)
87
87
  ensure
88
+ logger.end_ci_group
88
89
  clean_up
89
90
  process_test_results
90
91
  Dir.chdir(@old_dir) if @old_dir
91
92
  @results
92
93
  end
93
94
 
94
- def clean_up(trap_context = false)
95
- logger.end_ci_group
96
- kill_keep_terminal_alive unless trap_context
97
- cleanup_bucket # Clean up bucket if we're testing the cem_windows module
98
-
99
- return no_destroy if config.get('no_destroy_nodes')
95
+ def clean_up(_trap_context = false)
96
+ logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run finished at #{Time.now}")
97
+ logger.debug('CemAcpt::TestRunner') { "Starting clean up, provisioned: #{@provisioned}, destroyed: #{@destroyed}" }
100
98
 
101
- clean_ephemeral_ssh_keys
102
- destroy_test_nodes if @provisioned && !@destroyed
99
+ if config.get('no_destroy_nodes')
100
+ logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes because no-destroy-nodes is set...' }
101
+ @provisioner&.show
102
+ logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
103
+ else
104
+ cleanup_bucket # Clean up bucket if we're testing the cem_windows module
105
+ clean_ephemeral_ssh_keys
106
+ destroy_test_nodes
107
+ end
103
108
  rescue StandardError => e
104
109
  logger.verbose('CemAcpt::TestRunner') { "Error cleaning up: #{e}" }
105
110
  logger.verbose('CemAcpt::TestRunner') { e.backtrace.join("\n") }
111
+ ensure
112
+ logger.end_ci_group
106
113
  end
107
114
 
108
115
  private
109
116
 
110
117
  attr_reader :config
111
118
 
112
- # @return [Thread] The thread that keeps the terminal alive
113
- def keep_terminal_alive
114
- return unless config.ci?
115
-
116
- @keep_terminal_alive ||= CemAcpt::Utils::Terminal.keep_terminal_alive
117
- end
118
-
119
- def kill_keep_terminal_alive
120
- return if @trap_context
121
-
122
- keep_terminal_alive&.kill
123
- end
124
-
125
119
  # @return [String] The path to the module package
126
120
  def build_module_package
127
121
  if config.get('tests').first.include? 'windows'
@@ -163,23 +157,22 @@ module CemAcpt
163
157
  end
164
158
 
165
159
  def destroy_test_nodes
166
- return no_destroy if config.get('no_destroy_nodes')
167
-
168
- logger.info('CemAcpt::TestRunner') { 'Destroying test nodes...' }
169
- logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run finished at #{Time.now}")
170
- @provisioner&.destroy
171
- @destroyed = true
172
- ensure
173
- logger.end_ci_group
160
+ logger.info('CemAcpt::TestRunner') { 'Destroying test nodes if necessary...' }
161
+ if !@provisioned
162
+ logger.warn('CemAcpt::TestRunner') { 'Test nodes not provisioned, nothing to destroy...' }
163
+ elsif @destroyed
164
+ logger.warn('CemAcpt::TestRunner') { 'Test nodes already destroyed, not destroying...' }
165
+ else
166
+ logger.info('CemAcpt::TestRunner') { 'Test nodes are provisioned and not destroyed, destroying...' }
167
+ @provisioner&.destroy
168
+ @destroyed = true
169
+ end
174
170
  end
175
171
 
176
172
  def no_destroy
177
- logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes...' }
178
- logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run finished at #{Time.now}")
173
+ logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes because no-destroy-nodes is set...' }
179
174
  @provisioner&.show
180
175
  logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
181
- ensure
182
- logger.end_ci_group
183
176
  end
184
177
 
185
178
  def run_tests(hosts, only_actions, except_actions)
@@ -248,38 +241,25 @@ module CemAcpt
248
241
  # This should only be done once per cem_acpt run. It's important to update the module_package_path
249
242
  # in the run_data to reflect the new module path if we do end up changing the module name
250
243
  def upload_module_to_bucket
251
- curr_module_path = @run_data[:module_package_path].split('/')[0..-2].join('/')
252
- curr_module_name = @run_data[:module_package_path].split('/')[-1]
253
-
254
- logger.debug('CemAcpt') { 'Checking if there duplicate cem_windows module in the bucket already...' }
255
- # Checking if the module already exists in the bucket. Gcloud will return an empty string if the module
256
- # doesn't exist in the bucket.
257
- attempts = 0
258
- until `gcloud storage ls gs://win_cem_acpt/#{curr_module_name}`.empty? do
259
- raise "Failed to rename duplicate module in GCloud" if attempts >= 3
260
-
261
- logger.debug('CemAcpt') { 'Duplicate cem_windows module found. Renaming cem_windows module...' }
262
- # Rename the cem_windows module
263
- curr_module_name = SecureRandom.uuid << curr_module_name
264
- `mv #{@run_data[:module_package_path]} #{curr_module_path}/#{curr_module_name}`
265
- # update the module_package_path in the run_data
266
- @run_data[:module_package_path] = File.join(curr_module_path, curr_module_name)
267
- attempts += 1
268
- end
244
+ @run_data[:win_remote_module_name] = SecureRandom.uuid << File.split(@run_data[:module_package_path]).last
245
+ @run_data[:win_remote_module_path] = File.join('gs://win_cem_acpt', @run_data[:win_remote_module_name])
269
246
  # Upload the module from the local host to the bucket
270
- logger.info('CemAcpt') { 'Transporting cem_windows module...' }
271
- gcloud_cmd = `gcloud storage cp #{@run_data[:module_package_path]} gs://win_cem_acpt`
272
- logger.debug('CemAcpt') { "Uploaded cem_windows module to bucket: #{gcloud_cmd}" }
247
+ logger.info('CemAcpt') { "Uploading #{@run_data[:module_pakage_path]} to #{@run_data[:win_remote_module_path]}..." }
248
+ CemAcpt::Utils::Shell.run_cmd("gcloud storage cp #{@run_data[:module_package_path]} #{@run_data[:win_remote_module_path]}")
249
+ logger.debug('CemAcpt') { 'Successfully uploaded module' }
273
250
  end
274
251
 
275
252
  # We have to clean up our gcp bucket after we're done testing. This will limit the duplicated
276
253
  # modules existing in the bucket.
277
254
  def cleanup_bucket
278
- # Cleanup the module from the bucket
279
- cleanup_cmd = `gcloud storage rm gs://win_cem_acpt/#{@run_data[:module_package_path].split('/')[-1]}`
280
- logger.debug('CemAcpt') { "Removed module from bucket: #{cleanup_cmd}" }
281
- rescue StandardError
282
- logger.info('CemAcpt') { 'No module to clean up in the bucket. You might not be running test for cem_windows' }
255
+ if @run_data[:win_remote_module_path]
256
+ logger.info('CemAcpt::TestRunner') { "Cleaning up bucket #{@run_data[:win_remote_module_path]}..." }
257
+ # Cleanup the module from the bucket
258
+ cleanup_cmd = CemAcpt::Utils::Shell.run_cmd("gcloud storage rm #{@run_data[:win_remote_module_path]}", raise_on_error: false)
259
+ logger.debug('CemAcpt::TestRunner') { "Removed module from bucket: #{cleanup_cmd}" } unless cleanup_cmd.nil? || cleanup_cmd.empty?
260
+ else
261
+ logger.info('CemAcpt::TestRunner') { 'No module to clean up in bucket...' }
262
+ end
283
263
  end
284
264
  end
285
265
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'stringio'
5
+
6
+ module CemAcpt
7
+ # Error class for shell commands
8
+ class ShellCommandError < StandardError; end
9
+
10
+ module Utils
11
+ # Generic utilities for running local shell commands
12
+ module Shell
13
+ # Runs a command in a subshell and returns the Process::Status
14
+ # and the string output of the command.
15
+ # @param cmd [String] The command to run
16
+ # @param env [Hash] A hash of environment variables to set
17
+ # @param output [IO] An IO object that implements #:<< to write the output of the
18
+ # command to in real time. Typically this is a Logger object. Defaults to $stdout.
19
+ # If the object responds to #:debug, the command will be logged at the debug level.
20
+ # @param raise_on_fail [Boolean] Whether to raise an error if the command fails
21
+ # @return [String] The string output of the command
22
+ def self.run_cmd(cmd, env = {}, output: $stdout, raise_on_fail: true)
23
+ io_outerr = StringIO.new
24
+ if output.respond_to?(:debug)
25
+ output.debug('CemAcpt::Utils::Shell') { "Running command:\n\t#{cmd}\nWith environment:\n\t#{env}" }
26
+ else
27
+ output << "Running command:\n\t#{cmd}\nWith environment:\n\t#{env}\n"
28
+ end
29
+ val = Open3.popen2e(env, cmd) do |stdin, outerr, wait_thr|
30
+ stdin.close
31
+ outerr.sync = true
32
+ output_thread = Thread.new do
33
+ while (line = outerr.readline_nonblock)
34
+ output << line if output
35
+ io_outerr.write(line) unless line.chomp.empty?
36
+ end
37
+ rescue IO::WaitReadable
38
+ begin
39
+ IO.select([outerr])
40
+ retry
41
+ rescue IOError
42
+ # outerr closed, won't retry
43
+ end
44
+ rescue EOFError
45
+ # outerr closed, won't retry
46
+ end
47
+ wait_thr.join
48
+ output_thread.exit
49
+ wait_thr.value
50
+ end
51
+ io_string = io_outerr.string
52
+ raise CemAcpt::ShellCommandError, "Error running command: #{cmd}\n#{io_string}" if raise_on_fail && !val.success?
53
+
54
+ io_string
55
+ end
56
+
57
+ # Mimics the behavior of the `which` command.
58
+ # @param cmd [String] The command to find
59
+ # @return [String] The path to the command
60
+ # @return [nil] If the command is not found
61
+ def self.which(cmd)
62
+ return cmd if File.executable?(cmd) && !File.directory?(cmd)
63
+
64
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
65
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
66
+ exts.each do |ext|
67
+ exe = File.join(path, "#{cmd}#{ext}")
68
+ return exe if File.executable?(exe) && !File.directory?(exe)
69
+ end
70
+ end
71
+ nil
72
+ end
73
+
74
+ # IO monkey patch for non-blocking readline
75
+ class ::IO
76
+ def readline_nonblock
77
+ rlnb = []
78
+ rnlb << read_nonblock(1) while rlnb[-1] != "\n"
79
+ rlnb.join
80
+ rescue IO::WaitReadable => blocking
81
+ raise blocking if rlnb.empty?
82
+
83
+ rlnb.join
84
+ rescue EOFError
85
+ rlnb.join
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require_relative 'shell'
4
4
  require_relative '../logging'
5
5
 
6
6
  module CemAcpt
@@ -32,9 +32,7 @@ module CemAcpt
32
32
  delete(key_name) # Delete existing keys with same name
33
33
  cmd = new_keygen_cmd(key_name, **options)
34
34
  logger.debug('CemAcpt::Utils::SSH::Keygen') { "Creating SSH key with command: #{cmd}" }
35
- _stdout, stderr, status = Open3.capture3(cmd)
36
- raise "Failed to create SSH key! #{stderr}" unless status.success?
37
-
35
+ CemAcpt::Utils::Shell.run_cmd(cmd, output: logger)
38
36
  key_paths(key_name)
39
37
  end
40
38
 
@@ -53,20 +51,11 @@ module CemAcpt
53
51
 
54
52
  private
55
53
 
56
- FIND_BIN_PATH_COMMANDS = [
57
- "#{ENV['SHELL']} -c 'command -v ssh-keygen'",
58
- "#{ENV['SHELL']} -c 'which ssh-keygen'",
59
- ].freeze
60
-
61
- def find_bin_path(find_cmd = FIND_BIN_PATH_COMMANDS.first)
62
- bin_path, stderr, status = Open3.capture3(find_cmd)
63
- raise "Cannot find ssh-keygen with command #{find_cmd}: #{stderr}" unless status.success?
64
-
65
- bin_path.chomp
66
- rescue StandardError => e
67
- return find_bin_path(FIND_BIN_PATH_COMMANDS.last) unless FIND_BIN_PATH_COMMANDS.last == find_cmd
54
+ def find_bin_path
55
+ bin_path = CemAcpt::Utils::Shell.which('ssh-keygen')
56
+ raise 'Cannot find command ssh-keygen in PATH, make sure it is installed' if bin_path.nil?
68
57
 
69
- raise e
58
+ bin_path
70
59
  end
71
60
 
72
61
  def new_keygen_cmd(key_name, **options)
@@ -100,12 +89,10 @@ module CemAcpt
100
89
  end
101
90
 
102
91
  def self.ssh_keygen
103
- bin_path = `#{ENV['SHELL']} -c 'command -v ssh-keygen'`.chomp
104
- raise 'Cannot find ssh-keygen! Install it and verify PATH' unless bin_path
92
+ bin_path = CemAcpt::Utils::Shell.which('ssh-keygen')
93
+ raise 'Cannot find command ssh-keygen in PATH, make sure it is installed' if bin_path.nil?
105
94
 
106
95
  bin_path
107
- rescue StandardError => e
108
- raise "Cannot find ssh-keygen! Install it and verify PATH. Orignal error: #{e}"
109
96
  end
110
97
 
111
98
  def self.default_keydir
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'utils/puppet'
4
+ require_relative 'utils/shell'
4
5
  require_relative 'utils/ssh'
5
6
  require_relative 'utils/terminal'
7
+ require_relative 'utils/winrm_runner'
6
8
  require_relative 'logging'
7
9
 
8
10
  module CemAcpt
@@ -19,17 +21,33 @@ module CemAcpt
19
21
  FileUtils.rm_f(package_file)
20
22
  `cd #{module_dir} && touch puppetlabs-cem_windows.tar.gz && tar -czf puppetlabs-cem_windows.tar.gz --exclude=puppetlabs-cem_windows.tar.gz *`
21
23
  logger.info('CemAcpt') { "Windows module packaged at #{package_file}" }
22
- return package_file
24
+ package_file
23
25
  end
24
26
 
25
27
  def reset_password_readiness_polling(instance_name)
26
- result = %x(echo Y | gcloud compute reset-windows-password #{instance_name} --zone=us-west1-b)
27
- while result.empty?
28
+ attempts = 0
29
+ last_error = nil
30
+ result = nil
31
+ begin
32
+ result = CemAcpt::Utils::Shell.run_cmd("echo Y | gcloud compute reset-windows-password #{instance_name} --zone=us-west1-b")
33
+ rescue StandardError => e
34
+ logger.debug('CemAcpt::Utils') { "Error polling for password readiness: #{e}" }
35
+ last_error = e
36
+ end
37
+ while result.nil? || result.empty?
38
+ raise "Instance not ready for password reset. Last error: #{last_error}" if attempts >= 60 # 10 minutes
39
+
28
40
  logger.info('CemAcpt') { "Waiting for instance #{instance_name} to be ready for password reset..." }
29
41
  sleep 10
30
- result = %x(echo Y | gcloud compute reset-windows-password #{instance_name} --zone=us-west1-b)
42
+ begin
43
+ result = CemAcpt::Utils::Shell.run_cmd("echo Y | gcloud compute reset-windows-password #{instance_name} --zone=us-west1-b")
44
+ rescue StandardError => e
45
+ logger.debug('CemAcpt::Utils') { "Error polling for password readiness: #{e}" }
46
+ last_error = e
47
+ end
48
+ attempts += 1
31
49
  end
32
- return result
50
+ result
33
51
  end
34
52
 
35
53
  def get_windows_login_info(instance_name, hash_of_instance)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemAcpt
4
- VERSION = '0.8.0'
4
+ VERSION = '0.8.1'
5
5
  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.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - puppetlabs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-30 00:00:00.000000000 Z
11
+ date: 2023-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -237,6 +237,7 @@ files:
237
237
  - lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb
238
238
  - lib/cem_acpt/utils.rb
239
239
  - lib/cem_acpt/utils/puppet.rb
240
+ - lib/cem_acpt/utils/shell.rb
240
241
  - lib/cem_acpt/utils/ssh.rb
241
242
  - lib/cem_acpt/utils/terminal.rb
242
243
  - lib/cem_acpt/utils/winrm_runner.rb