beaker-openstack 2.0.0 → 3.0.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.
@@ -4,73 +4,84 @@ require 'fog/openstack'
4
4
  module Beaker
5
5
  describe Openstack do
6
6
 
7
- let(:options) { make_opts.merge({'logger' => double().as_null_object, 'openstack_floating_ip' => true}) }
7
+ # -------------------------------------------------------------------------
8
+ # Default options for OpenStack hypervisor tests
9
+ # -------------------------------------------------------------------------
10
+ let(:options) do
11
+ make_opts.merge(
12
+ 'logger' => double.as_null_object,
13
+ 'openstack_floating_ip' => true,
14
+ 'floating_ip_pool' => 'my_pool',
15
+ 'openstack_auth_url' => 'http://openstack_hypervisor.labs.net:5000/v3',
16
+ 'openstack_project_name' => 'test_project'
17
+ )
18
+ end
8
19
 
9
- let(:openstack) {
10
- Openstack.new(@hosts, options)
11
- }
20
+ let(:openstack) { Openstack.new(@hosts, options) }
12
21
 
13
22
  before :each do
14
23
  @hosts = make_hosts()
15
24
 
16
- @compute_client = double().as_null_object
17
- @network_client = double().as_null_object
25
+ @compute_client = double.as_null_object
26
+ @network_client = double.as_null_object
27
+
28
+ allow(Fog::Compute).to receive(:new).and_return(@compute_client)
29
+ allow(Fog::Network).to receive(:new).and_return(@network_client)
18
30
 
19
- allow( Fog::Compute ).to receive( :new ).and_return( @compute_client )
20
- allow( Fog::Network ).to receive( :new ).and_return( @network_client )
21
31
  end
22
32
 
33
+ # -------------------------------------------------------------------------
34
+ # Keystone version tests
35
+ # -------------------------------------------------------------------------
23
36
  context 'keystone version support' do
24
- it 'supports keystone v2' do
25
- credentials = openstack.instance_eval('@credentials')
26
- expect(credentials[:openstack_user_domain]).to be_nil
27
- expect(credentials[:openstack_project_domain]).to be_nil
37
+ it 'raises for keystone v2' do
38
+ v2_opts = options.dup
39
+ v2_opts[:openstack_auth_url] = 'http://example.com/identity/v2.0/tokens'
40
+
41
+ expect {
42
+ Openstack.new(@hosts, v2_opts)
43
+ }.to raise_error(RuntimeError, /Keystone v2 is no longer supported/)
28
44
  end
29
45
 
30
- it 'supports keystone v3 with implicit arguments' do
31
- v3_options = options
32
- v3_options[:openstack_auth_url] = 'https://example.com/identity/v3/auth'
33
- v3_options[:openstack_project_name] = 'TeamTest_ab_c'
34
- v3_options[:openstack_tenant] = nil
35
-
36
- credentials = Openstack.new(@hosts, v3_options).instance_eval('@credentials')
37
- expect(credentials[:openstack_user_domain]).to eq('Default')
38
- expect(credentials[:openstack_project_domain]).to eq('Default')
39
- expect(credentials[:openstack_project_name]).to eq('TeamTest_ab_c')
40
- expect(credentials[:openstack_tenant]).to be_nil
46
+ it 'supports keystone v3 with implicit defaults' do
47
+ v3 = options.dup
48
+ v3[:openstack_auth_url] = 'https://example.com/identity/v3'
49
+ v3[:openstack_project_name] = 'TeamTest'
50
+
51
+ creds = Openstack.new(@hosts, v3).instance_eval('@credentials')
52
+ expect(creds[:openstack_project_name]).to eq('TeamTest')
53
+ expect(creds[:openstack_user_domain]).to eq('Default')
54
+ expect(creds[:openstack_project_domain]).to eq('Default')
41
55
  end
42
56
 
