~ubuntu-branches/ubuntu/trusty/heat/trusty-security

« back to all changes in this revision

Viewing changes to contrib/rackspace/heat/tests/test_cloud_loadbalancer.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-10-03 09:43:04 UTC
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: package-import@ubuntu.com-20131003094304-zhhr2brapzlpvjmm
Tags: upstream-2013.2~rc1
ImportĀ upstreamĀ versionĀ 2013.2~rc1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#    vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
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
 
6
#
 
7
#         http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
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
 
13
#    under the License.
 
14
 
 
15
 
 
16
import uuid
 
17
import json
 
18
import copy
 
19
 
 
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
 
25
 
 
26
from ..engine.plugins import cloud_loadbalancer as lb
 
27
 
 
28
# The following fakes are for pyrax
 
29
 
 
30
 
 
31
class FakeClient(object):
 
32
    user_agent = "Fake"
 
33
    USER_AGENT = "Fake"
 
34
 
 
35
 
 
36
class FakeManager(object):
 
37
    api = FakeClient()
 
38
 
 
39
    def list(self):
 
40
        pass
 
41
 
 
42
    def get(self, item):
 
43
        pass
 
44
 
 
45
    def delete(self, item):
 
46
        pass
 
47
 
 
48
    def create(self, *args, **kwargs):
 
49
        pass
 
50
 
 
51
    def find(self, *args, **kwargs):
 
52
        pass
 
53
 
 
54
    def action(self, item, action_type, body={}):
 
55
        pass
 
56
 
 
57
 
 
58
class FakeLoadBalancerManager(object):
 
59
    def __init__(self, api=None, *args, **kwargs):
 
60
        pass
 
61
 
 
62
    def set_content_caching(self, *args, **kwargs):
 
63
        pass
 
64
 
 
65
 
 
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
 
70
        self.port = port
 
71
        self.condition = condition
 
72
        self.weight = weight
 
73
        self.status = status
 
74
        self.parent = parent
 
75
        self.type = type
 
76
        self.id = id
 
77
 
 
78
    def __eq__(self, other):
 
79
        return self.__dict__ == other.__dict__
 
80
 
 
81
    def __ne__(self, other):
 
82
        return not self.__eq__(other)
 
83
 
 
84
 
 
85
class FakeVirtualIP(object):
 
86
    def __init__(self, address=None, port=None, condition=None,
 
87
                 ipVersion=None, type=None):
 
88
        self.address = address
 
89
        self.port = port
 
90
        self.condition = condition
 
91
        self.ipVersion = ipVersion
 
92
        self.type = type
 
93
 
 
94
    def __eq__(self, other):
 
95
        return self.__dict__ == other.__dict__
 
96
 
 
97
    def __ne__(self, other):
 
98
        return not self.__eq__(other)
 
99
 
 
100
 
 
101
class FakeLoadBalancerClient(object):
 
102
    def __init__(self, *args, **kwargs):
 
103
        self.Node = FakeNode
 
104
        self.VirtualIP = FakeVirtualIP
 
105
        pass
 
106
 
 
107
    def get(self, *args, **kwargs):
 
108
        pass
 
109
 
 
110
    def create(self, *args, **kwargs):
 
111
        pass
 
112
 
 
113
 
 
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()
 
120
        self.Node = FakeNode
 
121
        self.VirtualIP = FakeVirtualIP
 
122
        self.nodes = []
 
123
 
 
124
    def get(self, *args, **kwargs):
 
125
        pass
 
126
 
 
127
    def add_nodes(self, *args, **kwargs):
 
128
        pass
 
129
 
 
130
    def add_ssl_termination(self, *args, **kwargs):
 
131
        pass
 
132
 
 
133
    def set_error_page(self, *args, **kwargs):
 
134
        pass
 
135
 
 
136
    def add_access_list(self, *args, **kwargs):
 
137
        pass
 
138
 
 
139
 
 
140
class LoadBalancerWithFakeClient(lb.CloudLoadBalancer):
 
141
    def cloud_lb(self):
 
142
        return FakeLoadBalancerClient()
 
143
 
 
144
 
 
145
def override_resource():
 
