cem_acpt 0.7.3 → 0.8.1
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 +4 -4
- data/.rubocop.yml +3 -1
- data/Gemfile.lock +42 -14
- data/README.md +8 -0
- data/cem_acpt.gemspec +3 -1
- data/exe/cem_acpt_image +0 -0
- data/lib/cem_acpt/action_result.rb +85 -0
- data/lib/cem_acpt/cli.rb +42 -22
- data/lib/cem_acpt/config/base.rb +4 -2
- data/lib/cem_acpt/goss/api.rb +8 -2
- data/lib/cem_acpt/image_builder.rb +64 -72
- data/lib/cem_acpt/logging.rb +41 -30
- data/lib/cem_acpt/platform.rb +4 -5
- data/lib/cem_acpt/provision/terraform/linux.rb +2 -2
- data/lib/cem_acpt/provision/terraform/terraform_cmd.rb +154 -0
- data/lib/cem_acpt/provision/terraform/windows.rb +9 -0
- data/lib/cem_acpt/provision/terraform.rb +41 -48
- data/lib/cem_acpt/provision.rb +1 -1
- data/lib/cem_acpt/puppet_helpers.rb +1 -1
- data/lib/cem_acpt/test_data.rb +3 -3
- data/lib/cem_acpt/test_runner/log_formatter/error_formatter.rb +33 -0
- data/lib/cem_acpt/test_runner/log_formatter.rb +10 -1
- data/lib/cem_acpt/test_runner.rb +151 -72
- data/lib/cem_acpt/utils/shell.rb +90 -0
- data/lib/cem_acpt/utils/ssh.rb +10 -23
- data/lib/cem_acpt/utils/terminal.rb +1 -5
- data/lib/cem_acpt/utils/winrm_runner.rb +160 -0
- data/lib/cem_acpt/utils.rb +59 -1
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +51 -21
- data/lib/terraform/gcp/windows/main.tf +26 -0
- metadata +47 -8
data/lib/cem_acpt/test_runner.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'fileutils'
|
4
|
+
require 'securerandom'
|
5
|
+
require_relative 'action_result'
|
3
6
|
require_relative 'goss'
|
4
7
|
require_relative 'logging'
|
5
8
|
require_relative 'platform'
|
@@ -17,14 +20,17 @@ module CemAcpt
|
|
17
20
|
include CemAcpt::Logging
|
18
21
|
|
19
22
|
attr_reader :duration, :exit_code
|
23
|
+
attr_accessor :run_data # This is opened up mainly for windows use.
|
20
24
|
|
21
25
|
def initialize(config)
|
22
26
|
@config = config
|
23
27
|
@run_data = {}
|
24
28
|
@duration = 0
|
25
29
|
@exit_code = 0
|
26
|
-
@results =
|
30
|
+
@results = []
|
27
31
|
@http_statuses = []
|
32
|
+
@provisioned = false
|
33
|
+
@destroyed = false
|
28
34
|
end
|
29
35
|
|
30
36
|
def inspect
|
@@ -38,149 +44,222 @@ module CemAcpt
|
|
38
44
|
def run
|
39
45
|
@run_data = {}
|
40
46
|
@start_time = Time.now
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
module_dir = config.get('module_dir')
|
48
|
+
@old_dir = Dir.pwd
|
49
|
+
Dir.chdir(module_dir)
|
50
|
+
logger.start_ci_group("CemAcpt v#{CemAcpt::VERSION} run started at #{@start_time}")
|
51
|
+
logger.info('CemAcpt::TestRunner') { "Using module directory: #{module_dir}..." }
|
52
|
+
@run_data[:private_key], @run_data[:public_key], @run_data[:known_hosts] = new_ephemeral_ssh_keys
|
53
|
+
logger.info('CemAcpt::TestRunner') { 'Created ephemeral SSH key pair...' }
|
54
|
+
@run_data[:module_package_path] = build_module_package
|
55
|
+
logger.info('CemAcpt::TestRunner') { "Created module package: #{@run_data[:module_package_path]}..." }
|
56
|
+
@run_data[:test_data] = new_test_data
|
57
|
+
logger.info('CemAcpt::TestRunner') { 'Created test data...' }
|
58
|
+
logger.verbose('CemAcpt::TestRunner') { "Test data: #{@run_data[:test_data]}" }
|
59
|
+
@run_data[:nodes] = new_node_data
|
60
|
+
logger.info('CemAcpt::TestRunner') { 'Created node data...' }
|
61
|
+
logger.verbose('CemAcpt::TestRunner') { "Node data: #{@run_data[:nodes]}" }
|
62
|
+
@instance_names_ips = provision_test_nodes
|
63
|
+
logger.info('CemAcpt::TestRunner') { "Instance names and IPs class: #{@instance_names_ips.class}" }
|
64
|
+
@provisioned = true
|
65
|
+
logger.info('CemAcpt::TestRunner') { 'Provisioned test nodes...' }
|
66
|
+
logger.debug('CemAcpt::TestRunner') { "Instance names and IPs: #{@instance_names_ips}" }
|
67
|
+
# Verifying that we're running on windows nodes or not
|
68
|
+
if config.get('tests').first.include? 'windows'
|
69
|
+
logger.info('CemAcpt') { 'Running on windows nodes...' }
|
70
|
+
upload_module_to_bucket
|
71
|
+
|
72
|
+
@instance_names_ips.each do |k, v|
|
73
|
+
# Login_info here is basically a super charged version of a hash from
|
74
|
+
# instance_names_ips. It contains the username, password, and ip of the
|
75
|
+
# windows node, as well as the test name that will be run on that node.
|
76
|
+
login_info = CemAcpt::Utils.get_windows_login_info(k, v)
|
77
|
+
win_node = CemAcpt::Utils::WinRMRunner::WinNode.new(login_info, @run_data[:win_remote_module_name])
|
78
|
+
win_node.run
|
61
79
|
end
|
62
80
|
end
|
81
|
+
@results = run_tests(@instance_names_ips.map { |_, v| v['ip'] },
|
82
|
+
config.get('actions.only'),
|
83
|
+
config.get('actions.except'))
|
84
|
+
rescue StandardError => e
|
85
|
+
logger.error('CemAcpt::TestRunner') { 'Run failed due to error...' }
|
86
|
+
@results << ActionResult.new(e)
|
63
87
|
ensure
|
88
|
+
logger.end_ci_group
|
64
89
|
clean_up
|
65
90
|
process_test_results
|
91
|
+
Dir.chdir(@old_dir) if @old_dir
|
92
|
+
@results
|
66
93
|
end
|
67
94
|
|
68
|
-
def clean_up(
|
69
|
-
|
70
|
-
|
71
|
-
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}" }
|
72
98
|
|
73
|
-
|
74
|
-
|
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
|
108
|
+
rescue StandardError => e
|
109
|
+
logger.verbose('CemAcpt::TestRunner') { "Error cleaning up: #{e}" }
|
110
|
+
logger.verbose('CemAcpt::TestRunner') { e.backtrace.join("\n") }
|
111
|
+
ensure
|
112
|
+
logger.end_ci_group
|
75
113
|
end
|
76
114
|
|
77
115
|
private
|
78
116
|
|
79
117
|
attr_reader :config
|
80
118
|
|
81
|
-
# @return [Thread] The thread that keeps the terminal alive
|
82
|
-
def keep_terminal_alive
|
83
|
-
return unless config.ci?
|
84
|
-
|
85
|
-
@keep_terminal_alive ||= CemAcpt::Utils::Terminal.keep_terminal_alive
|
86
|
-
end
|
87
|
-
|
88
|
-
def kill_keep_terminal_alive
|
89
|
-
return if @trap_context
|
90
|
-
|
91
|
-
keep_terminal_alive&.kill
|
92
|
-
end
|
93
|
-
|
94
119
|
# @return [String] The path to the module package
|
95
120
|
def build_module_package
|
96
|
-
|
121
|
+
if config.get('tests').first.include? 'windows'
|
122
|
+
CemAcpt::Utils.package_win_module(config.get('module_dir'))
|
123
|
+
else
|
124
|
+
CemAcpt::Utils::Puppet.build_module_package(config.get('module_dir'))
|
125
|
+
end
|
97
126
|
end
|
98
127
|
|
99
128
|
# @return [Array<String>] The paths to the ssh private key, public key, and known hosts file
|
100
129
|
def new_ephemeral_ssh_keys
|
101
130
|
return [nil, nil, nil] if config.get('no_ephemeral_ssh_key')
|
102
131
|
|
132
|
+
logger.info('CemAcpt::TestRunner') { 'Creating ephemeral SSH keys...' }
|
103
133
|
CemAcpt::Utils::SSH::Ephemeral.create
|
104
134
|
end
|
105
135
|
|
106
136
|
def clean_ephemeral_ssh_keys
|
107
137
|
return if config.get('no_ephemeral_ssh_key') || config.get('no_destroy_nodes')
|
108
138
|
|
139
|
+
logger.info('CemAcpt::TestRunner') { 'Cleaning ephemeral SSH keys...' }
|
109
140
|
CemAcpt::Utils::SSH::Ephemeral.clean
|
110
141
|
end
|
111
142
|
|
112
143
|
def new_test_data
|
144
|
+
logger.debug('CemAcpt::TestRunner') { 'Creating new test data...' }
|
113
145
|
CemAcpt::TestData.acceptance_test_data(config)
|
114
146
|
end
|
115
147
|
|
116
148
|
def new_node_data
|
149
|
+
logger.debug('CemAcpt::TestRunner') { 'Creating new node data...' }
|
117
150
|
CemAcpt::Platform.use(config.get('platform.name'), config, @run_data)
|
118
151
|
end
|
119
152
|
|
120
153
|
def provision_test_nodes
|
154
|
+
logger.info('CemAcpt::TestRunner') { 'Provisioning test nodes...' }
|
121
155
|
@provisioner = CemAcpt::Provision.new_provisioner(config, @run_data)
|
122
156
|
@provisioner.provision
|
123
157
|
end
|
124
158
|
|
125
159
|
def destroy_test_nodes
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
129
170
|
end
|
130
171
|
|
131
172
|
def no_destroy
|
132
|
-
logger.warn('CemAcpt') { 'Not destroying test nodes...' }
|
133
|
-
|
134
|
-
|
135
|
-
logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
|
136
|
-
end
|
173
|
+
logger.warn('CemAcpt::TestRunner') { 'Not destroying test nodes because no-destroy-nodes is set...' }
|
174
|
+
@provisioner&.show
|
175
|
+
logger.info('CemAcpt') { "Test SSH Keys:\n Private Key: #{@run_data[:private_key]}\n Public Key:#{@run_data[:public_key]}" }
|
137
176
|
end
|
138
177
|
|
139
178
|
def run_tests(hosts, only_actions, except_actions)
|
140
|
-
|
141
|
-
|
142
|
-
CemAcpt::
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
179
|
+
logger.info('CemAcpt::TestRunner') { 'Running tests...' }
|
180
|
+
logger.verbose('CemAcpt::TestRunner') { "Hosts: #{hosts}" }
|
181
|
+
logger.verbose('CemAcpt::TestRunner') { "Only actions: #{only_actions}" }
|
182
|
+
logger.verbose('CemAcpt::TestRunner') { "Except actions: #{except_actions}" }
|
183
|
+
api_results = CemAcpt::Goss::Api.run_actions_async(hosts,
|
184
|
+
only: only_actions || [],
|
185
|
+
except: except_actions || [])
|
186
|
+
res = []
|
187
|
+
api_results.close unless api_results.closed?
|
188
|
+
while (r = api_results.pop)
|
189
|
+
res << ActionResult.new(r)
|
190
|
+
end
|
191
|
+
res
|
147
192
|
end
|
148
193
|
|
149
194
|
def process_test_results
|
150
|
-
if @results.nil?
|
151
|
-
logger.error('CemAcpt') { 'No test results to process' }
|
195
|
+
if @results.nil? || @results.empty?
|
196
|
+
logger.error('CemAcpt::TestRunner') { 'No test results to process' }
|
152
197
|
@exit_code = 1
|
153
198
|
else
|
199
|
+
logger.info('CemAcpt::TestRunner') { "Processing #{@results.size} test result(s)..." }
|
154
200
|
until @results.empty?
|
155
201
|
result = @results.pop
|
156
202
|
@http_statuses << result.http_status
|
157
203
|
log_test_result(result)
|
158
204
|
end
|
159
205
|
if @http_statuses.empty?
|
160
|
-
logger.error('CemAcpt') { 'No test results to process' }
|
206
|
+
logger.error('CemAcpt::TestRunner') { 'No test results to process' }
|
161
207
|
@exit_code = 1
|
162
208
|
else
|
163
|
-
@exit_code = @http_statuses.any? { |s| s.to_i != 200 } ? 1 : 0
|
209
|
+
@exit_code = (@http_statuses.any? { |s| s.to_i != 200 }) ? 1 : 0
|
164
210
|
end
|
165
211
|
end
|
166
212
|
@duration = Time.now - @start_time
|
167
|
-
logger.info('CemAcpt') { "Test suite finished after ~#{duration.round} seconds." }
|
213
|
+
logger.info('CemAcpt::TestRunner') { "Test suite finished after ~#{duration.round} seconds." }
|
168
214
|
end
|
169
215
|
|
170
216
|
def log_test_result(result)
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
217
|
+
result_log_formatter = LogFormatter.new_formatter(result, config, @instance_names_ips)
|
218
|
+
logger.start_ci_group("Test results for #{result_log_formatter.test_name(result)}")
|
219
|
+
return log_error_test_result(result_log_formatter, result) if result.error?
|
220
|
+
|
221
|
+
logger.info(result_log_formatter.summary(result))
|
222
|
+
formatted_results = result_log_formatter.results(result)
|
223
|
+
formatted_results.each do |r|
|
224
|
+
if r.start_with?('Passed:')
|
225
|
+
logger.verbose { r }
|
226
|
+
elsif r.start_with?('Skipped:')
|
227
|
+
logger.info { r }
|
228
|
+
else
|
229
|
+
logger.error { r }
|
182
230
|
end
|
183
231
|
end
|
232
|
+
ensure
|
233
|
+
logger.end_ci_group
|
234
|
+
end
|
235
|
+
|
236
|
+
def log_error_test_result(formatter, result)
|
237
|
+
logger.fatal { formatter.results(result).join("\n") }
|
238
|
+
end
|
239
|
+
|
240
|
+
# Upload the cem_windows module to the bucket if we're testing the cem_windows module
|
241
|
+
# This should only be done once per cem_acpt run. It's important to update the module_package_path
|
242
|
+
# in the run_data to reflect the new module path if we do end up changing the module name
|
243
|
+
def upload_module_to_bucket
|
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])
|
246
|
+
# Upload the module from the local host to the bucket
|
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' }
|
250
|
+
end
|
251
|
+
|
252
|
+
# We have to clean up our gcp bucket after we're done testing. This will limit the duplicated
|
253
|
+
# modules existing in the bucket.
|
254
|
+
def cleanup_bucket
|
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
|
184
263
|
end
|
185
264
|
end
|
186
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
|
data/lib/cem_acpt/utils/ssh.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'shell'
|
4
4
|
require_relative '../logging'
|
5
5
|
|
6
6
|
module CemAcpt
|
@@ -31,10 +31,8 @@ module CemAcpt
|
|
31
31
|
def create(key_name, **options)
|
32
32
|
delete(key_name) # Delete existing keys with same name
|
33
33
|
cmd = new_keygen_cmd(key_name, **options)
|
34
|
-
logger.debug("Creating SSH key with command: #{cmd}"
|
35
|
-
|
36
|
-
raise "Failed to create SSH key! #{stderr}" unless status.success?
|
37
|
-
|
34
|
+
logger.debug('CemAcpt::Utils::SSH::Keygen') { "Creating SSH key with command: #{cmd}" }
|
35
|
+
CemAcpt::Utils::Shell.run_cmd(cmd, output: logger)
|
38
36
|
key_paths(key_name)
|
39
37
|
end
|
40
38
|
|
@@ -42,7 +40,7 @@ module CemAcpt
|
|
42
40
|
priv_key = key_path(key_name)
|
43
41
|
pub_key = key_path(key_name, public_key: true)
|
44
42
|
if ::File.file?(priv_key)
|
45
|
-
logger.debug("Deleting private key: #{priv_key}"
|
43
|
+
logger.debug('CemAcpt::Utils::SSH::Keygen') { "Deleting private key: #{priv_key}" }
|
46
44
|
::File.delete(priv_key)
|
47
45
|
end
|
48
46
|
if ::File.file?(pub_key)
|
@@ -53,20 +51,11 @@ module CemAcpt
|
|
53
51
|
|
54
52
|
private
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
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 =
|
104
|
-
raise 'Cannot find ssh-keygen
|
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,14 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'concurrent-ruby'
|
4
|
-
|
5
3
|
module CemAcpt
|
6
4
|
module Utils
|
7
5
|
# Terminal-related utilities
|
8
6
|
module Terminal
|
9
7
|
def self.keep_terminal_alive
|
10
|
-
|
11
|
-
executor.post do
|
8
|
+
Thread.new do
|
12
9
|
loop do
|
13
10
|
$stdout.print(".\r")
|
14
11
|
sleep(1)
|
@@ -20,7 +17,6 @@ module CemAcpt
|
|
20
17
|
sleep(1)
|
21
18
|
end
|
22
19
|
end
|
23
|
-
executor
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|