43
- it 'supports keystone v3 with explicit arguments' do
44
- v3_options = options
45
- v3_options[:openstack_auth_url] = 'https://example.com/identity/v3/auth'
46
- v3_options[:openstack_user_domain] = 'acme.com'
47
- v3_options[:openstack_project_domain] = 'R&D'
48
- v3_options[:openstack_project_name] = 'Team_test_abc'
49
- v3_options[:openstack_tenant] = nil
50
-
51
- credentials = Openstack.new(@hosts, v3_options).instance_eval('@credentials')
52
- expect(credentials[:openstack_user_domain]).to eq('acme.com')
53
- expect(credentials[:openstack_project_domain]).to eq('R&D')
54
- expect(credentials[:openstack_project_name]).to eq('Team_test_abc')
55
- expect(credentials[:openstack_tenant]).to be_nil
57
+ it 'supports keystone v3 with explicit domains' do
58
+ v3 = options.dup
59
+ v3[:openstack_auth_url] = 'https://example.com/identity/v3'
60
+ v3[:openstack_project_name] = 'TeamTest'
61
+ v3[:openstack_user_domain] = 'acme'
62
+ v3[:openstack_project_domain] = 'rnd'
63
+
64
+ creds = Openstack.new(@hosts, v3).instance_eval('@credentials')
65
+ expect(creds[:openstack_user_domain]).to eq('acme')
66
+ expect(creds[:openstack_project_domain]).to eq('rnd')
56
67
  end
57
68
  end
58
69
 
70
+ # -------------------------------------------------------------------------
71
+ # Provision tests
72
+ # -------------------------------------------------------------------------
59
73
  describe '#provision' do
60
-
61
- it 'check openstack options during initialization' do
62
- options = openstack.instance_eval('@options')
63
- expect(options['openstack_api_key']).to eq('P1as$w0rd')
64
- expect(options['openstack_username']).to eq('user')
65
- expect(options['openstack_auth_url']).to eq('http://openstack_hypervisor.labs.net:5000/v2.0/tokens')
66
- expect(options['openstack_tenant']).to eq('testing')
67
- expect(options['openstack_network']).to eq('testing')
68
- expect(options['openstack_keyname']).to eq('nopass')
69
- expect(options['security_group']).to eq(['my_sg', 'default'])
70
- expect(options['floating_ip_pool']).to eq('my_pool')
74
+ it 'initializes options correctly' do
75
+ opts = openstack.instance_eval('@options')
76
+ expect(opts['openstack_api_key']).to eq('P1as$w0rd')
77
+ expect(opts['openstack_username']).to eq('user')
78
+ expect(opts['openstack_auth_url']).to eq('http://openstack_hypervisor.labs.net:5000/v3')
79
+ expect(opts['openstack_network']).to eq('testing')
80
+ expect(opts['security_group']).to eq(['my_sg', 'default'])
81
+ expect(opts['floating_ip_pool']).to eq('my_pool')
71
82
  end
72
83
 
73
- it 'check hosts options during initialization' do
84
+ it 'initializes host defaults' do
74
85
  @hosts.each do |host|
75
86
  expect(host['image']).to eq('default_image')
76
87
  expect(host['flavor']).to eq('m1.large')
@@ -78,168 +89,226 @@ module Beaker
78
89
  end
79
90
  end
80
91
 
81
- it 'check host options during server creation' do
92
+ it 'passes correct parameters to server creation' do
93
+ mock_flavor = double(id: 12345)
94
+ mock_image = double(id: 54321)
82
95
 
83
- mock_flavor = Object.new
84
- allow( mock_flavor ).to receive( :id ).and_return( 12345 )
85
- allow( openstack ).to receive( :flavor ).and_return( mock_flavor )
86
- expect( openstack ).to receive( :flavor ).with( 'm1.large' )
96
+ allow(openstack).to receive(:flavor).and_return(mock_flavor)
97
+ allow(openstack).to receive(:image).and_return(mock_image)
87
98
 
