vm_shepherd 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d6273df41a1db4583c88e2b1139acf836f78983
4
- data.tar.gz: 94c0f08587030e100ed9d6dd534769b876516165
3
+ metadata.gz: e6cdd5ba469fb3cfa60e574e87ef6c374432b22f
4
+ data.tar.gz: dd14790e5da479762cbb30a2d65b550df146d636
5
5
  SHA512:
6
- metadata.gz: d03b514667b963aa9f8e15a377258a49fc8d68bf32a806203e3996549bce3ac3d6642dbd04145d71a9688843e86fb0a5f268a796c1da4be20473a8f35c4396d1
7
- data.tar.gz: 8559843b647a40f7c09a5ecd95f1063999592e60eb9628a5e0f9cc0718680da97e268ef871000d17f99f2c5c7b4e47cdcdbf8c021775037e6eaa774579a7b2a7
6
+ metadata.gz: 8f9f862403579de226ee581bbc3ff9305ff95657e23e03c278a73972e8c74e21d43310b33f89be4e31914a7602b800dfe59b010297e1cdb8fbb86e8452bafb65
7
+ data.tar.gz: 1031f76997f1fdb383d1ac36de08c9edae15cad882df6290b9cb62d423e357b9d67b3947e34e2b8c77302125c39085768c780def915be2b180483f5d1fc363ae
data/.gitignore CHANGED
@@ -12,3 +12,4 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ .idea
@@ -1 +1 @@
1
- 2.1.5
1
+ 2.2.1
@@ -42,15 +42,30 @@ module VmShepherd
42
42
  instance.add_tag('Name', value: aws_options.fetch(:vm_name))
43
43
  end
44
44
 
45
- def destroy
45
+ def clean_environment
46
46
  subnets = [
47
47
  AWS.ec2.subnets[aws_options.fetch(:public_subnet_id)],
48
48
  AWS.ec2.subnets[aws_options.fetch(:private_subnet_id)]
49
49
  ]
50
50
 
51
+ volumes = []
51
52
  subnets.each do |subnet|
52
53
  subnet.instances.each do |instance|
53
- instance.terminate unless instance.tags.to_h.fetch(DO_NOT_TERMINATE_TAG_KEY, false)
54
+ unless instance.tags.to_h.fetch(DO_NOT_TERMINATE_TAG_KEY, false)
55
+ instance.attachments.each do |_, attachment|
56
+ volumes.push(attachment.volume) unless attachment.delete_on_termination
57
+ end
58
+ instance.terminate
59
+ end
60
+ end
61
+ end
62
+ destroy_volumes(volumes)
63
+ end
64
+
65
+ def destroy
66
+ AWS.ec2.elastic_ips.each do |elastic_ip|
67
+ if elastic_ip.allocation_id == aws_options.fetch(:elastic_ip_id)
68
+ elastic_ip.instance.terminate if elastic_ip.instance
54
69
  end
55
70
  end
56
71
  end
@@ -58,6 +73,16 @@ module VmShepherd
58
73
  private
59
74
  attr_reader :aws_options
60
75
 
76
+ def destroy_volumes(volumes)
77
+ volumes.each do |volume|
78
+ begin
79
+ volume.delete
80
+ rescue AWS::EC2::Errors::VolumeInUse
81
+ sleep 5
82
+ retry
83
+ end
84
+ end
85
+ end
61
86
 
62
87
  def retry_ignoring_error_until(exception_class, &block)
63
88
  tries = 0
@@ -61,6 +61,9 @@ module VmShepherd
61
61
  say('Done destroying Ops Manager instances')
62
62
  end
63
63
 
64
+ def clean_environment
65
+ end
66
+
64
67
  def service
65
68
  @service ||= Fog::Compute.new(
66
69
  provider: 'openstack',
@@ -1,8 +1,6 @@
1
1
  module VmShepherd
2
2
  class Shepherd
3
- class InvalidIaas < StandardError
4
-
5
- end
3
+ class InvalidIaas < StandardError; end
6
4
 
7
5
  def initialize(settings:)
8
6
  @settings = settings
@@ -76,14 +74,14 @@ module VmShepherd
76
74
  network: settings.vapp_deployer.vdc.network,
77
75
  },
78
76
  logger
