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

« back to all changes in this revision

Viewing changes to test/unit/obj/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:
14
14
# See the License for the specific language governing permissions and
15
15
# limitations under the License.
16
16
 
17
 
""" Tests for swift.object_server """
 
17
""" Tests for swift.obj.server """
18
18
 
19
19
import cPickle as pickle
20
20
import operator
21
21
import os
22
22
import mock
23
23
import unittest
24
 
import email
25
24
from shutil import rmtree
26
25
from StringIO import StringIO
27
26
from time import gmtime, strftime, time
30
29
 
31
30
from eventlet import sleep, spawn, wsgi, listen, Timeout
32
31
from test.unit import FakeLogger
33
 
from test.unit import _setxattr as setxattr
34
32
from test.unit import connect_tcp, readuntil2crlfs
35
33
from swift.obj import server as object_server
 
34
from swift.obj import diskfile
36
35
from swift.common import utils
37
36
from swift.common.utils import hash_path, mkdirs, normalize_timestamp, \
38
 
                               NullLogger, storage_directory
39
 
from swift.common.exceptions import DiskFileNotExist
 
37
                               NullLogger, storage_directory, public, \
 
38
                               replication
40
39
from swift.common import constraints
41
40
from eventlet import tpool
42
41
from swift.common.swob import Request, HeaderKeyDict
43
42
 
44
43
 
45
 
class TestDiskFile(unittest.TestCase):
46
 
    """Test swift.obj.server.DiskFile"""
47
 
 
48
 
    def setUp(self):
49
 
        """ Set up for testing swift.object_server.ObjectController """
50
 
        self.testdir = os.path.join(mkdtemp(), 'tmp_test_obj_server_DiskFile')
51
 
        mkdirs(os.path.join(self.testdir, 'sda1', 'tmp'))
52
 
 
53
 
        self._real_tpool_execute = tpool.execute
54
 
        def fake_exe(meth, *args, **kwargs):
55
 
            return meth(*args, **kwargs)
56
 
        tpool.execute = fake_exe
57
 
 
58
 
    def tearDown(self):
59
 
        """ Tear down for testing swift.object_server.ObjectController """
60
 
        rmtree(os.path.dirname(self.testdir))
61
 
        tpool.execute = self._real_tpool_execute
62
 
 
63
 
    def _create_test_file(self, data, keep_data_fp=True):
64
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
65
 
                                    FakeLogger())
66
 
        mkdirs(df.datadir)
67
 
        f = open(os.path.join(df.datadir,
68
 
                              normalize_timestamp(time()) + '.data'), 'wb')
69
 
        f.write(data)
70
 
        setxattr(f.fileno(), object_server.METADATA_KEY,
71
 
                 pickle.dumps({}, object_server.PICKLE_PROTOCOL))
72
 
        f.close()
73
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
74
 
                                    FakeLogger(), keep_data_fp=keep_data_fp)
75
 
        return df
76
 
 
77
 
    def test_disk_file_app_iter_corners(self):
78
 
        df = self._create_test_file('1234567890')
79
 
        self.assertEquals(''.join(df.app_iter_range(0, None)), '1234567890')
80
 
 
81
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
82
 
                                    FakeLogger(), keep_data_fp=True)
83
 
        self.assertEqual(''.join(df.app_iter_range(5, None)), '67890')
84
 
 
85
 
    def test_disk_file_app_iter_ranges(self):
86
 
        df = self._create_test_file('012345678911234567892123456789')
87
 
        it = df.app_iter_ranges([(0, 10), (10, 20), (20, 30)], 'plain/text',
88
 
                                '\r\n--someheader\r\n', 30)
89
 
        value = ''.join(it)
90
 
        self.assert_('0123456789' in value)
91
 
        self.assert_('1123456789' in value)
92
 
        self.assert_('2123456789' in value)
93
 
 
94
 
    def test_disk_file_app_iter_ranges_edges(self):
95
 
        df = self._create_test_file('012345678911234567892123456789')
96
 
        it = df.app_iter_ranges([(3, 10), (0, 2)], 'application/whatever',
97
 
                                '\r\n--someheader\r\n', 30)
98
 
        value = ''.join(it)
99
 
        self.assert_('3456789' in value)
100
 
        self.assert_('01' in value)
101
 
 
102
 
    def test_disk_file_large_app_iter_ranges(self):
103
 
        """
104
 
        This test case is to make sure that the disk file app_iter_ranges
105
 
        method all the paths being tested.
106
 
        """
107
 
        long_str = '01234567890' * 65536
108
 
        target_strs = ['3456789', long_str[0:65590]]
109
 
        df = self._create_test_file(long_str)
110
 
 
111
 
        it = df.app_iter_ranges([(3, 10), (0, 65590)], 'plain/text',
112
 
                                '5e816ff8b8b8e9a5d355497e5d9e0301', 655360)
113
 
 
114
 
        """
115
 
        the produced string actually missing the MIME headers
116
 
        need to add these headers to make it as real MIME message.
117
 
        The body of the message is produced by method app_iter_ranges
118
 
        off of DiskFile object.
119
 
        """
120
 
        header = ''.join(['Content-Type: multipart/byteranges;',
121
 
                          'boundary=',
122
 
                          '5e816ff8b8b8e9a5d355497e5d9e0301\r\n'])
123
 
 
124
 
        value = header + ''.join(it)
125
 
 
126
 
        parts = map(lambda p: p.get_payload(decode=True),
127
 
                    email.message_from_string(value).walk())[1:3]
128
 
        self.assertEqual(parts, target_strs)
129
 
 
130
 
    def test_disk_file_app_iter_ranges_empty(self):
131
 
        """
132
 
        This test case tests when empty value passed into app_iter_ranges
133
 
        When ranges passed into the method is either empty array or None,
134
 
        this method will yield empty string
135
 
        """
136
 
        df = self._create_test_file('012345678911234567892123456789')
137
 
        it = df.app_iter_ranges([], 'application/whatever',
138
 
                                '\r\n--someheader\r\n', 100)
139
 
        self.assertEqual(''.join(it), '')
140
 
 
141
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
142
 
                                    FakeLogger(), keep_data_fp=True)
143
 
        it = df.app_iter_ranges(None, 'app/something',
144
 
                                '\r\n--someheader\r\n', 150)
145
 
        self.assertEqual(''.join(it), '')
146
 
 
147
 
    def test_disk_file_mkstemp_creates_dir(self):
148
 
        tmpdir = os.path.join(self.testdir, 'sda1', 'tmp')
149
 
        os.rmdir(tmpdir)
150
 
        with object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c',
151
 
                                    'o', FakeLogger()).writer() as writer:
152
 
            self.assert_(os.path.exists(tmpdir))
153
 
 
154
 
    def test_iter_hook(self):
155
 
        hook_call_count = [0]
156
 
        def hook():
157
 
            hook_call_count[0] += 1
158
 
 
159
 
        df = self._get_disk_file(fsize=65, csize=8, iter_hook=hook)
160
 
        for _ in df:
161
 
            pass
162
 
 
163
 
        self.assertEquals(hook_call_count[0], 9)
164
 
 
165
 
    def test_quarantine(self):
166
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
167
 
                                    FakeLogger())
168
 
        mkdirs(df.datadir)
169
 
        f = open(os.path.join(df.datadir,
170
 
                              normalize_timestamp(time()) + '.data'), 'wb')
171
 
        setxattr(f.fileno(), object_server.METADATA_KEY,
172
 
                 pickle.dumps({}, object_server.PICKLE_PROTOCOL))
173
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
174
 
                                    FakeLogger())
175
 
        df.quarantine()
176
 
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined',
177
 
                                'objects', os.path.basename(os.path.dirname(
178
 
                                                            df.data_file)))
179
 
        self.assert_(os.path.isdir(quar_dir))
180
 
 
181
 
    def test_quarantine_same_file(self):
182
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
183
 
                                    FakeLogger())
184
 
        mkdirs(df.datadir)
185
 
        f = open(os.path.join(df.datadir,
186
 
                              normalize_timestamp(time()) + '.data'), 'wb')
187
 
        setxattr(f.fileno(), object_server.METADATA_KEY,
188
 
                 pickle.dumps({}, object_server.PICKLE_PROTOCOL))
189
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
190
 
                                    FakeLogger())
191
 
        new_dir = df.quarantine()
192
 
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined',
193
 
                                'objects', os.path.basename(os.path.dirname(
194
 
                                                            df.data_file)))
195
 
        self.assert_(os.path.isdir(quar_dir))
