vm_shepherd 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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