From 0f8319dceb2d21a9d1ec74f78203b033f25c0969 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Fri, 22 Jan 2016 13:31:34 -0800 Subject: [PATCH] Use calendar.timegm not time.mktime time.mktime produces a POSIX timestamp that takes into account the local timezone, but we're parsing a Last-Modified header with a known (GMT) timezone. As a result, if one proxy-server is configured with a timezone other than GMT/UTC, we may have previous versions in the wrong order. Change-Id: I320e2368b243f4245725e73bfabc7ad19bc5bacb --- swift/common/middleware/versioned_writes.py | 7 +++-- .../middleware/test_versioned_writes.py | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/swift/common/middleware/versioned_writes.py b/swift/common/middleware/versioned_writes.py index cc5830fdb3..94542043f2 100644 --- a/swift/common/middleware/versioned_writes.py +++ b/swift/common/middleware/versioned_writes.py @@ -115,6 +115,7 @@ Disable versioning from a container (x is any value except empty):: -H "X-Remove-Versions-Location: x" http:///container """ +import calendar import json import six from six.moves.urllib.parse import quote, unquote @@ -209,9 +210,9 @@ class VersionedWritesContext(WSGIContext): lprefix = prefix_len + object_name + '/' ts_source = hresp.environ.get('swift_x_timestamp') if ts_source is None: - ts_source = time.mktime(time.strptime( - hresp.headers['last-modified'], - '%a, %d %b %Y %H:%M:%S GMT')) + ts_source = calendar.timegm(time.strptime( + hresp.headers['last-modified'], + '%a, %d %b %Y %H:%M:%S GMT')) new_ts = Timestamp(ts_source).internal vers_obj_name = lprefix + new_ts copy_headers = { diff --git a/test/unit/common/middleware/test_versioned_writes.py b/test/unit/common/middleware/test_versioned_writes.py index 87f2c525d4..64a31a6705 100644 --- a/test/unit/common/middleware/test_versioned_writes.py +++ b/test/unit/common/middleware/test_versioned_writes.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import functools +import os +import time import unittest from swift.common import swob from swift.common.middleware import versioned_writes @@ -31,6 +34,26 @@ class FakeCache(object): return self.val +def local_tz(func): + ''' + Decorator to change the timezone when running a test. + + This uses the Eastern Time Zone definition from the time module's docs. + Note that the timezone affects things like time.time() and time.mktime(). + ''' + @functools.wraps(func) + def wrapper(*args, **kwargs): + tz = os.environ.get('TZ', '') + try: + os.environ['TZ'] = 'EST+05EDT,M4.1.0,M10.5.0' + time.tzset() + return func(*args, **kwargs) + finally: + os.environ['TZ'] = tz + time.tzset() + return wrapper + + class VersionedWritesTestCase(unittest.TestCase): def setUp(self): self.app = FakeSwift() @@ -327,12 +350,13 @@ class VersionedWritesTestCase(unittest.TestCase): self.assertEqual(len(self.authorized), 1) self.assertRequestEqual(req, self.authorized[0]) + @local_tz def test_new_version_sysmeta_precedence(self): self.app.register( 'PUT', '/v1/a/c/o', swob.HTTPOk, {}, 'passed') self.app.register( 'HEAD', '/v1/a/c/o', swob.HTTPOk, - {'last-modified': 'Wed, 19 Nov 2014 18:19:02 GMT'}, 'passed') + {'last-modified': 'Thu, 1 Jan 1970 00:00:00 GMT'}, 'passed') self.app.register( 'COPY', '/v1/a/c/o', swob.HTTPCreated, {}, None) @@ -354,7 +378,8 @@ class VersionedWritesTestCase(unittest.TestCase): method, path, req_headers = calls[1] self.assertEqual('COPY', method) self.assertEqual('/v1/a/c/o', path) - self.assertTrue(req_headers['Destination'].startswith('ver_cont/')) + self.assertEqual('ver_cont/001o/0000000000.00000', + req_headers['Destination']) def test_copy_first_version(self): self.app.register(