vm_shepherd 1.10.1 → 1.11.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: 4c28ff60aceef4bca731813761ce712123b7f941
4
- data.tar.gz: 055d66480ba8775dab95bd80554556654da54c55
3
+ metadata.gz: 763cd128f7302c5fb7250c56c5fb6f2902659a7b
4
+ data.tar.gz: aac10b49759fcb9b8c5b385e8f9a3bb4fc6b32d6
5
5
  SHA512:
6
- metadata.gz: 930db4b3aba4201353764122080c6badbc9ba2021c5ada38018738647bdbcfb14f65a647113abebb80b3d1a9bb9f04fe1cf051ebcf13ffcc3ccf002f7146310a
7
- data.tar.gz: f9fa8ea157d70fb41454e44716616b1b682d868d9c23cfab097e8e8d895c5302e5f65422552bccdc7e52338d42c731c1d42059a9050d9d0153d8037bcfe708cf
6
+ metadata.gz: b67e18537b886374536aaf33ec441b389c81ffeb283fcaff7ddff1c53899db708f9fceebc9b22ea90a70d29b810b56f04f6486d7689d17721cea9a8304247075
7
+ data.tar.gz: 7e0e37f43205234107f6350dde7dcbf3d6c409b4b25357fd486710b95504563cd7e3d825f7e5ab6365a8981b0913d9a99d21c1a5e4748b8c19a0ae72377ac1b1
@@ -1,7 +1,11 @@
1
1
  require 'vm_shepherd/shepherd'
2
+ require 'vm_shepherd/data_object'
2
3
  require 'vm_shepherd/aws_manager'
3
4
  require 'vm_shepherd/openstack_manager'
4
5
  require 'vm_shepherd/vcloud_manager'
6
+ require 'vm_shepherd/vcloud/deployer'
7
+ require 'vm_shepherd/vcloud/destroyer'
8
+ require 'vm_shepherd/vcloud/vapp_config'
5
9
  require 'vm_shepherd/vsphere_manager'
6
10
 
7
11
  module VmShepherd
@@ -0,0 +1,11 @@
1
+ module VmShepherd
2
+ module DataObject
3
+ def ==(other_obj)
4
+ return false unless self.class === other_obj
5
+
6
+ instance_variables.all? do |ivar_name|
7
+ self.instance_variable_get(ivar_name) == other_obj.instance_variable_get(ivar_name)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -184,7 +184,7 @@ module VmShepherd
184
184
 
185
185
  def vcloud_deploy_options(vm_shepherd_config)
186
186
  vm = vm_shepherd_config.vapp
187
- {
187
+ VmShepherd::Vcloud::VappConfig.new(
188
188
  name: vm.ops_manager_name,
189
189
  ip: vm.ip,
190
190
  gateway: vm.gateway,
@@ -193,7 +193,7 @@ module VmShepherd
193
193
  ntp: vm.ntp,
194
194
  catalog: vm_shepherd_config.vdc.catalog,
195
195
  network: vm_shepherd_config.vdc.network,
196
- }
196
+ )
197
197
  end
198
198
 
199
199
  def ami_manager
