~ubuntu-branches/ubuntu/trusty/swift/trusty-updates

« back to all changes in this revision

Viewing changes to test/unit/proxy/test_server.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, James Page, Chuck Short
  • Date: 2013-08-13 10:37:13 UTC
  • mfrom: (1.2.21)
  • Revision ID: package-import@ubuntu.com-20130813103713-1ctbx4zifyljs2aq
Tags: 1.9.1-0ubuntu1
[ James Page ]
* d/control: Update VCS fields for new branch locations.

[ Chuck Short ]
* New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import unittest
22
22
import urlparse
23
23
import signal
24
 
from contextlib import contextmanager, nested
 
24
from contextlib import contextmanager, nested, closing
25
25
from gzip import GzipFile
26
26
from shutil import rmtree
27
27
import time
28
28
from urllib import quote
29
29
from hashlib import md5
30
30
from tempfile import mkdtemp
31
 
import random
32
31
 
33
32
import mock
34
33
from eventlet import sleep, spawn, wsgi, listen
41
40
from swift.container import server as container_server
42
41
from swift.obj import server as object_server
43
42
from swift.common import ring
44
 
from swift.common.exceptions import ChunkReadTimeout, SloSegmentError
 
43
from swift.common.exceptions import ChunkReadTimeout, SegmentError
45
44
from swift.common.constraints import MAX_META_NAME_LENGTH, \
46
45
    MAX_META_VALUE_LENGTH, MAX_META_COUNT, MAX_META_OVERALL_SIZE, \
47
46
    MAX_FILE_SIZE, MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH
61
60
 
62
61
STATIC_TIME = time.time()
63
62
_request_instances = 0
 
63
_test_coros = _test_servers = _test_sockets = _orig_container_listing_limit = \
 
64
    _testdir = _orig_SysLogHandler = None
64
65
 
65
66
 
66
67
def request_init(self, *args, **kwargs):
113
114
    obj2lis = listen(('localhost', 0))
114
115
    _test_sockets = \
115
116
        (prolis, acc1lis, acc2lis, con1lis, con2lis, obj1lis, obj2lis)
116
 
    pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
117
 
                [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
118
 
                  'port': acc1lis.getsockname()[1]},
119
 
                 {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
120
 
                  'port': acc2lis.getsockname()[1]}], 30),
121
 
                GzipFile(os.path.join(_testdir, 'account.ring.gz'), 'wb'))
122
 
    pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
123
 
                [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
124
 
                  'port': con1lis.getsockname()[1]},
125
 
                 {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
126
 
                  'port': con2lis.getsockname()[1]}], 30),
127
 
                GzipFile(os.path.join(_testdir, 'container.ring.gz'), 'wb'))
128
 
    pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
129
 
                [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
130
 
                  'port': obj1lis.getsockname()[1]},
131
 
                 {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
132
 
                  'port': obj2lis.getsockname()[1]}], 30),
133
 
                GzipFile(os.path.join(_testdir, 'object.ring.gz'), 'wb'))
 
117
    with closing(GzipFile(os.path.join(_testdir, 'account.ring.gz'), 'wb')) \
 
118
            as f:
 
119
        pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
 
120
                    [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
 
121
                      'port': acc1lis.getsockname()[1]},
 
122
                     {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
 
123
                      'port': acc2lis.getsockname()[1]}], 30),
 
124
                    f)
 
125
    with closing(GzipFile(os.path.join(_testdir, 'container.ring.gz'), 'wb')) \
 
126
             as f:
 
127
        pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
 
128
                    [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
 
129
                      'port': con1lis.getsockname()[1]},
 
130
                     {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
 
131
                      'port': con2lis.getsockname()[1]}], 30),
 
132
                    f)
 
133
    with closing(GzipFile(os.path.join(_testdir, 'object.ring.gz'), 'wb')) \
 
134
            as f:
 
135
        pickle.dump(ring.RingData([[0, 1, 0, 1], [1, 0, 1, 0]],
 
136
                    [{'id': 0, 'zone': 0, 'device': 'sda1', 'ip': '127.0.0.1',
 
137
                      'port': obj1lis.getsockname()[1]},
 
138
                     {'id': 1, 'zone': 1, 'device': 'sdb1', 'ip': '127.0.0.1',
 
139
                      'port': obj2lis.getsockname()[1]}], 30),
 
140
                    f)