79
- ).destroy(settings.vapp_deployer.vapp.name)
77
+ ).destroy([settings.vapp_deployer.vapp.ops_manager_name] + settings.vapp_deployer.vapp.product_names)
80
78
  when VmShepherd::VSPHERE_IAAS_TYPE then
81
79
  VmShepherd::VsphereManager.new(
82
80
  settings.vm_deployer.vcenter_creds.ip,
83
81
  settings.vm_deployer.vcenter_creds.username,
84
82
  settings.vm_deployer.vcenter_creds.password,
85
83
  settings.vm_deployer.vsphere.datacenter,
86
- ).destroy(settings.vm_deployer.vsphere.folder)
84
+ ).destroy(settings.vm_deployer.vm.ip)
87
85
  when VmShepherd::AWS_IAAS_TYPE then
88
86
  ami_manager.destroy
89
87
  when VmShepherd::OPENSTACK_IAAS_TYPE then
@@ -93,6 +91,43 @@ module VmShepherd
93
91
  end
94
92
  end
95
93
 
94
+ def clean_environment
95
+ case settings.iaas_type
96
+ when VmShepherd::VCLOUD_IAAS_TYPE then
97
+ VmShepherd::VcloudManager.new(
98
+ {
99
+ url: settings.vapp_deployer.creds.url,
100
+ organization: settings.vapp_deployer.creds.organization,
101
+ user: settings.vapp_deployer.creds.user,
102
+ password: settings.vapp_deployer.creds.password,
103
+ },
104
+ {
105
+ vdc: settings.vapp_deployer.vdc.name,
106
+ catalog: settings.vapp_deployer.vdc.catalog,
107
+ network: settings.vapp_deployer.vdc.network,
108
+ },
109
+ logger
110
+ ).clean_environment
111
+ when VmShepherd::VSPHERE_IAAS_TYPE then
112
+ VmShepherd::VsphereManager.new(
113
+ settings.vm_deployer.vcenter_creds.ip,
114
+ settings.vm_deployer.vcenter_creds.username,
115
+ settings.vm_deployer.vcenter_creds.password,
116
+ settings.vm_deployer.vsphere.datacenter,
117
+ ).clean_environment(
118
+ datacenter_folders_to_clean: settings.vm_deployer.vsphere.datacenter_folders_to_clean,
119
+ datastore: settings.vm_deployer.vsphere.datastore,
120
+ datastore_folders_to_clean: settings.vm_deployer.vsphere.datastore_folders_to_clean,
121
+ )
122
+ when VmShepherd::AWS_IAAS_TYPE then
123
+ ami_manager.clean_environment
124
+ when VmShepherd::OPENSTACK_IAAS_TYPE then
125
+ openstack_vm_manager.clean_environment
126
+ else
127
+ fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
128
+ end
129
+ end
130
+
96
131
  private
97
132
 
98
133
  attr_reader :settings
@@ -27,11 +27,14 @@ module VmShepherd
27
27
  FileUtils.remove_entry_secure(tmpdir, force: true)
28
28
  end
29
29
 
30
- def destroy(vapp_name)
31
- delete_vapp(vapp_name)
30
+ def destroy(vapp_names)
31
+ delete_vapps(vapp_names)
32
32
  delete_catalog
33
33
  end
34
34
 
35
+ def clean_environment
36
+ end
37
+
35
38
  private
36
39
 
37
40
  def check_vapp_status(vapp_config)
@@ -155,12 +158,16 @@ module VmShepherd
155
158
  @vdc ||= client.find_vdc_by_name(@location[:vdc])
156
159
  end
157
160
 
158
- def delete_vapp(vapp_name)
159
- vapp = vdc.find_vapp_by_name(vapp_name)
160
- vapp.power_off
161
- vapp.delete
162
- rescue VCloudSdk::ObjectNotFoundError => e
163
- @logger.debug "Could not delete vapp '#{vapp_name}': #{e.inspect}"
161
+ def delete_vapps(vapp_names)
162
+ vapp_names.each do |vapp_name|
163
+ begin
164
+ vapp = vdc.find_vapp_by_name(vapp_name)
165
+ vapp.power_off
166
+ vapp.delete
167
+ rescue VCloudSdk::ObjectNotFoundError => e
168
+ @logger.debug "Could not delete vapp '#{vapp_name}': #{e.inspect}"
169
+ end
170
+ end
164
171
  end