@@ -0,0 +1,23 @@
1
+ module VmShepherd
2
+ module Vcloud
3
+ class Deployer
4
+ def self.deploy_and_power_on_vapp(client:, ovf_dir:, vapp_config:, vdc_name:)
5
+ catalog = client.create_catalog(vapp_config.catalog)
6
+
7
+ # upload template and instantiate vapp
8
+ catalog.upload_vapp_template(vdc_name, vapp_config.name, ovf_dir)
9
+
10
+ # instantiate template
11
+ network_config = VCloudSdk::NetworkConfig.new(vapp_config.network, 'Network 1')
12
+ vapp = catalog.instantiate_vapp_template(vapp_config.name, vdc_name, vapp_config.name, nil, nil, network_config)
13
+
14
+ # reconfigure vm
15
+ vm = vapp.find_vm_by_name(vapp_config.name)
16
+ vm.product_section_properties = vapp_config.build_properties
17
+
18
+ # power on vapp
19
+ vapp.power_on
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ module VmShepherd
2
+ module Vcloud
3
+ class Destroyer
4
+ def initialize(client:, vdc_name:)
5
+ @client = client
6
+ @vdc_name = vdc_name
7
+ end
8
+
9
+ def delete_catalog_and_vapps(catalog, vapp_names, logger)
10
+ delete_vapps(vapp_names, logger)
11
+ delete_catalog(catalog)
12
+ end
13
+
14
+ private
15
+
16
+ def vdc
17
+ @vdc ||= @client.find_vdc_by_name(@vdc_name)
18
+ end
19
+
20
+ def delete_vapps(vapp_names, logger)
21
+ vapp_names.each do |vapp_name|
22
+ begin
23
+ delete_vapp(vapp_name)
24
+ rescue VCloudSdk::ObjectNotFoundError => e
25
+ logger.debug "Could not delete vapp '#{vapp_name}': #{e.inspect}"
26
+ end
27
+ end
28
+ end
29
+
30
+ def delete_catalog(catalog)
31
+ @client.delete_catalog_by_name(catalog) if @client.catalog_exists?(catalog)
32
+ end
33
+
34
+ def delete_vapp(vapp_name)
35
+ vapp = vdc.find_vapp_by_name(vapp_name)
36
+ vapp.vms.map do |vm|
37
+ vm.independent_disks.map do |disk|
38
+ vm.detach_disk(disk)
39
+ vdc.delete_disk_by_name(disk.name)
40
+ end
41
+ end
42
+ vapp.power_off
43
+ vapp.delete
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,78 @@
1
+ module VmShepherd
2
+ module Vcloud
3
+ class VappConfig
4
+ include VmShepherd::DataObject
5
+ attr_reader :name, :gateway, :dns, :ntp, :ip, :netmask, :catalog, :network
6
+
7
+ def initialize(name:, ip:, gateway:, netmask:, dns:, ntp:, catalog:, network:)
8
+ @name = name
9
+ @ip = ip
10
+ @gateway = gateway
11
+ @netmask = netmask
12
+ @dns = dns
13
+ @ntp = ntp
14
+ @catalog = catalog
15
+ @network = network
16
+ end
17
+
18
+ def build_properties
19
+ [
20
+ {
21
+ 'type' => 'string',
22
+ 'key' => 'gateway',
23
+ 'value' => gateway,
24
+ 'password' => 'false',
25
+ 'userConfigurable' => 'true',
26
+ 'Label' => 'Default Gateway',
27
+ 'Description' => 'The default gateway address for the VM network. Leave blank if DHCP is desired.'
28
+ },
29
+ {
30
+ 'type' => 'string',
31
+ 'key' => 'DNS',
32
+ 'value' => dns,
33
+ 'password' => 'false',
34
+ 'userConfigurable' => 'true',
35
+ 'Label' => 'DNS',
36
+ 'Description' => 'The domain name servers for the VM (comma separated). Leave blank if DHCP is desired.',
37
+ },
38
+ {
39
+ 'type' => 'string',
40
+ 'key' => 'ntp_servers',
41
+ 'value' => ntp,
42
+ 'password' => 'false',
43
+ 'userConfigurable' => 'true',
44
+ 'Label' => 'NTP Servers',
45
+ 'Description' => 'Comma-delimited list of NTP servers'
46
+ },
47
+ {
48
+ 'type' => 'string',
49
+ 'key' => 'admin_password',
50
+ 'value' => 'tempest',
51
+ 'password' => 'true',
52
+ 'userConfigurable' => 'true',
53
+ 'Label' => 'Admin Password',
54
+ 'Description' => 'This password is used to SSH into the VM. The username is "tempest".',
55
+ },
56
+ {
57
+ 'type' => 'string',
58
+ 'key' => 'ip0',
59
+ 'value' => ip,
60
+ 'password' => 'false',
61
+ 'userConfigurable' => 'true',
62
+ 'Label' => 'IP Address',
63
+ 'Description' => 'The IP address for the VM. Leave blank if DHCP is desired.',
64
+ },
65
+ {
66
+ 'type' => 'string',
67
+ 'key' => 'netmask0',
68
+ 'value' => netmask,
69
+ 'password' => 'false',
70
+ 'userConfigurable' => 'true',
71
+ 'Label' => 'Netmask',
72
+ 'Description' => 'The netmask for the VM network. Leave blank if DHCP is desired.'
73
+ }
74
+ ]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -17,9 +17,15 @@ module VmShepherd
17
17
 
18
18
  untar_vapp_template_tar(File.expand_path(vapp_template_tar_path), tmpdir)
19
19
 
20
- vapp = deploy_vapp(tmpdir, vapp_config)
21
- reconfigure_vm(vapp, vapp_config)
22
- vapp.power_on
20
+ VmShepherd::Vcloud::Deployer.deploy_and_power_on_vapp(
21
+ client: client,
22
+ ovf_dir: tmpdir,
23
+ vapp_config: vapp_config,
24
+ vdc_name: @vdc_name,
25
+ )
26
+ rescue => e
27
+ logger.error(e.http_body) if e.respond_to?(:http_body)
28
+ raise e
23
29
  ensure
24
30
  FileUtils.remove_entry_secure(tmpdir, force: true)
25
31
  end
@@ -28,8 +34,8 @@ module VmShepherd
28
34
  end
29
35
 
30
36
  def destroy(vapp_names, catalog)
31
- delete_vapps(vapp_names)
32
- delete_catalog(catalog)
37
+ VmShepherd::Vcloud::Destroyer.new(client: client, vdc_name: @vdc_name).
38
+ delete_catalog_and_vapps(catalog, vapp_names, @logger)
33
39
  end
34
40
 
35
41
  def clean_environment(vapp_names, catalog)
@@ -40,14 +46,15 @@ module VmShepherd
40
46
 
41
47
  def check_vapp_status(vapp_config)
42
48
  log('Checking for existing VM') do
43
- ip = vapp_config[:ip]
44
- raise "VM exists at #{ip}" if system("ping -c 5 #{ip}")
49
+ ip = vapp_config.ip
50
+ system("ping -c 5 #{ip}") and raise "VM exists at #{ip}"
45
51
  end
