vm_shepherd 0.0.1 → 0.1.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.
@@ -1,23 +1,19 @@
1
- require 'vm_shepherd/shepherd'
1
+ require 'vm_shepherd'
2
2
  require 'recursive_open_struct'
3
3
 
4
4
  module VmShepherd
5
5
  RSpec.describe Shepherd do
6
6
  subject(:manager) { Shepherd.new(settings: settings) }
7
7
 
8
- specify { expect(Shepherd::VCLOUD_IAAS_TYPE).to eq('vcloud') }
9
- specify { expect(Shepherd::VSPHERE_IAAS_TYPE).to eq('vsphere') }
10
- specify { expect(Shepherd::VSPHERE_TEMPLATE_PREFIX).to eq('tpl') }
11
-
12
8
  describe '#deploy' do
13
9
  context 'with vcloud settings' do
14
10
  let(:settings) do
15
11
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vcloud.yml')))
16
12
  end
17
- let(:deployer) { instance_double(VappManager::Deployer) }
13
+ let(:vcloud_manager) { instance_double(VcloudManager) }
18
14
 
19
- it 'uses VappManager::Deployer to launch a vm' do
20
- expect(VappManager::Deployer).to receive(:new).
15
+ it 'uses VcloudManager::Deployer to launch a vm' do
16
+ expect(VcloudManager).to receive(:new).
21
17
  with(
22
18
  {
23
19
  url: settings.vapp_deployer.creds.url,
@@ -31,9 +27,9 @@ module VmShepherd
31
27
  network: settings.vapp_deployer.vdc.network,
32
28
  },
33
29
  instance_of(Logger)
34
- ).and_return(deployer)
30
+ ).and_return(vcloud_manager)
35
31
 
