1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
20
from heat.common import template_format
21
from heat.engine import scheduler
22
from heat.engine import resource
23
from heat.tests.common import HeatTestCase
24
from heat.tests import utils
26
from ..engine.plugins import cloud_loadbalancer as lb
28
# The following fakes are for pyrax
31
class FakeClient(object):
36
class FakeManager(object):
45
def delete(self, item):
48
def create(self, *args, **kwargs):
51
def find(self, *args, **kwargs):
54
def action(self, item, action_type, body={}):
58
class FakeLoadBalancerManager(object):
59
def __init__(self, api=None, *args, **kwargs):
62
def set_content_caching(self, *args, **kwargs):
66
class FakeNode(object):
67
def __init__(self, address="0.0.0.0", port=80, condition=None, weight=None,
68
status=None, parent=None, type=None, id=None):
69
self.address = address
71
self.condition = condition
78
def __eq__(self, other):
79
return self.__dict__ == other.__dict__
81
def __ne__(self, other):
82
return not self.__eq__(other)
85
class FakeVirtualIP(object):
86
def __init__(self, address=None, port=None, condition=None,
87
ipVersion=None, type=None):
88
self.address = address
90
self.condition = condition
91
self.ipVersion = ipVersion
94
def __eq__(self, other):
95
return self.__dict__ == other.__dict__
97
def __ne__(self, other):
98
return not self.__eq__(other)
101
class FakeLoadBalancerClient(object):
102
def __init__(self, *args, **kwargs):
104
self.VirtualIP = FakeVirtualIP
107
def get(self, *args, **kwargs):
110
def create(self, *args, **kwargs):
114
class FakeLoadBalancer(object):
115
def __init__(self, name=None, info=None, *args, **kwargs):
116
name = name or uuid.uuid4()
117
info = info or {"fake": "fake"}
118
self.id = uuid.uuid4()
119
self.manager = FakeLoadBalancerManager()
121
self.VirtualIP = FakeVirtualIP
124
def get(self, *args, **kwargs):
127
def add_nodes(self, *args, **kwargs):
130
def add_ssl_termination(self, *args, **kwargs):
133
def set_error_page(self, *args, **kwargs):
136
def add_access_list(self, *args, **kwargs):
140
class LoadBalancerWithFakeClient(lb.CloudLoadBalancer):
142
return FakeLoadBalancerClient()
145
def override_resource():
147
'Rackspace::Cloud::LoadBalancer': LoadBalancerWithFakeClient
151
class LoadBalancerTest(HeatTestCase):
154
super(LoadBalancerTest, self).setUp()
157
"AWSTemplateFormatVersion": "2010-09-09",
158
"Description": "fawef",
160
self._get_lb_resource_name(): {
161
"Type": "Rackspace::Cloud::LoadBalancer",
164
"nodes": [{"address": "166.78.103.141", "port": 80,
165
"condition": "ENABLED"}],
169
{"type": "PUBLIC", "ipVersion": "IPV6"}],
170
"algorithm": 'LEAST_CONNECTIONS',
171
"connectionThrottle": {'maxConnectionRate': 1000},
173
'contentCaching': 'DISABLED'
179
self.lb_name = 'test-clb'
180
self.expected_body = {
181
"nodes": [FakeNode(address=u"166.78.103.141", port=80,
182
condition=u"ENABLED")],
185
"virtual_ips": [FakeVirtualIP(type=u"PUBLIC", ipVersion=u"IPV6")],
187
"algorithm": u'LEAST_CONNECTIONS',
188
"connectionThrottle": {'maxConnectionRate': 1000,
189
'maxConnections': None,
190
'rateInterval': None,
191
'minConnections': None},
192
"connectionLogging": None,
194
"healthMonitor": None,
196
"sessionPersistence": None,
200
lb.resource_mapping = override_resource
201
utils.setup_dummy_db()
202
resource._register_class("Rackspace::Cloud::LoadBalancer",
203
LoadBalancerWithFakeClient)
205
def _get_lb_resource_name(self):
206
return "lb-" + str(uuid.uuid4())
208
def __getattribute__(self, name):
209
if name == 'expected_body' or name == 'lb_template':
210
return copy.deepcopy(super(LoadBalancerTest, self)
211
.__getattribute__(name))
212
return super(LoadBalancerTest, self).__getattribute__(name)
214
def _mock_create(self, t, stack, resource_name, lb_name, lb_body):
215
rsrc = LoadBalancerWithFakeClient(resource_name,
216
t['Resources'][resource_name],
218
self.m.StubOutWithMock(rsrc.clb, 'create')
219
fake_loadbalancer = FakeLoadBalancer(name=lb_name)
220
rsrc.clb.create(lb_name, **lb_body).AndReturn(fake_loadbalancer)
221
return (rsrc, fake_loadbalancer)
223
def _get_first_resource_name(self, templ):
224
return next(k for k in templ['Resources'])
226
def _mock_loadbalancer(self, lb_template, expected_name, expected_body):
227
t = template_format.parse(json.dumps(lb_template))
228
s = utils.parse_stack(t, stack_name=utils.random_name())
230
rsrc, fake_loadbalancer = self._mock_create(t, s,
232
_get_first_resource_name(
236
self.m.StubOutWithMock(fake_loadbalancer, 'get')
237
fake_loadbalancer.get().MultipleTimes().AndReturn(None)
239
fake_loadbalancer.status = 'ACTIVE'
241
return (rsrc, fake_loadbalancer)
243
def _set_template(self, templ, **kwargs):
244
for k, v in kwargs.iteritems():
245
templ['Resources'][self._get_first_resource_name(templ)][
249
def _set_expected(self, expected, **kwargs):
250
for k, v in kwargs.iteritems():
254
def test_alter_properties(self):
255
#test alter properties functions
256
template = self._set_template(self.lb_template,
257
sessionPersistence='HTTP_COOKIE',
258
connectionLogging=True,
259
metadata={'yolo': 'heeyyy_gurl'})
261
expected = self._set_expected(self.expected_body,
263
{'persistenceType': 'HTTP_COOKIE'},
264
connectionLogging={'enabled': True},
267
'value': 'heeyyy_gurl'}])
269
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
274
scheduler.TaskRunner(rsrc.create)()
277
def test_validate_half_closed(self):
278
#test failure (invalid protocol)
279
template = self._set_template(self.lb_template, halfClosed=True)
280
expected = self._set_expected(self.expected_body, halfClosed=True)
281
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
284
self.assertEqual(rsrc.validate(), {
286
'The halfClosed property is only available for the '
287
'TCP or TCP_CLIENT_FIRST protocols'})
290
template = self._set_template(template, protocol='TCP')
291
expected = self._set_expected(expected, protocol='TCP')
292
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
295
self.assertEqual(rsrc.validate(), None)
297
#test TCP_CLIENT_FIRST protocol
298
template = self._set_template(template,
299
protocol='TCP_CLIENT_FIRST')
300
expected = self._set_expected(expected,
301
protocol='TCP_CLIENT_FIRST')
302
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
305
self.assertEqual(rsrc.validate(), None)
307
def test_validate_health_monitor(self):
308
#test connect success
311
'attemptsBeforeDeactivation': 1,
315
template = self._set_template(self.lb_template,
316
healthMonitor=health_monitor)
317
expected = self._set_expected(self.expected_body,
318
healthMonitor=health_monitor)
319
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
323
self.assertEqual(rsrc.validate(), None)
325
#test connect failure
326
#bodyRegex is only valid for type 'HTTP(S)'
327
health_monitor['bodyRegex'] = 'dfawefawe'
328
template = self._set_template(template,
329
healthMonitor=health_monitor)
330
expected = self._set_expected(expected,
331
healthMonitor=health_monitor)
332
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
335
self.assertEqual(rsrc.validate(),
336
{'Error': 'Unknown Property bodyRegex'})
339
health_monitor['type'] = 'HTTP'
340
health_monitor['bodyRegex'] = 'bodyRegex'
341
health_monitor['statusRegex'] = 'statusRegex'
342
health_monitor['hostHeader'] = 'hostHeader'
343
health_monitor['path'] = 'path'
345
template = self._set_template(template,
346
healthMonitor=health_monitor)
347
expected = self._set_expected(expected,
348
healthMonitor=health_monitor)
349
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
352
self.assertEqual(rsrc.validate(), None)
354
def test_validate_ssl_termination(self):
357
'privatekey': 'ewfawe',
358
'certificate': 'dfaewfwef',
359
'intermediateCertificate': 'fwaefawe',
360
'secureTrafficOnly': True
363
#test ssl termination enabled without required fields failure
364
template = self._set_template(self.lb_template,
365
sslTermination=ssl_termination)
366
expected = self._set_expected(self.expected_body,
367
sslTermination=ssl_termination)
368
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
371
self.assertEqual(rsrc.validate(),
373
'Property error : %s: Property securePort not '
374
'assigned' % rsrc.name})
376
ssl_termination['securePort'] = 443
377
template = self._set_template(template,
378
sslTermination=ssl_termination)
379
expected = self._set_expected(expected,
380
sslTermination=ssl_termination)
381
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
384
self.assertEqual(rsrc.validate(), None)
386
def test_post_creation_access_list(self):
387
access_list = [{"address": '192.168.1.1/0',
389
{'address': '172.165.3.43',
392
template = self._set_template(self.lb_template,
393
accessList=access_list)
394
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
397
self.m.StubOutWithMock(fake_loadbalancer, 'add_access_list')
398
fake_loadbalancer.add_access_list(access_list)
401
scheduler.TaskRunner(rsrc.create)()
404
def test_post_creation_error_page(self):
405
error_page = "REALLY BIG ERROR"
407
template = self._set_template(self.lb_template,
408
errorPage=error_page)
409
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
412
self.m.StubOutWithMock(fake_loadbalancer, 'set_error_page')
413
fake_loadbalancer.set_error_page(error_page)
416
scheduler.TaskRunner(rsrc.create)()
419
def test_post_creation_ssl_termination(self):
422
'privatekey': 'afwefawe',
423
'certificate': 'fawefwea',
424
'intermediateCertificate': "intermediate_certificate",
426
'secureTrafficOnly': False
429
template = self._set_template(self.lb_template,
430
sslTermination=ssl_termination)
431
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
434
self.m.StubOutWithMock(fake_loadbalancer, 'add_ssl_termination')
435
fake_loadbalancer.add_ssl_termination(
436
ssl_termination['securePort'],
437
ssl_termination['privatekey'],
438
ssl_termination['certificate'],
439
intermediateCertificate=ssl_termination['intermediateCertificate'],
440
enabled=ssl_termination['enabled'],
441
secureTrafficOnly=ssl_termination['secureTrafficOnly'])
444
scheduler.TaskRunner(rsrc.create)()
447
def test_post_creation_content_caching(self):
448
template = self._set_template(self.lb_template,
449
contentCaching='ENABLED')
450
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
454
scheduler.TaskRunner(rsrc.create)()
457
def test_update_add_node_by_ref(self):
458
added_node = {'nodes': [
459
{"address": "166.78.103.141", "port": 80, "condition": "ENABLED"},
460
{"ref": "TEST_NODE_REF", "port": 80, "condition": "ENABLED"}]}
461
expected_ip = '172.168.1.4'
462
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
465
fake_loadbalancer.nodes = self.expected_body['nodes']
467
scheduler.TaskRunner(rsrc.create)()
470
self.m.StubOutWithMock(rsrc.clb, 'get')
471
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
473
self.m.StubOutWithMock(rsrc.stack, 'resource_by_refid')
475
class FakeFn(object):
476
def FnGetAtt(self, attr):
479
rsrc.stack.resource_by_refid('TEST_NODE_REF').AndReturn(FakeFn())
481
self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
482
fake_loadbalancer.add_nodes([
483
fake_loadbalancer.Node(address=expected_ip,
485
condition='ENABLED')])
488
rsrc.handle_update({}, {}, added_node)
491
def test_update_add_node_by_address(self):
492
expected_ip = '172.168.1.4'
493
added_node = {'nodes': [
494
{"address": "166.78.103.141", "port": 80, "condition": "ENABLED"},
495
{"address": expected_ip, "port": 80, "condition": "ENABLED"}]}
496
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
499
fake_loadbalancer.nodes = self.expected_body['nodes']
501
scheduler.TaskRunner(rsrc.create)()
504
self.m.StubOutWithMock(rsrc.clb, 'get')
505
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
507
self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
508
fake_loadbalancer.add_nodes([
509
fake_loadbalancer.Node(address=expected_ip,
511
condition='ENABLED')])
514
rsrc.handle_update({}, {}, added_node)
517
def test_update_delete_node_failed(self):
518
deleted_node = {'nodes': []}
519
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
522
fake_loadbalancer.nodes = self.expected_body['nodes']
524
scheduler.TaskRunner(rsrc.create)()
527
self.m.StubOutWithMock(rsrc.clb, 'get')
528
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
531
self.assertRaises(ValueError, rsrc.handle_update, {}, {}, deleted_node)