46
52
  end
47
53
 
48
54
  def untar_vapp_template_tar(vapp_template_tar_path, dir)
49
55
  log("Untarring #{vapp_template_tar_path}") do
50
- system_or_exit("cd #{dir} && tar xfv '#{vapp_template_tar_path}'")
56
+ cmd = "cd #{dir} && tar xfv '#{vapp_template_tar_path}'"
57
+ system(cmd) or raise("Error executing: #{cmd}")
51
58
  end
52
59
  end
53
60
 
@@ -61,127 +68,10 @@ module VmShepherd
61
68
  )
62
69
  end
63
70
 
64
- def deploy_vapp(ovf_dir, vapp_config)
65
- vapp_name = vapp_config.fetch(:name)
66
- catalog_name = vapp_config.fetch(:catalog)
67
- network = vapp_config.fetch(:network)
68
- # setup the catalog
69
- client.delete_catalog_by_name(catalog_name) if client.catalog_exists?(catalog_name)
70
- catalog = client.create_catalog(catalog_name)
71
-
72
- # upload template and instantiate vapp
73
- catalog.upload_vapp_template(@vdc_name, vapp_name, ovf_dir)
74
-
75
- # instantiate template
76
- network_config = VCloudSdk::NetworkConfig.new(network, 'Network 1')
77
- catalog.instantiate_vapp_template(vapp_name, @vdc_name, vapp_name, nil, nil, network_config)
78
- rescue => e
79
- @logger.error(e.http_body) if e.respond_to?(:http_body)
80
- raise e
81
- end
82
-
83
- def reconfigure_vm(vapp, vapp_config)
84
- vapp_name = vapp_config.fetch(:name)
85
- gateway = vapp_config.fetch(:gateway)
86
- dns = vapp_config.fetch(:dns)
87
- ntp = vapp_config.fetch(:ntp)
88
- ip = vapp_config.fetch(:ip)
89
- netmask = vapp_config.fetch(:netmask)
90
-
91
- vm = vapp.find_vm_by_name(vapp_name)
92
- vm.product_section_properties = build_properties(gateway: gateway, dns: dns, ntp: ntp, ip: ip, netmask: netmask)
93
- vm
94
- end
95
-
96
- def build_properties(gateway:, dns:, ntp:, ip:, netmask:)
97
- [
98
- {
99
- 'type' => 'string',
100
- 'key' => 'gateway',
101
- 'value' => gateway,
102
- 'password' => 'false',
103
- 'userConfigurable' => 'true',
104
- 'Label' => 'Default Gateway',
105
- 'Description' => 'The default gateway address for the VM network. Leave blank if DHCP is desired.'
106
- },
107
- {
108
- 'type' => 'string',
109
- 'key' => 'DNS',
110
- 'value' => dns,
111
- 'password' => 'false',
112
- 'userConfigurable' => 'true',
113
- 'Label' => 'DNS',
114
- 'Description' => 'The domain name servers for the VM (comma separated). Leave blank if DHCP is desired.',
115
- },
116
- {
117
- 'type' => 'string',
118
- 'key' => 'ntp_servers',
119
- 'value' => ntp,
120
- 'password' => 'false',
121
- 'userConfigurable' => 'true',
122
- 'Label' => 'NTP Servers',
123
- 'Description' => 'Comma-delimited list of NTP servers'
124
- },
125
- {
126
- 'type' => 'string',
127
- 'key' => 'admin_password',
128
- 'value' => 'tempest',
129
- 'password' => 'true',
130
- 'userConfigurable' => 'true',
131
- 'Label' => 'Admin Password',
132
- 'Description' => 'This password is used to SSH into the VM. The username is "tempest".',
133
- },
134
- {
135
- 'type' => 'string',
136
- 'key' => 'ip0',
137
- 'value' => ip,
138
- 'password' => 'false',
139
- 'userConfigurable' => 'true',
140
- 'Label' => 'IP Address',
141
- 'Description' => 'The IP address for the VM. Leave blank if DHCP is desired.',
142
- },
143
- {
144
- 'type' => 'string',
145
- 'key' => 'netmask0',
146
- 'value' => netmask,
147
- 'password' => 'false',
148
- 'userConfigurable' => 'true',
149
- 'Label' => 'Netmask',
150
- 'Description' => 'The netmask for the VM network. Leave blank if DHCP is desired.'
151
- }
152
- ]
153
- end
154
-
155
71
  def log(title, &blk)
156
72
  @logger.debug "--- Begin: #{title.inspect} @ #{DateTime.now}"
157
73
  blk.call
158
74
  @logger.debug "--- End: #{title.inspect} @ #{DateTime.now}"
159
75
  end
