kitchen-google 1.1.0 → 1.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/README.md +37 -19
- data/lib/kitchen/driver/gce.rb +22 -99
- data/lib/kitchen/driver/gce_version.rb +1 -1
- data/spec/kitchen/driver/gce_spec.rb +89 -185
- 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: 6201b751412eb2c2ee39e9e48044db20388eb210
|
4
|
+
data.tar.gz: 747394a1e962ca4780e91ccf20a3d3acade150d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 447bfea307fe118344d6a1d067ad4ed9cf76706104c1908462e20765b6ec94b18440cab65b74cb3423efc7c8602375c152e2f2ad6462a4c0f0e0e09b914a8e5a
|
7
|
+
data.tar.gz: 83afed3f0a3988504c3e4be89221c40a3520454107d0e8fbfa356efbc9979608d70bab6f46c8cd93b51c6577b1285cc1ea983f772f87a1d9ed5a532193b830d9
|
data/README.md
CHANGED
@@ -24,7 +24,7 @@ which to run your test-kitchen instances, create one, noting the
|
|
24
24
|
|
25
25
|
### Authentication and Authorization
|
26
26
|
|
27
|
-
The [underlying API](https://github.com/google/google-api-ruby-client) this plugin uses relies on the
|
27
|
+
The [underlying API](https://github.com/google/google-api-ruby-client) this plugin uses relies on the
|
28
28
|
[Google Auth Library](https://github.com/google/google-auth-library-ruby) to handle authentication to the
|
29
29
|
Google Cloud API. The auth library expects that there is a JSON credentials file located at:
|
30
30
|
|
@@ -39,7 +39,7 @@ If you already have a file you'd like to use that is in a different location, se
|
|
39
39
|
### SSH Keys
|
40
40
|
|
41
41
|
In order to bootstrap Linux nodes, you will first need to ensure your SSH
|
42
|
-
keys are set up correctly. Ensure your SSH public key is properly entered
|
42
|
+
keys are set up correctly. Ensure your SSH public key is properly entered
|
43
43
|
into your project's Metadata tab in the GCP Console. GCE will add your key
|
44
44
|
to the appropriate user's `~/.ssh/authorized_keys` file when Chef first
|
45
45
|
connects to perform the bootstrap process.
|
@@ -113,26 +113,38 @@ Note that this parameter requires the "Project ID", not the "Project Name."
|
|
113
113
|
|
114
114
|
Example: "funky-penguin-12345"
|
115
115
|
|
116
|
+
### `image_project`
|
117
|
+
|
118
|
+
The project ID of the GCP project to search for the configured image or image
|
119
|
+
family. This must be specified to find either public images or your own images
|
120
|
+
that exist in another project.
|
121
|
+
|
122
|
+
Example: "ubuntu-os-cloud"
|
123
|
+
|
124
|
+
### `image_family`
|
125
|
+
|
126
|
+
The family of the image to initialize your boot disk from, the latest
|
127
|
+
non-deprecated image will be used.
|
128
|
+
|
129
|
+
Note that this parameter will be ignored if `image_name` is also specified.
|
130
|
+
|
131
|
+
Example: "ubuntu-1604-lts"
|
132
|
+
|
116
133
|
### `image_name`
|
117
134
|
|
118
|
-
|
119
|
-
the boot disk. Example: `centos-6-v20160219`
|
135
|
+
The name of the disk image to use as the source image for the boot disk.
|
120
136
|
|
121
|
-
|
122
|
-
be searched first. If the image cannot be found and it looks like a common
|
123
|
-
public image, the appropriate public project will be searched.
|
137
|
+
Example: `centos-7-v20170124`
|
124
138
|
|
125
|
-
|
126
|
-
`image_project` parameter.
|
139
|
+
This parameter will override `image_family` if they are both specified.
|
127
140
|
|
128
|
-
|
129
|
-
|
130
|
-
For a full list of aliases, see the output of `gcloud compute instances create --help`.
|
141
|
+
If `image_project` is not specified, only the GCP project specified in `project`
|
142
|
+
will be searched.
|
131
143
|
|
132
144
|
### `zone`
|
133
145
|
|
134
146
|
**Required if `region` is left blank.** The name of the GCE zone in which to
|
135
|
-
launch your instances.
|
147
|
+
launch your instances.
|
136
148
|
|
137
149
|
Example: `us-east1-b`
|
138
150
|
|
@@ -166,7 +178,7 @@ to a host in the event of host maintenance. Default: `false`
|
|
166
178
|
Size, in gigabytes, of boot disk. Default: `10`.
|
167
179
|
|
168
180
|
Some images, such as windows images, have a larger source image size
|
169
|
-
and require the disk_size to be the same size or larger than the source.
|
181
|
+
and require the disk_size to be the same size or larger than the source.
|
170
182
|
An error message will be displayed to you indicating this requirement
|
171
183
|
if necessary.
|
172
184
|
|
@@ -194,8 +206,8 @@ GCE subnetwork that instance will be attached to. Only applies to custom network
|
|
194
206
|
### `preemptible`
|
195
207
|
|
196
208
|
If set to `true`, GCE instance will be brought up as a [preemptible](https://cloud.google.com/compute/docs/instances/preemptible) virtual machine,
|
197
|
-
that runs at a much lower price than normal instances. However, Compute
|
198
|
-
Engine might terminate (preempt) these instances if it requires access
|
209
|
+
that runs at a much lower price than normal instances. However, Compute
|
210
|
+
Engine might terminate (preempt) these instances if it requires access
|
199
211
|
to those resources for other tasks; default: `false`
|
200
212
|
|
201
213
|
### `service_account_name`
|
@@ -285,12 +297,18 @@ transport:
|
|
285
297
|
username: chefuser
|
286
298
|
|
287
299
|
platforms:
|
288
|
-
- name: centos
|
300
|
+
- name: centos-7
|
301
|
+
driver:
|
302
|
+
image_project: centos-cloud
|
303
|
+
image_name: centos-7-v20170124
|
304
|
+
- name: ubuntu-16.04
|
289
305
|
driver:
|
290
|
-
|
306
|
+
image_project: ubuntu-os-cloud
|
307
|
+
image_family: ubuntu-1604-lts
|
291
308
|
- name: windows
|
292
309
|
driver:
|
293
|
-
|
310
|
+
image_project: windows-cloud
|
311
|
+
image_name: windows-server-2012-r2-dc-v20170117
|
294
312
|
disk_size: 50
|
295
313
|
|
296
314
|
suites:
|
data/lib/kitchen/driver/gce.rb
CHANGED
@@ -53,32 +53,10 @@ module Kitchen
|
|
53
53
|
"userinfo-email" => "userinfo.email",
|
54
54
|
}
|
55
55
|
|
56
|
-
IMAGE_ALIAS_MAP = {
|
57
|
-
"centos-6" => { project: "centos-cloud", prefix: "centos-6" },
|
58
|
-
"centos-7" => { project: "centos-cloud", prefix: "centos-7" },
|
59
|
-
"container-vm" => { project: "google-containers", prefix: "container-vm" },
|
60
|
-
"coreos" => { project: "coreos-cloud", prefix: "coreos-stable" },
|
61
|
-
"debian-7" => { project: "debian-cloud", prefix: "debian-7-wheezy" },
|
62
|
-
"debian-7-backports" => { project: "debian-cloud", prefix: "backports-debian-7-wheezy" },
|
63
|
-
"debian-8" => { project: "debian-cloud", prefix: "debian-8-jessie" },
|
64
|
-
"opensuse-13" => { project: "opensuse-cloud", prefix: "opensuse-13" },
|
65
|
-
"rhel-6" => { project: "rhel-cloud", prefix: "rhel-6" },
|
66
|
-
"rhel-7" => { project: "rhel-cloud", prefix: "rhel-7" },
|
67
|
-
"sles-11" => { project: "suse-cloud", prefix: "sles-11" },
|
68
|
-
"sles-12" => { project: "suse-cloud", prefix: "sles-12" },
|
69
|
-
"ubuntu-12-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1204-precise" },
|
70
|
-
"ubuntu-14-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1404-trusty" },
|
71
|
-
"ubuntu-15-04" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1504-vivid" },
|
72
|
-
"ubuntu-15-10" => { project: "ubuntu-os-cloud", prefix: "ubuntu-1510-wily" },
|
73
|
-
"windows-2008-r2" => { project: "windows-cloud", prefix: "windows-server-2008-r2" },
|
74
|
-
"windows-2012-r2" => { project: "windows-cloud", prefix: "windows-server-2012-r2" },
|
75
|
-
}
|
76
|
-
|
77
56
|
kitchen_driver_api_version 2
|
78
57
|
plugin_version Kitchen::Driver::GCE_VERSION
|
79
58
|
|
80
59
|
required_config :project
|
81
|
-
required_config :image_name
|
82
60
|
|
83
61
|
default_config :region, nil
|
84
62
|
default_config :zone, nil
|
@@ -96,6 +74,8 @@ module Kitchen
|
|
96
74
|
default_config :preemptible, false
|
97
75
|
default_config :auto_restart, false
|
98
76
|
default_config :auto_migrate, false
|
77
|
+
default_config :image_family, nil
|
78
|
+
default_config :image_name, nil
|
99
79
|
default_config :image_project, nil
|
100
80
|
default_config :email, nil
|
101
81
|
default_config :use_private_ip, false
|
@@ -166,12 +146,15 @@ module Kitchen
|
|
166
146
|
raise "Region #{config[:region]} is not a valid region" if config[:region] && !valid_region?
|
167
147
|
raise "Machine type #{config[:machine_type]} is not valid" unless valid_machine_type?
|
168
148
|
raise "Disk type #{config[:disk_type]} is not valid" unless valid_disk_type?
|
149
|
+
raise "Either image family or name must be specified" unless config[:image_family] || config[:image_name]
|
169
150
|
raise "Disk image #{config[:image_name]} is not valid - check your image name and image project" if boot_disk_source_image.nil?
|
170
151
|
raise "Network #{config[:network]} is not valid" unless valid_network?
|
171
|
-
raise "Subnet #{config[:subnet]} is not valid" if config[:subnet]
|
152
|
+
raise "Subnet #{config[:subnet]} is not valid" if config[:subnet] && !valid_subnet?
|
172
153
|
raise "Email address of GCE user is not set" if winrm_transport? && config[:email].nil?
|
173
154
|
|
174
155
|
warn("Both zone and region specified - region will be ignored.") if config[:zone] && config[:region]
|
156
|
+
warn("Both image family and name specified - image family will be ignored") if config[:image_family] && config[:image_name]
|
157
|
+
warn("Image project not specified - searching current project only") unless config[:image_project]
|
175
158
|
warn("Auto-migrate disabled for preemptible instance") if preemptible? && config[:auto_migrate]
|
176
159
|
warn("Auto-restart disabled for preemptible instance") if preemptible? && config[:auto_restart]
|
177
160
|
end
|
@@ -199,7 +182,7 @@ module Kitchen
|
|
199
182
|
end
|
200
183
|
|
201
184
|
def winrm_transport?
|
202
|
-
instance.transport.name.
|
185
|
+
instance.transport.name.casecmp("winrm") == 0
|
203
186
|
end
|
204
187
|
|
205
188
|
def update_windows_password(server_name)
|
@@ -263,7 +246,7 @@ module Kitchen
|
|
263
246
|
check_api_call { connection.get_disk_type(project, zone, config[:disk_type]) }
|
264
247
|
end
|
265
248
|
|
266
|
-
def image_exist?
|
249
|
+
def image_exist?
|
267
250
|
check_api_call { connection.get_image(image_project, image_name) }
|
268
251
|
end
|
269
252
|
|
@@ -275,6 +258,14 @@ module Kitchen
|
|
275
258
|
config[:project]
|
276
259
|
end
|
277
260
|
|
261
|
+
def image_name
|
262
|
+
@image_name ||= config[:image_name] || image_name_for_family(config[:image_family])
|
263
|
+
end
|
264
|
+
|
265
|
+
def image_project
|
266
|
+
config[:image_project].nil? ? project : config[:image_project]
|
267
|
+
end
|
268
|
+
|
278
269
|
def region
|
279
270
|
config[:region].nil? ? region_for_zone : config[:region]
|
280
271
|
end
|
@@ -366,84 +357,16 @@ module Kitchen
|
|
366
357
|
end
|
367
358
|
|
368
359
|
def boot_disk_source_image
|
369
|
-
@boot_disk_source ||=
|
360
|
+
@boot_disk_source ||= image_url
|
370
361
|
end
|
371
362
|
|
372
|
-
def
|
373
|
-
|
374
|
-
unless config[:image_project].nil?
|
375
|
-
debug("Searching project #{config[:image_project]} for image #{config[:image_name]}")
|
376
|
-
return image_url_for(config[:image_project], config[:image_name])
|
377
|
-
end
|
378
|
-
|
379
|
-
# No image project has been provided. Check to see if the image is a known alias.
|
380
|
-
alias_url = image_alias_url
|
381
|
-
unless alias_url.nil?
|
382
|
-
debug("Image #{config[:image_name]} is a known alias - using image URL: #{alias_url}")
|
383
|
-
return alias_url
|
384
|
-
end
|
385
|
-
|
386
|
-
# Doesn't match an alias. Let's check the user's project for the image.
|
387
|
-
url = image_url_for(project, config[:image_name])
|
388
|
-
unless url.nil?
|
389
|
-
debug("Located image #{config[:image_name]} in project #{project} - using image URL: #{url}")
|
390
|
-
return url
|
391
|
-
end
|
392
|
-
|
393
|
-
# Image not found in user's project. Is there a public project this image might exist in?
|
394
|
-
public_project = public_project_for_image(config[:image_name])
|
395
|
-
if public_project
|
396
|
-
debug("Searching public image project #{public_project} for image #{config[:image_name]}")
|
397
|
-
return image_url_for(public_project, config[:image_name])
|
398
|
-
end
|
399
|
-
|
400
|
-
# No image in user's project or public project, so it doesn't exist.
|
401
|
-
error("Image search failed for image #{config[:image_name]} - no suitable image found")
|
402
|
-
nil
|
403
|
-
end
|
404
|
-
|
405
|
-
def image_url_for(image_project, image_name)
|
406
|
-
return "projects/#{image_project}/global/images/#{image_name}" if image_exist?(image_project, image_name)
|
363
|
+
def image_url
|
364
|
+
return "projects/#{image_project}/global/images/#{image_name}" if image_exist?
|
407
365
|
end
|
408
366
|
|
409
|
-
def
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
image_project = IMAGE_ALIAS_MAP[image_alias][:project]
|
414
|
-
image_prefix = IMAGE_ALIAS_MAP[image_alias][:prefix]
|
415
|
-
|
416
|
-
latest_image = connection.list_images(image_project).items
|
417
|
-
.select { |image| image.name.start_with?(image_prefix) }
|
418
|
-
.sort { |a, b| a.name <=> b.name }
|
419
|
-
.last
|
420
|
-
|
421
|
-
return if latest_image.nil?
|
422
|
-
|
423
|
-
latest_image.self_link
|
424
|
-
end
|
425
|
-
|
426
|
-
def public_project_for_image(image)
|
427
|
-
case image
|
428
|
-
when /centos/
|
429
|
-
"centos-cloud"
|
430
|
-
when /container-vm/
|
431
|
-
"google-containers"
|
432
|
-
when /coreos/
|
433
|
-
"coreos-cloud"
|
434
|
-
when /debian/
|
435
|
-
"debian-cloud"
|
436
|
-
when /opensuse-cloud/
|
437
|
-
"opensuse-cloud"
|
438
|
-
when /rhel/
|
439
|
-
"rhel-cloud"
|
440
|
-
when /sles/
|
441
|
-
"suse-cloud"
|
442
|
-
when /ubuntu/
|
443
|
-
"ubuntu-os-cloud"
|
444
|
-
when /windows/
|
445
|
-
"windows-cloud"
|
446
|
-
end
|
367
|
+
def image_name_for_family(image_family)
|
368
|
+
image = connection.get_image_from_family(image_project, image_family)
|
369
|
+
image.name
|
447
370
|
end
|
448
371
|
|
449
372
|
def machine_type_url
|
@@ -78,13 +78,13 @@ describe Kitchen::Driver::Gce do
|
|
78
78
|
expect(driver.diagnose_plugin[:api_version]).to eq(2)
|
79
79
|
end
|
80
80
|
|
81
|
-
describe
|
81
|
+
describe "#name" do
|
82
82
|
it "has an overridden name" do
|
83
83
|
expect(driver.name).to eq("Google Compute (GCE)")
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
describe
|
87
|
+
describe "#create" do
|
88
88
|
let(:connection) { double("connection") }
|
89
89
|
let(:operation) { double("operation", name: "test_operation") }
|
90
90
|
let(:state) { {} }
|
@@ -151,7 +151,7 @@ describe Kitchen::Driver::Gce do
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
-
describe
|
154
|
+
describe "#destroy" do
|
155
155
|
let(:connection) { double("connection") }
|
156
156
|
let(:state) { { server_name: "server_1", hostname: "test_host", zone: "test_zone" } }
|
157
157
|
|
@@ -187,7 +187,7 @@ describe Kitchen::Driver::Gce do
|
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
-
describe
|
190
|
+
describe "#validate!" do
|
191
191
|
let(:config) do
|
192
192
|
{
|
193
193
|
project: "test_project",
|
@@ -217,14 +217,21 @@ describe Kitchen::Driver::Gce do
|
|
217
217
|
end
|
218
218
|
|
219
219
|
context "when neither zone nor region are specified" do
|
220
|
-
let(:config) { {} }
|
220
|
+
let(:config) { { image_name: "test_image" } }
|
221
221
|
it "raises an exception" do
|
222
222
|
expect { driver.validate! }.to raise_error(RuntimeError, "Either zone or region must be specified")
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
+
context "when neither image_family nor image_name are specified" do
|
227
|
+
let(:config) { { zone: "test_zone" } }
|
228
|
+
it "raises an exception" do
|
229
|
+
expect { driver.validate! }.to raise_error(RuntimeError, "Either image family or name must be specified")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
226
233
|
context "when zone and region are both set" do
|
227
|
-
let(:config) { { zone: "test_zone", region: "test_region" } }
|
234
|
+
let(:config) { { zone: "test_zone", region: "test_region", image_project: "test_project", image_name: "test_image" } }
|
228
235
|
|
229
236
|
it "warns the user that the region will be ignored" do
|
230
237
|
expect(driver).to receive(:warn).with("Both zone and region specified - region will be ignored.")
|
@@ -232,6 +239,15 @@ describe Kitchen::Driver::Gce do
|
|
232
239
|
end
|
233
240
|
end
|
234
241
|
|
242
|
+
context "when image family and name are both set" do
|
243
|
+
let(:config) { { image_project: "test_project", image_name: "test_image", image_family: "test_image_family", zone: "test_zone" } }
|
244
|
+
|
245
|
+
it "warns the user that the image family will be ignored" do
|
246
|
+
expect(driver).to receive(:warn).with("Both image family and name specified - image family will be ignored")
|
247
|
+
driver.validate!
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
235
251
|
context "when region is set to 'any'" do
|
236
252
|
let(:config) { { region: "any" } }
|
237
253
|
it "raises an exception" do
|
@@ -262,6 +278,7 @@ describe Kitchen::Driver::Gce do
|
|
262
278
|
{
|
263
279
|
project: "test_project",
|
264
280
|
zone: "test_zone",
|
281
|
+
image_name: "test_image",
|
265
282
|
machine_type: "test_machine_type",
|
266
283
|
disk_type: "test_disk_type",
|
267
284
|
network: "test_network",
|
@@ -306,7 +323,7 @@ describe Kitchen::Driver::Gce do
|
|
306
323
|
end
|
307
324
|
end
|
308
325
|
|
309
|
-
describe
|
326
|
+
describe "#connection" do
|
310
327
|
it "returns a properly configured ComputeService" do
|
311
328
|
compute_service = double("compute_service")
|
312
329
|
client_options = double("client_options")
|
@@ -324,7 +341,7 @@ describe Kitchen::Driver::Gce do
|
|
324
341
|
end
|
325
342
|
end
|
326
343
|
|
327
|
-
describe
|
344
|
+
describe "#authorization" do
|
328
345
|
it "returns a Google::Auth authorization object" do
|
329
346
|
auth_object = double("auth_object")
|
330
347
|
expect(Google::Auth).to receive(:get_application_default).and_return(auth_object)
|
@@ -332,7 +349,7 @@ describe Kitchen::Driver::Gce do
|
|
332
349
|
end
|
333
350
|
end
|
334
351
|
|
335
|
-
describe
|
352
|
+
describe "#winrm_transport?" do
|
336
353
|
it "returns true if the transport name is Winrm" do
|
337
354
|
expect(transport).to receive(:name).and_return("Winrm")
|
338
355
|
expect(driver.winrm_transport?).to eq(true)
|
@@ -344,7 +361,7 @@ describe Kitchen::Driver::Gce do
|
|
344
361
|
end
|
345
362
|
end
|
346
363
|
|
347
|
-
describe
|
364
|
+
describe "#update_windows_password" do
|
348
365
|
it "does not attempt to reset the password if the transport is not WinRM" do
|
349
366
|
expect(driver).to receive(:winrm_transport?).and_return(false)
|
350
367
|
expect(GoogleComputeWindowsPassword).not_to receive(:new)
|
@@ -374,14 +391,14 @@ describe Kitchen::Driver::Gce do
|
|
374
391
|
end
|
375
392
|
end
|
376
393
|
|
377
|
-
describe
|
394
|
+
describe "#check_api_call" do
|
378
395
|
it "returns false and logs a debug message if the block raises a ClientError" do
|
379
396
|
expect(driver).to receive(:debug).with("API error: whoops")
|
380
397
|
expect(driver.check_api_call { raise Google::Apis::ClientError.new("whoops") }).to eq(false)
|
381
398
|
end
|
382
399
|
|
383
400
|
it "raises an exception if the block raises something other than a ClientError" do
|
384
|
-
expect { driver.check_api_call { raise
|
401
|
+
expect { driver.check_api_call { raise "whoops" } }.to raise_error(RuntimeError)
|
385
402
|
end
|
386
403
|
|
387
404
|
it "returns true if the block does not raise an exception" do
|
@@ -389,47 +406,47 @@ describe Kitchen::Driver::Gce do
|
|
389
406
|
end
|
390
407
|
end
|
391
408
|
|
392
|
-
describe
|
409
|
+
describe "#valid_machine_type?" do
|
393
410
|
subject { driver.valid_machine_type? }
|
394
411
|
it_behaves_like "a validity checker", :machine_type, :get_machine_type, "test_project", "test_zone"
|
395
412
|
end
|
396
413
|
|
397
|
-
describe
|
414
|
+
describe "#valid_network?" do
|
398
415
|
subject { driver.valid_network? }
|
399
416
|
it_behaves_like "a validity checker", :network, :get_network, "test_project"
|
400
417
|
end
|
401
418
|
|
402
|
-
describe
|
419
|
+
describe "#valid_subnet?" do
|
403
420
|
subject { driver.valid_subnet? }
|
404
421
|
it_behaves_like "a validity checker", :subnet, :get_subnetwork, "test_project", "test_region"
|
405
422
|
end
|
406
423
|
|
407
|
-
describe
|
424
|
+
describe "#valid_zone?" do
|
408
425
|
subject { driver.valid_zone? }
|
409
426
|
it_behaves_like "a validity checker", :zone, :get_zone, "test_project"
|
410
427
|
end
|
411
428
|
|
412
|
-
describe
|
429
|
+
describe "#valid_region?" do
|
413
430
|
subject { driver.valid_region? }
|
414
431
|
it_behaves_like "a validity checker", :region, :get_region, "test_project"
|
415
432
|
end
|
416
433
|
|
417
|
-
describe
|
434
|
+
describe "#valid_disk_type?" do
|
418
435
|
subject { driver.valid_disk_type? }
|
419
436
|
it_behaves_like "a validity checker", :disk_type, :get_disk_type, "test_project", "test_zone"
|
420
437
|
end
|
421
438
|
|
422
|
-
describe
|
439
|
+
describe "#image_exist?" do
|
423
440
|
it "checks the outcome of the API call" do
|
424
441
|
connection = double("connection")
|
425
442
|
expect(driver).to receive(:connection).and_return(connection)
|
426
|
-
expect(connection).to receive(:get_image).with("
|
443
|
+
expect(connection).to receive(:get_image).with("test_project", "test_image")
|
427
444
|
expect(driver).to receive(:check_api_call).and_call_original
|
428
|
-
expect(driver.image_exist?
|
445
|
+
expect(driver.image_exist?).to eq(true)
|
429
446
|
end
|
430
447
|
end
|
431
448
|
|
432
|
-
describe
|
449
|
+
describe "#server_exist?" do
|
433
450
|
it "checks the outcome of the API call" do
|
434
451
|
expect(driver).to receive(:server_instance).with("server_1")
|
435
452
|
expect(driver).to receive(:check_api_call).and_call_original
|
@@ -437,7 +454,7 @@ describe Kitchen::Driver::Gce do
|
|
437
454
|
end
|
438
455
|
end
|
439
456
|
|
440
|
-
describe
|
457
|
+
describe "#project" do
|
441
458
|
it "returns the project from the config" do
|
442
459
|
allow(driver).to receive(:project).and_call_original
|
443
460
|
expect(driver).to receive(:config).and_return(project: "my_project")
|
@@ -445,7 +462,7 @@ describe Kitchen::Driver::Gce do
|
|
445
462
|
end
|
446
463
|
end
|
447
464
|
|
448
|
-
describe
|
465
|
+
describe "#region" do
|
449
466
|
it "returns the region from the config if specified" do
|
450
467
|
allow(driver).to receive(:region).and_call_original
|
451
468
|
allow(driver).to receive(:config).and_return(region: "my_region")
|
@@ -460,7 +477,7 @@ describe Kitchen::Driver::Gce do
|
|
460
477
|
end
|
461
478
|
end
|
462
479
|
|
463
|
-
describe
|
480
|
+
describe "#region_for_zone" do
|
464
481
|
it "returns the region for a given zone" do
|
465
482
|
connection = double("connection")
|
466
483
|
zone_obj = double("zone_obj", region: "/path/to/test_region")
|
@@ -471,7 +488,7 @@ describe Kitchen::Driver::Gce do
|
|
471
488
|
end
|
472
489
|
end
|
473
490
|
|
474
|
-
describe
|
491
|
+
describe "#zone" do
|
475
492
|
before do
|
476
493
|
allow(driver).to receive(:zone).and_call_original
|
477
494
|
end
|
@@ -505,7 +522,7 @@ describe Kitchen::Driver::Gce do
|
|
505
522
|
end
|
506
523
|
end
|
507
524
|
|
508
|
-
describe
|
525
|
+
describe "#find_zone" do
|
509
526
|
let(:zones_in_region) { double("zones_in_region") }
|
510
527
|
|
511
528
|
before do
|
@@ -525,7 +542,7 @@ describe Kitchen::Driver::Gce do
|
|
525
542
|
end
|
526
543
|
end
|
527
544
|
|
528
|
-
describe
|
545
|
+
describe "#zones_in_region" do
|
529
546
|
it "returns a correct list of available zones" do
|
530
547
|
zone1 = double("zone1", status: "UP", region: "a/b/c/test_region")
|
531
548
|
zone2 = double("zone2", status: "UP", region: "a/b/c/test_region")
|
@@ -542,7 +559,7 @@ describe Kitchen::Driver::Gce do
|
|
542
559
|
end
|
543
560
|
end
|
544
561
|
|
545
|
-
describe
|
562
|
+
describe "#server_instance" do
|
546
563
|
it "returns the instance from the API" do
|
547
564
|
connection = double("connection")
|
548
565
|
expect(driver).to receive(:connection).and_return(connection)
|
@@ -551,7 +568,7 @@ describe Kitchen::Driver::Gce do
|
|
551
568
|
end
|
552
569
|
end
|
553
570
|
|
554
|
-
describe
|
571
|
+
describe "#ip_address_for" do
|
555
572
|
it "returns the private IP if use_private_ip is true" do
|
556
573
|
expect(driver).to receive(:config).and_return(use_private_ip: true)
|
557
574
|
expect(driver).to receive(:private_ip_for).with("server").and_return("1.2.3.4")
|
@@ -565,7 +582,7 @@ describe Kitchen::Driver::Gce do
|
|
565
582
|
end
|
566
583
|
end
|
567
584
|
|
568
|
-
describe
|
585
|
+
describe "#private_ip_for" do
|
569
586
|
it "returns the IP address if it exists" do
|
570
587
|
network_interface = double("network_interface", network_ip: "1.2.3.4")
|
571
588
|
server = double("server", network_interfaces: [network_interface])
|
@@ -581,7 +598,7 @@ describe Kitchen::Driver::Gce do
|
|
581
598
|
end
|
582
599
|
end
|
583
600
|
|
584
|
-
describe
|
601
|
+
describe "#public_ip_for" do
|
585
602
|
it "returns the IP address if it exists" do
|
586
603
|
access_config = double("access_config", nat_ip: "4.3.2.1")
|
587
604
|
network_interface = double("network_interface", access_configs: [access_config])
|
@@ -599,7 +616,7 @@ describe Kitchen::Driver::Gce do
|
|
599
616
|
end
|
600
617
|
end
|
601
618
|
|
602
|
-
describe
|
619
|
+
describe "#generate_server_name" do
|
603
620
|
it "generates and returns a server name" do
|
604
621
|
expect(instance).to receive(:name).and_return("ABC123")
|
605
622
|
expect(SecureRandom).to receive(:hex).with(3).and_return("abcdef")
|
@@ -615,7 +632,7 @@ describe Kitchen::Driver::Gce do
|
|
615
632
|
end
|
616
633
|
end
|
617
634
|
|
618
|
-
describe
|
635
|
+
describe "#boot_disk" do
|
619
636
|
it "sets up a disk object and returns it" do
|
620
637
|
disk = double("disk")
|
621
638
|
params = double("params")
|
@@ -628,7 +645,7 @@ describe Kitchen::Driver::Gce do
|
|
628
645
|
|
629
646
|
allow(driver).to receive(:config).and_return(config)
|
630
647
|
expect(driver).to receive(:disk_type_url_for).with("test_type").and_return("disk_url")
|
631
|
-
expect(driver).to receive(:
|
648
|
+
expect(driver).to receive(:image_url).and_return("disk_image_url")
|
632
649
|
|
633
650
|
expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(disk)
|
634
651
|
expect(Google::Apis::ComputeV1::AttachedDiskInitializeParams).to receive(:new).and_return(params)
|
@@ -644,168 +661,55 @@ describe Kitchen::Driver::Gce do
|
|
644
661
|
end
|
645
662
|
end
|
646
663
|
|
647
|
-
describe
|
664
|
+
describe "#disk_type_url_for" do
|
648
665
|
it "returns a disk URL" do
|
649
666
|
expect(driver.disk_type_url_for("my_type")).to eq("zones/test_zone/diskTypes/my_type")
|
650
667
|
end
|
651
668
|
end
|
652
669
|
|
653
|
-
describe
|
670
|
+
describe "#image_name" do
|
654
671
|
before do
|
655
672
|
allow(driver).to receive(:config).and_return(config)
|
656
673
|
end
|
657
674
|
|
658
|
-
context "when the user supplies an image
|
675
|
+
context "when the user supplies an image name" do
|
659
676
|
let(:config) { { image_project: "my_image_project", image_name: "my_image" } }
|
660
677
|
|
661
|
-
it "returns the image
|
662
|
-
expect(driver).to
|
663
|
-
expect(driver.disk_image_url).to eq("image_url")
|
678
|
+
it "returns the image name supplied" do
|
679
|
+
expect(driver.image_name).to eq("my_image")
|
664
680
|
end
|
665
681
|
end
|
666
682
|
|
667
|
-
context "when the user
|
668
|
-
|
669
|
-
context "when the image provided is an alias" do
|
670
|
-
let(:config) { { image_name: "image_alias" } }
|
683
|
+
context "when the user supplies an image family" do
|
684
|
+
let(:config) { { image_project: "my_image_project", image_family: "my_image_family" } }
|
671
685
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
end
|
676
|
-
end
|
677
|
-
|
678
|
-
context "when the image provided is not an alias" do
|
679
|
-
let(:config) { { image_name: "my_image" } }
|
680
|
-
|
681
|
-
before do
|
682
|
-
expect(driver).to receive(:image_alias_url).and_return(nil)
|
683
|
-
end
|
684
|
-
|
685
|
-
context "when the image exists in the user's project" do
|
686
|
-
it "returns the image URL" do
|
687
|
-
expect(driver).to receive(:image_url_for).with(project, "my_image").and_return("image_url")
|
688
|
-
expect(driver.disk_image_url).to eq("image_url")
|
689
|
-
end
|
690
|
-
end
|
691
|
-
|
692
|
-
context "when the image does not exist in the user's project" do
|
693
|
-
before do
|
694
|
-
expect(driver).to receive(:image_url_for).with(project, "my_image").and_return(nil)
|
695
|
-
end
|
696
|
-
|
697
|
-
context "when the image matches a known public project" do
|
698
|
-
it "returns the image URL from the public project" do
|
699
|
-
expect(driver).to receive(:public_project_for_image).with("my_image").and_return("public_project")
|
700
|
-
expect(driver).to receive(:image_url_for).with("public_project", "my_image").and_return("image_url")
|
701
|
-
expect(driver.disk_image_url).to eq("image_url")
|
702
|
-
end
|
703
|
-
end
|
704
|
-
|
705
|
-
context "when the image does not match a known project" do
|
706
|
-
it "returns nil" do
|
707
|
-
expect(driver).to receive(:public_project_for_image).with("my_image").and_return(nil)
|
708
|
-
expect(driver).not_to receive(:image_url_for)
|
709
|
-
expect(driver.disk_image_url).to eq(nil)
|
710
|
-
end
|
711
|
-
end
|
712
|
-
end
|
686
|
+
it "returns the image found in the family" do
|
687
|
+
expect(driver).to receive(:image_name_for_family).with("my_image_family").and_return("my_image")
|
688
|
+
expect(driver.image_name).to eq("my_image")
|
713
689
|
end
|
714
690
|
end
|
715
691
|
end
|
716
692
|
|
717
|
-
describe
|
693
|
+
describe "#image_url" do
|
718
694
|
it "returns nil if the image does not exist" do
|
719
|
-
expect(driver).to receive(:image_exist?).
|
720
|
-
expect(driver.
|
695
|
+
expect(driver).to receive(:image_exist?).and_return(false)
|
696
|
+
expect(driver.image_url).to eq(nil)
|
721
697
|
end
|
722
698
|
|
723
699
|
it "returns a properly formatted image URL if the image exists" do
|
724
|
-
expect(driver).to receive(:image_exist?).
|
725
|
-
expect(driver.
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
|
-
describe '#image_alias_url' do
|
730
|
-
before do
|
731
|
-
allow(driver).to receive(:config).and_return(config)
|
732
|
-
end
|
733
|
-
|
734
|
-
context "when the image_alias is not a valid alias" do
|
735
|
-
let(:config) { { image_name: "fake_alias" } }
|
736
|
-
|
737
|
-
it "returns nil" do
|
738
|
-
expect(driver.image_alias_url).to eq(nil)
|
739
|
-
end
|
740
|
-
end
|
741
|
-
|
742
|
-
context "when the image_alias is a valid alias" do
|
743
|
-
let(:config) { { image_name: "centos-7" } }
|
744
|
-
let(:connection) { double("connection") }
|
745
|
-
|
746
|
-
before do
|
747
|
-
allow(driver).to receive(:connection).and_return(connection)
|
748
|
-
allow(connection).to receive(:list_images).and_return(response)
|
749
|
-
end
|
750
|
-
|
751
|
-
context "when the response contains no images" do
|
752
|
-
let(:response) { double("response", items: []) }
|
753
|
-
|
754
|
-
it "returns nil" do
|
755
|
-
expect(driver.image_alias_url).to eq(nil)
|
756
|
-
end
|
757
|
-
end
|
758
|
-
|
759
|
-
context "when the response contains images but none match the name" do
|
760
|
-
let(:image1) { double("image1", name: "centos-6-v20150101") }
|
761
|
-
let(:image2) { double("image2", name: "centos-6-v20150202") }
|
762
|
-
let(:image3) { double("image3", name: "ubuntu-14-v20150303") }
|
763
|
-
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
764
|
-
|
765
|
-
it "returns nil" do
|
766
|
-
expect(driver.image_alias_url).to eq(nil)
|
767
|
-
end
|
768
|
-
end
|
769
|
-
|
770
|
-
context "when the response contains images that match the name" do
|
771
|
-
let(:image1) { double("image1", name: "centos-7-v20160201", self_link: "image1_selflink") }
|
772
|
-
let(:image2) { double("image2", name: "centos-7-v20160301", self_link: "image2_selflink") }
|
773
|
-
let(:image3) { double("image3", name: "centos-6-v20160401", self_link: "image3_selflink") }
|
774
|
-
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
775
|
-
|
776
|
-
it "returns the link for image2 which is the most recent image" do
|
777
|
-
expect(driver.image_alias_url).to eq("image2_selflink")
|
778
|
-
end
|
779
|
-
end
|
780
|
-
end
|
781
|
-
end
|
782
|
-
|
783
|
-
describe '#public_project_for_image' do
|
784
|
-
{
|
785
|
-
"centos" => "centos-cloud",
|
786
|
-
"container-vm" => "google-containers",
|
787
|
-
"coreos" => "coreos-cloud",
|
788
|
-
"debian" => "debian-cloud",
|
789
|
-
"opensuse-cloud" => "opensuse-cloud",
|
790
|
-
"rhel" => "rhel-cloud",
|
791
|
-
"sles" => "suse-cloud",
|
792
|
-
"ubuntu" => "ubuntu-os-cloud",
|
793
|
-
"windows" => "windows-cloud",
|
794
|
-
}.each do |image_name, project_name|
|
795
|
-
it "returns project #{project_name} for an image named #{image_name}" do
|
796
|
-
expect(driver.public_project_for_image(image_name)).to eq(project_name)
|
797
|
-
end
|
700
|
+
expect(driver).to receive(:image_exist?).and_return(true)
|
701
|
+
expect(driver.image_url).to eq("projects/test_project/global/images/test_image")
|
798
702
|
end
|
799
703
|
end
|
800
704
|
|
801
|
-
describe
|
705
|
+
describe "#machine_type_url" do
|
802
706
|
it "returns a machine type URL" do
|
803
707
|
expect(driver).to receive(:config).and_return(machine_type: "machine_type")
|
804
708
|
expect(driver.machine_type_url).to eq("zones/test_zone/machineTypes/machine_type")
|
805
709
|
end
|
806
710
|
end
|
807
711
|
|
808
|
-
describe
|
712
|
+
describe "#instance_metadata" do
|
809
713
|
it "returns a properly-configured metadata object" do
|
810
714
|
item1 = double("item1")
|
811
715
|
item2 = double("item2")
|
@@ -830,7 +734,7 @@ describe Kitchen::Driver::Gce do
|
|
830
734
|
end
|
831
735
|
end
|
832
736
|
|
833
|
-
describe
|
737
|
+
describe "#env_user" do
|
834
738
|
it "returns the current user from the environment" do
|
835
739
|
expect(ENV).to receive(:[]).with("USER").and_return("test_user")
|
836
740
|
expect(driver.env_user).to eq("test_user")
|
@@ -842,7 +746,7 @@ describe Kitchen::Driver::Gce do
|
|
842
746
|
end
|
843
747
|
end
|
844
748
|
|
845
|
-
describe
|
749
|
+
describe "#instance_network_interfaces" do
|
846
750
|
let(:interface) { double("interface") }
|
847
751
|
|
848
752
|
before do
|
@@ -885,14 +789,14 @@ describe Kitchen::Driver::Gce do
|
|
885
789
|
end
|
886
790
|
end
|
887
791
|
|
888
|
-
describe
|
792
|
+
describe "#network_url" do
|
889
793
|
it "returns a network URL" do
|
890
794
|
expect(driver).to receive(:config).and_return(network: "test_network")
|
891
795
|
expect(driver.network_url).to eq("projects/test_project/global/networks/test_network")
|
892
796
|
end
|
893
797
|
end
|
894
798
|
|
895
|
-
describe
|
799
|
+
describe "#subnet_url_for" do
|
896
800
|
it "returns nil if no subnet is specified" do
|
897
801
|
expect(driver).to receive(:config).and_return({})
|
898
802
|
expect(driver.subnet_url).to eq(nil)
|
@@ -905,7 +809,7 @@ describe Kitchen::Driver::Gce do
|
|
905
809
|
end
|
906
810
|
end
|
907
811
|
|
908
|
-
describe
|
812
|
+
describe "#interface_access_configs" do
|
909
813
|
it "returns a properly-configured access config object" do
|
910
814
|
access_config = double("access_config")
|
911
815
|
|
@@ -923,7 +827,7 @@ describe Kitchen::Driver::Gce do
|
|
923
827
|
end
|
924
828
|
end
|
925
829
|
|
926
|
-
describe
|
830
|
+
describe "#instance_scheduling" do
|
927
831
|
it "returns a properly-configured scheduling object" do
|
928
832
|
scheduling = double("scheduling")
|
929
833
|
|
@@ -938,14 +842,14 @@ describe Kitchen::Driver::Gce do
|
|
938
842
|
end
|
939
843
|
end
|
940
844
|
|
941
|
-
describe
|
845
|
+
describe "#preemptible?" do
|
942
846
|
it "returns the preemptible setting from the config" do
|
943
847
|
expect(driver).to receive(:config).and_return(preemptible: "test_preempt")
|
944
848
|
expect(driver.preemptible?).to eq("test_preempt")
|
945
849
|
end
|
946
850
|
end
|
947
851
|
|
948
|
-
describe
|
852
|
+
describe "#auto_migrate?" do
|
949
853
|
it "returns false if the instance is preemptible" do
|
950
854
|
expect(driver).to receive(:preemptible?).and_return(true)
|
951
855
|
expect(driver.auto_migrate?).to eq(false)
|
@@ -958,7 +862,7 @@ describe Kitchen::Driver::Gce do
|
|
958
862
|
end
|
959
863
|
end
|
960
864
|
|
961
|
-
describe
|
865
|
+
describe "#auto_restart?" do
|
962
866
|
it "returns false if the instance is preemptible" do
|
963
867
|
expect(driver).to receive(:preemptible?).and_return(true)
|
964
868
|
expect(driver.auto_restart?).to eq(false)
|
@@ -971,7 +875,7 @@ describe Kitchen::Driver::Gce do
|
|
971
875
|
end
|
972
876
|
end
|
973
877
|
|
974
|
-
describe
|
878
|
+
describe "#migrate_setting" do
|
975
879
|
it "returns MIGRATE if auto_migrate is true" do
|
976
880
|
expect(driver).to receive(:auto_migrate?).and_return(true)
|
977
881
|
expect(driver.migrate_setting).to eq("MIGRATE")
|
@@ -983,7 +887,7 @@ describe Kitchen::Driver::Gce do
|
|
983
887
|
end
|
984
888
|
end
|
985
889
|
|
986
|
-
describe
|
890
|
+
describe "#instance_service_accounts" do
|
987
891
|
it "returns nil if service_account_scopes is nil" do
|
988
892
|
allow(driver).to receive(:config).and_return({})
|
989
893
|
expect(driver.instance_service_accounts).to eq(nil)
|
@@ -1011,7 +915,7 @@ describe Kitchen::Driver::Gce do
|
|
1011
915
|
end
|
1012
916
|
end
|
1013
917
|
|
1014
|
-
describe
|
918
|
+
describe "#service_account_scope_url" do
|
1015
919
|
it "returns the passed-in scope if it already looks like a scope URL" do
|
1016
920
|
scope = "https://www.googleapis.com/auth/fake_scope"
|
1017
921
|
expect(driver.service_account_scope_url(scope)).to eq(scope)
|
@@ -1023,7 +927,7 @@ describe Kitchen::Driver::Gce do
|
|
1023
927
|
end
|
1024
928
|
end
|
1025
929
|
|
1026
|
-
describe
|
930
|
+
describe "#translate_scope_alias" do
|
1027
931
|
it "returns a scope for a given alias" do
|
1028
932
|
expect(driver.translate_scope_alias("storage-rw")).to eq("devstorage.read_write")
|
1029
933
|
end
|
@@ -1033,7 +937,7 @@ describe Kitchen::Driver::Gce do
|
|
1033
937
|
end
|
1034
938
|
end
|
1035
939
|
|
1036
|
-
describe
|
940
|
+
describe "#instance_tags" do
|
1037
941
|
it "returns a properly-formatted tags object" do
|
1038
942
|
tags_obj = double("tags_obj")
|
1039
943
|
|
@@ -1045,21 +949,21 @@ describe Kitchen::Driver::Gce do
|
|
1045
949
|
end
|
1046
950
|
end
|
1047
951
|
|
1048
|
-
describe
|
952
|
+
describe "#wait_time" do
|
1049
953
|
it "returns the configured wait time" do
|
1050
954
|
expect(driver).to receive(:config).and_return(wait_time: 123)
|
1051
955
|
expect(driver.wait_time).to eq(123)
|
1052
956
|
end
|
1053
957
|
end
|
1054
958
|
|
1055
|
-
describe
|
959
|
+
describe "#refresh_rate" do
|
1056
960
|
it "returns the configured refresh rate" do
|
1057
961
|
expect(driver).to receive(:config).and_return(refresh_rate: 321)
|
1058
962
|
expect(driver.refresh_rate).to eq(321)
|
1059
963
|
end
|
1060
964
|
end
|
1061
965
|
|
1062
|
-
describe
|
966
|
+
describe "#wait_for_status" do
|
1063
967
|
let(:item) { double("item") }
|
1064
968
|
|
1065
969
|
before do
|
@@ -1103,7 +1007,7 @@ describe Kitchen::Driver::Gce do
|
|
1103
1007
|
end
|
1104
1008
|
end
|
1105
1009
|
|
1106
|
-
describe
|
1010
|
+
describe "#wait_for_operation" do
|
1107
1011
|
let(:operation) { double("operation", name: "operation-123") }
|
1108
1012
|
|
1109
1013
|
it "raises a properly-formatted exception when errors exist" do
|
@@ -1126,7 +1030,7 @@ describe Kitchen::Driver::Gce do
|
|
1126
1030
|
end
|
1127
1031
|
end
|
1128
1032
|
|
1129
|
-
describe
|
1033
|
+
describe "#zone_operation" do
|
1130
1034
|
it "fetches the operation from the API and returns it" do
|
1131
1035
|
connection = double("connection")
|
1132
1036
|
expect(driver).to receive(:connection).and_return(connection)
|
@@ -1135,7 +1039,7 @@ describe Kitchen::Driver::Gce do
|
|
1135
1039
|
end
|
1136
1040
|
end
|
1137
1041
|
|
1138
|
-
describe
|
1042
|
+
describe "#operation_errors" do
|
1139
1043
|
let(:operation) { double("operation") }
|
1140
1044
|
let(:error_obj) { double("error_obj") }
|
1141
1045
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-google
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Leonard
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
165
|
version: '0'
|
166
166
|
requirements: []
|
167
167
|
rubyforge_project:
|
168
|
-
rubygems_version: 2.
|
168
|
+
rubygems_version: 2.6.10
|
169
169
|
signing_key:
|
170
170
|
specification_version: 4
|
171
171
|
summary: Kitchen::Driver::Gce
|