146
    return {
 
147
        'Rackspace::Cloud::LoadBalancer': LoadBalancerWithFakeClient
 
148
    }
 
149
 
 
150
 
 
151
class LoadBalancerTest(HeatTestCase):
 
152
 
 
153
    def setUp(self):
 
154
        super(LoadBalancerTest, self).setUp()
 
155
 
 
156
        self.lb_template = {
 
157
            "AWSTemplateFormatVersion": "2010-09-09",
 
158
            "Description": "fawef",
 
159
            "Resources": {
 
160
                self._get_lb_resource_name(): {
 
161
                    "Type": "Rackspace::Cloud::LoadBalancer",
 
162
                    "Properties": {
 
163
                        "name": "test-clb",
 
164
                        "nodes": [{"address": "166.78.103.141", "port": 80,
 
165
                                   "condition": "ENABLED"}],
 
166
                        "protocol": "HTTP",
 
167
                        "port": 80,
 
168
                        "virtualIps": [
 
169
                            {"type": "PUBLIC", "ipVersion": "IPV6"}],
 
170
                        "algorithm": 'LEAST_CONNECTIONS',
 
171
                        "connectionThrottle": {'maxConnectionRate': 1000},
 
172
                        'timeout': 110,
 
173
                        'contentCaching': 'DISABLED'
 
174
                    }
 
175
                }
 
176
            }
 
177
        }
 
178
 
 
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")],
 
183
            "protocol": u'HTTP',
 
184
            "port": 80,
 
185
            "virtual_ips": [FakeVirtualIP(type=u"PUBLIC", ipVersion=u"IPV6")],
 
186
            "halfClosed": None,
 
187
            "algorithm": u'LEAST_CONNECTIONS',
 
188
            "connectionThrottle": {'maxConnectionRate': 1000,
 
189
                                   'maxConnections': None,
 
190
                                   'rateInterval': None,
 
191
                                   'minConnections': None},
 
192
            "connectionLogging": None,
 
193
            "halfClosed": None,
 
194
            "healthMonitor": None,
 
195
            "metadata": None,
 
196
            "sessionPersistence": None,
 
197
            "timeout": 110
 
198
        }
 
199
 
 
200
        lb.resource_mapping = override_resource
 
201
        utils.setup_dummy_db()
 
202
        resource._register_class("Rackspace::Cloud::LoadBalancer",
 
203
                                 LoadBalancerWithFakeClient)
 
204
 
 
205
    def _get_lb_resource_name(self):
 
206
        return "lb-" + str(uuid.uuid4())
 
207
 
 
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)
 
213
 
 
214
    def _mock_create(self, t, stack, resource_name, lb_name, lb_body):
 
215
        rsrc = LoadBalancerWithFakeClient(resource_name,
 
216
                                          t['Resources'][resource_name],
 
217
                                          stack)
 
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)
 
222
 
 
223
    def _get_first_resource_name(self, templ):
 
224
        return next(k for k in templ['Resources'])
 
225
 
 
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())
 
229
 
 
230
        rsrc, fake_loadbalancer = self._mock_create(t, s,
 
231
                                                    self.
 
232
                                                    _get_first_resource_name(
 
233
                                                        lb_template),
 
234
                                                    expected_name,
 
235
                                                    expected_body)
 
236
        self.m.StubOutWithMock(fake_loadbalancer, 'get')
 
237
        fake_loadbalancer.get().MultipleTimes().AndReturn(None)
 
238
 
 
239
        fake_loadbalancer.status = 'ACTIVE'
 
240
 
 
241
        return (rsrc, fake_loadbalancer)
 
242
 
 
243
    def _set_template(self, templ, **kwargs):
 
244
        for k, v in kwargs.iteritems():
 
245
            templ['Resources'][self._get_first_resource_name(templ)][
 
246
                'Properties'][k] = v
 
247
        return templ
 
248
 
 
249
    def _set_expected(self, expected, **kwargs):
 
250
        for k, v in kwargs.iteritems():
 
251
            expected[k] = v
 
252
        return expected
 
253
 
 
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'})
 