36
- expect(deployer).to receive(:deploy).with(
32
+ expect(vcloud_manager).to receive(:deploy).with(
37
33
  'FAKE_PATH',
38
34
  {
39
35
  name: settings.vapp_deployer.vapp.name,
@@ -53,28 +49,17 @@ module VmShepherd
53
49
  let(:settings) do
54
50
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vsphere.yml')))
55
51
  end
56
- let(:deployer) { instance_double(OvaManager::Deployer) }
52
+ let(:ova_manager) { instance_double(VsphereManager) }
57
53
 
58
- it 'uses OvaManager::Deployer to launch a vm' do
59
- expect(OvaManager::Deployer).to receive(:new).
60
- with(
61
- {
62
- host: settings.vm_deployer.vcenter_creds.ip,
63
- user: settings.vm_deployer.vcenter_creds.username,
64
- password: settings.vm_deployer.vcenter_creds.password,
65
- },
66
- {
67
- datacenter: settings.vm_deployer.vsphere.datacenter,
68
- cluster: settings.vm_deployer.vsphere.cluster,
69
- resource_pool: settings.vm_deployer.vsphere.resource_pool,
70
- datastore: settings.vm_deployer.vsphere.datastore,
71
- network: settings.vm_deployer.vsphere.network,
72
- folder: settings.vm_deployer.vsphere.folder,
73
- },
74
- ).and_return(deployer)
54
+ it 'uses VsphereManager::Deployer to launch a vm' do
55
+ expect(VsphereManager).to receive(:new).with(
56
+ settings.vm_deployer.vcenter_creds.ip,
57
+ settings.vm_deployer.vcenter_creds.username,
58
+ settings.vm_deployer.vcenter_creds.password,
59
+ settings.vm_deployer.vsphere.datacenter,
60
+ ).and_return(ova_manager)
75
61
 
76
- expect(deployer).to receive(:deploy).with(
77
- Shepherd::VSPHERE_TEMPLATE_PREFIX,
62
+ expect(ova_manager).to receive(:deploy).with(
78
63
  'FAKE_PATH',
79
64
  {
80
65
  ip: settings.vm_deployer.vm.ip,
@@ -82,7 +67,14 @@ module VmShepherd
82
67
  netmask: settings.vm_deployer.vm.netmask,
83
68
  dns: settings.vm_deployer.vm.dns,
84
69
  ntp_servers: settings.vm_deployer.vm.ntp_servers,
85
- }
70
+ },
71
+ {
72
+ cluster: settings.vm_deployer.vsphere.cluster,
73
+ resource_pool: settings.vm_deployer.vsphere.resource_pool,
74
+ datastore: settings.vm_deployer.vsphere.datastore,
75
+ network: settings.vm_deployer.vsphere.network,
76
+ folder: settings.vm_deployer.vsphere.folder,
77
+ },
86
78
  )
87
79
 
88
80
  manager.deploy(path: 'FAKE_PATH')
@@ -93,7 +85,7 @@ module VmShepherd
93
85
  let(:settings) do
94
86
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
95
87
  end
96
- let(:ams_manager) { instance_double(AmiManager) }
88
+ let(:ams_manager) { instance_double(AwsManager) }
97
89
  let(:ami_file_path) { 'PATH_TO_AMI_FILE' }
98
90
  let(:aws_options) do
99
91
  {
@@ -109,12 +101,49 @@ module VmShepherd
109
101
  end
110
102
 
111
103
  it 'uses AwsManager::Deployer to launch a VM' do
112
- expect(AmiManager).to receive(:new).with(aws_options).and_return(ams_manager)
104
+ expect(AwsManager).to receive(:new).with(aws_options).and_return(ams_manager)
113
105
  expect(ams_manager).to receive(:deploy).with(ami_file_path)
114
106
  manager.deploy(path: ami_file_path)
115
107
  end
116
108
  end
117
109
 
110
+ context 'with OpenStack settings' do
111
+ let(:settings) do
112
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'openstack.yml')))
113
+ end
114
+ let(:qcow2_manager) { instance_double(OpenstackManager) }
115
+ let(:qcow2_file_path) { 'PATH_TO_QCOW2_FILE' }
116
+ let(:openstack_options) do
117
+ {
118
+ auth_url: 'http://example.com/version/tokens',
119
+ username: 'username',
120
+ api_key: 'api-key',
121
+ tenant: 'tenant',
122
+ }
123
+ end
124
+ let(:openstack_vm_options) do
125
+ {
126
+ name: 'some-vm-name',
127
+ min_disk_size: 150,
128
+ network_name: 'some-network',
129
+ key_name: 'some-key',
130
+ security_group_names: [
131
+ 'security-group-A',
132
+ 'security-group-B',
133
+ 'security-group-C',
134
+ ],
135
+ public_ip: '198.11.195.5',
136
+ private_ip: '192.168.100.100',
137
+ }
138
+ end
139
+
140
+ it 'uses QCow2Manager to launch a VM' do
141
+ expect(OpenstackManager).to receive(:new).with(openstack_options).and_return(qcow2_manager)
142
+ expect(qcow2_manager).to receive(:deploy).with(qcow2_file_path, openstack_vm_options)
143
+ manager.deploy(path: qcow2_file_path)
144
+ end
145
+ end
146
+
118
147
  context 'when IAAS is unknown' do
119
148
  let(:settings) do
120
149
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'unknown.yml')))
@@ -131,10 +160,10 @@ module VmShepherd
131
160
  let(:settings) do
132
161
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vcloud.yml')))
133
162
  end
134
- let(:destroyer) { instance_double(VappManager::Destroyer) }
163
+ let(:vcloud_manager) { instance_double(VcloudManager) }
135
164
 
