Merge "Last-Modified header support on HEAD/GET container"

This commit is contained in:
Jenkins 2016-08-11 14:44:12 +00:00 committed by Gerrit Code Review
commit 9d29ca1c76
5 changed files with 84 additions and 8 deletions

View File

@ -17,6 +17,7 @@ import json
import os
import time
import traceback
import math
from swift import gettext_ as _
from xml.etree.cElementTree import Element, SubElement, tostring
@ -433,7 +434,9 @@ class ContainerController(BaseStorageServer):
if value != '' and (key.lower() in self.save_headers or
is_sys_or_user_meta('container', key)))
headers['Content-Type'] = out_content_type
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
resp = HTTPNoContent(request=req, headers=headers, charset='utf-8')
resp.last_modified = math.ceil(float(headers['X-PUT-Timestamp']))
return resp
def update_data_record(self, record):
"""
@ -530,6 +533,7 @@ class ContainerController(BaseStorageServer):
if not container_list:
return HTTPNoContent(request=req, headers=resp_headers)
ret.body = '\n'.join(rec[0] for rec in container_list) + '\n'
ret.last_modified = math.ceil(float(resp_headers['X-PUT-Timestamp']))
return ret
@public

View File

@ -625,7 +625,8 @@ class Container(Base):
if self.conn.response.status == 204:
required_fields = [['bytes_used', 'x-container-bytes-used'],
['object_count', 'x-container-object-count']]
['object_count', 'x-container-object-count'],
['last_modified', 'last-modified']]
optional_fields = [
['versions', 'x-versions-location'],
['tempurl_key', 'x-container-meta-temp-url-key'],

View File

@ -851,6 +851,45 @@ class TestContainer(Base):
file_item = cont.file(Utils.create_name())
file_item.write_random()
def testContainerLastModified(self):
container = self.env.account.container(Utils.create_name())
self.assertTrue(container.create())
info = container.info()
t0 = info['last_modified']
# last modified header is in date format which supports in second
# so we need to wait to increment a sec in the header.
eventlet.sleep(1)
# POST container change last modified timestamp
self.assertTrue(
container.update_metadata({'x-container-meta-japan': 'mitaka'}))
info = container.info()
t1 = info['last_modified']
self.assertNotEqual(t0, t1)
eventlet.sleep(1)
# PUT container (overwrite) also change last modified
self.assertTrue(container.create())
info = container.info()
t2 = info['last_modified']
self.assertNotEqual(t1, t2)
eventlet.sleep(1)
# PUT object doesn't change container last modified timestamp
obj = container.file(Utils.create_name())
self.assertTrue(
obj.write("aaaaa", hdrs={'Content-Type': 'text/plain'}))
info = container.info()
t3 = info['last_modified']
self.assertEqual(t2, t3)
# POST object also doesn't change container last modified timestamp
self.assertTrue(
obj.sync_metadata({'us': 'austin'}))
info = container.info()
t4 = info['last_modified']
self.assertEqual(t2, t4)
class TestContainerUTF8(Base2, TestContainer):
set_up = False

View File

@ -180,12 +180,7 @@ class TestContainerController(unittest.TestCase):
self.assertEqual(response.headers.get('x-container-write'),
'account:user')
def test_HEAD(self):
start = int(time.time())
ts = (Timestamp(t).internal for t in itertools.count(start))
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
'x-timestamp': next(ts)})
req.get_response(self.controller)
def _test_head(self, start, ts):
req = Request.blank('/sda1/p/a/c', method='HEAD')
response = req.get_response(self.controller)
self.assertEqual(response.status_int, 204)
@ -213,6 +208,9 @@ class TestContainerController(unittest.TestCase):
self.assertTrue(created_at_header >= start)
self.assertEqual(response.headers['x-put-timestamp'],
Timestamp(start).normal)
self.assertEqual(
response.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(start)))
# backend headers
self.assertEqual(int(response.headers
@ -227,6 +225,22 @@ class TestContainerController(unittest.TestCase):
self.assertEqual(response.headers['x-backend-status-changed-at'],
Timestamp(start).internal)
def test_HEAD(self):
start = int(time.time())
ts = (Timestamp(t).internal for t in itertools.count(start))
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
'x-timestamp': next(ts)})
req.get_response(self.controller)
self._test_head(Timestamp(start), ts)
def test_HEAD_timestamp_with_offset(self):
start = int(time.time())
ts = (Timestamp(t, offset=1).internal for t in itertools.count(start))
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
'x-timestamp': next(ts)})
req.get_response(self.controller)
self._test_head(Timestamp(start, offset=1), ts)
def test_HEAD_not_found(self):
req = Request.blank('/sda1/p/a/c', method='HEAD')
resp = req.get_response(self.controller)
@ -241,6 +255,8 @@ class TestContainerController(unittest.TestCase):
Timestamp(0).internal)
self.assertEqual(resp.headers['x-backend-delete-timestamp'],
Timestamp(0).internal)
self.assertIsNone(resp.last_modified)
for header in ('x-container-object-count', 'x-container-bytes-used',
'x-timestamp', 'x-put-timestamp'):
self.assertEqual(resp.headers[header], None)
@ -264,6 +280,7 @@ class TestContainerController(unittest.TestCase):
req = Request.blank('/sda1/p/a/c', method=method)
resp = req.get_response(self.controller)
self.assertEqual(resp.status_int, 404)
self.assertIsNone(resp.last_modified)
# backend headers
self.assertEqual(int(resp.headers[
'X-Backend-Storage-Policy-Index']),
@ -2021,6 +2038,9 @@ class TestContainerController(unittest.TestCase):
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.content_type, 'application/json')
self.assertEqual(
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
self.assertEqual(json.loads(resp.body), json_body)
self.assertEqual(resp.charset, 'utf-8')
@ -2082,6 +2102,9 @@ class TestContainerController(unittest.TestCase):
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.content_type, 'text/plain')
self.assertEqual(
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
self.assertEqual(resp.body, plain_body)
self.assertEqual(resp.charset, 'utf-8')
@ -2212,6 +2235,9 @@ class TestContainerController(unittest.TestCase):
environ={'REQUEST_METHOD': 'GET'})
resp = req.get_response(self.controller)
self.assertEqual(resp.content_type, 'application/xml')
self.assertEqual(
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
self.assertEqual(resp.body, xml_body)
self.assertEqual(resp.charset, 'utf-8')

View File

@ -6229,6 +6229,9 @@ class TestContainerController(unittest.TestCase):
if expected < 400:
self.assertIn('x-works', res.headers)
self.assertEqual(res.headers['x-works'], 'yes')
if expected < 300:
self.assertIn('last-modified', res.headers)
self.assertEqual(res.headers['last-modified'], '1')
if c_expected:
self.assertIn('container/a/c', infocache)
self.assertEqual(
@ -6254,6 +6257,9 @@ class TestContainerController(unittest.TestCase):
if expected < 400:
self.assertTrue('x-works' in res.headers)
self.assertEqual(res.headers['x-works'], 'yes')
if expected < 300:
self.assertIn('last-modified', res.headers)
self.assertEqual(res.headers['last-modified'], '1')
if c_expected:
self.assertIn('container/a/c', infocache)
self.assertEqual(