cem_acpt 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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