134
141
    prosrv = proxy_server.Application(conf, FakeMemcacheReturnsNone())
135
142
    acc1srv = account_server.AccountController(conf)
136
143
    acc2srv = account_server.AccountController(conf)
542
549
            req = Request.blank('/v1/a')
543
550
            req.environ['swift.authorize'] = authorize
544
551
            app.update_request(req)
545
 
            resp = app.handle_request(req)
 
552
            app.handle_request(req)
546
553
        self.assert_(called[0])
547
554
 
548
555
    def test_calls_authorize_deny(self):
558
565
        req = Request.blank('/v1/a')
559
566
        req.environ['swift.authorize'] = authorize
560
567
        app.update_request(req)
561
 
        resp = app.handle_request(req)
 
568
        app.handle_request(req)
562
569
        self.assert_(called[0])
563
570
 
564
571
    def test_negative_content_length(self):
633
640
 
634
641
        nodes = [{'region': 2, 'zone': 1, 'ip': '127.0.0.1'},
635
642
                 {'region': 1, 'zone': 2, 'ip': '127.0.0.2'}]
636
 
        with mock.patch('swift.proxy.server.shuffle', lambda x:x):
 
643
        with mock.patch('swift.proxy.server.shuffle', lambda x: x):
637
644
            app_sorted = baseapp.sort_nodes(nodes)
638
645
            exp_sorted = [{'region': 1, 'zone': 2, 'ip': '127.0.0.2'},
639
646
                          {'region': 2, 'zone': 1, 'ip': '127.0.0.1'}]
1036
1043
                                headers={'X-If-Delete-At': 1},
1037
1044
                                environ={'REQUEST_METHOD': 'DELETE'})
1038
1045
            self.app.update_request(req)
1039
 
            res = controller.DELETE(req)
 
1046
            controller.DELETE(req)
1040
1047
            self.assertEquals(test_errors, [])
1041
1048
 
1042
1049
    def test_GET_manifest_no_segments(self):
1268
1275
            self.assertEqual(resp.status_int, 200)
1269
1276
            self.assertEqual(resp.body, json_listing)
1270
1277
            self.assertEqual(resp.content_type, 'application/json')
 
1278
            self.assertEqual(resp.charset.lower(), 'utf-8')
1271
1279
 
1272
1280
            self.assertEqual(
1273
1281
                requested,
1370
1378
            self.assertEqual(resp.status_int, 200)
1371
1379
            self.assertEqual(resp.content_length, 4)  # content incomplete
1372
1380
            self.assertEqual(resp.content_type, 'text/html')
1373
 
            self.assertRaises(SloSegmentError, lambda: resp.body)
 
1381
            self.assertRaises(SegmentError, lambda: resp.body)
1374
1382
            # dropped connection, exception is caught by eventlet as it is
1375
1383
            # iterating over response
1376
1384
 
1388
1396
                    "bytes": 2,
1389
1397
                    "name": "/d1/seg01",
1390
1398
                    "content_type": "application/octet-stream"},
1391
 
                   {"hash": "d526f1c8ef6c1e4e980e2b8471352d23",
 
1399
                   {"hash": "8681fb3ada2715c8754706ee5f23d4f8",
1392
1400
                    "last_modified": "2012-11-08T04:05:37.846710",
 
1401
                    "bytes": 4,
 
1402
                    "name": "/d2/sub_manifest",
 
1403
                    "content_type": "application/octet-stream"},
 
1404
                   {"hash": "419af6d362a14b7a789ba1c7e772bbae",
 
1405
                    "last_modified": "2012-11-08T04:05:37.866820",
1393
1406
                    "bytes": 2,
1394
 
                    "name": "/d2/seg02",
 
1407
                    "name": "/d1/seg04",
1395
1408
                    "content_type": "application/octet-stream"}]
1396
1409
 
 
1410
        sub_listing = [{"hash": "d526f1c8ef6c1e4e980e2b8471352d23",
 
1411
                        "last_modified": "2012-11-08T04:05:37.866820",
 
1412
                        "bytes": 2,
 
1413
                        "name": "/d1/seg02",
 
1414
                        "content_type": "application/octet-stream"},
 
1415
                       {"hash": "e4c8f1de1c0855c7c2be33196d3c3537",
 
1416
                        "last_modified": "2012-11-08T04:05:37.846710",
 
1417
                        "bytes": 2,
 
1418
                        "name": "/d2/seg03",
 
1419
                        "content_type": "application/octet-stream"}]
 
