vm_shepherd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/README.md +31 -0
- data/Rakefile +11 -0
- data/ci/run_specs.sh +11 -0
- data/lib/vm_shepherd/ami_manager.rb +81 -0
- data/lib/vm_shepherd/ova_manager/base.rb +31 -0
- data/lib/vm_shepherd/ova_manager/deployer.rb +202 -0
- data/lib/vm_shepherd/ova_manager/destroyer.rb +29 -0
- data/lib/vm_shepherd/ova_manager/open_monkey_patch.rb +14 -0
- data/lib/vm_shepherd/shepherd.rb +166 -0
- data/lib/vm_shepherd/vapp_manager/deployer.rb +151 -0
- data/lib/vm_shepherd/vapp_manager/destroyer.rb +46 -0
- data/lib/vm_shepherd/version.rb +3 -0
- data/spec/fixtures/ova_manager/foo.ova +0 -0
- data/spec/fixtures/shepherd/aws.yml +11 -0
- data/spec/fixtures/shepherd/unknown.yml +1 -0
- data/spec/fixtures/shepherd/vcloud.yml +19 -0
- data/spec/fixtures/shepherd/vsphere.yml +20 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/vm_shepherd/ami_manager_spec.rb +146 -0
- data/spec/vm_shepherd/ova_manager/base_spec.rb +56 -0
- data/spec/vm_shepherd/ova_manager/deployer_spec.rb +134 -0
- data/spec/vm_shepherd/ova_manager/destroyer_spec.rb +42 -0
- data/spec/vm_shepherd/shepherd_spec.rb +213 -0
- data/spec/vm_shepherd/vapp_manager/deployer_spec.rb +287 -0
- data/spec/vm_shepherd/vapp_manager/destroyer_spec.rb +104 -0
- data/vm_shepherd.gemspec +31 -0
- metadata +198 -0
@@ -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
|