165
172
 
166
173
  def delete_catalog
@@ -1,3 +1,3 @@
1
1
  module VmShepherd
2
- VERSION = '0.1.2'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -5,6 +5,9 @@ module VmShepherd
5
5
  class VsphereManager
6
6
  TEMPLATE_PREFIX = 'tpl'.freeze
7
7
  VALID_FOLDER_REGEX = /\A([\w-]{1,80}\/)*[\w-]{1,80}\/?\z/
8
+ VALID_DISK_FOLDER_REGEX = /\A[\w-]{1,80}\z/
9
+
10
+ attr_writer :logger
8
11
 
9
12
  def initialize(host, username, password, datacenter_name)
10
13
  @host = host
@@ -26,10 +29,41 @@ module VmShepherd
26
29
  FileUtils.remove_entry_secure(ovf_file_path, force: true) unless ovf_file_path.nil?
27
30
  end
28
31
 
29
- def destroy(folder_name)
30
- validate_folder_name!(folder_name)
32
+ def clean_environment(datacenter_folders_to_clean:, datastore:, datastore_folders_to_clean:)
33
+ datacenter_folders_to_clean.each do |folder_name|
34
+ validate_folder_name!(folder_name)
35
+ delete_folder_and_vms(folder_name)
36
+ end
37
+
38
+ datastore_folders_to_clean.each do |folder_name|
39
+ VALID_DISK_FOLDER_REGEX.match(folder_name) || fail("#{folder_name.inspect} is not a valid disk folder name")
40
+ begin
41
+ logger.info("BEGIN datastore_folder.destroy_task folder=#{folder_name}")
42
+
43
+ connection.serviceContent.fileManager.DeleteDatastoreFile_Task(
44
+ datacenter: datacenter,
45
+ name: "[#{datastore}] #{folder_name}"
46
+ ).wait_for_completion
47
+
48
+ logger.info("END datastore_folder.destroy_task folder=#{folder_name}")
49
+ rescue RbVmomi::Fault => e
50
+ logger.info("ERROR datastore_folder.destroy_task folder=#{folder_name} #{e.inspect}")
51
+ end
52
+ end
53
+ end
54
+
55
+ def destroy(ip_address)
56
+ vm = datacenter.vmFolder.findByIp(ip_address)
57
+ return unless vm
58
+ power_off_vm(vm)
59
+ destroy_vm(vm)
60
+ end
31
61
 
32
- delete_folder_and_vms(folder_name)
62
+ def destroy_vm(vm)
63
+ vm_name = vm.name
64
+ logger.info("BEGIN vm.destroy_task vm=#{vm_name}")
65
+ vm.Destroy_Task.wait_for_completion
66
+ logger.info("END vm.destroy_task vm=#{vm_name}")
33
67
  end
34
68
 
35
69
  private
@@ -79,7 +113,7 @@ module VmShepherd
79
113
  folder = datacenter.vmFolder.traverse(folder_name) ||
80
114
  fail("ERROR no folder found with name=#{folder_name.inspect}")
81
115
 
82
- find_vms(folder).each { |vm| power_off(vm) }
116
+ find_vms(folder).each { |vm| power_off_vm(vm) }
83
117
 
84
118
  logger.info("BEGIN folder.destroy_task folder=#{folder_name}")
85
119
  folder.Destroy_Task.wait_for_completion
@@ -87,7 +121,7 @@ module VmShepherd
87
121
 
88
122
  fail("#{folder_name.inspect} already exists") unless datacenter.vmFolder.traverse(folder_name).nil?
89
123
  rescue RbVmomi::Fault => e
90
- logger.info("ERROR folder.destroy_task folder=#{folder_name}", e)
124
+ logger.info("ERROR folder.destroy_task folder=#{folder_name} #{e.inspect}")
91
125
  raise
92
126
  end
93
127
 
@@ -97,7 +131,7 @@ module VmShepherd
97
131
  vms.flatten
98
132
  end
99
133
 
100
- def power_off(vm)
134
+ def power_off_vm(vm)
101
135
  2.times do
102
136
  break if vm.runtime.powerState == 'poweredOff'