1420
 
1397
1421
        response_bodies = (
1398
1422
            '',                           # HEAD /a
1399
1423
            '',                           # HEAD /a/c
1400
1424
            simplejson.dumps(listing),    # GET manifest
1401
1425
            'Aa',                         # GET seg01
1402
 
            'Bb')                         # GET seg02
 
1426
            simplejson.dumps(sub_listing),  # GET sub_manifest
 
1427
            'Bb',                         # GET seg02
 
1428
            'Cc',                         # GET seg03
 
1429
            'Dd')                         # GET seg04
1403
1430
        with save_globals():
1404
1431
            controller = proxy_server.ObjectController(
1405
1432
                self.app, 'a', 'c', 'manifest')
1419
1446
                200,    # HEAD /a/c
1420
1447
                200,    # GET listing1
1421
1448
                200,    # GET seg01
 
1449
                200,    # GET sub listing1
1422
1450
                200,    # GET seg02
1423
 
                headers=[{}, {}, slob_headers, {}, slob_headers],
 
1451
                200,    # GET seg03
 
1452
                200,    # GET seg04
 
1453
                headers=[{}, {}, slob_headers, {}, slob_headers, {}, {}, {}],
1424
1454
                body_iter=response_bodies,
1425
1455
                give_connect=capture_requested_paths)
1426
1456
            req = Request.blank('/a/c/manifest')
1427
1457
            resp = controller.GET(req)
1428
1458
            self.assertEqual(resp.status_int, 200)
1429
 
            self.assertEqual(resp.content_length, 4)  # content incomplete
 
1459
            self.assertEqual(resp.content_length, 8)
1430
1460
            self.assertEqual(resp.content_type, 'text/html')
1431
 
            self.assertRaises(SloSegmentError, lambda: resp.body)
1432
 
            # dropped connection, exception is caught by eventlet as it is
1433
 
            # iterating over response
1434
1461
 
1435
1462
            self.assertEqual(
1436
1463
                requested,
1437
1464
                [['HEAD', '/a', {}],
1438
1465
                 ['HEAD', '/a/c', {}],
 
1466
                 ['GET', '/a/c/manifest', {}]])
 
1467
            # iterating over body will retrieve manifest and sub manifest's
 
1468
            # objects
 
1469
            self.assertEqual(resp.body, 'AaBbCcDd')
 
1470
            self.assertEqual(
 
1471
                requested,
 
1472
                [['HEAD', '/a', {}],
 
1473
                 ['HEAD', '/a/c', {}],
1439
1474
                 ['GET', '/a/c/manifest', {}],
1440
1475
                 ['GET', '/a/d1/seg01', {}],
1441
 
                 ['GET', '/a/d2/seg02', {}]])
 
1476
                 ['GET', '/a/d2/sub_manifest', {}],
 
1477
                 ['GET', '/a/d1/seg02', {}],
 
1478
                 ['GET', '/a/d2/seg03', {}],
 
1479
                 ['GET', '/a/d1/seg04', {}]])
1442
1480
 
1443
1481
    def test_GET_bad_404_manifest_slo(self):