88
- mock_image = Object.new
89
- allow( mock_image ).to receive( :id ).and_return( 54321 )
90
- allow( openstack ).to receive( :image ).and_return( mock_image )
91
- expect( openstack ).to receive( :image ).with( 'default_image' )
92
-
93
- mock_servers = double().as_null_object
94
- allow( @compute_client ).to receive( :servers ).and_return( mock_servers )
99
+ mock_servers = double.as_null_object
100
+ allow(@compute_client).to receive(:servers).and_return(mock_servers)
95
101
 
96
102
  expect(mock_servers).to receive(:create).with(hash_including(
97
- :user_data => '#cloud-config\nmanage_etc_hosts: true\nfinal_message: "The host is finally up!"',
98
- :flavor_ref => 12345,
99
- :image_ref => 54321)
100
- )
103
+ flavor_ref: 12345,
104
+ user_data: '#cloud-config\nmanage_etc_hosts: true\nfinal_message: "The host is finally up!"'
105
+ ))
101
106
 
102
- @hosts.each do |host|
103
- allow(host).to receive(:wait_for_port).and_return(true)
104
- end
107
+ @hosts.each { |h| allow(h).to receive(:wait_for_port).and_return(true) }
108
+
109
+ mock_ip = double(ip: '172.16.0.1')
110
+ allow(mock_ip).to receive(:server=)
111
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
105
112
 
106
113
  openstack.provision
107
114
  end
108
115
 
109
- it 'generates valid keynames during server creation' do
110
- # Simulate getting a dynamic IP from OpenStack to test key generation code
111
- # after provisioning. See _validate_new_key_pair in openstack/nova for reference
112
- mock_ip = double().as_null_object
113
- allow( openstack ).to receive( :get_floating_ip ).and_return( mock_ip )
114
- allow( mock_ip ).to receive( :ip ).and_return( '172.16.0.1' )
116
+ it 'generates valid keynames' do
117
+ mock_ip = double(ip: '172.16.0.1')
118
+ allow(mock_ip).to receive(:server=)
119
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
115
120
  openstack.instance_eval('@options')['openstack_keyname'] = nil
116
121
 
117
- @hosts.each do |host|
118
- allow(host).to receive(:wait_for_port).and_return(true)
119
- end
122
+ @hosts.each { |h| allow(h).to receive(:wait_for_port).and_return(true) }
120
123
 
121
124
  openstack.provision
122
125
 
123
126
  @hosts.each do |host|
124
- expect(host[:keyname]).to match(/^[_\-0-9a-zA-Z]+$/)
127
+ expect(host[:keyname]).to match(/^[A-Za-z0-9.\-_]+$/)
125
128
  end
126
129
  end
127
130
 
