vm_shepherd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,146 @@
1
+ require 'vm_shepherd/ami_manager'
2
+
3
+ module VmShepherd
4
+ RSpec.describe AmiManager do
5
+ let(:access_key) { 'access-key' }
6
+ let(:secret_key) { 'secret-key' }
7
+ let(:ami_id) { 'ami-deadbeef' }
8
+ let(:ami_file_path) { Tempfile.new('ami-id-file').tap { |f| f.write("#{ami_id}\n"); f.close }.path }
9
+ let(:ec2) { double('AWS.ec2') }
10
+
11
+ let(:aws_options) do
12
+ {
13
+ aws_access_key: 'aws-access-key',
14
+ aws_secret_key: 'aws-secret-key',
15
+ ssh_key_name: 'ssh-key-name',
16
+ security_group_id: 'security-group-id',
17
+ public_subnet_id: 'public-subnet-id',
18
+ private_subnet_id: 'private-subnet-id',
19
+ elastic_ip_id: 'elastic-ip-id',
20
+ vm_name: 'Ops Manager: clean_install_spec'
21
+ }
22
+ end
23
+
24
+ subject(:ami_manager) { AmiManager.new(aws_options) }
25
+
26
+ before do
27
+ expect(AWS).to receive(:config).with(
28
+ access_key_id: aws_options.fetch(:aws_access_key),
29
+ secret_access_key: aws_options.fetch(:aws_secret_key),
30
+ region: 'us-east-1',
31
+ )
32
+
33
+ allow(AWS).to receive(:ec2).and_return(ec2)
34
+ end
35
+
36
+ describe '#deploy' do
37
+ let(:instance) { instance_double(AWS::EC2::Instance, status: :running, associate_elastic_ip: nil, add_tag: nil) }
38
+ let(:instances) { instance_double(AWS::EC2::InstanceCollection, create: instance) }
39
+
40
+ before do
41
+ allow(ec2).to receive(:instances).and_return(instances)
42
+ allow(ami_manager).to receive(:sleep) # speed up retry logic
43
+ end
44
+
45
+ it 'creates an instance using AWS SDK v1' do
46
+ expect(ec2).to receive_message_chain(:instances, :create).with(
47
+ image_id: ami_id,
48
+ key_name: 'ssh-key-name',
49
+ security_group_ids: ['security-group-id'],
50
+ subnet: aws_options.fetch(:public_subnet_id),
51
+ private_ip_address: AmiManager::OPS_MANAGER_PRIVATE_IP,
52
+ instance_type: 'm3.medium').and_return(instance)
53
+
54
+ ami_manager.deploy(ami_file_path)
55
+ end
56
+
57
+ context 'when the ip address is in use' do
58
+ it 'retries until the IP address is available' do
59
+ expect(instances).to receive(:create).and_raise(AWS::EC2::Errors::InvalidIPAddress::InUse).once
60
+ expect(instances).to receive(:create).and_return(instance).once
61
+
62
+ ami_manager.deploy(ami_file_path)
63
+ end
64
+
65
+ it 'stops retrying after 60 times' do
66
+ expect(instances).to receive(:create).and_raise(AWS::EC2::Errors::InvalidIPAddress::InUse).
67
+ exactly(AmiManager::RETRY_LIMIT).times
68
+
69
+ expect { ami_manager.deploy(ami_file_path) }.to raise_error(AmiManager::RetryLimitExceeded)
70
+ end
71
+ end
72
+
73
+ it 'does not return until the instance is running' do
74
+ expect(instance).to receive(:status).and_return(:pending, :pending, :pending, :running)
75
+
76
+ ami_manager.deploy(ami_file_path)
77
+ end
78
+
79
+ it 'handles API endpoints not knowing (right away) about the instance created' do
80
+ expect(instance).to receive(:status).and_raise(AWS::EC2::Errors::InvalidInstanceID::NotFound).
81
+ exactly(AmiManager::RETRY_LIMIT - 1).times
82
+ expect(instance).to receive(:status).and_return(:running).once
83
+
84
+ ami_manager.deploy(ami_file_path)
85
+ end
86
+
87
+ it 'stops retrying after 60 times' do
88
+ expect(instance).to receive(:status).and_return(:pending).
89
+ exactly(AmiManager::RETRY_LIMIT).times
90
+
91
+ expect { ami_manager.deploy(ami_file_path) }.to raise_error(AmiManager::RetryLimitExceeded)
92
+ end
93
+
94
+ it 'attaches the elastic IP' do
95
+ expect(instance).to receive(:associate_elastic_ip).with(aws_options.fetch(:elastic_ip_id))
96
+
97
+ ami_manager.deploy(ami_file_path)
98
+ end
99
+
100
+ it 'tags the instance with a name' do
101
+ expect(instance).to receive(:add_tag).with('Name', value: aws_options.fetch(:vm_name))
102
+
103
+ ami_manager.deploy(ami_file_path)
104
+ end
105
+ end
106
+
107
+ describe '#destroy' do
108
+ let(:subnets) { instance_double(AWS::EC2::SubnetCollection) }
109
+ let(:subnet1) { instance_double(AWS::EC2::Subnet, instances: subnet1_instances) }
110
+ let(:subnet2) { instance_double(AWS::EC2::Subnet, instances: subnet2_instances) }
111
+ let(:instance1) { instance_double(AWS::EC2::Instance, tags: {}) }
112
+ let(:instance2) { instance_double(AWS::EC2::Instance, tags: {}) }
113
+ let(:subnet1_instances) { [instance1] }
114
+ let(:subnet2_instances) { [instance2] }
115
+
116
+ before do
117
+ allow(ec2).to receive(:subnets).and_return(subnets)
118
+ allow(subnets).to receive(:[]).with('public-subnet-id').and_return(subnet1)
119
+ allow(subnets).to receive(:[]).with('private-subnet-id').and_return(subnet2)
120
+ end
121
+
122
+ it 'terminates all VMs in the subnet' do
123
+ expect(instance1).to receive(:terminate)
124
+ expect(instance2).to receive(:terminate)
125
+
126
+ ami_manager.destroy
127
+ end
128
+
129
+ context 'when an instance has the magical tag' do
130
+ let(:persistent_instance) { instance_double(AWS::EC2::Instance, tags: persist_tag) }
131
+ let(:instances) { [instance1, instance2, persistent_instance] }
132
+
133
+ context 'when the do not terminate tag is present' do
134
+ let(:persist_tag) { { AmiManager::DO_NOT_TERMINATE_TAG_KEY => 'any value' } }
135
+ it 'does not attempt to terminate this instance' do
136
+ expect(instance1).to receive(:terminate)
137
+ expect(instance2).to receive(:terminate)
138
+ expect(persistent_instance).not_to receive(:terminate)
139
+
140
+ ami_manager.destroy
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,56 @@
1
+ require 'vm_shepherd/ova_manager/base'
2
+
3
+ module VmShepherd
4
+ module OvaManager
5
+ RSpec.describe Base do
6
+ subject(:base) { Base.new(vcenter) }
7
+
8
+ let(:vcenter) { { host: 'host', user: 'user', password: 'password' } }
9
+ let(:search_index) { double('searchIndex') }
10
+ let(:connection) { double('RbVmomi::VIM', searchIndex: search_index) }
11
+ let(:datacenter) { FakeDatacenter.new }
12
+
13
+ class FakeDatacenter < RbVmomi::VIM::Datacenter
14
+ def initialize
15
+ end
16
+ end
17
+
18
+ before { allow(RbVmomi::VIM).to receive(:connect).and_return(connection) }
19
+
20
+ def stub_search(find_result)
21
+ allow(search_index).to receive(:FindByInventoryPath).and_return(find_result)
22
+ end
23
+
24
+ describe '#find_datacenter' do
25
+ it 'should return datacenter with valid name' do
26
+ stub_search(datacenter)
27
+ expect(base.find_datacenter('valid_datacenter')).to be(datacenter)
28
+ end
29
+
30
+ it 'should return nil with invalid name' do
31
+ stub_search(nil)
32
+ expect(base.find_datacenter('does_not_exist')).to be_nil
33
+ end
34
+
35
+ it 'should return nil when find returns non-datacenter' do
36
+ stub_search(double)
37
+ expect(base.find_datacenter('non_a_datacenter')).to be_nil
38
+ end
39
+ end
40
+
41
+ describe '#connection' do
42
+ it 'should return a connection' do
43
+ conn = base.send(:connection)
44
+ expect(conn).to be(connection)
45
+ end
46
+
47
+ it 'should return the same connection on subsequent invocations' do
48
+ conn = base.send(:connection)
49
+ conn_again = base.send(:connection)
50
+ expect(conn).to be(conn_again)
51
+ expect(RbVmomi::VIM).to have_received(:connect).once
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,134 @@
1
+ require 'tempfile'
2
+ require 'vm_shepherd/ova_manager/deployer'
3
+
4
+ module VmShepherd
5
+ module OvaManager
6
+ RSpec.describe Deployer do
7
+ let(:connection) { double('connection') }
8
+ let(:cluster) { double('cluster', resourcePool: cluster_resource_pool) }
9
+ let(:cluster_resource_pool) { double(:cluster_resource_pool, resourcePool: [a_resource_pool, another_resource_pool]) }
10
+
11
+ let(:a_resource_pool) { double('resource_pool', name: 'resource_pool_name') }
12
+ let(:another_resource_pool) { double('another_resource_pool', name: 'another_resource_pool_name') }
13
+
14
+ let(:target_folder) { double('target_folder') }
15
+ let(:datastore) { double('datastore') }
16
+ let(:network) { double('network', name: 'network') }
17
+ let(:datacenter) { double('datacenter', network: [network]) }
18
+ let(:cached_ova_deployer) {
19
+ double('CachedOvaDeployer', upload_ovf_as_template: template, linked_clone: linked_clone)
20
+ }
21
+ let(:template) { double('template', name: 'template name') }
22
+ let(:ova_path) { File.join(SPEC_ROOT, 'fixtures', 'ova_manager', 'foo.ova') }
23
+ let(:linked_clone) { double('linked clone', guest_ip: '1.1.1.1') }
24
+ let(:vcenter_config) {
25
+ {
26
+ host: 'foo',
27
+ user: 'bar',
28
+ password: 'secret'
29
+ }
30
+ }
31
+
32
+ subject(:deployer) { Deployer.new(vcenter_config, location) }
33
+
34
+ before do
35
+ allow(deployer).to receive(:system).with(/cd .* && tar xfv .*/).and_call_original
36
+ allow(deployer).to receive(:system).with(/nc -z -w 5 .* 443/).and_return(false)
37
+
38
+ allow(datacenter).to receive(:find_compute_resource).with('cluster').and_return(cluster)
39
+
40
+ allow(datacenter).to receive(:find_datastore).with('datastore').and_return(datastore)
41
+
42
+ allow(linked_clone).to receive_message_chain(:ReconfigVM_Task, :wait_for_completion)
43
+ allow(linked_clone).to receive_message_chain(:PowerOnVM_Task, :wait_for_completion)
44
+
45
+ allow(RbVmomi::VIM).to receive(:connect).with({
46
+ host: vcenter_config[:host],
47
+ user: vcenter_config[:user],
48
+ password: vcenter_config[:password],
49
+ ssl: true,
50
+ insecure: true
51
+ }).and_return connection
52
+
53
+ allow(deployer).to receive(:find_datacenter).and_return(datacenter)
54
+
55
+ allow(datacenter).to receive_message_chain(:vmFolder, :traverse).
56
+ with('target_folder', RbVmomi::VIM::Folder, true).and_return(target_folder)
57
+ allow(datacenter).to receive(:networkFolder).and_return(
58
+ double(:networkFolder).tap do |network_folder|
59
+ allow(network_folder).to receive(:traverse).with('network').and_return(network)
60
+ end
61
+ )
62
+ end
63
+
64
+ context 'resource pool is a parameter' do
65
+ let(:location) {
66
+ {
67
+ connection: connection,
68
+ network: 'network',
69
+ cluster: 'cluster',
70
+ folder: 'target_folder',
71
+ datastore: 'datastore',
72
+ datacenter: 'datacenter',
73
+ resource_pool: resource_pool_name
74
+ }
75
+ }
76
+
77
+ context 'resource pool can be found' do
78
+ let(:resource_pool_name) { a_resource_pool.name }
79
+
80
+ it 'uses VsphereClient::CachedOvfDeployer to deploy an OVA within a resource pool' do
81
+ expect(VsphereClients::CachedOvfDeployer).to receive(:new).with(
82
+ connection,
83
+ network,
84
+ cluster,
85
+ a_resource_pool,
86
+ target_folder,
87
+ target_folder,
88
+ datastore
89
+ ).and_return(cached_ova_deployer)
90
+
91
+ deployer.deploy('foo', ova_path, { ip: '1.1.1.1' })
92
+ end
93
+ end
94
+
95
+ context 'resource pool cannot be found' do
96
+ let(:resource_pool_name) { 'i am no body' }
97
+
98
+ it 'uses VsphereClient::CachedOvfDeployer to deploy an OVA within a resource pool' do
99
+ expect {
100
+ deployer.deploy('foo', ova_path, { ip: '1.1.1.1' })
101
+ }.to raise_error(/Failed to find resource pool '#{resource_pool_name}'/)
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'when there is no resource pool in location' do
107
+ let(:location) {
108
+ {
109
+ connection: connection,
110
+ network: 'network',
111
+ cluster: 'cluster',
112
+ folder: 'target_folder',
113
+ datastore: 'datastore',
114
+ datacenter: 'datacenter',
115
+ }
116
+ }
117
+
118
+ it 'uses the cluster resource pool' do
119
+ expect(VsphereClients::CachedOvfDeployer).to receive(:new).with(
120
+ connection,
121
+ network,
122
+ cluster,
123
+ cluster_resource_pool,
124
+ target_folder,
125
+ target_folder,
126
+ datastore
127
+ ).and_return(cached_ova_deployer)
128
+
129
+ deployer.deploy('foo', ova_path, { ip: '1.1.1.1' })
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,42 @@
1
+ require 'vm_shepherd/ova_manager/destroyer'
2
+
3
+ module VmShepherd
4
+ module OvaManager
5
+ RSpec.describe Destroyer do
6
+ subject(:destroyer) { Destroyer.new('datacenter_name', vcenter_config) }
7
+ let(:vcenter_config) { { host: 'host', user: 'user', password: 'password' } }
8
+
9
+ describe '#clean_folder' do
10
+ # Crappy temporary test to at least ensure that Destroyer requires the appropriate files
11
+ it 'is wired up correctly' do
12
+ connection = double('connection', serviceInstance: double('serviceInstance'))
13
+ datacenter = double('datacenter')
14
+ allow(datacenter).to receive(:is_a?).with(RbVmomi::VIM::Datacenter).and_return(true)
15
+ vm_folder_client = double('vm_folder_client')
16
+
17
+ expect(RbVmomi::VIM).to receive(:connect).with(
18
+ host: 'host',
19
+ user: 'user',
20
+ password: 'password',
21
+ ssl: true,
22
+ insecure: true,
23
+ ).and_return(connection)
24
+ expect(connection).to receive(:searchIndex).and_return(
25
+ double(:searchIndex).tap do |search_index|
26
+ expect(search_index).to receive(:FindByInventoryPath).
27
+ with(inventoryPath: 'datacenter_name').
28
+ and_return(datacenter)
29
+ end
30
+ )
31
+ expect(VsphereClients::VmFolderClient).to receive(:new).
32
+ with(datacenter, instance_of(Logger)).
33
+ and_return(vm_folder_client)
34
+ expect(vm_folder_client).to receive(:delete_folder).with('folder_name')
35
+ expect(vm_folder_client).to receive(:create_folder).with('folder_name')
36
+
37
+ destroyer.clean_folder('folder_name')
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,213 @@
1
+ require 'vm_shepherd/shepherd'
2
+ require 'recursive_open_struct'
3
+
4
+ module VmShepherd
5
+ RSpec.describe Shepherd do
6
+ subject(:manager) { Shepherd.new(settings: settings) }
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
+ describe '#deploy' do
13
+ context 'with vcloud settings' do
14
+ let(:settings) do
15
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vcloud.yml')))
16
+ end
17
+ let(:deployer) { instance_double(VappManager::Deployer) }
18
+
19
+ it 'uses VappManager::Deployer to launch a vm' do
20
+ expect(VappManager::Deployer).to receive(:new).
21
+ with(
22
+ {
23
+ url: settings.vapp_deployer.creds.url,
24
+ organization: settings.vapp_deployer.creds.organization,
25
+ user: settings.vapp_deployer.creds.user,
26
+ password: settings.vapp_deployer.creds.password,
27
+ },
28
+ {
29
+ vdc: settings.vapp_deployer.vdc.name,
30
+ catalog: settings.vapp_deployer.vdc.catalog,
31
+ network: settings.vapp_deployer.vdc.network,
32
+ },
33
+ instance_of(Logger)
34
+ ).and_return(deployer)
35
+
36
+ expect(deployer).to receive(:deploy).with(
37
+ 'FAKE_PATH',
38
+ {
39
+ name: settings.vapp_deployer.vapp.name,
40
+ ip: settings.vapp_deployer.vapp.ip,
41
+ gateway: settings.vapp_deployer.vapp.gateway,
42
+ netmask: settings.vapp_deployer.vapp.netmask,
43
+ dns: settings.vapp_deployer.vapp.dns,
44
+ ntp: settings.vapp_deployer.vapp.ntp,
45
+ }
46
+ )
47
+
48
+ manager.deploy(path: 'FAKE_PATH')
49
+ end
50
+ end
51
+
52
+ context 'with vsphere settings' do
53
+ let(:settings) do
54
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vsphere.yml')))
55
+ end
56
+ let(:deployer) { instance_double(OvaManager::Deployer) }
57
+
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)
75
+
76
+ expect(deployer).to receive(:deploy).with(
77
+ Shepherd::VSPHERE_TEMPLATE_PREFIX,
78
+ 'FAKE_PATH',
79
+ {
80
+ ip: settings.vm_deployer.vm.ip,
81
+ gateway: settings.vm_deployer.vm.gateway,
82
+ netmask: settings.vm_deployer.vm.netmask,
83
+ dns: settings.vm_deployer.vm.dns,
84
+ ntp_servers: settings.vm_deployer.vm.ntp_servers,
85
+ }
86
+ )
87
+
88
+ manager.deploy(path: 'FAKE_PATH')
89
+ end
90
+ end
91
+
92
+ context 'with AWS settings' do
93
+ let(:settings) do
94
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
95
+ end
96
+ let(:ams_manager) { instance_double(AmiManager) }
97
+ let(:ami_file_path) { 'PATH_TO_AMI_FILE' }
98
+ let(:aws_options) do
99
+ {
100
+ aws_access_key: 'aws-access-key',
101
+ aws_secret_key: 'aws-secret-key',
102
+ ssh_key_name: 'ssh-key-name',
103
+ security_group_id: 'security-group-id',
104
+ public_subnet_id: 'public-subnet-id',
105
+ private_subnet_id: 'private-subnet-id',
106
+ elastic_ip_id: 'elastic-ip-id',
107
+ vm_name: 'vm-name'
108
+ }
109
+ end
110
+
111
+ it 'uses AwsManager::Deployer to launch a VM' do
112
+ expect(AmiManager).to receive(:new).with(aws_options).and_return(ams_manager)
113
+ expect(ams_manager).to receive(:deploy).with(ami_file_path)
114
+ manager.deploy(path: ami_file_path)
115
+ end
116
+ end
117
+
118
+ context 'when IAAS is unknown' do
119
+ let(:settings) do
120
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'unknown.yml')))
121
+ end
122
+
123
+ it 'raises an exception' do
124
+ expect { manager.deploy(path: 'FAKE_PATH') }.to raise_error(Shepherd::InvalidIaas)
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#destroy' do
130
+ context 'when IAAS is vcloud' do
131
+ let(:settings) do
132
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vcloud.yml')))
133
+ end
134
+ let(:destroyer) { instance_double(VappManager::Destroyer) }
135
+
136
+ it 'uses VappManager::Destroyer to destroy a vm' do
137
+ expect(VappManager::Destroyer).to receive(:new).with(
138
+ {
139
+ url: settings.vapp_deployer.creds.url,
140
+ organization: settings.vapp_deployer.creds.organization,
141
+ user: settings.vapp_deployer.creds.user,
142
+ password: settings.vapp_deployer.creds.password,
143
+ },
144
+ {
145
+ vdc: settings.vapp_deployer.vdc.name,
146
+ catalog: settings.vapp_deployer.vdc.catalog,
147
+ },
148
+ instance_of(Logger)
149
+ ).and_return(destroyer)
150
+ expect(destroyer).to receive(:destroy).with(settings.vapp_deployer.vapp.name)
151
+
152
+ manager.destroy
153
+ end
154
+ end
155
+
156
+ context 'when IAAS is vsphere' do
157
+ let(:settings) do
158
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'vsphere.yml')))
159
+ end
160
+ let(:destroyer) { instance_double(OvaManager::Destroyer) }
161
+
162
+ it 'uses OvaManager::Destroyer to destroy a vm' do
163
+ expect(OvaManager::Destroyer).to receive(:new).with(
164
+ 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)
172
+
173
+ manager.destroy
174
+ end
175
+ end
176
+
177
+ context 'when IAAS is AWS' do
178
+ let(:settings) do
179
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'aws.yml')))
180
+ end
181
+ let(:ams_manager) { instance_double(AmiManager) }
182
+ let(:ami_destroy_options) do
183
+ {
184
+ aws_access_key: 'aws-access-key',
185
+ aws_secret_key: 'aws-secret-key',
186
+ ssh_key_name: 'ssh-key-name',
187
+ security_group_id: 'security-group-id',
188
+ public_subnet_id: 'public-subnet-id',
189
+ private_subnet_id: 'private-subnet-id',
190
+ elastic_ip_id: 'elastic-ip-id',
191
+ vm_name: 'vm-name'
192
+ }
193
+ end
194
+
195
+ it 'uses AwsManager::Deployer to launch a VM' do
196
+ expect(AmiManager).to receive(:new).with(ami_destroy_options).and_return(ams_manager)
197
+ expect(ams_manager).to receive(:destroy)
198
+ manager.destroy
199
+ end
200
+ end
201
+
202
+ context 'when IAAS is unknown' do
203
+ let(:settings) do
204
+ RecursiveOpenStruct.new(YAML.load_file(File.join(SPEC_ROOT, 'fixtures', 'shepherd', 'unknown.yml')))
205
+ end
206
+
207
+ it 'raises an exception' do
208
+ expect { manager.deploy(path: 'FAKE_PATH') }.to raise_error(Shepherd::InvalidIaas)
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end