Stop using email.utils.mktime_tz
Prior to Python 2.7.4 [1], this would convert the input to a local timestamp, then adjust for both the local timezone and the timezone of the input. Ordinarily, this would be fine (excluding, apparently, some "argument out of range" issues on Windows). However, Swift (since v1.9) manually adjusts the TZ environment variable, apparently with the intention of making the timezone static and avoiding extra overhead from checking /etc/timezone for changes. In practice this sets TZ to "+0000" which has the effect of setting the timezone to UTC (at least for some functions, such as time.localtime and time.mktime) while *not* changing the offset stored in time.timezone. This, in turn, causes email.utils.mktime_tz to produce bad timestamps and swift3 to reject requests with RequestTimeTooSkewed errors if the server was not in UTC. Now, we'll avoid local timestamps by using calendar.timegm ourselves, essentially inlining the upstream Python fix. Note that Ubuntu Precise provides Python 2.7.3 (and thus *is* affected); neither Ubuntu Trusty (which provides 2.7.6) nor CentOS 7 (which provides 2.7.5) is affected. [1] https://hg.python.org/cpython/rev/a283563c8cc4 Change-Id: Iee7488d03ab404072d3d0c1a262f004bb0f2da26 Related-Change: I007425301914144e228b9cfece5533443e851b6e Related-Change: Ifc78236a99ed193a42389e383d062b38f57a5a31 Related-Change: I8ec80202789707f723abfe93ccc9cf1e677e4dc6 Closes-Bug: 1593863
This commit is contained in:
parent
6c12c58867
commit
f12274138a
@ -13,10 +13,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
import mock
|
||||
|
||||
from swift3 import utils
|
||||
from swift3 import utils, request
|
||||
|
||||
strs = [
|
||||
('Owner', 'owner'),
|
||||
@ -97,6 +99,38 @@ class TestSwift3Utils(unittest.TestCase):
|
||||
ts = utils.S3Timestamp(1.9)
|
||||
self.assertEqual(expected, ts.s3xmlformat)
|
||||
|
||||
def test_mktime(self):
|
||||
date_headers = [
|
||||
'Thu, 01 Jan 1970 00:00:00 -0000',
|
||||
'Thu, 01 Jan 1970 00:00:00 GMT',
|
||||
'Thu, 01 Jan 1970 00:00:00 UTC',
|
||||
'Thu, 01 Jan 1970 08:00:00 +0800',
|
||||
'Wed, 31 Dec 1969 16:00:00 -0800',
|
||||
'Wed, 31 Dec 1969 16:00:00 PST',
|
||||
]
|
||||
for header in date_headers:
|
||||
ts = utils.mktime(header)
|
||||
self.assertEqual(0, ts, 'Got %r for header %s' % (ts, header))
|
||||
|
||||
# Last-Modified response style
|
||||
self.assertEqual(0, utils.mktime('1970-01-01T00:00:00'))
|
||||
|
||||
# X-Amz-Date style
|
||||
self.assertEqual(0, utils.mktime('19700101T000000Z',
|
||||
request.SIGV4_X_AMZ_DATE_FORMAT))
|
||||
|
||||
def test_mktime_weird_tz(self):
|
||||
orig_tz = os.environ.get('TZ', '')
|
||||
try:
|
||||
os.environ['TZ'] = 'EST+05EDT,M4.1.0,M10.5.0'
|
||||
time.tzset()
|
||||
os.environ['TZ'] = '+0000'
|
||||
# No tzset! Simulating what Swift would do.
|
||||
self.assertNotEqual(0, time.timezone)
|
||||
self.test_mktime()
|
||||
finally:
|
||||
os.environ['TZ'] = orig_tz
|
||||
time.tzset()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -13,19 +13,20 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import re
|
||||
import uuid
|
||||
import base64
|
||||
import calendar
|
||||
import email.utils
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
from urllib import unquote
|
||||
import uuid
|
||||
|
||||
from swift.common.utils import get_logger
|
||||
import email.utils
|
||||
|
||||
# Need for check_path_header
|
||||
from swift.common import utils
|
||||
from swift.common.swob import HTTPPreconditionFailed
|
||||
from urllib import unquote
|
||||
|
||||
from swift3.cfg import CONF
|
||||
|
||||
@ -186,14 +187,17 @@ def mktime(timestamp_str, time_format='%Y-%m-%dT%H:%M:%S'):
|
||||
:param time_format: a string of format to parse in (b) process
|
||||
:return : a float instance in epoch time
|
||||
"""
|
||||
try:
|
||||
epoch_time = email.utils.mktime_tz(
|
||||
email.utils.parsedate_tz(timestamp_str))
|
||||
except TypeError:
|
||||
# time_tuple is the *remote* local time
|
||||
time_tuple = email.utils.parsedate_tz(timestamp_str)
|
||||
if time_tuple is None:
|
||||
time_tuple = time.strptime(timestamp_str, time_format)
|
||||
|
||||
# add timezone info as utc (no time difference)
|
||||
time_tuple += (0, )
|
||||
epoch_time = email.utils.mktime_tz(time_tuple)
|
||||
|
||||
# We prefer calendar.gmtime and a manual adjustment over
|
||||
# email.utils.mktime_tz because older versions of Python (<2.7.4) may
|
||||
# double-adjust for timezone in some situations (such when swift changes
|
||||
# os.environ['TZ'] without calling time.tzset()).
|
||||
epoch_time = calendar.timegm(time_tuple) - time_tuple[9]
|
||||
|
||||
return epoch_time
|
||||
|
Loading…
x
Reference in New Issue
Block a user