136
- it 'uses VappManager::Destroyer to destroy a vm' do
137
- expect(VappManager::Destroyer).to receive(:new).with(
165
+ it 'uses VcloudManager::Destroyer to destroy a vm' do
166
+ expect(VcloudManager).to receive(:new).with(
138
167
  {
139
168
  url: settings.vapp_deployer.creds.url,
140
169
  organization: settings.vapp_deployer.creds.organization,
@@ -144,10 +173,11 @@ module VmShepherd
144
173
  {
145
174
  vdc: settings.vapp_deployer.vdc.name,
146
175
  catalog: settings.vapp_deployer.vdc.catalog,
176
+ network: settings.vapp_deployer.vdc.network,
147
177
  },
148
178
  instance_of(Logger)
149
- ).and_return(destroyer)
150
- expect(destroyer).to receive(:destroy).with(settings.vapp_deployer.vapp.name)
179
+ ).and_return(vcloud_manager)
180
+ expect(vcloud_manager).to receive(:destroy).with(settings.vapp_deployer.vapp.name)
151
181
 
152
182
  manager.destroy
153
183
  end
@@ -157,18 +187,16 @@ module VmShepherd
157
187
  let(:settings) do
158
188
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vsphere.yml')))
159
189
  end
160
- let(:destroyer) { instance_double(OvaManager::Destroyer) }
190
+ let(:ova_manager) { instance_double(VsphereManager) }
161
191
 
162
- it 'uses OvaManager::Destroyer to destroy a vm' do
163
- expect(OvaManager::Destroyer).to receive(:new).with(
192
+ it 'uses VsphereManager::Destroyer to destroy a vm' do
193
+ expect(VsphereManager).to receive(:new).with(
194
+ settings.vm_deployer.vcenter_creds.ip,
195
+ settings.vm_deployer.vcenter_creds.username,
196
+ settings.vm_deployer.vcenter_creds.password,
164
197
  settings.vm_deployer.vsphere.datacenter,
165
- {
166
- host: settings.vm_deployer.vcenter_creds.ip,
167
- user: settings.vm_deployer.vcenter_creds.username,
168
- password: settings.vm_deployer.vcenter_creds.password,
169
- }
170
- ).and_return(destroyer)
171
- expect(destroyer).to receive(:clean_folder).with(settings.vm_deployer.vsphere.folder)
198
+ ).and_return(ova_manager)
199
+ expect(ova_manager).to receive(:destroy).with(settings.vm_deployer.vsphere.folder)
172
200
 
173
201
  manager.destroy
174
202
  end
@@ -178,7 +206,7 @@ module VmShepherd
178
206
  let(:settings) do
179
207
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
180
208
  end
181
- let(:ams_manager) { instance_double(AmiManager) }
209
+ let(:ams_manager) { instance_double(AwsManager) }
182
210
  let(:ami_destroy_options) do
183
211
  {
184
212
  aws_access_key: 'aws-access-key',
@@ -193,12 +221,49 @@ module VmShepherd
193
221
  end
194
222
 
195
223
  it 'uses AwsManager::Deployer to launch a VM' do
196
- expect(AmiManager).to receive(:new).with(ami_destroy_options).and_return(ams_manager)
224
+ expect(AwsManager).to receive(:new).with(ami_destroy_options).and_return(ams_manager)
197
225
  expect(ams_manager).to receive(:destroy)
198
226
  manager.destroy
199
227
  end
200
228
  end
201
229
 
230
+ context 'when IAAS is Openstack' do
231
+ let(:settings) do
232
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'openstack.yml')))
233
+ end
234
+ let(:qcow2_manager) { instance_double(OpenstackManager) }
235
+ let(:qcow2_file_path) { 'PATH_TO_QCOW2_FILE' }
236
+ let(:openstack_options) do
237
+ {
238
+ auth_url: 'http://example.com/version/tokens',
239
+ username: 'username',
240
+ api_key: 'api-key',
241
+ tenant: 'tenant',
242
+ }
243
+ end
244
+ let(:openstack_vm_options) do
245
+ {
246
+ name: 'some-vm-name',
247
+ min_disk_size: 150,
248
+ network_name: 'some-network',
249
+ key_name: 'some-key',
250
+ security_group_names: [
251
+ 'security-group-A',
252
+ 'security-group-B',
253
+ 'security-group-C',
254
+ ],
255
+ public_ip: '198.11.195.5',
256
+ private_ip: '192.168.100.100',
257
+ }
258
+ end
259
+
260
+ it 'uses QCow2Manager to destroy a VM' do
261
+ expect(OpenstackManager).to receive(:new).with(openstack_options).and_return(qcow2_manager)
262
+ expect(qcow2_manager).to receive(:destroy).with(openstack_vm_options)
263
+ manager.destroy
264
+ end
265
+ end
266
+
202
267
  context 'when IAAS is unknown' do
203
268
  let(:settings) do