196
 
        self.assertEquals(quar_dir, new_dir)
197
 
        # have to remake the datadir and file
198
 
        mkdirs(df.datadir)
199
 
        f = open(os.path.join(df.datadir,
200
 
                              normalize_timestamp(time()) + '.data'), 'wb')
201
 
        setxattr(f.fileno(), object_server.METADATA_KEY,
202
 
                 pickle.dumps({}, object_server.PICKLE_PROTOCOL))
203
 
 
204
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c', 'o',
205
 
                                    FakeLogger(), keep_data_fp=True)
206
 
        double_uuid_path = df.quarantine()
207
 
        self.assert_(os.path.isdir(double_uuid_path))
208
 
        self.assert_('-' in os.path.basename(double_uuid_path))
209
 
 
210
 
    def _get_disk_file(self, invalid_type=None, obj_name='o',
211
 
                       fsize=1024, csize=8, extension='.data', ts=None,
212
 
                       iter_hook=None):
213
 
        '''returns a DiskFile'''
214
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c',
215
 
                                    obj_name, FakeLogger())
216
 
        data = '0' * fsize
217
 
        etag = md5()
218
 
        if ts:
219
 
            timestamp = ts
220
 
        else:
221
 
            timestamp = str(normalize_timestamp(time()))
222
 
        with df.writer() as writer:
223
 
            writer.write(data)
224
 
            etag.update(data)
225
 
            etag = etag.hexdigest()
226
 
            metadata = {
227
 
                'ETag': etag,
228
 
                'X-Timestamp': timestamp,
229
 
                'Content-Length': str(os.fstat(writer.fd).st_size),
230
 
            }
231
 
            writer.put(metadata, extension=extension)
232
 
            if invalid_type == 'ETag':
233
 
                etag = md5()
234
 
                etag.update('1' + '0' * (fsize - 1))
235
 
                etag = etag.hexdigest()
236
 
                metadata['ETag'] = etag
237
 
                object_server.write_metadata(writer.fd, metadata)
238
 
            if invalid_type == 'Content-Length':
239
 
                metadata['Content-Length'] = fsize - 1
240
 
                object_server.write_metadata(writer.fd, metadata)
241
 
 
242
 
        df = object_server.DiskFile(self.testdir, 'sda1', '0', 'a', 'c',
243
 
                                    obj_name, FakeLogger(),
244
 
                                    keep_data_fp=True, disk_chunk_size=csize,
245
 
                                    iter_hook=iter_hook)
246
 
        if invalid_type == 'Zero-Byte':
247
 
            os.remove(df.data_file)
248
 
            fp = open(df.data_file, 'w')
249
 
            fp.close()
250
 
        df.unit_test_len = fsize
251
 
        return df
252
 
 
253
 
    def test_quarantine_valids(self):
254
 
        df = self._get_disk_file(obj_name='1')
255
 
        for chunk in df:
256
 
            pass
257
 
        self.assertFalse(df.quarantined_dir)
258
 
 
259
 
        df = self._get_disk_file(obj_name='2', csize=1)
260
 
        for chunk in df:
261
 
            pass
262
 
        self.assertFalse(df.quarantined_dir)
263
 
 
264
 
        df = self._get_disk_file(obj_name='3', csize=100000)
265
 
        for chunk in df:
266
 
            pass
267
 
        self.assertFalse(df.quarantined_dir)
268
 
 
269
 
    def run_quarantine_invalids(self, invalid_type):
270
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='1')
271
 
        for chunk in df:
272
 
            pass
273
 
        self.assertTrue(df.quarantined_dir)
274
 
        df = self._get_disk_file(invalid_type=invalid_type,
275
 
                                 obj_name='2', csize=1)
276
 
        for chunk in df:
277
 
            pass
278
 
        self.assertTrue(df.quarantined_dir)
279
 
        df = self._get_disk_file(invalid_type=invalid_type,
280
 
                                 obj_name='3', csize=100000)
281
 
        for chunk in df:
282
 
            pass
283
 
        self.assertTrue(df.quarantined_dir)
284
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='4')
285
 
        self.assertFalse(df.quarantined_dir)
286
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='5')
287
 
        for chunk in df.app_iter_range(0, df.unit_test_len):
288
 
            pass
289
 
        self.assertTrue(df.quarantined_dir)
290
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='6')
291
 
        for chunk in df.app_iter_range(0, df.unit_test_len + 100):
292
 
            pass
293
 
        self.assertTrue(df.quarantined_dir)
294
 
        expected_quar = False
295
 
        # for the following, Content-Length/Zero-Byte errors will always result
296
 
        # in a quarantine, even if the whole file isn't check-summed
297
 
        if invalid_type in ('Zero-Byte', 'Content-Length'):
298
 
            expected_quar = True
299
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='7')
300
 
        for chunk in df.app_iter_range(1, df.unit_test_len):
301
 
            pass
302
 
        self.assertEquals(bool(df.quarantined_dir), expected_quar)
303
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='8')
304
 
        for chunk in df.app_iter_range(0, df.unit_test_len - 1):
305
 
            pass
306
 
        self.assertEquals(bool(df.quarantined_dir), expected_quar)
307
 
        df = self._get_disk_file(invalid_type=invalid_type, obj_name='8')
308
 
        for chunk in df.app_iter_range(1, df.unit_test_len + 1):
309
 
            pass
310
 
        self.assertEquals(bool(df.quarantined_dir), expected_quar)
311
 
 
312
 
    def test_quarantine_invalids(self):
313
 
        self.run_quarantine_invalids('ETag')
314
 
        self.run_quarantine_invalids('Content-Length')
315
 
        self.run_quarantine_invalids('Zero-Byte')
316
 
 
317
 
    def test_quarantine_deleted_files(self):
318
 
        df = self._get_disk_file(invalid_type='Content-Length',
319
 
                                 extension='.data')
320
 
        df.close()
321
 
        self.assertTrue(df.quarantined_dir)
322
 
        df = self._get_disk_file(invalid_type='Content-Length',
323
 
                                 extension='.ts')
324
 
        df.close()
325
 
        self.assertFalse(df.quarantined_dir)
326
 
        df = self._get_disk_file(invalid_type='Content-Length',
327
 
                                 extension='.ts')
328
 
        self.assertRaises(DiskFileNotExist, df.get_data_file_size)
329
 
 
330
 
    def test_put_metadata(self):
331
 
        df = self._get_disk_file()
332
 
        ts = time()
333
 
        metadata = { 'X-Timestamp': ts, 'X-Object-Meta-test': 'data' }
334
 
        df.put_metadata(metadata)
335
 
        exp_name = '%s.meta' % str(normalize_timestamp(ts))
336
 
        dl = os.listdir(df.datadir)
337
 
        self.assertEquals(len(dl), 2)
338
 
        self.assertTrue(exp_name in set(dl))
339
 
 
340
 
    def test_put_metadata_ts(self):
341
 
        df = self._get_disk_file()
342
 
        ts = time()
343
 
        metadata = { 'X-Timestamp': ts, 'X-Object-Meta-test': 'data' }
344
 
        df.put_metadata(metadata, tombstone=True)
345
 
        exp_name = '%s.ts' % str(normalize_timestamp(ts))
346
 
        dl = os.listdir(df.datadir)
347
 
        self.assertEquals(len(dl), 2)
348
 
        self.assertTrue(exp_name in set(dl))
349
 
 
350
 
    def test_unlinkold(self):
351
 
        df1 = self._get_disk_file()
352
 
        future_time = str(normalize_timestamp(time() + 100))
353
 
        df2 = self._get_disk_file(ts=future_time)
354
 
        self.assertEquals(len(os.listdir(df1.datadir)), 2)
355
 
        df1.unlinkold(future_time)
356
 
        self.assertEquals(len(os.listdir(df1.datadir)), 1)
357
 
        self.assertEquals(os.listdir(df1.datadir)[0], "%s.data" % future_time)
358
 
 
359
 
    def test_close_error(self):
360
 
 
361
 
        def err():
362
 
            raise Exception("bad")
363
 
 
364
 
        df = self._get_disk_file(fsize=1024 * 1024 * 2)
365
 
        df._handle_close_quarantine = err
366
 
        for chunk in df:
367
 
            pass
368
 
        # close is called at the end of the iterator
369
 
        self.assertEquals(df.fp, None)
370
 
        self.assertEquals(len(df.logger.log_dict['error']), 1)
371
 
 
372
 
    def test_quarantine_twice(self):
373
 
        df = self._get_disk_file(invalid_type='Content-Length',
374
 
                                 extension='.data')
