beaker-google 1.1.0 → 1.3.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.
- checksums.yaml +4 -4
- data/.github/release.yml +41 -0
- data/.github/workflows/ci.yml +30 -17
- data/.github/workflows/release.yml +91 -17
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -518
- data/.rubocop_todo.yml +20 -0
- data/.simplecov +2 -0
- data/CHANGELOG.md +50 -1
- data/Gemfile +13 -2
- data/README.md +2 -2
- data/Rakefile +23 -6
- data/beaker-google.gemspec +27 -23
- data/bin/beaker-google +1 -0
- data/lib/beaker/hypervisor/google.rb +5 -1
- data/lib/beaker/hypervisor/google_compute.rb +55 -25
- data/lib/beaker/hypervisor/google_compute_helper.rb +599 -581
- data/lib/beaker-google/version.rb +3 -1
- data/spec/classes/beaker/google_compute_constants_and_defaults_spec.rb +141 -0
- data/spec/classes/beaker/google_compute_helper_api_version_spec.rb +37 -0
- data/spec/classes/beaker/google_compute_helper_configuration_validation_spec.rb +100 -0
- data/spec/classes/beaker/google_compute_helper_constants_and_defaults_spec.rb +66 -0
- data/spec/classes/beaker/google_compute_helper_edge_cases_spec.rb +173 -0
- data/spec/classes/beaker/google_compute_helper_error_handling_spec.rb +105 -0
- data/spec/classes/beaker/google_compute_helper_spec.rb +368 -0
- data/spec/classes/beaker/google_compute_helper_utilities_spec.rb +95 -0
- data/spec/classes/beaker/google_compute_initialization_spec.rb +52 -0
- data/spec/classes/beaker/google_compute_provision_spec.rb +68 -0
- data/spec/classes/beaker/google_compute_spec.rb +95 -0
- data/spec/classes/beaker/google_compute_ssh_key_management_spec.rb +115 -0
- data/spec/classes/beaker/google_spec.rb +68 -0
- data/spec/classes/beaker-google/beaker_google_version_spec.rb +19 -0
- data/spec/classes/beaker_integration_spec.rb +67 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/google_compute_shared_setup.rb +138 -0
- data/spec/support/shared_mocks.rb +89 -0
- metadata +102 -38
- data/.github/workflows/codeql-analysis.yml +0 -70
|
@@ -10,636 +10,654 @@ require 'ostruct'
|
|
|
10
10
|
# TODO: Figure out what to do about the timeout thing
|
|
11
11
|
# TODO: Implement Google::Apis::RequestOptions on all calls (In lib/google/apis/options.rb)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class
|
|
16
|
-
|
|
13
|
+
module Beaker
|
|
14
|
+
# Beaker helper module for doing API level Google Compute Engine interaction.
|
|
15
|
+
class GoogleComputeHelper
|
|
16
|
+
class GoogleComputeError < StandardError
|
|
17
|
+
end
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
attr_reader :options
|
|
19
20
|
|
|
20
|
-
|
|
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'
|
|
21
|
+
SLEEPWAIT = 5
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
AUTH_URL = 'https://www.googleapis.com/auth/compute'
|
|
24
|
+
API_VERSION = 'v1'
|
|
25
|
+
BASE_URL = "https://www.googleapis.com/compute/#{API_VERSION}/projects/"
|
|
26
|
+
DEFAULT_ZONE_NAME = 'us-central1-a'
|
|
27
|
+
DEFAULT_MACHINE_TYPE = 'e2-standard-4'
|
|
28
|
+
DEFAULT_NETWORK_NAME = 'default'
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
Google::Apis::ComputeV1::AUTH_COMPUTE,
|
|
31
|
-
Google::Apis::OsloginV1::AUTH_CLOUD_PLATFORM_READ_ONLY,
|
|
32
|
-
].freeze
|
|
30
|
+
VALID_PROTOS = %w[tcp udp icmp esp ah ipip sctp].freeze
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@options = options
|
|
39
|
-
@logger = options[:logger]
|
|
32
|
+
GCP_AUTH_SCOPE = [
|
|
33
|
+
::Google::Apis::ComputeV1::AUTH_COMPUTE,
|
|
34
|
+
::Google::Apis::OsloginV1::AUTH_CLOUD_PLATFORM_READ_ONLY,
|
|
35
|
+
].freeze
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
##
|
|
38
|
+
# Create a new instance of the Google Compute Engine helper object
|
|
39
|
+
#
|
|
40
|
+
def initialize(options)
|
|
41
|
+
@options = options
|
|
42
|
+
@logger = options[:logger]
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
# ::Google::Apis.logger.level = ::Logger::DEBUG
|
|
45
|
-
# ::Google::Apis.logger.level = ::Logger::WARN
|
|
44
|
+
client(Beaker::Version::STRING)
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
# ::Google::Apis.logger = ::Logger.new(::STDERR)
|
|
47
|
+
# ::Google::Apis.logger.level = ::Logger::DEBUG
|
|
48
|
+
# ::Google::Apis.logger.level = ::Logger::WARN
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
@options[:gce_network] = ENV.fetch('BEAKER_gce_network', DEFAULT_NETWORK_NAME)
|
|
51
|
-
@options[:gce_subnetwork] = ENV.fetch('BEAKER_gce_subnetwork', nil)
|
|
50
|
+
@options[:gce_project] = ENV['BEAKER_gce_project'] if ENV['BEAKER_gce_project']
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
@options[:gce_zone] = ENV.fetch('BEAKER_gce_zone', DEFAULT_ZONE_NAME)
|
|
53
|
+
@options[:gce_network] = ENV.fetch('BEAKER_gce_network', DEFAULT_NETWORK_NAME)
|
|
54
|
+
@options[:gce_subnetwork] = ENV.fetch('BEAKER_gce_subnetwork', nil)
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
# A non-empty BEAKER_gce_ports replaces gce_ports from the hosts file, as
|
|
57
|
+
# BEAKER_gce_project and BEAKER_gce_machine_type replace theirs. Either
|
|
58
|
+
# source may be a comma-separated string or a list.
|
|
59
|
+
env_ports = ENV.fetch('BEAKER_gce_ports', '').strip
|
|
60
|
+
ports = env_ports.empty? ? @options[:gce_ports] : env_ports
|
|
61
|
+
@options[:gce_ports] = Array(ports).flat_map { |entry| entry.to_s.split(',') }.map(&:strip).reject(&:empty?)
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
parts = port.split('/', 2)
|
|
61
|
-
raise "Invalid format for port #{port}. Should be 'port/proto'" unless parts.length == 2
|
|
62
|
-
proto = parts[1]
|
|
63
|
-
raise "Invalid value '#{proto}' for protocol in '#{port}'. Must be one of '#{VALID_PROTOS.join("', '")}'" unless VALID_PROTOS.include? proto
|
|
64
|
-
end
|
|
65
|
-
authorizer = authenticate
|
|
66
|
-
@compute = ::Google::Apis::ComputeV1::ComputeService.new
|
|
67
|
-
@compute.authorization = authorizer
|
|
63
|
+
raise 'You must specify a gce_project for Google Compute Engine instances!' unless @options[:gce_project]
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
65
|
+
@options[:gce_ports].each do |port|
|
|
66
|
+
parts = port.split('/', 2)
|
|
67
|
+
raise "Invalid format for port #{port}. Should be 'port/proto'" unless parts.length == 2
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
proto = parts[1]
|
|
70
|
+
unless VALID_PROTOS.include? proto
|
|
71
|
+
raise "Invalid value '#{proto}' for protocol in '#{port}'. Must be one of '#{VALID_PROTOS.join("', '")}'"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
authorizer = authenticate
|
|
75
|
+
@compute = ::Google::Apis::ComputeV1::ComputeService.new
|
|
76
|
+
@compute.authorization = authorizer
|
|
77
|
+
# google-apis-compute_v1 sends a dated X-Goog-Api-Version header on every
|
|
78
|
+
# call, and a release can name a version Compute Engine doesn't accept yet
|
|
79
|
+
# (0.157.0 sends 2026-11-01, which is rejected). Request options are
|
|
80
|
+
# applied after that header, so pin the API version this helper targets.
|
|
81
|
+
@compute.request_options.header = { 'X-Goog-Api-Version' => API_VERSION }
|
|
82
|
+
|
|
83
|
+
# Find the appropriate username to log into created instances
|
|
84
|
+
@cloudoslogin = ::Google::Apis::OsloginV1::CloudOSLoginService.new
|
|
85
|
+
@cloudoslogin.authorization = authorizer
|
|
86
|
+
end
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@options[:gce_zone].split('-')[0..1].join('-')
|
|
92
|
-
end
|
|
88
|
+
##
|
|
89
|
+
# Determines the default Google Compute zone based upon options and
|
|
90
|
+
# defaults
|
|
91
|
+
#
|
|
92
|
+
# @return [String] The name of the zone
|
|
93
|
+
def default_zone
|
|
94
|
+
@options[:gce_zone]
|
|
95
|
+
end
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
@
|
|
101
|
-
|
|
97
|
+
##
|
|
98
|
+
# Get the region name from the provided zone.
|
|
99
|
+
#
|
|
100
|
+
# Assume that the region is the name of the zone without
|
|
101
|
+
# the final - and zone letter
|
|
102
|
+
#
|
|
103
|
+
# @return [String] The name of the region
|
|
104
|
+
def default_region
|
|
105
|
+
@options[:gce_zone].split('-')[0..1].join('-')
|
|
106
|
+
end
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# Additional data may be available in the error subclass and message.
|
|
111
|
-
def ssh_username
|
|
112
|
-
authorizer = @compute.authorization
|
|
113
|
-
# This is a bit of a hack based on what I found in a user (default application credentials)
|
|
114
|
-
# and a service account. There might be a better way of doing this.
|
|
115
|
-
case authorizer.class.to_s
|
|
116
|
-
when 'Google::Auth::UserRefreshCredentials'
|
|
117
|
-
authorizer.refresh!
|
|
118
|
-
userid = ::Google::Auth::IDTokens.verify_oidc(authorizer.id_token)['email']
|
|
119
|
-
when 'Google::Auth::ServiceAccountCredentials'
|
|
120
|
-
userid = authorizer.issuer
|
|
121
|
-
else
|
|
122
|
-
raise 'Unknown type of credential'
|
|
108
|
+
##
|
|
109
|
+
# Determines the default Google Compute network based upon defaults and
|
|
110
|
+
# options
|
|
111
|
+
#
|
|
112
|
+
# @return [String] The short name of the VPC network
|
|
113
|
+
def default_network
|
|
114
|
+
@options[:gce_network]
|
|
123
115
|
end
|
|
124
|
-
userid = "users/#{userid}" unless userid.start_with? 'users/'
|
|
125
|
-
@cloudoslogin.get_user_login_profile(userid).posix_accounts[0].username
|
|
126
|
-
end
|
|
127
116
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
117
|
+
##
|
|
118
|
+
# Find the username for ssh to use with this connection
|
|
119
|
+
#
|
|
120
|
+
# @return [String] The username for ssh
|
|
121
|
+
#
|
|
122
|
+
# @raise [Google::Auth::IDTokens::KeySourceError] if the key source failed to obtain public keys
|
|
123
|
+
# @raise [Google::Auth::IDTokens::VerificationError] if the token verification failed.
|
|
124
|
+
# Additional data may be available in the error subclass and message.
|
|
125
|
+
def ssh_username
|
|
126
|
+
authorizer = @compute.authorization
|
|
127
|
+
# This is a bit of a hack based on what I found in a user (default application credentials)
|
|
128
|
+
# and a service account. There might be a better way of doing this.
|
|
129
|
+
case authorizer.class.to_s
|
|
130
|
+
when 'Google::Auth::UserRefreshCredentials'
|
|
131
|
+
authorizer.refresh!
|
|
132
|
+
userid = ::Google::Auth::IDTokens.verify_oidc(authorizer.id_token)['email']
|
|
133
|
+
when 'Google::Auth::ServiceAccountCredentials'
|
|
134
|
+
userid = authorizer.issuer
|
|
135
|
+
else
|
|
136
|
+
raise 'Unknown type of credential'
|
|
137
|
+
end
|
|
138
|
+
userid = "users/#{userid}" unless userid.start_with? 'users/'
|
|
139
|
+
@cloudoslogin.get_user_login_profile(userid).posix_accounts[0].username
|
|
140
|
+
end
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
m
|
|
160
|
-
raise "Unable to find a subnetwork in provided network #{network_name}" if m.nil?
|
|
161
|
-
|
|
162
|
-
@options[:gce_subnetwork] = m['subnetwork_name']
|
|
142
|
+
##
|
|
143
|
+
# Infer the network that a given subnetwork is attached to
|
|
144
|
+
#
|
|
145
|
+
# @param [String] subnetwork_name The name of the subnetwork
|
|
146
|
+
#
|
|
147
|
+
# @return [String] The short name of the network
|
|
148
|
+
#
|
|
149
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
150
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
151
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
152
|
+
def default_network_from_subnet(subnetwork_name)
|
|
153
|
+
subnetwork = @compute.get_subnetwork(@options[:gce_project], default_region, subnetwork_name)
|
|
154
|
+
m = %r{.*/networks/(?<network_name>.*)\Z}.match subnetwork.network
|
|
155
|
+
nil if m.nil?
|
|
156
|
+
m['network_name']
|
|
163
157
|
end
|
|
164
|
-
@options[:gce_subnetwork]
|
|
165
|
-
end
|
|
166
158
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
159
|
+
##
|
|
160
|
+
# Determine the subnetwork to use for instances
|
|
161
|
+
#
|
|
162
|
+
# If the network is the 'default' network, get the 'default' subnetwork for the region.
|
|
163
|
+
# If no subnet is provided by the user, pick the first one out of the user-provided network
|
|
164
|
+
#
|
|
165
|
+
# @return [String] The name of the subnetwork that should be attached to the instances
|
|
166
|
+
def default_subnetwork
|
|
167
|
+
network_name = @options[:gce_network]
|
|
168
|
+
if network_name == 'default'
|
|
169
|
+
@options[:gce_subnetwork] ||= @compute.get_subnetwork(@options[:gce_project], default_region, 'default').name
|
|
170
|
+
elsif @options[:gce_subnetwork].nil?
|
|
171
|
+
# No subnet set, get the first subnet in our current region for the network
|
|
172
|
+
subnetwork = @compute.get_network(@options[:gce_project], network_name).subnetworks[0]
|
|
173
|
+
m = %r{.*/subnetworks/(?<subnetwork_name>.*)\Z}.match subnetwork
|
|
174
|
+
raise "Unable to find a subnetwork in provided network #{network_name}" if m.nil?
|
|
175
|
+
|
|
176
|
+
@options[:gce_subnetwork] = m['subnetwork_name']
|
|
177
|
+
end
|
|
178
|
+
@options[:gce_subnetwork]
|
|
179
|
+
end
|
|
175
180
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
# @return [Google::Auth::UserRefreshCredentials|Google::Auth::ServiceAccountCredentials]
|
|
184
|
-
# Authorization object to pass to Google APIs
|
|
185
|
-
def authenticate
|
|
186
|
-
if ENV['GOOGLE_APPLICATION_CREDENTIALS']
|
|
187
|
-
::Google::Auth::ServiceAccountCredentials.from_env(scope: GCP_AUTH_SCOPE)
|
|
188
|
-
else
|
|
189
|
-
# Fall back to default application auth
|
|
190
|
-
::Google::Auth.get_application_default(GCP_AUTH_SCOPE)
|
|
181
|
+
##
|
|
182
|
+
# Set the user-agent information for the application.
|
|
183
|
+
#
|
|
184
|
+
# @param version The version number of Beaker currently running
|
|
185
|
+
def client(version)
|
|
186
|
+
::Google::Apis::ClientOptions.default.application_name = 'beaker-google'
|
|
187
|
+
::Google::Apis::ClientOptions.default.application_version = version
|
|
191
188
|
end
|
|
192
|
-
end
|
|
193
189
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
190
|
+
##
|
|
191
|
+
# Creates an authentication object to use in the various Google APIs
|
|
192
|
+
#
|
|
193
|
+
# This method currently supports using application credentials via the
|
|
194
|
+
# GOOGLE_APPLICATION_CREDENTIALS environment variable, and application default
|
|
195
|
+
# credentials.
|
|
196
|
+
#
|
|
197
|
+
# @return [Google::Auth::UserRefreshCredentials|Google::Auth::ServiceAccountCredentials]
|
|
198
|
+
# Authorization object to pass to Google APIs
|
|
199
|
+
def authenticate
|
|
200
|
+
if ENV['GOOGLE_APPLICATION_CREDENTIALS']
|
|
201
|
+
::Google::Auth::ServiceAccountCredentials.from_env(scope: GCP_AUTH_SCOPE)
|
|
202
|
+
else
|
|
203
|
+
# Fall back to default application auth
|
|
204
|
+
::Google::Auth.get_application_default(GCP_AUTH_SCOPE)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
210
207
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
208
|
+
##
|
|
209
|
+
# Find the correct image object for a given project and name
|
|
210
|
+
#
|
|
211
|
+
# @param [String] image_project The project that owns the requested image
|
|
212
|
+
#
|
|
213
|
+
# @param [String] name The name of the image in the project. This must
|
|
214
|
+
# be the exact name of the image
|
|
215
|
+
#
|
|
216
|
+
# @return [Google::Apis::ComputeV1::Image]
|
|
217
|
+
#
|
|
218
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
219
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
220
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
221
|
+
def get_image(project, name)
|
|
222
|
+
@compute.get_image(project, name)
|
|
223
|
+
end
|
|
226
224
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
225
|
+
##
|
|
226
|
+
# Find the latest non-deprecated image in the given project and family
|
|
227
|
+
#
|
|
228
|
+
# @param [String] image_project The project that owns the requested image
|
|
229
|
+
#
|
|
230
|
+
# @param [String] family The name of the image family
|
|
231
|
+
#
|
|
232
|
+
# @return [Google::Apis::ComputeV1::Image]
|
|
233
|
+
#
|
|
234
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
235
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
236
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
237
|
+
def get_latest_image_from_family(image_project, family)
|
|
238
|
+
@compute.get_image_from_family(image_project, family)
|
|
239
|
+
end
|
|
241
240
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
@
|
|
252
|
-
|
|
241
|
+
##
|
|
242
|
+
# Determines the Google Compute machineType object based upon the selected
|
|
243
|
+
# gce_machine_type option
|
|
244
|
+
#
|
|
245
|
+
# @param [String] type_name The name of the type to get
|
|
246
|
+
#
|
|
247
|
+
# @return [Google::Apis::ComputeV1::MachineType]
|
|
248
|
+
#
|
|
249
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
250
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
251
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
252
|
+
def get_machine_type(type_name = DEFAULT_MACHINE_TYPE)
|
|
253
|
+
@compute.get_machine_type(@options[:gce_project], default_zone, type_name)
|
|
254
|
+
end
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
256
|
+
##
|
|
257
|
+
# Determines the Google Compute network object in use for the current connection
|
|
258
|
+
#
|
|
259
|
+
# @return [Google::Apis::ComputeV1::Network]
|
|
260
|
+
#
|
|
261
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
262
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
263
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
264
|
+
def get_network(network_name = default_network)
|
|
265
|
+
@compute.get_network(@options[:gce_project], network_name)
|
|
266
|
+
end
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
278
|
-
def list_disks
|
|
279
|
-
@compute.list_disks(@options[:gce_project], default_zone).items
|
|
280
|
-
end
|
|
268
|
+
##
|
|
269
|
+
# Determines a list of existing Google Compute instances
|
|
270
|
+
#
|
|
271
|
+
# @return [Array[Google::Apis::ComputeV1::Instance]]
|
|
272
|
+
#
|
|
273
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
274
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
275
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
276
|
+
def list_instances
|
|
277
|
+
@compute.list_instances(@options[:gce_project], default_zone).items
|
|
278
|
+
end
|
|
281
279
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
@
|
|
292
|
-
|
|
293
|
-
|
|
280
|
+
##
|
|
281
|
+
# Determines a list of existing Google Compute disks
|
|
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
|
+
# @return [Array[Google::Apis::ComputeV1::Disk]]
|
|
288
|
+
#
|
|
289
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
290
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
291
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
292
|
+
def list_disks
|
|
293
|
+
@compute.list_disks(@options[:gce_project], default_zone).items
|
|
294
|
+
end
|
|
294
295
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
#
|
|
307
|
-
# @param [Array<String>] source_ranges List of ranges in CIDR format to accept through the firewall. If neither 'source_ranges'
|
|
308
|
-
# or 'source_tags' is specified, GCP adds a default 'source_range' of '0.0.0.0/0' (allow all)
|
|
309
|
-
#
|
|
310
|
-
# @param [Array<String>] source_tags List of network tags to accept through the firewall. If neither 'source_ranges'
|
|
311
|
-
# or 'source_tags' is specified, GCP adds a default 'source_range' of '0.0.0.0/0' (allow all)
|
|
312
|
-
#
|
|
313
|
-
# @param [Array<String>] target_ranges List of ranges in CIDR format to apply this firewall. If neither 'target_ranges'
|
|
314
|
-
# or 'target_tags' is specified, the firewall applies to all hosts in the VPC
|
|
315
|
-
#
|
|
316
|
-
# @param [Array<String>] target_tags List of network tags to apply this firewall. If neither 'target_ranges'
|
|
317
|
-
# or 'target_tags' is specified, the firewall applies to all hosts in the VPC
|
|
318
|
-
#
|
|
319
|
-
# @return [Google::Apis::ComputeV1::Operation]
|
|
320
|
-
#
|
|
321
|
-
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
322
|
-
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
323
|
-
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
324
|
-
def create_firewall(name, network, allow: [], deny: [], source_ranges: [], source_tags: [], target_ranges: [], target_tags: [])
|
|
325
|
-
allowed = []
|
|
326
|
-
allow.each do |port|
|
|
327
|
-
parts = port.split('/', 2)
|
|
328
|
-
if parts[1] == 'tcp' || parts[1] == 'udp' || parts[1] == 'sctp' then
|
|
329
|
-
allowed << ::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1], ports: [parts[0]])
|
|
330
|
-
else
|
|
331
|
-
allowed << ::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1])
|
|
332
|
-
end
|
|
296
|
+
##
|
|
297
|
+
# Determines a list of existing Google Compute firewalls
|
|
298
|
+
#
|
|
299
|
+
# @return [Array[Google::Apis::ComputeV1::Firewall]]
|
|
300
|
+
#
|
|
301
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
302
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
303
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
304
|
+
def list_firewalls
|
|
305
|
+
@compute.list_firewalls(@options[:gce_project],
|
|
306
|
+
filter: 'name != default-allow-internal AND name != default-ssh').items
|
|
333
307
|
end
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
# Create a Google Compute firewall
|
|
311
|
+
#
|
|
312
|
+
# @param [String] name The name of the firewall to create
|
|
313
|
+
#
|
|
314
|
+
# @param [::Google::Apis::ComputeV1::Network] network The Google Compute networkin which to create
|
|
315
|
+
# the firewall
|
|
316
|
+
#
|
|
317
|
+
# @param [Array<String>] allow List of ports to allow through the firewall. One of 'allow' or 'deny' must be
|
|
318
|
+
# specified, but not both.
|
|
319
|
+
#
|
|
320
|
+
# @param [Array<String>] deny List of ports to deny through the firewall. One of 'allow' or 'deny' must be
|
|
321
|
+
# specified, but not both.
|
|
322
|
+
#
|
|
323
|
+
# @param [Array<String>] source_ranges List of ranges in CIDR format to accept through the firewall. If neither
|
|
324
|
+
# 'source_ranges' or 'source_tags' is specified, GCP adds a default 'source_range' of '0.0.0.0/0' (allow all)
|
|
325
|
+
#
|
|
326
|
+
# @param [Array<String>] source_tags List of network tags to accept through the firewall. If neither 'source_ranges'
|
|
327
|
+
# or 'source_tags' is specified, GCP adds a default 'source_range' of '0.0.0.0/0' (allow all)
|
|
328
|
+
#
|
|
329
|
+
# @param [Array<String>] target_ranges List of ranges in CIDR format to apply this firewall. If neither
|
|
330
|
+
# 'target_ranges' or 'target_tags' is specified, the firewall applies to all hosts in the VPC
|
|
331
|
+
#
|
|
332
|
+
# @param [Array<String>] target_tags List of network tags to apply this firewall. If neither 'target_ranges' or
|
|
333
|
+
# 'target_tags' is specified, the firewall applies to all hosts in the VPC
|
|
334
|
+
#
|
|
335
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
336
|
+
#
|
|
337
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
338
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
339
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
340
|
+
def create_firewall(name, network, allow: [], deny: [], source_ranges: [], source_tags: [], target_ranges: [],
|
|
341
|
+
target_tags: [])
|
|
342
|
+
allowed = []
|
|
343
|
+
allow.each do |port|
|
|
344
|
+
parts = port.split('/', 2)
|
|
345
|
+
allowed << if %w[tcp udp sctp].include?(parts[1])
|
|
346
|
+
::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1], ports: [parts[0]])
|
|
347
|
+
else
|
|
348
|
+
::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: parts[1])
|
|
349
|
+
end
|
|
341
350
|
end
|
|
351
|
+
denied = []
|
|
352
|
+
deny.each do |port|
|
|
353
|
+
parts = port.split('/', 2)
|
|
354
|
+
denied << if %w[tcp udp sctp].include?(parts[1])
|
|
355
|
+
::Google::Apis::ComputeV1::Firewall::Denied.new(ip_protocol: parts[1], ports: [parts[0]])
|
|
356
|
+
else
|
|
357
|
+
::Google::Apis::ComputeV1::Firewall::Denied.new(ip_protocol: parts[1])
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
firewall_object = ::Google::Apis::ComputeV1::Firewall.new(
|
|
362
|
+
name: name,
|
|
363
|
+
direction: 'INGRESS',
|
|
364
|
+
network: network.self_link,
|
|
365
|
+
allowed: allowed,
|
|
366
|
+
denied: denied,
|
|
367
|
+
source_ranges: source_ranges,
|
|
368
|
+
source_tags: source_tags,
|
|
369
|
+
target_ranges: target_ranges,
|
|
370
|
+
target_tags: target_tags,
|
|
371
|
+
)
|
|
372
|
+
operation = @compute.insert_firewall(@options[:gce_project], firewall_object)
|
|
373
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
342
374
|
end
|
|
343
375
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
end
|
|
376
|
+
##
|
|
377
|
+
# Get the named firewall
|
|
378
|
+
#
|
|
379
|
+
# @param [String] name The name of the firewall
|
|
380
|
+
#
|
|
381
|
+
# @return [Google::Apis::ComputeV1::Firewall]
|
|
382
|
+
#
|
|
383
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
384
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
385
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
386
|
+
def get_firewall(name)
|
|
387
|
+
@compute.get_firewall(@options[:gce_project], name)
|
|
388
|
+
end
|
|
358
389
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
390
|
+
##
|
|
391
|
+
# Add a source range to the firewall.
|
|
392
|
+
#
|
|
393
|
+
# @param [String] name The name of the firewall
|
|
394
|
+
#
|
|
395
|
+
# @param [String] range The IP range in CIDR format to add to the firewall
|
|
396
|
+
#
|
|
397
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
398
|
+
#
|
|
399
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
400
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
401
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
402
|
+
def add_firewall_source_range(name, range)
|
|
403
|
+
firewall = get_firewall(name)
|
|
404
|
+
firewall.source_ranges = [] if firewall.source_ranges.nil?
|
|
405
|
+
firewall.source_ranges << range
|
|
406
|
+
operation = @compute.patch_firewall(@options[:gce_project], name, firewall)
|
|
407
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
408
|
+
end
|
|
372
409
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
410
|
+
##
|
|
411
|
+
# Add an allowed port to the firewall
|
|
412
|
+
#
|
|
413
|
+
# @param [String] name The name of the firewall
|
|
414
|
+
#
|
|
415
|
+
# @param [String] port The port number to open on the firewall
|
|
416
|
+
#
|
|
417
|
+
# @param [String] proto The protocol of the port. This should be 'tcp' or 'udp'
|
|
418
|
+
#
|
|
419
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
420
|
+
#
|
|
421
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
422
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
423
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
424
|
+
def add_firewall_port(name, port, proto)
|
|
425
|
+
firewall = get_firewall(name)
|
|
426
|
+
firewall.allowed = [] if firewall.allowed.nil?
|
|
427
|
+
firewall.allowed << ::Google::Apis::ComputeV1::Firewall::Allowed.new(ip_protocol: proto, ports: [port])
|
|
428
|
+
operation = @compute.patch_firewall(@options[:gce_project], name, firewall)
|
|
429
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
430
|
+
end
|
|
392
431
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
413
|
-
end
|
|
432
|
+
##
|
|
433
|
+
# Add a taget_tag to an existing firewall
|
|
434
|
+
#
|
|
435
|
+
# @param [String] the name of the firewall to update
|
|
436
|
+
#
|
|
437
|
+
# @param [String] tag The target tag to add to the firewall
|
|
438
|
+
#
|
|
439
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
440
|
+
#
|
|
441
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
442
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
443
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
444
|
+
def add_firewall_target_tag(name, tag)
|
|
445
|
+
firewall = @compute.get_firewall(@options[:gce_project], name)
|
|
446
|
+
firewall.target_tags = [] if firewall.target_tags.nil?
|
|
447
|
+
firewall.target_tags << tag
|
|
448
|
+
operation = @compute.patch_firewall(@options[:gce_project], name, firewall)
|
|
449
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
450
|
+
end
|
|
414
451
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
452
|
+
##
|
|
453
|
+
# Add a source_tag to an existing firewall
|
|
454
|
+
#
|
|
455
|
+
# @param [String] the name of the firewall to update
|
|
456
|
+
#
|
|
457
|
+
# @param [String] tag The source tag to add to the firewall
|
|
458
|
+
#
|
|
459
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
460
|
+
#
|
|
461
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
462
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
463
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
464
|
+
def add_firewall_source_tag(name, tag)
|
|
465
|
+
firewall = @compute.get_firewall(@options[:gce_project], name)
|
|
466
|
+
firewall.source_tags = [] if firewall.source_tags.nil?
|
|
467
|
+
firewall.source_tags << tag
|
|
468
|
+
operation = @compute.patch_firewall(@options[:gce_project], name, firewall)
|
|
469
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
470
|
+
end
|
|
434
471
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
472
|
+
##
|
|
473
|
+
# Create a Google Compute disk
|
|
474
|
+
#
|
|
475
|
+
# @param [String] name The name of the disk to create
|
|
476
|
+
#
|
|
477
|
+
# @param [String] img The existing disk image to clone for this image
|
|
478
|
+
# or nil to create a blank disk
|
|
479
|
+
#
|
|
480
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
481
|
+
#
|
|
482
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
483
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
484
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
485
|
+
def create_disk(name, size, img = nil)
|
|
486
|
+
new_disk = ::Google::Apis::ComputeV1::Disk.new(
|
|
487
|
+
name: name,
|
|
488
|
+
size_gb: size,
|
|
489
|
+
source_image: img,
|
|
490
|
+
)
|
|
491
|
+
operation = @compute.insert_disk(@options[:gce_project], @options[:gce_zone], new_disk)
|
|
492
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
493
|
+
end
|
|
454
494
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
495
|
+
##
|
|
496
|
+
# Create a Google Compute instance
|
|
497
|
+
#
|
|
498
|
+
# @param [String] name The name of the instance to create
|
|
499
|
+
#
|
|
500
|
+
# @param [Google::Apis::ComputeV1::Image] img The Google Compute image to use for instance creation
|
|
501
|
+
#
|
|
502
|
+
# @param [Google::Apis::ComputeV1::MachineType] machine_type The Google Compute Machine Type
|
|
503
|
+
#
|
|
504
|
+
# @param [Integer] disk_size The size of the boot disk for the new instance. Must be equal to or
|
|
505
|
+
# greater than the image disk's size
|
|
506
|
+
#
|
|
507
|
+
# @param [String] hostname The custom hostname to set in the OS of the instance
|
|
508
|
+
#
|
|
509
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
510
|
+
#
|
|
511
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
512
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
513
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
514
|
+
def create_instance(name, img, machine_type, disk_size, hostname)
|
|
515
|
+
initialize_params = ::Google::Apis::ComputeV1::AttachedDiskInitializeParams.new(
|
|
516
|
+
disk_size_gb: disk_size,
|
|
517
|
+
source_image: img.self_link,
|
|
518
|
+
)
|
|
519
|
+
disk_params = ::Google::Apis::ComputeV1::AttachedDisk.new(
|
|
520
|
+
boot: true,
|
|
521
|
+
auto_delete: true,
|
|
522
|
+
initialize_params: initialize_params,
|
|
523
|
+
)
|
|
524
|
+
# attached_network = ::Google::Apis::ComputeV1::networkInterfaces.new()
|
|
525
|
+
tags = ::Google::Apis::ComputeV1::Tags.new(
|
|
526
|
+
items: [name],
|
|
527
|
+
)
|
|
528
|
+
network_interface = ::Google::Apis::ComputeV1::NetworkInterface.new(
|
|
529
|
+
network: get_network(default_network).self_link,
|
|
530
|
+
subnetwork: @compute.get_subnetwork(@options[:gce_project], default_region, default_subnetwork).self_link,
|
|
531
|
+
# Create an AccessConfig to add a NAT IP to the host.
|
|
532
|
+
# TODO: Make this configurable
|
|
533
|
+
access_configs: [
|
|
534
|
+
::Google::Apis::ComputeV1::AccessConfig.new(
|
|
535
|
+
network_tier: 'STANDARD',
|
|
536
|
+
),
|
|
537
|
+
],
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
instance_opts = {
|
|
541
|
+
machine_type: machine_type.self_link,
|
|
542
|
+
name: name,
|
|
543
|
+
disks: [disk_params],
|
|
544
|
+
network_interfaces: [network_interface],
|
|
545
|
+
tags: tags,
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
# use custom hostname if specified
|
|
549
|
+
if hostname && ENV.fetch('BEAKER_set_gce_hostname', false)
|
|
550
|
+
# The google api requires an FQDN for the custom hostname
|
|
551
|
+
valid_hostname = hostname.include?('.') ? hostname : "#{hostname}.beaker.test"
|
|
552
|
+
instance_opts[:hostname] = valid_hostname
|
|
553
|
+
end
|
|
477
554
|
|
|
478
|
-
|
|
479
|
-
# Create a Google Compute instance
|
|
480
|
-
#
|
|
481
|
-
# @param [String] name The name of the instance to create
|
|
482
|
-
#
|
|
483
|
-
# @param [Google::Apis::ComputeV1::Image] img The Google Compute image to use for instance creation
|
|
484
|
-
#
|
|
485
|
-
# @param [Google::Apis::ComputeV1::MachineType] machine_type The Google Compute Machine Type
|
|
486
|
-
#
|
|
487
|
-
# @param [Integer] disk_size The size of the boot disk for the new instance. Must be equal to or
|
|
488
|
-
# greater than the image disk's size
|
|
489
|
-
#
|
|
490
|
-
# @param [String] hostname The custom hostname to set in the OS of the instance
|
|
491
|
-
#
|
|
492
|
-
# @return [Google::Apis::ComputeV1::Operation]
|
|
493
|
-
#
|
|
494
|
-
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
495
|
-
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
496
|
-
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
497
|
-
def create_instance(name, img, machine_type, disk_size, hostname)
|
|
498
|
-
initialize_params = ::Google::Apis::ComputeV1::AttachedDiskInitializeParams.new(
|
|
499
|
-
disk_size_gb: disk_size,
|
|
500
|
-
source_image: img.self_link,
|
|
501
|
-
)
|
|
502
|
-
disk_params = ::Google::Apis::ComputeV1::AttachedDisk.new(
|
|
503
|
-
boot: true,
|
|
504
|
-
auto_delete: true,
|
|
505
|
-
initialize_params: initialize_params,
|
|
506
|
-
)
|
|
507
|
-
# attached_network = ::Google::Apis::ComputeV1::networkInterfaces.new()
|
|
508
|
-
tags = ::Google::Apis::ComputeV1::Tags.new(
|
|
509
|
-
items: [name],
|
|
510
|
-
)
|
|
511
|
-
network_interface = ::Google::Apis::ComputeV1::NetworkInterface.new(
|
|
512
|
-
network: get_network(default_network).self_link,
|
|
513
|
-
subnetwork: @compute.get_subnetwork(@options[:gce_project], default_region, default_subnetwork).self_link,
|
|
514
|
-
# Create an AccessConfig to add a NAT IP to the host.
|
|
515
|
-
# TODO: Make this configurable
|
|
516
|
-
access_configs: [
|
|
517
|
-
::Google::Apis::ComputeV1::AccessConfig.new(
|
|
518
|
-
network_tier: 'STANDARD',
|
|
519
|
-
),
|
|
520
|
-
],
|
|
521
|
-
)
|
|
522
|
-
|
|
523
|
-
instance_opts = {
|
|
524
|
-
:machine_type => machine_type.self_link,
|
|
525
|
-
:name => name,
|
|
526
|
-
:disks => [disk_params],
|
|
527
|
-
:network_interfaces => [network_interface],
|
|
528
|
-
:tags => tags,
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
# use custom hostname if specified
|
|
532
|
-
if hostname && ENV.fetch('BEAKER_set_gce_hostname', false)
|
|
533
|
-
# The google api requires an FQDN for the custom hostname
|
|
534
|
-
hostname.include?('.') ? valid_hostname = hostname : valid_hostname = hostname + '.beaker.test'
|
|
535
|
-
instance_opts[:hostname] = valid_hostname
|
|
536
|
-
end
|
|
537
|
-
|
|
538
|
-
new_instance = ::Google::Apis::ComputeV1::Instance.new(instance_opts)
|
|
555
|
+
new_instance = ::Google::Apis::ComputeV1::Instance.new(**instance_opts)
|
|
539
556
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
557
|
+
operation = @compute.insert_instance(@options[:gce_project], @options[:gce_zone], new_instance)
|
|
558
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
559
|
+
end
|
|
543
560
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
561
|
+
##
|
|
562
|
+
# Get the named instace from Google Compute Image
|
|
563
|
+
#
|
|
564
|
+
# @param [String] name The name of the instance
|
|
565
|
+
#
|
|
566
|
+
# @return [Google::Apis::ComputeV1::Instance]
|
|
567
|
+
#
|
|
568
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
569
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
570
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
571
|
+
def get_instance(name)
|
|
572
|
+
@compute.get_instance(@options[:gce_project], @options[:gce_zone], name)
|
|
573
|
+
end
|
|
557
574
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
575
|
+
##
|
|
576
|
+
# Add a tag to a Google Compute Instance
|
|
577
|
+
#
|
|
578
|
+
# @param [String] name The name of the instance
|
|
579
|
+
#
|
|
580
|
+
# @param [String] tag The tag to add to the instance
|
|
581
|
+
#
|
|
582
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
583
|
+
#
|
|
584
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
585
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
586
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
587
|
+
def add_instance_tag(name, tag)
|
|
588
|
+
instance = get_instance(name)
|
|
589
|
+
tags = instance.tags
|
|
590
|
+
tags.items << tag
|
|
591
|
+
operation = @compute.set_instance_tags(@options[:gce_project], @options[:gce_zone], name, tags)
|
|
592
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
593
|
+
end
|
|
577
594
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
595
|
+
##
|
|
596
|
+
# Set key/value metadata pairs to a Google Compute instance
|
|
597
|
+
#
|
|
598
|
+
# This function replaces any existing items in the metadata hash!
|
|
599
|
+
#
|
|
600
|
+
# @param [String] name The name of the instance to set metadata
|
|
601
|
+
#
|
|
602
|
+
# @param [String] data An array of hashes to set ass metadata. Each array
|
|
603
|
+
# item should have a 'key' and 'value' key.
|
|
604
|
+
#
|
|
605
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
606
|
+
#
|
|
607
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
608
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
609
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
610
|
+
def set_metadata_on_instance(name, data)
|
|
611
|
+
instance = @compute.get_instance(@options[:gce_project], @options[:gce_zone], name)
|
|
612
|
+
mdata = instance.metadata.dup
|
|
613
|
+
mdata.items = data
|
|
614
|
+
operation = @compute.set_instance_metadata(@options[:gce_project], @options[:gce_zone], name, mdata)
|
|
615
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
616
|
+
end
|
|
600
617
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
618
|
+
##
|
|
619
|
+
# Delete a Google Compute instance
|
|
620
|
+
#
|
|
621
|
+
# @param [String] name The name of the instance to delete
|
|
622
|
+
#
|
|
623
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
624
|
+
#
|
|
625
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
626
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
627
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
628
|
+
def delete_instance(name)
|
|
629
|
+
operation = @compute.delete_instance(@options[:gce_project], default_zone, name)
|
|
630
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
631
|
+
end
|
|
615
632
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
633
|
+
##
|
|
634
|
+
# Delete a Google Compute disk
|
|
635
|
+
#
|
|
636
|
+
# @param [String] name The name of the disk to delete
|
|
637
|
+
#
|
|
638
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
639
|
+
#
|
|
640
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
641
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
642
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
643
|
+
def delete_disk(name)
|
|
644
|
+
operation = @compute.delete_disk(@options[:gce_project], default_zone, name)
|
|
645
|
+
@compute.wait_zone_operation(@options[:gce_project], @options[:gce_zone], operation.name)
|
|
646
|
+
end
|
|
630
647
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
648
|
+
##
|
|
649
|
+
# Delete a Google Compute firewall
|
|
650
|
+
#
|
|
651
|
+
# @param [String] name The name of the firewall to delete
|
|
652
|
+
#
|
|
653
|
+
# @return [Google::Apis::ComputeV1::Operation]
|
|
654
|
+
#
|
|
655
|
+
# @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
|
|
656
|
+
# @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
|
|
657
|
+
# @raise [Google::Apis::AuthorizationError] Authorization is required
|
|
658
|
+
def delete_firewall(name)
|
|
659
|
+
operation = @compute.delete_firewall(@options[:gce_project], name)
|
|
660
|
+
@compute.wait_global_operation(@options[:gce_project], operation.name)
|
|
661
|
+
end
|
|
644
662
|
end
|
|
645
663
|
end
|