vm_shepherd 0.0.1

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.
@@ -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