375
 
        self.assert_(os.path.isfile(df.data_file))
376
 
        quar_dir = df.quarantine()
377
 
        self.assertFalse(os.path.isfile(df.data_file))
378
 
        self.assert_(os.path.isdir(quar_dir))
379
 
        self.assertEquals(df.quarantine(), None)
380
 
 
381
 
 
382
44
class TestObjectController(unittest.TestCase):
383
45
    """ Test swift.obj.server.ObjectController """
384
46
 
392
54
        conf = {'devices': self.testdir, 'mount_check': 'false'}
393
55
        self.object_controller = object_server.ObjectController(conf)
394
56
        self.object_controller.bytes_per_sync = 1
 
57
        self._orig_tpool_exc = tpool.execute
 
58
        tpool.execute = lambda f, *args, **kwargs: f(*args, **kwargs)
395
59
 
396
60
    def tearDown(self):
397
61
        """ Tear down for testing swift.object_server.ObjectController """
398
62
        rmtree(os.path.dirname(self.testdir))
 
63
        tpool.execute = self._orig_tpool_exc 
399
64
 
400
65
    def test_REQUEST_SPECIAL_CHARS(self):
401
66
        obj = 'special昆%20/%'
558
223
                     "X-Object-Meta-3" in resp.headers)
559
224
        self.assertEquals(resp.headers['Content-Type'], 'application/x-test')
560
225
 
 
226
    def test_POST_old_timestamp(self):
 
227
        ts = time()
 
228
        timestamp = normalize_timestamp(ts)
 
229
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
230
                            headers={'X-Timestamp': timestamp,
 
231
                                     'Content-Type': 'application/x-test',
 
232
                                     'X-Object-Meta-1': 'One',
 
233
                                     'X-Object-Meta-Two': 'Two'})
 
234
        req.body = 'VERIFY'
 
235
        resp = self.object_controller.PUT(req)
 
236
        self.assertEquals(resp.status_int, 201)
 
237
 
 
238
        # Same timestamp should result in 409
 
239
        req = Request.blank('/sda1/p/a/c/o',
 
240
                            environ={'REQUEST_METHOD': 'POST'},
 
241
                            headers={'X-Timestamp': timestamp,
 
242
                                     'X-Object-Meta-3': 'Three',
 
243
                                     'X-Object-Meta-4': 'Four',
 
244
                                     'Content-Encoding': 'gzip',
 
245
                                     'Content-Type': 'application/x-test'})
 
246
        resp = self.object_controller.POST(req)
 
247
        self.assertEquals(resp.status_int, 409)
 
248
 
 
249
        # Earlier timestamp should result in 409
 
250
        timestamp = normalize_timestamp(ts - 1)
 
251
        req = Request.blank('/sda1/p/a/c/o',
 
252
                            environ={'REQUEST_METHOD': 'POST'},
 
253
                            headers={'X-Timestamp': timestamp,
 
254
                                     'X-Object-Meta-5': 'Five',
 
255
                                     'X-Object-Meta-6': 'Six',
 
256
                                     'Content-Encoding': 'gzip',
 
257
                                     'Content-Type': 'application/x-test'})
 
258
        resp = self.object_controller.POST(req)
 
259
        self.assertEquals(resp.status_int, 409)
 
260
 
561
261
    def test_POST_not_exist(self):
562
262
        timestamp = normalize_timestamp(time())
563
263
        req = Request.blank('/sda1/p/a/c/fail',
566
266
                                     'X-Object-Meta-1': 'One',
567
267
                                     'X-Object-Meta-2': 'Two',
568
268
                                     'Content-Type': 'text/plain'})
569
 
        resp = self.object_controller.POST(req)
 
269
        resp = req.get_response(self.object_controller)
570
270
        self.assertEquals(resp.status_int, 404)
571
271
 
572
272
    def test_POST_invalid_path(self):
576
276
                                     'X-Object-Meta-1': 'One',
577
277
                                     'X-Object-Meta-2': 'Two',
578
278
                                     'Content-Type': 'text/plain'})
579
 
        resp = self.object_controller.POST(req)
 
279
        resp = req.get_response(self.object_controller)
580
280
        self.assertEquals(resp.status_int, 400)
581
281
 
582
282
    def test_POST_container_connection(self):
604
304
 
605
305
        old_http_connect = object_server.http_connect
606
306
        try:
607
 
            timestamp = normalize_timestamp(time())
 
307
            ts = time()
 
308
            timestamp = normalize_timestamp(ts)
608
309
            req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD':
609
310
                'POST'}, headers={'X-Timestamp': timestamp, 'Content-Type':
610
311
                'text/plain', 'Content-Length': '0'})
611
312
            resp = self.object_controller.PUT(req)
 
313
            self.assertEquals(resp.status_int, 201)
612
314
            req = Request.blank('/sda1/p/a/c/o',
613
315
                    environ={'REQUEST_METHOD': 'POST'},
614
 
                    headers={'X-Timestamp': timestamp,
 
316
                    headers={'X-Timestamp': normalize_timestamp(ts + 1),
615
317
                             'X-Container-Host': '1.2.3.4:0',
616
318
                             'X-Container-Partition': '3',
617
319
                             'X-Container-Device': 'sda1',
622
324
            self.assertEquals(resp.status_int, 202)
623
325
            req = Request.blank('/sda1/p/a/c/o',
624
326
                    environ={'REQUEST_METHOD': 'POST'},
625
 
                    headers={'X-Timestamp': timestamp,
 
327
                    headers={'X-Timestamp': normalize_timestamp(ts + 2),
626
328
                             'X-Container-Host': '1.2.3.4:0',
627
329
                             'X-Container-Partition': '3',
628
330
                             'X-Container-Device': 'sda1',
633
335
            self.assertEquals(resp.status_int, 202)
634
336
            req = Request.blank('/sda1/p/a/c/o',
635
337
                    environ={'REQUEST_METHOD': 'POST'},
636
 
                    headers={'X-Timestamp': timestamp,
 
338
                    headers={'X-Timestamp': normalize_timestamp(ts + 3),
637
339
                             'X-Container-Host': '1.2.3.4:0',
638
340
                             'X-Container-Partition': '3',
639
341
                             'X-Container-Device': 'sda1',
654
356
        req.body = 'VERIFY'
655
357
        resp = self.object_controller.PUT(req)
656
358
        self.assertEquals(resp.status_int, 201)
657
 
        file = object_server.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
 
359
        file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
658
360
                                      FakeLogger(), keep_data_fp=True)
659
361
 
660
362
        file_name = os.path.basename(file.data_file)
661
363
        with open(file.data_file) as fp:
662
 
            metadata = object_server.read_metadata(fp)
 
364
            metadata = diskfile.read_metadata(fp)
663
365
        os.unlink(file.data_file)
664
366
        with open(file.data_file, 'w') as fp:
665
 
            object_server.write_metadata(fp, metadata)
 
367
            diskfile.write_metadata(fp, metadata)
666
368
 
667
369
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
668
370
        req = Request.blank('/sda1/p/a/c/o',
676
378
 
677
379
    def test_PUT_invalid_path(self):
678
380
        req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'})
679
 
        resp = self.object_controller.PUT(req)
 
381
        resp = req.get_response(self.object_controller)
680
382
        self.assertEquals(resp.status_int, 400)
681
383
 
682
384
    def test_PUT_no_timestamp(self):
683
385
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT',
684
386
                                                      'CONTENT_LENGTH': '0'})
685
 
        resp = self.object_controller.PUT(req)
 
387
        resp = req.get_response(self.object_controller)
686
388
        self.assertEquals(resp.status_int, 400)
687
389
 
688
390
    def test_PUT_no_content_type(self):
690
392
                headers={'X-Timestamp': normalize_timestamp(time()),
691
393
                         'Content-Length': '6'})
692
394
        req.body = 'VERIFY'
693
 
        resp = self.object_controller.PUT(req)
 
395
        resp = req.get_response(self.object_controller)
694
396
        self.assertEquals(resp.status_int, 400)
695
397
 
696
398
    def test_PUT_invalid_content_type(self):
699
401
                         'Content-Length': '6',
700
402
                         'Content-Type': '\xff\xff'})
701
403
        req.body = 'VERIFY'
702
 
        resp = self.object_controller.PUT(req)
 
404
        resp = req.get_response(self.object_controller)
703
405
        self.assertEquals(resp.status_int, 400)
704
406
        self.assert_('Content-Type' in resp.body)
705
407
 
709
411
                         'Content-Type': 'application/octet-stream'})
710
412
        req.body = 'VERIFY'
711
413
        del req.headers['Content-Length']
712
 
        resp = self.object_controller.PUT(req)
 
414
        resp = req.get_response(self.object_controller)
713
415
        self.assertEquals(resp.status_int, 411)
714
416
 
715
417
    def test_PUT_zero_content_length(self):
718
420
                         'Content-Type': 'application/octet-stream'})
