knife-google 3.3.6 → 3.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2016 Chef Software, Inc.
5
- # License:: Apache License, Version 2.0
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- #
19
-
20
- require "chef/knife/cloud/google_service"
21
- require "chef/knife/cloud/google_service_helpers"
22
-
23
- class Tester
24
- include Chef::Knife::Cloud::GoogleServiceHelpers
25
- end
26
-
27
- describe Chef::Knife::Cloud::GoogleServiceHelpers do
28
- let(:tester) { Tester.new }
29
-
30
- describe "#create_service_instance" do
31
- it "creates a GoogleService instance" do
32
- expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("test_project")
33
- expect(tester).to receive(:locate_config_value).with(:gce_zone).and_return("test_zone")
34
- expect(tester).to receive(:locate_config_value).with(:request_timeout).and_return(123)
35
- expect(tester).to receive(:locate_config_value).with(:request_refresh_rate).and_return(321)
36
- expect(tester).to receive(:locate_config_value).with(:max_pages).and_return(456)
37
- expect(tester).to receive(:locate_config_value).with(:max_page_size).and_return(654)
38
-
39
- expect(Chef::Knife::Cloud::GoogleService).to receive(:new).with(
40
- project: "test_project",
41
- zone: "test_zone",
42
- wait_time: 123,
43
- refresh_rate: 321,
44
- max_pages: 456,
45
- max_page_size: 654
46
- ).and_return("service_object")
47
-
48
- expect(tester.create_service_instance).to eq("service_object")
49
- end
50
- end
51
-
52
- describe "#check_for_missing_config_values" do
53
- it "does not raise an exception if all parameters are present" do
54
- expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("project")
55
- expect(tester).to receive(:locate_config_value).with(:key1).and_return("value1")
56
- expect(tester).to receive(:locate_config_value).with(:key2).and_return("value2")
57
-
58
- expect { tester.check_for_missing_config_values!(:key1, :key2) }.not_to raise_error
59
- end
60
-
61
- it "raises an exception if a parameter is missing" do
62
- ui = double("ui")
63
- expect(tester).to receive(:ui).and_return(ui)
64
- expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("project")
65
- expect(tester).to receive(:locate_config_value).with(:key1).and_return("value1")
66
- expect(tester).to receive(:locate_config_value).with(:key2).and_return(nil)
67
- expect(ui).to receive(:error).with("The following required parameters are missing: key2")
68
- expect { tester.check_for_missing_config_values!(:key1, :key2) }.to raise_error(RuntimeError)
69
- end
70
- end
71
-
72
- describe "#private_ip_for" do
73
- it "returns the IP address if it exists" do
74
- network_interface = double("network_interface", network_ip: "1.2.3.4")
75
- server = double("server", network_interfaces: [network_interface])
76
-
77
- expect(tester.private_ip_for(server)).to eq("1.2.3.4")
78
- end
79
-
80
- it "returns 'unknown' if the IP cannot be found" do
81
- server = double("server")
82
-
83
- expect(server).to receive(:network_interfaces).and_raise(NoMethodError)
84
- expect(tester.private_ip_for(server)).to eq("unknown")
85
- end
86
- end
87
-
88
- describe "#public_ip_for" do
89
- it "returns the IP address if it exists" do
90
- access_config = double("access_config", nat_ip: "4.3.2.1")
91
- network_interface = double("network_interface", access_configs: [access_config])
92
- server = double("server", network_interfaces: [network_interface])
93
-
94
- expect(tester.public_ip_for(server)).to eq("4.3.2.1")
95
- end
96
-
97
- it "returns 'unknown' if the IP cannot be found" do
98
- network_interface = double("network_interface")
99
- server = double("server", network_interfaces: [network_interface])
100
-
101
- expect(network_interface).to receive(:access_configs).and_raise(NoMethodError)
102
- expect(tester.public_ip_for(server)).to eq("unknown")
103
- end
104
- end
105
-
106
- describe "#valid_disk_size?" do
107
- it "returns true if the disk is between 10 and 10,000" do
108
- expect(tester.valid_disk_size?(50)).to eq(true)
109
- end
110
-
111
- it "returns false if the disk is less than 10" do
112
- expect(tester.valid_disk_size?(5)).to eq(false)
113
- end
114
-
115
- it "returns false if the disk is greater than 10,000" do
116
- expect(tester.valid_disk_size?(20_000)).to eq(false)
117
- end
118
- end
119
- end
@@ -1,1021 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2016 Chef Software, Inc.
5
- # License:: Apache License, Version 2.0
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- #
19
-
20
- require "chef/knife"
21
- require "chef/knife/cloud/exceptions"
22
- require "chef/knife/cloud/google_service"
23
- require "support/shared_examples_for_service"
24
-
25
- shared_examples_for "a paginated list fetcher" do |fetch_method, items_method, *args|
26
- it "retrieves paginated results from the API" do
27
- expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args)
28
- subject
29
- end
30
-
31
- it "returns the results if they exist" do
32
- expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args).and_return("results")
33
- expect(subject).to eq("results")
34
- end
35
-
36
- it "returns an empty array if there are no results" do
37
- expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args).and_return(nil)
38
- expect(subject).to eq([])
39
- end
40
- end
41
-
42
- describe Chef::Knife::Cloud::GoogleService do
43
- let(:project) { "test_project" }
44
- let(:zone) { "test_zone" }
45
- let(:wait_time) { 123 }
46
- let(:refresh_rate) { 321 }
47
- let(:max_pages) { 456 }
48
- let(:max_pages_size) { 654 }
49
- let(:connection) { double("connection") }
50
-
51
- let(:service) do
52
- Chef::Knife::Cloud::GoogleService.new(
53
- project: project,
54
- zone: zone,
55
- wait_time: wait_time,
56
- refresh_rate: refresh_rate,
57
- max_pages: max_pages,
58
- max_pages_size: max_pages_size
59
- )
60
- end
61
-
62
- before do
63
- service.ui = Chef::Knife::UI.new($stdout, $stderr, $stdin, {})
64
- allow(service.ui).to receive(:msg)
65
- allow(service.ui).to receive(:error)
66
- allow(service).to receive(:connection).and_return(connection)
67
- end
68
-
69
- describe "#connection" do
70
- it "returns a properly configured ComputeService" do
71
- compute_service = double("compute_service")
72
- client_options = double("client_options")
73
-
74
- allow(service).to receive(:connection).and_call_original
75
-
76
- expect(Google::Apis::ClientOptions).to receive(:new).and_return(client_options)
77
- expect(client_options).to receive(:application_name=).with("knife-google")
78
- expect(client_options).to receive(:application_version=).with(Knife::Google::VERSION)
79
-
80
- expect(Google::Apis::ComputeV1::ComputeService).to receive(:new).and_return(compute_service)
81
- expect(service).to receive(:authorization).and_return("authorization_object")
82
- expect(compute_service).to receive(:authorization=).with("authorization_object")
83
- expect(compute_service).to receive(:client_options=).with(client_options)
84
-
85
- expect(service.connection).to eq(compute_service)
86
- end
87
- end
88
-
89
- describe "#authorization" do
90
- it "returns a Google::Auth authorization object" do
91
- auth_object = double("auth_object")
92
- expect(Google::Auth).to receive(:get_application_default).and_return(auth_object)
93
- expect(service.authorization).to eq(auth_object)
94
- end
95
- end
96
-
97
- describe "#create_server" do
98
- it "creates and returns the created instance" do
99
- create_instance_obj = double("instance_obj")
100
- create_options = { name: "test_instance" }
101
- instance = double("instance")
102
-
103
- expect(service).to receive(:validate_server_create_options!).with(create_options)
104
- expect(service).to receive(:instance_object_for).with(create_options).and_return(create_instance_obj)
105
- expect(connection).to receive(:insert_instance).with(project, zone, create_instance_obj).and_return("operation_id")
106
- expect(service).to receive(:wait_for_operation).with("operation_id")
107
- expect(service).to receive(:wait_for_status).with("RUNNING")
108
- expect(service).to receive(:get_server).with("test_instance").and_return(instance)
109
-
110
- expect(service.create_server(create_options)).to eq(instance)
111
- end
112
- end
113
-
114
- describe "#delete_server" do
115
- context "when the instance does not exist" do
116
- before do
117
- allow(service.ui).to receive(:warn)
118
- expect(service).to receive(:get_server).and_raise(Google::Apis::ClientError.new("not found"))
119
- end
120
-
121
- it "prints a warning to the user" do
122
- expect(service.ui).to receive(:warn).with("Unable to locate instance test_instance in project #{project}, zone #{zone}")
123
-
124
- service.delete_server("test_instance")
125
- end
126
-
127
- it "does not attempt to delete the instance" do
128
- expect(connection).not_to receive(:delete_instance)
129
-
130
- service.delete_server("test_instance")
131
- end
132
- end
133
-
134
- context "when the instance exists" do
135
- it "confirms the deletion and deletes the instance" do
136
- instance = double("instance")
137
- expect(service).to receive(:get_server).with("test_instance").and_return(instance)
138
- expect(service).to receive(:server_summary).with(instance)
139
- expect(service.ui).to receive(:confirm)
140
- expect(connection).to receive(:delete_instance).with(project, zone, "test_instance").and_return("operation-123")
141
- expect(service).to receive(:wait_for_operation).with("operation-123")
142
-
143
- service.delete_server("test_instance")
144
- end
145
- end
146
- end
147
-
148
- describe "#get_server" do
149
- it "returns an instance" do
150
- expect(connection).to receive(:get_instance).with(project, zone, "test_instance").and_return("instance")
151
- expect(service.get_server("test_instance")).to eq("instance")
152
- end
153
- end
154
-
155
- describe "#list_zones" do
156
- subject { service.list_zones }
157
- it_behaves_like "a paginated list fetcher", :list_zones, :items, "test_project"
158
- end
159
-
160
- describe "#list_disks" do
161
- subject { service.list_disks }
162
- it_behaves_like "a paginated list fetcher", :list_disks, :items, "test_project", "test_zone"
163
- end
164
-
165
- describe "#list_regions" do
166
- subject { service.list_regions }
167
- it_behaves_like "a paginated list fetcher", :list_regions, :items, "test_project"
168
- end
169
-
170
- describe "#list_project_quotas" do
171
- let(:response) { double("response") }
172
-
173
- before do
174
- expect(service).to receive(:project).and_return(project)
175
- expect(connection).to receive(:get_project).with(project).and_return(response)
176
- end
177
-
178
- it "returns the results if they exist" do
179
- expect(response).to receive(:quotas).and_return("results")
180
- expect(service.list_project_quotas).to eq("results")
181
- end
182
-
183
- it "returns an empty array if there are no results" do
184
- expect(response).to receive(:quotas).and_return(nil)
185
- expect(service.list_project_quotas).to eq([])
186
- end
187
- end
188
-
189
- describe "#validate_server_create_options!" do
190
- let(:options) do
191
- {
192
- machine_type: "test_type",
193
- network: "test_network",
194
- subnet: "test_subnet",
195
- public_ip: "public_ip",
196
- image: "test_image",
197
- image_project: "test_image_project",
198
- }
199
- end
200
-
201
- before do
202
- allow(service).to receive(:valid_machine_type?).and_return(true)
203
- allow(service).to receive(:valid_network?).and_return(true)
204
- allow(service).to receive(:valid_subnet?).and_return(true)
205
- allow(service).to receive(:valid_public_ip_setting?).and_return(true)
206
- allow(service).to receive(:image_search_for).and_return(true)
207
- end
208
-
209
- it "does not raise an exception when all parameters are supplied and accurate" do
210
- expect { service.validate_server_create_options!(options) }.not_to raise_error
211
- end
212
-
213
- it "raises an exception if the machine type is not valid" do
214
- expect(service).to receive(:valid_machine_type?).with("test_type").and_return(false)
215
- expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
216
- end
217
-
218
- it "raises an exception if the network is not valid" do
219
- expect(service).to receive(:valid_network?).with("test_network").and_return(false)
220
- expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
221
- end
222
-
223
- it "raises an exception if the network is not valid" do
224
- expect(service).to receive(:valid_subnet?).with("test_subnet").and_return(false)
225
- expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
226
- end
227
-
228
- it "raises an exception if the public ip setting is not valid" do
229
- expect(service).to receive(:valid_public_ip_setting?).with("public_ip").and_return(false)
230
- expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
231
- end
232
-
233
- it "raises an exception if the image parameters are not valid" do
234
- expect(service).to receive(:image_search_for).with("test_image", "test_image_project").and_return(nil)
235
- expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
236
- end
237
- end
238
-
239
- describe "#check_api_call" do
240
- it "returns false if the block raises a ClientError" do
241
- expect(service.check_api_call { raise Google::Apis::ClientError.new("whoops") }).to eq(false)
242
- end
243
-
244
- it "raises an exception if the block raises something other than a ClientError" do
245
- expect { service.check_api_call { raise "whoops" } }.to raise_error(RuntimeError)
246
- end
247
-
248
- it "returns true if the block does not raise an exception" do
249
- expect(service.check_api_call { true }).to eq(true)
250
- end
251
- end
252
-
253
- describe "#valid_machine_type?" do
254
- it "returns false if no matchine type was specified" do
255
- expect(service.valid_machine_type?(nil)).to eq(false)
256
- end
257
-
258
- it "checks the machine type using check_api_call" do
259
- expect(connection).to receive(:get_machine_type).with(project, zone, "test_type")
260
- expect(service).to receive(:check_api_call).and_call_original
261
-
262
- service.valid_machine_type?("test_type")
263
- end
264
- end
265
-
266
- describe "#valid_network?" do
267
- it "returns false if no network was specified" do
268
- expect(service.valid_network?(nil)).to eq(false)
269
- end
270
-
271
- it "checks the network using check_api_call" do
272
- expect(connection).to receive(:get_network).with(project, "test_network")
273
- expect(service).to receive(:check_api_call).and_call_original
274
-
275
- service.valid_network?("test_network")
276
- end
277
- end
278
-
279
- describe "#valid_subnet?" do
280
- it "returns false if no subnet was specified" do
281
- expect(service.valid_subnet?(nil)).to eq(false)
282
- end
283
-
284
- it "checks the network using check_api_call" do
285
- expect(service).to receive(:region).and_return("test_region")
286
- expect(connection).to receive(:get_subnetwork).with(project, "test_region", "test_subnet")
287
- expect(service).to receive(:check_api_call).and_call_original
288
-
289
- service.valid_subnet?("test_subnet")
290
- end
291
- end
292
-
293
- describe "#image_exist?" do
294
- it "checks the image using check_api_call" do
295
- expect(connection).to receive(:get_image).with("image_project", "image_name")
296
- expect(service).to receive(:check_api_call).and_call_original
297
-
298
- service.image_exist?("image_project", "image_name")
299
- end
300
- end
301
-
302
- describe "#valid_public_ip_setting?" do
303
- it "returns true if the public_ip is nil" do
304
- expect(service.valid_public_ip_setting?(nil)).to eq(true)
305
- end
306
-
307
- it "returns true if the public_ip is ephemeral" do
308
- expect(service.valid_public_ip_setting?("EPHEMERAL")).to eq(true)
309
- end
310
-
311
- it "returns true if the public_ip is none" do
312
- expect(service.valid_public_ip_setting?("NONE")).to eq(true)
313
- end
314
-
315
- it "returns true if the public_ip is a valid IP address" do
316
- expect(service).to receive(:valid_ip_address?).with("1.2.3.4").and_return(true)
317
- expect(service.valid_public_ip_setting?("1.2.3.4")).to eq(true)
318
- end
319
-
320
- it "returns false if it is not nil, ephemeral, none, or a valid IP address" do
321
- expect(service).to receive(:valid_ip_address?).with("not_an_ip").and_return(false)
322
- expect(service.valid_public_ip_setting?("not_an_ip")).to eq(false)
323
- end
324
- end
325
-
326
- describe "#valid_ip_address" do
327
- it "returns false if IPAddr is unable to parse the address" do
328
- expect(IPAddr).to receive(:new).with("not_an_ip").and_raise(IPAddr::InvalidAddressError)
329
- expect(service.valid_ip_address?("not_an_ip")).to eq(false)
330
- end
331
-
332
- it "returns true if IPAddr can parse the address" do
333
- expect(IPAddr).to receive(:new).with("1.2.3.4")
334
- expect(service.valid_ip_address?("1.2.3.4")).to eq(true)
335
- end
336
- end
337
-
338
- describe "#region" do
339
- it "returns the region for a given zone" do
340
- zone_obj = double("zone_obj", region: "/path/to/test_region")
341
- expect(connection).to receive(:get_zone).with(project, zone).and_return(zone_obj)
342
- expect(service.region).to eq("test_region")
343
- end
344
- end
345
-
346
- describe "#instance_object_for" do
347
- let(:instance_object) { double("instance_object") }
348
- let(:options) do
349
- {
350
- name: "test_instance",
351
- can_ip_forward: "ip_forwarding",
352
- machine_type: "test_machine_type",
353
- metadata: "test_metadata",
354
- tags: "test_tags",
355
- }
356
- end
357
-
358
- before do
359
- expect(service).to receive(:instance_disks_for).with(options).and_return("test_disks")
360
- expect(service).to receive(:machine_type_url_for).with("test_machine_type").and_return("test_machine_type_url")
361
- expect(service).to receive(:instance_metadata_for).with("test_metadata").and_return("test_metadata_obj")
362
- expect(service).to receive(:instance_network_interfaces_for).with(options).and_return("test_network_interfaces")
363
- expect(service).to receive(:instance_scheduling_for).with(options).and_return("test_scheduling")
364
- allow(service).to receive(:instance_service_accounts_for).with(options).at_least(:once).and_return("test_service_accounts")
365
- expect(service).to receive(:instance_tags_for).with("test_tags").and_return("test_tags_obj")
366
- end
367
-
368
- it "builds and returns a valid object for creating an instance" do
369
- expect(Google::Apis::ComputeV1::Instance).to receive(:new).and_return(instance_object)
370
- expect(instance_object).to receive(:name=).with("test_instance")
371
- expect(instance_object).to receive(:can_ip_forward=).with("ip_forwarding")
372
- expect(instance_object).to receive(:disks=).with("test_disks")
373
- expect(instance_object).to receive(:machine_type=).with("test_machine_type_url")
374
- expect(instance_object).to receive(:metadata=).with("test_metadata_obj")
375
- expect(instance_object).to receive(:network_interfaces=).with("test_network_interfaces")
376
- expect(instance_object).to receive(:scheduling=).with("test_scheduling")
377
- expect(instance_object).to receive(:service_accounts=).with("test_service_accounts")
378
- expect(instance_object).to receive(:tags=).with("test_tags_obj")
379
-
380
- expect(service.instance_object_for(options)).to eq(instance_object)
381
- end
382
-
383
- it "does not include service accounts if none exist" do
384
- expect(service).to receive(:instance_service_accounts_for).with(options).and_return(nil)
385
- expect(instance_object).not_to receive(:service_accounts=)
386
-
387
- service.instance_object_for(options)
388
- end
389
- end
390
-
391
- describe "#instance_disks_for" do
392
-
393
- before do
394
- expect(service).to receive(:instance_boot_disk_for).with(options).and_return("boot_disk")
395
- end
396
-
397
- context "when no additional disks are to be attached" do
398
- let(:options) { { additional_disks: [] } }
399
-
400
- it "returns an array containing only the boot disk" do
401
- expect(service.instance_disks_for(options)).to eq(%w{boot_disk})
402
- end
403
- end
404
-
405
- context "when additional disks are to be attached and they exist" do
406
- let(:options) { { additional_disks: %w{disk1 disk2} } }
407
-
408
- it "returns an array containing all three disks" do
409
- disk1 = double("disk1")
410
- disk2 = double("disk2")
411
- attached_disk1 = double("attached_disk1")
412
- attached_disk2 = double("attached_disk2")
413
-
414
- expect(connection).to receive(:get_disk).with(project, zone, "disk1").and_return(disk1)
415
- expect(connection).to receive(:get_disk).with(project, zone, "disk2").and_return(disk2)
416
- expect(disk1).to receive(:self_link).and_return("disk1_url")
417
- expect(disk2).to receive(:self_link).and_return("disk2_url")
418
- expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(attached_disk1)
419
- expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(attached_disk2)
420
- expect(attached_disk1).to receive(:source=).and_return("disk1_url")
421
- expect(attached_disk2).to receive(:source=).and_return("disk2_url")
422
- expect(service.instance_disks_for(options)).to eq(["boot_disk", attached_disk1, attached_disk2])
423
- end
424
- end
425
-
426
- context "when an additional disk is to be attached but does not exist" do
427
- let(:options) { { additional_disks: %w{bad_disk} } }
428
-
429
- it "raises an exception" do
430
- expect(connection).to receive(:get_disk).with(project, zone, "bad_disk").and_raise(Google::Apis::ClientError.new("disk not found"))
431
- expect(service.ui).to receive(:error).with("Unable to attach disk bad_disk to the instance: disk not found")
432
- expect { service.instance_disks_for(options) }.to raise_error(Google::Apis::ClientError)
433
- end
434
- end
435
- end
436
-
437
- describe "#instance_boot_disk_for" do
438
- it "sets up a disk object and returns it" do
439
- disk = double("disk")
440
- params = double("params")
441
- options = {
442
- boot_disk_autodelete: "autodelete_param",
443
- boot_disk_size: "disk_size",
444
- boot_disk_ssd: "disk_ssd",
445
- image: "disk_image",
446
- image_project: "disk_image_project",
447
- }
448
-
449
- expect(service).to receive(:boot_disk_name_for).with(options).and_return("disk_name")
450
- expect(service).to receive(:boot_disk_type_for).with(options).and_return("disk_type")
451
- expect(service).to receive(:disk_type_url_for).with("disk_type").and_return("disk_type_url")
452
- expect(service).to receive(:image_search_for).with("disk_image", "disk_image_project").and_return("source_image")
453
-
454
- expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(disk)
455
- expect(Google::Apis::ComputeV1::AttachedDiskInitializeParams).to receive(:new).and_return(params)
456
- expect(disk).to receive(:boot=).with(true)
457
- expect(disk).to receive(:auto_delete=).with("autodelete_param")
458
- expect(disk).to receive(:initialize_params=).with(params)
459
- expect(params).to receive(:disk_name=).with("disk_name")
460
- expect(params).to receive(:disk_size_gb=).with("disk_size")
461
- expect(params).to receive(:disk_type=).with("disk_type_url")
462
- expect(params).to receive(:source_image=).with("source_image")
463
-
464
- expect(service.instance_boot_disk_for(options)).to eq(disk)
465
- end
466
- end
467
-
468
- describe "#boot_disk_type_for" do
469
- it "returns pd-ssd if boot_disk_ssd is true" do
470
- expect(service.boot_disk_type_for(boot_disk_ssd: true)).to eq("pd-ssd")
471
- end
472
-
473
- it "returns pd-standard if boot_disk_ssd is false" do
474
- expect(service.boot_disk_type_for(boot_disk_ssd: false)).to eq("pd-standard")
475
- end
476
- end
477
-
478
- describe "#image_search_for" do
479
- context "when the user supplies an image project" do
480
- it "returns the image URL based on the image project" do
481
- expect(service).to receive(:image_url_for).with("test_project", "test_image").and_return("image_url")
482
- expect(service.image_search_for("test_image", "test_project")).to eq("image_url")
483
- end
484
- end
485
-
486
- context "when the user does not supply an image project" do
487
- context "when the image provided is an alias" do
488
- it "returns the alias URL" do
489
- expect(service).to receive(:image_alias_url).with("test_image").and_return("image_alias_url")
490
- expect(service.image_search_for("test_image", nil)).to eq("image_alias_url")
491
- end
492
- end
493
-
494
- context "when the image provided is not an alias" do
495
- before do
496
- expect(service).to receive(:image_alias_url).and_return(nil)
497
- end
498
-
499
- context "when the image exists in the user's project" do
500
- it "returns the image URL" do
501
- expect(service).to receive(:image_url_for).with(project, "test_image").and_return("image_url")
502
- expect(service.image_search_for("test_image", nil)).to eq("image_url")
503
- end
504
- end
505
-
506
- context "when the image does not exist in the user's project" do
507
- before do
508
- expect(service).to receive(:image_url_for).with(project, "test_image").and_return(nil)
509
- end
510
-
511
- context "when the image matches a known public project" do
512
- it "returns the image URL from the public project" do
513
- expect(service).to receive(:public_project_for_image).with("test_image").and_return("public_project")
514
- expect(service).to receive(:image_url_for).with("public_project", "test_image").and_return("image_url")
515
- expect(service.image_search_for("test_image", nil)).to eq("image_url")
516
- end
517
- end
518
-
519
- context "when the image does not match a known project" do
520
- it "returns nil" do
521
- expect(service).to receive(:public_project_for_image).with("test_image").and_return(nil)
522
- expect(service).not_to receive(:image_url_for)
523
- expect(service.image_search_for("test_image", nil)).to eq(nil)
524
- end
525
- end
526
- end
527
- end
528
- end
529
- end
530
-
531
- describe "#image_url_for" do
532
- it "returns nil if the image does not exist" do
533
- expect(service).to receive(:image_exist?).with("image_project", "image_name").and_return(false)
534
- expect(service.image_url_for("image_project", "image_name")).to eq(nil)
535
- end
536
-
537
- it "returns a properly formatted image URL if the image exists" do
538
- expect(service).to receive(:image_exist?).with("image_project", "image_name").and_return(true)
539
- expect(service.image_url_for("image_project", "image_name")).to eq("projects/image_project/global/images/image_name")
540
- end
541
- end
542
-
543
- describe "#image_alias_url" do
544
- context "when the image_alias is not a valid alias" do
545
- it "returns nil" do
546
- expect(service.image_alias_url("fake_alias")).to eq(nil)
547
- end
548
- end
549
-
550
- context "when the image_alias is a valid alias" do
551
- before do
552
- allow(connection).to receive(:list_images).and_return(response)
553
- end
554
-
555
- context "when the response contains no images" do
556
- let(:response) { double("response", items: []) }
557
-
558
- it "returns nil" do
559
- expect(service.image_alias_url("centos-7")).to eq(nil)
560
- end
561
- end
562
-
563
- context "when the response contains images but none match the name" do
564
- let(:image1) { double("image1", name: "centos-6") }
565
- let(:image2) { double("image2", name: "centos-6") }
566
- let(:image3) { double("image3", name: "ubuntu-14") }
567
- let(:response) { double("response", items: [ image1, image2, image3 ]) }
568
-
569
- it "returns nil" do
570
- expect(service.image_alias_url("centos-7")).to eq(nil)
571
- end
572
- end
573
-
574
- context "when the response contains images that match the name" do
575
- let(:image1) { double("image1", name: "centos-7-v20160201", self_link: "image1_selflink") }
576
- let(:image2) { double("image2", name: "centos-7-v20160301", self_link: "image2_selflink") }
577
- let(:image3) { double("image3", name: "centos-6", self_link: "image3_selflink") }
578
- let(:response) { double("response", items: [ image1, image2, image3 ]) }
579
-
580
- it "returns the link for image2 which is the most recent image" do
581
- expect(service.image_alias_url("centos-7")).to eq("image2_selflink")
582
- end
583
- end
584
- end
585
- end
586
-
587
- describe "#boot_disk_name_for" do
588
- it "returns the boot disk name if supplied by the user" do
589
- options = { name: "instance_name", boot_disk_name: "disk_name" }
590
- expect(service.boot_disk_name_for(options)).to eq("disk_name")
591
- end
592
-
593
- it "returns the instance name if the boot disk name is not supplied" do
594
- options = { name: "instance_name" }
595
- expect(service.boot_disk_name_for(options)).to eq("instance_name")
596
- end
597
- end
598
-
599
- describe "#machine_type_url_for" do
600
- it "returns a properly-formatted machine type URL" do
601
- expect(service.machine_type_url_for("test_type")).to eq("zones/test_zone/machineTypes/test_type")
602
- end
603
- end
604
-
605
- describe "#instance_metadata_for" do
606
- it "returns nil if the passed-in metadata is nil" do
607
- expect(service.instance_metadata_for(nil)).to eq(nil)
608
- end
609
-
610
- it "returns nil if the passed-in metadata is empty" do
611
- expect(service.instance_metadata_for([])).to eq(nil)
612
- end
613
-
614
- it "returns a properly-formatted metadata object if metadata is passed in" do
615
- metadata = { "key1" => "value1", "key2" => "value2" }
616
- metadata_obj = double("metadata_obj")
617
- item_1 = double("item_1")
618
- item_2 = double("item_2")
619
-
620
- expect(Google::Apis::ComputeV1::Metadata).to receive(:new).and_return(metadata_obj)
621
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item_1)
622
- expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item_2)
623
- expect(item_1).to receive(:key=).with("key1")
624
- expect(item_1).to receive(:value=).with("value1")
625
- expect(item_2).to receive(:key=).with("key2")
626
- expect(item_2).to receive(:value=).with("value2")
627
- expect(metadata_obj).to receive(:items=).with([item_1, item_2])
628
-
629
- expect(service.instance_metadata_for(metadata)).to eq(metadata_obj)
630
- end
631
- end
632
-
633
- describe "#instance_network_interfaces_for" do
634
- let(:interface) { double("interface" ) }
635
- let(:options) { { network: "test_network", public_ip: "public_ip" } }
636
-
637
- before do
638
- allow(service).to receive(:network_url_for)
639
- allow(service).to receive(:subnet_url_for)
640
- allow(service).to receive(:instance_access_configs_for)
641
- allow(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
642
- allow(interface).to receive(:network=)
643
- allow(interface).to receive(:subnetwork=)
644
- allow(interface).to receive(:access_configs=)
645
- end
646
-
647
- it "creates a network interface object and returns it" do
648
- expect(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
649
- expect(service.instance_network_interfaces_for(options)).to eq([interface])
650
- end
651
-
652
- it "sets the network" do
653
- expect(service).to receive(:network_url_for).with("test_network").and_return("network_url")
654
- expect(interface).to receive(:network=).with("network_url")
655
- service.instance_network_interfaces_for(options)
656
- end
657
-
658
- it "sets the access configs" do
659
- expect(service).to receive(:instance_access_configs_for).with("public_ip").and_return("access_configs")
660
- expect(interface).to receive(:access_configs=).with("access_configs")
661
- service.instance_network_interfaces_for(options)
662
- end
663
-
664
- it "does not set a subnetwork" do
665
- expect(service).not_to receive(:subnet_url_for)
666
- expect(interface).not_to receive(:subnetwork=)
667
- service.instance_network_interfaces_for(options)
668
- end
669
-
670
- context "when a subnet exists" do
671
- let(:options) { { network: "test_network", subnet: "test_subnet", public_ip: "public_ip" } }
672
-
673
- it "sets the subnetwork" do
674
- expect(service).to receive(:subnet_url_for).with("test_subnet").and_return("subnet_url")
675
- expect(interface).to receive(:subnetwork=).with("subnet_url")
676
- service.instance_network_interfaces_for(options)
677
- end
678
- end
679
- end
680
-
681
- describe "#instance_access_configs_for" do
682
- let(:interface) { double("interface" ) }
683
-
684
- context "for None public_ip" do
685
- it "empty public_ip none|None|NONE|~" do
686
- expect(service.instance_access_configs_for("none")).to eq([])
687
-
688
- expect(service.instance_access_configs_for("None")).to eq([])
689
-
690
- expect(service.instance_access_configs_for("NONE")).to eq([])
691
- end
692
- end
693
-
694
- context "for valid public_ip" do
695
- it "valid public_ip" do
696
- access_config = service.instance_access_configs_for("8.8.8.8")
697
- expect(access_config.first.nat_ip).to eq("8.8.8.8")
698
- end
699
- end
700
-
701
- context "for invalid public_ip" do
702
- it "empty public_ip none|None|NONE|~" do
703
- access_config = service.instance_access_configs_for("oh no not a valid IP")
704
- expect(access_config.first.nat_ip).to eq(nil)
705
- end
706
- end
707
- end
708
-
709
- describe "#network_url_for" do
710
- it "returns a properly-formatted network URL" do
711
- expect(service.network_url_for("test_network")).to eq("projects/test_project/global/networks/test_network")
712
- end
713
- end
714
-
715
- describe "#subnet_url_for" do
716
- it "returns a properly-formatted subnet URL" do
717
- expect(service).to receive(:region).and_return("test_region")
718
- expect(service.subnet_url_for("test_subnet")).to eq("projects/test_project/regions/test_region/subnetworks/test_subnet")
719
- end
720
- end
721
-
722
- describe "#instance_scheduling_for" do
723
- it "returns a properly-formatted scheduling object" do
724
- scheduling = double("scheduling")
725
- options = { auto_restart: "auto_restart", auto_migrate: "auto_migrate", preemptible: "preempt" }
726
-
727
- expect(service).to receive(:migrate_setting_for).with("auto_migrate").and_return("host_maintenance")
728
- expect(Google::Apis::ComputeV1::Scheduling).to receive(:new).and_return(scheduling)
729
- expect(scheduling).to receive(:automatic_restart=).with("auto_restart")
730
- expect(scheduling).to receive(:on_host_maintenance=).with("host_maintenance")
731
- expect(scheduling).to receive(:preemptible=).with("preempt")
732
-
733
- expect(service.instance_scheduling_for(options)).to eq(scheduling)
734
- end
735
- end
736
-
737
- describe "#migrate_setting_for" do
738
- it "returns MIGRATE when auto_migrate is true" do
739
- expect(service.migrate_setting_for(true)).to eq("MIGRATE")
740
- end
741
-
742
- it "returns TERMINATE when auto_migrate is false" do
743
- expect(service.migrate_setting_for(false)).to eq("TERMINATE")
744
- end
745
- end
746
-
747
- describe "#instance_service_accounts_for" do
748
- it "returns nil if service_account_scopes is nil" do
749
- expect(service.instance_service_accounts_for({})).to eq(nil)
750
- end
751
-
752
- it "returns nil if service_account_scopes is empty" do
753
- expect(service.instance_service_accounts_for({ service_account_scopes: [] })).to eq(nil)
754
- end
755
-
756
- it "returns an array containing a properly-formatted service account" do
757
- service_account = double("service_account")
758
- options = { service_account_name: "account_name", service_account_scopes: %w{scope1 scope2} }
759
-
760
- expect(Google::Apis::ComputeV1::ServiceAccount).to receive(:new).and_return(service_account)
761
- expect(service_account).to receive(:email=).with("account_name")
762
- expect(service).to receive(:service_account_scope_url).with("scope1").and_return("https://www.googleapis.com/auth/scope1")
763
- expect(service).to receive(:service_account_scope_url).with("scope2").and_return("https://www.googleapis.com/auth/scope2")
764
- expect(service_account).to receive(:scopes=).with([
765
- "https://www.googleapis.com/auth/scope1",
766
- "https://www.googleapis.com/auth/scope2",
767
- ])
768
-
769
- expect(service.instance_service_accounts_for(options)).to eq([service_account])
770
- end
771
- end
772
-
773
- describe "#service_account_scope_url" do
774
- it "returns the passed-in scope if it already looks like a scope URL" do
775
- scope = "https://www.googleapis.com/auth/fake_scope"
776
- expect(service.service_account_scope_url(scope)).to eq(scope)
777
- end
778
-
779
- it "returns a properly-formatted scope URL if a short-name or alias is provided" do
780
- expect(service).to receive(:translate_scope_alias).with("scope_alias").and_return("real_scope")
781
- expect(service.service_account_scope_url("scope_alias")).to eq("https://www.googleapis.com/auth/real_scope")
782
- end
783
- end
784
-
785
- describe "#translate_scope_alias" do
786
- it "returns a scope for a given alias" do
787
- expect(service.translate_scope_alias("storage-rw")).to eq("devstorage.read_write")
788
- end
789
-
790
- it "returns the passed-in scope alias if nothing matches in the alias map" do
791
- expect(service.translate_scope_alias("fake_scope")).to eq("fake_scope")
792
- end
793
- end
794
-
795
- describe "#instance_tags_for" do
796
- it "returns nil if tags is nil" do
797
- expect(service.instance_tags_for(nil)).to eq(nil)
798
- end
799
-
800
- it "returns nil if tags is empty" do
801
- expect(service.instance_tags_for([])).to eq(nil)
802
- end
803
-
804
- it "returns a properly-formatted tags object" do
805
- tags_obj = double("tags_obj")
806
-
807
- expect(Google::Apis::ComputeV1::Tags).to receive(:new).and_return(tags_obj)
808
- expect(tags_obj).to receive(:items=).with("test_tags")
809
-
810
- expect(service.instance_tags_for("test_tags")).to eq(tags_obj)
811
- end
812
- end
813
-
814
- describe "#network_for" do
815
- it "returns the network name if it exists" do
816
- interface = double("interface", network: "/some/path/to/default_network")
817
- instance = double("instance", network_interfaces: [interface])
818
-
819
- expect(service.network_for(instance)).to eq("default_network")
820
- end
821
-
822
- it "returns 'unknown' if the network cannot be found" do
823
- instance = double("instance")
824
- expect(instance).to receive(:network_interfaces).and_raise(NoMethodError)
825
-
826
- expect(service.network_for(instance)).to eq("unknown")
827
- end
828
- end
829
-
830
- describe "#machine_type_for" do
831
- it "returns the machine type name" do
832
- instance = double("instance", machine_type: "/some/path/to/test_type")
833
- expect(service.machine_type_for(instance)).to eq("test_type")
834
- end
835
- end
836
-
837
- describe "#public_project_for_image" do
838
- {
839
- "centos" => "centos-cloud",
840
- "container-vm" => "google-containers",
841
- "coreos" => "coreos-cloud",
842
- "debian" => "debian-cloud",
843
- "opensuse-cloud" => "opensuse-cloud",
844
- "rhel" => "rhel-cloud",
845
- "sles" => "suse-cloud",
846
- "ubuntu" => "ubuntu-os-cloud",
847
- "windows" => "windows-cloud",
848
- }.each do |image_name, project_name|
849
- it "returns project #{project_name} for an image named #{image_name}" do
850
- expect(service.public_project_for_image(image_name)).to eq(project_name)
851
- end
852
- end
853
- end
854
-
855
- describe "#disk_type_url_for" do
856
- it "returns a properly-formatted disk type URL" do
857
- expect(service.disk_type_url_for("disk_type")).to eq("zones/test_zone/diskTypes/disk_type")
858
- end
859
- end
860
-
861
- describe "#paginated_results" do
862
- let(:response) { double("response") }
863
- let(:api_method) { :list_stuff }
864
- let(:items_method) { :items }
865
- let(:args) { %w{arg1 arg2} }
866
- let(:max_pages) { 5 }
867
- let(:max_page_size) { 100 }
868
-
869
- subject { service.paginated_results(api_method, items_method, *args) }
870
-
871
- before do
872
- allow(response).to receive(:next_page_token)
873
- allow(service).to receive(:max_pages).and_return(max_pages)
874
- allow(service).to receive(:max_page_size).and_return(max_page_size)
875
- end
876
-
877
- context "when the response has no items" do
878
- it "returns an empty array" do
879
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response)
880
- expect(response).to receive(:items).and_return(nil)
881
- expect(subject).to eq([])
882
- end
883
- end
884
-
885
- context "when the response has items with no additional pages" do
886
- it "calls the API once and returns the fetched results" do
887
- expect(response).to receive(:items).and_return(%w{item1 item2})
888
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response)
889
- expect(subject).to eq(%w{item1 item2})
890
- end
891
- end
892
-
893
- context "when the response has items spanning 3 pages" do
894
-
895
- it "calls the API 3 times and returns the results" do
896
- response1 = double("response1", items: %w{item1 item2}, next_page_token: "page2")
897
- response2 = double("response2", items: %w{item3 item4}, next_page_token: "page3")
898
- response3 = double("response3", items: %w{item5 item6}, next_page_token: nil)
899
-
900
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response1)
901
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page2").and_return(response2)
902
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page3").and_return(response3)
903
- expect(subject).to eq(%w{item1 item2 item3 item4 item5 item6})
904
- end
905
- end
906
-
907
- context "when the response has items spanning more than max allowed pages" do
908
- it "only calls the API the maximum allow number of times and returns results" do
909
- response1 = double("response1", items: %w{item1}, next_page_token: "page2")
910
- response2 = double("response2", items: %w{item2}, next_page_token: "page3")
911
- response3 = double("response3", items: %w{item3}, next_page_token: "page4")
912
- response4 = double("response4", items: %w{item4}, next_page_token: "page5")
913
- response5 = double("response5", items: %w{item5}, next_page_token: "page6")
914
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response1)
915
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page2").and_return(response2)
916
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page3").and_return(response3)
917
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page4").and_return(response4)
918
- expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page5").and_return(response5)
919
- expect(service.ui).to receive(:warn).with("Max pages (5) reached, but more results exist - truncating results...")
920
- expect(subject).to eq(%w{item1 item2 item3 item4 item5})
921
- end
922
- end
923
- end
924
-
925
- describe "#wait_for_status" do
926
- let(:item) { double("item") }
927
-
928
- before do
929
- allow(service).to receive(:wait_time).and_return(600)
930
- allow(service).to receive(:refresh_rate).and_return(2)
931
-
932
- # muffle any stdout output from this method
933
- allow(service).to receive(:print)
934
-
935
- # don"t actually sleep
936
- allow(service).to receive(:sleep)
937
- end
938
-
939
- context "when the items completes normally, 3 loops" do
940
- it "only refreshes the item 3 times" do
941
- allow(item).to receive(:status).exactly(3).times.and_return("PENDING", "RUNNING", "DONE")
942
-
943
- service.wait_for_status("DONE") { item }
944
- end
945
- end
946
-
947
- context "when the item is completed on the first loop" do
948
- it "only refreshes the item 1 time" do
949
- allow(item).to receive(:status).once.and_return("DONE")
950
-
951
- service.wait_for_status("DONE") { item }
952
- end
953
- end
954
-
955
- context "when the timeout is exceeded" do
956
- it "prints a warning and exits" do
957
- allow(Timeout).to receive(:timeout).and_raise(Timeout::Error)
958
- expect(service.ui).to receive(:error)
959
- .with("Request did not complete in 600 seconds. Check the Google Cloud Console for more info.")
960
- expect { service.wait_for_status("DONE") { item } }.to raise_error(SystemExit)
961
- end
962
- end
963
-
964
- context "when a non-timeout exception is raised" do
965
- it "raises the original exception" do
966
- allow(item).to receive(:status).and_raise(RuntimeError)
967
- expect { service.wait_for_status("DONE") { item } }.to raise_error(RuntimeError)
968
- end
969
- end
970
- end
971
-
972
- describe "#wait_for_operation" do
973
- let(:operation) { double("operation", name: "operation-123") }
974
-
975
- it "raises a properly-formatted exception when errors exist" do
976
- error1 = double("error1", code: "ERROR1", message: "error 1")
977
- error2 = double("error2", code: "ERROR2", message: "error 2")
978
- expect(service).to receive(:wait_for_status).with("DONE")
979
- expect(service).to receive(:operation_errors).with("operation-123").and_return([error1, error2])
980
- expect(service.ui).to receive(:error).with("#{service.ui.color("ERROR1", :bold)}: error 1")
981
- expect(service.ui).to receive(:error).with("#{service.ui.color("ERROR2", :bold)}: error 2")
982
-
983
- expect { service.wait_for_operation(operation) }.to raise_error(RuntimeError, "Operation operation-123 failed.")
984
- end
985
-
986
- it "does not raise an exception if no errors are encountered" do
987
- expect(service).to receive(:wait_for_status).with("DONE")
988
- expect(service).to receive(:operation_errors).with("operation-123").and_return([])
989
- expect(service.ui).not_to receive(:error)
990
-
991
- expect { service.wait_for_operation(operation) }.not_to raise_error
992
- end
993
- end
994
-
995
- describe "#zone_operation" do
996
- it "fetches the operation from the API and returns it" do
997
- expect(connection).to receive(:get_zone_operation).with(project, zone, "operation-123").and_return("operation")
998
- expect(service.zone_operation("operation-123")).to eq("operation")
999
- end
1000
- end
1001
-
1002
- describe "#operation_errors" do
1003
- let(:operation) { double("operation") }
1004
- let(:error_obj) { double("error_obj") }
1005
-
1006
- before do
1007
- expect(service).to receive(:zone_operation).with("operation-123").and_return(operation)
1008
- end
1009
-
1010
- it "returns an empty array if there are no errors" do
1011
- expect(operation).to receive(:error).and_return(nil)
1012
- expect(service.operation_errors("operation-123")).to eq([])
1013
- end
1014
-
1015
- it "returns the errors from the operation if they exist" do
1016
- expect(operation).to receive(:error).twice.and_return(error_obj)
1017
- expect(error_obj).to receive(:errors).and_return("some errors")
1018
- expect(service.operation_errors("operation-123")).to eq("some errors")
1019
- end
1020
- end
1021
- end