204
269
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'unknown.yml')))
@@ -0,0 +1,364 @@
1
+ require 'vm_shepherd/vcloud_manager'
2
+
3
+ module VmShepherd
4
+ RSpec.describe VcloudManager do
5
+ let(:login_info) do
6
+ {
7
+ url: 'FAKE_URL',
8
+ organization: 'FAKE_ORGANIZATION',
9
+ user: 'FAKE_USER',
10
+ password: 'FAKE_PASSWORD',
11
+ }
12
+ end
13
+ let(:location) do
14
+ {
15
+ catalog: 'FAKE_CATALOG',
16
+ network: 'FAKE_NETWORK',
17
+ vdc: 'FAKE_VDC',
18
+ }
19
+ end
20
+ let(:logger) { instance_double(Logger).as_null_object }
21
+
22
+ let(:vcloud_manager) { VcloudManager.new(login_info, location, logger) }
23
+
24
+ describe '#deploy' do
25
+ let(:vapp_config) do
26
+ {
27
+ ip: 'FAKE_IP',
28
+ name: 'FAKE_NAME',
29
+ gateway: 'FAKE_GATEWAY',
30
+ dns: 'FAKE_DNS',
31
+ ntp: 'FAKE_NTP',
32
+ ip: 'FAKE_IP',
33
+ netmask: 'FAKE_NETMASK',
34
+ }
35
+ end
36
+ let(:vapp_template_path) { 'FAKE_VAPP_TEMPLATE_PATH' }
37
+ let(:tmpdir) { 'FAKE_TMP_DIR' }
38
+
39
+ before { allow(Dir).to receive(:mktmpdir).and_return(tmpdir) }
40
+
41
+ context 'when NO host exists at the specified IP' do
42
+ let(:expanded_vapp_template_path) { 'FAKE_EXPANDED_VAPP_TEMPLATE_PATH' }
43
+
44
+ before do
45
+ allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.fetch(:ip)}").and_return(false)
46
+
47
+ allow(File).to receive(:expand_path).with(vapp_template_path).and_return(expanded_vapp_template_path)
48
+
49
+ allow(FileUtils).to receive(:remove_entry_secure)
50
+ end
51
+
52
+ it 'expands the vapp_template into a TMP dir' do
53
+ expect(vcloud_manager).to receive(:system).with("cd #{tmpdir} && tar xfv '#{expanded_vapp_template_path}'")
54
+
55
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error
56
+ end
57
+
58
+ context 'when the template can be expanded' do
59
+ let(:client) { instance_double(VCloudSdk::Client) }
60
+ let(:catalog) { instance_double(VCloudSdk::Catalog) }
61
+ let(:network_config) { instance_double(VCloudSdk::NetworkConfig) }
62
+ let(:vapp) { instance_double(VCloudSdk::VApp) }
63
+ let(:vm) { instance_double(VCloudSdk::VM) }
64
+
65
+ before do
66
+ allow(vcloud_manager).to receive(:system).with("cd #{tmpdir} && tar xfv '#{expanded_vapp_template_path}'").
67
+ and_return(true)
68
+ end
69
+
70
+ context 'when the vApp can be deployed' do
71
+ let(:expected_properties) do
72
+ [
73
+ {
74
+ 'type' => 'string',
75
+ 'key' => 'gateway',
76
+ 'value' => vapp_config.fetch(:gateway),
77
+ 'password' => 'false',
78
+ 'userConfigurable' => 'true',
79
+ 'Label' => 'Default Gateway',
80
+ 'Description' => 'The default gateway address for the VM network. Leave blank if DHCP is desired.'
81
+ },
82
+ {
83
+ 'type' => 'string',
84
+ 'key' => 'DNS',
85
+ 'value' => vapp_config.fetch(:dns),
86
+ 'password' => 'false',
87
+ 'userConfigurable' => 'true',
88
+ 'Label' => 'DNS',
89
+ 'Description' => 'The domain name servers for the VM (comma separated). Leave blank if DHCP is desired.',
90
+ },
91
+ {
92
+ 'type' => 'string',
93
+ 'key' => 'ntp_servers',
94
+ 'value' => vapp_config.fetch(:ntp),
95
+ 'password' => 'false',
96
+ 'userConfigurable' => 'true',
97
+ 'Label' => 'NTP Servers',
98
+ 'Description' => 'Comma-delimited list of NTP servers'
99
+ },
100
+ {
101
+ 'type' => 'string',
102
+ 'key' => 'admin_password',
103
+ 'value' => 'tempest',
104
+ 'password' => 'true',
105
+ 'userConfigurable' => 'true',
106
+ 'Label' => 'Admin Password',
107
+ 'Description' => 'This password is used to SSH into the VM. The username is "tempest".',
108
+ },
109
+ {
110
+ 'type' => 'string',
111
+ 'key' => 'ip0',
112
+ 'value' => vapp_config.fetch(:ip),
113
+ 'password' => 'false',
114
+ 'userConfigurable' => 'true',
115
+ 'Label' => 'IP Address',
116
+ 'Description' => 'The IP address for the VM. Leave blank if DHCP is desired.',
117
+ },
118
+ {
119
+ 'type' => 'string',
120
+ 'key' => 'netmask0',
121
+ 'value' => vapp_config.fetch(:netmask),
122
+ 'password' => 'false',
123
+ 'userConfigurable' => 'true',
124
+ 'Label' => 'Netmask',
125
+ 'Description' => 'The netmask for the VM network. Leave blank if DHCP is desired.'
126
+ }
127
+ ]
128
+ end
129
+
130
+ before do
131
+ allow(VCloudSdk::Client).to receive(:new).and_return(client)
132
+ allow(client).to receive(:catalog_exists?)
133
+ allow(client).to receive(:delete_catalog_by_name)
134
+
135
+ allow(client).to receive(:create_catalog).and_return(catalog)
136
+ allow(catalog).to receive(:upload_vapp_template)
137
+ allow(catalog).to receive(:instantiate_vapp_template).and_return(vapp)
138
+
139
+ allow(VCloudSdk::NetworkConfig).to receive(:new).and_return(network_config)
140
+
141
+ allow(vapp).to receive(:find_vm_by_name).and_return(vm)
142
+ allow(vm).to receive(:product_section_properties=)
143
+ allow(vapp).to receive(:power_on)
144
+ end
145
+
146
+ it 'uses VCloudSdk::Client' do
147
+ expect(VCloudSdk::Client).to receive(:new).with(
148
+ login_info.fetch(:url),
149
+ [login_info.fetch(:user), login_info.fetch(:organization)].join('@'),
150
+ login_info.fetch(:password),
151
+ {},
152
+ logger,
153
+ ).and_return(client)
154
+
155
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
156
+ end
157
+
158
+ describe 'catalog deletion' do
159
+ before do
160
+ allow(client).to receive(:catalog_exists?).and_return(catalog_exists)
161
+ end
162
+
163
+ context 'when the catalog exists' do
164
+ let(:catalog_exists) { true }
165
+
166
+ it 'deletes the catalog' do
167
+ expect(client).to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
168
+
169
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
170
+ end
171
+ end
172
+
173
+ context 'when the catalog does not exist' do
174
+ let(:catalog_exists) { false }
175
+
176
+ it 'does not delete the catalog' do
177
+ expect(client).not_to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
178
+
179
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
180
+ end
181
+ end
182
+ end
183
+
184
+ it 'creates the catalog' do
185
+ expect(client).to receive(:create_catalog).with(location.fetch(:catalog)).and_return(catalog)
186
+
187
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
188
+ end
189
+
190
+ it 'uploads the vApp template' do
191
+ expect(catalog).to receive(:upload_vapp_template).with(
192
+ location.fetch(:vdc),
193
+ vapp_config.fetch(:name),
194
+ tmpdir,
195
+ ).and_return(catalog)
196
+
197
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
198
+ end
199
+
200
+ it 'creates a VCloudSdk::NetworkConfig' do
201
+ expect(VCloudSdk::NetworkConfig).to receive(:new).with(
202
+ location.fetch(:network),
203
+ 'Network 1',
204
+ ).and_return(network_config)
205
+
206
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
207
+ end
208
+
209
+ it 'instantiates the vApp template' do
210
+ expect(catalog).to receive(:instantiate_vapp_template).with(
211
+ vapp_config.fetch(:name),
212
+ location.fetch(:vdc),
213
+ vapp_config.fetch(:name),
214
+ nil,
215
+ nil,
216
+ network_config
217
+ ).and_return(vapp)
218
+
219
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
220
+ end
221
+
222
+ it 'sets the product section properties' do
223
+ expect(vm).to receive(:product_section_properties=).with(expected_properties)
224
+
225
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
226
+ end
227
+
228
+ it 'powers on the vApp' do
229
+ expect(vapp).to receive(:power_on)
230
+
231
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
232
+ end
233
+
234
+ it 'removes the expanded vApp template' do
235
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir, force: true)
236
+
237
+ vcloud_manager.deploy(vapp_template_path, vapp_config)
238
+ end
239
+ end
240
+
241
+ context 'when the vApp can NOT be deployed' do
242
+ it 'removes the expanded vApp template' do
243
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir, force: true)
244
+
245
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'when the template can NOT be expanded' do
251
+ let(:tar_expand_cmd) { "cd #{tmpdir} && tar xfv '#{expanded_vapp_template_path}'" }
252
+ before do
253
+ allow(vcloud_manager).to receive(:system).with(tar_expand_cmd).and_return(false)
254
+ end
255
+
256
+ it 'raises an error' do
257
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("Error executing: #{tar_expand_cmd.inspect}")
258
+ end
259
+
260
+ it 'removes the expanded vApp template' do
261
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir, force: true)
262
+
263
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error
264
+ end
265
+ end
266
+ end
267
+
268
+ context 'when a host exists at the specified IP' do
269
+ before do
270
+ allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.fetch(:ip)}").and_return(true)
271
+ end
272
+
273
+ it 'raises an error' do
274
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("VM exists at #{vapp_config.fetch(:ip)}")
275
+ end
276
+
277
+ it 'removes the expanded vApp template' do
278
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir, force: true)
279
+
280
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error
281
+ end
282
+ end
283
+ end
284
+
285
+ describe '#destroy' do
286
+ let(:client) { instance_double(VCloudSdk::Client) }
287
+ let(:vdc) { instance_double(VCloudSdk::VDC) }
288
+ let(:vapp) { instance_double(VCloudSdk::VApp) }
289
+ let(:vapp_name) { 'FAKE_VAPP_NAME' }
290
+
291
+ context 'when the catalog exists' do
292
+ before do
293
+ allow(client).to receive(:catalog_exists?).with(location.fetch(:catalog)).and_return(true)
294
+ end
295
+
296
+ it 'uses VCloudSdk::Client to delete the vApp' do
297
+ expect(client).to receive(:find_vdc_by_name).with(location.fetch(:vdc)).and_return(vdc)
298
+ expect(vdc).to receive(:find_vapp_by_name).with(vapp_name).and_return(vapp)
299
+ expect(vapp).to receive(:power_off)
300
+ expect(vapp).to receive(:delete)
301
+ expect(client).to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
302
+
303
+ expect(VCloudSdk::Client).to receive(:new).with(
304
+ login_info.fetch(:url),
305
+ [login_info.fetch(:user), login_info.fetch(:organization)].join('@'),
306
+ login_info.fetch(:password),
307
+ {},
308
+ logger,
309
+ ).and_return(client)
310
+
311
+ vcloud_manager.destroy(vapp_name)
312
+ end
313
+
314
+ context 'when an VCloudSdk::ObjectNotFoundError is thrown' do
315
+ before do
316
+ allow(VCloudSdk::Client).to receive(:new).and_return(client)
317
+ allow(client).to receive(:find_vdc_by_name).and_return(vdc)
318
+ allow(vdc).to receive(:find_vapp_by_name).and_return(vapp)
319
+ allow(vapp).to receive(:power_off)
320
+ allow(vapp).to receive(:delete)
321
+
322
+ allow(client).to receive(:delete_catalog_by_name)
323
+ end
324
+
325
+ it 'catches the error' do
326
+ allow(client).to receive(:find_vdc_by_name).and_raise(VCloudSdk::ObjectNotFoundError)
327
+
328
+ expect { vcloud_manager.destroy(vapp_name) }.not_to raise_error
329
+ end
330
+
331
+ it 'deletes to catalog' do
332
+ expect(client).to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
333
+
334
+ vcloud_manager.destroy(vapp_name)
335
+ end
336
+ end
337
+ end
338
+
339
+ context 'when the catalog does not exist' do
340
+ before do
341
+ allow(client).to receive(:catalog_exists?).with(location.fetch(:catalog)).and_return(false)
342
+ end
343
+
344
+ it 'uses VCloudSdk::Client to delete the vApp' do
345
+ expect(client).to receive(:find_vdc_by_name).with(location.fetch(:vdc)).and_return(vdc)
346
+ expect(vdc).to receive(:find_vapp_by_name).with(vapp_name).and_return(vapp)
347
+ expect(vapp).to receive(:power_off)
348
+ expect(vapp).to receive(:delete)
349
+ expect(client).not_to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
350
+
351
+ expect(VCloudSdk::Client).to receive(:new).with(
352
+ login_info.fetch(:url),
353
+ [login_info.fetch(:user), login_info.fetch(:organization)].join('@'),
354
+ login_info.fetch(:password),
355
+ {},
356
+ logger,
357
+ ).and_return(client)
358
+
359
+ vcloud_manager.destroy(vapp_name)
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end