719
421
        req.body = ''
720
422
        self.assertEquals(req.headers['Content-Length'], '0')
721
 
        resp = self.object_controller.PUT(req)
 
423
        resp = req.get_response(self.object_controller)
722
424
        self.assertEquals(resp.status_int, 201)
723
425
 
724
426
    def test_PUT_common(self):
728
430
                         'Content-Length': '6',
729
431
                         'Content-Type': 'application/octet-stream'})
730
432
        req.body = 'VERIFY'
731
 
        resp = self.object_controller.PUT(req)
 
433
        resp = req.get_response(self.object_controller)
732
434
        self.assertEquals(resp.status_int, 201)
733
435
        objfile = os.path.join(self.testdir, 'sda1',
734
436
            storage_directory(object_server.DATADIR, 'p',
736
438
            timestamp + '.data')
737
439
        self.assert_(os.path.isfile(objfile))
738
440
        self.assertEquals(open(objfile).read(), 'VERIFY')
739
 
        self.assertEquals(object_server.read_metadata(objfile),
 
441
        self.assertEquals(diskfile.read_metadata(objfile),
740
442
                          {'X-Timestamp': timestamp,
741
443
                           'Content-Length': '6',
742
444
                           'ETag': '0b4c12d7e0a73840c1c4f148fda3b037',
749
451
                         'Content-Length': '6',
750
452
                         'Content-Type': 'application/octet-stream'})
751
453
        req.body = 'VERIFY'
752
 
        resp = self.object_controller.PUT(req)
 
454
        resp = req.get_response(self.object_controller)
753
455
        self.assertEquals(resp.status_int, 201)
754
456
        sleep(.00001)
755
457
        timestamp = normalize_timestamp(time())
758
460
                                     'Content-Type': 'text/plain',
759
461
                                     'Content-Encoding': 'gzip'})
760
462
        req.body = 'VERIFY TWO'
761
 
        resp = self.object_controller.PUT(req)
 
463
        resp = req.get_response(self.object_controller)
762
464
        self.assertEquals(resp.status_int, 201)
763
465
        objfile = os.path.join(self.testdir, 'sda1',
764
466
            storage_directory(object_server.DATADIR, 'p',
766
468
            timestamp + '.data')
767
469
        self.assert_(os.path.isfile(objfile))
768
470
        self.assertEquals(open(objfile).read(), 'VERIFY TWO')
769
 
        self.assertEquals(object_server.read_metadata(objfile),
 
471
        self.assertEquals(diskfile.read_metadata(objfile),
770
472
                          {'X-Timestamp': timestamp,
771
473
                           'Content-Length': '10',
772
474
                           'ETag': 'b381a4c5dab1eaa1eb9711fa647cd039',
774
476
                           'name': '/a/c/o',
775
477
                           'Content-Encoding': 'gzip'})
776
478
 
 
479
    def test_PUT_old_timestamp(self):
 
480
        ts = time()
 
481
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
482
                headers={'X-Timestamp': normalize_timestamp(ts),
 
483
                         'Content-Length': '6',
 
484
                         'Content-Type': 'application/octet-stream'})
 
485
        req.body = 'VERIFY'
 
486
        resp = self.object_controller.PUT(req)
 
487
        self.assertEquals(resp.status_int, 201)
 
488
 
 
489
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
490
                            headers={'X-Timestamp': normalize_timestamp(ts),
 
491
                                     'Content-Type': 'text/plain',
 
492
                                     'Content-Encoding': 'gzip'})
 
493
        req.body = 'VERIFY TWO'
 
494
        resp = self.object_controller.PUT(req)
 
495
        self.assertEquals(resp.status_int, 409)
 
496
 
 
497
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
498
                            headers={'X-Timestamp': normalize_timestamp(ts - 1),
 
499
                                     'Content-Type': 'text/plain',
 
500
                                     'Content-Encoding': 'gzip'})
 
501
        req.body = 'VERIFY THREE'
 
502
        resp = self.object_controller.PUT(req)
 
503
        self.assertEquals(resp.status_int, 409)
 
504
 
777
505
    def test_PUT_no_etag(self):
778
506
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
779
507
                           headers={'X-Timestamp': normalize_timestamp(time()),
780
508
                                    'Content-Type': 'text/plain'})
781
509
        req.body = 'test'
782
 
        resp = self.object_controller.PUT(req)
 
510
        resp = req.get_response(self.object_controller)
783
511
        self.assertEquals(resp.status_int, 201)
784
512
 
785
513
    def test_PUT_invalid_etag(self):
788
516
                                    'Content-Type': 'text/plain',
789
517
                                    'ETag': 'invalid'})
790
518
        req.body = 'test'
791
 
        resp = self.object_controller.PUT(req)
 
519
        resp = req.get_response(self.object_controller)
792
520
        self.assertEquals(resp.status_int, 422)
793
521
 
794
522
    def test_PUT_user_metadata(self):
800
528
                         'X-Object-Meta-1': 'One',
801
529
                         'X-Object-Meta-Two': 'Two'})
802
530
        req.body = 'VERIFY THREE'
803
 
        resp = self.object_controller.PUT(req)
 
531
        resp = req.get_response(self.object_controller)
804
532
        self.assertEquals(resp.status_int, 201)
805
533
        objfile = os.path.join(self.testdir, 'sda1',
806
534
            storage_directory(object_server.DATADIR, 'p',
808
536
            timestamp + '.data')
809
537
        self.assert_(os.path.isfile(objfile))
810
538
        self.assertEquals(open(objfile).read(), 'VERIFY THREE')
811
 
        self.assertEquals(object_server.read_metadata(objfile),
 
539
        self.assertEquals(diskfile.read_metadata(objfile),
812
540
                          {'X-Timestamp': timestamp,
813
541
                           'Content-Length': '12',
814
542
                           'ETag': 'b114ab7b90d9ccac4bd5d99cc7ebb568',
886
614
 
887
615
    def test_HEAD(self):
888
616
        """ Test swift.object_server.ObjectController.HEAD """
889
 
        req = Request.blank('/sda1/p/a/c')
890
 
        resp = self.object_controller.HEAD(req)
 
617
        req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
 
618
        resp = req.get_response(self.object_controller)
891
619
        self.assertEquals(resp.status_int, 400)
892
620
 
893
 
        req = Request.blank('/sda1/p/a/c/o')
894
 
        resp = self.object_controller.HEAD(req)
 
621
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'})
 
622
        resp = req.get_response(self.object_controller)
895
623
        self.assertEquals(resp.status_int, 404)
896
624
 
897
625
        timestamp = normalize_timestamp(time())
901
629
                                     'X-Object-Meta-1': 'One',
902
630
                                     'X-Object-Meta-Two': 'Two'})
903
631
        req.body = 'VERIFY'
904
 
        resp = self.object_controller.PUT(req)
 
632
        resp = req.get_response(self.object_controller)
905
633
        self.assertEquals(resp.status_int, 201)
906
634
 
907
 
        req = Request.blank('/sda1/p/a/c/o')
908
 
        resp = self.object_controller.HEAD(req)
 
635
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'})
 
636
        resp = req.get_response(self.object_controller)
909
637
        self.assertEquals(resp.status_int, 200)
910
638
        self.assertEquals(resp.content_length, 6)
911
639
        self.assertEquals(resp.content_type, 'application/x-test')
922
650
                              hash_path('a', 'c', 'o')),
923
651
            timestamp + '.data')
924
652
        os.unlink(objfile)
925
 
        req = Request.blank('/sda1/p/a/c/o')
926
 
        resp = self.object_controller.HEAD(req)
 
653
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'})
 
654
        resp = req.get_response(self.object_controller)
927
655
        self.assertEquals(resp.status_int, 404)
928
656
 
929
657
        sleep(.00001)
934
662
                                'Content-Type': 'application/octet-stream',
935
663
                                'Content-length': '6'})
936
664
        req.body = 'VERIFY'
937
 
        resp = self.object_controller.PUT(req)
 
665
        resp = req.get_response(self.object_controller)
938
666
        self.assertEquals(resp.status_int, 201)
939
667
 
