beaker-google 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +46 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/release.yml +32 -0
- data/.rubocop.yml +519 -0
- data/.simplecov +1 -1
- data/CHANGELOG.md +59 -0
- data/Gemfile +8 -12
- data/README.md +48 -19
- data/Rakefile +49 -39
- data/beaker-google.gemspec +11 -13
- data/bin/beaker-google +1 -3
- data/lib/beaker/hypervisor/google_compute.rb +152 -113
- data/lib/beaker/hypervisor/google_compute_helper.rb +445 -754
- data/lib/beaker-google/version.rb +1 -1
- metadata +49 -17
- data/.github/workflows/snyk_scan.yaml +0 -23
- data/CODEOWNERS +0 -2
@@ -1,797 +1,488 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'google/apis/compute_v1'
|
4
|
+
require 'google/apis/oslogin_v1'
|
5
|
+
require 'googleauth'
|
2
6
|
require 'json'
|
3
7
|
require 'time'
|
4
8
|
require 'ostruct'
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
class GoogleComputeHelper
|
10
|
+
# TODO: Figure out what to do about the timeout thing
|
11
|
+
# TODO: Implement Google::Apis::RequestOptions on all calls (In lib/google/apis/options.rb)
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
# Beaker helper module for doing API level Google Compute Engine interaction.
|
14
|
+
class Beaker::GoogleComputeHelper
|
15
|
+
class GoogleComputeError < StandardError
|
16
|
+
end
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
AUTH_URL = 'https://www.googleapis.com/auth/compute'
|
16
|
-
API_VERSION = 'v1'
|
17
|
-
BASE_URL = "https://www.googleapis.com/compute/#{API_VERSION}/projects/"
|
18
|
-
CENTOS_PROJECT = 'centos-cloud'
|
19
|
-
DEBIAN_PROJECT = 'debian-cloud'
|
20
|
-
RHEL_PROJECT = 'rhel-cloud'
|
21
|
-
SLES_PROJECT = 'sles-cloud'
|
22
|
-
DEFAULT_ZONE_NAME = 'us-central1-a'
|
23
|
-
DEFAULT_MACHINE_TYPE = 'n1-highmem-2'
|
24
|
-
DEFAULT_DISK_SIZE = 25
|
25
|
-
|
26
|
-
# Create a new instance of the Google Compute Engine helper object
|
27
|
-
#
|
28
|
-
# @param [Hash{Symbol=>String}] options The options hash containing
|
29
|
-
# configuration values
|
30
|
-
#
|
31
|
-
# @option options [String] :gce_project The Google Compute Project name to
|
32
|
-
# connect to
|
33
|
-
#
|
34
|
-
# @option options [String] :gce_keyfile The location of the Google Compute
|
35
|
-
# service account keyfile
|
36
|
-
#
|
37
|
-
# @option options [String] :gce_password The password for the Google Compute
|
38
|
-
# service account key
|
39
|
-
#
|
40
|
-
# @option options [String] :gce_email The email address for the Google
|
41
|
-
# Compute service account
|
42
|
-
#
|
43
|
-
# @option options [String] :gce_machine_type A Google Compute machine type
|
44
|
-
# used to create instances, defaults to n1-highmem-2
|
45
|
-
#
|
46
|
-
# @option options [Integer] :timeout The amount of time to attempt execution
|
47
|
-
# before quiting and exiting with failure
|
48
|
-
def initialize(options)
|
49
|
-
@options = options
|
50
|
-
@logger = options[:logger]
|
51
|
-
try = 1
|
52
|
-
attempts = @options[:timeout].to_i / SLEEPWAIT
|
53
|
-
start = Time.now
|
54
|
-
|
55
|
-
set_client(Beaker::Version::STRING)
|
56
|
-
set_compute_api(API_VERSION, start, attempts)
|
57
|
-
|
58
|
-
@options[:gce_project] = ENV['BEAKER_gce_project'] if ENV['BEAKER_gce_project']
|
59
|
-
@options[:gce_keyfile] = ENV['BEAKER_gce_keyfile'] if ENV['BEAKER_gce_keyfile']
|
60
|
-
|
61
|
-
unless (@options[:gce_keyfile] && File.exist?(@options[:gce_keyfile]))
|
62
|
-
@options[:gce_keyfile] = File.join(ENV['HOME'], '.beaker', 'gce', %(#{@options[:gce_project]}.p12))
|
63
|
-
end
|
64
|
-
|
65
|
-
@options[:gce_password] = ENV['BEAKER_gce_password'] if ENV['BEAKER_gce_password']
|
66
|
-
# This is the GCE default so there's usually not a reason to specify it
|
67
|
-
@options[:gce_password] = 'notasecret' unless @options[:gce_password]
|
68
|
-
|
69
|
-
@options[:gce_email] = ENV['BEAKER_gce_email'] if ENV['BEAKER_gce_email']
|
70
|
-
|
71
|
-
raise 'You must specify a gce_project for Google Compute Engine instances!' unless @options[:gce_project]
|
72
|
-
|
73
|
-
raise "Could not find gce_keyfile for Google Compute Engine at '#{@options[:gce_keyfile]}'!" unless File.exist?(@options[:gce_keyfile])
|
74
|
-
|
75
|
-
raise 'You must specify a gce_email for Google Compute Engine instances!' unless @options[:gce_email]
|
76
|
-
|
77
|
-
authenticate(@options[:gce_keyfile], @options[:gce_password], @options[:gce_email], start, attempts)
|
78
|
-
end
|
18
|
+
SLEEPWAIT = 5
|
79
19
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
BASE_URL + @options[:gce_project] + '/global/zones/' + DEFAULT_ZONE_NAME
|
87
|
-
end
|
20
|
+
AUTH_URL = 'https://www.googleapis.com/auth/compute'
|
21
|
+
API_VERSION = 'v1'
|
22
|
+
BASE_URL = "https://www.googleapis.com/compute/#{API_VERSION}/projects/"
|
23
|
+
DEFAULT_ZONE_NAME = 'us-central1-a'
|
24
|
+
DEFAULT_MACHINE_TYPE = 'e2-standard-4'
|
25
|
+
DEFAULT_NETWORK_NAME = 'default'
|
88
26
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
# instances will operate
|
94
|
-
def default_network
|
95
|
-
BASE_URL + @options[:gce_project] + '/global/networks/default'
|
96
|
-
end
|
27
|
+
GCP_AUTH_SCOPE = [
|
28
|
+
Google::Apis::ComputeV1::AUTH_COMPUTE,
|
29
|
+
Google::Apis::OsloginV1::AUTH_CLOUD_PLATFORM_READ_ONLY,
|
30
|
+
].freeze
|
97
31
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
# @raise [Exception] If the provided platform type name is unsupported
|
106
|
-
def get_platform_project(name)
|
107
|
-
if name =~ /debian/
|
108
|
-
return DEBIAN_PROJECT
|
109
|
-
elsif name =~ /centos/
|
110
|
-
return CENTOS_PROJECT
|
111
|
-
elsif name =~ /rhel/
|
112
|
-
return RHEL_PROJECT
|
113
|
-
elsif name =~ /sles/
|
114
|
-
return SLES_PROJECT
|
115
|
-
else
|
116
|
-
raise "Unsupported platform for Google Compute Engine: #{name}"
|
117
|
-
end
|
118
|
-
end
|
32
|
+
##
|
33
|
+
# Create a new instance of the Google Compute Engine helper object
|
34
|
+
#
|
35
|
+
def initialize(options)
|
36
|
+
@options = options
|
37
|
+
@logger = options[:logger]
|
119
38
|
|
120
|
-
|
121
|
-
# Google Compute API
|
122
|
-
#
|
123
|
-
# @param version The version number of Beaker currently running
|
124
|
-
def set_client(version)
|
125
|
-
@client = ::Google::APIClient.new({:application_name => "Beaker", :application_version => version})
|
126
|
-
end
|
39
|
+
set_client(Beaker::Version::STRING)
|
127
40
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
# @param [Integer] start The time when we started code execution, it is
|
133
|
-
# compared to Time.now to determine how many further code execution
|
134
|
-
# attempts remain
|
135
|
-
#
|
136
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
137
|
-
# are willing to allow
|
138
|
-
#
|
139
|
-
# @raise [Exception] Raised if we fail to discover the Google Compute API,
|
140
|
-
# either through errors or running out of attempts
|
141
|
-
def set_compute_api version, start, attempts
|
142
|
-
try = (Time.now - start)/SLEEPWAIT
|
143
|
-
while try <= attempts
|
144
|
-
begin
|
145
|
-
@compute = @client.discovered_api('compute', version)
|
146
|
-
@logger.debug("Google Compute API discovered")
|
147
|
-
return
|
148
|
-
rescue => e
|
149
|
-
@logger.debug("Failed to discover Google Compute API")
|
150
|
-
if try >= attempts
|
151
|
-
raise e
|
152
|
-
end
|
153
|
-
end
|
154
|
-
try += 1
|
155
|
-
end
|
156
|
-
end
|
41
|
+
# ::Google::Apis.logger = ::Logger.new(::STDERR)
|
42
|
+
# ::Google::Apis.logger.level = ::Logger::DEBUG
|
43
|
+
# ::Google::Apis.logger.level = ::Logger::WARN
|
157
44
|
|
158
|
-
|
159
|
-
#
|
160
|
-
# @param [String] keyfile The location of the Google Compute Service
|
161
|
-
# Account keyfile to use for authentication
|
162
|
-
#
|
163
|
-
# @param [String] password The password for the provided Google Compute
|
164
|
-
# Service Account key
|
165
|
-
#
|
166
|
-
# @param [String] email The email address of the Google Compute Service
|
167
|
-
# Account we are using to connect
|
168
|
-
#
|
169
|
-
# @param [Integer] start The time when we started code execution, it is
|
170
|
-
# compared to Time.now to determine how many further code execution
|
171
|
-
# attempts remain
|
172
|
-
#
|
173
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
174
|
-
# are willing to allow
|
175
|
-
#
|
176
|
-
# @raise [Exception] Raised if we fail to create an authenticated
|
177
|
-
# connection to the Google Compute API, either through errors or running
|
178
|
-
# out of attempts
|
179
|
-
def authenticate(keyfile, password, email, start, attempts)
|
180
|
-
# OAuth authentication, using the service account
|
181
|
-
key = ::Google::APIClient::PKCS12.load_key(keyfile, password)
|
182
|
-
service_account = ::Google::APIClient::JWTAsserter.new(
|
183
|
-
email,
|
184
|
-
AUTH_URL,
|
185
|
-
key)
|
186
|
-
try = (Time.now - start) / SLEEPWAIT
|
187
|
-
while try <= attempts
|
188
|
-
begin
|
189
|
-
@client.authorization = service_account.authorize
|
190
|
-
@logger.debug("Authorized to use Google Compute")
|
191
|
-
return
|
192
|
-
rescue => e
|
193
|
-
@logger.debug("Failed to authorize to use Google Compute")
|
194
|
-
if try >= attempts
|
195
|
-
raise e
|
196
|
-
end
|
197
|
-
end
|
198
|
-
try += 1
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# Executes a provided Google Compute request using a previously configured
|
203
|
-
# and authenticated Google Compute client connection
|
204
|
-
#
|
205
|
-
# @param [Hash] req A correctly formatted Google Compute request object
|
206
|
-
#
|
207
|
-
# @param [Integer] start The time when we started code execution, it is
|
208
|
-
# compared to Time.now to determine how many further code execution
|
209
|
-
# attempts remain
|
210
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
211
|
-
# are willing to allow
|
212
|
-
#
|
213
|
-
# @raise [Exception] Raised if we fail to execute the request, either
|
214
|
-
# through errors or running out of attempts
|
215
|
-
def execute req, start, attempts
|
216
|
-
last_error = parsed = nil
|
217
|
-
try = (Time.now - start) / SLEEPWAIT
|
218
|
-
while try <= attempts
|
219
|
-
begin
|
220
|
-
result = @client.execute(req)
|
221
|
-
parsed = JSON.parse(result.body)
|
222
|
-
if not result.success?
|
223
|
-
error_code = parsed["error"] ? parsed["error"]["code"] : 0
|
224
|
-
if error_code == 404
|
225
|
-
raise GoogleComputeError, "Resource Not Found: #{result.body}"
|
226
|
-
elsif error_code == 400
|
227
|
-
raise GoogleComputeError, "Bad Request: #{result.body}"
|
228
|
-
else
|
229
|
-
raise GoogleComputeError, "Error attempting Google Compute API execute: #{result.body}"
|
230
|
-
end
|
231
|
-
end
|
232
|
-
return parsed
|
233
|
-
# retry errors
|
234
|
-
rescue Faraday::Error::ConnectionFailed => e
|
235
|
-
@logger.debug "ConnectionFailed attempting Google Compute execute command"
|
236
|
-
try += 1
|
237
|
-
last_error = e
|
238
|
-
end
|
239
|
-
end
|
240
|
-
# we only get down here if we've used up all our tries
|
241
|
-
raise last_error
|
242
|
-
end
|
243
|
-
|
244
|
-
# Determines the latest image available for the provided platform name.
|
245
|
-
#
|
246
|
-
# Only images of the form (platform)-(version)-(version) are currently supported
|
247
|
-
#
|
248
|
-
# @param [String] platform The platform type to search for an instance of.
|
249
|
-
#
|
250
|
-
# @param [Integer] start The time when we started code execution, it is
|
251
|
-
# compared to Time.now to determine how many further code execution
|
252
|
-
# attempts remain
|
253
|
-
#
|
254
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
255
|
-
# are willing to allow
|
256
|
-
#
|
257
|
-
# @return [Hash] The image hash of the latest, non-deprecated image for the
|
258
|
-
# provided platform
|
259
|
-
#
|
260
|
-
# @raise [Exception] Raised if we fail to execute the request, either
|
261
|
-
# through errors or running out of attempts
|
262
|
-
def get_latest_image(platform, start, attempts)
|
263
|
-
#break up my platform for information
|
264
|
-
platform_name, platform_version, platform_extra_info = platform.split('-', 3)
|
265
|
-
#find latest image to use
|
266
|
-
result = execute( image_list_req(get_platform_project(platform_name)), start, attempts )
|
267
|
-
images = result["items"]
|
268
|
-
|
269
|
-
#reject images of the wrong version of the given platform
|
270
|
-
images.delete_if { |image| image['name'] !~ /^#{platform_name}-#{platform_version}/}
|
271
|
-
#reject deprecated images
|
272
|
-
images.delete_if { |image| image['deprecated']}
|
273
|
-
#find a match based upon platform type
|
274
|
-
if images.length != 1
|
275
|
-
raise "Unable to find a single matching image for #{platform}, found #{images}"
|
276
|
-
end
|
277
|
-
images[0]
|
278
|
-
end
|
279
|
-
|
280
|
-
# Determines the Google Compute machineType object based upon the selected
|
281
|
-
# gce_machine_type option
|
282
|
-
#
|
283
|
-
# @param [Integer] start The time when we started code execution, it is
|
284
|
-
# compared to Time.now to determine how many further code execution
|
285
|
-
# attempts remain
|
286
|
-
#
|
287
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
288
|
-
# are willing to allow
|
289
|
-
#
|
290
|
-
# @return [Hash] The machineType hash
|
291
|
-
#
|
292
|
-
# @raise [Exception] Raised if we fail get the machineType, either through
|
293
|
-
# errors or running out of attempts
|
294
|
-
def get_machineType(start, attempts)
|
295
|
-
execute( machineType_get_req, start, attempts )
|
296
|
-
end
|
45
|
+
@options[:gce_project] = ENV['BEAKER_gce_project'] if ENV['BEAKER_gce_project']
|
297
46
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
# attempts remain
|
302
|
-
#
|
303
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
304
|
-
# are willing to allow
|
305
|
-
#
|
306
|
-
# @return [Hash] The network hash
|
307
|
-
#
|
308
|
-
# @raise [Exception] Raised if we fail get the network, either through
|
309
|
-
# errors or running out of attempts
|
310
|
-
def get_network(start, attempts)
|
311
|
-
execute( network_get_req, start, attempts)
|
312
|
-
end
|
47
|
+
@options[:gce_zone] = ENV.fetch('BEAKER_gce_zone', DEFAULT_ZONE_NAME)
|
48
|
+
@options[:gce_network] = ENV.fetch('BEAKER_gce_network', DEFAULT_NETWORK_NAME)
|
49
|
+
@options[:gce_subnetwork] = ENV.fetch('BEAKER_gce_subnetwork', nil)
|
313
50
|
|
314
|
-
|
315
|
-
#
|
316
|
-
# @param [Integer] start The time when we started code execution, it is
|
317
|
-
# compared to Time.now to determine how many further code execution
|
318
|
-
# attempts remain
|
319
|
-
#
|
320
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
321
|
-
# are willing to allow
|
322
|
-
#
|
323
|
-
# @return [Array[Hash]] The instances array of hashes
|
324
|
-
#
|
325
|
-
# @raise [Exception] Raised if we fail determine the list of existing
|
326
|
-
# instances, either through errors or running out of attempts
|
327
|
-
def list_instances(start, attempts)
|
328
|
-
instances = execute( instance_list_req(), start, attempts )
|
329
|
-
instances["items"]
|
330
|
-
end
|
51
|
+
raise 'You must specify a gce_project for Google Compute Engine instances!' unless @options[:gce_project]
|
331
52
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
# compared to Time.now to determine how many further code execution
|
336
|
-
# attempts remain
|
337
|
-
#
|
338
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
339
|
-
# are willing to allow
|
340
|
-
#
|
341
|
-
# @return [Array[Hash]] The disks array of hashes
|
342
|
-
#
|
343
|
-
# @raise [Exception] Raised if we fail determine the list of existing
|
344
|
-
# disks, either through errors or running out of attempts
|
345
|
-
def list_disks(start, attempts)
|
346
|
-
disks = execute( disk_list_req(), start, attempts )
|
347
|
-
disks["items"]
|
348
|
-
end
|
53
|
+
authorizer = authenticate
|
54
|
+
@compute = ::Google::Apis::ComputeV1::ComputeService.new
|
55
|
+
@compute.authorization = authorizer
|
349
56
|
|
350
|
-
#
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
# attempts remain
|
355
|
-
#
|
356
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
357
|
-
# are willing to allow
|
358
|
-
#
|
359
|
-
# @return [Array[Hash]] The firewalls array of hashes
|
360
|
-
#
|
361
|
-
# @raise [Exception] Raised if we fail determine the list of existing
|
362
|
-
# firewalls, either through errors or running out of attempts
|
363
|
-
def list_firewalls(start, attempts)
|
364
|
-
result = execute( firewall_list_req(), start, attempts )
|
365
|
-
firewalls = result["items"]
|
366
|
-
firewalls.delete_if{|f| f['name'] =~ /default-allow-internal|default-ssh/}
|
367
|
-
firewalls
|
368
|
-
end
|
57
|
+
# Find the appropriate username to log into created instances
|
58
|
+
@cloudoslogin = Google::Apis::OsloginV1::CloudOSLoginService.new
|
59
|
+
@cloudoslogin.authorization = authorizer
|
60
|
+
end
|
369
61
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
# compared to Time.now to determine how many further code execution
|
379
|
-
# attempts remain
|
380
|
-
#
|
381
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
382
|
-
# are willing to allow
|
383
|
-
#
|
384
|
-
# @raise [Exception] Raised if we fail create the firewall, either through
|
385
|
-
# errors or running out of attempts
|
386
|
-
def create_firewall(name, network, start, attempts)
|
387
|
-
execute( firewall_insert_req( name, network['selfLink'] ), start, attempts )
|
388
|
-
end
|
62
|
+
##
|
63
|
+
# Determines the default Google Compute zone based upon options and
|
64
|
+
# defaults
|
65
|
+
#
|
66
|
+
# @return [String] The name of the zone
|
67
|
+
def default_zone
|
68
|
+
@options[:gce_zone]
|
69
|
+
end
|
389
70
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
401
|
-
# are willing to allow
|
402
|
-
#
|
403
|
-
# @raise [Exception] Raised if we fail create the disk, either through
|
404
|
-
# errors or running out of attempts
|
405
|
-
def create_disk(name, img, start, attempts)
|
406
|
-
#create a new boot disk for this instance
|
407
|
-
disk = execute( disk_insert_req( name, img['selfLink'] ), start, attempts )
|
408
|
-
|
409
|
-
status = ''
|
410
|
-
try = (Time.now - start) / SLEEPWAIT
|
411
|
-
while status !~ /READY/ and try <= attempts
|
412
|
-
begin
|
413
|
-
disk = execute( disk_get_req( name ), start, attempts )
|
414
|
-
status = disk['status']
|
415
|
-
rescue GoogleComputeError => e
|
416
|
-
@logger.debug("Waiting for #{name} disk creation")
|
417
|
-
sleep(SLEEPWAIT)
|
418
|
-
end
|
419
|
-
try += 1
|
420
|
-
end
|
421
|
-
if status == ''
|
422
|
-
raise "Unable to create disk #{name}"
|
423
|
-
end
|
424
|
-
disk
|
425
|
-
end
|
71
|
+
##
|
72
|
+
# Get the region name from the provided zone.
|
73
|
+
#
|
74
|
+
# Assume that the region is the name of the zone without
|
75
|
+
# the final - and zone letter
|
76
|
+
#
|
77
|
+
# @return [String] The name of the region
|
78
|
+
def default_region
|
79
|
+
@options[:gce_zone].split('-')[0..1].join('-')
|
80
|
+
end
|
426
81
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
# @param [Hash] disk The Google Compute disk to attach to the newly created
|
436
|
-
# instance
|
437
|
-
#
|
438
|
-
# @param [Integer] start The time when we started code execution, it is
|
439
|
-
# compared to Time.now to determine how many further code execution
|
440
|
-
# attempts remain
|
441
|
-
#
|
442
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
443
|
-
# are willing to allow
|
444
|
-
#
|
445
|
-
# @raise [Exception] Raised if we fail create the instance, either through
|
446
|
-
# errors or running out of attempts
|
447
|
-
def create_instance(name, img, machineType, disk, start, attempts)
|
448
|
-
#add a new instance of the image
|
449
|
-
instance = execute( instance_insert_req( name, img['selfLink'], machineType['selfLink'], disk['selfLink'] ), start, attempts)
|
450
|
-
status = ''
|
451
|
-
try = (Time.now - start) / SLEEPWAIT
|
452
|
-
while status !~ /RUNNING/ and try <= attempts
|
453
|
-
begin
|
454
|
-
instance = execute( instance_get_req( name ), start, attempts )
|
455
|
-
status = instance['status']
|
456
|
-
rescue GoogleComputeError => e
|
457
|
-
@logger.debug("Waiting for #{name} instance creation")
|
458
|
-
sleep(SLEEPWAIT)
|
459
|
-
end
|
460
|
-
try += 1
|
461
|
-
end
|
462
|
-
if status == ''
|
463
|
-
raise "Unable to create instance #{name}"
|
464
|
-
end
|
465
|
-
instance
|
466
|
-
end
|
82
|
+
##
|
83
|
+
# Determines the default Google Compute network based upon defaults and
|
84
|
+
# options
|
85
|
+
#
|
86
|
+
# @return [String] The short name of the VPC network
|
87
|
+
def default_network
|
88
|
+
@options[:gce_network]
|
89
|
+
end
|
467
90
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
#
|
479
|
-
#
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
while status !~ /DONE/ and try <= attempts
|
493
|
-
begin
|
494
|
-
operation = execute( operation_get_req( zone_operation['name'] ), start, attempts )
|
495
|
-
status = operation['status']
|
496
|
-
rescue GoogleComputeError => e
|
497
|
-
@logger.debug("Waiting for tags to be added to #{name}")
|
498
|
-
sleep(SLEEPWAIT)
|
499
|
-
end
|
500
|
-
try += 1
|
501
|
-
end
|
502
|
-
if status == ''
|
503
|
-
raise "Unable to set metaData (#{tags.to_s}) on #{name}"
|
504
|
-
end
|
505
|
-
zone_operation
|
506
|
-
end
|
91
|
+
##
|
92
|
+
# Find the username for ssh to use with this connection
|
93
|
+
#
|
94
|
+
# @return [String] The username for ssh
|
95
|
+
#
|
96
|
+
# @raise [Google::Auth::IDTokens::KeySourceError] if the key source failed to obtain public keys
|
97
|
+
# @raise [Google::Auth::IDTokens::VerificationError] if the token verification failed.
|
98
|
+
# Additional data may be available in the error subclass and message.
|
99
|
+
def ssh_username
|
100
|
+
authorizer = @compute.authorization
|
101
|
+
# This is a bit of a hack based on what I found in a user (default application credentials)
|
102
|
+
# and a service account. There might be a better way of doing this.
|
103
|
+
case authorizer.class.to_s
|
104
|
+
when 'Google::Auth::UserRefreshCredentials'
|
105
|
+
authorizer.refresh!
|
106
|
+
userid = ::Google::Auth::IDTokens.verify_oidc(authorizer.id_token)['email']
|
107
|
+
when 'Google::Auth::ServiceAccountCredentials'
|
108
|
+
userid = authorizer.issuer
|
109
|
+
else
|
110
|
+
raise 'Unknown type of credential'
|
111
|
+
end
|
112
|
+
userid = "users/#{userid}" unless userid.start_with? 'users/'
|
113
|
+
@cloudoslogin.get_user_login_profile(userid).posix_accounts[0].username
|
114
|
+
end
|
507
115
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
# Ensure deletion of instance
|
525
|
-
try = (Time.now - start) / SLEEPWAIT
|
526
|
-
while try <= attempts
|
527
|
-
begin
|
528
|
-
result = execute( instance_get_req( name ), start, attempts )
|
529
|
-
@logger.debug("Waiting for #{name} instance deletion")
|
530
|
-
sleep(SLEEPWAIT)
|
531
|
-
rescue GoogleComputeError => e
|
532
|
-
@logger.debug("#{name} instance deleted!")
|
533
|
-
return
|
534
|
-
end
|
535
|
-
try += 1
|
536
|
-
end
|
537
|
-
@logger.debug("#{name} instance was not removed before timeout, may still exist")
|
538
|
-
end
|
116
|
+
##
|
117
|
+
# Infer the network that a given subnetwork is attached to
|
118
|
+
#
|
119
|
+
# @param [String] subnetwork_name The name of the subnetwork
|
120
|
+
#
|
121
|
+
# @return [String] The short name of the network
|
122
|
+
#
|
123
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
124
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
125
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
126
|
+
def default_network_from_subnet(subnetwork_name)
|
127
|
+
subnetwork = @compute.get_subnetwork(@options[:gce_project], default_region, subnetwork_name)
|
128
|
+
m = %r{.*/networks/(?<network_name>.*)\Z}.match subnetwork.network
|
129
|
+
nil if m.nil?
|
130
|
+
m['network_name']
|
131
|
+
end
|
539
132
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
@logger.debug("Waiting for #{name} disk deletion")
|
562
|
-
sleep(SLEEPWAIT)
|
563
|
-
rescue GoogleComputeError => e
|
564
|
-
@logger.debug("#{name} disk deleted!")
|
565
|
-
return
|
566
|
-
end
|
567
|
-
try += 1
|
568
|
-
end
|
569
|
-
@logger.debug("#{name} disk was not removed before timeout, may still exist")
|
570
|
-
end
|
133
|
+
##
|
134
|
+
# Determine the subnetwork to use for instances
|
135
|
+
#
|
136
|
+
# If the network is the 'default' network, get the 'default' subnetwork for the region.
|
137
|
+
# If no subnet is provided by the user, pick the first one out of the user-provided network
|
138
|
+
#
|
139
|
+
# @return [String] The name of the subnetwork that should be attached to the instances
|
140
|
+
def default_subnetwork
|
141
|
+
network_name = @options[:gce_network]
|
142
|
+
if network_name == 'default'
|
143
|
+
@options[:gce_subnetwork] ||= @compute.get_subnetwork(@options[:gce_project], default_region, 'default').name
|
144
|
+
elsif @options[:gce_subnetwork].nil?
|
145
|
+
# No subnet set, get the first subnet in our current region for the network
|
146
|
+
subnetwork = @compute.get_network(@options[:gce_project], network_name).subnetworks[0]
|
147
|
+
m = %r{.*/subnetworks/(?<subnetwork_name>.*)\Z}.match subnetwork
|
148
|
+
raise "Unable to find a subnetwork in provided network #{network_name}" if m.nil?
|
149
|
+
|
150
|
+
@options[:gce_subnetwork] = m['subnetwork_name']
|
151
|
+
end
|
152
|
+
@options[:gce_subnetwork]
|
153
|
+
end
|
571
154
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
# @param [Integer] attempts The total amount of attempts to execute that we
|
581
|
-
# are willing to allow
|
582
|
-
#
|
583
|
-
# @raise [Exception] Raised if we fail delete the firewall, either through
|
584
|
-
# errors or running out of attempts
|
585
|
-
def delete_firewall(name, start, attempts)
|
586
|
-
result = execute( firewall_delete_req( name ), start, attempts )
|
587
|
-
#ensure deletion of disk
|
588
|
-
try = (Time.now - start) / SLEEPWAIT
|
589
|
-
while try <= attempts
|
590
|
-
begin
|
591
|
-
firewall = execute( firewall_get_req( name ), start, attempts )
|
592
|
-
@logger.debug("Waiting for #{name} firewall deletion")
|
593
|
-
sleep(SLEEPWAIT)
|
594
|
-
rescue GoogleComputeError => e
|
595
|
-
@logger.debug("#{name} firewall deleted!")
|
596
|
-
return
|
597
|
-
end
|
598
|
-
try += 1
|
599
|
-
end
|
600
|
-
@logger.debug("#{name} firewall was not removed before timeout, may still exist")
|
601
|
-
end
|
155
|
+
##
|
156
|
+
# Set the user-agent information for the application.
|
157
|
+
#
|
158
|
+
# @param version The version number of Beaker currently running
|
159
|
+
def set_client(version)
|
160
|
+
::Google::Apis::ClientOptions.default.application_name = 'beaker-google'
|
161
|
+
::Google::Apis::ClientOptions.default.application_version = version
|
162
|
+
end
|
602
163
|
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
164
|
+
##
|
165
|
+
# Creates an authentication object to use in the various Google APIs
|
166
|
+
#
|
167
|
+
# This method currently supports using application credentials via the
|
168
|
+
# GOOGLE_APPLICATION_CREDENTIALS environment variable, and application default
|
169
|
+
# credentials.
|
170
|
+
#
|
171
|
+
# @return [Google::Auth::UserRefreshCredentials|Google::Auth::ServiceAccountCredentials]
|
172
|
+
# Authorization object to pass to Google APIs
|
173
|
+
def authenticate
|
174
|
+
if ENV['GOOGLE_APPLICATION_CREDENTIALS']
|
175
|
+
::Google::Auth::ServiceAccountCredentials.from_env(scope: GCP_AUTH_SCOPE)
|
176
|
+
else
|
177
|
+
# Fall back to default application auth
|
178
|
+
::Google::Auth.get_application_default(GCP_AUTH_SCOPE)
|
611
179
|
end
|
180
|
+
end
|
612
181
|
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
182
|
+
##
|
183
|
+
# Find the correct image object for a given project and name
|
184
|
+
#
|
185
|
+
# @param [String] image_project The project that owns the requested image
|
186
|
+
#
|
187
|
+
# @param [String] name The name of the image in the project. This must
|
188
|
+
# be the exact name of the image
|
189
|
+
#
|
190
|
+
# @return [Google::Apis::ComputeV1::Image]
|
191
|
+
#
|
192
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
193
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
194
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
195
|
+
def get_image(project, name)
|
196
|
+
@compute.get_image(project, name)
|
197
|
+
end
|
620
198
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
199
|
+
##
|
200
|
+
# Find the latest non-deprecated image in the given project and family
|
201
|
+
#
|
202
|
+
# @param [String] image_project The project that owns the requested image
|
203
|
+
#
|
204
|
+
# @param [String] family The name of the image family
|
205
|
+
#
|
206
|
+
# @return [Google::Apis::ComputeV1::Image]
|
207
|
+
#
|
208
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
209
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
210
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
211
|
+
def get_latest_image_from_family(image_project, family)
|
212
|
+
@compute.get_image_from_family(image_project, family)
|
213
|
+
end
|
630
214
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
215
|
+
##
|
216
|
+
# Determines the Google Compute machineType object based upon the selected
|
217
|
+
# gce_machine_type option
|
218
|
+
#
|
219
|
+
# @param [String] type_name The name of the type to get
|
220
|
+
#
|
221
|
+
# @return [Google::Apis::ComputeV1::MachineType]
|
222
|
+
#
|
223
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
224
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
225
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
226
|
+
def get_machine_type(type_name = DEFAULT_MACHINE_TYPE)
|
227
|
+
@compute.get_machine_type(@options[:gce_project], default_zone, type_name)
|
228
|
+
end
|
640
229
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
:parameters => { 'project' => @options[:gce_project], 'zone' => DEFAULT_ZONE_NAME, 'sourceImage' => source },
|
653
|
-
:body_object => { 'name' => name, 'sizeGb' => DEFAULT_DISK_SIZE } }
|
654
|
-
end
|
230
|
+
##
|
231
|
+
# Determines the Google Compute network object in use for the current connection
|
232
|
+
#
|
233
|
+
# @return [Google::Apis::ComputeV1::Network]
|
234
|
+
#
|
235
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
236
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
237
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
238
|
+
def get_network(network_name = default_network)
|
239
|
+
@compute.get_network(@options[:gce_project], network_name)
|
240
|
+
end
|
655
241
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
242
|
+
##
|
243
|
+
# Determines a list of existing Google Compute instances
|
244
|
+
#
|
245
|
+
# @return [Array[Google::Apis::ComputeV1::Instance]]
|
246
|
+
#
|
247
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
248
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
249
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
250
|
+
def list_instances
|
251
|
+
@compute.list_instances(@options[:gce_project], default_zone).items
|
252
|
+
end
|
665
253
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
'sourceRanges' => [ "0.0.0.0/0" ] } }
|
682
|
-
end
|
254
|
+
##
|
255
|
+
# Determines a list of existing Google Compute disks
|
256
|
+
#
|
257
|
+
# @param [Integer] start The time when we started code execution, it is
|
258
|
+
# compared to Time.now to determine how many further code execution
|
259
|
+
# attempts remain
|
260
|
+
#
|
261
|
+
# @return [Array[Google::Apis::ComputeV1::Disk]]
|
262
|
+
#
|
263
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
264
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
265
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
266
|
+
def list_disks
|
267
|
+
@compute.list_disks(@options[:gce_project], default_zone).items
|
268
|
+
end
|
683
269
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
270
|
+
##
|
271
|
+
# Determines a list of existing Google Compute firewalls
|
272
|
+
#
|
273
|
+
# @return [Array[Google::Apis::ComputeV1::Firewall]]
|
274
|
+
#
|
275
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
276
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
277
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
278
|
+
def list_firewalls
|
279
|
+
@compute.list_firewalls(@options[:gce_project],
|
280
|
+
filter: 'name != default-allow-internal AND name != default-ssh').items
|
281
|
+
end
|
693
282
|
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
283
|
+
##
|
284
|
+
# Create a Google Compute firewall
|
285
|
+
#
|
286
|
+
# @param [String] name The name of the firewall to create
|
287
|
+
#
|
288
|
+
# @param [::Google::Apis::ComputeV1::Network] network The Google Compute networkin which to create
|
289
|
+
# the firewall
|
290
|
+
#
|
291
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
292
|
+
#
|
293
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
294
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
295
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
296
|
+
def create_firewall(name, network)
|
297
|
+
firewall_object = ::Google::Apis::ComputeV1::Firewall.new(
|
298
|
+
name: name,
|
299
|
+
allowed: [
|
300
|
+
::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: 'tcp',
|
301
|
+
ports: ['443', '8140', '61613', '8080', '8081', '22']),
|
302
|
+
],
|
303
|
+
network: network.self_link,
|
304
|
+
# TODO: Is there a better way to do this?
|
305
|
+
sourceRanges: ['0.0.0.0/0'], # Allow from anywhere
|
306
|
+
)
|
307
|
+
operation = @compute.insert_firewall(@options[:gce_project], firewall_object)
|
308
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
309
|
+
end
|
701
310
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
311
|
+
##
|
312
|
+
# Add a taget_tag to an existing firewall
|
313
|
+
#
|
314
|
+
# @param [String] the name of the firewall to update
|
315
|
+
#
|
316
|
+
# @ param [String] tag The tag to add to the firewall
|
317
|
+
#
|
318
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
319
|
+
#
|
320
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
321
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
322
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
323
|
+
def add_firewall_tag(name, tag)
|
324
|
+
firewall = @compute.get_firewall(@options[:gce_project], name)
|
325
|
+
firewall.target_tags = [] if firewall.target_tags.nil?
|
326
|
+
firewall.target_tags << tag
|
327
|
+
operation = @compute.patch_firewall(@options[:gce_project], name, firewall)
|
328
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
329
|
+
end
|
712
330
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
331
|
+
##
|
332
|
+
# Create a Google Compute disk
|
333
|
+
#
|
334
|
+
# @param [String] name The name of the disk to create
|
335
|
+
#
|
336
|
+
# @param [String] img The existing disk image to clone for this image
|
337
|
+
# or nil to create a blank disk
|
338
|
+
#
|
339
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
340
|
+
#
|
341
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
342
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
343
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
344
|
+
def create_disk(name, size, img = nil)
|
345
|
+
new_disk = ::Google::Apis::ComputeV1::Disk.new(
|
346
|
+
name: name,
|
347
|
+
size_gb: size,
|
348
|
+
source_image: img,
|
349
|
+
)
|
350
|
+
operation = @compute.insert_disk(@options[:gce_project], @options[:gce_zone], new_disk)
|
351
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
352
|
+
end
|
720
353
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
354
|
+
##
|
355
|
+
# Create a Google Compute instance
|
356
|
+
#
|
357
|
+
# @param [String] name The name of the instance to create
|
358
|
+
#
|
359
|
+
# @param [Google::Apis::ComputeV1::Image] img The Google Compute image to use for instance creation
|
360
|
+
#
|
361
|
+
# @param [Google::Apis::ComputeV1::MachineType] machine_type The Google Compute Machine Type
|
362
|
+
#
|
363
|
+
# @param [Integer] disk_size The size of the boot disk for the new instance. Must be equal to or
|
364
|
+
# greater than the image disk's size
|
365
|
+
#
|
366
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
367
|
+
#
|
368
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
369
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
370
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
371
|
+
def create_instance(name, img, machine_type, disk_size)
|
372
|
+
initialize_params = ::Google::Apis::ComputeV1::AttachedDiskInitializeParams.new(
|
373
|
+
disk_size_gb: disk_size,
|
374
|
+
source_image: img.self_link,
|
375
|
+
)
|
376
|
+
disk_params = ::Google::Apis::ComputeV1::AttachedDisk.new(
|
377
|
+
boot: true,
|
378
|
+
auto_delete: true,
|
379
|
+
initialize_params: initialize_params,
|
380
|
+
)
|
381
|
+
# attached_network = ::Google::Apis::ComputeV1::networkInterfaces.new()
|
382
|
+
tags = ::Google::Apis::ComputeV1::Tags.new(
|
383
|
+
items: [name],
|
384
|
+
)
|
385
|
+
network_interface = ::Google::Apis::ComputeV1::NetworkInterface.new(
|
386
|
+
network: get_network(default_network).self_link,
|
387
|
+
subnetwork: @compute.get_subnetwork(@options[:gce_project], default_region, default_subnetwork).self_link,
|
388
|
+
# Create an AccessConfig to add a NAT IP to the host.
|
389
|
+
# TODO: Make this configurable
|
390
|
+
access_configs: [
|
391
|
+
::Google::Apis::ComputeV1::AccessConfig.new(
|
392
|
+
network_tier: 'STANDARD',
|
393
|
+
),
|
394
|
+
],
|
395
|
+
)
|
396
|
+
new_instance = ::Google::Apis::ComputeV1::Instance.new(
|
397
|
+
machine_type: machine_type.self_link,
|
398
|
+
name: name,
|
399
|
+
disks: [disk_params],
|
400
|
+
network_interfaces: [network_interface],
|
401
|
+
tags: tags,
|
402
|
+
)
|
403
|
+
operation = @compute.insert_instance(@options[:gce_project], @options[:gce_zone], new_instance)
|
404
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
405
|
+
end
|
734
406
|
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
407
|
+
##
|
408
|
+
# Get the named instace from Google Compute Image
|
409
|
+
#
|
410
|
+
# @param [String] name The name of the instance
|
411
|
+
#
|
412
|
+
# @return [Google::Apis::ComputeV1::Instance]
|
413
|
+
#
|
414
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
415
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
416
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
417
|
+
def get_instance(name)
|
418
|
+
@compute.get_instance(@options[:gce_project], @options[:gce_zone], name)
|
419
|
+
end
|
742
420
|
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
421
|
+
##
|
422
|
+
# Set key/value metadata pairs to a Google Compute instance
|
423
|
+
#
|
424
|
+
# This function replaces any existing items in the metadata hash!
|
425
|
+
#
|
426
|
+
# @param [String] name The name of the instance to set metadata
|
427
|
+
#
|
428
|
+
# @param [String] data An array of hashes to set ass metadata. Each array
|
429
|
+
# item should have a 'key' and 'value' key.
|
430
|
+
#
|
431
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
432
|
+
#
|
433
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
434
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
435
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
436
|
+
def set_metadata_on_instance(name, data)
|
437
|
+
instance = @compute.get_instance(@options[:gce_project], @options[:gce_zone], name)
|
438
|
+
mdata = instance.metadata.dup
|
439
|
+
mdata.items = data
|
440
|
+
operation = @compute.set_instance_metadata(@options[:gce_project], @options[:gce_zone], name, mdata)
|
441
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
442
|
+
end
|
752
443
|
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
444
|
+
##
|
445
|
+
# Delete a Google Compute instance
|
446
|
+
#
|
447
|
+
# @param [String] name The name of the instance to delete
|
448
|
+
#
|
449
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
450
|
+
#
|
451
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
452
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
453
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
454
|
+
def delete_instance(name)
|
455
|
+
operation = @compute.delete_instance(@options[:gce_project], default_zone, name)
|
456
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
457
|
+
end
|
762
458
|
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
{ :api_method => @compute.instances.insert,
|
778
|
-
:parameters => { 'project' => @options[:gce_project], 'zone' => DEFAULT_ZONE_NAME },
|
779
|
-
:body_object => { 'name' => name,
|
780
|
-
'image' => image,
|
781
|
-
'zone' => default_zone,
|
782
|
-
'machineType' => machineType,
|
783
|
-
'disks' => [ { 'source' => disk,
|
784
|
-
'type' => 'PERSISTENT', 'boot' => 'true'} ],
|
785
|
-
'networkInterfaces' => [ { 'accessConfigs' => [{ 'type' => 'ONE_TO_ONE_NAT', 'name' => 'External NAT' }],
|
786
|
-
'network' => default_network } ] } }
|
787
|
-
end
|
459
|
+
##
|
460
|
+
# Delete a Google Compute disk
|
461
|
+
#
|
462
|
+
# @param [String] name The name of the disk to delete
|
463
|
+
#
|
464
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
465
|
+
#
|
466
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
467
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
468
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
469
|
+
def delete_disk(name)
|
470
|
+
operation = @compute.delete_disk(@options[:gce_project], default_zone, name)
|
471
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
472
|
+
end
|
788
473
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
474
|
+
##
|
475
|
+
# Delete a Google Compute firewall
|
476
|
+
#
|
477
|
+
# @param [String] name The name of the firewall to delete
|
478
|
+
#
|
479
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
480
|
+
#
|
481
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
482
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
483
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
484
|
+
def delete_firewall(name)
|
485
|
+
operation = @compute.delete_firewall(@options[:gce_project], name)
|
486
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
796
487
|
end
|
797
488
|
end
|