knife-google 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|