knife-google 3.3.6 → 3.3.7

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.
@@ -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