128
- it 'get_floating_ip always allocates a new floatingip' do
129
- # Assume beaker is being executed in parallel N times by travis (or similar).
130
- # IPs are allocated (but not associated) before an instance is created; it is
131
- # hightly possible the first instance will allocate a new IP and create an ssh
132
- # key. While the instance is being created the other N-1 instances come along,
133
- # find the unused IP and try to use it as well which causes keyname clashes
134
- # and other IP related shenannigans. Ensure we allocate a new IP each and every
135
- # time
136
- mock_addresses = double().as_null_object
137
- mock_ip = double().as_null_object
138
- allow(@compute_client).to receive(:addresses).and_return(mock_addresses)
139
- allow(mock_addresses).to receive(:create).and_return(mock_ip)
140
- expect(mock_addresses).to receive(:create).exactly(3).times
141
- (1..3).each { openstack.get_floating_ip }
131
+ it 'allocates a new floating IP each time' do
132
+ mock_fips = double.as_null_object
133
+ allow(@network_client).to receive(:floating_ips).and_return(mock_fips)
134
+ expect(mock_fips).to receive(:create).exactly(3).times
135
+
136
+ 3.times { openstack.get_floating_ip }
137
+ end
138
+
139
+ it 'attempts metadata update but rescues failures' do
140
+ mock_flavor = double(id: 12345)
141
+ mock_image = double(id: 54321)
142
+
143
+ allow(openstack).to receive(:flavor).and_return(mock_flavor)
144
+ allow(openstack).to receive(:image).and_return(mock_image)
145
+
146
+ mock_servers = double.as_null_object
147
+ allow(@compute_client).to receive(:servers).and_return(mock_servers)
148
+
149
+ # Stub VM with id, addresses, attach_volume, wait_for
150
+ vm = double('vm', as_null_object: true)
151
+ allow(vm).to receive(:wait_for)
152
+ allow(vm).to receive(:id).and_return('vm-123')
153
+ allow(vm).to receive(:addresses).and_return({'private' => [{'addr' => '10.0.0.1'}]})
154
+ allow(vm).to receive(:attach_volume)
155
+ allow(mock_servers).to receive(:create).and_return(vm)
156
+
157
+ mock_metadata = double()
158
+ allow(mock_metadata).to receive(:update).and_raise("metadata disabled")
159
+ allow(vm).to receive(:metadata).and_return(mock_metadata)
160
+
161
+ mock_ip = double(ip: '172.16.0.1')
162
+ allow(mock_ip).to receive(:server=)
163
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
164
+
165
+ @hosts.each { |h| allow(h).to receive(:wait_for_port).and_return(true) }
166
+
167
+ expect { openstack.provision }.not_to raise_error
168
+ end
169
+
170
+ # ---------------------------------------------------------------------
171
+ # Boot-from-volume test
172
+ # ---------------------------------------------------------------------
173
+ it 'uses block_device_mapping_v2 when boot_from_volume is enabled' do
174
+ host = @hosts.first
175
+ host['boot_from_volume'] = true
176
+ host['root_volume'] = { 'size' => 20 }
177
+
178
+ mock_flavor = double(id: 12345)
179
+ mock_image = double(id: 'img-123')
180
+
181
+ allow(openstack).to receive(:flavor).and_return(mock_flavor)
182
+ allow(openstack).to receive(:image).and_return(mock_image)
183
+
184
+ mock_servers = double.as_null_object
185
+ allow(@compute_client).to receive(:servers).and_return(mock_servers)
186
+
187
+ expect(mock_servers).to receive(:create).with(hash_including(
188
+ flavor_ref: 12345,
189
+ block_device_mapping_v2: [
190
+ hash_including(
191
+ boot_index: 0,
192
+ uuid: 'img-123',
193
+ source_type: 'image',
194
+ destination_type: 'volume',
195
+ volume_size: 20
196
+ )
197
+ ]
198
+ ))
199
+
200
+ @hosts.each { |h| allow(h).to receive(:wait_for_port).and_return(true) }
201
+
202
+ mock_ip = double(ip: '172.16.0.1')
203
+ allow(mock_ip).to receive(:server=)
204
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
205
+
206
+ openstack.provision
142
207
  end
208
+ end
143
209
 
144
- context 'volume creation option' do
145
- it 'provisions volume by default' do
146
- mock_flavor = Object.new
147
- allow( mock_flavor ).to receive( :id ).and_return( 12345 )
148
- allow( openstack ).to receive( :flavor ).and_return( mock_flavor )
149
- mock_image = Object.new
150
- allow( mock_image ).to receive( :id ).and_return( 54321 )
151
- allow( openstack ).to receive( :image ).and_return( mock_image )
152
- mock_servers = double().as_null_object
153
- allow( @compute_client ).to receive( :servers ).and_return( mock_servers )
154
-
155
- @hosts.each do |host|
156
- allow(host).to receive(:wait_for_port).and_return(true)
157
- expect(openstack).to receive(:provision_storage)
158
- end
159
-
160
- openstack.provision
210
+ # -------------------------------------------------------------------------
211
+ # Volume support tests
212
+ # -------------------------------------------------------------------------
213
+ context 'volume creation option' do
214
+ it 'calls provision_storage when enabled' do
215
+ mock_flavor = double(id: 12345)
216
+ mock_image = double(id: 54321)
217
+
218
+ allow(openstack).to receive(:flavor).and_return(mock_flavor)
219
+ allow(openstack).to receive(:image).and_return(mock_image)
220
+
221
+ mock_servers = double.as_null_object
222
+ allow(@compute_client).to receive(:servers).and_return(mock_servers)
223
+
224
+ @hosts.each do |host|
225
+ allow(host).to receive(:wait_for_port).and_return(true)
226
+ expect(openstack).to receive(:provision_storage)
161
227
  end
