kitchen-google 1.5.0 → 2.1.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.
data/Rakefile DELETED
@@ -1,14 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "chefstyle"
9
- require "rubocop/rake_task"
10
- RuboCop::RakeTask.new(:style) do |task|
11
- task.options << "--display-cop-names"
12
- end
13
-
14
- task default: [:spec, :style]
@@ -1,29 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "kitchen/driver/gce_version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "kitchen-google"
7
- s.version = Kitchen::Driver::GCE_VERSION
8
- s.date = "2016-03-10"
9
- s.summary = "Kitchen::Driver::Gce"
10
- s.description = "A Test-Kitchen driver for Google Compute Engine"
11
- s.authors = ["Andrew Leonard", "Chef Partner Engineering"]
12
- s.email = ["andy@hurricane-ridge.com", "partnereng@chef.io"]
13
- s.files = `git ls-files`.split($/)
14
- s.homepage = "https://github.com/test-kitchen/kitchen-google"
15
- s.license = "Apache 2.0"
16
-
17
- s.add_dependency "gcewinpass", "~> 1.1"
18
- s.add_dependency "google-api-client", "~> 0.19"
19
- s.add_dependency "test-kitchen"
20
-
21
- s.add_development_dependency "bundler"
22
- s.add_development_dependency "pry"
23
- s.add_development_dependency "rake", "~> 10.5"
24
- s.add_development_dependency "rspec"
25
- s.add_development_dependency "rubocop"
26
- s.add_development_dependency "byebug"
27
-
28
- s.required_ruby_version = ">= 2.0"
29
- end
@@ -1,1278 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Author:: Andrew Leonard (<andy@hurricane-ridge.com>)
4
- # Author:: Chef Partner Engineering (<partnereng@chef.io>)
5
- #
6
- # Copyright (C) 2013-2016, Andrew Leonard and Chef Software, Inc.
7
- #
8
- # Licensed under the Apache License, Version 2.0 (the "License");
9
- # you may not use this file except in compliance with the License.
10
- # You may obtain a copy of the License at
11
- #
12
- # http://www.apache.org/licenses/LICENSE-2.0
13
- #
14
- # Unless required by applicable law or agreed to in writing, software
15
- # distributed under the License is distributed on an "AS IS" BASIS,
16
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- # See the License for the specific language governing permissions and
18
- # limitations under the License.
19
-
20
- require "spec_helper"
21
- require "google/apis/compute_v1"
22
- require "kitchen/driver/gce"
23
- require "kitchen/provisioner/dummy"
24
- require "kitchen/transport/dummy"
25
- require "kitchen/verifier/dummy"
26
-
27
- shared_examples_for "a validity checker" do |config_key, api_method, *args|
28
- it "returns false if the config value is nil" do
29
- expect(driver).to receive(:config).and_return({})
30
- expect(subject).to eq(false)
31
- end
32
-
33
- it "checks the outcome of the API call" do
34
- connection = double("connection")
35
- allow(driver).to receive(:config).and_return({ config_key => "test_value" })
36
- expect(driver).to receive(:connection).and_return(connection)
37
- expect(connection).to receive(api_method).with(*args, "test_value")
38
- expect(driver).to receive(:check_api_call).and_call_original
39
- expect(subject).to eq(true)
40
- end
41
- end
42
-
43
- describe Kitchen::Driver::Gce do
44
- let(:logged_output) { StringIO.new }
45
- let(:logger) { Logger.new(logged_output) }
46
- let(:platform) { Kitchen::Platform.new(name: "fake_platform") }
47
- let(:transport) { Kitchen::Transport::Dummy.new }
48
- let(:driver) { Kitchen::Driver::Gce.new(config) }
49
-
50
- let(:project) { "test_project" }
51
- let(:zone) { "test_zone" }
52
-
53
- let(:config) do
54
- {
55
- project: project,
56
- zone: zone,
57
- image_name: "test_image",
58
- }
59
- end
60
-
61
- let(:instance) do
62
- instance_double(Kitchen::Instance,
63
- logger: logger,
64
- transport: transport,
65
- platform: platform,
66
- to_str: "instance_str"
67
- )
68
- end
69
-
70
- before do
71
- allow(driver).to receive(:instance).and_return(instance)
72
- allow(driver).to receive(:project).and_return("test_project")
73
- allow(driver).to receive(:zone).and_return("test_zone")
74
- allow(driver).to receive(:region).and_return("test_region")
75
- end
76
-
77
- it "driver API version is 2" do
78
- expect(driver.diagnose_plugin[:api_version]).to eq(2)
79
- end
80
-
81
- describe "#name" do
82
- it "has an overridden name" do
83
- expect(driver.name).to eq("Google Compute (GCE)")
84
- end
85
- end
86
-
87
- describe "#create" do
88
- let(:connection) { double("connection") }
89
- let(:operation) { double("operation", name: "test_operation") }
90
- let(:state) { {} }
91
-
92
- before do
93
- allow(driver).to receive(:validate!)
94
- allow(driver).to receive(:connection).and_return(connection)
95
- allow(driver).to receive(:generate_server_name)
96
- allow(driver).to receive(:wait_for_operation)
97
- allow(driver).to receive(:server_instance)
98
- allow(driver).to receive(:create_instance_object)
99
- allow(driver).to receive(:ip_address_for)
100
- allow(driver).to receive(:update_windows_password)
101
- allow(driver).to receive(:wait_for_server)
102
- allow(connection).to receive(:insert_instance).and_return(operation)
103
- end
104
-
105
- it "does not create the server if the hostname is in the state file" do
106
- expect(connection).not_to receive(:insert_instance)
107
- driver.create(server_name: "server_exists")
108
- end
109
-
110
- it "generates a unique server name and sets the state" do
111
- expect(driver).to receive(:generate_server_name).and_return("server_1")
112
- driver.create(state)
113
- expect(state[:server_name]).to eq("server_1")
114
- end
115
-
116
- it "creates the instance via the API and waits for it to complete" do
117
- expect(driver).to receive(:generate_server_name).and_return("server_1")
118
- expect(driver).to receive(:create_instance_object).with("server_1").and_return("create_obj")
119
- expect(connection).to receive(:insert_instance).with("test_project", "test_zone", "create_obj").and_return(operation)
120
- expect(driver).to receive(:wait_for_operation).with(operation)
121
-
122
- driver.create(state)
123
- end
124
-
125
- it "sets the correct data in the state object" do
126
- expect(driver).to receive(:generate_server_name).and_return("server_1")
127
- expect(driver).to receive(:server_instance).with("server_1").and_return("server_obj")
128
- expect(driver).to receive(:ip_address_for).with("server_obj").and_return("1.2.3.4")
129
- driver.create(state)
130
-
131
- expect(state[:server_name]).to eq("server_1")
132
- expect(state[:hostname]).to eq("1.2.3.4")
133
- expect(state[:zone]).to eq("test_zone")
134
- end
135
-
136
- it "updates the windows password" do
137
- expect(driver).to receive(:generate_server_name).and_return("server_1")
138
- expect(driver).to receive(:update_windows_password).with("server_1")
139
- driver.create(state)
140
- end
141
-
142
- it "waits for the server to be ready" do
143
- expect(driver).to receive(:wait_for_server)
144
- driver.create(state)
145
- end
146
-
147
- it "destroys the server if any exceptions are raised" do
148
- expect(connection).to receive(:insert_instance).and_raise(RuntimeError)
149
- expect(driver).to receive(:destroy).with(state)
150
- expect { driver.create(state) }.to raise_error(RuntimeError)
151
- end
152
- end
153
-
154
- describe "#destroy" do
155
- let(:connection) { double("connection") }
156
- let(:state) { { server_name: "server_1", hostname: "test_host", zone: "test_zone" } }
157
-
158
- before do
159
- allow(driver).to receive(:connection).and_return(connection)
160
- allow(driver).to receive(:server_exist?).and_return(true)
161
- allow(driver).to receive(:wait_for_operation)
162
- allow(connection).to receive(:delete_instance)
163
- end
164
-
165
- it "does not attempt to delete the instance if there is no server_name" do
166
- expect(connection).not_to receive(:delete_instance)
167
- driver.destroy({})
168
- end
169
-
170
- it "does not attempt to delete the instance if it does not exist" do
171
- expect(driver).to receive(:server_exist?).with("server_1").and_return(false)
172
- expect(connection).not_to receive(:delete_instance)
173
- driver.destroy(state)
174
- end
175
-
176
- it "deletes the instance via the API and waits for it to complete" do
177
- expect(connection).to receive(:delete_instance).with("test_project", "test_zone", "server_1").and_return("operation")
178
- expect(driver).to receive(:wait_for_operation).with("operation")
179
- driver.destroy(state)
180
- end
181
-
182
- it "deletes the state keys" do
183
- driver.destroy(state)
184
- expect(state.key?(:server_name)).to eq(false)
185
- expect(state.key?(:hostname)).to eq(false)
186
- expect(state.key?(:zone)).to eq(false)
187
- end
188
- end
189
-
190
- describe "#validate!" do
191
- let(:config) do
192
- {
193
- project: "test_project",
194
- zone: "test_zone",
195
- machine_type: "test_machine_type",
196
- disk_type: "test_disk_type",
197
- image_name: "test_image",
198
- network: "test_network",
199
- }
200
- end
201
-
202
- before do
203
- allow(driver).to receive(:valid_project?).and_return(true)
204
- allow(driver).to receive(:valid_zone?).and_return(true)
205
- allow(driver).to receive(:valid_region?).and_return(true)
206
- allow(driver).to receive(:valid_machine_type?).and_return(true)
207
- allow(driver).to receive(:valid_disk_type?).and_return(true)
208
- allow(driver).to receive(:boot_disk_source_image).and_return("image")
209
- allow(driver).to receive(:valid_network?).and_return(true)
210
- allow(driver).to receive(:valid_subnet?).and_return(true)
211
- allow(driver).to receive(:winrm_transport?).and_return(false)
212
- allow(driver).to receive(:config).and_return(config)
213
- end
214
-
215
- it "does not raise an exception when all validations are successful" do
216
- expect { driver.validate! }.not_to raise_error
217
- end
218
-
219
- context "when neither zone nor region are specified" do
220
- let(:config) { { image_name: "test_image" } }
221
- it "raises an exception" do
222
- expect { driver.validate! }.to raise_error(RuntimeError, "Either zone or region must be specified")
223
- end
224
- end
225
-
226
- context "when neither image_family nor image_name are specified" do
227
- let(:config) { { zone: "test_zone" } }
228
- it "raises an exception" do
229
- expect { driver.validate! }.to raise_error(RuntimeError, "Either image family or name must be specified")
230
- end
231
- end
232
-
233
- context "when zone and region are both set" do
234
- let(:config) { { zone: "test_zone", region: "test_region", image_project: "test_project", image_name: "test_image" } }
235
-
236
- it "warns the user that the region will be ignored" do
237
- expect(driver).to receive(:warn).with("Both zone and region specified - region will be ignored.")
238
- driver.validate!
239
- end
240
- end
241
-
242
- context "when image family and name are both set" do
243
- let(:config) { { image_project: "test_project", image_name: "test_image", image_family: "test_image_family", zone: "test_zone" } }
244
-
245
- it "warns the user that the image family will be ignored" do
246
- expect(driver).to receive(:warn).with("Both image family and name specified - image family will be ignored")
247
- driver.validate!
248
- end
249
- end
250
-
251
- context "when region is set to 'any'" do
252
- let(:config) { { region: "any" } }
253
- it "raises an exception" do
254
- expect { driver.validate! }.to raise_error(RuntimeError, "'any' is no longer a valid region")
255
- end
256
- end
257
-
258
- context "when zone is set" do
259
- let(:config) { { zone: "test_zone" } }
260
-
261
- it "raises an exception if the zone is not valid" do
262
- expect(driver).to receive(:valid_zone?).and_return(false)
263
- expect { driver.validate! }.to raise_error(RuntimeError, "Zone test_zone is not a valid zone")
264
- end
265
- end
266
-
267
- context "when region is set" do
268
- let(:config) { { region: "test_region" } }
269
-
270
- it "raises an exception if the region is not valid" do
271
- expect(driver).to receive(:valid_region?).and_return(false)
272
- expect { driver.validate! }.to raise_error(RuntimeError, "Region test_region is not a valid region")
273
- end
274
- end
275
-
276
- context "when subnet is set" do
277
- let(:config) do
278
- {
279
- project: "test_project",
280
- zone: "test_zone",
281
- image_name: "test_image",
282
- machine_type: "test_machine_type",
283
- disk_type: "test_disk_type",
284
- network: "test_network",
285
- subnet: "test_subnet",
286
- }
287
- end
288
-
289
- it "raises an exception if the subnet is invalid" do
290
- expect(driver).to receive(:valid_subnet?).and_return(false)
291
- expect { driver.validate! }.to raise_error(RuntimeError, "Subnet test_subnet is not valid")
292
- end
293
- end
294
-
295
- it "raises an exception if the project is invalid" do
296
- expect(driver).to receive(:valid_project?).and_return(false)
297
- expect { driver.validate! }.to raise_error(RuntimeError, "Project test_project is not a valid project")
298
- end
299
-
300
- it "raises an exception if the machine_type is invalid" do
301
- expect(driver).to receive(:valid_machine_type?).and_return(false)
302
- expect { driver.validate! }.to raise_error(RuntimeError, "Machine type test_machine_type is not valid")
303
- end
304
-
305
- it "raises an exception if the boot disk source image is invalid" do
306
- expect(driver).to receive(:boot_disk_source_image).and_return(nil)
307
- expect { driver.validate! }.to raise_error(RuntimeError, "Disk image test_image is not valid - check your image name and image project")
308
- end
309
-
310
- context "both disk configurations are active" do
311
- let(:config) do
312
- {
313
- project: "test_project",
314
- zone: "test_zone",
315
- image_name: "test_image",
316
- machine_type: "test_machine_type",
317
- autodelete_disk: true,
318
- disks: {
319
- disk0: {
320
- autodelete_disk: true,
321
- },
322
- },
323
- }
324
- end
325
- it "raises an exception if parameters from both disk configurations are present" do
326
- expect { driver.validate! }.to raise_error(RuntimeError, "You cannot use autodelete_disk, disk_size or disk_type with the new disks configuration")
327
- end
328
- end
329
-
330
- it "raises an exception if the network is invalid" do
331
- expect(driver).to receive(:valid_network?).and_return(false)
332
- expect { driver.validate! }.to raise_error(RuntimeError, "Network test_network is not valid")
333
- end
334
-
335
- it "raises an exception if WinRM transport is used but no email is set" do
336
- expect(driver).to receive(:winrm_transport?).and_return(true)
337
- expect { driver.validate! }.to raise_error(RuntimeError, "Email address of GCE user is not set")
338
- end
339
- end
340
-
341
- describe "#connection" do
342
- it "returns a properly configured ComputeService" do
343
- compute_service = double("compute_service")
344
- client_options = double("client_options")
345
-
346
- expect(Google::Apis::ClientOptions).to receive(:new).and_return(client_options)
347
- expect(client_options).to receive(:application_name=).with("GoogleChefTestKitchen")
348
- expect(client_options).to receive(:application_version=).with(Kitchen::Driver::GCE_VERSION)
349
-
350
- expect(Google::Apis::ComputeV1::ComputeService).to receive(:new).and_return(compute_service)
351
- expect(driver).to receive(:authorization).and_return("authorization_object")
352
- expect(compute_service).to receive(:authorization=).with("authorization_object")
353
- expect(compute_service).to receive(:client_options=).with(client_options)
354
-
355
- expect(driver.connection).to eq(compute_service)
356
- end
357
- end
358
-
359
- describe "#authorization" do
360
- it "returns a Google::Auth authorization object" do
361
- auth_object = double("auth_object")
362
- expect(Google::Auth).to receive(:get_application_default).and_return(auth_object)
363
- expect(driver.authorization).to eq(auth_object)
364
- end
365
- end
366
-
367
- describe "#winrm_transport?" do
368
- it "returns true if the transport name is Winrm" do
369
- expect(transport).to receive(:name).and_return("Winrm")
370
- expect(driver.winrm_transport?).to eq(true)
371
- end
372
-
373
- it "returns false if the transport name is not Winrm" do
374
- expect(transport).to receive(:name).and_return("Ssh")
375
- expect(driver.winrm_transport?).to eq(false)
376
- end
377
- end
378
-
379
- describe "#update_windows_password" do
380
- it "does not attempt to reset the password if the transport is not WinRM" do
381
- expect(driver).to receive(:winrm_transport?).and_return(false)
382
- expect(GoogleComputeWindowsPassword).not_to receive(:new)
383
-
384
- driver.update_windows_password("server_1")
385
- end
386
-
387
- it "resets the password and puts it in the state object if the transport is WinRM" do
388
- state = {}
389
- winpass = double("winpass")
390
- winpass_config = {
391
- project: "test_project",
392
- zone: "test_zone",
393
- instance_name: "server_1",
394
- email: "test_email",
395
- username: "test_username",
396
- }
397
-
398
- allow(driver).to receive(:state).and_return(state)
399
- expect(transport).to receive(:config).and_return(username: "test_username")
400
- expect(driver).to receive(:config).and_return(email: "test_email")
401
- expect(driver).to receive(:winrm_transport?).and_return(true)
402
- expect(GoogleComputeWindowsPassword).to receive(:new).with(winpass_config).and_return(winpass)
403
- expect(winpass).to receive(:new_password).and_return("password123")
404
- driver.update_windows_password("server_1")
405
- expect(state[:password]).to eq("password123")
406
- end
407
- end
408
-
409
- describe "#check_api_call" do
410
- it "returns false and logs a debug message if the block raises a ClientError" do
411
- expect(driver).to receive(:debug).with("API error: whoops")
412
- expect(driver.check_api_call { raise Google::Apis::ClientError.new("whoops") }).to eq(false)
413
- end
414
-
415
- it "raises an exception if the block raises something other than a ClientError" do
416
- expect { driver.check_api_call { raise "whoops" } }.to raise_error(RuntimeError)
417
- end
418
-
419
- it "returns true if the block does not raise an exception" do
420
- expect(driver.check_api_call { true }).to eq(true)
421
- end
422
- end
423
-
424
- describe "#valid_machine_type?" do
425
- subject { driver.valid_machine_type? }
426
- it_behaves_like "a validity checker", :machine_type, :get_machine_type, "test_project", "test_zone"
427
- end
428
-
429
- describe "#valid_network?" do
430
- subject { driver.valid_network? }
431
- it_behaves_like "a validity checker", :network, :get_network, "test_project"
432
- end
433
-
434
- describe "#valid_subnet?" do
435
- subject { driver.valid_subnet? }
436
- it_behaves_like "a validity checker", :subnet, :get_subnetwork, "test_project", "test_region"
437
- end
438
-
439
- describe "#valid_zone?" do
440
- subject { driver.valid_zone? }
441
- it_behaves_like "a validity checker", :zone, :get_zone, "test_project"
442
- end
443
-
444
- describe "#valid_region?" do
445
- subject { driver.valid_region? }
446
- it_behaves_like "a validity checker", :region, :get_region, "test_project"
447
- end
448
-
449
- describe "#image_exist?" do
450
- it "checks the outcome of the API call" do
451
- connection = double("connection")
452
- expect(driver).to receive(:connection).and_return(connection)
453
- expect(connection).to receive(:get_image).with("test_project", "test_image")
454
- expect(driver).to receive(:check_api_call).and_call_original
455
- expect(driver.image_exist?).to eq(true)
456
- end
457
- end
458
-
459
- describe "#server_exist?" do
460
- it "checks the outcome of the API call" do
461
- expect(driver).to receive(:server_instance).with("server_1")
462
- expect(driver).to receive(:check_api_call).and_call_original
463
- expect(driver.server_exist?("server_1")).to eq(true)
464
- end
465
- end
466
-
467
- describe "#project" do
468
- it "returns the project from the config" do
469
- allow(driver).to receive(:project).and_call_original
470
- expect(driver).to receive(:config).and_return(project: "my_project")
471
- expect(driver.project).to eq("my_project")
472
- end
473
- end
474
-
475
- describe "#region" do
476
- it "returns the region from the config if specified" do
477
- allow(driver).to receive(:region).and_call_original
478
- allow(driver).to receive(:config).and_return(region: "my_region")
479
- expect(driver.region).to eq("my_region")
480
- end
481
-
482
- it "returns the region for the zone if no region is specified" do
483
- allow(driver).to receive(:region).and_call_original
484
- allow(driver).to receive(:config).and_return({})
485
- expect(driver).to receive(:region_for_zone).and_return("zone_region")
486
- expect(driver.region).to eq("zone_region")
487
- end
488
- end
489
-
490
- describe "#region_for_zone" do
491
- it "returns the region for a given zone" do
492
- connection = double("connection")
493
- zone_obj = double("zone_obj", region: "/path/to/test_region")
494
-
495
- expect(driver).to receive(:connection).and_return(connection)
496
- expect(connection).to receive(:get_zone).with(project, zone).and_return(zone_obj)
497
- expect(driver.region_for_zone).to eq("test_region")
498
- end
499
- end
500
-
501
- describe "#zone" do
502
- before do
503
- allow(driver).to receive(:zone).and_call_original
504
- end
505
-
506
- context "when a zone exists in the state" do
507
- let(:state) { { zone: "state_zone" } }
508
-
509
- it "returns the zone from the state" do
510
- expect(driver).to receive(:state).and_return(state)
511
- expect(driver.zone).to eq("state_zone")
512
- end
513
- end
514
-
515
- context "when a zone does not exist in the state" do
516
- let(:state) { {} }
517
-
518
- before do
519
- allow(driver).to receive(:state).and_return(state)
520
- end
521
-
522
- it "returns the zone from the config if it exists" do
523
- expect(driver).to receive(:config).and_return(zone: "config_zone")
524
- expect(driver.zone).to eq("config_zone")
525
- end
526
-
527
- it "returns the zone from find_zone if it does not exist in the config" do
528
- expect(driver).to receive(:config).and_return({})
529
- expect(driver).to receive(:find_zone).and_return("found_zone")
530
- expect(driver.zone).to eq("found_zone")
531
- end
532
- end
533
- end
534
-
535
- describe "#find_zone" do
536
- let(:zones_in_region) { double("zones_in_region") }
537
-
538
- before do
539
- expect(driver).to receive(:zones_in_region).and_return(zones_in_region)
540
- end
541
-
542
- it "returns a random zone from the list of zones in the region" do
543
- zone = double("zone", name: "random_zone")
544
- expect(zones_in_region).to receive(:sample).and_return(zone)
545
- expect(driver.find_zone).to eq("random_zone")
546
- end
547
-
548
- it "raises an exception if no zones are found" do
549
- expect(zones_in_region).to receive(:sample).and_return(nil)
550
- expect(driver).to receive(:region).and_return("test_region")
551
- expect { driver.find_zone }.to raise_error(RuntimeError, "Unable to find a suitable zone in test_region")
552
- end
553
- end
554
-
555
- describe "#zones_in_region" do
556
- it "returns a correct list of available zones" do
557
- zone1 = double("zone1", status: "UP", region: "a/b/c/test_region")
558
- zone2 = double("zone2", status: "UP", region: "a/b/c/test_region")
559
- zone3 = double("zone3", status: "DOWN", region: "a/b/c/test_region")
560
- zone4 = double("zone4", status: "UP", region: "a/b/c/wrong_region")
561
- zone5 = double("zone5", status: "UP", region: "a/b/c/test_region")
562
- connection = double("connection")
563
- response = double("response", items: [zone1, zone2, zone3, zone4, zone5])
564
-
565
- allow(driver).to receive(:region).and_return("test_region")
566
- expect(driver).to receive(:connection).and_return(connection)
567
- expect(connection).to receive(:list_zones).and_return(response)
568
- expect(driver.zones_in_region).to eq([zone1, zone2, zone5])
569
- end
570
- end
571
-
572
- describe "#server_instance" do
573
- it "returns the instance from the API" do
574
- connection = double("connection")
575
- expect(driver).to receive(:connection).and_return(connection)
576
- expect(connection).to receive(:get_instance).with("test_project", "test_zone", "server_1").and_return("instance")
577
- expect(driver.server_instance("server_1")).to eq("instance")
578
- end
579
- end
580
-
581
- describe "#ip_address_for" do
582
- it "returns the private IP if use_private_ip is true" do
583
- expect(driver).to receive(:config).and_return(use_private_ip: true)
584
- expect(driver).to receive(:private_ip_for).with("server").and_return("1.2.3.4")
585
- expect(driver.ip_address_for("server")).to eq("1.2.3.4")
586
- end
587
-
588
- it "returns the public IP if use_private_ip is false" do
589
- expect(driver).to receive(:config).and_return(use_private_ip: false)
590
- expect(driver).to receive(:public_ip_for).with("server").and_return("4.3.2.1")
591
- expect(driver.ip_address_for("server")).to eq("4.3.2.1")
592
- end
593
- end
594
-
595
- describe "#private_ip_for" do
596
- it "returns the IP address if it exists" do
597
- network_interface = double("network_interface", network_ip: "1.2.3.4")
598
- server = double("server", network_interfaces: [network_interface])
599
-
600
- expect(driver.private_ip_for(server)).to eq("1.2.3.4")
601
- end
602
-
603
- it "raises an exception if the IP cannot be found" do
604
- server = double("server")
605
-
606
- expect(server).to receive(:network_interfaces).and_raise(NoMethodError)
607
- expect { driver.private_ip_for(server) }.to raise_error(RuntimeError, "Unable to determine private IP for instance")
608
- end
609
- end
610
-
611
- describe "#public_ip_for" do
612
- it "returns the IP address if it exists" do
613
- access_config = double("access_config", nat_ip: "4.3.2.1")
614
- network_interface = double("network_interface", access_configs: [access_config])
615
- server = double("server", network_interfaces: [network_interface])
616
-
617
- expect(driver.public_ip_for(server)).to eq("4.3.2.1")
618
- end
619
-
620
- it "raises an exception if the IP cannot be found" do
621
- network_interface = double("network_interface")
622
- server = double("server", network_interfaces: [network_interface])
623
-
624
- expect(network_interface).to receive(:access_configs).and_raise(NoMethodError)
625
- expect { driver.public_ip_for(server) }.to raise_error(RuntimeError, "Unable to determine public IP for instance")
626
- end
627
- end
628
-
629
- describe "#generate_server_name" do
630
- it "generates and returns a server name" do
631
- expect(instance).to receive(:name).and_return("ABC123")
632
- expect(SecureRandom).to receive(:hex).with(3).and_return("abcdef")
633
- expect(driver.generate_server_name).to eq("tk-abc123-abcdef")
634
- end
635
-
636
- it "uses a UUID-based server name if the instance name is too long" do
637
- expect(instance).to receive(:name).twice.and_return("123456789012345678901234567890123456789012345678901235467890")
638
- expect(driver).to receive(:warn)
639
- expect(SecureRandom).to receive(:hex).with(3).and_return("abcdef")
640
- expect(SecureRandom).to receive(:uuid).and_return("lmnop")
641
- expect(driver.generate_server_name).to eq("tk-lmnop")
642
- end
643
-
644
- it "returns a specific name for the server if given in the config" do
645
- config[:inst_name] = "the_instance_name"
646
-
647
- expect(driver.generate_server_name).to eq("the-instance-name")
648
- end
649
- end
650
-
651
- describe "#create_disks_config" do
652
- it "creates the new disk config from the old one" do
653
- config = {
654
- disk_size: 30,
655
- }
656
-
657
- allow(driver).to receive(:config).and_return(config)
658
- allow(driver).to receive(:valid_disk_type?).and_return(true)
659
- driver.create_disks_config
660
- expect(config[:disks][:disk1][:autodelete_disk]).to eq(true)
661
- expect(config[:disks][:disk1][:disk_type]).to eq("pd-standard")
662
- expect(config[:disks][:disk1][:disk_size]).to eq(30)
663
- end
664
-
665
- it "creates the default disk config, with an incomplete new configuration" do
666
- config = {
667
- disks: {
668
- disk1: {
669
- boot: true,
670
- },
671
- },
672
- }
673
-
674
- allow(driver).to receive(:config).and_return(config)
675
- allow(driver).to receive(:valid_disk_type?).and_return(true)
676
- driver.create_disks_config
677
- expect(config[:disks][:disk1][:autodelete_disk]).to eq(true)
678
- expect(config[:disks][:disk1][:disk_type]).to eq("pd-standard")
679
- expect(config[:disks][:disk1][:disk_size]).to eq(10)
680
- end
681
-
682
- it "creates the default disk config with no config" do
683
- config = {}
684
-
685
- allow(driver).to receive(:config).and_return(config)
686
- allow(driver).to receive(:valid_disk_type?).and_return(true)
687
- driver.create_disks_config
688
- expect(config[:disks][:disk1][:autodelete_disk]).to eq(true)
689
- expect(config[:disks][:disk1][:disk_type]).to eq("pd-standard")
690
- expect(config[:disks][:disk1][:disk_size]).to eq(10)
691
- end
692
-
693
- it "raises an error if disk_size is specified for a local ssd" do
694
- config = {
695
- disks: {
696
- disk0: {
697
- boot: true,
698
- },
699
- disk1: {
700
- disk_type: "local-ssd",
701
- disk_size: "15",
702
- },
703
- },
704
- }
705
- allow(driver).to receive(:config).and_return(config)
706
- allow(driver).to receive(:valid_disk_type?).and_return(true)
707
- expect { driver.create_disks_config }.to raise_error("disk1: Cannot use 'disk_size' with local SSD. They always have 375 GB (https://cloud.google.com/compute/docs/disks/#localssds).")
708
- end
709
-
710
- it "raises an error if the boot disk is specified as local ssd" do
711
- config = {
712
- disks: {
713
- disk0: {
714
- boot: true,
715
- disk_type: "local-ssd",
716
- },
717
- },
718
- }
719
- allow(driver).to receive(:config).and_return(config)
720
- allow(driver).to receive(:valid_disk_type?).and_return(true)
721
- expect { driver.create_disks_config }.to raise_error("Boot disk cannot be local SSD.")
722
- end
723
-
724
- it "raises an error if the disk_name is not valid" do
725
- config = {
726
- disks: {
727
- 'my-invalid&disk/name': {
728
- boot: true,
729
- },
730
- },
731
- }
732
- allow(driver).to receive(:config).and_return(config)
733
- allow(driver).to receive(:valid_disk_type?).and_return(true)
734
- expect { driver.create_disks_config }.to raise_error("Disk name invalid. Must match (?-mix:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?).")
735
- end
736
- end
737
-
738
- describe "#create_disks" do
739
- it "sets up a boot disk if boot is present and returns it" do
740
-
741
- config = {
742
- disks: {
743
- disk1: {
744
- boot: true,
745
- },
746
- },
747
- }
748
-
749
- connection = double("connection")
750
- image = double("image")
751
- allow(image).to receive(:name).and_return("test_image")
752
- allow(connection).to receive(:get_image_from_family).and_return(image)
753
- allow(connection).to receive(:get_image).and_return(image)
754
- allow(driver).to receive(:config).and_return(config)
755
- allow(driver).to receive(:connection).and_return(connection)
756
- disk = driver.create_disks("server_1")
757
- expect(disk.first.initialize_params.disk_name).to eq("server_1-disk1")
758
- expect(disk.first.initialize_params.source_image).to eq("projects/test_project/global/images/test_image")
759
- expect(disk.first.is_a?(Google::Apis::ComputeV1::AttachedDisk)).to eq(true)
760
- expect(disk.first.boot).to eq(true)
761
- end
762
-
763
- it "sets up an local ssd as scratch disk and returns it" do
764
- config = {
765
- disks: {
766
- disk1: {
767
- disk_type: "local-ssd",
768
- },
769
- },
770
- }
771
-
772
- connection = double("connection")
773
- allow(driver).to receive(:config).and_return(config)
774
- allow(driver).to receive(:connection).and_return(connection)
775
- disk = driver.create_disks("server_1")
776
- expect(disk.first.initialize_params.disk_name).to eq(nil)
777
- expect(disk.first.initialize_params.source_image).to eq(nil)
778
- expect(disk.first.initialize_params.disk_size_gb).to eq(nil)
779
- expect(disk.first.initialize_params.disk_type).to eq("zones/test_zone/diskTypes/local-ssd")
780
- expect(disk.first.type).to eq("SCRATCH")
781
- expect(disk.first.is_a?(Google::Apis::ComputeV1::AttachedDisk)).to eq(true)
782
- end
783
-
784
- it "sets up an attached disk if boot is not present and returns it" do
785
- config = {
786
- disks: {
787
- disk1: {
788
- autodelete_disk: false,
789
- },
790
- },
791
- }
792
- connection = double("connection")
793
- item = double("item")
794
- allow(driver).to receive(:connection).and_return(connection)
795
- allow(driver).to receive(:wait_for_operation).and_return(true)
796
- allow(item).to receive(:status).and_return("READY")
797
- allow(connection).to receive(:insert_disk).and_return("DONE")
798
- allow(connection).to receive(:get_disk).with(project, zone, "server_1-disk1").and_return(item)
799
- allow(driver).to receive(:config).and_return(config)
800
- disk = driver.create_disks("server_1")
801
- expect(disk.first.is_a?(Google::Apis::ComputeV1::AttachedDisk)).to eq(true)
802
- expect(disk.first.source).to eq("projects/#{project}/zones/#{zone}/disks/server_1-disk1")
803
- end
804
-
805
- it "sets up a boot disk and an attached disk if two disks are defined" do
806
- config = {
807
- disks: {
808
- disk1: {
809
- autodelete_disk: false,
810
- },
811
- disk2: {
812
- disk_size: 15,
813
- },
814
- },
815
- }
816
- connection = double("connection")
817
- item = double("item")
818
- allow(driver).to receive(:connection).and_return(connection)
819
- allow(driver).to receive(:wait_for_operation).and_return(true)
820
- allow(item).to receive(:status).and_return("READY")
821
- allow(connection).to receive(:insert_disk).and_return("DONE")
822
- allow(connection).to receive(:get_disk).with(project, zone, "server_1-disk2").and_return(item)
823
- allow(connection).to receive(:get_disk).with(project, zone, "server_1-disk1").and_return(item)
824
- allow(driver).to receive(:config).and_return(config)
825
- disks = driver.create_disks("server_1")
826
- expect(disks.first.is_a?(Google::Apis::ComputeV1::AttachedDisk)).to eq(true)
827
- expect(disks.first.source).to eq("projects/#{project}/zones/#{zone}/disks/server_1-disk1")
828
- expect(disks.first.auto_delete).to eq(false)
829
- expect(disks.last.is_a?(Google::Apis::ComputeV1::AttachedDisk)).to eq(true)
830
- expect(disks.last.auto_delete).to eq(nil)
831
- expect(disks.last.source).to eq("projects/#{project}/zones/#{zone}/disks/server_1-disk2")
832
- end
833
- end
834
-
835
- describe "#disk_type_url_for" do
836
- it "returns a disk URL" do
837
- expect(driver.disk_type_url_for("my_type")).to eq("zones/test_zone/diskTypes/my_type")
838
- end
839
- end
840
-
841
- describe "#image_name" do
842
- before do
843
- allow(driver).to receive(:config).and_return(config)
844
- end
845
-
846
- context "when the user supplies an image name" do
847
- let(:config) { { image_project: "my_image_project", image_name: "my_image" } }
848
-
849
- it "returns the image name supplied" do
850
- expect(driver.image_name).to eq("my_image")
851
- end
852
- end
853
-
854
- context "when the user supplies an image family" do
855
- let(:config) { { image_project: "my_image_project", image_family: "my_image_family" } }
856
-
857
- it "returns the image found in the family" do
858
- expect(driver).to receive(:image_name_for_family).with("my_image_family").and_return("my_image")
859
- expect(driver.image_name).to eq("my_image")
860
- end
861
- end
862
- end
863
-
864
- describe "#image_url" do
865
- it "returns nil if the image does not exist" do
866
- expect(driver).to receive(:image_exist?).and_return(false)
867
- expect(driver.image_url).to eq(nil)
868
- end
869
-
870
- it "returns a properly formatted image URL if the image exists" do
871
- expect(driver).to receive(:image_exist?).and_return(true)
872
- expect(driver.image_url).to eq("projects/test_project/global/images/test_image")
873
- end
874
- end
875
-
876
- describe "#machine_type_url" do
877
- it "returns a machine type URL" do
878
- expect(driver).to receive(:config).and_return(machine_type: "machine_type")
879
- expect(driver.machine_type_url).to eq("zones/test_zone/machineTypes/machine_type")
880
- end
881
- end
882
-
883
- describe "#instance_metadata" do
884
- let(:item1 ) { double("item1") }
885
- let(:item2 ) { double("item2") }
886
- let(:item3 ) { double("item3") }
887
- let(:metadata) { double("metadata") }
888
-
889
- it "returns a properly-configured metadata object" do
890
-
891
- expect(instance).to receive(:name).and_return("instance_name")
892
- expect(driver).to receive(:env_user).and_return("env_user")
893
- expect(Google::Apis::ComputeV1::Metadata).to receive(:new).and_return(metadata)
894
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item1)
895
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item2)
896
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item3)
897
- expect(item1).to receive(:key=).with("created-by")
898
- expect(item1).to receive(:value=).with("test-kitchen")
899
- expect(item2).to receive(:key=).with("test-kitchen-instance")
900
- expect(item2).to receive(:value=).with("instance_name")
901
- expect(item3).to receive(:key=).with("test-kitchen-user")
902
- expect(item3).to receive(:value=).with("env_user")
903
- expect(metadata).to receive(:items=).with([item1, item2, item3])
904
-
905
- expect(driver.instance_metadata).to eq(metadata)
906
- end
907
-
908
- it "accepts custom metadata" do
909
- foo = double("foo")
910
- config[:metadata] = { "foo" => "bar" }
911
-
912
- expect(instance).to receive(:name).and_return("instance_name")
913
- expect(driver).to receive(:env_user).and_return("env_user")
914
-
915
- expect(Google::Apis::ComputeV1::Metadata).to receive(:new).and_return(metadata)
916
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(foo)
917
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item1)
918
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item2)
919
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item3)
920
- expect(item1).to receive(:key=).with("created-by")
921
- expect(item1).to receive(:value=).with("test-kitchen")
922
- expect(item2).to receive(:key=).with("test-kitchen-instance")
923
- expect(item2).to receive(:value=).with("instance_name")
924
- expect(item3).to receive(:key=).with("test-kitchen-user")
925
- expect(item3).to receive(:value=).with("env_user")
926
- expect(foo).to receive(:key=).with("foo")
927
- expect(foo).to receive(:value=).with("bar")
928
-
929
- expect(metadata).to receive(:items=).with([foo, item1, item2, item3])
930
-
931
- expect(driver.instance_metadata).to eq(metadata)
932
- end
933
- end
934
-
935
- describe "#env_user" do
936
- it "returns the current user from the environment" do
937
- expect(ENV).to receive(:[]).with("USER").and_return("test_user")
938
- expect(driver.env_user).to eq("test_user")
939
- end
940
-
941
- it "returns 'unknown' if there is no USER present" do
942
- expect(ENV).to receive(:[]).with("USER").and_return(nil)
943
- expect(driver.env_user).to eq("unknown")
944
- end
945
- end
946
-
947
- describe "#instance_network_interfaces" do
948
- let(:interface) { double("interface") }
949
-
950
- before do
951
- allow(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
952
- allow(driver).to receive(:network_url)
953
- allow(driver).to receive(:subnet_url)
954
- allow(driver).to receive(:interface_access_configs)
955
- allow(interface).to receive(:network=)
956
- allow(interface).to receive(:subnetwork=)
957
- allow(interface).to receive(:access_configs=)
958
- end
959
-
960
- it "creates a network interface object and returns it" do
961
- expect(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
962
- expect(driver.instance_network_interfaces).to eq([interface])
963
- end
964
-
965
- it "sets the network" do
966
- expect(driver).to receive(:network_url).and_return("network_url")
967
- expect(interface).to receive(:network=).with("network_url")
968
- driver.instance_network_interfaces
969
- end
970
-
971
- it "sets the access configs" do
972
- expect(driver).to receive(:interface_access_configs).and_return("access_configs")
973
- expect(interface).to receive(:access_configs=).with("access_configs")
974
- driver.instance_network_interfaces
975
- end
976
-
977
- it "does not set a subnetwork by default" do
978
- allow(driver).to receive(:subnet_url).and_return(nil)
979
- expect(interface).not_to receive(:subnetwork=)
980
- driver.instance_network_interfaces
981
- end
982
-
983
- it "sets a subnetwork if one was specified" do
984
- allow(driver).to receive(:subnet_url).and_return("subnet_url")
985
- expect(interface).to receive(:subnetwork=).with("subnet_url")
986
- driver.instance_network_interfaces
987
- end
988
- end
989
-
990
- describe "#network_url" do
991
- context "when the user does not provide network_project" do
992
- it "returns a network URL" do
993
- allow(driver).to receive(:config).and_return(network: "test_network")
994
- expect(driver.network_url).to eq("projects/test_project/global/networks/test_network")
995
- end
996
- end
997
-
998
- context "when the user provides network_project" do
999
- it "returns a network URL" do
1000
- allow(driver).to receive(:config).and_return(network: "test_network", network_project: "test_xpn_project")
1001
- expect(driver.network_url).to eq("projects/test_xpn_project/global/networks/test_network")
1002
- end
1003
- end
1004
- end
1005
-
1006
- describe "#subnet_url_for" do
1007
- it "returns nil if no subnet is specified" do
1008
- expect(driver).to receive(:config).and_return({})
1009
- expect(driver.subnet_url).to eq(nil)
1010
- end
1011
-
1012
- context "when the user does not provide subnet_project" do
1013
- it "returns a properly-formatted subnet URL" do
1014
- allow(driver).to receive(:config).and_return(subnet: "test_subnet")
1015
- expect(driver).to receive(:region).and_return("test_region")
1016
- expect(driver.subnet_url).to eq("projects/test_project/regions/test_region/subnetworks/test_subnet")
1017
- end
1018
- end
1019
-
1020
- context "when the user provides subnet_project" do
1021
- it "returns a properly-formatted subnet URL" do
1022
- allow(driver).to receive(:config).and_return(subnet_project: "test_xpn_project", subnet: "test_subnet")
1023
- expect(driver).to receive(:region).and_return("test_region")
1024
- expect(driver.subnet_url).to eq("projects/test_xpn_project/regions/test_region/subnetworks/test_subnet")
1025
- end
1026
- end
1027
- end
1028
-
1029
- describe "#interface_access_configs" do
1030
- it "returns a properly-configured access config object" do
1031
- access_config = double("access_config")
1032
-
1033
- expect(driver).to receive(:config).and_return({})
1034
- expect(Google::Apis::ComputeV1::AccessConfig).to receive(:new).and_return(access_config)
1035
- expect(access_config).to receive(:name=).with("External NAT")
1036
- expect(access_config).to receive(:type=).with("ONE_TO_ONE_NAT")
1037
-
1038
- expect(driver.interface_access_configs).to eq([access_config])
1039
- end
1040
-
1041
- it "returns an empty array if use_private_ip is true" do
1042
- expect(driver).to receive(:config).and_return(use_private_ip: true)
1043
- expect(driver.interface_access_configs).to eq([])
1044
- end
1045
- end
1046
-
1047
- describe "#instance_scheduling" do
1048
- it "returns a properly-configured scheduling object" do
1049
- scheduling = double("scheduling")
1050
-
1051
- expect(driver).to receive(:auto_restart?).and_return("restart")
1052
- expect(driver).to receive(:preemptible?).and_return("preempt")
1053
- expect(driver).to receive(:migrate_setting).and_return("host_maintenance")
1054
- expect(Google::Apis::ComputeV1::Scheduling).to receive(:new).and_return(scheduling)
1055
- expect(scheduling).to receive(:automatic_restart=).with("restart")
1056
- expect(scheduling).to receive(:preemptible=).with("preempt")
1057
- expect(scheduling).to receive(:on_host_maintenance=).with("host_maintenance")
1058
- expect(driver.instance_scheduling).to eq(scheduling)
1059
- end
1060
- end
1061
-
1062
- describe "#preemptible?" do
1063
- it "returns the preemptible setting from the config" do
1064
- expect(driver).to receive(:config).and_return(preemptible: "test_preempt")
1065
- expect(driver.preemptible?).to eq("test_preempt")
1066
- end
1067
- end
1068
-
1069
- describe "#auto_migrate?" do
1070
- it "returns false if the instance is preemptible" do
1071
- expect(driver).to receive(:preemptible?).and_return(true)
1072
- expect(driver.auto_migrate?).to eq(false)
1073
- end
1074
-
1075
- it "returns the setting from the config if preemptible is false" do
1076
- expect(driver).to receive(:config).and_return(auto_migrate: "test_migrate")
1077
- expect(driver).to receive(:preemptible?).and_return(false)
1078
- expect(driver.auto_migrate?).to eq("test_migrate")
1079
- end
1080
- end
1081
-
1082
- describe "#auto_restart?" do
1083
- it "returns false if the instance is preemptible" do
1084
- expect(driver).to receive(:preemptible?).and_return(true)
1085
- expect(driver.auto_restart?).to eq(false)
1086
- end
1087
-
1088
- it "returns the setting from the config if preemptible is false" do
1089
- expect(driver).to receive(:config).and_return(auto_restart: "test_restart")
1090
- expect(driver).to receive(:preemptible?).and_return(false)
1091
- expect(driver.auto_restart?).to eq("test_restart")
1092
- end
1093
- end
1094
-
1095
- describe "#migrate_setting" do
1096
- it "returns MIGRATE if auto_migrate is true" do
1097
- expect(driver).to receive(:auto_migrate?).and_return(true)
1098
- expect(driver.migrate_setting).to eq("MIGRATE")
1099
- end
1100
-
1101
- it "returns TERMINATE if auto_migrate is false" do
1102
- expect(driver).to receive(:auto_migrate?).and_return(false)
1103
- expect(driver.migrate_setting).to eq("TERMINATE")
1104
- end
1105
- end
1106
-
1107
- describe "#instance_service_accounts" do
1108
- it "returns nil if service_account_scopes is nil" do
1109
- allow(driver).to receive(:config).and_return({})
1110
- expect(driver.instance_service_accounts).to eq(nil)
1111
- end
1112
-
1113
- it "returns nil if service_account_scopes is empty" do
1114
- allow(driver).to receive(:config).and_return(service_account_scopes: [])
1115
- expect(driver.instance_service_accounts).to eq(nil)
1116
- end
1117
-
1118
- it "returns an array containing a properly-formatted service account" do
1119
- service_account = double("service_account")
1120
-
1121
- allow(driver).to receive(:config).and_return(service_account_name: "account_name", service_account_scopes: %w{scope1 scope2})
1122
- expect(Google::Apis::ComputeV1::ServiceAccount).to receive(:new).and_return(service_account)
1123
- expect(service_account).to receive(:email=).with("account_name")
1124
- expect(driver).to receive(:service_account_scope_url).with("scope1").and_return("https://www.googleapis.com/auth/scope1")
1125
- expect(driver).to receive(:service_account_scope_url).with("scope2").and_return("https://www.googleapis.com/auth/scope2")
1126
- expect(service_account).to receive(:scopes=).with([
1127
- "https://www.googleapis.com/auth/scope1",
1128
- "https://www.googleapis.com/auth/scope2",
1129
- ])
1130
-
1131
- expect(driver.instance_service_accounts).to eq([service_account])
1132
- end
1133
- end
1134
-
1135
- describe "#service_account_scope_url" do
1136
- it "returns the passed-in scope if it already looks like a scope URL" do
1137
- scope = "https://www.googleapis.com/auth/fake_scope"
1138
- expect(driver.service_account_scope_url(scope)).to eq(scope)
1139
- end
1140
-
1141
- it "returns a properly-formatted scope URL if a short-name or alias is provided" do
1142
- expect(driver).to receive(:translate_scope_alias).with("scope_alias").and_return("real_scope")
1143
- expect(driver.service_account_scope_url("scope_alias")).to eq("https://www.googleapis.com/auth/real_scope")
1144
- end
1145
- end
1146
-
1147
- describe "#translate_scope_alias" do
1148
- it "returns a scope for a given alias" do
1149
- expect(driver.translate_scope_alias("storage-rw")).to eq("devstorage.read_write")
1150
- end
1151
-
1152
- it "returns the passed-in scope alias if nothing matches in the alias map" do
1153
- expect(driver.translate_scope_alias("fake_scope")).to eq("fake_scope")
1154
- end
1155
- end
1156
-
1157
- describe "#instance_tags" do
1158
- it "returns a properly-formatted tags object" do
1159
- tags_obj = double("tags_obj")
1160
-
1161
- expect(driver).to receive(:config).and_return(tags: "test_tags")
1162
- expect(Google::Apis::ComputeV1::Tags).to receive(:new).and_return(tags_obj)
1163
- expect(tags_obj).to receive(:items=).with("test_tags")
1164
-
1165
- expect(driver.instance_tags).to eq(tags_obj)
1166
- end
1167
- end
1168
-
1169
- describe "#wait_time" do
1170
- it "returns the configured wait time" do
1171
- expect(driver).to receive(:config).and_return(wait_time: 123)
1172
- expect(driver.wait_time).to eq(123)
1173
- end
1174
- end
1175
-
1176
- describe "#refresh_rate" do
1177
- it "returns the configured refresh rate" do
1178
- expect(driver).to receive(:config).and_return(refresh_rate: 321)
1179
- expect(driver.refresh_rate).to eq(321)
1180
- end
1181
- end
1182
-
1183
- describe "#wait_for_status" do
1184
- let(:item) { double("item") }
1185
-
1186
- before do
1187
- allow(driver).to receive(:wait_time).and_return(600)
1188
- allow(driver).to receive(:refresh_rate).and_return(2)
1189
-
1190
- # don"t actually sleep
1191
- allow(driver).to receive(:sleep)
1192
- end
1193
-
1194
- context "when the items completes normally, 3 loops" do
1195
- it "only refreshes the item 3 times" do
1196
- allow(item).to receive(:status).exactly(3).times.and_return("PENDING", "RUNNING", "DONE")
1197
-
1198
- driver.wait_for_status("DONE") { item }
1199
- end
1200
- end
1201
-
1202
- context "when the item is completed on the first loop" do
1203
- it "only refreshes the item 1 time" do
1204
- allow(item).to receive(:status).once.and_return("DONE")
1205
-
1206
- driver.wait_for_status("DONE") { item }
1207
- end
1208
- end
1209
-
1210
- context "when the timeout is exceeded" do
1211
- it "prints a warning and exits" do
1212
- allow(Timeout).to receive(:timeout).and_raise(Timeout::Error)
1213
- expect(driver).to receive(:error)
1214
- .with("Request did not complete in 600 seconds. Check the Google Cloud Console for more info.")
1215
- expect { driver.wait_for_status("DONE") { item } }.to raise_error(RuntimeError)
1216
- end
1217
- end
1218
-
1219
- context "when a non-timeout exception is raised" do
1220
- it "raises the original exception" do
1221
- allow(item).to receive(:status).and_raise(NoMethodError)
1222
- expect { driver.wait_for_status("DONE") { item } }.to raise_error(NoMethodError)
1223
- end
1224
- end
1225
- end
1226
-
1227
- describe "#wait_for_operation" do
1228
- let(:operation) { double("operation", name: "operation-123") }
1229
-
1230
- it "raises a properly-formatted exception when errors exist" do
1231
- error1 = double("error1", code: "ERROR1", message: "error 1")
1232
- error2 = double("error2", code: "ERROR2", message: "error 2")
1233
- expect(driver).to receive(:wait_for_status).with("DONE")
1234
- expect(driver).to receive(:operation_errors).with("operation-123").and_return([error1, error2])
1235
- expect(driver).to receive(:error).with("ERROR1: error 1")
1236
- expect(driver).to receive(:error).with("ERROR2: error 2")
1237
-
1238
- expect { driver.wait_for_operation(operation) }.to raise_error(RuntimeError, "Operation operation-123 failed.")
1239
- end
1240
-
1241
- it "does not raise an exception if no errors are encountered" do
1242
- expect(driver).to receive(:wait_for_status).with("DONE")
1243
- expect(driver).to receive(:operation_errors).with("operation-123").and_return([])
1244
- expect(driver).not_to receive(:error)
1245
-
1246
- expect { driver.wait_for_operation(operation) }.not_to raise_error
1247
- end
1248
- end
1249
-
1250
- describe "#zone_operation" do
1251
- it "fetches the operation from the API and returns it" do
1252
- connection = double("connection")
1253
- expect(driver).to receive(:connection).and_return(connection)
1254
- expect(connection).to receive(:get_zone_operation).with(project, zone, "operation-123").and_return("operation")
1255
- expect(driver.zone_operation("operation-123")).to eq("operation")
1256
- end
1257
- end
1258
-
1259
- describe "#operation_errors" do
1260
- let(:operation) { double("operation") }
1261
- let(:error_obj) { double("error_obj") }
1262
-
1263
- before do
1264
- expect(driver).to receive(:zone_operation).with("operation-123").and_return(operation)
1265
- end
1266
-
1267
- it "returns an empty array if there are no errors" do
1268
- expect(operation).to receive(:error).and_return(nil)
1269
- expect(driver.operation_errors("operation-123")).to eq([])
1270
- end
1271
-
1272
- it "returns the errors from the operation if they exist" do
1273
- expect(operation).to receive(:error).twice.and_return(error_obj)
1274
- expect(error_obj).to receive(:errors).and_return("some errors")
1275
- expect(driver.operation_errors("operation-123")).to eq("some errors")
1276
- end
1277
- end
1278
- end