knife-google 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/README.md +2 -1
- data/lib/chef/knife/cloud/google_service.rb +82 -10
- data/lib/chef/knife/google_server_create.rb +28 -2
- data/lib/knife-google/version.rb +1 -1
- data/spec/cloud/google_service_spec.rb +149 -22
- data/spec/google_server_create_spec.rb +54 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fb67127a3cc6cd2859f32160e8b08612eeff968
|
4
|
+
data.tar.gz: 99833b9f9980664e97fb5e2d243fd5ed541f7ee2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf712c5c7f3b7cc0a4086b04cf5d7db3c27c9f110bfcb56f8e5234faf5688e05711a1fd59d000de4035b8621c7787ff9a87195024fd5122ddc196978b668dd66
|
7
|
+
data.tar.gz: 051a36a7ce5831cbedf78dbcf0179d779a8e18aab4fe9ae7d720443172b7da1df1b132f444ac938927cb06b50b768484e724c98a10f597b07d52b74ba0d42652
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# knife-google Change Log
|
2
2
|
|
3
|
-
## v2.
|
3
|
+
## v2.2.0 (2016-03-17)
|
4
|
+
|
5
|
+
* [pr#102](https://github.com/chef/knife-google/pull/102) Added support for preemptible instances
|
6
|
+
* [pr#103](https://github.com/chef/knife-google/pull/103) Added support for deploying instances on subnetworks
|
7
|
+
* [pr#103](https://github.com/chef/knife-google/pull/104) Added gcloud-style image aliases (i.e. image "centos-7" will get you the latest CentOS 7 disk image)
|
8
|
+
|
9
|
+
## v2.1.0 (2016-03-04)
|
4
10
|
* [pr#99](https://github.com/chef/knife-google/pull/99) Support service account scope aliases, similar to the gcloud SDK
|
5
11
|
* [pr#101](https://github.com/chef/knife-google/pull/101) Set the application name and version on the API object for proper user-agent formatting
|
6
12
|
|
data/README.md
CHANGED
@@ -185,7 +185,8 @@ See the [SSH Keys](#ssh-keys) section for more information.
|
|
185
185
|
* **INSTANCE_NAME**: required. The name to use when creating the instance.
|
186
186
|
* **--gce-machine-type**: required. The machine type to use when creating the server, such as `n1-standard-2` or `n1-highcpu-2-d`.
|
187
187
|
* **--gce-network**: The name of the network to which your instance will be attached. Defaults to "default".
|
188
|
-
* **--gce-
|
188
|
+
* **--gce-subnet**: The name of the subnet to which your instance will be attached. Only applies to custom networks.
|
189
|
+
* **--gce-image**: required. The name of the disk image to use when creating the server. knife-google will search your current project for this disk image. If the image cannot be found but looks like a common public image, the public image project will be searched as well. Additionally, this parameter supports the same image aliases that `gcloud compute instances create` supports. See the output of `gcloud compute instances create --help` for a full list of aliases.
|
189
190
|
* Example: if you supply a gce-image of `centos-7-v20160219`, knife-google will first look for an image with that name in your currently-configured project. If it cannot be found, it will look in the `centos-cloud` project.
|
190
191
|
* This behavior can be overridden with the `--gce-image-project` parameter.
|
191
192
|
* **--gce-image-project**: optional. The name of the GCP project that contains the image specified with the `--gce-image` flag. If this is specified, knife-google will not search any known public projects for your image.
|
@@ -52,6 +52,27 @@ class Chef::Knife::Cloud
|
|
52
52
|
"userinfo-email" => "userinfo.email",
|
53
53
|
}
|
54
54
|
|
55
|
+
IMAGE_ALIAS_MAP = {
|
56
|
+
"centos-6" => { project: "centos-cloud", prefix: "centos-6" },
|
57
|
+
"centos-7" => { project: "centos-cloud", prefix: "centos-7" },
|
58
|
+
"container-vm" => { project: "google-containers", prefix: "container-vm" },
|
59
|
+
"coreos" => { project: "coreos-cloud", prefix: "coreos-stable" },
|
60
|
+
"debian-7" => { project: "debian-cloud", prefix: "debian-7-wheezy" },
|
61
|
+
"debian-7-backports" => { project: "debian-cloud", prefix: "backports-debian-7-wheezy" },
|
62
|
+
"debian-8" => { project: "debian-cloud", prefix: "debian-8-jessie" },
|
63
|
+
"opensuse-13" => { project: "opensuse-cloud", prefix: "opensuse-13" },
|
64
|
+
"rhel-6" => { project: "rhel-cloud", prefix: "rhel-6" },
|
65
|
+
"rhel-7" => { project: "rhel-cloud", prefix: "rhel-7" },
|
66
|
+
"sles-11" => { project: "suse-cloud", prefix: "sles-11" },
|
67
|
+
"sles-12" => { project: "suse-cloud", prefix: "sles-12" },
|
68
|
+
"ubuntu-12-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1204-precise" },
|
69
|
+
"ubuntu-14-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1404-trusty" },
|
70
|
+
"ubuntu-15-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1504-vivid" },
|
71
|
+
"ubuntu-15-10" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1510-wily" },
|
72
|
+
"windows-2008-r2" => { project: "windows-cloud", prefix: "windows-server-2008-r2" },
|
73
|
+
"windows-2012-r2" => { project: "windows-cloud", prefix: "windows-server-2012-r2" },
|
74
|
+
}
|
75
|
+
|
55
76
|
def initialize(options = {})
|
56
77
|
@project = options[:project]
|
57
78
|
@zone = options[:zone]
|
@@ -186,12 +207,13 @@ class Chef::Knife::Cloud
|
|
186
207
|
def validate_server_create_options!(options)
|
187
208
|
raise "Invalid machine type: #{options[:machine_type]}" unless valid_machine_type?(options[:machine_type])
|
188
209
|
raise "Invalid network: #{options[:network]}" unless valid_network?(options[:network])
|
210
|
+
raise "Invalid subnet: #{options[:subnet]}" if options[:subnet] && !valid_subnet?(options[:subnet])
|
189
211
|
raise "Invalid Public IP setting: #{options[:public_ip]}" unless valid_public_ip_setting?(options[:public_ip])
|
190
|
-
raise "Invalid image: #{options[:image]} - check your image name, or set an image project if needed" if
|
212
|
+
raise "Invalid image: #{options[:image]} - check your image name, or set an image project if needed" if boot_disk_source_image(options[:image], options[:image_project]).nil?
|
191
213
|
end
|
192
214
|
|
193
|
-
def check_api_call
|
194
|
-
|
215
|
+
def check_api_call
|
216
|
+
yield
|
195
217
|
rescue Google::Apis::ClientError
|
196
218
|
false
|
197
219
|
else
|
@@ -208,6 +230,11 @@ class Chef::Knife::Cloud
|
|
208
230
|
check_api_call { connection.get_network(project, network) }
|
209
231
|
end
|
210
232
|
|
233
|
+
def valid_subnet?(subnet)
|
234
|
+
return false if subnet.nil?
|
235
|
+
check_api_call { connection.get_subnetwork(project, region, subnet) }
|
236
|
+
end
|
237
|
+
|
211
238
|
def image_exist?(image_project, image_name)
|
212
239
|
check_api_call { connection.get_image(image_project, image_name) }
|
213
240
|
end
|
@@ -232,6 +259,10 @@ class Chef::Knife::Cloud
|
|
232
259
|
true
|
233
260
|
end
|
234
261
|
|
262
|
+
def region
|
263
|
+
@region ||= connection.get_zone(project, zone).region.split("/").last
|
264
|
+
end
|
265
|
+
|
235
266
|
def instance_object_for(options)
|
236
267
|
inst_obj = Google::Apis::ComputeV1::Instance.new
|
237
268
|
inst_obj.name = options[:name]
|
@@ -273,7 +304,7 @@ class Chef::Knife::Cloud
|
|
273
304
|
params.disk_name = boot_disk_name_for(options)
|
274
305
|
params.disk_size_gb = options[:boot_disk_size]
|
275
306
|
params.disk_type = disk_type_url_for(boot_disk_type_for(options))
|
276
|
-
params.source_image =
|
307
|
+
params.source_image = boot_disk_source_image(options[:image], options[:image_project])
|
277
308
|
|
278
309
|
disk.initialize_params = params
|
279
310
|
disk
|
@@ -283,21 +314,40 @@ class Chef::Knife::Cloud
|
|
283
314
|
options[:boot_disk_ssd] ? "pd-ssd" : "pd-standard"
|
284
315
|
end
|
285
316
|
|
317
|
+
def boot_disk_source_image(image, image_project)
|
318
|
+
@boot_disk_source ||= image_search_for(image, image_project)
|
319
|
+
end
|
320
|
+
|
286
321
|
def image_search_for(image, image_project)
|
287
322
|
# if the user provided an image_project, assume they want it, no questions asked
|
288
|
-
|
323
|
+
unless image_project.nil?
|
324
|
+
ui.msg("Searching project #{image_project} for image #{image}")
|
325
|
+
return image_url_for(image_project, image)
|
326
|
+
end
|
327
|
+
|
328
|
+
# no image project has been provided. Check to see if the image is a known alias.
|
329
|
+
alias_url = image_alias_url(image)
|
330
|
+
unless alias_url.nil?
|
331
|
+
ui.msg("image #{image} is a known alias - using image URL: #{alias_url}")
|
332
|
+
return alias_url
|
333
|
+
end
|
289
334
|
|
290
|
-
#
|
335
|
+
# Doesn't match an alias. Let's check the user's project for the image.
|
291
336
|
url = image_url_for(project, image)
|
292
|
-
|
337
|
+
unless url.nil?
|
338
|
+
ui.msg("Located image #{image} in project #{project} - using image URL: #{url}")
|
339
|
+
return url
|
340
|
+
end
|
293
341
|
|
294
342
|
# Image not found in user's project. Is there a public project this image might exist in?
|
295
343
|
public_project = public_project_for_image(image)
|
296
344
|
if public_project
|
345
|
+
ui.msg("Searching public image project #{public_project} for image #{image}")
|
297
346
|
return image_url_for(public_project, image)
|
298
347
|
end
|
299
348
|
|
300
349
|
# No image in user's project or public project, so it doesn't exist.
|
350
|
+
ui.error("Image search failed for image #{image} - no suitable image located")
|
301
351
|
nil
|
302
352
|
end
|
303
353
|
|
@@ -305,6 +355,22 @@ class Chef::Knife::Cloud
|
|
305
355
|
return "projects/#{image_project}/global/images/#{image_name}" if image_exist?(image_project, image_name)
|
306
356
|
end
|
307
357
|
|
358
|
+
def image_alias_url(image_alias)
|
359
|
+
return unless IMAGE_ALIAS_MAP.key?(image_alias)
|
360
|
+
|
361
|
+
image_project = IMAGE_ALIAS_MAP[image_alias][:project]
|
362
|
+
image_prefix = IMAGE_ALIAS_MAP[image_alias][:prefix]
|
363
|
+
|
364
|
+
latest_image = connection.list_images(image_project).items
|
365
|
+
.select { |image| image.name.start_with?(image_prefix) }
|
366
|
+
.sort { |a, b| a.name <=> b.name }
|
367
|
+
.last
|
368
|
+
|
369
|
+
return if latest_image.nil?
|
370
|
+
|
371
|
+
latest_image.self_link
|
372
|
+
end
|
373
|
+
|
308
374
|
def boot_disk_name_for(options)
|
309
375
|
options[:boot_disk_name].nil? ? options[:name] : options[:boot_disk_name]
|
310
376
|
end
|
@@ -331,6 +397,7 @@ class Chef::Knife::Cloud
|
|
331
397
|
def instance_network_interfaces_for(options)
|
332
398
|
interface = Google::Apis::ComputeV1::NetworkInterface.new
|
333
399
|
interface.network = network_url_for(options[:network])
|
400
|
+
interface.subnetwork = subnet_url_for(options[:subnet]) if options[:subnet]
|
334
401
|
interface.access_configs = instance_access_configs_for(options[:public_ip])
|
335
402
|
|
336
403
|
Array(interface)
|
@@ -351,10 +418,15 @@ class Chef::Knife::Cloud
|
|
351
418
|
"projects/#{project}/global/networks/#{network}"
|
352
419
|
end
|
353
420
|
|
421
|
+
def subnet_url_for(subnet)
|
422
|
+
"projects/#{project}/regions/#{region}/subnetworks/#{subnet}"
|
423
|
+
end
|
424
|
+
|
354
425
|
def instance_scheduling_for(options)
|
355
426
|
scheduling = Google::Apis::ComputeV1::Scheduling.new
|
356
|
-
scheduling.automatic_restart
|
427
|
+
scheduling.automatic_restart = options[:auto_restart].to_s
|
357
428
|
scheduling.on_host_maintenance = migrate_setting_for(options[:auto_migrate])
|
429
|
+
scheduling.preemptible = options[:preemptible].to_s
|
358
430
|
|
359
431
|
scheduling
|
360
432
|
end
|
@@ -466,13 +538,13 @@ class Chef::Knife::Cloud
|
|
466
538
|
items
|
467
539
|
end
|
468
540
|
|
469
|
-
def wait_for_status(requested_status
|
541
|
+
def wait_for_status(requested_status)
|
470
542
|
last_status = ""
|
471
543
|
|
472
544
|
begin
|
473
545
|
Timeout.timeout(wait_time) do
|
474
546
|
loop do
|
475
|
-
item =
|
547
|
+
item = yield
|
476
548
|
current_status = item.status
|
477
549
|
|
478
550
|
if current_status == requested_status
|
@@ -85,6 +85,12 @@ class Chef::Knife::Cloud
|
|
85
85
|
boolean: true,
|
86
86
|
default: true
|
87
87
|
|
88
|
+
option :preemptible,
|
89
|
+
long: "--[no-]gce-preemptible",
|
90
|
+
description: "Create the instance as a preemptible instance, allowing GCE to shut it down at any time. Defaults to false.",
|
91
|
+
boolean: true,
|
92
|
+
default: false
|
93
|
+
|
88
94
|
option :can_ip_forward,
|
89
95
|
long: "--[no-]gce-can-ip-forward",
|
90
96
|
description: "Allow server network forwarding",
|
@@ -96,6 +102,10 @@ class Chef::Knife::Cloud
|
|
96
102
|
description: "The network for this server; default is 'default'",
|
97
103
|
default: "default"
|
98
104
|
|
105
|
+
option :subnet,
|
106
|
+
long: "--gce-subnet SUBNET",
|
107
|
+
description: "The name of the subnet in the network on which to deploy the instance"
|
108
|
+
|
99
109
|
option :tags,
|
100
110
|
short: "-T TAG1,TAG2,TAG3",
|
101
111
|
long: "--gce-tags TAG1,TAG2,TAG3",
|
@@ -147,9 +157,11 @@ class Chef::Knife::Cloud
|
|
147
157
|
image: locate_config_value(:image),
|
148
158
|
image_project: locate_config_value(:image_project),
|
149
159
|
network: locate_config_value(:network),
|
160
|
+
subnet: locate_config_value(:subnet),
|
150
161
|
public_ip: locate_config_value(:public_ip),
|
151
|
-
auto_migrate:
|
152
|
-
auto_restart:
|
162
|
+
auto_migrate: auto_migrate?,
|
163
|
+
auto_restart: auto_restart?,
|
164
|
+
preemptible: preemptible?,
|
153
165
|
boot_disk_autodelete: locate_config_value(:boot_disk_autodelete),
|
154
166
|
boot_disk_name: locate_config_value(:boot_disk_name),
|
155
167
|
boot_disk_size: boot_disk_size,
|
@@ -177,6 +189,8 @@ class Chef::Knife::Cloud
|
|
177
189
|
raise "Please provide your Google Cloud console email address via --gce-email. " \
|
178
190
|
"It is required when resetting passwords on Windows hosts." if locate_config_value(:bootstrap_protocol) == "winrm" && locate_config_value(:gce_email).nil?
|
179
191
|
|
192
|
+
ui.warn("Auto-migrate disabled for preemptible instance") if preemptible? && locate_config_value(:auto_migrate)
|
193
|
+
ui.warn("Auto-restart disabled for preemptible instance") if preemptible? && locate_config_value(:auto_restart)
|
180
194
|
super
|
181
195
|
end
|
182
196
|
|
@@ -214,6 +228,18 @@ class Chef::Knife::Cloud
|
|
214
228
|
locate_config_value(:gce_email)
|
215
229
|
end
|
216
230
|
|
231
|
+
def preemptible?
|
232
|
+
locate_config_value(:preemptible)
|
233
|
+
end
|
234
|
+
|
235
|
+
def auto_migrate?
|
236
|
+
preemptible? ? false : locate_config_value(:auto_migrate)
|
237
|
+
end
|
238
|
+
|
239
|
+
def auto_restart?
|
240
|
+
preemptible? ? false : locate_config_value(:auto_restart)
|
241
|
+
end
|
242
|
+
|
217
243
|
def ip_address_for_bootstrap
|
218
244
|
ip = locate_config_value(:use_private_ip) ? private_ip_for(server) : public_ip_for(server)
|
219
245
|
|
data/lib/knife-google/version.rb
CHANGED
@@ -61,6 +61,7 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
61
61
|
before do
|
62
62
|
service.ui = Chef::Knife::UI.new($stdout, $stderr, $stdin, {})
|
63
63
|
allow(service.ui).to receive(:msg)
|
64
|
+
allow(service.ui).to receive(:error)
|
64
65
|
allow(service).to receive(:connection).and_return(connection)
|
65
66
|
end
|
66
67
|
|
@@ -189,6 +190,7 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
189
190
|
{
|
190
191
|
machine_type: "test_type",
|
191
192
|
network: "test_network",
|
193
|
+
subnet: "test_subnet",
|
192
194
|
public_ip: "public_ip",
|
193
195
|
image: "test_image",
|
194
196
|
image_project: "test_image_project",
|
@@ -198,6 +200,7 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
198
200
|
before do
|
199
201
|
allow(service).to receive(:valid_machine_type?).and_return(true)
|
200
202
|
allow(service).to receive(:valid_network?).and_return(true)
|
203
|
+
allow(service).to receive(:valid_subnet?).and_return(true)
|
201
204
|
allow(service).to receive(:valid_public_ip_setting?).and_return(true)
|
202
205
|
allow(service).to receive(:image_search_for).and_return(true)
|
203
206
|
end
|
@@ -216,6 +219,11 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
216
219
|
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
217
220
|
end
|
218
221
|
|
222
|
+
it "raises an exception if the network is not valid" do
|
223
|
+
expect(service).to receive(:valid_subnet?).with("test_subnet").and_return(false)
|
224
|
+
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
225
|
+
end
|
226
|
+
|
219
227
|
it "raises an exception if the public ip setting is not valid" do
|
220
228
|
expect(service).to receive(:valid_public_ip_setting?).with("public_ip").and_return(false)
|
221
229
|
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
@@ -267,6 +275,20 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
267
275
|
end
|
268
276
|
end
|
269
277
|
|
278
|
+
describe '#valid_subnet?' do
|
279
|
+
it "returns false if no subnet was specified" do
|
280
|
+
expect(service.valid_subnet?(nil)).to eq(false)
|
281
|
+
end
|
282
|
+
|
283
|
+
it "checks the network using check_api_call" do
|
284
|
+
expect(service).to receive(:region).and_return("test_region")
|
285
|
+
expect(connection).to receive(:get_subnetwork).with(project, "test_region", "test_subnet")
|
286
|
+
expect(service).to receive(:check_api_call).and_call_original
|
287
|
+
|
288
|
+
service.valid_subnet?("test_subnet")
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
270
292
|
describe '#image_exist?' do
|
271
293
|
it "checks the image using check_api_call" do
|
272
294
|
expect(connection).to receive(:get_image).with("image_project", "image_name")
|
@@ -312,6 +334,14 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
312
334
|
end
|
313
335
|
end
|
314
336
|
|
337
|
+
describe '#region' do
|
338
|
+
it "returns the region for a given zone" do
|
339
|
+
zone_obj = double("zone_obj", region: "/path/to/test_region")
|
340
|
+
expect(connection).to receive(:get_zone).with(project, zone).and_return(zone_obj)
|
341
|
+
expect(service.region).to eq("test_region")
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
315
345
|
describe '#instance_object_for' do
|
316
346
|
let(:instance_object) { double("instance_object") }
|
317
347
|
let(:options) do
|
@@ -453,31 +483,44 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
453
483
|
end
|
454
484
|
|
455
485
|
context "when the user does not supply an image project" do
|
456
|
-
context "when the image
|
457
|
-
it "returns the
|
458
|
-
expect(service).to receive(:
|
459
|
-
expect(service.image_search_for("test_image", nil)).to eq("
|
486
|
+
context "when the image provided is an alias" do
|
487
|
+
it "returns the alias URL" do
|
488
|
+
expect(service).to receive(:image_alias_url).with("test_image").and_return("image_alias_url")
|
489
|
+
expect(service.image_search_for("test_image", nil)).to eq("image_alias_url")
|
460
490
|
end
|
461
491
|
end
|
462
492
|
|
463
|
-
context "when the image
|
493
|
+
context "when the image provided is not an alias" do
|
464
494
|
before do
|
465
|
-
expect(service).to receive(:
|
495
|
+
expect(service).to receive(:image_alias_url).and_return(nil)
|
466
496
|
end
|
467
497
|
|
468
|
-
context "when the image
|
469
|
-
it "returns the image URL
|
470
|
-
expect(service).to receive(:
|
471
|
-
expect(service).to receive(:image_url_for).with("public_project", "test_image").and_return("image_url")
|
498
|
+
context "when the image exists in the user's project" do
|
499
|
+
it "returns the image URL" do
|
500
|
+
expect(service).to receive(:image_url_for).with(project, "test_image").and_return("image_url")
|
472
501
|
expect(service.image_search_for("test_image", nil)).to eq("image_url")
|
473
502
|
end
|
474
503
|
end
|
475
504
|
|
476
|
-
context "when the image does not
|
477
|
-
|
478
|
-
expect(service).to receive(:
|
479
|
-
|
480
|
-
|
505
|
+
context "when the image does not exist in the user's project" do
|
506
|
+
before do
|
507
|
+
expect(service).to receive(:image_url_for).with(project, "test_image").and_return(nil)
|
508
|
+
end
|
509
|
+
|
510
|
+
context "when the image matches a known public project" do
|
511
|
+
it "returns the image URL from the public project" do
|
512
|
+
expect(service).to receive(:public_project_for_image).with("test_image").and_return("public_project")
|
513
|
+
expect(service).to receive(:image_url_for).with("public_project", "test_image").and_return("image_url")
|
514
|
+
expect(service.image_search_for("test_image", nil)).to eq("image_url")
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context "when the image does not match a known project" do
|
519
|
+
it "returns nil" do
|
520
|
+
expect(service).to receive(:public_project_for_image).with("test_image").and_return(nil)
|
521
|
+
expect(service).not_to receive(:image_url_for)
|
522
|
+
expect(service.image_search_for("test_image", nil)).to eq(nil)
|
523
|
+
end
|
481
524
|
end
|
482
525
|
end
|
483
526
|
end
|
@@ -496,6 +539,50 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
496
539
|
end
|
497
540
|
end
|
498
541
|
|
542
|
+
describe '#image_alias_url' do
|
543
|
+
context "when the image_alias is not a valid alias" do
|
544
|
+
it "returns nil" do
|
545
|
+
expect(service.image_alias_url("fake_alias")).to eq(nil)
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context "when the image_alias is a valid alias" do
|
550
|
+
before do
|
551
|
+
allow(connection).to receive(:list_images).and_return(response)
|
552
|
+
end
|
553
|
+
|
554
|
+
context "when the response contains no images" do
|
555
|
+
let(:response) { double("response", items: []) }
|
556
|
+
|
557
|
+
it "returns nil" do
|
558
|
+
expect(service.image_alias_url("centos-7")).to eq(nil)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
context "when the response contains images but none match the name" do
|
563
|
+
let(:image1) { double("image1", name: "centos-6") }
|
564
|
+
let(:image2) { double("image2", name: "centos-6") }
|
565
|
+
let(:image3) { double("image3", name: "ubuntu-14") }
|
566
|
+
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
567
|
+
|
568
|
+
it "returns nil" do
|
569
|
+
expect(service.image_alias_url("centos-7")).to eq(nil)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
context "when the response contains images that match the name" do
|
574
|
+
let(:image1) { double("image1", name: "centos-7-v20160201", self_link: "image1_selflink") }
|
575
|
+
let(:image2) { double("image2", name: "centos-7-v20160301", self_link: "image2_selflink") }
|
576
|
+
let(:image3) { double("image3", name: "centos-6", self_link: "image3_selflink") }
|
577
|
+
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
578
|
+
|
579
|
+
it "returns the link for image2 which is the most recent image" do
|
580
|
+
expect(service.image_alias_url("centos-7")).to eq("image2_selflink")
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
499
586
|
describe '#boot_disk_name_for' do
|
500
587
|
it "returns the boot disk name if supplied by the user" do
|
501
588
|
options = { name: "instance_name", boot_disk_name: "disk_name" }
|
@@ -543,18 +630,50 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
543
630
|
end
|
544
631
|
|
545
632
|
describe '#instance_network_interfaces_for' do
|
546
|
-
|
547
|
-
|
548
|
-
options = { network: "test_network", public_ip: "public_ip" }
|
633
|
+
let(:interface) { double("interface" ) }
|
634
|
+
let(:options) { { network: "test_network", public_ip: "public_ip" } }
|
549
635
|
|
550
|
-
|
551
|
-
|
636
|
+
before do
|
637
|
+
allow(service).to receive(:network_url_for)
|
638
|
+
allow(service).to receive(:subnet_url_for)
|
639
|
+
allow(service).to receive(:instance_access_configs_for)
|
640
|
+
allow(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
|
641
|
+
allow(interface).to receive(:network=)
|
642
|
+
allow(interface).to receive(:subnetwork=)
|
643
|
+
allow(interface).to receive(:access_configs=)
|
644
|
+
end
|
552
645
|
|
646
|
+
it "creates a network interface object and returns it" do
|
553
647
|
expect(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
|
648
|
+
expect(service.instance_network_interfaces_for(options)).to eq([interface])
|
649
|
+
end
|
650
|
+
|
651
|
+
it "sets the network" do
|
652
|
+
expect(service).to receive(:network_url_for).with("test_network").and_return("network_url")
|
554
653
|
expect(interface).to receive(:network=).with("network_url")
|
654
|
+
service.instance_network_interfaces_for(options)
|
655
|
+
end
|
656
|
+
|
657
|
+
it "sets the access configs" do
|
658
|
+
expect(service).to receive(:instance_access_configs_for).with("public_ip").and_return("access_configs")
|
555
659
|
expect(interface).to receive(:access_configs=).with("access_configs")
|
660
|
+
service.instance_network_interfaces_for(options)
|
661
|
+
end
|
556
662
|
|
557
|
-
|
663
|
+
it "does not set a subnetwork" do
|
664
|
+
expect(service).not_to receive(:subnet_url_for)
|
665
|
+
expect(interface).not_to receive(:subnetwork=)
|
666
|
+
service.instance_network_interfaces_for(options)
|
667
|
+
end
|
668
|
+
|
669
|
+
context "when a subnet exists" do
|
670
|
+
let(:options) { { network: "test_network", subnet: "test_subnet", public_ip: "public_ip" } }
|
671
|
+
|
672
|
+
it "sets the subnetwork" do
|
673
|
+
expect(service).to receive(:subnet_url_for).with("test_subnet").and_return("subnet_url")
|
674
|
+
expect(interface).to receive(:subnetwork=).with("subnet_url")
|
675
|
+
service.instance_network_interfaces_for(options)
|
676
|
+
end
|
558
677
|
end
|
559
678
|
end
|
560
679
|
|
@@ -564,15 +683,23 @@ describe Chef::Knife::Cloud::GoogleService do
|
|
564
683
|
end
|
565
684
|
end
|
566
685
|
|
686
|
+
describe '#subnet_url_for' do
|
687
|
+
it "returns a properly-formatted subnet URL" do
|
688
|
+
expect(service).to receive(:region).and_return("test_region")
|
689
|
+
expect(service.subnet_url_for("test_subnet")).to eq("projects/test_project/regions/test_region/subnetworks/test_subnet")
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
567
693
|
describe '#instance_scheduling_for' do
|
568
694
|
it "returns a properly-formatted scheduling object" do
|
569
695
|
scheduling = double("scheduling")
|
570
|
-
options = { auto_restart: "auto_restart", auto_migrate: "auto_migrate" }
|
696
|
+
options = { auto_restart: "auto_restart", auto_migrate: "auto_migrate", preemptible: "preempt" }
|
571
697
|
|
572
698
|
expect(service).to receive(:migrate_setting_for).with("auto_migrate").and_return("host_maintenance")
|
573
699
|
expect(Google::Apis::ComputeV1::Scheduling).to receive(:new).and_return(scheduling)
|
574
700
|
expect(scheduling).to receive(:automatic_restart=).with("auto_restart")
|
575
701
|
expect(scheduling).to receive(:on_host_maintenance=).with("host_maintenance")
|
702
|
+
expect(scheduling).to receive(:preemptible=).with("preempt")
|
576
703
|
|
577
704
|
expect(service.instance_scheduling_for(options)).to eq(scheduling)
|
578
705
|
end
|
@@ -38,8 +38,13 @@ describe Chef::Knife::Cloud::GoogleServerCreate do
|
|
38
38
|
allow(command).to receive(:check_for_missing_config_values!)
|
39
39
|
allow(command).to receive(:valid_disk_size?).and_return(true)
|
40
40
|
allow(command).to receive(:boot_disk_size)
|
41
|
-
command.
|
42
|
-
command.
|
41
|
+
allow(command).to receive(:locate_config_value).with(:bootstrap_protocol).and_return("ssh")
|
42
|
+
allow(command).to receive(:locate_config_value).with(:identity_file).and_return("/path/to/file")
|
43
|
+
allow(command).to receive(:locate_config_value).with(:auto_migrate)
|
44
|
+
allow(command).to receive(:locate_config_value).with(:auto_restart)
|
45
|
+
allow(command).to receive(:locate_config_value).with(:chef_node_name)
|
46
|
+
allow(command).to receive(:locate_config_value).with(:chef_node_name_prefix)
|
47
|
+
allow(command).to receive(:preemptible?).and_return(false)
|
43
48
|
end
|
44
49
|
|
45
50
|
context "when no instance name has been provided" do
|
@@ -66,6 +71,20 @@ describe Chef::Knife::Cloud::GoogleServerCreate do
|
|
66
71
|
expect(command).to receive(:locate_config_value).with(:gce_email).and_return(nil)
|
67
72
|
expect { command.validate_params! }.to raise_error(RuntimeError)
|
68
73
|
end
|
74
|
+
|
75
|
+
it "prints a warning if auto-migrate is true for a preemptible instance" do
|
76
|
+
allow(command).to receive(:preemptible?).and_return(true)
|
77
|
+
allow(command).to receive(:locate_config_value).with(:auto_migrate).and_return(true)
|
78
|
+
expect(command.ui).to receive(:warn).with("Auto-migrate disabled for preemptible instance")
|
79
|
+
command.validate_params!
|
80
|
+
end
|
81
|
+
|
82
|
+
it "prints a warning if auto-restart is true for a preemptible instance" do
|
83
|
+
allow(command).to receive(:preemptible?).and_return(true)
|
84
|
+
allow(command).to receive(:locate_config_value).with(:auto_restart).and_return(true)
|
85
|
+
expect(command.ui).to receive(:warn).with("Auto-restart disabled for preemptible instance")
|
86
|
+
command.validate_params!
|
87
|
+
end
|
69
88
|
end
|
70
89
|
|
71
90
|
describe '#before_bootstrap' do
|
@@ -133,6 +152,39 @@ describe Chef::Knife::Cloud::GoogleServerCreate do
|
|
133
152
|
end
|
134
153
|
end
|
135
154
|
|
155
|
+
describe '#preemptible?' do
|
156
|
+
it "returns the preemptible setting from the config" do
|
157
|
+
expect(command).to receive(:locate_config_value).with(:preemptible).and_return("test_preempt")
|
158
|
+
expect(command.preemptible?).to eq("test_preempt")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#auto_migrate?' do
|
163
|
+
it "returns false if the instance is preemptible" do
|
164
|
+
expect(command).to receive(:preemptible?).and_return(true)
|
165
|
+
expect(command.auto_migrate?).to eq(false)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns the setting from the config if preemptible is false" do
|
169
|
+
expect(command).to receive(:preemptible?).and_return(false)
|
170
|
+
expect(command).to receive(:locate_config_value).with(:auto_migrate).and_return("test_migrate")
|
171
|
+
expect(command.auto_migrate?).to eq("test_migrate")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '#auto_restart?' do
|
176
|
+
it "returns false if the instance is preemptible" do
|
177
|
+
expect(command).to receive(:preemptible?).and_return(true)
|
178
|
+
expect(command.auto_restart?).to eq(false)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "returns the setting from the config if preemptible is false" do
|
182
|
+
expect(command).to receive(:preemptible?).and_return(false)
|
183
|
+
expect(command).to receive(:locate_config_value).with(:auto_restart).and_return("test_restart")
|
184
|
+
expect(command.auto_restart?).to eq("test_restart")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
136
188
|
describe '#ip_address_for_bootstrap' do
|
137
189
|
it "returns the public IP by default" do
|
138
190
|
expect(command).to receive(:locate_config_value).with(:use_private_ip).and_return(false)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-google
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chiraq Jog
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2016-03-
|
16
|
+
date: 2016-03-17 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: chef
|