103
137
 
@@ -10,7 +10,10 @@ vapp_deployer:
10
10
  catalog: VDC_CATALOG
11
11
  network: VDC_NETWORK
12
12
  vapp:
13
- name: VAPP_NAME
13
+ ops_manager_name: VAPP_NAME
14
+ product_names:
15
+ - PRODUCT_1
16
+ - PRODUCT_2
14
17
  ip: VAPP_IP
15
18
  public_ip:
16
19
  gateway: VAPP_GATEWAY
@@ -12,6 +12,11 @@ vm_deployer:
12
12
  resource_pool: VSPHERE_RESOURCE_POOL
13
13
  datastore: VSPHERE_DATASTORE
14
14
  folder: VSPHERE_FOLDER
15
+ datacenter_folders_to_clean:
16
+ - DC_FOLDER_ONE
17
+ - DC_FOLDER_TWO
18
+ datastore_folders_to_clean:
19
+ - DS_DISK_FOLDER
15
20
  vm:
16
21
  ip: OVA_IP
17
22
  gateway: OVA_GATEWAY
@@ -6,6 +6,7 @@ module VmShepherd
6
6
  let(:secret_key) { 'secret-key' }
7
7
  let(:ami_id) { 'ami-deadbeef' }
8
8
  let(:ami_file_path) { Tempfile.new('ami-id-file').tap { |f| f.write("#{ami_id}\n"); f.close }.path }
9
+ let(:elastic_ip_id) { 'elastic-ip-id' }
9
10
  let(:ec2) { double('AWS.ec2') }
10
11
 
11
12
  let(:aws_options) do
@@ -16,7 +17,7 @@ module VmShepherd
16
17
  security_group_id: 'security-group-id',
17
18
  public_subnet_id: 'public-subnet-id',
18
19
  private_subnet_id: 'private-subnet-id',
19
- elastic_ip_id: 'elastic-ip-id',
20
+ elastic_ip_id: elastic_ip_id,
20
21
  vm_name: 'Ops Manager: clean_install_spec'
21
22
  }
22
23
  end
@@ -103,7 +104,7 @@ module VmShepherd
103
104
  end
104
105
  end
105
106
 
106
- describe '#destroy' do
107
+ describe '#clean_environment' do
107
108
  let(:subnets) { instance_double(AWS::EC2::SubnetCollection) }
108
109
  let(:subnet1) { instance_double(AWS::EC2::Subnet, instances: subnet1_instances) }
109
110
  let(:subnet2) { instance_double(AWS::EC2::Subnet, instances: subnet2_instances) }
@@ -112,17 +113,25 @@ module VmShepherd
112
113
  let(:subnet1_instances) { [instance1] }
113
114
  let(:subnet2_instances) { [instance2] }
114
115
 
116
+ let(:instance1_volume) { instance_double(AWS::EC2::Volume)}
117
+ let(:instance1_attachment) do
118
+ instance_double(AWS::EC2::Attachment, volume: instance1_volume, delete_on_termination: true)
119
+ end
120
+
115
121
  before do
116
122
  allow(ec2).to receive(:subnets).and_return(subnets)
117
123
  allow(subnets).to receive(:[]).with('public-subnet-id').and_return(subnet1)
118
124
  allow(subnets).to receive(:[]).with('private-subnet-id').and_return(subnet2)
125
+
126
+ allow(instance1).to receive(:attachments).and_return({'/dev/test' => instance1_attachment})
127
+ allow(instance2).to receive(:attachments).and_return({})
119
128
  end
120
129
 
121
130
  it 'terminates all VMs in the subnet' do
122
131
  expect(instance1).to receive(:terminate)
123
132
  expect(instance2).to receive(:terminate)
124
133
 
125
- ami_manager.destroy
134
+ ami_manager.clean_environment
126
135
  end
127
136
 
128
137
  context 'when an instance has the magical tag' do
@@ -136,6 +145,67 @@ module VmShepherd
136
145
  expect(instance2).to receive(:terminate)
137
146
  expect(persistent_instance).not_to receive(:terminate)
138
147
 