160
-
161
- def system_or_exit(command)
162
- log(command) do
163
- system(command) || raise("Error executing: #{command.inspect}")
164
- end
165
- end
166
-
167
- def vdc
168
- @vdc ||= client.find_vdc_by_name(@vdc_name)
169
- end
170
-
171
- def delete_vapps(vapp_names)
172
- vapp_names.each do |vapp_name|
173
- begin
174
- vapp = vdc.find_vapp_by_name(vapp_name)
175
- vapp.power_off
176
- vapp.delete
177
- rescue VCloudSdk::ObjectNotFoundError => e
178
- @logger.debug "Could not delete vapp '#{vapp_name}': #{e.inspect}"
179
- end
180
- end
181
- end
182
-
183
- def delete_catalog(catalog)
184
- client.delete_catalog_by_name(catalog) if client.catalog_exists?(catalog)
185
- end
186
76
  end
187
77
  end
@@ -1,3 +1,3 @@
1
1
  module VmShepherd
2
- VERSION = '1.10.1'.freeze
2
+ VERSION = '1.11.0'.freeze
3
3
  end
@@ -0,0 +1,49 @@
1
+ require 'vm_shepherd/data_object'
2
+
3
+ module VmShepherd
4
+ class TestDataObject
5
+ include DataObject
6
+
7
+ attr_accessor :name
8
+ end
9
+
10
+ RSpec.describe(DataObject) do
11
+ describe '#==' do
12
+ it 'returns false for objects of a different class' do
13
+ class DifferentDataObject
14
+ include DataObject
15
+ end
16
+
17
+ expect(TestDataObject.new == DifferentDataObject.new).to be_falsey
18
+ end
19
+
20
+ it 'returns true for objects of a descendent class' do
21
+ class DescendentDataObject < TestDataObject
22
+ include DataObject
23
+ end
24
+
25
+ expect(TestDataObject.new == DescendentDataObject.new).to be_truthy
26
+ end
27
+
28
+ it 'returns false when any attribute is unequal' do
29
+ a = TestDataObject.new
30
+ b = TestDataObject.new
31
+
32
+ a.name = 'a'
33
+ b.name = 'b'
34
+
35
+ expect(a == b).to be_falsey
36
+ end
37
+
38
+ it 'returns true when all attributes are equal' do
39
+ eleventy_one = TestDataObject.new
40
+ hundred_and_eleven = TestDataObject.new
41
+
42
+ eleventy_one.name = '111'
43
+ hundred_and_eleven.name = '111'
44
+
45
+ expect(eleventy_one == hundred_and_eleven).to be_truthy
46
+ end
47
+ end
48
+ end
49
+ end
@@ -84,7 +84,7 @@ module VmShepherd
84
84
 
85
85
  expect(first_vcloud_manager).to receive(:deploy).with(
86
86
  'FIRST_FAKE_PATH',
87
- {
87
+ Vcloud::VappConfig.new(
88
88
  name: first_config.vapp.ops_manager_name,
89
89
  ip: first_config.vapp.ip,
90
90
  gateway: first_config.vapp.gateway,
@@ -93,12 +93,12 @@ module VmShepherd
93
93
  ntp: first_config.vapp.ntp,
94
94
  catalog: first_config.vdc.catalog,
95
95
  network: first_config.vdc.network,
96
- }
96
+ )
97
97
  )
98
98
 
99
99
  expect(last_vcloud_manager).to receive(:deploy).with(
100
100
  'LAST_FAKE_PATH',
101
- {
101
+ Vcloud::VappConfig.new(
102
102
  name: last_config.vapp.ops_manager_name,
103
103
  ip: last_config.vapp.ip,
104
104
  gateway: last_config.vapp.gateway,
@@ -107,7 +107,7 @@ module VmShepherd
107
107
  ntp: last_config.vapp.ntp,
108
108
  catalog: last_config.vdc.catalog,
109
109
  network: last_config.vdc.network,
110
- }
110
+ )
111
111
  )
112
112
 
113
113
  manager.deploy(paths: ['FIRST_FAKE_PATH', 'LAST_FAKE_PATH'])
