Merge "Fix X-*-Container-Update-Override-* header/footer precedence"
This commit is contained in:
commit
82c342c88e
@ -447,7 +447,8 @@ class ObjectController(BaseStorageServer):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise HTTPBadRequest("invalid JSON for footer doc")
|
raise HTTPBadRequest("invalid JSON for footer doc")
|
||||||
|
|
||||||
def _check_container_override(self, update_headers, metadata):
|
def _check_container_override(self, update_headers, metadata,
|
||||||
|
footers=None):
|
||||||
"""
|
"""
|
||||||
Applies any overrides to the container update headers.
|
Applies any overrides to the container update headers.
|
||||||
|
|
||||||
@ -463,7 +464,10 @@ class ObjectController(BaseStorageServer):
|
|||||||
|
|
||||||
:param update_headers: a dict of headers used in the container update
|
:param update_headers: a dict of headers used in the container update
|
||||||
:param metadata: a dict that may container override items
|
:param metadata: a dict that may container override items
|
||||||
|
:param footers: another dict that may container override items, at a
|
||||||
|
higher priority than metadata
|
||||||
"""
|
"""
|
||||||
|
footers = footers or {}
|
||||||
# the order of this list is significant:
|
# the order of this list is significant:
|
||||||
# x-object-sysmeta-container-update-override-* headers take precedence
|
# x-object-sysmeta-container-update-override-* headers take precedence
|
||||||
# over x-backend-container-update-override-* headers
|
# over x-backend-container-update-override-* headers
|
||||||
@ -474,6 +478,12 @@ class ObjectController(BaseStorageServer):
|
|||||||
if key.lower().startswith(override_prefix):
|
if key.lower().startswith(override_prefix):
|
||||||
override = key.lower().replace(override_prefix, 'x-')
|
override = key.lower().replace(override_prefix, 'x-')
|
||||||
update_headers[override] = val
|
update_headers[override] = val
|
||||||
|
# apply x-backend-container-update-override* from footers *before*
|
||||||
|
# x-object-sysmeta-container-update-override-* from headers
|
||||||
|
for key, val in footers.items():
|
||||||
|
if key.lower().startswith(override_prefix):
|
||||||
|
override = key.lower().replace(override_prefix, 'x-')
|
||||||
|
update_headers[override] = val
|
||||||
|
|
||||||
def _preserve_slo_manifest(self, update_metadata, orig_metadata):
|
def _preserve_slo_manifest(self, update_metadata, orig_metadata):
|
||||||
if 'X-Static-Large-Object' in orig_metadata:
|
if 'X-Static-Large-Object' in orig_metadata:
|
||||||
@ -829,8 +839,8 @@ class ObjectController(BaseStorageServer):
|
|||||||
'x-timestamp': metadata['X-Timestamp'],
|
'x-timestamp': metadata['X-Timestamp'],
|
||||||
'x-etag': metadata['ETag']})
|
'x-etag': metadata['ETag']})
|
||||||
# apply any container update header overrides sent with request
|
# apply any container update header overrides sent with request
|
||||||
self._check_container_override(update_headers, request.headers)
|
self._check_container_override(update_headers, request.headers,
|
||||||
self._check_container_override(update_headers, footer_meta)
|
footer_meta)
|
||||||
self.container_update(
|
self.container_update(
|
||||||
'PUT', account, container, obj, request,
|
'PUT', account, container, obj, request,
|
||||||
update_headers,
|
update_headers,
|
||||||
|
@ -1435,6 +1435,86 @@ class TestObjectController(unittest.TestCase):
|
|||||||
with open(objfile) as fh:
|
with open(objfile) as fh:
|
||||||
self.assertEqual(fh.read(), "obj data")
|
self.assertEqual(fh.read(), "obj data")
|
||||||
|
|
||||||
|
def test_PUT_container_override_etag_in_footer(self):
|
||||||
|
ts_iter = make_timestamp_iter()
|
||||||
|
|
||||||
|
def do_test(override_headers, override_footers):
|
||||||
|
def mock_container_update(ctlr, op, account, container, obj, req,
|
||||||
|
headers_out, objdevice, policy):
|
||||||
|
calls_made.append((headers_out, policy))
|
||||||
|
calls_made = []
|
||||||
|
ts_put = next(ts_iter)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'X-Timestamp': ts_put.internal,
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
'Etag': 'other-etag',
|
||||||
|
'X-Backend-Obj-Metadata-Footer': 'yes',
|
||||||
|
'X-Backend-Obj-Multipart-Mime-Boundary': 'boundary'}
|
||||||
|
headers.update(override_headers)
|
||||||
|
req = Request.blank(
|
||||||
|
'/sda1/p/a/c/o', headers=headers,
|
||||||
|
environ={'REQUEST_METHOD': 'PUT'})
|
||||||
|
|
||||||
|
obj_etag = md5("obj data").hexdigest()
|
||||||
|
footers = {'Etag': obj_etag}
|
||||||
|
footers.update(override_footers)
|
||||||
|
footer_meta = json.dumps(footers)
|
||||||
|
footer_meta_cksum = md5(footer_meta).hexdigest()
|
||||||
|
|
||||||
|
req.body = "\r\n".join((
|
||||||
|
"--boundary",
|
||||||
|
"",
|
||||||
|
"obj data",
|
||||||
|
"--boundary",
|
||||||
|
"Content-MD5: " + footer_meta_cksum,
|
||||||
|
"",
|
||||||
|
footer_meta,
|
||||||
|
"--boundary--",
|
||||||
|
))
|
||||||
|
req.headers.pop("Content-Length", None)
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
'swift.obj.server.ObjectController.container_update',
|
||||||
|
mock_container_update):
|
||||||
|
resp = req.get_response(self.object_controller)
|
||||||
|
self.assertEqual(resp.etag, obj_etag)
|
||||||
|
self.assertEqual(resp.status_int, 201)
|
||||||
|
self.assertEqual(1, len(calls_made))
|
||||||
|
self.assertEqual({
|
||||||
|
'X-Size': str(len('obj data')),
|
||||||
|
'X-Etag': 'update-etag',
|
||||||
|
'X-Content-Type': 'text/plain',
|
||||||
|
'X-Timestamp': ts_put.internal,
|
||||||
|
}, calls_made[0][0])
|
||||||
|
self.assertEqual(POLICIES[0], calls_made[0][1])
|
||||||
|
|
||||||
|
# lone headers/footers work
|
||||||
|
do_test({'X-Backend-Container-Update-Override-Etag': 'update-etag'},
|
||||||
|
{})
|
||||||
|
do_test({},
|
||||||
|
{'X-Backend-Container-Update-Override-Etag': 'update-etag'})
|
||||||
|
do_test({'X-Object-Sysmeta-Container-Update-Override-Etag':
|
||||||
|
'update-etag'},
|
||||||
|
{})
|
||||||
|
do_test({},
|
||||||
|
{'X-Object-Sysmeta-Container-Update-Override-Etag':
|
||||||
|
'update-etag'})
|
||||||
|
|
||||||
|
# footer trumps header
|
||||||
|
do_test({'X-Backend-Container-Update-Override-Etag': 'ignored-etag'},
|
||||||
|
{'X-Backend-Container-Update-Override-Etag': 'update-etag'})
|
||||||
|
do_test({'X-Object-Sysmeta-Container-Update-Override-Etag':
|
||||||
|
'ignored-etag'},
|
||||||
|
{'X-Object-Sysmeta-Container-Update-Override-Etag':
|
||||||
|
'update-etag'})
|
||||||
|
|
||||||
|
# but sysmeta header trumps backend footer
|
||||||
|
do_test({'X-Object-Sysmeta-Container-Update-Override-Etag':
|
||||||
|
'update-etag'},
|
||||||
|
{'X-Backend-Container-Update-Override-Etag': 'ignored-etag'})
|
||||||
|
|
||||||
def test_PUT_etag_in_footer_mismatch(self):
|
def test_PUT_etag_in_footer_mismatch(self):
|
||||||
timestamp = normalize_timestamp(time())
|
timestamp = normalize_timestamp(time())
|
||||||
req = Request.blank(
|
req = Request.blank(
|
||||||
|
Loading…
Reference in New Issue
Block a user