cem_acpt 0.1.0 → 0.2.0

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.
@@ -5,8 +5,55 @@ require 'logger'
5
5
  module CemAcpt
6
6
  # Logging for CemAcpt
7
7
  module Logging
8
+ LEVEL_MAP = {
9
+ 'debug' => Logger::DEBUG,
10
+ 'info' => Logger::INFO,
11
+ 'warn' => Logger::WARN,
12
+ 'error' => Logger::ERROR,
13
+ 'fatal' => Logger::FATAL,
14
+ 'unknown' => Logger::UNKNOWN,
15
+ }
16
+
17
+ # Delegator class for when you want to log to multiple devices
18
+ # at the same time, such as STDOUT and a file.
19
+ # @param loggers [::Logger] one or more instances of Logger
20
+ class MultiLogger
21
+ def initialize(*loggers)
22
+ @loggers = loggers
23
+ end
24
+
25
+ def method_missing(m, *args, &block)
26
+ if @loggers.all? { |l| l.respond_to?(m) }
27
+ @loggers.map { |l| l.send(m, *args, &block) }
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def respond_to_missing?(m, include_private = false)
34
+ @loggers.all? { |l| l.respond_to?(m) } || super
35
+ end
36
+ end
37
+
8
38
  class << self
9
- attr_writer :logger
39
+ def new_logger(*logdevs, **configs)
40
+ new_configs = current_log_config.merge(configs.reject { |_, v| v.nil? })
41
+ if new_configs.key?(:level) && !LEVEL_MAP.values.include?(new_configs[:level])
42
+ new_configs[:level] = LEVEL_MAP[new_configs[:level].downcase]
43
+ end
44
+ loggers = logdevs.map do |dev|
45
+ logdev = dev.is_a?(String) ? $stdout : dev
46
+ logger = Logger.new(
47
+ logdev,
48
+ new_configs[:shift_age],
49
+ new_configs[:shift_size],
50
+ **new_configs.reject { |k, _| %i[logdev shift_age shift_size].include?(k) },
51
+ )
52
+ logger.reopen(dev) if dev.is_a?(String)
53
+ logger
54
+ end
55
+ @logger = CemAcpt::Logging::MultiLogger.new(*loggers)
56
+ end
10
57
 
11
58
  # Exposes a logger instance. Will either use the currently set logger or
12
59
  # create a new one.
@@ -29,22 +76,9 @@ module CemAcpt
29
76
  # Shortcut method to set logger.level
30
77
  # @param level [String] the log level to set
31
78
  def new_log_level(level)
32
- case level.downcase
33
- when 'debug'
34
- @logger.level = Logger::DEBUG
35
- when 'info'
36
- @logger.level = Logger::INFO
37
- when 'warn'
38
- @logger.level = Logger::WARN
39
- when 'error'
40
- @logger.level = Logger::ERROR
41
- when 'fatal'
42
- @logger.level = Logger::FATAL
43
- when 'unknown'
44
- @logger.level = Logger::UNKNOWN
45
- else
46
- raise "Cannot set log level #{level}: invalid level"
47
- end
79
+ raise ArgumentError, 'Log level not recognized' unless LEVEL_MAP[level.downcase]
80
+
81
+ @logger.level = LEVEL_MAP[level.downcase]
48
82
  end
49
83
 
50
84
  # Shows the current log format style if set, or the default if not.
@@ -64,7 +98,7 @@ module CemAcpt
64
98
  @current_log_format = :json
65
99
  proc do |severity, datetime, progname, msg|