1444
1482
        listing = [{"hash": "98568d540134639be4655198a36614a4",
1490
1528
            self.assertEqual(resp.status_int, 200)
1491
1529
            self.assertEqual(resp.content_length, 6)  # content incomplete
1492
1530
            self.assertEqual(resp.content_type, 'text/html')
1493
 
            self.assertRaises(SloSegmentError, lambda: resp.body)
 
1531
            self.assertRaises(SegmentError, lambda: resp.body)
1494
1532
            # dropped connection, exception is caught by eventlet as it is
1495
1533
            # iterating over response
1496
1534
 
1591
1629
        try:
1592
1630
            with open(os.path.join(swift_dir, 'mime.types'), 'w') as fp:
1593
1631
                fp.write('foo/bar foo\n')
1594
 
            ba = proxy_server.Application({'swift_dir': swift_dir},
1595
 
                                          FakeMemcache(), FakeLogger(),
1596
 
                                          FakeRing(), FakeRing(),
1597
 
                                          FakeRing())
 
1632
            proxy_server.Application({'swift_dir': swift_dir},
 
1633
                                     FakeMemcache(), FakeLogger(),
 
1634
                                     FakeRing(), FakeRing(),
 
1635
                                     FakeRing())
1598
1636
            self.assertEquals(proxy_server.mimetypes.guess_type('blah.foo')[0],
1599
1637
                              'foo/bar')
1600
1638
            self.assertEquals(proxy_server.mimetypes.guess_type('blah.jpg')[0],
1998
2036
            res = controller.POST(req)
1999
2037
            self.assertEquals(res.status_int, 400)
2000
2038
 
 
2039
    def test_PUT_not_autodetect_content_type(self):
 
2040
        with save_globals():
 
2041
            controller = proxy_server.ObjectController(
 
2042
                self.app, 'a', 'c', 'o.html')
 
2043
 
 
2044
            headers = {'Content-Type': 'something/right', 'Content-Length': 0}
 
2045
            it_worked = []
 
2046
 
 
2047
            def verify_content_type(ipaddr, port, device, partition,
 
2048
                                    method, path, headers=None,
 
2049
                                    query_string=None):
 
2050
                if path == '/a/c/o.html':
 
2051
                    it_worked.append(
 
2052
                        headers['Content-Type'].startswith('something/right'))
 
2053
 
 
2054
            set_http_connect(204, 204, 201, 201, 201,
 
2055
                             give_connect=verify_content_type)
 
2056
            req = Request.blank('/a/c/o.html', {}, headers=headers)
 
2057
            self.app.update_request(req)
 
2058
            res = controller.PUT(req)
 
2059
            self.assertNotEquals(it_worked, [])
 
2060
            self.assertTrue(all(it_worked))
 
2061
 
 
2062
    def test_PUT_autodetect_content_type(self):
 
2063
        with save_globals():
 
2064
            controller = proxy_server.ObjectController(
 
2065
                self.app, 'a', 'c', 'o.html')
 
2066
 
 
2067
            headers = {'Content-Type': 'something/wrong', 'Content-Length': 0,
 
2068
                       'X-Detect-Content-Type': 'True'}
 
2069
            it_worked = []
 
2070
 
 
2071
            def verify_content_type(ipaddr, port, device, partition,
 
2072
                                    method, path, headers=None,
 
2073
                                    query_string=None):
 
2074
                if path == '/a/c/o.html':
 
2075
                    it_worked.append(
 
2076
                        headers['Content-Type'].startswith('text/html'))
 
2077
 
 
2078
            set_http_connect(204, 204, 201, 201, 201,
 
2079
                             give_connect=verify_content_type)
 
2080
            req = Request.blank('/a/c/o.html', {}, headers=headers)
 
2081
            self.app.update_request(req)
 
2082
            res = controller.PUT(req)
 
2083
            self.assertNotEquals(it_worked, [])
 
2084
            self.assertTrue(all(it_worked))
 
2085
 
2001
2086
    def test_client_timeout(self):
2002
2087
        with save_globals():
2003
2088
            self.app.account_ring.get_nodes('account')
4004
4089
            req = Request.blank('/a/c/o')
4005
4090
            req.environ['swift.authorize'] = authorize
4006
4091
            self.app.update_request(req)
4007
 
            res = controller.GET(req)
 
4092
            controller.GET(req)
4008
4093
        self.assert_(called[0])
4009
4094
 
4010
4095
    def test_HEAD_calls_authorize(self):
4020
4105
            req = Request.blank('/a/c/o', {'REQUEST_METHOD': 'HEAD'})
4021
4106
            req.environ['swift.authorize'] = authorize
4022
4107
            self.app.update_request(req)
4023
 
            res = controller.HEAD(req)
 
4108
            controller.HEAD(req)
4024
4109
        self.assert_(called[0])
4025
4110
 
4026
4111
    def test_POST_calls_authorize(self):
4038
4123
                                headers={'Content-Length': '5'}, body='12345')
4039
4124
            req.environ['swift.authorize'] = authorize
4040
4125
            self.app.update_request(req)
4041
 
            res = controller.POST(req)
 
4126
            controller.POST(req)
4042
4127
        self.assert_(called[0])
4043
4128
 
4044
4129
    def test_POST_as_copy_calls_authorize(self):
4055
4140
                                headers={'Content-Length': '5'}, body='12345')
4056
4141
            req.environ['swift.authorize'] = authorize
4057
4142
            self.app.update_request(req)
4058
 
            res = controller.POST(req)
 
4143
            controller.POST(req)
4059
4144
        self.assert_(called[0])
4060
4145
 
4061
4146
    def test_PUT_calls_authorize(self):
4072
4157
                                headers={'Content-Length': '5'}, body='12345')
