kitchen-vcair 1.0.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 +7 -0
- data/.gitignore +21 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +201 -0
- data/README.md +188 -0
- data/examples/VIDEOS.md +57 -0
- data/examples/windows_customization.bat +53 -0
- data/kitchen-vcair.gemspec +32 -0
- data/lib/kitchen/driver/vcair.rb +359 -0
- data/lib/kitchen/driver/vcair_version.rb +25 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/vcair_spec.rb +825 -0
- metadata +167 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Authors:: Chris McClimans (<c@vulk.co>)
|
|
4
|
+
# Authors:: Taylor Carpenter (<t@vulk.co>)
|
|
5
|
+
# Authors:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
6
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
|
7
|
+
# License:: Apache License, Version 2.0
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
|
|
21
|
+
module Kitchen
|
|
22
|
+
module Driver
|
|
23
|
+
VCAIR_VERSION = '1.0.0'
|
|
24
|
+
end
|
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Authors:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
|
5
|
+
# License:: Apache License, Version 2.0
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
data/spec/vcair_spec.rb
ADDED
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
# Encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Authors:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
|
5
|
+
# License:: Apache License, Version 2.0
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require 'spec_helper'
|
|
20
|
+
require 'excon'
|
|
21
|
+
require 'kitchen/driver/vcair'
|
|
22
|
+
require 'kitchen/provisioner/dummy'
|
|
23
|
+
require 'kitchen/transport/dummy'
|
|
24
|
+
require 'kitchen/verifier/dummy'
|
|
25
|
+
|
|
26
|
+
describe Kitchen::Driver::Vcair do
|
|
27
|
+
let(:logged_output) { StringIO.new }
|
|
28
|
+
let(:logger) { Logger.new(logged_output) }
|
|
29
|
+
let(:platform) { Kitchen::Platform.new(name: 'fake_platform') }
|
|
30
|
+
let(:transport) { Kitchen::Transport::Dummy.new }
|
|
31
|
+
let(:driver) { Kitchen::Driver::Vcair.new(config) }
|
|
32
|
+
|
|
33
|
+
let(:config) do
|
|
34
|
+
{
|
|
35
|
+
vcair_username: 'myuser',
|
|
36
|
+
vcair_password: 'mypassword',
|
|
37
|
+
vcair_api_host: 'https://vcloud.air',
|
|
38
|
+
vcair_api_path: '/api/compute/api',
|
|
39
|
+
vcair_org: 'myorg',
|
|
40
|
+
cpus: 2,
|
|
41
|
+
memory: 2048,
|
|
42
|
+
vdc_id: 1,
|
|
43
|
+
catalog_id: 2,
|
|
44
|
+
image_id: 3,
|
|
45
|
+
network_id: 4
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
let(:instance) do
|
|
50
|
+
instance_double(Kitchen::Instance,
|
|
51
|
+
logger: logger,
|
|
52
|
+
transport: transport,
|
|
53
|
+
platform: platform,
|
|
54
|
+
to_str: 'instance_str'
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
before do
|
|
59
|
+
allow(driver).to receive(:instance).and_return(instance)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#create' do
|
|
63
|
+
context 'when the server is already created' do
|
|
64
|
+
let(:state) { { vapp_id: 'vapp1' } }
|
|
65
|
+
it 'does not call create_server' do
|
|
66
|
+
expect(driver).not_to receive(:create_server)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when the server is not yet created' do
|
|
71
|
+
let(:state) { {} }
|
|
72
|
+
let(:vm) { double('vm') }
|
|
73
|
+
|
|
74
|
+
it 'calls the expected methods' do
|
|
75
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
76
|
+
allow(driver.vm).to receive(:ip_address).and_return('1.2.3.4')
|
|
77
|
+
|
|
78
|
+
expect(driver).to receive(:validate!)
|
|
79
|
+
expect(driver).to receive(:create_server).with(state)
|
|
80
|
+
expect(driver.vm).to receive(:wait_for)
|
|
81
|
+
expect(driver).to receive(:wait_for_server).with(state)
|
|
82
|
+
|
|
83
|
+
driver.create(state)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe '#destroy' do
|
|
89
|
+
context 'when the server does not exist' do
|
|
90
|
+
let(:state) { {} }
|
|
91
|
+
it 'does not fetch the vapp' do
|
|
92
|
+
expect(driver).not_to receive(:vapp)
|
|
93
|
+
|
|
94
|
+
driver.destroy(state)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'when the server exists' do
|
|
99
|
+
let(:state) { { vapp_id: 'vapp1' } }
|
|
100
|
+
let(:vapp) { double('vapp') }
|
|
101
|
+
before do
|
|
102
|
+
allow(driver).to receive(:validate!)
|
|
103
|
+
allow(driver).to receive(:vapp).and_return(vapp)
|
|
104
|
+
allow(vapp).to receive(:power_off)
|
|
105
|
+
allow(vapp).to receive(:undeploy)
|
|
106
|
+
allow(vapp).to receive(:destroy)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'calls validate!' do
|
|
110
|
+
expect(driver).to receive(:validate!)
|
|
111
|
+
driver.destroy(state)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'sets the vapp_id' do
|
|
115
|
+
expect(driver).to receive(:vapp_id=).with('vapp1')
|
|
116
|
+
driver.destroy(state)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context 'when the vapp does not exist' do
|
|
120
|
+
it 'does not call vapp again to power_off, etc.' do
|
|
121
|
+
expect(driver).to receive(:vapp).once.and_raise(Fog::Compute::VcloudDirector::Forbidden)
|
|
122
|
+
driver.destroy(state)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'fetches the vapp' do
|
|
127
|
+
expect(driver).to receive(:vapp)
|
|
128
|
+
driver.destroy(state)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'gets rid of the vapp' do
|
|
132
|
+
expect(driver.vapp).to receive(:power_off)
|
|
133
|
+
expect(driver.vapp).to receive(:undeploy)
|
|
134
|
+
expect(driver.vapp).to receive(:destroy)
|
|
135
|
+
|
|
136
|
+
driver.destroy(state)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe '#vcloud_client' do
|
|
142
|
+
let(:fog_server_def) { double('fog_server_def') }
|
|
143
|
+
let(:client) { double('client') }
|
|
144
|
+
|
|
145
|
+
before do
|
|
146
|
+
allow(driver).to receive(:fog_server_def).and_return(fog_server_def)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'sets up a new Fog::Compute instance' do
|
|
150
|
+
expect(Fog::Compute).to receive(:new).with(fog_server_def).and_return(client)
|
|
151
|
+
expect(driver.vcloud_client).to eq(client)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'raises an error if unauthorized' do
|
|
155
|
+
allow(Fog::Compute).to receive(:new).and_raise(Excon::Errors::Unauthorized, 'auth failed')
|
|
156
|
+
expect { driver.vcloud_client }.to raise_error(
|
|
157
|
+
RuntimeError,
|
|
158
|
+
'Connection failure, please check your username and password. -- auth failed'
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe '#org' do
|
|
164
|
+
let(:client) { double('client') }
|
|
165
|
+
let(:organizations) { double('organizations') }
|
|
166
|
+
let(:org) { double('org') }
|
|
167
|
+
|
|
168
|
+
it 'fetches the organization by name' do
|
|
169
|
+
allow(driver).to receive(:vcloud_client).and_return(client)
|
|
170
|
+
allow(client).to receive(:organizations).and_return(organizations)
|
|
171
|
+
expect(organizations).to receive(:get_by_name).with('myorg').and_return(org)
|
|
172
|
+
expect(driver.org).to eq(org)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe '#create_server' do
|
|
177
|
+
let(:state) { {} }
|
|
178
|
+
|
|
179
|
+
it 'calls the expected methods' do
|
|
180
|
+
expect(driver).to receive(:instantiate).and_return('vapp1')
|
|
181
|
+
expect(driver).to receive(:vapp_id=).with('vapp1')
|
|
182
|
+
expect(driver).to receive(:validate_vapp).and_return(true)
|
|
183
|
+
expect(driver).to receive(:update_customization)
|
|
184
|
+
expect(driver).to receive(:adjust_hardware)
|
|
185
|
+
expect(driver).to receive(:attach_network)
|
|
186
|
+
expect(driver).to receive(:tag_vm)
|
|
187
|
+
expect(driver).to receive(:power_on)
|
|
188
|
+
|
|
189
|
+
driver.create_server(state)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context 'when the vapp validation fails' do
|
|
193
|
+
before do
|
|
194
|
+
allow(driver).to receive(:instantiate)
|
|
195
|
+
allow(driver).to receive(:validate_vapp).and_return(false)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'destroys the server' do
|
|
199
|
+
expect(driver).to receive(:destroy).with(state)
|
|
200
|
+
driver.create_server(state)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'does not power on the vapp' do
|
|
204
|
+
expect(driver).not_to receive(:power_on)
|
|
205
|
+
driver.create_server(state)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
describe '#adjust_hardware' do
|
|
211
|
+
let(:vm) { double('vm') }
|
|
212
|
+
before do
|
|
213
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
context 'when config parameters are supplied' do
|
|
217
|
+
it 'sets the cpus and memory on the VM' do
|
|
218
|
+
expect(vm).to receive(:cpu=).with(2)
|
|
219
|
+
expect(vm).to receive(:memory=).with(2048)
|
|
220
|
+
|
|
221
|
+
driver.adjust_hardware
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
context 'when config parameters are not supplied' do
|
|
226
|
+
before do
|
|
227
|
+
config[:cpus] = nil
|
|
228
|
+
config[:memory] = nil
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it 'does not set the cpus and memory on the VM' do
|
|
232
|
+
expect(vm).not_to receive(:cpu=)
|
|
233
|
+
expect(vm).not_to receive(:memory=)
|
|
234
|
+
|
|
235
|
+
driver.adjust_hardware
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
describe '#attach_network' do
|
|
241
|
+
let(:client) { double('client') }
|
|
242
|
+
let(:payload) { {} }
|
|
243
|
+
let(:task) { double('task', body: 'body text') }
|
|
244
|
+
let(:vm) { double('vm', id: 'vm1') }
|
|
245
|
+
|
|
246
|
+
it 'submits the request and processes it' do
|
|
247
|
+
allow(driver).to receive(:vcloud_client).and_return(client)
|
|
248
|
+
allow(driver).to receive(:attach_network_payload).and_return(payload)
|
|
249
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
250
|
+
|
|
251
|
+
expect(client).to receive(:put_network_connection_system_section_vapp)
|
|
252
|
+
.with('vm1', payload).and_return(task)
|
|
253
|
+
expect(client).to receive(:process_task).with('body text')
|
|
254
|
+
|
|
255
|
+
driver.attach_network
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
describe '#tag_vm' do
|
|
260
|
+
let(:vm) { double('vm') }
|
|
261
|
+
let(:tags) { double('tags') }
|
|
262
|
+
|
|
263
|
+
it 'tags the VM' do
|
|
264
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
265
|
+
allow(vm).to receive(:tags).and_return(tags)
|
|
266
|
+
expect(tags).to receive(:create).with('created-by', 'test-kitchen')
|
|
267
|
+
|
|
268
|
+
driver.tag_vm
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
describe '#power_on' do
|
|
273
|
+
let(:vapp) { double('vapp') }
|
|
274
|
+
|
|
275
|
+
it 'powers on the vApp' do
|
|
276
|
+
allow(driver).to receive(:vapp).and_return(vapp)
|
|
277
|
+
expect(vapp).to receive(:power_on)
|
|
278
|
+
|
|
279
|
+
driver.power_on
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
describe '#wait_for_server' do
|
|
284
|
+
let(:connection) { instance.transport.connection(state) }
|
|
285
|
+
let(:state) { {} }
|
|
286
|
+
let(:vapp) { double('vapp', id: 'vapp1') }
|
|
287
|
+
let(:vm) { double('vm', name: 'vm1') }
|
|
288
|
+
|
|
289
|
+
before do
|
|
290
|
+
allow(transport).to receive(:connection).and_return(connection)
|
|
291
|
+
allow(driver).to receive(:vapp).and_return(vapp)
|
|
292
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it 'calls wait_until_ready on the transport connection' do
|
|
296
|
+
expect(connection).to receive(:wait_until_ready)
|
|
297
|
+
driver.wait_for_server(state)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it 'destroys the vApp if the server failed to become ready' do
|
|
301
|
+
allow(connection).to receive(:wait_until_ready).and_raise(RuntimeError)
|
|
302
|
+
expect(driver).to receive(:destroy).with(state)
|
|
303
|
+
expect { driver.wait_for_server(state) }.to raise_error(RuntimeError)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
describe '#vcloud_username' do
|
|
308
|
+
it 'returns a properly-formatted username' do
|
|
309
|
+
expect(driver.vcloud_username).to eq('myuser@myorg')
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
describe '#image' do
|
|
314
|
+
let(:catalog) { double('catalog') }
|
|
315
|
+
let(:catalog_items) { double('catalog_items') }
|
|
316
|
+
|
|
317
|
+
before do
|
|
318
|
+
allow(driver).to receive(:catalog).and_return(catalog)
|
|
319
|
+
allow(catalog).to receive(:catalog_items).and_return(catalog_items)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
context 'when an ID is provided' do
|
|
323
|
+
before do
|
|
324
|
+
config[:image_id] = 1
|
|
325
|
+
config[:image_name] = nil
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'fetches the catalog item by ID' do
|
|
329
|
+
expect(catalog_items).to receive(:get).with(1)
|
|
330
|
+
driver.image
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
context 'when a name is provided' do
|
|
335
|
+
before do
|
|
336
|
+
config[:image_id] = nil
|
|
337
|
+
config[:image_name] = 'image name'
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it 'fetches the catalog item by name' do
|
|
341
|
+
expect(catalog_items).to receive(:get_by_name).with('image name')
|
|
342
|
+
driver.image
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
describe '#catalog' do
|
|
348
|
+
let(:org) { double('org') }
|
|
349
|
+
let(:catalogs) { double('catalogs') }
|
|
350
|
+
|
|
351
|
+
before do
|
|
352
|
+
allow(driver).to receive(:org).and_return(org)
|
|
353
|
+
allow(org).to receive(:catalogs).and_return(catalogs)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
context 'when an ID is provided' do
|
|
357
|
+
before do
|
|
358
|
+
config[:catalog_id] = 1
|
|
359
|
+
config[:catalog_name] = nil
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
it 'fetches the catalog by ID' do
|
|
363
|
+
expect(catalogs).to receive(:get).with(1)
|
|
364
|
+
driver.catalog
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
context 'when a name is provided' do
|
|
369
|
+
before do
|
|
370
|
+
config[:catalog_id] = nil
|
|
371
|
+
config[:catalog_name] = 'catalog name'
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it 'fetches the catalog by name' do
|
|
375
|
+
expect(catalogs).to receive(:get_by_name).with('catalog name')
|
|
376
|
+
driver.catalog
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
describe '#vdc' do
|
|
382
|
+
let(:org) { double('org') }
|
|
383
|
+
let(:vdcs) { double('vdcs') }
|
|
384
|
+
|
|
385
|
+
before do
|
|
386
|
+
allow(driver).to receive(:org).and_return(org)
|
|
387
|
+
allow(org).to receive(:vdcs).and_return(vdcs)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
context 'when an ID is provided' do
|
|
391
|
+
before do
|
|
392
|
+
config[:vdc_id] = 1
|
|
393
|
+
config[:vdc_name] = nil
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
it 'fetches the vdc by ID' do
|
|
397
|
+
expect(vdcs).to receive(:get).with(1)
|
|
398
|
+
driver.vdc
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
context 'when a name is provided' do
|
|
403
|
+
before do
|
|
404
|
+
config[:vdc_id] = nil
|
|
405
|
+
config[:vdc_name] = 'vdc name'
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it 'fetches the vdc by name' do
|
|
409
|
+
expect(vdcs).to receive(:get_by_name).with('vdc name')
|
|
410
|
+
driver.vdc
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
describe '#network' do
|
|
416
|
+
let(:org) { double('org') }
|
|
417
|
+
let(:networks) { double('networks') }
|
|
418
|
+
|
|
419
|
+
before do
|
|
420
|
+
allow(driver).to receive(:org).and_return(org)
|
|
421
|
+
allow(org).to receive(:networks).and_return(networks)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
context 'when an ID is provided' do
|
|
425
|
+
before do
|
|
426
|
+
config[:network_id] = 1
|
|
427
|
+
config[:network_name] = nil
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it 'fetches the network by ID' do
|
|
431
|
+
expect(networks).to receive(:get).with(1)
|
|
432
|
+
driver.network
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
context 'when a name is provided' do
|
|
437
|
+
before do
|
|
438
|
+
config[:network_id] = nil
|
|
439
|
+
config[:network_name] = 'network name'
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it 'fetches the network by name' do
|
|
443
|
+
expect(networks).to receive(:get_by_name).with('network name')
|
|
444
|
+
driver.network
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
describe '#node_description' do
|
|
450
|
+
context 'when a node description is provided' do
|
|
451
|
+
before do
|
|
452
|
+
config[:node_description] = 'sample description'
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it 'returns the configured description' do
|
|
456
|
+
expect(driver.node_description).to eq('sample description')
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
context 'when a node description is not provided' do
|
|
461
|
+
it 'returns the default description' do
|
|
462
|
+
allow(driver).to receive(:node_name).and_return('node')
|
|
463
|
+
expect(driver.node_description).to eq('Test Kitchen: node')
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
describe '#node_name' do
|
|
469
|
+
context 'when a node name is provided' do
|
|
470
|
+
before do
|
|
471
|
+
config[:node_name] = 'testnode'
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
it 'returns the configured node name' do
|
|
475
|
+
expect(driver.node_name).to eq('testnode')
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
context 'when a node name is not provided' do
|
|
480
|
+
it 'returns a generated node name' do
|
|
481
|
+
expect(driver).to receive(:generate_node_name).and_return('a12345')
|
|
482
|
+
expect(driver.node_name).to eq('a12345')
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
describe '#generate_node_name' do
|
|
488
|
+
it 'generates a node name using SecureRandom' do
|
|
489
|
+
expect(SecureRandom).to receive(:hex).with(6).and_return('randomchars')
|
|
490
|
+
expect(driver.generate_node_name).to eq('tk-randomchars')
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
describe '#print_error_and_exit' do
|
|
495
|
+
it 'prints an error message and raises an exception' do
|
|
496
|
+
expect(driver).to receive(:error).with('error text')
|
|
497
|
+
expect { driver.print_error_and_exit('error text') }.to raise_error(RuntimeError)
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
describe '#validate!' do
|
|
502
|
+
it 'calls all the expected validate methods' do
|
|
503
|
+
expect(driver).to receive(:validate_parameter_pair!).with('vdc')
|
|
504
|
+
expect(driver).to receive(:validate_parameter_pair!).with('catalog')
|
|
505
|
+
expect(driver).to receive(:validate_parameter_pair!).with('image')
|
|
506
|
+
expect(driver).to receive(:validate_parameter_pair!).with('network')
|
|
507
|
+
|
|
508
|
+
expect(driver).to receive(:validate_method!).with(:org)
|
|
509
|
+
expect(driver).to receive(:validate_method!).with(:vdc)
|
|
510
|
+
expect(driver).to receive(:validate_method!).with(:catalog)
|
|
511
|
+
expect(driver).to receive(:validate_method!).with(:image)
|
|
512
|
+
expect(driver).to receive(:validate_method!).with(:network)
|
|
513
|
+
|
|
514
|
+
expect(driver).to receive(:validate_customization_script!)
|
|
515
|
+
expect(driver).to receive(:validate_computer_name!)
|
|
516
|
+
|
|
517
|
+
driver.validate!
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
describe '#validate_parameter_pair!' do
|
|
522
|
+
context 'when an ID exists but not a name' do
|
|
523
|
+
before do
|
|
524
|
+
config[:test_id] = 1
|
|
525
|
+
config[:test_name] = nil
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
it 'does not print an error' do
|
|
529
|
+
expect(driver).not_to receive(:print_error_and_exit)
|
|
530
|
+
driver.validate_parameter_pair!('test')
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
context 'when a name exists but not an ID' do
|
|
535
|
+
before do
|
|
536
|
+
config[:test_id] = nil
|
|
537
|
+
config[:test_name] = 'test'
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
it 'does not print an error' do
|
|
541
|
+
expect(driver).not_to receive(:print_error_and_exit)
|
|
542
|
+
driver.validate_parameter_pair!('test')
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
context 'when neither a name nor an ID exists' do
|
|
547
|
+
before do
|
|
548
|
+
config[:test_id] = nil
|
|
549
|
+
config[:test_name] = nil
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
it 'prints an error' do
|
|
553
|
+
expect(driver).to receive(:print_error_and_exit)
|
|
554
|
+
driver.validate_parameter_pair!('test')
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
describe '#validate_method!' do
|
|
560
|
+
context 'when the method is successful' do
|
|
561
|
+
it 'does not raise an exception' do
|
|
562
|
+
allow(driver).to receive(:test_method)
|
|
563
|
+
expect { driver.validate_method!(:test_method) }.not_to raise_error
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
context 'when the method is not successful' do
|
|
568
|
+
it 'raises an exception' do
|
|
569
|
+
allow(driver).to receive(:test_method).and_raise(RuntimeError)
|
|
570
|
+
expect { driver.validate_method!(:test_method) }.to raise_error(RuntimeError)
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
describe '#validate_computer_name' do
|
|
576
|
+
it 'allows an alphanumeric 15-char string' do
|
|
577
|
+
allow(driver).to receive(:node_name).and_return('a12345678901234')
|
|
578
|
+
expect(driver).not_to receive(:print_error_and_exit)
|
|
579
|
+
|
|
580
|
+
driver.validate_computer_name!
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it 'does not allow a computer name that only has numbers' do
|
|
584
|
+
allow(driver).to receive(:node_name).and_return('12345')
|
|
585
|
+
expect(driver).to receive(:print_error_and_exit)
|
|
586
|
+
|
|
587
|
+
driver.validate_computer_name!
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
it 'does not allow a 16 character name' do
|
|
591
|
+
allow(driver).to receive(:node_name).and_return('a123456789012345')
|
|
592
|
+
expect(driver).to receive(:print_error_and_exit)
|
|
593
|
+
|
|
594
|
+
driver.validate_computer_name!
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it 'does not allow a hyphen at the end' do
|
|
598
|
+
allow(driver).to receive(:node_name).and_return('a12345-')
|
|
599
|
+
expect(driver).to receive(:print_error_and_exit)
|
|
600
|
+
|
|
601
|
+
driver.validate_computer_name!
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
describe '#validate_customization_script!' do
|
|
606
|
+
context 'when no customization script has been configured' do
|
|
607
|
+
before do
|
|
608
|
+
config[:customization_script] = nil
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
it 'does not print an error' do
|
|
612
|
+
expect(driver).not_to receive(:print_error_and_exit)
|
|
613
|
+
|
|
614
|
+
driver.validate_customization_script!
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
context 'when a script is configured and is readable' do
|
|
619
|
+
before do
|
|
620
|
+
config[:customization_script] = '/path/to/script'
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
it 'does not print an error' do
|
|
624
|
+
expect(File).to receive(:readable?).with('/path/to/script').and_return(true)
|
|
625
|
+
expect(driver).not_to receive(:print_error_and_exit)
|
|
626
|
+
|
|
627
|
+
driver.validate_customization_script!
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
context 'when a script is configured but is not readable' do
|
|
632
|
+
before do
|
|
633
|
+
config[:customization_script] = '/path/to/script'
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
it 'does not print an error' do
|
|
637
|
+
expect(File).to receive(:readable?).with('/path/to/script').and_return(false)
|
|
638
|
+
expect(driver).to receive(:print_error_and_exit)
|
|
639
|
+
|
|
640
|
+
driver.validate_customization_script!
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
describe '#instantiate' do
|
|
646
|
+
let(:image) { double('image') }
|
|
647
|
+
|
|
648
|
+
it 'calls instantiate on the image' do
|
|
649
|
+
allow(driver).to receive(:image).and_return(image)
|
|
650
|
+
allow(driver).to receive(:node_name).and_return('node')
|
|
651
|
+
allow(driver).to receive(:instantiate_config).and_return('config')
|
|
652
|
+
expect(image).to receive(:instantiate).with('node', 'config')
|
|
653
|
+
|
|
654
|
+
driver.instantiate
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
describe '#vapp' do
|
|
659
|
+
let(:vdc) { double('vdc') }
|
|
660
|
+
let(:vapps) { double('vapps') }
|
|
661
|
+
let(:vapp) { double('vapp') }
|
|
662
|
+
|
|
663
|
+
it 'gets the vApp by ID' do
|
|
664
|
+
allow(driver).to receive(:vapp_id).and_return('vapp1')
|
|
665
|
+
allow(driver).to receive(:vdc).and_return(vdc)
|
|
666
|
+
allow(vdc).to receive(:vapps).and_return(vapps)
|
|
667
|
+
expect(vapps).to receive(:get).with('vapp1').and_return(vapp)
|
|
668
|
+
expect(driver.vapp).to eq(vapp)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
describe '#vm' do
|
|
673
|
+
let(:vapp) { double('vapp') }
|
|
674
|
+
let(:vms) { %w(vm1 vm2) }
|
|
675
|
+
|
|
676
|
+
it 'returns the first VM from the array' do
|
|
677
|
+
allow(driver).to receive(:vapp).and_return(vapp)
|
|
678
|
+
allow(vapp).to receive(:vms).and_return(vms)
|
|
679
|
+
|
|
680
|
+
expect(driver.vm).to eq('vm1')
|
|
681
|
+
end
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
describe '#validate_vapp' do
|
|
685
|
+
let(:vapp) { double('vapp') }
|
|
686
|
+
before do
|
|
687
|
+
allow(driver).to receive(:vapp).and_return(vapp)
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
it 'returns true when only 1 VM is present' do
|
|
691
|
+
allow(vapp).to receive(:vms).and_return([1])
|
|
692
|
+
expect(driver.validate_vapp).to eq(true)
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
it 'returns false when 0 VMs are present' do
|
|
696
|
+
allow(vapp).to receive(:vms).and_return([])
|
|
697
|
+
expect(driver.validate_vapp).to eq(false)
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
it 'returns false when >1 VMs are present' do
|
|
701
|
+
allow(vapp).to receive(:vms).and_return([1, 2])
|
|
702
|
+
expect(driver.validate_vapp).to eq(false)
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
describe '#customization' do
|
|
707
|
+
let(:vm) { double('vm') }
|
|
708
|
+
let(:customization) { double('customization') }
|
|
709
|
+
it 'fetches the customization from the VM' do
|
|
710
|
+
allow(driver).to receive(:vm).and_return(vm)
|
|
711
|
+
expect(vm).to receive(:customization).and_return(customization)
|
|
712
|
+
expect(driver.customization).to eq(customization)
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
describe '#update_customization' do
|
|
717
|
+
before do
|
|
718
|
+
allow(driver).to receive(:set_customization_script)
|
|
719
|
+
allow(driver).to receive(:set_customization_password)
|
|
720
|
+
allow(driver).to receive(:set_customization_computer_name)
|
|
721
|
+
allow(driver).to receive(:save_customization)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
context 'when a customization script is provided' do
|
|
725
|
+
before do
|
|
726
|
+
config[:customization_script] = '/path/to/script'
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
it 'calls set_customization_script' do
|
|
730
|
+
expect(driver).to receive(:set_customization_script)
|
|
731
|
+
driver.update_customization
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
context 'when a customization script is not provided' do
|
|
736
|
+
before do
|
|
737
|
+
config[:customization_script] = nil
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
it 'does not call set_customization_script' do
|
|
741
|
+
expect(driver).not_to receive(:set_customization_script)
|
|
742
|
+
driver.update_customization
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
it 'calls the expected methods' do
|
|
747
|
+
expect(driver).to receive(:set_customization_password)
|
|
748
|
+
expect(driver).to receive(:set_customization_computer_name)
|
|
749
|
+
expect(driver).to receive(:save_customization)
|
|
750
|
+
driver.update_customization
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
describe '#set_customization_script' do
|
|
755
|
+
let(:customization) { double('customization') }
|
|
756
|
+
|
|
757
|
+
before do
|
|
758
|
+
config[:customization_script] = '/path/to/script'
|
|
759
|
+
allow(driver).to receive(:customization).and_return(customization)
|
|
760
|
+
allow(File).to receive(:read).with('/path/to/script').and_return('script body')
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
it 'sets the customization script to the file contents' do
|
|
764
|
+
expect(customization).to receive(:script=).with('script body')
|
|
765
|
+
driver.set_customization_script
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
describe '#set_customization_password' do
|
|
770
|
+
let(:customization) { double('customization') }
|
|
771
|
+
before do
|
|
772
|
+
allow(driver).to receive(:customization).and_return(customization)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
context 'when a VM password is provided' do
|
|
776
|
+
before do
|
|
777
|
+
config[:vm_password] = 'password123'
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
it 'sets the password and disables auto-generation and reset' do
|
|
781
|
+
expect(customization).to receive(:admin_password=).with('password123')
|
|
782
|
+
expect(customization).to receive(:admin_password_auto=).with(false)
|
|
783
|
+
expect(customization).to receive(:reset_password_required=).with(false)
|
|
784
|
+
|
|
785
|
+
driver.set_customization_password
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
context 'when a VM password is not provided' do
|
|
790
|
+
before do
|
|
791
|
+
config[:vm_password] = nil
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
it 'sets nulls-out the password, enables auto-generation, disables reset reset' do
|
|
795
|
+
expect(customization).to receive(:admin_password=).with(nil)
|
|
796
|
+
expect(customization).to receive(:admin_password_auto=).with(true)
|
|
797
|
+
expect(customization).to receive(:reset_password_required=).with(false)
|
|
798
|
+
|
|
799
|
+
driver.set_customization_password
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
describe '#set_customization_computer_name' do
|
|
805
|
+
let(:customization) { double('customization') }
|
|
806
|
+
it 'sets the computer name' do
|
|
807
|
+
allow(driver).to receive(:customization).and_return(customization)
|
|
808
|
+
allow(driver).to receive(:node_name).and_return('test node')
|
|
809
|
+
expect(customization).to receive(:computer_name=).with('test node')
|
|
810
|
+
|
|
811
|
+
driver.set_customization_computer_name
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
describe '#save_customization' do
|
|
816
|
+
let(:customization) { double('customization') }
|
|
817
|
+
it 'enables and saves the customization' do
|
|
818
|
+
allow(driver).to receive(:customization).and_return(customization)
|
|
819
|
+
expect(customization).to receive(:enabled=).with(true)
|
|
820
|
+
expect(customization).to receive(:save)
|
|
821
|
+
|
|
822
|
+
driver.save_customization
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
end
|