148
+ ami_manager.clean_environment
149
+ end
150
+ end
151
+ end
152
+
153
+ context 'when the instance has volumes that are NOT delete_on_termination' do
154
+ let(:instance1_attachment) do
155
+ instance_double(AWS::EC2::Attachment, volume: instance1_volume, delete_on_termination: false)
156
+ end
157
+
158
+ before do
159
+ allow(instance1).to receive(:terminate)
160
+ allow(instance2).to receive(:terminate)
161
+ end
162
+
163
+ it 'deletes the volumes' do
164
+ expect(instance1_volume).to receive(:delete)
165
+
166
+ ami_manager.clean_environment
167
+ end
168
+
169
+ context 'when the instance has not finished termination' do
170
+ before do
171
+ expect(instance1_volume).to receive(:delete).and_raise(AWS::EC2::Errors::VolumeInUse)
172
+ expect(instance1_volume).to receive(:delete).and_return(nil)
173
+ allow(ami_manager).to receive(:sleep)
174
+ end
175
+
176
+ it 'retries the delete' do
177
+ ami_manager.clean_environment
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ describe '#destroy' do
184
+ let(:elastic_ips) { instance_double(AWS::EC2::ElasticIpCollection) }
185
+ let(:elastic_ip) { instance_double(AWS::EC2::ElasticIp, instance: instance, allocation_id: elastic_ip_id) }
186
+ let(:instance) { instance_double(AWS::EC2::Instance, tags: {}) }
187
+
188
+ before do
189
+ allow(ec2).to receive(:elastic_ips).and_return(elastic_ips)
190
+ allow(elastic_ips).to receive(:each).and_yield(elastic_ip)
191
+ end
192
+
193
+ it 'terminates the VM that matches the IP' do
194
+ expect(instance).to receive(:terminate)
195
+
196
+ ami_manager.destroy
197
+ end
198
+
199
+ context 'when an instance has the magical tag' do
200
+ let(:persistent_instance) { instance_double(AWS::EC2::Instance, tags: persist_tag) }
201
+ let(:instances) { [instance1, instance2, persistent_instance] }
202
+
203
+ context 'when there is no instance attached' do
204
+ before do
205
+ allow(elastic_ip).to receive(:instance).and_return(nil)
206
+ end
207
+
208
+ it 'does not explode' do
139
209
  ami_manager.destroy
140
210
  end
141
211
  end
@@ -12,7 +12,7 @@ module VmShepherd
12
12
  end
13
13
  let(:vcloud_manager) { instance_double(VcloudManager) }
14
14
 
15
- it 'uses VcloudManager::Deployer to launch a vm' do
15
+ it 'uses VcloudManager to launch a vm' do
16
16
  expect(VcloudManager).to receive(:new).