4073
4158
            req.environ['swift.authorize'] = authorize
4074
4159
            self.app.update_request(req)
4075
 
            res = controller.PUT(req)
 
4160
            controller.PUT(req)
4076
4161
        self.assert_(called[0])
4077
4162
 
4078
4163
    def test_COPY_calls_authorize(self):
4089
4174
                                headers={'Destination': 'c/o'})
4090
4175
            req.environ['swift.authorize'] = authorize
4091
4176
            self.app.update_request(req)
4092
 
            res = controller.COPY(req)
 
4177
            controller.COPY(req)
4093
4178
        self.assert_(called[0])
4094
4179
 
4095
4180
    def test_POST_converts_delete_after_to_delete_at(self):
4489
4574
 
4490
4575
    def _gather_x_container_headers(self, controller_call, req, *connect_args,
4491
4576
                                    **kwargs):
4492
 
        header_list = kwargs.pop('header_list', ['X-Container-Partition',
 
4577
        header_list = kwargs.pop('header_list', ['X-Container-Device',
4493
4578
                                                 'X-Container-Host',
4494
 
                                                 'X-Container-Device'])
 
4579
                                                 'X-Container-Partition'])
4495
4580
        seen_headers = []
4496
4581
 
4497
4582
        def capture_headers(ipaddr, port, device, partition, method,
4512
4597
            # don't care about the account/container HEADs, so chuck
4513
4598
            # the first two requests
4514
4599
            return sorted(seen_headers[2:],
4515
 
                          key=lambda d: d.get(header_list[0]) or 'Z')
 
4600
                          key=lambda d: d.get(header_list[0]) or 'z')
4516
4601
 
4517
4602
    def test_PUT_x_container_headers_with_equal_replicas(self):
4518
4603
        req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
4607
4692
            200, 200, 200, 200, 200)   # HEAD HEAD DELETE DELETE DELETE
4608
4693
 
4609
4694
        self.assertEqual(seen_headers, [
4610
 
                {'X-Container-Host': '10.0.0.0:1000,10.0.0.3:1003',
4611
 
                 'X-Container-Partition': '1',
4612
 
                 'X-Container-Device': 'sda,sdd'},
4613
 
                {'X-Container-Host': '10.0.0.1:1001',
4614
 
                 'X-Container-Partition': '1',
4615
 
                 'X-Container-Device': 'sdb'},
4616
 
                {'X-Container-Host': '10.0.0.2:1002',
4617
 
                 'X-Container-Partition': '1',
4618
 
                 'X-Container-Device': 'sdc'}])
 
4695
            {'X-Container-Host': '10.0.0.0:1000,10.0.0.3:1003',
 
4696
             'X-Container-Partition': '1',
 
4697
             'X-Container-Device': 'sda,sdd'},
 
4698
            {'X-Container-Host': '10.0.0.1:1001',
 
4699
             'X-Container-Partition': '1',
 
4700
             'X-Container-Device': 'sdb'},
 
4701
            {'X-Container-Host': '10.0.0.2:1002',
 
4702
             'X-Container-Partition': '1',
 
4703
             'X-Container-Device': 'sdc'}
 
4704
        ])
4619
4705
 
4620
4706
    @mock.patch('time.time', new=lambda: STATIC_TIME)
4621
4707
    def test_PUT_x_delete_at_with_fewer_container_replicas(self):
4638
4724
                         'X-Delete-At-Partition', 'X-Delete-At-Container'))
4639
4725
 