66
100
  {
67
- timestamp: datetime.iso8601(3),
101
+ timestamp: datetime,
68
102
  progname: progname,
69
103
  severity: severity,
70
104
  message: msg,
@@ -75,10 +109,22 @@ module CemAcpt
75
109
  proc do |severity, _datetime, _progname, msg|
76
110
  "#{severity} - #{msg}\n"
77
111
  end
112
+ when :github_action
113
+ @current_log_format = :github_action
114
+ sev_map = {
115
+ 'DEBUG' => '::debug::',
116
+ 'INFO' => '::notice::',
117
+ 'WARN' => '::warning::',
118
+ 'ERROR' => '::error::',
119
+ 'FATAL' => '::error::',
120
+ }
121
+ proc do |severity, _datetime, _progname, msg|
122
+ "#{sev_map[severity]}{#{msg}}\n"
123
+ end
78
124
  else
79
125
  @current_log_format = :file
80
126
  proc do |severity, datetime, _progname, msg|
81
- "[#{datetime.iso8601(3)}] #{severity}: #{msg}\n"
127
+ "[#{datetime}] #{severity}: #{msg}\n"
82
128
  end
83
129
  end
84
130
  end
@@ -143,6 +189,10 @@ module CemAcpt
143
189
  # Provides class method wrappers for logging methods
144
190
  def self.included(base)
145
191
  class << base
192
+ def new_logger(*logdevs, **configs)
193
+ CemAcpt::Logging.new_logger(*logdevs, **configs)
194
+ end
195
+
146
196
  def logger
147
197
  CemAcpt::Logging.logger
148
198
  end
@@ -155,6 +205,14 @@ module CemAcpt
155
205
  CemAcpt::Logging.new_log_level(level)
156
206
  end
157
207
 
208
+ def current_log_format
209
+ CemAcpt::Logging.current_log_format
210
+ end
211
+
212
+ def new_log_formatter(f)
213
+ CemAcpt::Logging.new_log_formatter(f)
214
+ end
215
+
158
216
  def current_log_config
159
217
  CemAcpt::Logging.current_log_config
160
218
  end
@@ -165,6 +223,10 @@ module CemAcpt
165
223
  end
166
224
  end
167
225
 
226
+ def new_logger(*logdevs, **configs)
227
+ CemAcpt::Logging.new_logger(*logdevs, **configs)
228
+ end
229
+
168
230
  # Exposes the logger instance
169
231
  def logger
170
232
  CemAcpt::Logging.logger
@@ -180,6 +242,14 @@ module CemAcpt
180
242
  CemAcpt::Logging.new_log_level(level)
181
243
  end
182
244
 
245
+ def current_log_format
246
+ CemAcpt::Logging.current_log_format
247
+ end
248
+
249
+ def new_log_formatter(f)
250
+ CemAcpt::Logging.new_log_formatter(f)
251
+ end
252
+
183
253
  # Exposes the current log config
184
254
  def current_log_config
185
255
  CemAcpt::Logging.current_log_config
@@ -190,4 +260,92 @@ module CemAcpt
190
260
  CemAcpt::Logging.new_log_config(logdev: logdev, shift_age: shift_age, shift_size: shift_size, level: level, formatter: formatter, datetime_format: datetime_format)
191
261
  end
192
262
  end
263
+
264
+ module LoggingAsync
265
+ require 'concurrent-ruby'
266
+
267
+ class << self
268
+ def log_write_thread
269
+ @log_write_thread ||= Concurrent::SingleThreadExecutor.new
270
+ end
271
+
272
+ def async_debug(message, prefix = nil)
273
+ msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
274
+ CemAcpt::Logging.logger.debug(msg)
275
+ end
276
+
277
+ def async_info(message, prefix = nil)
278
+ msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
279
+ CemAcpt::Logging.logger.info(msg)
280
+ end
281
+
282
+ def async_warn(message, prefix = nil)
283
+ msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
284
+ CemAcpt::Logging.logger.warn(msg)
285
+ end
286
+
287
+ def async_error(message, prefix = nil)
288
+ msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
289
+ CemAcpt::Logging.logger.error(msg)
290
+ end
291
+
292
+ def async_fatal(message, prefix = nil)
293
+ msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
294
+ CemAcpt::Logging.logger.fatal(msg)
295
+ end
296
+ end
297
+
298
+ # Provides class method wrappers for logging methods
299
+ def self.included(base)
300
+ class << base
301
+ def log_write_thread
302
+ CemAcpt::LoggingAsync.log_write_thread
303
+ end
304
+
305
+ def async_debug(message, prefix = nil)
306
+ CemAcpt::LoggingAsync.async_debug(message, prefix)
307
+ end
308
+
309
+ def async_info(message, prefix = nil)
310
+ CemAcpt::LoggingAsync.async_info(message, prefix)
311
+ end
312
+
313
+ def async_warn(message, prefix = nil)
314
+ CemAcpt::LoggingAsync.async_warn(message, prefix)
315
+ end
316
+
317
+ def async_error(message, prefix = nil)
318
+ CemAcpt::LoggingAsync.async_error(message, prefix)
319
+ end
320
+
321
+ def async_fatal(message, prefix = nil)
322
+ CemAcpt::LoggingAsync.async_fatal(message, prefix)
323
+ end
324
+ end
325
+ end
326
+
327
+ def log_write_thread
328
+ CemAcpt::LoggingAsync.log_write_thread
329
+ end
330
+
331
+ def async_debug(message, prefix = nil)
332
+ CemAcpt::LoggingAsync.async_debug(message, prefix)
333
+ end
334
+
335
+ def async_info(message, prefix = nil)
336
+ CemAcpt::LoggingAsync.async_info(message, prefix)
337
+ end
338
+
339
+ def async_warn(message, prefix = nil)
340
+ CemAcpt::LoggingAsync.async_warn(message, prefix)
341
+ end
342
+
343
+ def async_error(message, prefix = nil)
344
+ CemAcpt::LoggingAsync.async_error(message, prefix)
345
+ end
346
+
347
+ def async_fatal(message, prefix = nil)
348
+ CemAcpt::LoggingAsync.async_fatal(message, prefix)
349
+ end
350
+ end
193
351
  end
@@ -3,11 +3,17 @@
3
3
  module CemAcpt::Platform
4
4
  require_relative File.join(__dir__, '..', '..', 'logging.rb')
5
5
 
6
+ class CmdError < StandardError; end
7
+
6
8
  # Base class for command providers. Provides an API for subclasses to implement.
7
9
  class CmdBase
8
10
  include CemAcpt::Logging
9
11
 
10
- def initialize(*args, **kwargs); end
12
+ attr_reader :env
13
+
14
+ def initialize(*_args, env: {}, **_kwargs)
15
+ @env = env
16
+ end
11
17
 
12
18
  def local_exec(*_args, **_kwargs)
13
19
  raise NotImplementedError, '#local_exec must be implemented by a subclass'
@@ -56,7 +62,7 @@ module CemAcpt::Platform
56
62
  return output
57
63
  rescue StandardError => e
58
64
  last_error = e
59
- sleep(5)
65
+ sleep(10)
60
66
  end
61
67
  end
62
68
  raise last_error
@@ -1,19 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'open3'
5
- require_relative File.join(__dir__, '..', 'base', 'cmd.rb')
6
-
7
3
  module CemAcpt::Platform::Gcp
4
+ require 'json'
5
+ require 'open3'
6
+ require_relative File.join(__dir__, '..', 'base', 'cmd.rb')
7
+
8
8
  # This class provides methods to run gcloud commands. It allows for default values to be
9
9
  # set for the project, zone, and user and can also find these values from the local config.
10
10
  # Additionally, this class provides a way to run SSH commands against GCP VMs using IAP.
11
11
  class Cmd < CemAcpt::Platform::CmdBase
12
- def initialize(project: nil, zone: nil, out_format: nil, filter: nil, user_name: nil, local_port: nil)
13
- super
12
+ def initialize(project: nil, zone: nil, out_format: nil, filter: nil, user_name: nil, local_port: nil, ssh_key: nil)
13
+ super(env: { 'CLOUDSDK_PYTHON_SITEPACKAGES' => '1' })
14
14
  require 'net/ssh'
15
15
  require 'net/ssh/proxy/command'
16
- require 'net/scp'
17
16
 
18
17
  @project = project unless project.nil?
19
18
  @zone = zone unless zone.nil?
@@ -21,8 +20,9 @@ module CemAcpt::Platform::Gcp
21
20
  @default_filter = filter
22
21
  @user_name = user_name
23
22
  @local_port = local_port
24
- raise 'gcloud command not available' unless gcloud?
25
- raise 'gcloud is not authenticated' unless authenticated?
23
+ @ssh_key = ssh_key
24
+ raise CemAcpt::Platform::CmdError, 'gcloud command not available' unless gcloud?
25
+ raise CemAcpt::Platform::CmdError, 'gcloud is not authenticated' unless authenticated?
26
26
  end
27
27
 
28
28
  # Returns either the project name passed in during object initialization
@@ -40,25 +40,37 @@ module CemAcpt::Platform::Gcp
40
40
  # Returns either the user name passed in during object initialization
41
41
  # or queries the current active, authenticated user from gcloud and returns the name.
42
42
  def user_name
43
- @user_name ||= authenticated_user_name&.chomp
43
+ @user_name ||= authenticated_user_name
44
44
  end
45
45
 
46
46
  # Returns the format string passed in during object initialization or
47
47
  # the default format string.
48
48
  def format(out_format = nil)
49
- out_format&.chomp || @default_out_format&.chomp
49
+ out_format&.chomp || @default_out_format
50
50
  end
51
51
 
52
52
  # Returns the filter string passed in during object initialization or
53
53
  # the default filter string.
54
54
  def filter(out_filter = nil)
55
- out_filter&.chomp || @default_filter&.chomp
55
+ out_filter&.chomp || @default_filter
56
56
  end
57
57
 
58
58
  def local_port
59
59
  @local_port ||= rand(49_512..65_535)
60
60
  end
61
61
 
62
+ def ssh_key
63
+ return @ssh_key unless @ssh_key.nil?
64
+
65
+ if File.exist?(File.join([ENV['HOME'], '.ssh', 'acpt_test_key']))
66
+ @ssh_key = File.join([ENV['HOME'], '.ssh', 'acpt_test_key'])
67
+ else
68
+ logger.debug("Test SSH key not found at #{File.join([ENV['HOME'], '.ssh', 'acpt_test_key'])}, using default")
69
+ @ssh_key = File.join([ENV['HOME'], '.ssh', 'google_compute_engine'])
70
+ end
71
+ @ssh_key
72
+ end
73
+
62
74
  # Returns a formatted hash of ssh options to be used with Net::SSH.start.
63
75
  # If you pass in a GCP VM instance name, this method will configure the
64
76
  # IAP tunnel ProxyCommand to use. If you pass in an opts hash, it will
@@ -74,21 +86,26 @@ module CemAcpt::Platform::Gcp
74
86
 
75
87
  # Runs `gcloud` commands locally.
76
88
  def local_exec(command, out_format: 'json', out_filter: nil, project_flag: true)
77
- cmd_parts = ['gcloud', command]
78
- cmd_parts << "--project=#{project.chomp}" unless !project_flag || project.nil?
79
- cmd_parts << "--format=#{format(out_format)}" if format(out_format)
80
- cmd_parts << "--filter=\"#{filter(out_filter)}\"" if filter(out_filter)
81
- final_command = cmd_parts.join(' ')
82
- stdout, stderr, status = Open3.capture3(final_command)
89
+ final_command = format_gcloud_command(command, out_format: out_format, out_filter: out_filter, project_flag: project_flag)
90
+ stdout, stderr, status = Open3.capture3(env, final_command)
83
91
  raise "gcloud command '#{final_command}' failed: #{stderr}" unless status.success?
84
92
 
85
- begin
86
- return ::JSON.parse(stdout) if format(out_format) == 'json'
87
- rescue ::JSON::ParserError
88
- return stdout
93
+ if format(out_format) == 'json'
94
+ begin
95
+ ::JSON.parse(stdout)
96
+ rescue ::JSON::ParserError
97
+ <<<<<<< HEAD
98
+ stdout
99
+ end
100
+ else
101
+ stdout
102
+ =======
103
+ stdout.chomp
104
+ end
105
+ else
106
+ stdout.chomp
107
+ >>>>>>> 489757f (Fixes for race conditions)
89
108
  end
90
-
91
- stdout
92
109
  end
93
110
 
94
111
  # Stops a GCP VM instance.
@@ -112,7 +129,9 @@ module CemAcpt::Platform::Gcp
112
129
  # on the given CGP VM via SSH. Using `ssh` does not invoke the gcloud command
113
130
  # and is dependent on the system's SSH configuration.
114
131
  def ssh(instance_name, cmd, ignore_command_errors: false, use_proxy_command: true, opts: {})
115
- Net::SSH.start(instance_name, user_name, ssh_opts(instance_name: instance_name, use_proxy_command: use_proxy_command, opts: opts)) do |ssh|
132
+ Net::SSH.start(instance_name, user_name,
133
+ ssh_opts(instance_name: instance_name, use_proxy_command: use_proxy_command, opts: opts)) do |ssh|
134
+ logger.debug("Running SSH command '#{cmd}' on instance '#{instance_name}'")
116
135
  result = ssh.exec!(cmd)
117
136
  if result.exitstatus != 0 && !ignore_command_errors
118
137
  abridged_cmd = cmd.length > 100 ? "#{cmd[0..100]}..." : cmd
@@ -126,29 +145,45 @@ module CemAcpt::Platform::Gcp
126
145
  def scp_upload(instance_name, local, remote, use_proxy_command: true, scp_opts: {}, opts: {})
127
146
  raise "Local file #{local} does not exist" unless File.exist?(local)
128
147
 
129
- hostname = use_proxy_command ? vm_alias(instance_name) : instance_name
130
- Net::SCP.start(hostname, user_name, ssh_opts(instance_name: instance_name, use_proxy_command: use_proxy_command, opts: opts)) do |scp|
131
- scp.upload(local, remote, scp_opts).wait
132
- end
148
+ cmd = [
149
+ 'compute',
150
+ 'scp',
151
+ ]
152
+ cmd << '--strict-host-key-checking=no'
153
+ cmd << "--tunnel-through-iap" if use_proxy_command
154
+ cmd << "--recurse" if scp_opts[:recurse]
155
+ cmd << "#{local} #{instance_name}:#{remote}"
156
+ local_exec(cmd.join(' '), out_format: 'json')
133
157
  end
134
158
 
135
159
  # Downloads a file from the specified VM.
136
160
  def scp_download(instance_name, remote, local, use_proxy_command: true, scp_opts: {}, opts: {})
137
161
  raise "Local file #{local} does not exist" unless File.exist?(local)
138
162
 
139
- hostname = use_proxy_command ? vm_alias(instance_name) : instance_name
140
- Net::SCP.start(hostname, user_name, ssh_opts(instance_name: instance_name, use_proxy_command: use_proxy_command, opts: opts)) do |scp|
141
- scp.download(remote, local, scp_opts).wait
142
- end
163
+ cmd = [
164
+ 'compute',
165
+ 'scp',
166
+ ]
167
+ cmd << '--strict-host-key-checking=no'
168
+ cmd << '--tunnel-through-iap' if use_proxy_command
169
+ cmd << '--recurse' if scp_opts[:recurse]
170
+ cmd << "#{instance_name}:#{remote} #{local}"
171
+ local_exec(cmd.join(' '), out_format: 'json')
143
172
  end
144
173
 
145
- def ssh_ready?(instance_name, timeout = 300, opts: {})
146
- with_timed_retry(timeout) do
147
- logger.debug("Testing SSH connection to #{instance_name}")
148
- Net::SSH.start(instance_name, user_name, ssh_opts(instance_name: instance_name, opts: opts)) do |_|
149
- true
150
- end
151
- end
174
+ def ssh_ready?(instance_name, opts: {})
175
+ ssh_options = ssh_opts(instance_name: instance_name, opts: opts)
176
+ logger.debug("Testing SSH connection to #{instance_name} with options #{ssh_options}")
177
+ gcloud_ssh(instance_name, 'echo "SSH is ready"')
178
+ logger.debug('Removing ecdsa & ed25519 host keys from config due to bug in jruby-openssl')
179
+ gcloud_ssh(instance_name, 'sudo sed -E -i "s;HostKey /etc/ssh/ssh_host_(ecdsa|ed25519)_key;;g" /etc/ssh/sshd_config')
180
+ logger.debug('Restarting SSH service')
181
+ gcloud_ssh(instance_name, 'sudo systemctl restart sshd', ignore_command_errors: true)
182
+ logger.info("SSH connection to #{instance_name} is ready")
183
+ true
184
+ rescue StandardError => e
185
+ logger.debug("SSH connection to #{instance_name} failed: #{e}")
186
+ false
152
187
  end
153
188
 
154
189
  # This function spawns a background thread to run a GCP IAP tunnel, run the given
@@ -160,9 +195,7 @@ module CemAcpt::Platform::Gcp
160
195
  # @param instance_name [String] The name of the GCP VM instance to connect to.
161
196
  # @param instance_port [Integer] The port to connect to on the GCP VM instance.
162
197
  # @param disable_connection_check [Boolean] If true, the connection check will be disabled.
163
- def with_iap_tunnel(instance_name, instance_port = 22, disable_connection_check: false)
164
- return unless block_given?
165
-
198
+ def with_iap_tunnel(instance_name, instance_port = 22, disable_connection_check: false, &block)
166
199
  cmd = [
167
200
  'compute start-iap-tunnel',
168
201
  "#{instance_name} #{instance_port}",
@@ -174,16 +207,12 @@ module CemAcpt::Platform::Gcp
174
207
  port: local_port,
175
208
  forward_agent: false,
176
209
  }
210
+ final_cmd = format_gcloud_command(cmd.join(' '), out_format: nil, project_flag: false)
211
+ tunnel_pid = Process.spawn(env, final_cmd)
177
212
  begin
178
- thread = Thread.new do
179
- local_exec(cmd.join(' '))
180
- end
181
- yield ssh_opts(use_proxy_command: false, opts: tunnel_ssh_opts)
182
- thread.exit
183
- rescue IOError
184
- # Ignore errors when killing the thread.
213
+ block.call(ssh_opts(use_proxy_command: false, opts: tunnel_ssh_opts))
185
214
  ensure
186
- thread.exit
215
+ Process.kill('TERM', tunnel_pid)
187
216
  end
188
217
  end
189
218
 
@@ -205,47 +234,87 @@ module CemAcpt::Platform::Gcp
205
234
  instance_name,
206
235
  command.join(' '),
207
236
  ignore_command_errors: true,
208
- use_proxy_command: false,
209
237
  opts: ssh_opts,
210
238
  )