17
17
  with(
18
18
  {
@@ -51,7 +51,7 @@ module VmShepherd
51
51
  end
52
52
  let(:ova_manager) { instance_double(VsphereManager) }
53
53
 
54
- it 'uses VsphereManager::Deployer to launch a vm' do
54
+ it 'uses VsphereManager to launch a vm' do
55
55
  expect(VsphereManager).to receive(:new).with(
56
56
  settings.vm_deployer.vcenter_creds.ip,
57
57
  settings.vm_deployer.vcenter_creds.username,
@@ -100,7 +100,7 @@ module VmShepherd
100
100
  }
101
101
  end
102
102
 
103
- it 'uses AwsManager::Deployer to launch a VM' do
103
+ it 'uses AwsManager to launch a VM' do
104
104
  expect(AwsManager).to receive(:new).with(aws_options).and_return(ams_manager)
105
105
  expect(ams_manager).to receive(:deploy).with(ami_file_path)
106
106
  manager.deploy(path: ami_file_path)
@@ -137,7 +137,7 @@ module VmShepherd
137
137
  }
138
138
  end
139
139
 
140
- it 'uses QCow2Manager to launch a VM' do
140
+ it 'uses OpenstackManager to launch a VM' do
141
141
  expect(OpenstackManager).to receive(:new).with(openstack_options).and_return(qcow2_manager)
142
142
  expect(qcow2_manager).to receive(:deploy).with(qcow2_file_path, openstack_vm_options)
143
143
  manager.deploy(path: qcow2_file_path)
@@ -162,7 +162,7 @@ module VmShepherd
162
162
  end
163
163
  let(:vcloud_manager) { instance_double(VcloudManager) }
164
164
 
165
- it 'uses VcloudManager::Destroyer to destroy a vm' do
165
+ it 'uses VcloudManager to destroy a vm' do
166
166
  expect(VcloudManager).to receive(:new).with(
167
167
  {
168
168
  url: settings.vapp_deployer.creds.url,
@@ -177,7 +177,8 @@ module VmShepherd
177
177
  },
178
178
  instance_of(Logger)
179
179
  ).and_return(vcloud_manager)
180
- expect(vcloud_manager).to receive(:destroy).with(settings.vapp_deployer.vapp.name)
180
+ vapp_names = [settings.vapp_deployer.vapp.ops_manager_name] + settings.vapp_deployer.vapp.product_names
181
+ expect(vcloud_manager).to receive(:destroy).with(vapp_names)
181
182
 
182
183
  manager.destroy
183
184
  end
@@ -189,14 +190,14 @@ module VmShepherd
189
190
  end
190
191
  let(:ova_manager) { instance_double(VsphereManager) }
191
192
 
192
- it 'uses VsphereManager::Destroyer to destroy a vm' do
193
+ it 'uses VsphereManager to destroy a vm' do
193
194
  expect(VsphereManager).to receive(:new).with(
194
195
  settings.vm_deployer.vcenter_creds.ip,
195
196
  settings.vm_deployer.vcenter_creds.username,
196
197
  settings.vm_deployer.vcenter_creds.password,
197
198
  settings.vm_deployer.vsphere.datacenter,
198
199
  ).and_return(ova_manager)
199
- expect(ova_manager).to receive(:destroy).with(settings.vm_deployer.vsphere.folder)
200
+ expect(ova_manager).to receive(:destroy).with(settings.vm_deployer.vm.ip)
200
201
 
201
202
  manager.destroy
202
203
  end
@@ -207,7 +208,7 @@ module VmShepherd
207
208
  RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
208
209
  end
209
210
  let(:ams_manager) { instance_double(AwsManager) }
210
- let(:ami_destroy_options) do
211
+ let(:ami_options) do
211
212
  {
212
213
  aws_access_key: 'aws-access-key',
213
214
  aws_secret_key: 'aws-secret-key',
@@ -220,8 +221,8 @@ module VmShepherd
220
221
  }
221
222
  end
222
223
 
223
- it 'uses AwsManager::Deployer to launch a VM' do
224
- expect(AwsManager).to receive(:new).with(ami_destroy_options).and_return(ams_manager)
224
+ it 'uses AwsManager to destroy a VM' do
225
+ expect(AwsManager).to receive(:new).with(ami_options).and_return(ams_manager)
225
226
  expect(ams_manager).to receive(:destroy)
226
227
  manager.destroy
227
228
  end
@@ -257,7 +258,7 @@ module VmShepherd
257
258
  }
258
259
  end
259
260
 
260
- it 'uses QCow2Manager to destroy a VM' do
261
+ it 'uses OpenstackManager to destroy a VM' do
261
262
  expect(OpenstackManager).to receive(:new).with(openstack_options).and_return(qcow2_manager)
262
263
  expect(qcow2_manager).to receive(:destroy).with(openstack_vm_options)
263
264
  manager.destroy
@@ -270,7 +271,119 @@ module VmShepherd
270
271
  end
271
272
 
272
273
  it 'raises an exception' do