4640
4726
        self.assertEqual(seen_headers, [
4641
 
                {'X-Delete-At-Host': '10.0.0.0:1000',
4642
 
                 'X-Delete-At-Container': delete_at_container,
4643
 
                 'X-Delete-At-Partition': '1',
4644
 
                 'X-Delete-At-Device': 'sda'},
4645
 
                {'X-Delete-At-Host': '10.0.0.1:1001',
4646
 
                 'X-Delete-At-Container': delete_at_container,
4647
 
                 'X-Delete-At-Partition': '1',
4648
 
                 'X-Delete-At-Device': 'sdb'},
4649
 
                {'X-Delete-At-Host': None,
4650
 
                 'X-Delete-At-Container': None,
4651
 
                 'X-Delete-At-Partition': None,
4652
 
                 'X-Delete-At-Device': None}])
 
4727
            {'X-Delete-At-Host': '10.0.0.0:1000',
 
4728
             'X-Delete-At-Container': delete_at_container,
 
4729
             'X-Delete-At-Partition': '1',
 
4730
             'X-Delete-At-Device': 'sda'},
 
4731
            {'X-Delete-At-Host': '10.0.0.1:1001',
 
4732
             'X-Delete-At-Container': delete_at_container,
 
4733
             'X-Delete-At-Partition': '1',
 
4734
             'X-Delete-At-Device': 'sdb'},
 
4735
            {'X-Delete-At-Host': None,
 
4736
             'X-Delete-At-Container': None,
 
4737
             'X-Delete-At-Partition': None,
 
4738
             'X-Delete-At-Device': None}
 
4739
        ])
4653
4740
 
4654
4741
    @mock.patch('time.time', new=lambda: STATIC_TIME)
4655
4742
    def test_PUT_x_delete_at_with_more_container_replicas(self):
4673
4760
            header_list=('X-Delete-At-Host', 'X-Delete-At-Device',
4674
4761
                         'X-Delete-At-Partition', 'X-Delete-At-Container'))
4675
4762
        self.assertEqual(seen_headers, [
4676
 
                {'X-Delete-At-Host': '10.0.0.0:1000,10.0.0.3:1003',
4677
 
                 'X-Delete-At-Container': delete_at_container,
4678
 
                 'X-Delete-At-Partition': '1',
4679
 
                 'X-Delete-At-Device': 'sda,sdd'},
4680
 
                {'X-Delete-At-Host': '10.0.0.1:1001',
4681
 
                 'X-Delete-At-Container': delete_at_container,
4682
 
                 'X-Delete-At-Partition': '1',
4683
 
                 'X-Delete-At-Device': 'sdb'},
4684
 
                {'X-Delete-At-Host': '10.0.0.2:1002',
4685
 
                 'X-Delete-At-Container': delete_at_container,
4686
 
                 'X-Delete-At-Partition': '1',
4687
 
                 'X-Delete-At-Device': 'sdc'}])
 
4763
            {'X-Delete-At-Host': '10.0.0.0:1000,10.0.0.3:1003',
 
4764
             'X-Delete-At-Container': delete_at_container,
 
4765
             'X-Delete-At-Partition': '1',
 
4766
             'X-Delete-At-Device': 'sda,sdd'},
 
4767
            {'X-Delete-At-Host': '10.0.0.1:1001',
 
4768
             'X-Delete-At-Container': delete_at_container,
 
4769
             'X-Delete-At-Partition': '1',
 
4770
             'X-Delete-At-Device': 'sdb'},
 
4771
            {'X-Delete-At-Host': '10.0.0.2:1002',
 
4772
             'X-Delete-At-Container': delete_at_container,
 
4773
             'X-Delete-At-Partition': '1',
 
4774
             'X-Delete-At-Device': 'sdc'}
 
4775
        ])
4688
4776
 
4689
4777
 
4690
4778
class TestContainerController(unittest.TestCase):
4990
5078
                if self.allow_lock:
4991
5079
                    yield True
4992
5080
                else:
4993
 
                    raise MemcacheLockError()
 
5081
                    raise NotImplementedError
4994
5082
 
4995
5083
        with save_globals():
4996
5084
            controller = proxy_server.ContainerController(self.app, 'account',
5113
5201
                req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
5114
5202
                                    headers={test_header: test_value})
5115
5203
                self.app.update_request(req)
5116
 
                res = getattr(controller, method)(req)
 