@@ -0,0 +1,83 @@
1
+ require 'vm_shepherd/data_object'
2
+ require 'vm_shepherd/vcloud/vapp_config'
3
+ require 'vm_shepherd/vcloud/deployer'
4
+ require 'ruby_vcloud_sdk'
5
+
6
+ module VmShepherd
7
+ module Vcloud
8
+ RSpec.describe Deployer do
9
+ describe '.deploy_and_power_on_vapp' do
10
+ let(:vapp_config) do
11
+ VappConfig.new(
12
+ name: 'NAME',
13
+ ip: 'IP',
14
+ gateway: 'GATEWAY',
15
+ netmask: 'NETMASK',
16
+ dns: 'DNS',
17
+ ntp: 'NTP',
18
+ catalog: 'CATALOG',
19
+ network: 'NETWORK',
20
+ )
21
+ end
22
+
23
+ let(:client) { instance_double(VCloudSdk::Client) }
24
+ let(:catalog) { instance_double(VCloudSdk::Catalog) }
25
+ let(:vapp) { instance_double(VCloudSdk::VApp) }
26
+ let(:vm) { instance_double(VCloudSdk::VM) }
27
+ let(:network_config) { instance_double(VCloudSdk::NetworkConfig) }
28
+
29
+ before do
30
+ allow(client).to receive(:create_catalog).and_return(catalog)
31
+ allow(catalog).to receive(:upload_vapp_template)
32
+ allow(catalog).to receive(:instantiate_vapp_template).and_return(vapp)
33
+ allow(vapp).to receive(:find_vm_by_name).and_return(vm)
34
+ allow(vm).to receive(:product_section_properties=)
35
+ allow(vapp).to receive(:power_on)
36
+
37
+ allow(VCloudSdk::NetworkConfig).to receive(:new).with('NETWORK', 'Network 1').and_return(network_config)
38
+ end
39
+
40
+ it 'creates a catalog' do
41
+ expect(client).to receive(:create_catalog).with('CATALOG')
42
+
43
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: nil, vapp_config: vapp_config, vdc_name: nil)
44
+ end
45
+
46
+ it 'uploads a vapp template' do
47
+ expect(catalog).to receive(:upload_vapp_template).with('VDC_NAME', 'NAME', 'OVF_DIR')
48
+
49
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: 'OVF_DIR', vapp_config: vapp_config, vdc_name: 'VDC_NAME')
50
+ end
51
+
52
+ it 'instantiates the template' do
53
+ expect(catalog).to receive(:upload_vapp_template).with('VDC_NAME', 'NAME', 'OVF_DIR')
54
+ expect(catalog).to receive(:instantiate_vapp_template).with('NAME', 'VDC_NAME', 'NAME', nil, nil, network_config)
55
+
56
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: 'OVF_DIR', vapp_config: vapp_config, vdc_name: 'VDC_NAME')
57
+ end
58
+
59
+ it 'reconfigures the vm' do
60
+ expect(vm).to receive(:product_section_properties=).with(vapp_config.build_properties)
61
+
62
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: 'OVF_DIR', vapp_config: vapp_config, vdc_name: 'VDC_NAME')
63
+ end
64
+
65
+ it 'powers on the vapp' do
66
+ expect(vapp).to receive(:power_on)
67
+
68
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: 'OVF_DIR', vapp_config: vapp_config, vdc_name: 'VDC_NAME')
69
+ end
70
+
71
+ it 'does things in order' do
72
+ Deployer.deploy_and_power_on_vapp(client: client, ovf_dir: 'OVF_DIR', vapp_config: vapp_config, vdc_name: 'VDC_NAME')
73
+
74
+ expect(client).to have_received(:create_catalog).ordered
75
+ expect(catalog).to have_received(:upload_vapp_template).ordered
76
+ expect(catalog).to have_received(:instantiate_vapp_template).ordered
77
+ expect(vm).to have_received(:product_section_properties=).ordered
78
+ expect(vapp).to have_received(:power_on).ordered
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,72 @@
1
+ require 'vm_shepherd/data_object'
2
+ require 'vm_shepherd/vcloud/vapp_config'
3
+ require 'vm_shepherd/vcloud/destroyer'
4
+ require 'ruby_vcloud_sdk'
5
+
6
+ module VmShepherd
7
+ module Vcloud
8
+ RSpec.describe Destroyer do
9
+ subject(:destroyer) { Destroyer.new(client: client, vdc_name: vdc_name) }
10
+ let(:client) { instance_double(VCloudSdk::Client) }
11
+ let(:vdc_name) { 'VDC_NAME' }
12
+ let(:fake_logger) { double(:logger, debug: nil) }
13
+ let(:vm) { instance_double(VCloudSdk::VM) }
14
+ let(:vdc) { instance_double(VCloudSdk::VDC) }
15
+ let(:vapp) { instance_double(VCloudSdk::VApp) }
16
+ let(:disk) { instance_double(VCloudSdk::InternalDisk, name: 'DISK_NAME') }
17
+
18
+ before do
19
+ allow(client).to receive(:delete_catalog_by_name)
20
+ allow(client).to receive(:catalog_exists?)
21
+ allow(client).to receive(:find_vdc_by_name).with(vdc_name).and_return(vdc)
22
+ allow(vdc).to receive(:find_vapp_by_name).with('VAPP_NAME').and_return(vapp)
23
+ allow(vdc).to receive(:delete_disk_by_name)
24
+ allow(vapp).to receive(:vms).and_return([vm])
25
+ allow(vapp).to receive(:power_off)
26
+ allow(vapp).to receive(:delete)
27
+ allow(vm).to receive(:independent_disks).and_return([disk])
28
+ allow(vm).to receive(:detach_disk)
29
+ end
30
+
31
+ describe '#delete_catalog_and_vapps' do
32
+ context 'when the catalog exists' do
33
+ before do
34
+ allow(client).to receive(:catalog_exists?).with('CATALOG_NAME').and_return(true)
35
+ end
36
+
37
+ it 'deletes the catalog' do
38
+ destroyer.delete_catalog_and_vapps('CATALOG_NAME', [], fake_logger)
39
+
40
+ expect(client).to have_received(:delete_catalog_by_name).with('CATALOG_NAME')
41
+ end
42
+ end
43
+
44
+ context 'when the catalog does not exist' do
45
+ before do
46
+ allow(client).to receive(:catalog_exists?).with('CATALOG_NAME').and_return(false)
47
+ end
48
+
49
+ it 'skips deleting the catalog' do
50
+ destroyer.delete_catalog_and_vapps('CATALOG_NAME', [], fake_logger)
51
+
52
+ expect(client).not_to have_received(:delete_catalog_by_name).with('CATALOG_NAME')
53
+ end
54
+ end
55
+
56
+ it 'detaches and deletes persistent disks' do
57
+ destroyer.delete_catalog_and_vapps('CATALOG_NAME', ['VAPP_NAME'], fake_logger)
58
+
59
+ expect(vm).to have_received(:detach_disk).with(disk)
60
+ expect(vdc).to have_received(:delete_disk_by_name).with('DISK_NAME')
61
+ end
62
+
63
+ it 'powers off and deletes vapps' do
64
+ destroyer.delete_catalog_and_vapps('CATALOG_NAME', ['VAPP_NAME'], fake_logger)
65
+
66
+ expect(vapp).to have_received(:power_off)
67
+ expect(vapp).to have_received(:delete)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+ require 'vm_shepherd/data_object'
2
+ require 'vm_shepherd/vcloud/vapp_config'
3
+
4
+ module VmShepherd
5
+ module Vcloud
6
+ RSpec.describe(VappConfig) do
7
+ subject(:vapp_config) do
8
+ VappConfig.new(
9
+ name: 'NAME',
10
+ ip: 'IP',
11
+ gateway: 'GATEWAY',
12
+ netmask: 'NETMASK',
13
+ dns: 'DNS',
14
+ ntp: 'NTP',
15
+ catalog: 'CATALOG',
16
+ network: 'NETWORK',
17
+ )
18
+ end
19
+
20
+ it 'is a DataObject' do
21
+ expect(vapp_config).to be_a(DataObject)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,4 +1,8 @@
1
+ require 'vm_shepherd/data_object'
1
2
  require 'vm_shepherd/vcloud_manager'