940
668
        sleep(.00001)
942
670
        req = Request.blank('/sda1/p/a/c/o',
943
671
                            environ={'REQUEST_METHOD': 'DELETE'},
944
672
                            headers={'X-Timestamp': timestamp})
945
 
        resp = self.object_controller.DELETE(req)
 
673
        resp = req.get_response(self.object_controller)
946
674
        self.assertEquals(resp.status_int, 204)
947
675
 
948
 
        req = Request.blank('/sda1/p/a/c/o')
949
 
        resp = self.object_controller.HEAD(req)
 
676
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'})
 
677
        resp = req.get_response(self.object_controller)
950
678
        self.assertEquals(resp.status_int, 404)
951
679
 
952
680
    def test_HEAD_quarantine_zbyte(self):
956
684
                            headers={'X-Timestamp': timestamp,
957
685
                                     'Content-Type': 'application/x-test'})
958
686
        req.body = 'VERIFY'
959
 
        resp = self.object_controller.PUT(req)
 
687
        resp = req.get_response(self.object_controller)
960
688
        self.assertEquals(resp.status_int, 201)
961
 
        file = object_server.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
 
689
        file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
962
690
                                      FakeLogger(), keep_data_fp=True)
963
691
 
964
692
        file_name = os.path.basename(file.data_file)
965
693
        with open(file.data_file) as fp:
966
 
            metadata = object_server.read_metadata(fp)
 
694
            metadata = diskfile.read_metadata(fp)
967
695
        os.unlink(file.data_file)
968
696
        with open(file.data_file, 'w') as fp:
969
 
            object_server.write_metadata(fp, metadata)
 
697
            diskfile.write_metadata(fp, metadata)
970
698
 
971
699
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
972
 
        req = Request.blank('/sda1/p/a/c/o')
973
 
        resp = self.object_controller.HEAD(req)
 
700
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'HEAD'})
 
701
        resp = req.get_response(self.object_controller)
974
702
        self.assertEquals(resp.status_int, 404)
975
703
 
976
704
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined', 'objects',
979
707
 
980
708
    def test_GET(self):
981
709
        """ Test swift.object_server.ObjectController.GET """
982
 
        req = Request.blank('/sda1/p/a/c')
983
 
        resp = self.object_controller.GET(req)
 
710
        req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
 
711
        resp = req.get_response(self.object_controller)
984
712
        self.assertEquals(resp.status_int, 400)
985
713
 
986
 
        req = Request.blank('/sda1/p/a/c/o')
987
 
        resp = self.object_controller.GET(req)
 
714
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
 
715
        resp = req.get_response(self.object_controller)
988
716
        self.assertEquals(resp.status_int, 404)
989
717
 
990
718
        timestamp = normalize_timestamp(time())
994
722
                                     'X-Object-Meta-1': 'One',
995
723
                                     'X-Object-Meta-Two': 'Two'})
996
724
        req.body = 'VERIFY'
997
 
        resp = self.object_controller.PUT(req)
 
725
        resp = req.get_response(self.object_controller)
998
726
        self.assertEquals(resp.status_int, 201)
999
727
 
1000
 
        req = Request.blank('/sda1/p/a/c/o')
1001
 
        resp = self.object_controller.GET(req)
 
728
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
 
729
        resp = req.get_response(self.object_controller)
1002
730
        self.assertEquals(resp.status_int, 200)
1003
731
        self.assertEquals(resp.body, 'VERIFY')
1004
732
        self.assertEquals(resp.content_length, 6)
1012
740
        self.assertEquals(resp.headers['x-object-meta-1'], 'One')
1013
741
        self.assertEquals(resp.headers['x-object-meta-two'], 'Two')
1014
742
 
1015
 
        req = Request.blank('/sda1/p/a/c/o')
 
743
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
1016
744
        req.range = 'bytes=1-3'
1017
 
        resp = self.object_controller.GET(req)
 
745
        resp = req.get_response(self.object_controller)
1018
746
        self.assertEquals(resp.status_int, 206)
1019
747
        self.assertEquals(resp.body, 'ERI')
1020
748
        self.assertEquals(resp.headers['content-length'], '3')
1021
749
 
1022
 
        req = Request.blank('/sda1/p/a/c/o')
 
750
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
1023
751
        req.range = 'bytes=1-'
1024
 
        resp = self.object_controller.GET(req)
 
752
        resp = req.get_response(self.object_controller)
1025
753
        self.assertEquals(resp.status_int, 206)
1026
754
        self.assertEquals(resp.body, 'ERIFY')
1027
755
        self.assertEquals(resp.headers['content-length'], '5')
1028
756
 
1029
 
        req = Request.blank('/sda1/p/a/c/o')
 
757
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
1030
758
        req.range = 'bytes=-2'
1031
 
        resp = self.object_controller.GET(req)
 
759
        resp = req.get_response(self.object_controller)
1032
760
        self.assertEquals(resp.status_int, 206)
1033
761
        self.assertEquals(resp.body, 'FY')
1034
762
        self.assertEquals(resp.headers['content-length'], '2')
1038
766
                              hash_path('a', 'c', 'o')),
1039
767
            timestamp + '.data')
1040
768
        os.unlink(objfile)
1041
 
        req = Request.blank('/sda1/p/a/c/o')
1042
 
        resp = self.object_controller.GET(req)
 
769
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
 
770
        resp = req.get_response(self.object_controller)
1043
771
        self.assertEquals(resp.status_int, 404)
1044
772
 
1045
773
        sleep(.00001)
1050
778
                                'Content-Type': 'application:octet-stream',
1051
779
                                'Content-Length': '6'})
1052
780
        req.body = 'VERIFY'
1053
 
        resp = self.object_controller.PUT(req)
 
781
        resp = req.get_response(self.object_controller)
1054
782
        self.assertEquals(resp.status_int, 201)
1055
783
 
1056
784
        sleep(.00001)
1058
786
        req = Request.blank('/sda1/p/a/c/o',
1059
787
                            environ={'REQUEST_METHOD': 'DELETE'},
1060
788
                            headers={'X-Timestamp': timestamp})
1061
 
        resp = self.object_controller.DELETE(req)
 
789
        resp = req.get_response(self.object_controller)
1062
790
        self.assertEquals(resp.status_int, 204)
1063
791
 
1064
 
        req = Request.blank('/sda1/p/a/c/o')
1065
 
        resp = self.object_controller.GET(req)
 
792
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'})
 
793
        resp = req.get_response(self.object_controller)
1066
794
        self.assertEquals(resp.status_int, 404)
1067
795
 
1068
796
    def test_GET_if_match(self):
1247
975
        req.body = 'VERIFY'
1248
976
        resp = self.object_controller.PUT(req)
1249
977
        self.assertEquals(resp.status_int, 201)
1250
 
        file = object_server.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
 
978
        file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
1251
979
                                      FakeLogger(), keep_data_fp=True)
1252
980
        file_name = os.path.basename(file.data_file)
1253
981
        etag = md5()
1255
983
        etag = etag.hexdigest()
1256
984
        metadata = {'X-Timestamp': timestamp,
1257
985
                    'Content-Length': 6, 'ETag': etag}
1258
 
        object_server.write_metadata(file.fp, metadata)
 
986
        diskfile.write_metadata(file.fp, metadata)
1259
987
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1260
988
        req = Request.blank('/sda1/p/a/c/o')
1261
989
        resp = self.object_controller.GET(req)
1278
1006
        req.body = 'VERIFY'
1279
1007
        resp = self.object_controller.PUT(req)
1280
1008
        self.assertEquals(resp.status_int, 201)
1281
 
        file = object_server.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
 
1009
        file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
1282
1010
                                      FakeLogger(), keep_data_fp=True)
1283
1011
        file_name = os.path.basename(file.data_file)
1284
1012
        with open(file.data_file) as fp:
1285
 
            metadata = object_server.read_metadata(fp)
 
1013
            metadata = diskfile.read_metadata(fp)
1286
1014
        os.unlink(file.data_file)
1287
1015
        with open(file.data_file, 'w') as fp:
1288
 
            object_server.write_metadata(fp, metadata)
 
1016
            diskfile.write_metadata(fp, metadata)
1289
1017
 
1290
1018
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1291
1019
        req = Request.blank('/sda1/p/a/c/o')
1305
1033
        req.body = 'VERIFY'
1306
1034
        resp = self.object_controller.PUT(req)
1307
1035
        self.assertEquals(resp.status_int, 201)
1308
 
        file = object_server.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
 