5204
                getattr(controller, method)(req)
5117
5205
                self.assertEquals(test_errors, [])
5118
5206
 
5119
5207
    def test_PUT_bad_metadata(self):
5220
5308
                                headers={'X-Container-Read': '.r:*'})
5221
5309
            req.environ['swift.clean_acl'] = clean_acl
5222
5310
            self.app.update_request(req)
5223
 
            res = controller.POST(req)
 
5311
            controller.POST(req)
5224
5312
        self.assert_(called[0])
5225
5313
        called[0] = False
5226
5314
        with save_globals():
5231
5319
                                headers={'X-Container-Write': '.r:*'})
5232
5320
            req.environ['swift.clean_acl'] = clean_acl
5233
5321
            self.app.update_request(req)
5234
 
            res = controller.POST(req)
 
5322
            controller.POST(req)
5235
5323
        self.assert_(called[0])
5236
5324
 
5237
5325
    def test_PUT_calls_clean_acl(self):
5248
5336
                                headers={'X-Container-Read': '.r:*'})
5249
5337
            req.environ['swift.clean_acl'] = clean_acl
5250
5338
            self.app.update_request(req)
5251
 
            res = controller.PUT(req)
 
5339
            controller.PUT(req)
5252
5340
        self.assert_(called[0])
5253
5341
        called[0] = False
5254
5342
        with save_globals():
5259
5347
                                headers={'X-Container-Write': '.r:*'})
5260
5348
            req.environ['swift.clean_acl'] = clean_acl
5261
5349
            self.app.update_request(req)
5262
 
            res = controller.PUT(req)
 
5350
            controller.PUT(req)
5263
5351
        self.assert_(called[0])
5264
5352
 
5265
5353
    def test_GET_no_content(self):
5304
5392
            req = Request.blank('/a/c', {'REQUEST_METHOD': 'HEAD'})
5305
5393
            req.environ['swift.authorize'] = authorize
5306
5394
            self.app.update_request(req)
5307
 
            res = controller.HEAD(req)
 
5395
            controller.HEAD(req)
5308
5396
        self.assert_(called[0])
5309
5397
 
5310
5398
    def test_OPTIONS(self):
5508
5596
            controller.PUT, req,
5509
5597
            200, 201, 201, 201)    # HEAD PUT PUT PUT
5510
5598
        self.assertEqual(seen_headers, [
5511
 
                {'X-Account-Host': '10.0.0.0:1000',
5512
 
                 'X-Account-Partition': '1',
5513
 
                 'X-Account-Device': 'sda'},
5514
 
                {'X-Account-Host': '10.0.0.1:1001',
5515
 
                 'X-Account-Partition': '1',
5516
 
                 'X-Account-Device': 'sdb'},
5517
 
                {'X-Account-Host': None,
5518
 
                 'X-Account-Partition': None,
5519
 
                 'X-Account-Device': None}])
 
5599
            {'X-Account-Host': '10.0.0.0:1000',
 
5600
             'X-Account-Partition': '1',
 
5601
             'X-Account-Device': 'sda'},
 
5602
            {'X-Account-Host': '10.0.0.1:1001',
 
5603
             'X-Account-Partition': '1',
 
5604
             'X-Account-Device': 'sdb'},
 
5605
            {'X-Account-Host': None,
 
5606
             'X-Account-Partition': None,
 
5607
             'X-Account-Device': None}
 
5608
        ])
5520
5609
 
5521
5610
    def test_PUT_x_account_headers_with_more_account_replicas(self):
5522
5611
        self.app.account_ring.set_replicas(4)
5527
5616
            controller.PUT, req,
5528
5617
            200, 201, 201, 201)    # HEAD PUT PUT PUT
5529
5618
        self.assertEqual(seen_headers, [
5530
 
                {'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
5531
 
                 'X-Account-Partition': '1',
5532
 
                 'X-Account-Device': 'sda,sdd'},
5533
 
                {'X-Account-Host': '10.0.0.1:1001',
5534
 
                 'X-Account-Partition': '1',
5535
 
                 'X-Account-Device': 'sdb'},
5536
 
                {'X-Account-Host': '10.0.0.2:1002',
5537
 
                 'X-Account-Partition': '1',
5538
 
                 'X-Account-Device': 'sdc'}])
 
