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.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/LICENSE.txt +22 -0
- data/README.md +15 -2
- data/ci/run_specs.sh +6 -1
- data/lib/vm_shepherd.rb +12 -0
- data/lib/vm_shepherd/{ami_manager.rb → aws_manager.rb} +1 -3
- data/lib/vm_shepherd/openstack_manager.rb +102 -0
- data/lib/vm_shepherd/shepherd.rb +74 -81
- data/lib/vm_shepherd/vcloud_manager.rb +170 -0
- data/lib/vm_shepherd/version.rb +1 -1
- data/lib/vm_shepherd/vsphere_manager.rb +293 -0
- data/spec/fixtures/shepherd/openstack.yml +19 -0
- data/spec/support/patched_fog.rb +20 -0
- data/spec/vm_shepherd/{ami_manager_spec.rb → aws_manager_spec.rb} +9 -10
- data/spec/vm_shepherd/openstack_manager_spec.rb +237 -0
- data/spec/vm_shepherd/shepherd_spec.rb +115 -50
- data/spec/vm_shepherd/vcloud_manager_spec.rb +364 -0
- data/spec/vm_shepherd/vsphere_manager_spec.rb +16 -0
- data/vm_shepherd.gemspec +1 -1
- metadata +31 -32
- data/lib/vm_shepherd/ova_manager/base.rb +0 -31
- data/lib/vm_shepherd/ova_manager/deployer.rb +0 -202
- data/lib/vm_shepherd/ova_manager/destroyer.rb +0 -29
- data/lib/vm_shepherd/ova_manager/open_monkey_patch.rb +0 -14
- data/lib/vm_shepherd/vapp_manager/deployer.rb +0 -151
- data/lib/vm_shepherd/vapp_manager/destroyer.rb +0 -46
- data/spec/vm_shepherd/ova_manager/base_spec.rb +0 -56
- data/spec/vm_shepherd/ova_manager/deployer_spec.rb +0 -134
- data/spec/vm_shepherd/ova_manager/destroyer_spec.rb +0 -42
- data/spec/vm_shepherd/vapp_manager/deployer_spec.rb +0 -287
- data/spec/vm_shepherd/vapp_manager/destroyer_spec.rb +0 -104
@@ -1,23 +1,19 @@
|
|
1
|
-
require 'vm_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(:
|
13
|
+
let(:vcloud_manager) { instance_double(VcloudManager) }
|
18
14
|
|
19
|
-
it 'uses
|
20
|
-
expect(
|
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(
|
30
|
+
).and_return(vcloud_manager)
|
35
31
|
|
36
|
-
expect(
|
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(:
|
52
|
+
let(:ova_manager) { instance_double(VsphereManager) }
|
57
53
|
|
58
|
-
it 'uses
|
59
|
-
expect(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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(
|
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(
|
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(
|
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(:
|
163
|
+
let(:vcloud_manager) { instance_double(VcloudManager) }
|
135
164
|
|
136
|
-
it 'uses
|
137
|
-
expect(
|
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(
|
150
|
-
expect(
|
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(:
|
190
|
+
let(:ova_manager) { instance_double(VsphereManager) }
|
161
191
|
|
162
|
-
it 'uses
|
163
|
-
expect(
|
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
|
-
|
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(
|
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(
|
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
|