162
228
 
163
- it 'skips provisioning when disabled' do
164
- mock_flavor = Object.new
165
- allow( mock_flavor ).to receive( :id ).and_return( 12345 )
166
- allow( openstack ).to receive( :flavor ).and_return( mock_flavor )
167
- mock_image = Object.new
168
- allow( mock_image ).to receive( :id ).and_return( 54321 )
169
- allow( openstack ).to receive( :image ).and_return( mock_image )
170
- mock_servers = double().as_null_object
171
- allow( @compute_client ).to receive( :servers ).and_return( mock_servers )
229
+ mock_ip = double(ip: '172.16.0.1')
230
+ allow(mock_ip).to receive(:server=)
231
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
232
+
233
+ openstack.provision
234
+ end
172
235
 
173
- openstack.instance_eval('@options')['openstack_volume_support'] = false
236
+ it 'skips provision_storage when disabled' do
237
+ openstack.instance_eval('@options')[:openstack_volume_support] = false
174
238
 
175
- @hosts.each do |host|
176
- allow(host).to receive(:wait_for_port).and_return(true)
177
- expect(openstack).not_to receive(:provision_storage)
178
- end
239
+ mock_flavor = double(id: 12345)
240
+ mock_image = double(id: 54321)
179
241
 
180
- openstack.provision
242
+ allow(openstack).to receive(:flavor).and_return(mock_flavor)
243
+ allow(openstack).to receive(:image).and_return(mock_image)
244
+
245
+ mock_servers = double.as_null_object
246
+ allow(@compute_client).to receive(:servers).and_return(mock_servers)
247
+
248
+ @hosts.each do |host|
249
+ allow(host).to receive(:wait_for_port).and_return(true)
250
+ expect(openstack).not_to receive(:provision_storage)
181
251
  end
252
+
253
+ mock_ip = double(ip: '172.16.0.1')
254
+ allow(mock_ip).to receive(:server=)
255
+ allow(openstack).to receive(:get_floating_ip).and_return(mock_ip)
256
+
257
+ openstack.provision
182
258
  end
183
259
  end
184
260
 
261
+ # -------------------------------------------------------------------------
262
+ # provision_storage tests
263
+ # -------------------------------------------------------------------------
185
264
  describe '#provision_storage' do
265
+ it 'creates and attaches volumes' do
266
+ allow(openstack).to receive(:get_volumes).and_return({
267
+ 'vol1' => { 'size' => 10 }
268
+ })
269
+
270
+ # Struct mimics a real host object with .name and .ssh
271
+ mock_host = Struct.new(:name, :ssh, :root_volume).new('host1', {}, nil)
272
+
273
+ # Mock volume client
274
+ mock_volume_client = double.as_null_object
275
+ openstack.instance_variable_set(:@volume_client, mock_volume_client)
276
+
277
+ mock_volume = double(status: 'available', as_null_object: true)
278
+ allow(mock_volume_client).to receive(:volumes).and_return(double(create: mock_volume))
279
+
280
+ allow(mock_volume).to receive(:wait_for).and_return(true)
281
+ allow(mock_volume).to receive(:id).and_return('vol-id')
186
282
 
