From b98fe3b77b6b422e5e5978f6cf82a11fb87aedfc Mon Sep 17 00:00:00 2001 From: Clay Gerrard Date: Tue, 11 Nov 2014 17:03:29 -0800 Subject: [PATCH] Prefer X-Backend-Timestamp for X-Newest When a X-Backend-Timestamp is available it would generally preferred over a less specific value and sorts correctly against any X-Timestamp values anyway. Change-Id: I08b7eb37ab8bd6eb3afbb7dee44ed07a8c69b57e --- swift/proxy/controllers/base.py | 5 +- test/unit/__init__.py | 26 +++++---- test/unit/proxy/controllers/test_obj.py | 70 +++++++++++++++++++++++++ test/unit/proxy/test_server.py | 2 + 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/swift/proxy/controllers/base.py b/swift/proxy/controllers/base.py index 728ec594de..a180b7ccd3 100644 --- a/swift/proxy/controllers/base.py +++ b/swift/proxy/controllers/base.py @@ -78,8 +78,9 @@ def source_key(resp): :param resp: bufferedhttp response object """ - return float(resp.getheader('x-put-timestamp') or - resp.getheader('x-timestamp') or 0) + return Timestamp(resp.getheader('x-backend-timestamp') or + resp.getheader('x-put-timestamp') or + resp.getheader('x-timestamp') or 0) def delay_denial(func): diff --git a/test/unit/__init__.py b/test/unit/__init__.py index b869c9a1e3..98f7c1ac6a 100644 --- a/test/unit/__init__.py +++ b/test/unit/__init__.py @@ -29,6 +29,7 @@ from eventlet.green import socket from tempfile import mkdtemp from shutil import rmtree from test import get_config +from swift.common import swob from swift.common.utils import config_true_value, LogAdapter from swift.common.ring import Ring, RingData from hashlib import md5 @@ -671,20 +672,23 @@ def fake_http_connect(*code_iter, **kwargs): else: etag = '"68b329da9893e34099c7d8ad5cb9c940"' - headers = {'content-length': len(self.body), - 'content-type': 'x-application/test', - 'x-timestamp': self.timestamp, - 'x-backend-timestamp': self.timestamp, - 'last-modified': self.timestamp, - 'x-object-meta-test': 'testing', - 'x-delete-at': '9876543210', - 'etag': etag, - 'x-works': 'yes'} + headers = swob.HeaderKeyDict({ + 'content-length': len(self.body), + 'content-type': 'x-application/test', + 'x-timestamp': self.timestamp, + 'x-backend-timestamp': self.timestamp, + 'last-modified': self.timestamp, + 'x-object-meta-test': 'testing', + 'x-delete-at': '9876543210', + 'etag': etag, + 'x-works': 'yes', + }) if self.status // 100 == 2: headers['x-account-container-count'] = \ kwargs.get('count', 12345) if not self.timestamp: - del headers['x-timestamp'] + # when timestamp is None, HeaderKeyDict raises KeyError + headers.pop('x-timestamp', None) try: if container_ts_iter.next() is False: headers['x-container-timestamp'] = '1' @@ -725,7 +729,7 @@ def fake_http_connect(*code_iter, **kwargs): sleep(value) 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)) etag_iter = iter(kwargs.get('etags') or [None] * len(code_iter)) diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py index afde6137f9..6d8c561f66 100755 --- a/test/unit/proxy/controllers/test_obj.py +++ b/test/unit/proxy/controllers/test_obj.py @@ -15,6 +15,7 @@ # limitations under the License. import itertools +import random import time import unittest from contextlib import contextmanager @@ -543,6 +544,75 @@ class TestObjController(unittest.TestCase): resp = req.get_response(self.app) 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): req = swift.common.swob.Request.blank('/v1/a/c/o', method='PUT') req.headers['x-copy-from'] = 'some/where' diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index b3b18a744d..09d6124b6d 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -2007,6 +2007,8 @@ class TestObjectController(unittest.TestCase): None, None), None) test_status_map((200, 200, 200, 200, 200), 200, ('0', '0', None, None, '1'), '1') + test_status_map((200, 200, 404, 404, 200), 200, ('0', '0', None, + None, '1'), '1') def test_GET_newest(self): with save_globals():