1036
        file = diskfile.DiskFile(self.testdir, 'sda1', 'p', 'a', 'c', 'o',
1309
1037
                                      FakeLogger(), keep_data_fp=True)
1310
1038
        file_name = os.path.basename(file.data_file)
1311
1039
        etag = md5()
1313
1041
        etag = etag.hexdigest()
1314
1042
        metadata = {'X-Timestamp': timestamp,
1315
1043
                    'Content-Length': 6, 'ETag': etag}
1316
 
        object_server.write_metadata(file.fp, metadata)
 
1044
        diskfile.write_metadata(file.fp, metadata)
1317
1045
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1318
1046
        req = Request.blank('/sda1/p/a/c/o')
1319
1047
        req.range = 'bytes=0-4'  # partial
1320
1048
        resp = self.object_controller.GET(req)
1321
1049
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined', 'objects',
1322
1050
                            os.path.basename(os.path.dirname(file.data_file)))
1323
 
        body = resp.body
 
1051
        resp.body
1324
1052
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1325
1053
        self.assertFalse(os.path.isdir(quar_dir))
1326
1054
        req = Request.blank('/sda1/p/a/c/o')
1332
1060
        resp = self.object_controller.GET(req)
1333
1061
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined', 'objects',
1334
1062
                            os.path.basename(os.path.dirname(file.data_file)))
1335
 
        body = resp.body
 
1063
        resp.body
1336
1064
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1337
1065
        self.assertFalse(os.path.isdir(quar_dir))
1338
1066
 
1342
1070
        quar_dir = os.path.join(self.testdir, 'sda1', 'quarantined', 'objects',
1343
1071
                       os.path.basename(os.path.dirname(file.data_file)))
1344
1072
        self.assertEquals(os.listdir(file.datadir)[0], file_name)
1345
 
        body = resp.body
 
1073
        resp.body
1346
1074
        self.assertTrue(os.path.isdir(quar_dir))
1347
1075
        req = Request.blank('/sda1/p/a/c/o')
1348
1076
        resp = self.object_controller.GET(req)
1349
1077
        self.assertEquals(resp.status_int, 404)
1350
1078
 
1351
1079
    def test_DELETE(self):
1352
 
        """ Test swift.object_server.ObjectController.DELETE """
 
1080
        # Test swift.object_server.ObjectController.DELETE
1353
1081
        req = Request.blank('/sda1/p/a/c',
1354
1082
                            environ={'REQUEST_METHOD': 'DELETE'})
1355
1083
        resp = self.object_controller.DELETE(req)
1361
1089
        self.assertEquals(resp.status_int, 400)
1362
1090
        # self.assertRaises(KeyError, self.object_controller.DELETE, req)
1363
1091
 
1364
 
        timestamp = normalize_timestamp(time())
1365
 
        req = Request.blank('/sda1/p/a/c/o',
1366
 
                            environ={'REQUEST_METHOD': 'DELETE'},
1367
 
                            headers={'X-Timestamp': timestamp})
1368
 
        resp = self.object_controller.DELETE(req)
1369
 
        self.assertEquals(resp.status_int, 404)
1370
 
 
1371
 
        sleep(.00001)
1372
 
        timestamp = normalize_timestamp(time())
1373
 
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
1374
 
                            headers={
1375
 
                                'X-Timestamp': timestamp,
1376
 
                                'Content-Type': 'application/octet-stream',
1377
 
                                'Content-Length': '4',
1378
 
                                })
1379
 
        req.body = 'test'
1380
 
        resp = self.object_controller.PUT(req)
1381
 
        self.assertEquals(resp.status_int, 201)
1382
 
 
1383
 
        timestamp = normalize_timestamp(float(timestamp) - 1)
1384
 
        req = Request.blank('/sda1/p/a/c/o',
1385
 
                            environ={'REQUEST_METHOD': 'DELETE'},
1386
 
                            headers={'X-Timestamp': timestamp})
1387
 
        resp = self.object_controller.DELETE(req)
1388
 
        self.assertEquals(resp.status_int, 204)
1389
 
        objfile = os.path.join(self.testdir, 'sda1',
1390
 
            storage_directory(object_server.DATADIR, 'p',
1391
 
                              hash_path('a', 'c', 'o')),
1392
 
            timestamp + '.ts')
1393
 
        self.assert_(os.path.isfile(objfile))
1394
 
 
1395
 
        sleep(.00001)
1396
 
        timestamp = normalize_timestamp(time())
1397
 
        req = Request.blank('/sda1/p/a/c/o',
1398
 
                            environ={'REQUEST_METHOD': 'DELETE'},
1399
 
                            headers={'X-Timestamp': timestamp})
1400
 
        resp = self.object_controller.DELETE(req)
1401
 
        self.assertEquals(resp.status_int, 204)
1402
 
        objfile = os.path.join(self.testdir, 'sda1',
1403
 
            storage_directory(object_server.DATADIR, 'p',
1404
 
                              hash_path('a', 'c', 'o')),
1405
 
            timestamp + '.ts')
1406
 
        self.assert_(os.path.isfile(objfile))
 
1092
        # The following should have created a tombstone file
 
1093
        timestamp = normalize_timestamp(time())
 
1094
        req = Request.blank('/sda1/p/a/c/o',
 
1095
                            environ={'REQUEST_METHOD': 'DELETE'},
 
1096
                            headers={'X-Timestamp': timestamp})
 
1097
        resp = self.object_controller.DELETE(req)
 
1098
        self.assertEquals(resp.status_int, 404)
 
1099
        objfile = os.path.join(self.testdir, 'sda1',
 
1100
            storage_directory(object_server.DATADIR, 'p',
 
1101
                              hash_path('a', 'c', 'o')),
 
1102
            timestamp + '.ts')
 
1103
        self.assert_(os.path.isfile(objfile))
 
1104
 
 
1105
        # The following should *not* have created a tombstone file.
 
1106
        timestamp = normalize_timestamp(float(timestamp) - 1)
 
1107
        req = Request.blank('/sda1/p/a/c/o',
 
1108
                            environ={'REQUEST_METHOD': 'DELETE'},
 
1109
                            headers={'X-Timestamp': timestamp})
 
1110
        resp = self.object_controller.DELETE(req)
 
1111
        self.assertEquals(resp.status_int, 404)
 
1112
        objfile = os.path.join(self.testdir, 'sda1',
 
1113
            storage_directory(object_server.DATADIR, 'p',
 
1114
                              hash_path('a', 'c', 'o')),
 
1115
            timestamp + '.ts')
 
1116
        self.assertFalse(os.path.isfile(objfile))
 
1117
        self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1118
 
 
1119
        sleep(.00001)
 
1120
        timestamp = normalize_timestamp(time())
 
1121
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
1122
                            headers={
 
1123
                                'X-Timestamp': timestamp,
 
1124
                                'Content-Type': 'application/octet-stream',
 
1125
                                'Content-Length': '4',
 
1126
                                })
 
1127
        req.body = 'test'
 
1128
        resp = self.object_controller.PUT(req)
 
1129
        self.assertEquals(resp.status_int, 201)
 
1130
 
 
1131
        # The following should *not* have created a tombstone file.
 
1132
        timestamp = normalize_timestamp(float(timestamp) - 1)
 
1133
        req = Request.blank('/sda1/p/a/c/o',
 
1134
                            environ={'REQUEST_METHOD': 'DELETE'},
 
1135
                            headers={'X-Timestamp': timestamp})
 
1136
        resp = self.object_controller.DELETE(req)
 
1137
        self.assertEquals(resp.status_int, 409)
 
1138
        objfile = os.path.join(self.testdir, 'sda1',
 
1139
            storage_directory(object_server.DATADIR, 'p',
 
1140
                              hash_path('a', 'c', 'o')),
 
1141
            timestamp + '.ts')
 
1142
        self.assertFalse(os.path.isfile(objfile))
 
1143
        self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1144
 
 
1145
        sleep(.00001)
 
1146
        timestamp = normalize_timestamp(time())
 
1147
        req = Request.blank('/sda1/p/a/c/o',
 
1148
                            environ={'REQUEST_METHOD': 'DELETE'},
 
1149
                            headers={'X-Timestamp': timestamp})
 
1150
        resp = self.object_controller.DELETE(req)
 
1151
        self.assertEquals(resp.status_int, 204)
 
1152
        objfile = os.path.join(self.testdir, 'sda1',
 
1153
            storage_directory(object_server.DATADIR, 'p',
 
1154
                              hash_path('a', 'c', 'o')),
 
1155
            timestamp + '.ts')
 