260
 
 
261
        expected = self._set_expected(self.expected_body,
 
262
                                      sessionPersistence=
 
263
                                      {'persistenceType': 'HTTP_COOKIE'},
 
264
                                      connectionLogging={'enabled': True},
 
265
                                      metadata=[
 
266
                                          {'key': 'yolo',
 
267
                                           'value': 'heeyyy_gurl'}])
 
268
 
 
269
        rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
 
270
                                                          self.lb_name,
 
271
                                                          expected)
 
272
 
 
273
        self.m.ReplayAll()
 
274
        scheduler.TaskRunner(rsrc.create)()
 
275
        self.m.VerifyAll()
 
276
 
 
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,
 
282
                                                          self.lb_name,
 
283
                                                          expected)
 
284
        self.assertEqual(rsrc.validate(), {
 
285
            'Error':
 
286
            'The halfClosed property is only available for the '
 
287
            'TCP or TCP_CLIENT_FIRST protocols'})
 
288
 
 
289
        #test TCP protocol
 
290
        template = self._set_template(template, protocol='TCP')
 
291
        expected = self._set_expected(expected, protocol='TCP')
 
292
        rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
 
293
                                                          self.lb_name,
 
294
                                                          expected)
 
295
        self.assertEqual(rsrc.validate(), None)
 
296
 
 
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,
 
303
                                                          self.lb_name,
 
304
                                                          expected)
 
305
        self.assertEqual(rsrc.validate(), None)
 
306
 
 
307
    def test_validate_health_monitor(self):
 
308
        #test connect success
 
309
        health_monitor = {
 
310
            'type': 'CONNECT',
 
311
            'attemptsBeforeDeactivation': 1,
 
312
            'delay': 1,
 
313
            'timeout': 1
 
314
        }
 
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,
 
320
                                                          self.lb_name,
 
321
                                                          expected)
 
322
 
 
323
        self.assertEqual(rsrc.validate(), None)
 
324
 
 
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,
 
333
                                                          self.lb_name,
 
334
                                                          expected)
 
335
        self.assertEqual(rsrc.validate(),
 
336
                         {'Error': 'Unknown Property bodyRegex'})
 
337
 
 
338
        #test http fields
 
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'
 
344
 
 
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,
 
350
                                                          self.lb_name,
 
351
                                                          expected)
 
352
        self.assertEqual(rsrc.validate(), None)
 
353
 
 
354
    def test_validate_ssl_termination(self):
 
355
        ssl_termination = {
 
356
            'enabled': True,
 
357
            'privatekey': 'ewfawe',
 
358
            'certificate': 'dfaewfwef',
 
359
            'intermediateCertificate': 'fwaefawe',
 
360
            'secureTrafficOnly': True
 
361
        }
 
362
 
 
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,
 
369
                                                          self.lb_name,
 
370
                                                          expected)
 
371
        self.assertEqual(rsrc.validate(),
 
372
                         {'Error':
 
373
                         'Property error : %s: Property securePort not '
 
374
                         'assigned' % rsrc.name})
 
375
 
 
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,
 
382
                                                          self.lb_name,
 
383
                                                          expected)
 
384
        self.assertEqual(rsrc.validate(), None)
 
385
 
 
386
    def test_post_creation_access_list(self):
 
387
        access_list = [{"address": '192.168.1.1/0',
 
388
                        'type': 'ALLOW'},
 
389
                       {'address': '172.165.3.43',
 
390
                        'type': 'DENY'}]
 
391
 
 
392
        template = self._set_template(self.lb_template,
 
393
                                      accessList=access_list)
 
394
        rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
 
395
                                                          self.lb_name,
 
396
                                                          self.expected_body)
 
397
        self.m.StubOutWithMock(fake_loadbalancer, 'add_access_list')
 
398
        fake_loadbalancer.add_access_list(access_list)
 
399
 
 
400
        self.m.ReplayAll()
 
401
        scheduler.TaskRunner(rsrc.create)()
 
402
        self.m.VerifyAll()
 
403
 
 
404
    def test_post_creation_error_page(self):
 
405
        error_page = "REALLY BIG ERROR"
 
406
 
 
407
        template = self._set_template(self.lb_template,
 
408
                                      errorPage=error_page)
 
409
        rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
 
410
                                                          self.lb_name,
 
411
                                                          self.expected_body)
 