187
- it 'creates volumes with cinder v1' do
188
- # Mock a volume
189
- allow(openstack).to receive(:get_volumes).and_return({'volume1' => {'size' => 1000000 }})
190
-
191
- # Stub out the call to create the client and hard code the return value
192
- allow(openstack).to receive(:volume_client_create).and_return(nil)
193
- client = double().as_null_object
194
- openstack.instance_variable_set(:@volume_client, client)
195
- allow(openstack).to receive(:get_volume_api_version).and_return(1)
196
-
197
- # Check the parameters are valid, correct 'name' parameter and correct size conversion
198
- mock_volume = double().as_null_object
199
- expect(client).to receive(:create).with(:display_name => 'volume1',
200
- :description => 'Beaker volume: host=alan volume=volume1',
201
- :size => 1000
202
- ).and_return(mock_volume)
203
- allow(mock_volume).to receive(:wait_for).and_return(nil)
204
-
205
- # Perform the test!
206
- mock_vm = double().as_null_object
207
- allow(mock_volume).to receive(:id).and_return('Fake ID')
208
- expect(mock_vm).to receive(:attach_volume).with('Fake ID', '/dev/vdb')
209
-
210
- mock_host = double().as_null_object
211
- allow(mock_host).to receive(:name).and_return('alan')
212
-
213
- openstack.provision_storage mock_host, mock_vm
283
+ # Mock VM
284
+ mock_vm = double(as_null_object: true)
285
+ allow(mock_vm).to receive(:attach_volume)
286
+ expect(mock_vm).to receive(:attach_volume).with('vol-id', '/dev/vdc')
287
+
288
+ # Call provision_storage
289
+ openstack.provision_storage(mock_host, mock_vm)
214
290
  end
291
+ end
292
+
293
+ # -------------------------------------------------------------------------
294
+ # cleanup tests
295
+ # -------------------------------------------------------------------------
296
+ describe '#cleanup' do
297
+ it 'deletes ephemeral keypairs created during provisioning' do
298
+ openstack.instance_variable_set(:@ephemeral_keypairs, ['kp1', 'kp2'])
299
+
300
+ fake_kp = double()
301
+ allow(fake_kp).to receive(:destroy)
302
+
303
+ mock_keypairs = double()
304
+ allow(@compute_client).to receive(:key_pairs).and_return(mock_keypairs)
305
+
306
+ allow(mock_keypairs).to receive(:get).with('kp1').and_return(fake_kp)
307
+ allow(mock_keypairs).to receive(:get).with('kp2').and_return(fake_kp)
308
+
309
+ expect(fake_kp).to receive(:destroy).twice
215
310
 
216
- it 'creates volumes with cinder v2' do
217
- # Mock a volume
218
- allow(openstack).to receive(:get_volumes).and_return({'volume1' => {'size' => 1000000 }})
219
-
220
- # Stub out the call to create the client and hard code the return value
221
- allow(openstack).to receive(:volume_client_create).and_return(nil)
222
- client = double().as_null_object
223
- openstack.instance_variable_set(:@volume_client, client)
224
- allow(openstack).to receive(:get_volume_api_version).and_return(-1)
225
-
226
- # Check the parameters are valid, correct 'name' parameter and correct size conversion
227
- mock_volume = double().as_null_object
228
- expect(client).to receive(:create).with(:name => 'volume1',
229
- :description => 'Beaker volume: host=alan volume=volume1',
230
- :size => 1000
231
- ).and_return(mock_volume)
232
- allow(mock_volume).to receive(:wait_for).and_return(nil)
233
-
234
- # Perform the test!
235
- mock_vm = double().as_null_object
236
- allow(mock_volume).to receive(:id).and_return('Fake ID')
237
- expect(mock_vm).to receive(:attach_volume).with('Fake ID', '/dev/vdb')
238
-
239
- mock_host = double().as_null_object
240
- allow(mock_host).to receive(:name).and_return('alan')
241
-
242
- openstack.provision_storage mock_host, mock_vm
311
+ expect { openstack.cleanup }.not_to raise_error
243
312
  end
244
313
  end
245
314
  end