1156
        self.assert_(os.path.isfile(objfile))
 
1157
 
 
1158
    def test_DELETE_container_updates(self):
 
1159
        # Test swift.object_server.ObjectController.DELETE and container
 
1160
        # updates, making sure container update is called in the correct
 
1161
        # state.
 
1162
        timestamp = normalize_timestamp(time())
 
1163
        req = Request.blank('/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
 
1164
                            headers={
 
1165
                                'X-Timestamp': timestamp,
 
1166
                                'Content-Type': 'application/octet-stream',
 
1167
                                'Content-Length': '4',
 
1168
                                })
 
1169
        req.body = 'test'
 
1170
        resp = self.object_controller.PUT(req)
 
1171
        self.assertEquals(resp.status_int, 201)
 
1172
 
 
1173
        calls_made = [0]
 
1174
 
 
1175
        def our_container_update(*args, **kwargs):
 
1176
            calls_made[0] += 1
 
1177
 
 
1178
        orig_cu = self.object_controller.container_update
 
1179
        self.object_controller.container_update = our_container_update
 
1180
        try:
 
1181
            # The following request should return 409 (HTTP Conflict). A
 
1182
            # tombstone file should not have been created with this timestamp.
 
1183
            timestamp = normalize_timestamp(float(timestamp) - 1)
 
1184
            req = Request.blank('/sda1/p/a/c/o',
 
1185
                                environ={'REQUEST_METHOD': 'DELETE'},
 
1186
                                headers={'X-Timestamp': timestamp})
 
1187
            resp = self.object_controller.DELETE(req)
 
1188
            self.assertEquals(resp.status_int, 409)
 
1189
            objfile = os.path.join(self.testdir, 'sda1',
 
1190
                storage_directory(object_server.DATADIR, 'p',
 
1191
                                  hash_path('a', 'c', 'o')),
 
1192
                timestamp + '.ts')
 
1193
            self.assertFalse(os.path.isfile(objfile))
 
1194
            self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1195
            self.assertEquals(0, calls_made[0])
 
1196
 
 
1197
            # The following request should return 204, and the object should
 
1198
            # be truly deleted (container update is performed) because this
 
1199
            # timestamp is newer. A tombstone file should have been created
 
1200
            # with this timestamp.
 
1201
            sleep(.00001)
 
1202
            timestamp = normalize_timestamp(time())
 
1203
            req = Request.blank('/sda1/p/a/c/o',
 
1204
                                environ={'REQUEST_METHOD': 'DELETE'},
 
1205
                                headers={'X-Timestamp': timestamp})
 
1206
            resp = self.object_controller.DELETE(req)
 
1207
            self.assertEquals(resp.status_int, 204)
 
1208
            objfile = os.path.join(self.testdir, 'sda1',
 
1209
                storage_directory(object_server.DATADIR, 'p',
 
1210
                                  hash_path('a', 'c', 'o')),
 
1211
                timestamp + '.ts')
 
1212
            self.assert_(os.path.isfile(objfile))
 
1213
            self.assertEquals(1, calls_made[0])
 
1214
            self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1215
 
 
1216
            # The following request should return a 404, as the object should
 
1217
            # already have been deleted, but it should have also performed a
 
1218
            # container update because the timestamp is newer, and a tombstone
 
1219
            # file should also exist with this timestamp.
 
1220
            sleep(.00001)
 
1221
            timestamp = normalize_timestamp(time())
 
1222
            req = Request.blank('/sda1/p/a/c/o',
 
1223
                                environ={'REQUEST_METHOD': 'DELETE'},
 
1224
                                headers={'X-Timestamp': timestamp})
 
1225
            resp = self.object_controller.DELETE(req)
 
1226
            self.assertEquals(resp.status_int, 404)
 
1227
            objfile = os.path.join(self.testdir, 'sda1',
 
1228
                storage_directory(object_server.DATADIR, 'p',
 
1229
                                  hash_path('a', 'c', 'o')),
 
1230
                timestamp + '.ts')
 
1231
            self.assert_(os.path.isfile(objfile))
 
1232
            self.assertEquals(2, calls_made[0])
 
1233
            self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1234
 
 
1235
            # The following request should return a 404, as the object should
 
1236
            # already have been deleted, and it should not have performed a
 
1237
            # container update because the timestamp is older, or created a
 
1238
            # tombstone file with this timestamp.
 
1239
            timestamp = normalize_timestamp(float(timestamp) - 1)
 
1240
            req = Request.blank('/sda1/p/a/c/o',
 
1241
                                environ={'REQUEST_METHOD': 'DELETE'},
 
1242
                                headers={'X-Timestamp': timestamp})
 
1243
            resp = self.object_controller.DELETE(req)
 
1244
            self.assertEquals(resp.status_int, 404)
 
1245
            objfile = os.path.join(self.testdir, 'sda1',
 
1246
                storage_directory(object_server.DATADIR, 'p',
 
1247
                                  hash_path('a', 'c', 'o')),
 
1248
                timestamp + '.ts')
 
1249
            self.assertFalse(os.path.isfile(objfile))
 
1250
            self.assertEquals(2, calls_made[0])
 
1251
            self.assertEquals(len(os.listdir(os.path.dirname(objfile))), 1)
 
1252
        finally:
 
1253
            self.object_controller.container_update = orig_cu
1407
1254
 
1408
1255
    def test_call(self):
1409
1256
        """ Test swift.object_server.ObjectController.__call__ """
1477
1324
 
1478
1325
        def my_check(*args):
1479
1326
            return False
 
1327
 
1480
1328
        def my_storage_directory(*args):
1481
1329
            return self.testdir+'/collide'
1482
 
        _storage_directory = object_server.storage_directory
 
1330
        _storage_directory = diskfile.storage_directory
1483
1331
        _check = object_server.check_object_creation
1484
1332
        try:
1485
 
            object_server.storage_directory = my_storage_directory
 
1333
            diskfile.storage_directory = my_storage_directory
1486
1334
            object_server.check_object_creation = my_check
1487
1335
            inbuf = StringIO()
1488
1336
            errbuf = StringIO()
1495
1343
                                             'SERVER_PROTOCOL': 'HTTP/1.0',
1496
1344
                                             'CONTENT_LENGTH': '0',
1497
1345
                                             'CONTENT_TYPE': 'text/html',
1498
 
                                             'HTTP_X_TIMESTAMP': 1.2,
 
1346
                                             'HTTP_X_TIMESTAMP': '1.2',
1499
1347
                                             'wsgi.version': (1, 0),
1500
1348
                                             'wsgi.url_scheme': 'http',
1501
1349
                                             'wsgi.input': inbuf,
1518
1366
                                             'SERVER_PROTOCOL': 'HTTP/1.0',
1519
1367
                                             'CONTENT_LENGTH': '0',
1520
1368
                                             'CONTENT_TYPE': 'text/html',
1521
 
                                             'HTTP_X_TIMESTAMP': 1.3,
 
1369
                                             'HTTP_X_TIMESTAMP': '1.3',
1522
1370
                                             'wsgi.version': (1, 0),
1523
1371
                                             'wsgi.url_scheme': 'http',
1524
1372
                                             'wsgi.input': inbuf,
1531
1379
            self.assertEquals(outbuf.getvalue()[:4], '403 ')
1532
1380
 
1533
1381
        finally:
1534
 
            object_server.storage_directory = _storage_directory
 
1382
            diskfile.storage_directory = _storage_directory
1535
1383
            object_server.check_object_creation = _check
1536
1384
 
1537
1385
    def test_invalid_method_doesnt_exist(self):
1538
1386
        errbuf = StringIO()
1539
1387
        outbuf = StringIO()
 
1388
 
1540
1389
        def start_response(*args):
1541
1390
            outbuf.writelines(args)