5619
            {'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
 
5620
             'X-Account-Partition': '1',
 
5621
             'X-Account-Device': 'sda,sdd'},
 
5622
            {'X-Account-Host': '10.0.0.1:1001',
 
5623
             'X-Account-Partition': '1',
 
5624
             'X-Account-Device': 'sdb'},
 
5625
            {'X-Account-Host': '10.0.0.2:1002',
 
5626
             'X-Account-Partition': '1',
 
5627
             'X-Account-Device': 'sdc'}
 
5628
        ])
5539
5629
 
5540
5630
    def test_DELETE_x_account_headers_with_fewer_account_replicas(self):
5541
5631
        self.app.account_ring.set_replicas(2)
5546
5636
            controller.DELETE, req,
5547
5637
            200, 204, 204, 204)    # HEAD DELETE DELETE DELETE
5548
5638
        self.assertEqual(seen_headers, [
5549
 
                {'X-Account-Host': '10.0.0.0:1000',
5550
 
                 'X-Account-Partition': '1',
5551
 
                 'X-Account-Device': 'sda'},
5552
 
                {'X-Account-Host': '10.0.0.1:1001',
5553
 
                 'X-Account-Partition': '1',
5554
 
                 'X-Account-Device': 'sdb'},
5555
 
                {'X-Account-Host': None,
5556
 
                 'X-Account-Partition': None,
5557
 
                 'X-Account-Device': None}])
 
5639
            {'X-Account-Host': '10.0.0.0:1000',
 
5640
             'X-Account-Partition': '1',
 
5641
             'X-Account-Device': 'sda'},
 
5642
            {'X-Account-Host': '10.0.0.1:1001',
 
5643
             'X-Account-Partition': '1',
 
5644
             'X-Account-Device': 'sdb'},
 
5645
            {'X-Account-Host': None,
 
5646
             'X-Account-Partition': None,
 
5647
             'X-Account-Device': None}
 
5648
        ])
5558
5649
 
5559
5650
    def test_DELETE_x_account_headers_with_more_account_replicas(self):
5560
5651
        self.app.account_ring.set_replicas(4)
5565
5656
            controller.DELETE, req,
5566
5657
            200, 204, 204, 204)    # HEAD DELETE DELETE DELETE
5567
5658
        self.assertEqual(seen_headers, [
5568
 
                {'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
5569
 
                 'X-Account-Partition': '1',
5570
 
                 'X-Account-Device': 'sda,sdd'},
5571
 
                {'X-Account-Host': '10.0.0.1:1001',
5572
 
                 'X-Account-Partition': '1',
5573
 
                 'X-Account-Device': 'sdb'},
5574
 
                {'X-Account-Host': '10.0.0.2:1002',
5575
 
                 'X-Account-Partition': '1',
5576
 
                 'X-Account-Device': 'sdc'}])
 
5659
            {'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
 
5660
             'X-Account-Partition': '1',
 
5661
             'X-Account-Device': 'sda,sdd'},
 
5662
            {'X-Account-Host': '10.0.0.1:1001',
 
5663
             'X-Account-Partition': '1',
 
5664
             'X-Account-Device': 'sdb'},
 
5665
            {'X-Account-Host': '10.0.0.2:1002',
 
5666
             'X-Account-Partition': '1',
 
5667
             'X-Account-Device': 'sdc'}
 
5668
        ])
5577
5669
 
5578
5670
 
5579
5671
class TestAccountController(unittest.TestCase):
5665
5757
            self.app.memcache = FakeMemcacheReturnsNone()
5666
5758
            self.assert_status_map(controller.GET, (404, 404, 404), 404, 404)
5667
5759
 
5668
 
 
5669
5760
    def test_GET_autocreate(self):
5670
5761
        with save_globals():
5671
5762
            controller = proxy_server.AccountController(self.app, 'account')
5862
5953
                req = Request.blank('/a/c', environ={'REQUEST_METHOD': method},
5863
5954
                                    headers={test_header: test_value})
5864
5955
                self.app.update_request(req)
5865
 
                res = getattr(controller, method)(req)
 
5956
                getattr(controller, method)(req)
5866
5957
                self.assertEquals(test_errors, [])
5867
5958
 
5868
5959
    def test_PUT_bad_metadata(self):