3
+ require 'vm_shepherd/vcloud/deployer'
4
+ require 'vm_shepherd/vcloud/destroyer'
5
+ require 'vm_shepherd/vcloud/vapp_config'
2
6
 
3
7
  module VmShepherd
4
8
  RSpec.describe VcloudManager do
@@ -18,7 +22,7 @@ module VmShepherd
18
22
 
19
23
  describe '#deploy' do
20
24
  let(:vapp_config) do
21
- {
25
+ Vcloud::VappConfig.new(
22
26
  ip: 'FAKE_IP',
23
27
  name: 'FAKE_NAME',
24
28
  gateway: 'FAKE_GATEWAY',
@@ -27,7 +31,7 @@ module VmShepherd
27
31
  netmask: 'FAKE_NETMASK',
28
32
  catalog: 'FAKE_VAPP_CATALOG',
29
33
  network: 'FAKE_NETWORK',
30
- }
34
+ )
31
35
  end
32
36
 
33
37
  let(:vapp_template_path) { 'FAKE_VAPP_TEMPLATE_PATH' }
@@ -39,7 +43,7 @@ module VmShepherd
39
43
  let(:expanded_vapp_template_path) { 'FAKE_EXPANDED_VAPP_TEMPLATE_PATH' }
40
44
 
41
45
  before do
42
- allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.fetch(:ip)}").and_return(false)
46
+ allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.ip}").and_return(false)
43
47
 
44
48
  allow(File).to receive(:expand_path).with(vapp_template_path).and_return(expanded_vapp_template_path)
45
49
 