1542
1391
        self.object_controller.__call__({'REQUEST_METHOD': 'method_doesnt_exist',
1548
1397
    def test_invalid_method_is_not_public(self):
1549
1398
        errbuf = StringIO()
1550
1399
        outbuf = StringIO()
 
1400
 
1551
1401
        def start_response(*args):
1552
1402
            outbuf.writelines(args)
1553
1403
        self.object_controller.__call__({'REQUEST_METHOD': '__init__',
1736
1586
            storage_directory(object_server.DATADIR, 'p', hash_path('a', 'c',
1737
1587
            'o')), timestamp + '.data')
1738
1588
        self.assert_(os.path.isfile(objfile))
1739
 
        self.assertEquals(object_server.read_metadata(objfile),
 
1589
        self.assertEquals(diskfile.read_metadata(objfile),
1740
1590
            {'X-Timestamp': timestamp,
1741
1591
            'Content-Length': '0', 'Content-Type': 'text/plain', 'name':
1742
1592
            '/a/c/o', 'X-Object-Manifest': 'c/o/', 'ETag':
1783
1633
            '/a/c/o', {'x-timestamp': '1', 'x-out': 'set',
1784
1634
                       'user-agent': 'obj-server %s' % os.getpid()}])
1785
1635
 
1786
 
 
1787
1636
    def test_updating_multiple_delete_at_container_servers(self):
1788
1637
        self.object_controller.expiring_objects_account = 'exp'
1789
1638
        self.object_controller.expiring_objects_container_divisor = 60
1790
1639
 
1791
1640
        http_connect_args = []
 
1641
 
1792
1642
        def fake_http_connect(ipaddr, port, device, partition, method, path,
1793
1643
                              headers=None, query_string=None, ssl=False):
1794
1644
            class SuccessfulFakeConn(object):
1808
1658
                             'headers': headers, 'query_string': query_string}
1809
1659
 
1810
1660
            http_connect_args.append(
1811
 
                dict((k,v) for k,v in captured_args.iteritems()
 
1661
                dict((k, v) for k, v in captured_args.iteritems()
1812
1662
                     if v is not None))
1813
1663
 
1814
1664
        req = Request.blank(
1835
1685
 
1836
1686
        self.assertEqual(resp.status_int, 201)
1837
1687
 
1838
 
 
1839
1688
        http_connect_args.sort(key=operator.itemgetter('ipaddr'))
1840
1689
 
1841
1690
        self.assertEquals(len(http_connect_args), 3)
1890
1739
 
1891
1740
    def test_updating_multiple_container_servers(self):
1892
1741
        http_connect_args = []
 
1742
 
1893
1743
        def fake_http_connect(ipaddr, port, device, partition, method, path,
1894
1744
                              headers=None, query_string=None, ssl=False):
1895
1745
            class SuccessfulFakeConn(object):
1909
1759
                             'headers': headers, 'query_string': query_string}
1910
1760
 
1911
1761
            http_connect_args.append(
1912
 
                dict((k,v) for k,v in captured_args.iteritems()
 
1762
                dict((k, v) for k, v in captured_args.iteritems()
1913
1763
                     if v is not None))
1914
1764
 
1915
1765
        req = Request.blank(
2753
2603
        def fake_fallocate(fd, size):
2754
2604
            raise OSError(42, 'Unable to fallocate(%d)' % size)
2755
2605
 
2756
 
        orig_fallocate = object_server.fallocate
 
2606
        orig_fallocate = diskfile.fallocate
2757
2607
        try:
2758
 
            object_server.fallocate = fake_fallocate
 
2608
            diskfile.fallocate = fake_fallocate
2759
2609
            timestamp = normalize_timestamp(time())
2760
2610
            body_reader = IgnoredBody()
2761
2611
            req = Request.blank('/sda1/p/a/c/o',
2769
2619
            self.assertEquals(resp.status_int, 507)
2770
2620
            self.assertFalse(body_reader.read_called)
2771
2621
        finally:
2772
 
            object_server.fallocate = orig_fallocate
 
2622
            diskfile.fallocate = orig_fallocate
2773
2623
 
2774
2624
    def test_serv_reserv(self):
2775
2625
        """
2790
2640
 
2791
2641
    def test_list_allowed_methods(self):
2792
2642
        """ Test list of allowed_methods """
2793
 
        methods = ['DELETE', 'PUT', 'HEAD', 'GET', 'REPLICATE', 'POST']
2794
 
        self.assertEquals(self.object_controller.allowed_methods, methods)
2795
 
 
2796
 
    def test_allowed_methods_from_configuration_file(self):
2797
 
        """
2798
 
        Test list of allowed_methods which
2799
 
        were set from configuration file.
2800
 
        """
2801
 
        conf = {'devices': self.testdir, 'mount_check': 'false'}
2802
 
        self.assertEquals(object_server.ObjectController(conf).allowed_methods,
2803
 
                          ['DELETE', 'PUT', 'HEAD', 'GET', 'REPLICATE',
2804
 
                           'POST'])
2805
 
        conf['replication_server'] = 'True'
2806
 
        self.assertEquals(object_server.ObjectController(conf).allowed_methods,
2807
 
                          ['REPLICATE'])
2808
 
        conf['replication_server'] = 'False'
2809
 
        self.assertEquals(object_server.ObjectController(conf).allowed_methods,
2810
 
                          ['DELETE', 'PUT', 'HEAD', 'GET', 'POST'])
 
2643
        obj_methods = ['DELETE', 'PUT', 'HEAD', 'GET', 'POST']
 
2644
        repl_methods = ['REPLICATE']
 
2645
        for method_name in obj_methods:
 
2646
            method = getattr(self.object_controller, method_name)
 
2647
            self.assertFalse(hasattr(method, 'replication'))
 
2648
        for method_name in repl_methods:
 
2649
            method = getattr(self.object_controller, method_name)
 
2650
            self.assertEquals(method.replication, True)
2811
2651
 
2812
2652
    def test_correct_allowed_method(self):
2813
2653
        """
2817
2657
        inbuf = StringIO()
2818
2658
        errbuf = StringIO()
2819
2659
        outbuf = StringIO()
 
2660
        self.object_controller = object_server.ObjectController(
 
2661
            {'devices': self.testdir, 'mount_check': 'false',
 
2662
             'replication_server': 'false'})
2820
2663
 
2821
2664
        def start_response(*args):
2822
2665
            """ Sends args to outbuf """
2823
2666
            outbuf.writelines(args)
2824
2667
 
2825
 
        method = self.object_controller.allowed_methods[0]
2826
 
 
 
2668
        method = 'PUT'
2827
2669
        env = {'REQUEST_METHOD': method,
2828
2670
               'SCRIPT_NAME': '',
2829
2671
               'PATH_INFO': '/sda1/p/a/c',
2839
2681
               'wsgi.multiprocess': False,
2840
2682
               'wsgi.run_once': False}
2841
2683
 
2842
 
        answer = ['<html><h1>Method Not Allowed</h1><p>The method is not '
2843
 
                  'allowed for this resource.</p></html>']
2844
 
 
 
2684
        method_res = mock.MagicMock()
 
2685
        mock_method = public(lambda x: mock.MagicMock(return_value=method_res))
2845
2686
        with mock.patch.object(self.object_controller, method,
2846
 
                               return_value=mock.MagicMock()) as mock_method:
 
2687
                               new=mock_method):
2847
2688
            response = self.object_controller.__call__(env, start_response)
2848
 
            self.assertNotEqual(response, answer)
2849
 
            self.assertEqual(mock_method.call_count, 1)
 
2689
            self.assertEqual(response, method_res)
2850
2690
 
2851
2691
    def test_not_allowed_method(self):
2852
2692
        """
2856
2696
        inbuf = StringIO()
2857
2697
        errbuf = StringIO()
2858
2698
        outbuf = StringIO()
 
2699
        self.object_controller = object_server.ObjectController(
 
2700
            {'devices': self.testdir, 'mount_check': 'false',
 
2701
             'replication_server': 'false'})
2859
2702
 
2860
2703
        def start_response(*args):
2861
2704
            """ Sends args to outbuf """
2862
2705
            outbuf.writelines(args)
2863
2706
 
2864
 
        method = self.object_controller.allowed_methods[0]
 
2707
        method = 'PUT'
2865
2708
 
2866
2709
        env = {'REQUEST_METHOD': method,
2867
2710
               'SCRIPT_NAME': '',
2880
2723
 
2881
2724
        answer = ['<html><h1>Method Not Allowed</h1><p>The method is not '
2882
2725
                  'allowed for this resource.</p></html>']
2883
 
 
 
2726
        mock_method = replication(public(lambda x: mock.MagicMock()))
2884
2727
        with mock.patch.object(self.object_controller, method,
2885
 
                               return_value=mock.MagicMock()) as mock_method:
2886
 
            self.object_controller.allowed_methods.remove(method)
 
2728
                               new=mock_method):
 
2729
            mock_method.replication = True
2887
2730
            response = self.object_controller.__call__(env, start_response)
2888
 
            self.assertEqual(mock_method.call_count, 0)
2889
2731
            self.assertEqual(response, answer)
2890
 
            self.object_controller.allowed_methods.append(method)
2891
2732
 
2892
2733
 
2893
2734
if __name__ == '__main__':