211
239
  end
212
240
 
213
241
  def apply_manifest(instance_name, manifest, opts = {})
214
- require 'tempfile'
215
-
216
- temp_manifest = Tempfile.new('acpt_manifest')
217
- temp_manifest.write(manifest)
218
- temp_manifest.close
219
- begin
220
- scp_upload(
221
- instance_name,
222
- temp_manifest.path,
223
- '/tmp/acpt_manifest.pp',
224
- opts: opts[:ssh_opts],
225
- )
226
- ensure
227
- temp_manifest.unlink
242
+ <<<<<<< HEAD
243
+ keep_manifest_file = opts[:apply][:keep_manifest_file] || false
244
+ with_temp_manifest_file(manifest, keep_manifest_file: keep_manifest_file) do |tf|
245
+ upload_temp_manifest(instance_name, tf.path, opts: opts)
228
246
  end
229
- apply_cmd = [
230
- 'sudo -n -u root -i',
231
- "#{opts[:puppet_path]} apply /tmp/acpt_manifest.pp"
232
- ]
247
+ apply_cmd = ["#{opts[:puppet_path]} apply /tmp/acpt_manifest.pp"]
248
+ =======
249
+ unless opts[:apply][:no_upload]
250
+ with_temp_manifest_file(manifest) do |tf|
251
+ upload_temp_manifest(instance_name, tf.path, remote_path: '/tmp/acpt_manifest.pp', opts: opts)
252
+ end
253
+ end
254
+ apply_cmd = [opts[:puppet_path], 'apply', '/tmp/acpt_manifest.pp']
255
+ >>>>>>> 489757f (Fixes for race conditions)
233
256
  apply_cmd << '--trace' if opts[:apply][:trace]