@@ -71,7 +75,7 @@ module VmShepherd
71
75
  {
72
76
  'type' => 'string',
73
77
  'key' => 'gateway',
74
- 'value' => vapp_config.fetch(:gateway),
78
+ 'value' => vapp_config.gateway,
75
79
  'password' => 'false',
76
80
  'userConfigurable' => 'true',
77
81
  'Label' => 'Default Gateway',
@@ -80,7 +84,7 @@ module VmShepherd
80
84
  {
81
85
  'type' => 'string',
82
86
  'key' => 'DNS',
83
- 'value' => vapp_config.fetch(:dns),
87
+ 'value' => vapp_config.dns,
84
88
  'password' => 'false',
85
89
  'userConfigurable' => 'true',
86
90
  'Label' => 'DNS',
@@ -89,7 +93,7 @@ module VmShepherd
89
93
  {
90
94
  'type' => 'string',
91
95
  'key' => 'ntp_servers',
92
- 'value' => vapp_config.fetch(:ntp),
96
+ 'value' => vapp_config.ntp,
93
97
  'password' => 'false',
94
98
  'userConfigurable' => 'true',
95
99
  'Label' => 'NTP Servers',
@@ -107,7 +111,7 @@ module VmShepherd
107
111
  {
108
112
  'type' => 'string',
109
113
  'key' => 'ip0',
110
- 'value' => vapp_config.fetch(:ip),
114
+ 'value' => vapp_config.ip,
111
115
  'password' => 'false',
112
116
  'userConfigurable' => 'true',
113
117
  'Label' => 'IP Address',
@@ -116,7 +120,7 @@ module VmShepherd
116
120
  {
117
121
  'type' => 'string',
118
122
  'key' => 'netmask0',
119
- 'value' => vapp_config.fetch(:netmask),
123
+ 'value' => vapp_config.netmask,
120
124
  'password' => 'false',
121
125
  'userConfigurable' => 'true',
122
126
  'Label' => 'Netmask',
@@ -153,34 +157,8 @@ module VmShepherd
153
157
  vcloud_manager.deploy(vapp_template_path, vapp_config)
154
158
  end
155
159
 
156
- describe 'catalog deletion' do
157
- before do
158
- allow(client).to receive(:catalog_exists?).and_return(catalog_exists)
159
- end
160
-
161
- context 'when the catalog exists' do
162
- let(:catalog_exists) { true }
163
-
164
- it 'deletes the catalog' do
165
- expect(client).to receive(:delete_catalog_by_name).with(vapp_config.fetch(:catalog))
166
-
167
- vcloud_manager.deploy(vapp_template_path, vapp_config)
168
- end
169
- end
170
-
171
- context 'when the catalog does not exist' do
172
- let(:catalog_exists) { false }
173
-
174
- it 'does not delete the catalog' do
175
- expect(client).not_to receive(:delete_catalog_by_name).with(vapp_config.fetch(:catalog))
176
-
177
- vcloud_manager.deploy(vapp_template_path, vapp_config)
178
- end
179
- end
180
- end
181
-
182
160
  it 'creates the catalog' do
183
- expect(client).to receive(:create_catalog).with(vapp_config.fetch(:catalog)).and_return(catalog)
161
+ expect(client).to receive(:create_catalog).with(vapp_config.catalog).and_return(catalog)
184
162
 
185
163
  vcloud_manager.deploy(vapp_template_path, vapp_config)
186
164
  end
@@ -188,7 +166,7 @@ module VmShepherd
188
166
  it 'uploads the vApp template' do
189
167
  expect(catalog).to receive(:upload_vapp_template).with(
190
168
  vdc_name,
191
- vapp_config.fetch(:name),
169
+ vapp_config.name,
192
170
  tmpdir,
193
171
  ).and_return(catalog)
194
172
 
@@ -197,7 +175,7 @@ module VmShepherd
197
175
 
198
176
  it 'creates a VCloudSdk::NetworkConfig' do
199
177
  expect(VCloudSdk::NetworkConfig).to receive(:new).with(
200
- vapp_config.fetch(:network),
178
+ vapp_config.network,
201
179
  'Network 1',
202
180
  ).and_return(network_config)
203
181
 
@@ -206,9 +184,9 @@ module VmShepherd
206
184
 
207
185
  it 'instantiates the vApp template' do
208
186
  expect(catalog).to receive(:instantiate_vapp_template).with(
209
- vapp_config.fetch(:name),
187
+ vapp_config.name,
210
188
  vdc_name,
211
- vapp_config.fetch(:name),
189
+ vapp_config.name,
212
190
  nil,
213
191
  nil,
214
192
  network_config
@@ -256,7 +234,7 @@ module VmShepherd
256
234
  end
257
235
 
258
236
  it 'raises an error' do
259
- expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("Error executing: #{tar_expand_cmd.inspect}")
237
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("Error executing: #{tar_expand_cmd}")
260
238
  end
261
239
 
262
240
  it 'removes the expanded vApp template' do
@@ -269,11 +247,11 @@ module VmShepherd
269
247
 
270
248
  context 'when a host exists at the specified IP' do
271
249
  before do
272
- allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.fetch(:ip)}").and_return(true)
250
+ allow(vcloud_manager).to receive(:system).with("ping -c 5 #{vapp_config.ip}").and_return(true)
273
251
  end
274
252
 
275
253
  it 'raises an error' do
276
- expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("VM exists at #{vapp_config.fetch(:ip)}")
254
+ expect { vcloud_manager.deploy(vapp_template_path, vapp_config) }.to raise_error("VM exists at #{vapp_config.ip}")
277
255
  end
278
256
 
279
257
  it 'removes the expanded vApp template' do
@@ -290,6 +268,13 @@ module VmShepherd
290
268
  let(:vapp) { instance_double(VCloudSdk::VApp) }
291
269
  let(:vapp_name) { 'FAKE_VAPP_NAME' }
292
270
  let(:vapp_catalog) { 'FAKE_VAPP_CATALOG' }
271
+ let(:vm) { instance_double(VCloudSdk::VM) }
272
+ let(:disk) { instance_double(VCloudSdk::InternalDisk, name: 'disk name') }
273
+
274
+ before do
275
+ allow(vapp).to receive(:vms).and_return([vm])
276
+ allow(vm).to receive(:independent_disks).and_return([disk])
277
+ end
293
278
 
294
279
  context 'when the catalog exists' do
295
280
  before do
@@ -299,6 +284,8 @@ module VmShepherd
299
284
  it 'uses VCloudSdk::Client to delete the vApp' do
300
285
  expect(client).to receive(:find_vdc_by_name).with(vdc_name).and_return(vdc)
301
286
  expect(vdc).to receive(:find_vapp_by_name).with(vapp_name).and_return(vapp)
287
+ expect(vm).to receive(:detach_disk).with(disk)
288
+ expect(vdc).to receive(:delete_disk_by_name).with('disk name')
302
289
  expect(vapp).to receive(:power_off)
303
290
  expect(vapp).to receive(:delete)
304
291
  expect(client).to receive(:delete_catalog_by_name).with(vapp_catalog)
@@ -319,6 +306,8 @@ module VmShepherd
319
306
  allow(VCloudSdk::Client).to receive(:new).and_return(client)
320
307
  allow(client).to receive(:find_vdc_by_name).and_return(vdc)
321
308
  allow(vdc).to receive(:find_vapp_by_name).and_return(vapp)
309
+ allow(vm).to receive(:detach_disk)
310
+ allow(vdc).to receive(:delete_disk_by_name)
322
311
  allow(vapp).to receive(:power_off)
323
312
  allow(vapp).to receive(:delete)
324
313
 
@@ -326,7 +315,7 @@ module VmShepherd
326
315
  end
327
316
 
328
317
  it 'catches the error' do
329
- allow(client).to receive(:find_vdc_by_name).and_raise(VCloudSdk::ObjectNotFoundError)
318
+ allow(vdc).to receive(:find_vapp_by_name).and_raise(VCloudSdk::ObjectNotFoundError)
330
319
 
331
320
  expect { vcloud_manager.destroy([vapp_name], vapp_catalog) }.not_to raise_error
332
321
  end
@@ -347,6 +336,8 @@ module VmShepherd
347
336
  it 'uses VCloudSdk::Client to delete the vApp' do
348
337
  expect(client).to receive(:find_vdc_by_name).with(vdc_name).and_return(vdc)
349
338
  expect(vdc).to receive(:find_vapp_by_name).with(vapp_name).and_return(vapp)
339
+ expect(vm).to receive(:detach_disk).with(disk)
340
+ expect(vdc).to receive(:delete_disk_by_name).with('disk name')
350
341
  expect(vapp).to receive(:power_off)
351
342
  expect(vapp).to receive(:delete)
352
343
  expect(client).not_to receive(:delete_catalog_by_name).with(vapp_catalog)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vm_shepherd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.1
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ops Manager Team
@@ -168,9 +168,13 @@ files:
168
168
  - ci/run_specs.sh
169
169
  - lib/vm_shepherd.rb
170
170
  - lib/vm_shepherd/aws_manager.rb
171
+ - lib/vm_shepherd/data_object.rb
171
172
  - lib/vm_shepherd/openstack_manager.rb
172
173
  - lib/vm_shepherd/retry_helper.rb
173
174
  - lib/vm_shepherd/shepherd.rb
175
+ - lib/vm_shepherd/vcloud/deployer.rb
176
+ - lib/vm_shepherd/vcloud/destroyer.rb
177
+ - lib/vm_shepherd/vcloud/vapp_config.rb
174
178
  - lib/vm_shepherd/vcloud_manager.rb
175
179
  - lib/vm_shepherd/version.rb
176
180
  - lib/vm_shepherd/vsphere_manager.rb
@@ -184,9 +188,13 @@ files:
184
188
  - spec/spec_helper.rb
185
189
  - spec/support/patched_fog.rb
186
190
  - spec/vm_shepherd/aws_manager_spec.rb
191
+ - spec/vm_shepherd/data_object_spec.rb
187
192
  - spec/vm_shepherd/openstack_manager_spec.rb
188
193
  - spec/vm_shepherd/retry_helper_spec.rb
189
194
  - spec/vm_shepherd/shepherd_spec.rb
195
+ - spec/vm_shepherd/vcloud/deployer_spec.rb
196
+ - spec/vm_shepherd/vcloud/destroyer_spec.rb
197
+ - spec/vm_shepherd/vcloud/vapp_config_spec.rb
190
198
  - spec/vm_shepherd/vcloud_manager_spec.rb
191
199
  - spec/vm_shepherd/vsphere_manager_spec.rb
192
200
  - vm_shepherd.gemspec
@@ -209,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
217
  version: '0'
210
218
  requirements: []
211
219
  rubyforge_project:
212
- rubygems_version: 2.4.6
220
+ rubygems_version: 2.4.7
213
221
  signing_key:
214
222
  specification_version: 4
215
223
  summary: A tool for booting and tearing down Ops Manager VMs on various Infrastructures.
@@ -224,8 +232,12 @@ test_files:
224
232
  - spec/spec_helper.rb
225
233
  - spec/support/patched_fog.rb
226
234
  - spec/vm_shepherd/aws_manager_spec.rb
235
+ - spec/vm_shepherd/data_object_spec.rb
227
236
  - spec/vm_shepherd/openstack_manager_spec.rb
228
237
  - spec/vm_shepherd/retry_helper_spec.rb
229
238
  - spec/vm_shepherd/shepherd_spec.rb
239
+ - spec/vm_shepherd/vcloud/deployer_spec.rb
240
+ - spec/vm_shepherd/vcloud/destroyer_spec.rb
241
+ - spec/vm_shepherd/vcloud/vapp_config_spec.rb
230
242
  - spec/vm_shepherd/vcloud_manager_spec.rb
231
243
  - spec/vm_shepherd/vsphere_manager_spec.rb