273
- expect { manager.deploy(path: 'FAKE_PATH') }.to raise_error(Shepherd::InvalidIaas)
274
+ expect { manager.destroy }.to raise_error(Shepherd::InvalidIaas)
275
+ end
276
+ end
277
+ end
278
+
279
+ describe '#clean_environment' do
280
+ context 'when IAAS is vcloud' do
281
+ let(:settings) do
282
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vcloud.yml')))
283
+ end
284
+ let(:vcloud_manager) { instance_double(VcloudManager) }
285
+
286
+ it 'uses VcloudManager to destroy a vm' do
287
+ expect(VcloudManager).to receive(:new).with(
288
+ {
289
+ url: settings.vapp_deployer.creds.url,
290
+ organization: settings.vapp_deployer.creds.organization,
291
+ user: settings.vapp_deployer.creds.user,
292
+ password: settings.vapp_deployer.creds.password,
293
+ },
294
+ {
295
+ vdc: settings.vapp_deployer.vdc.name,
296
+ catalog: settings.vapp_deployer.vdc.catalog,
297
+ network: settings.vapp_deployer.vdc.network,
298
+ },
299
+ instance_of(Logger)
300
+ ).and_return(vcloud_manager)
301
+ expect(vcloud_manager).to receive(:clean_environment)
302
+
303
+ manager.clean_environment
304
+ end
305
+ end
306
+
307
+ context 'when IAAS is vsphere' do
308
+ let(:settings) do
309
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vsphere.yml')))
310
+ end
311
+ let(:ova_manager) { instance_double(VsphereManager) }
312
+ let(:clean_environment_options) do
313
+ {
314
+ datacenter_folders_to_clean: settings.vm_deployer.vsphere.datacenter_folders_to_clean,
315
+ datastore: settings.vm_deployer.vsphere.datastore,
316
+ datastore_folders_to_clean: settings.vm_deployer.vsphere.datastore_folders_to_clean,
317
+ }
318
+ end
319
+
320
+ it 'uses VsphereManager to destroy a vm' do
321
+ expect(VsphereManager).to receive(:new).with(
322
+ settings.vm_deployer.vcenter_creds.ip,
323
+ settings.vm_deployer.vcenter_creds.username,
324
+ settings.vm_deployer.vcenter_creds.password,
325
+ settings.vm_deployer.vsphere.datacenter,
326
+ ).and_return(ova_manager)
327
+ expect(ova_manager).to receive(:clean_environment).with(clean_environment_options)
328
+
329
+ manager.clean_environment
330
+ end
331
+ end
332
+
333
+ context 'when IAAS is AWS' do
334
+ let(:settings) do
335
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
336
+ end
337
+ let(:ams_manager) { instance_double(AwsManager) }
338
+ let(:ami_options) do
339
+ {
340
+ aws_access_key: 'aws-access-key',
341
+ aws_secret_key: 'aws-secret-key',
342
+ ssh_key_name: 'ssh-key-name',
343
+ security_group_id: 'security-group-id',
344
+ public_subnet_id: 'public-subnet-id',
345
+ private_subnet_id: 'private-subnet-id',
346
+ elastic_ip_id: 'elastic-ip-id',
347
+ vm_name: 'vm-name'
348
+ }
349
+ end
350
+
351
+ it 'uses AwsManager to destroy a VM' do
352
+ expect(AwsManager).to receive(:new).with(ami_options).and_return(ams_manager)
353
+ expect(ams_manager).to receive(:clean_environment)
354
+ manager.clean_environment
355
+ end
356
+ end
357
+
358
+ context 'when IAAS is Openstack' do
359
+ let(:settings) do
360
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'openstack.yml')))
361
+ end
362
+ let(:qcow2_manager) { instance_double(OpenstackManager) }
363
+ let(:qcow2_file_path) { 'PATH_TO_QCOW2_FILE' }
364
+ let(:openstack_options) do
365
+ {
366
+ auth_url: 'http://example.com/version/tokens',
367
+ username: 'username',
368
+ api_key: 'api-key',
369
+ tenant: 'tenant',
370
+ }
371
+ end
372
+
373
+ it 'uses OpenstackManager to destroy a VM' do
374
+ expect(OpenstackManager).to receive(:new).with(openstack_options).and_return(qcow2_manager)
375
+ expect(qcow2_manager).to receive(:clean_environment)
376
+ manager.clean_environment
377
+ end
378
+ end
379
+
380
+ context 'when IAAS is unknown' do
381
+ let(:settings) do
382
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'unknown.yml')))
383
+ end
384
+
385
+ it 'raises an exception' do
386
+ expect { manager.clean_environment }.to raise_error(Shepherd::InvalidIaas)
274
387
  end
275
388
  end
276
389
  end
@@ -29,7 +29,6 @@ module VmShepherd
29
29
  gateway: 'FAKE_GATEWAY',
30
30
  dns: 'FAKE_DNS',
31
31
  ntp: 'FAKE_NTP',
32
- ip: 'FAKE_IP',
33
32
  netmask: 'FAKE_NETMASK',
34
33
  }