234
257
  apply_cmd << "--hiera_config=#{opts[:apply][:hiera_config]}" if opts[:apply][:hiera_config]
235
258
  apply_cmd << '--debug' if opts[:apply][:debug]
236
259
  apply_cmd << '--noop' if opts[:apply][:noop]
237
260
  apply_cmd << '--detailed-exitcodes' if opts[:apply][:detailed_exitcodes]
238
261
 
239
- ssh(
262
+ run_shell(
240
263
  instance_name,
241
264
  apply_cmd.join(' '),
242
- ignore_command_errors: true,
243
- opts: opts[:ssh_opts]
265
+ opts
244
266
  )
245
267
  end
246
268
 
247
269
  private
248
270
 
271
+ def format_gcloud_command(command, out_format: 'json', out_filter: nil, project_flag: true)
272
+ cmd_parts = ['gcloud', command]
273
+ cmd_parts << "--project=#{project.chomp}" unless !project_flag || project.nil?
274
+ cmd_parts << "--format=#{format(out_format)}" if format(out_format)
275
+ cmd_parts << "--filter=\"#{filter(out_filter)}\"" if filter(out_filter)
276
+ cmd_parts.join(' ')
277
+ end
278
+
279
+ <<<<<<< HEAD
280
+ def with_temp_manifest_file(manifest, file_name: 'acpt_manifest', keep_manifest_file: false)
281
+ =======
282
+ def with_temp_manifest_file(manifest, file_name: 'acpt_manifest')
283
+ >>>>>>> 489757f (Fixes for race conditions)
284
+ require 'tempfile'
285
+ tf = Tempfile.new(file_name)
286
+ tf.write(manifest)
287
+ tf.close
288
+ begin
289
+ yield tf
290
+ ensure
291
+ <<<<<<< HEAD
292
+ if keep_manifest_file
293
+ @manifest_file_exists = true
294
+ else
295
+ tf.unlink
296
+ @manifest_file_exists = false
297
+ end
298
+ =======
299
+ tf.unlink
300
+ >>>>>>> 489757f (Fixes for race conditions)
301
+ end
302
+ end
303
+
304
+ def upload_temp_manifest(instance_name, local_path, remote_path: '/tmp/acpt_manifest.pp', opts: {})
305
+ <<<<<<< HEAD
306
+ scp_upload(instance_name, local_path, remote_path, opts: opts[:ssh_opts]) unless @manifest_file_exists
307
+ =======
308
+ scp_upload(instance_name, local_path, remote_path, opts: opts[:ssh_opts]) unless opts[:apply][:no_upload]
309
+ >>>>>>> 489757f (Fixes for race conditions)
310
+ end
311
+
312
+ def gcloud_ssh(instance_name, command, ignore_command_errors: false)
313
+ local_exec("compute ssh --ssh-key-file #{ssh_key} --tunnel-through-iap #{instance_name} --command='#{command}'")
314
+ rescue StandardError => e
315
+ raise e unless ignore_command_errors
316
+ end
317
+
249
318
  def vm_alias(vm)
250
319
  "compute.#{vm_describe(vm)['id']}"
251
320
  end
@@ -253,21 +322,35 @@ module CemAcpt::Platform::Gcp
253
322
  # Default options for Net::SSH
254
323
  def default_ssh_opts
255
324
  {
256
- verify_host_key: :accept_new_or_local_tunnel,
257
- keys: [File.join(ENV['HOME'], '.ssh', 'google_compute_engine')],
325
+ <<<<<<< HEAD
326
+ verify_host_key: :never,
327
+ keys: [ssh_key],
328
+ kex: ['diffie-hellman-group-exchange-sha256'], # ecdh algos cause jruby to shit the bed
258
329
  keys_only: true,
259
330
  config: false,
331
+ =======
332
+ auth_methods: ['publickey'],
333
+ >>>>>>> 489757f (Fixes for race conditions)
260
334
  check_host_ip: false,
335
+ compression: true,
336
+ config: false,
337
+ keys: [ssh_key],
338
+ keys_only: true,
339
+ kex: ['diffie-hellman-group-exchange-sha256'], # ecdh algos cause jruby to shit the bed
261
340
  non_interactive: true,
262
341
  port: 22,
263
342
  user: user_name,
264
- user_known_hosts_file: File.join(ENV['HOME'], '.ssh', 'google_compute_known_hosts'),
343
+ user_known_hosts_file: File.join(ENV['HOME'], '.ssh', 'acpt_test_known_hosts'),
344
+ <<<<<<< HEAD
345
+ =======
346
+ verify_host_key: :never,
347
+ >>>>>>> 489757f (Fixes for race conditions)
265
348
  }
266
349
  end
267
350
 
268
351
  # Returns a Proxy::Command object to use for the SSH option ProxyCommand.
269
352
  # This works in the same way that `gcloud compute ssh --tunnel-through-iap` does.
270
- def proxy_command(instance_name, port: 22, quiet: true, verbosity: nil)
353
+ def proxy_command(instance_name, port: 22, quiet: true, verbosity: 'debug')
271
354
  proxy_command = [
272
355
  'gcloud compute start-iap-tunnel',
273
356
  "#{instance_name} #{port}",
@@ -275,7 +358,7 @@ module CemAcpt::Platform::Gcp
275
358
  "--project=#{project}",
276
359
  "--zone=#{zone}",
277
360
  ]
278
- proxy_command << '--no-user-output-enabled --quiet' if quiet
361
+ #proxy_command << '--no-user-output-enabled --quiet' if quiet
279
362
  proxy_command << "--verbosity=#{verbosity}" unless quiet || verbosity.nil? || verbosity.empty?
280
363
  Net::SSH::Proxy::Command.new(proxy_command.join(' '))
281
364
  end
@@ -307,7 +390,7 @@ module CemAcpt::Platform::Gcp
307
390
 
308
391
  raise 'failed to find authenticated user name' unless uname
309
392
 
310
- uname.split(%r{[^a-zA-Z0-9_]}).join('_')
393
+ uname.gsub(/[^a-zA-Z0-9_]/, '_')
311
394
  end
312
395
  end
313
396
  end