cem_acpt 0.8.0 → 0.8.2

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: e7eb7faf465af2f2ea5ab7649db1cf7e05c55573b869966c7e99c49aff498bf7
4
+ data.tar.gz: d79aadcc053957630a339b49a5ea456c5d67cc0dab24b036184cfec3e6c67609
5
5
  SHA512:
6
- metadata.gz: 244c701ba966fa878c399efba4953db7be8f5977c906501f9d2da8410583ebf2c2c505c687dc03e8cd97b325f362b28472f68c089065cda21f83d01d16c15d22
7
- data.tar.gz: ae7ca222e4264ed32ed47df2745d76c8347c1b5eab94cd2f0278af581c7c7108fb006232c5bbb4d37b2455559c0826a5630498cd620d4fd85a8948b8a0856c22
6
+ metadata.gz: cee1f24c8c38d68b01078f56e41963a02f5c3c5e2df8a35907b86e19b20600fe5ba612d7382be02da41b0a38f662a09019f629cf4decae30f35dad14f878fd38
7
+ data.tar.gz: 968a449a29d6f487e802954ca9af7d1d64bd2dd85c7f95cbaf95cecf7357a261a113293e712b8be8b01a593564de885d25c80ab5b01632215b98e8bd43310012
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.2)
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 = '')
@@ -25,6 +25,7 @@ module CemAcpt
25
25
  @module_package_path = nil
26
26
  @private_key = nil
27
27
  @public_key = nil
28
+ @applied = false
28
29
  end
29
30
 
30
31
  # @return [Hash] A hash of instance names and IPs
@@ -36,7 +37,18 @@ module CemAcpt
36
37
  terraform_init
37
38
  terraform_plan(formatted_vars, DEFAULT_PLAN_NAME)
38
39
  terraform_apply(DEFAULT_PLAN_NAME)
39
- JSON.parse(terraform_output('instance_name_ip', json: true))
40
+ @applied = true
41
+ end
42
+
43
+ def output
44
+ raise 'Terraform has not been applied yet' unless @applied
45
+
46
+ output = terraform_output('instance_name_ip', json: true)
47
+ logger.debug('CemAcpt::Provision::Terraform') { "Terraform output:\n#{output}" }
48
+ JSON.parse(output)
49
+ rescue JSON::ParserError => e
50
+ logger.error('CemAcpt::Provision::Terraform') { "Error parsing Terraform output: #{output}" }
51
+ raise e
40
52
  end
41
53
 
42
54
  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
@@ -59,7 +59,8 @@ module CemAcpt
59
59
  @run_data[:nodes] = new_node_data
60
60
  logger.info('CemAcpt::TestRunner') { 'Created node data...' }
61
61
  logger.verbose('CemAcpt::TestRunner') { "Node data: #{@run_data[:nodes]}" }
62
- @instance_names_ips = provision_test_nodes
62
+ provision_test_nodes
63
+ @instance_names_ips = provisioner_output
63
64
  logger.info('CemAcpt::TestRunner') { "Instance names and IPs class: #{@instance_names_ips.class}" }
64
65
  @provisioned = true
65
66
  logger.info('CemAcpt::TestRunner') { 'Provisioned test nodes...' }
@@ -74,7 +75,7 @@ module CemAcpt
74
75
  # instance_names_ips. It contains the username, password, and ip of the
75
76
  # windows node, as well as the test name that will be run on that node.
76
77
  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)
78
+ win_node = CemAcpt::Utils::WinRMRunner::WinNode.new(login_info, @run_data[:win_remote_module_name])
78
79
  win_node.run
79
80
  end
80
81
  end
@@ -85,43 +86,37 @@ module CemAcpt
85
86
  logger.error('CemAcpt::TestRunner') { 'Run failed due to error...' }
86
87
  @results << ActionResult.new(e)
87
88
  ensure
89
+ logger.end_ci_group
88
90
  clean_up
89
91
  process_test_results
90
92
  Dir.chdir(@old_dir) if @old_dir
91
93
  @results
92
94
  end
93
95
 
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')
96
+ def clean_up(_trap_context = false)
97
+ logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run finished at #{Time.now}")
98
+ logger.debug('CemAcpt::TestRunner') { "Starting clean up, provisioned: #{@provisioned}, destroyed: #{@destroyed}" }
100
99
 
101
- clean_ephemeral_ssh_keys
102
- destroy_test_nodes if @provisioned && !@destroyed
100
+ if config.get('no_destroy_nodes')
101
+ logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes because no-destroy-nodes is set...' }
102
+ @provisioner&.show
103
+ logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
104
+ else
105
+ cleanup_bucket # Clean up bucket if we're testing the cem_windows module
106
+ clean_ephemeral_ssh_keys
107
+ destroy_test_nodes
108
+ end
103
109
  rescue StandardError => e
104
110
  logger.verbose('CemAcpt::TestRunner') { "Error cleaning up: #{e}" }
105
111
  logger.verbose('CemAcpt::TestRunner') { e.backtrace.join("\n") }
112
+ ensure
113
+ logger.end_ci_group
106
114
  end
107
115
 
108
116
  private