35
34
  end
@@ -308,7 +307,7 @@ module VmShepherd
308
307
  logger,
309
308
  ).and_return(client)
310
309
 
311
- vcloud_manager.destroy(vapp_name)
310
+ vcloud_manager.destroy([vapp_name])
312
311
  end
313
312
 
314
313
  context 'when an VCloudSdk::ObjectNotFoundError is thrown' do
@@ -325,13 +324,13 @@ module VmShepherd
325
324
  it 'catches the error' do
326
325
  allow(client).to receive(:find_vdc_by_name).and_raise(VCloudSdk::ObjectNotFoundError)
327
326
 
328
- expect { vcloud_manager.destroy(vapp_name) }.not_to raise_error
327
+ expect { vcloud_manager.destroy([vapp_name]) }.not_to raise_error
329
328
  end
330
329
 
331
330
  it 'deletes to catalog' do
332
331
  expect(client).to receive(:delete_catalog_by_name).with(location.fetch(:catalog))
333
332
 
334
- vcloud_manager.destroy(vapp_name)
333
+ vcloud_manager.destroy([vapp_name])
335
334
  end
336
335
  end
337
336
  end
@@ -356,7 +355,7 @@ module VmShepherd
356
355
  logger,
357
356
  ).and_return(client)
358
357
 
359
- vcloud_manager.destroy(vapp_name)
358
+ vcloud_manager.destroy([vapp_name])
360
359
  end
361
360
  end
362
361
  end
@@ -6,11 +6,61 @@ module VmShepherd
6
6
  let(:username) { 'FAKE_USERNAME' }
7
7
  let(:password) { 'FAKE_PASSWORD' }
8
8
  let(:datacenter_name) { 'FAKE_DATACENTER_NAME' }
9
+ let(:vm) { instance_double(RbVmomi::VIM::VirtualMachine, name: 'vm_name') }
9
10
 
10
- subject(:vsphere_manager) { VsphereManager.new(host, username, password, datacenter_name) }
11
+ subject(:vsphere_manager) do
12
+ manager = VsphereManager.new(host, username, password, datacenter_name)
13
+ manager.logger = Logger.new(StringIO.new)
14
+ manager
15
+ end
11
16
 
12
17
  it 'loads' do
13
18
  expect { vsphere_manager }.not_to raise_error
14
19
  end
20
+
21
+ describe 'destroy' do
22
+ let(:datacenter) { instance_double(RbVmomi::VIM::Datacenter, vmFolder: vm_folder) }
23
+ let(:vm_folder) { instance_double(RbVmomi::VIM::Folder) }
24
+ let(:ip_address) { '127.0.0.1' }
25
+
26
+ before do
27
+ allow(vsphere_manager).to receive(:datacenter).and_return(datacenter)
28
+ allow(vm_folder).to receive(:findByIp).with(ip_address).and_return(vm)
29
+ end
30
+
31
+ it 'destroys the VM that matches the given ip address' do
32
+ expect(vsphere_manager).to receive(:power_off_vm).with(vm)
33
+ expect(vsphere_manager).to receive(:destroy_vm).with(vm)
34
+
35
+ vsphere_manager.destroy(ip_address)
36
+ end
37
+
38
+ context 'when the vm does not exist' do
39
+ before do
40
+ allow(vm_folder).to receive(:findByIp).and_return(nil)
41
+ end
42
+
43
+ it 'does not explode' do
44
+ expect(vsphere_manager).not_to receive(:power_off_vm)
45
+ expect(vsphere_manager).not_to receive(:destroy_vm)
46
+
47
+ vsphere_manager.destroy(ip_address)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'destroy_vm' do
53
+ let(:destroy_task) { instance_double(RbVmomi::VIM::Task) }
54
+
55
+ before do
56
+ allow(vm).to receive(:Destroy_Task).and_return(destroy_task)
57
+ end
58
+
59
+ it 'runs the Destroy_Task and waits for completion' do
60
+ expect(destroy_task).to receive(:wait_for_completion)
61
+
62
+ vsphere_manager.destroy_vm(vm)
63
+ end
64
+ end
15
65
  end
16
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vm_shepherd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ops Manager Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-08 00:00:00.000000000 Z
11
+ date: 2015-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-v1