copy X-Delete-At unless X-Fresh-Metadata: true is supplied on an object copy
Current codes will copy metadata headers when x-fresh-metadata:false, we still need copy "x-delete-at" header and ensure expiring work at the same time. Change-Id: Ie31326b5f7b565e51e5aa249279bc1786f7bc847 Fixes: bug #1067528
This commit is contained in:
parent
5d52d2d1cc
commit
fef0f491ff
1
.mailmap
1
.mailmap
@ -42,3 +42,4 @@ David Hadas <davidh@il.ibm.com> <david.hadas@gmail.com>
|
|||||||
Yaguang Wang <yaguang.wang@intel.com> ywang19 <yaguang.wang@intel.com>
|
Yaguang Wang <yaguang.wang@intel.com> ywang19 <yaguang.wang@intel.com>
|
||||||
Liu Siqi <meizu647@gmail.com> dk647 <meizu647@gmail.com>
|
Liu Siqi <meizu647@gmail.com> dk647 <meizu647@gmail.com>
|
||||||
James E. Blair <jeblair@openstack.org> <james.blair@rackspace.com>
|
James E. Blair <jeblair@openstack.org> <james.blair@rackspace.com>
|
||||||
|
Kun Huang <gareth@unitedstack.com> <academicgareth@gmail.com>
|
||||||
|
@ -71,8 +71,9 @@ def copy_headers_into(from_r, to_r):
|
|||||||
:params from_r: a swob Request or Response
|
:params from_r: a swob Request or Response
|
||||||
:params to_r: a swob Request or Response
|
:params to_r: a swob Request or Response
|
||||||
"""
|
"""
|
||||||
|
pass_headers = ['x-delete-at']
|
||||||
for k, v in from_r.headers.items():
|
for k, v in from_r.headers.items():
|
||||||
if k.lower().startswith('x-object-meta-'):
|
if k.lower().startswith('x-object-meta-') or k.lower() in pass_headers:
|
||||||
to_r.headers[k] = v
|
to_r.headers[k] = v
|
||||||
|
|
||||||
|
|
||||||
@ -712,25 +713,6 @@ class ObjectController(Controller):
|
|||||||
content_type='text/plain',
|
content_type='text/plain',
|
||||||
body='Non-integer X-Delete-After')
|
body='Non-integer X-Delete-After')
|
||||||
req.headers['x-delete-at'] = '%d' % (time.time() + x_delete_after)
|
req.headers['x-delete-at'] = '%d' % (time.time() + x_delete_after)
|
||||||
if 'x-delete-at' in req.headers:
|
|
||||||
try:
|
|
||||||
x_delete_at = int(req.headers['x-delete-at'])
|
|
||||||
if x_delete_at < time.time():
|
|
||||||
return HTTPBadRequest(
|
|
||||||
body='X-Delete-At in past', request=req,
|
|
||||||
content_type='text/plain')
|
|
||||||
except ValueError:
|
|
||||||
return HTTPBadRequest(request=req, content_type='text/plain',
|
|
||||||
body='Non-integer X-Delete-At')
|
|
||||||
delete_at_container = str(
|
|
||||||
x_delete_at /
|
|
||||||
self.app.expiring_objects_container_divisor *
|
|
||||||
self.app.expiring_objects_container_divisor)
|
|
||||||
delete_at_part, delete_at_nodes = \
|
|
||||||
self.app.container_ring.get_nodes(
|
|
||||||
self.app.expiring_objects_account, delete_at_container)
|
|
||||||
else:
|
|
||||||
delete_at_part = delete_at_nodes = None
|
|
||||||
partition, nodes = self.app.object_ring.get_nodes(
|
partition, nodes = self.app.object_ring.get_nodes(
|
||||||
self.account_name, self.container_name, self.object_name)
|
self.account_name, self.container_name, self.object_name)
|
||||||
# do a HEAD request for container sync and checking object versions
|
# do a HEAD request for container sync and checking object versions
|
||||||
@ -863,6 +845,27 @@ class ObjectController(Controller):
|
|||||||
source_resp.headers['X-Static-Large-Object']
|
source_resp.headers['X-Static-Large-Object']
|
||||||
|
|
||||||
req = new_req
|
req = new_req
|
||||||
|
|
||||||
|
if 'x-delete-at' in req.headers:
|
||||||
|
try:
|
||||||
|
x_delete_at = int(req.headers['x-delete-at'])
|
||||||
|
if x_delete_at < time.time():
|
||||||
|
return HTTPBadRequest(
|
||||||
|
body='X-Delete-At in past', request=req,
|
||||||
|
content_type='text/plain')
|
||||||
|
except ValueError:
|
||||||
|
return HTTPBadRequest(request=req, content_type='text/plain',
|
||||||
|
body='Non-integer X-Delete-At')
|
||||||
|
delete_at_container = str(
|
||||||
|
x_delete_at /
|
||||||
|
self.app.expiring_objects_container_divisor *
|
||||||
|
self.app.expiring_objects_container_divisor)
|
||||||
|
delete_at_part, delete_at_nodes = \
|
||||||
|
self.app.container_ring.get_nodes(
|
||||||
|
self.app.expiring_objects_account, delete_at_container)
|
||||||
|
else:
|
||||||
|
delete_at_part = delete_at_nodes = None
|
||||||
|
|
||||||
node_iter = self.iter_nodes(self.app.object_ring, partition)
|
node_iter = self.iter_nodes(self.app.object_ring, partition)
|
||||||
pile = GreenPile(len(nodes))
|
pile = GreenPile(len(nodes))
|
||||||
chunked = req.headers.get('transfer-encoding')
|
chunked = req.headers.get('transfer-encoding')
|
||||||
|
@ -318,6 +318,7 @@ def fake_http_connect(*code_iter, **kwargs):
|
|||||||
'x-timestamp': self.timestamp,
|
'x-timestamp': self.timestamp,
|
||||||
'last-modified': self.timestamp,
|
'last-modified': self.timestamp,
|
||||||
'x-object-meta-test': 'testing',
|
'x-object-meta-test': 'testing',
|
||||||
|
'x-delete-at': '9876543210',
|
||||||
'etag': etag,
|
'etag': etag,
|
||||||
'x-works': 'yes',
|
'x-works': 'yes',
|
||||||
'x-account-container-count': kwargs.get('count', 12345)}
|
'x-account-container-count': kwargs.get('count', 12345)}
|
||||||
|
@ -2510,6 +2510,7 @@ class TestObjectController(unittest.TestCase):
|
|||||||
self.assertEquals(resp.headers.get('x-object-meta-test'),
|
self.assertEquals(resp.headers.get('x-object-meta-test'),
|
||||||
'testing')
|
'testing')
|
||||||
self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
|
self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
|
||||||
|
self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
|
||||||
|
|
||||||
# copy-from object is too large to fit in target object
|
# copy-from object is too large to fit in target object
|
||||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
|
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
|
||||||
@ -2641,6 +2642,7 @@ class TestObjectController(unittest.TestCase):
|
|||||||
self.assertEquals(resp.headers.get('x-object-meta-test'),
|
self.assertEquals(resp.headers.get('x-object-meta-test'),
|
||||||
'testing')
|
'testing')
|
||||||
self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
|
self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay')
|
||||||
|
self.assertEquals(resp.headers.get('x-delete-at'), '9876543210')
|
||||||
|
|
||||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'COPY'},
|
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'COPY'},
|
||||||
headers={'Destination': '/c/o'})
|
headers={'Destination': '/c/o'})
|
||||||
@ -2678,6 +2680,29 @@ class TestObjectController(unittest.TestCase):
|
|||||||
self.assertEquals(resp.headers['x-copied-from-last-modified'],
|
self.assertEquals(resp.headers['x-copied-from-last-modified'],
|
||||||
'3')
|
'3')
|
||||||
|
|
||||||
|
def test_COPY_delete_at(self):
|
||||||
|
with save_globals():
|
||||||
|
given_headers = {}
|
||||||
|
|
||||||
|
def fake_connect_put_node(nodes, part, path, headers,
|
||||||
|
logger_thread_locals):
|
||||||
|
given_headers.update(headers)
|
||||||
|
|
||||||
|
controller = proxy_server.ObjectController(self.app, 'a',
|
||||||
|
'c', 'o')
|
||||||
|
controller._connect_put_node = fake_connect_put_node
|
||||||
|
set_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||||
|
self.app.memcache.store = {}
|
||||||
|
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'COPY'},
|
||||||
|
headers={'Destination': '/c/o'})
|
||||||
|
|
||||||
|
self.app.update_request(req)
|
||||||
|
controller.COPY(req)
|
||||||
|
self.assertEquals(given_headers.get('X-Delete-At'), '9876543210')
|
||||||
|
self.assertTrue('X-Delete-At-Host' in given_headers)
|
||||||
|
self.assertTrue('X-Delete-At-Device' in given_headers)
|
||||||
|
self.assertTrue('X-Delete-At-Partition' in given_headers)
|
||||||
|
|
||||||
def test_chunked_put(self):
|
def test_chunked_put(self):
|
||||||
|
|
||||||
class ChunkedFile():
|
class ChunkedFile():
|
||||||
|
Loading…
Reference in New Issue
Block a user