412
        self.m.StubOutWithMock(fake_loadbalancer, 'set_error_page')
 
413
        fake_loadbalancer.set_error_page(error_page)
 
414
 
 
415
        self.m.ReplayAll()
 
416
        scheduler.TaskRunner(rsrc.create)()
 
417
        self.m.VerifyAll()
 
418
 
 
419
    def test_post_creation_ssl_termination(self):
 
420
        ssl_termination = {
 
421
            'securePort': 443,
 
422
            'privatekey': 'afwefawe',
 
423
            'certificate': 'fawefwea',
 
424
            'intermediateCertificate': "intermediate_certificate",
 
425
            'enabled': True,
 
426
            'secureTrafficOnly': False
 
427
        }
 
428
 
 
429
        template = self._set_template(self.lb_template,
 
430
                                      sslTermination=ssl_termination)
 
431
        rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
 
432
                                                          self.lb_name,
 
433
                                                          self.expected_body)
 
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'])
 
442
 
 
443
        self.m.ReplayAll()
 
444
        scheduler.TaskRunner(rsrc.create)()
 
445
        self.m.VerifyAll()
 
446
 
 
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,
 
451
                                                          self.lb_name,
 
452
                                                          self.expected_body)
 
453
        self.m.ReplayAll()
 
454
        scheduler.TaskRunner(rsrc.create)()
 
455
        self.m.VerifyAll()
 
456
 
 
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,
 
463
                                                          self.lb_name,
 
464
                                                          self.expected_body)
 
465
        fake_loadbalancer.nodes = self.expected_body['nodes']
 
466
        self.m.ReplayAll()
 
467
        scheduler.TaskRunner(rsrc.create)()
 
468
        self.m.VerifyAll()
 
469
 
 
470
        self.m.StubOutWithMock(rsrc.clb, 'get')
 
471
        rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
 
472
 
 
473
        self.m.StubOutWithMock(rsrc.stack, 'resource_by_refid')
 
474
 
 
475
        class FakeFn(object):
 
476
            def FnGetAtt(self, attr):
 
477
                return expected_ip
 
478
 
 
479
        rsrc.stack.resource_by_refid('TEST_NODE_REF').AndReturn(FakeFn())
 
480
 
 
481
        self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
 
482
        fake_loadbalancer.add_nodes([
 
483
            fake_loadbalancer.Node(address=expected_ip,
 
484
                                   port=80,
 
485
                                   condition='ENABLED')])
 
486
 
 
487
        self.m.ReplayAll()
 
488
        rsrc.handle_update({}, {}, added_node)
 
489
        self.m.VerifyAll()
 
490
 
 
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,
 
497
                                                          self.lb_name,
 
498
                                                          self.expected_body)
 
499
        fake_loadbalancer.nodes = self.expected_body['nodes']
 
500
        self.m.ReplayAll()
 
501
        scheduler.TaskRunner(rsrc.create)()
 
502
        self.m.VerifyAll()
 
503
 
 
504
        self.m.StubOutWithMock(rsrc.clb, 'get')
 
505
        rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
 
506
 
 
507
        self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
 
508
        fake_loadbalancer.add_nodes([
 
509
            fake_loadbalancer.Node(address=expected_ip,
 
510
                                   port=80,
 
511
                                   condition='ENABLED')])
 
512
 
 
513
        self.m.ReplayAll()
 
514
        rsrc.handle_update({}, {}, added_node)
 
515
        self.m.VerifyAll()
 
516
 
 
517
    def test_update_delete_node_failed(self):
 
518
        deleted_node = {'nodes': []}
 
519
        rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
 
520
                                                          self.lb_name,
 
521
                                                          self.expected_body)
 
522
        fake_loadbalancer.nodes = self.expected_body['nodes']
 
523
        self.m.ReplayAll()
 
524
        scheduler.TaskRunner(rsrc.create)()
 
525
        self.m.VerifyAll()
 
526
 
 
527
        self.m.StubOutWithMock(rsrc.clb, 'get')
 
528
        rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
 
529
 
 
530
        self.m.ReplayAll()
 
531
        self.assertRaises(ValueError, rsrc.handle_update, {}, {}, deleted_node)
 
532
        self.m.VerifyAll()