Merge "Prefer X-Backend-Timestamp for X-Newest"
This commit is contained in:
@@ -78,8 +78,9 @@ def source_key(resp):
|
|||||||
|
|
||||||
:param resp: bufferedhttp response object
|
:param resp: bufferedhttp response object
|
||||||
"""
|
"""
|
||||||
return float(resp.getheader('x-put-timestamp') or
|
return Timestamp(resp.getheader('x-backend-timestamp') or
|
||||||
resp.getheader('x-timestamp') or 0)
|
resp.getheader('x-put-timestamp') or
|
||||||
|
resp.getheader('x-timestamp') or 0)
|
||||||
|
|
||||||
|
|
||||||
def delay_denial(func):
|
def delay_denial(func):
|
||||||
|
@@ -29,6 +29,7 @@ from eventlet.green import socket
|
|||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from test import get_config
|
from test import get_config
|
||||||
|
from swift.common import swob
|
||||||
from swift.common.utils import config_true_value, LogAdapter
|
from swift.common.utils import config_true_value, LogAdapter
|
||||||
from swift.common.ring import Ring, RingData
|
from swift.common.ring import Ring, RingData
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
@@ -671,20 +672,23 @@ def fake_http_connect(*code_iter, **kwargs):
|
|||||||
else:
|
else:
|
||||||
etag = '"68b329da9893e34099c7d8ad5cb9c940"'
|
etag = '"68b329da9893e34099c7d8ad5cb9c940"'
|
||||||
|
|
||||||
headers = {'content-length': len(self.body),
|
headers = swob.HeaderKeyDict({
|
||||||
'content-type': 'x-application/test',
|
'content-length': len(self.body),
|
||||||
'x-timestamp': self.timestamp,
|
'content-type': 'x-application/test',
|
||||||
'x-backend-timestamp': self.timestamp,
|
'x-timestamp': self.timestamp,
|
||||||
'last-modified': self.timestamp,
|
'x-backend-timestamp': self.timestamp,
|
||||||
'x-object-meta-test': 'testing',
|
'last-modified': self.timestamp,
|
||||||
'x-delete-at': '9876543210',
|
'x-object-meta-test': 'testing',
|
||||||
'etag': etag,
|
'x-delete-at': '9876543210',
|
||||||
'x-works': 'yes'}
|
'etag': etag,
|
||||||
|
'x-works': 'yes',
|
||||||
|
})
|
||||||
if self.status // 100 == 2:
|
if self.status // 100 == 2:
|
||||||
headers['x-account-container-count'] = \
|
headers['x-account-container-count'] = \
|
||||||
kwargs.get('count', 12345)
|
kwargs.get('count', 12345)
|
||||||
if not self.timestamp:
|
if not self.timestamp:
|
||||||
del headers['x-timestamp']
|
# when timestamp is None, HeaderKeyDict raises KeyError
|
||||||
|
headers.pop('x-timestamp', None)
|
||||||
try:
|
try:
|
||||||
if container_ts_iter.next() is False:
|
if container_ts_iter.next() is False:
|
||||||
headers['x-container-timestamp'] = '1'
|
headers['x-container-timestamp'] = '1'
|
||||||
@@ -725,7 +729,7 @@ def fake_http_connect(*code_iter, **kwargs):
|
|||||||
sleep(value)
|
sleep(value)
|
||||||
|
|
||||||
def getheader(self, name, default=None):
|
def getheader(self, name, default=None):
|
||||||
return dict(self.getheaders()).get(name.lower(), default)
|
return swob.HeaderKeyDict(self.getheaders()).get(name, default)
|
||||||
|
|
||||||
timestamps_iter = iter(kwargs.get('timestamps') or ['1'] * len(code_iter))
|
timestamps_iter = iter(kwargs.get('timestamps') or ['1'] * len(code_iter))
|
||||||
etag_iter = iter(kwargs.get('etags') or [None] * len(code_iter))
|
etag_iter = iter(kwargs.get('etags') or [None] * len(code_iter))
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import random
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@@ -543,6 +544,75 @@ class TestObjController(unittest.TestCase):
|
|||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
self.assertEquals(resp.status_int, 200)
|
self.assertEquals(resp.status_int, 200)
|
||||||
|
|
||||||
|
def test_HEAD_x_newest_different_timestamps(self):
|
||||||
|
req = swob.Request.blank('/v1/a/c/o', method='HEAD',
|
||||||
|
headers={'X-Newest': 'true'})
|
||||||
|
ts = (utils.Timestamp(t) for t in itertools.count(int(time.time())))
|
||||||
|
timestamps = [next(ts) for i in range(3)]
|
||||||
|
newest_timestamp = timestamps[-1]
|
||||||
|
random.shuffle(timestamps)
|
||||||
|
backend_response_headers = [{
|
||||||
|
'X-Backend-Timestamp': t.internal,
|
||||||
|
'X-Timestamp': t.normal
|
||||||
|
} for t in timestamps]
|
||||||
|
with set_http_connect(200, 200, 200,
|
||||||
|
headers=backend_response_headers):
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
self.assertEqual(resp.headers['x-timestamp'], newest_timestamp.normal)
|
||||||
|
|
||||||
|
def test_HEAD_x_newest_with_two_vector_timestamps(self):
|
||||||
|
req = swob.Request.blank('/v1/a/c/o', method='HEAD',
|
||||||
|
headers={'X-Newest': 'true'})
|
||||||
|
ts = (utils.Timestamp(time.time(), offset=offset)
|
||||||
|
for offset in itertools.count())
|
||||||
|
timestamps = [next(ts) for i in range(3)]
|
||||||
|
newest_timestamp = timestamps[-1]
|
||||||
|
random.shuffle(timestamps)
|
||||||
|
backend_response_headers = [{
|
||||||
|
'X-Backend-Timestamp': t.internal,
|
||||||
|
'X-Timestamp': t.normal
|
||||||
|
} for t in timestamps]
|
||||||
|
with set_http_connect(200, 200, 200,
|
||||||
|
headers=backend_response_headers):
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
self.assertEqual(resp.headers['x-backend-timestamp'],
|
||||||
|
newest_timestamp.internal)
|
||||||
|
|
||||||
|
def test_HEAD_x_newest_with_some_missing(self):
|
||||||
|
req = swob.Request.blank('/v1/a/c/o', method='HEAD',
|
||||||
|
headers={'X-Newest': 'true'})
|
||||||
|
ts = (utils.Timestamp(t) for t in itertools.count(int(time.time())))
|
||||||
|
request_count = self.app.request_node_count(self.obj_ring.replicas)
|
||||||
|
backend_response_headers = [{
|
||||||
|
'x-timestamp': next(ts).normal,
|
||||||
|
} for i in range(request_count)]
|
||||||
|
responses = [404] * (request_count - 1)
|
||||||
|
responses.append(200)
|
||||||
|
request_log = []
|
||||||
|
|
||||||
|
def capture_requests(ip, port, device, part, method, path,
|
||||||
|
headers=None, **kwargs):
|
||||||
|
req = {
|
||||||
|
'ip': ip,
|
||||||
|
'port': port,
|
||||||
|
'device': device,
|
||||||
|
'part': part,
|
||||||
|
'method': method,
|
||||||
|
'path': path,
|
||||||
|
'headers': headers,
|
||||||
|
}
|
||||||
|
request_log.append(req)
|
||||||
|
with set_http_connect(*responses,
|
||||||
|
headers=backend_response_headers,
|
||||||
|
give_connect=capture_requests):
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
for req in request_log:
|
||||||
|
self.assertEqual(req['method'], 'HEAD')
|
||||||
|
self.assertEqual(req['path'], '/a/c/o')
|
||||||
|
|
||||||
def test_PUT_log_info(self):
|
def test_PUT_log_info(self):
|
||||||
req = swift.common.swob.Request.blank('/v1/a/c/o', method='PUT')
|
req = swift.common.swob.Request.blank('/v1/a/c/o', method='PUT')
|
||||||
req.headers['x-copy-from'] = 'some/where'
|
req.headers['x-copy-from'] = 'some/where'
|
||||||
|
@@ -2007,6 +2007,8 @@ class TestObjectController(unittest.TestCase):
|
|||||||
None, None), None)
|
None, None), None)
|
||||||
test_status_map((200, 200, 200, 200, 200), 200, ('0', '0', None,
|
test_status_map((200, 200, 200, 200, 200), 200, ('0', '0', None,
|
||||||
None, '1'), '1')
|
None, '1'), '1')
|
||||||
|
test_status_map((200, 200, 404, 404, 200), 200, ('0', '0', None,
|
||||||
|
None, '1'), '1')
|
||||||
|
|
||||||
def test_GET_newest(self):
|
def test_GET_newest(self):
|
||||||
with save_globals():
|
with save_globals():
|
||||||
|
Reference in New Issue
Block a user