109
117
 
110
118
  attr_reader :config
111
119
 
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
120
  # @return [String] The path to the module package
126
121
  def build_module_package
127
122
  if config.get('tests').first.include? 'windows'
@@ -162,24 +157,41 @@ module CemAcpt
162
157
  @provisioner.provision
163
158
  end
164
159
 
165
- def destroy_test_nodes
166
- return no_destroy if config.get('no_destroy_nodes')
160
+ def provisioner_output
161
+ logger.info('CemAcpt::TestRunner') { 'Getting provisioner output...' }
162
+ @attempts = 0
163
+ @max_attempts = 3
164
+ output = nil
165
+ while @attempts < @max_attempts
166
+ @attempts += 1
167
+ output = @provisioner.output
168
+ break unless (output.nil? || output.empty?) && @attempts < @max_attempts # Don't sleep if this is last attempt
169
+
170
+ logger.info('CemAcpt::TestRunner') { "Provisioner output is nil or empty, retrying (#{@attempts}/#{@max_attempts})" }
171
+ sleep 3
172
+ end
173
+ raise 'Provisioner output is nil or empty' if output.nil? || output.empty?
167
174
 
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
175
+ output
176
+ end
177
+
178
+ def destroy_test_nodes
179
+ logger.info('CemAcpt::TestRunner') { 'Destroying test nodes if necessary...' }
180
+ if !@provisioned
181
+ logger.warn('CemAcpt::TestRunner') { 'Test nodes not provisioned, nothing to destroy...' }
182
+ elsif @destroyed
183
+ logger.warn('CemAcpt::TestRunner') { 'Test nodes already destroyed, not destroying...' }
184
+ else
185
+ logger.info('CemAcpt::TestRunner') { 'Test nodes are provisioned and not destroyed, destroying...' }
186
+ @provisioner&.destroy
187
+ @destroyed = true
188
+ end
174
189
  end
175
190
 
176
191
  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}")
192
+ logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes because no-destroy-nodes is set...' }
179
193
  @provisioner&.show
180
194
  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
195
  end
184
196
 
185
197
  def run_tests(hosts, only_actions, except_actions)
@@ -248,38 +260,25 @@ module CemAcpt
248
260
  # This should only be done once per cem_acpt run. It's important to update the module_package_path
249
261
  # in the run_data to reflect the new module path if we do end up changing the module name
250
262
  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
263
+ @run_data[:win_remote_module_name] = SecureRandom.uuid << File.split(@run_data[:module_package_path]).last
264
+ @run_data[:win_remote_module_path] = File.join('gs://win_cem_acpt', @run_data[:win_remote_module_name])
269
265
  # 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}" }
266
+ logger.info('CemAcpt') { "Uploading #{@run_data[:module_pakage_path]} to #{@run_data[:win_remote_module_path]}..." }
267
+ CemAcpt::Utils::Shell.run_cmd("gcloud storage cp #{@run_data[:module_package_path]} #{@run_data[:win_remote_module_path]}")
268
+ logger.debug('CemAcpt') { 'Successfully uploaded module' }
273
269
  end
274
270
 
275
271
  # We have to clean up our gcp bucket after we're done testing. This will limit the duplicated
276
272
  # modules existing in the bucket.
277
273
  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' }
274
+ if @run_data[:win_remote_module_path]
275
+ logger.info('CemAcpt::TestRunner') { "Cleaning up bucket #{@run_data[:win_remote_module_path]}..." }
276
+ # Cleanup the module from the bucket
277
+ cleanup_cmd = CemAcpt::Utils::Shell.run_cmd("gcloud storage rm #{@run_data[:win_remote_module_path]}", raise_on_error: false)
278
+ logger.debug('CemAcpt::TestRunner') { "Removed module from bucket: #{cleanup_cmd}" } unless cleanup_cmd.nil? || cleanup_cmd.empty?
279
+ else
280
+ logger.info('CemAcpt::TestRunner') { 'No module to clean up in bucket...' }
281
+ end
283
282
  end
284
283
  end
285
284
  end
@@ -0,0 +1,66 @@
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.gets("\n"))
34
+ output << line if output
35
+ io_outerr.write(line) unless line.chomp.empty?
36
+ end
37
+ end
38
+ wait_thr.join
39
+ output_thread.exit
40
+ wait_thr.value
41
+ end
42
+ io_string = io_outerr.string
43
+ raise CemAcpt::ShellCommandError, "Error running command: #{cmd}\n#{io_string}" if raise_on_fail && !val.success?
44
+
45
+ io_string
46
+ end
47
+
48
+ # Mimics the behavior of the `which` command.
49
+ # @param cmd [String] The command to find
50
+ # @return [String] The path to the command
51
+ # @return [nil] If the command is not found
52
+ def self.which(cmd)
53
+ return cmd if File.executable?(cmd) && !File.directory?(cmd)
54
+
55
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
56
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
57
+ exts.each do |ext|
58
+ exe = File.join(path, "#{cmd}#{ext}")
59
+ return exe if File.executable?(exe) && !File.directory?(exe)
60
+ end
61
+ end
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ 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.2'
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.2
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