Merge "Remove confusable query string on post as copy"
This commit is contained in:
@@ -549,7 +549,8 @@ class StaticLargeObject(object):
|
|||||||
def slo_hook(source_req, source_resp, sink_req):
|
def slo_hook(source_req, source_resp, sink_req):
|
||||||
x_slo = source_resp.headers.get('X-Static-Large-Object')
|
x_slo = source_resp.headers.get('X-Static-Large-Object')
|
||||||
if (config_true_value(x_slo)
|
if (config_true_value(x_slo)
|
||||||
and source_req.params.get('multipart-manifest') != 'get'):
|
and source_req.params.get('multipart-manifest') != 'get'
|
||||||
|
and 'swift.post_as_copy' not in source_req.environ):
|
||||||
source_resp = SloGetContext(self).get_or_head_response(
|
source_resp = SloGetContext(self).get_or_head_response(
|
||||||
source_req, source_resp.headers.items(),
|
source_req, source_resp.headers.items(),
|
||||||
source_resp.app_iter)
|
source_resp.app_iter)
|
||||||
|
@@ -268,12 +268,8 @@ class BaseObjectController(Controller):
|
|||||||
req.headers['Content-Length'] = 0
|
req.headers['Content-Length'] = 0
|
||||||
req.headers['X-Copy-From'] = quote('/%s/%s' % (self.container_name,
|
req.headers['X-Copy-From'] = quote('/%s/%s' % (self.container_name,
|
||||||
self.object_name))
|
self.object_name))
|
||||||
req.headers['X-Fresh-Metadata'] = 'true'
|
req.environ['swift.post_as_copy'] = True
|
||||||
req.environ['swift_versioned_copy'] = True
|
req.environ['swift_versioned_copy'] = True
|
||||||
if req.environ.get('QUERY_STRING'):
|
|
||||||
req.environ['QUERY_STRING'] += '&multipart-manifest=get'
|
|
||||||
else:
|
|
||||||
req.environ['QUERY_STRING'] = 'multipart-manifest=get'
|
|
||||||
resp = self.PUT(req)
|
resp = self.PUT(req)
|
||||||
# Older editions returned 202 Accepted on object POSTs, so we'll
|
# Older editions returned 202 Accepted on object POSTs, so we'll
|
||||||
# convert any 201 Created responses to that for compatibility with
|
# convert any 201 Created responses to that for compatibility with
|
||||||
@@ -577,8 +573,11 @@ class BaseObjectController(Controller):
|
|||||||
if not req.content_type_manually_set:
|
if not req.content_type_manually_set:
|
||||||
sink_req.headers['Content-Type'] = \
|
sink_req.headers['Content-Type'] = \
|
||||||
source_resp.headers['Content-Type']
|
source_resp.headers['Content-Type']
|
||||||
if config_true_value(
|
|
||||||
sink_req.headers.get('x-fresh-metadata', 'false')):
|
fresh_meta_flag = config_true_value(
|
||||||
|
sink_req.headers.get('x-fresh-metadata', 'false'))
|
||||||
|
|
||||||
|
if fresh_meta_flag or 'swift.post_as_copy' in sink_req.environ:
|
||||||
# post-as-copy: ignore new sysmeta, copy existing sysmeta
|
# post-as-copy: ignore new sysmeta, copy existing sysmeta
|
||||||
condition = lambda k: is_sys_meta('object', k)
|
condition = lambda k: is_sys_meta('object', k)
|
||||||
remove_items(sink_req.headers, condition)
|
remove_items(sink_req.headers, condition)
|
||||||
@@ -590,7 +589,8 @@ class BaseObjectController(Controller):
|
|||||||
|
|
||||||
# copy over x-static-large-object for POSTs and manifest copies
|
# copy over x-static-large-object for POSTs and manifest copies
|
||||||
if 'X-Static-Large-Object' in source_resp.headers and \
|
if 'X-Static-Large-Object' in source_resp.headers and \
|
||||||
req.params.get('multipart-manifest') == 'get':
|
(req.params.get('multipart-manifest') == 'get' or
|
||||||
|
'swift.post_as_copy' in req.environ):
|
||||||
sink_req.headers['X-Static-Large-Object'] = \
|
sink_req.headers['X-Static-Large-Object'] = \
|
||||||
source_resp.headers['X-Static-Large-Object']
|
source_resp.headers['X-Static-Large-Object']
|
||||||
|
|
||||||
|
@@ -851,7 +851,7 @@ class File(Base):
|
|||||||
finally:
|
finally:
|
||||||
fobj.close()
|
fobj.close()
|
||||||
|
|
||||||
def sync_metadata(self, metadata=None, cfg=None):
|
def sync_metadata(self, metadata=None, cfg=None, parms=None):
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
metadata = {}
|
metadata = {}
|
||||||
if cfg is None:
|
if cfg is None:
|
||||||
@@ -868,7 +868,8 @@ class File(Base):
|
|||||||
else:
|
else:
|
||||||
headers['Content-Length'] = 0
|
headers['Content-Length'] = 0
|
||||||
|
|
||||||
self.conn.make_request('POST', self.path, hdrs=headers, cfg=cfg)
|
self.conn.make_request('POST', self.path, hdrs=headers,
|
||||||
|
parms=parms, cfg=cfg)
|
||||||
|
|
||||||
if self.conn.response.status not in (201, 202):
|
if self.conn.response.status not in (201, 202):
|
||||||
raise ResponseError(self.conn.response, 'POST',
|
raise ResponseError(self.conn.response, 'POST',
|
||||||
|
@@ -2151,6 +2151,7 @@ class TestSloEnv(object):
|
|||||||
'manifest-bcd-submanifest')},
|
'manifest-bcd-submanifest')},
|
||||||
seg_info['seg_e']]),
|
seg_info['seg_e']]),
|
||||||
parms={'multipart-manifest': 'put'})
|
parms={'multipart-manifest': 'put'})
|
||||||
|
cls.seg_info = seg_info
|
||||||
|
|
||||||
file_item = cls.container.file("manifest-db")
|
file_item = cls.container.file("manifest-db")
|
||||||
file_item.write(
|
file_item.write(
|
||||||
@@ -2411,6 +2412,58 @@ class TestSlo(Base):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("COPY didn't copy the manifest (invalid json on GET)")
|
self.fail("COPY didn't copy the manifest (invalid json on GET)")
|
||||||
|
|
||||||
|
def _make_manifest(self):
|
||||||
|
# To avoid the bug 1453807 on fast-post, make a new manifest
|
||||||
|
# for post test.
|
||||||
|
file_item = self.env.container.file("manifest-post")
|
||||||
|
seg_info = self.env.seg_info
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([seg_info['seg_a'], seg_info['seg_b'],
|
||||||
|
seg_info['seg_c'], seg_info['seg_d'],
|
||||||
|
seg_info['seg_e']]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
return file_item
|
||||||
|
|
||||||
|
def test_slo_post_the_manifest_metadata_update(self):
|
||||||
|
file_item = self._make_manifest()
|
||||||
|
# sanity check, check the object is an SLO manifest
|
||||||
|
file_item.info()
|
||||||
|
file_item.header_fields([('slo', 'x-static-large-object')])
|
||||||
|
|
||||||
|
# POST a user metadata (i.e. x-object-meta-post)
|
||||||
|
file_item.sync_metadata({'post': 'update'})
|
||||||
|
|
||||||
|
updated = self.env.container.file("manifest-post")
|
||||||
|
updated.info()
|
||||||
|
updated.header_fields([('user-meta', 'x-object-meta-post')]) # sanity
|
||||||
|
updated_contents = updated.read(parms={'multipart-manifest': 'get'})
|
||||||
|
try:
|
||||||
|
json.loads(updated_contents)
|
||||||
|
except ValueError:
|
||||||
|
self.fail("Unexpected content on GET, expected a json body")
|
||||||
|
|
||||||
|
def test_slo_post_the_manifest_metadata_update_with_qs(self):
|
||||||
|
# multipart-manifest query should be ignored on post
|
||||||
|
for verb in ('put', 'get', 'delete'):
|
||||||
|
file_item = self._make_manifest()
|
||||||
|
# sanity check, check the object is an SLO manifest
|
||||||
|
file_item.info()
|
||||||
|
file_item.header_fields([('slo', 'x-static-large-object')])
|
||||||
|
# POST a user metadata (i.e. x-object-meta-post)
|
||||||
|
file_item.sync_metadata(metadata={'post': 'update'},
|
||||||
|
parms={'multipart-manifest': verb})
|
||||||
|
updated = self.env.container.file("manifest-post")
|
||||||
|
updated.info()
|
||||||
|
updated.header_fields(
|
||||||
|
[('user-meta', 'x-object-meta-post')]) # sanity
|
||||||
|
updated_contents = updated.read(
|
||||||
|
parms={'multipart-manifest': 'get'})
|
||||||
|
try:
|
||||||
|
json.loads(updated_contents)
|
||||||
|
except ValueError:
|
||||||
|
self.fail(
|
||||||
|
"Unexpected content on GET, expected a json body")
|
||||||
|
|
||||||
def test_slo_get_the_manifest(self):
|
def test_slo_get_the_manifest(self):
|
||||||
manifest = self.env.container.file("manifest-abcde")
|
manifest = self.env.container.file("manifest-abcde")
|
||||||
got_body = manifest.read(parms={'multipart-manifest': 'get'})
|
got_body = manifest.read(parms={'multipart-manifest': 'get'})
|
||||||
|
@@ -598,13 +598,31 @@ class TestReplicatedObjController(BaseObjectControllerMixin,
|
|||||||
|
|
||||||
def test_POST_as_COPY_simple(self):
|
def test_POST_as_COPY_simple(self):
|
||||||
req = swift.common.swob.Request.blank('/v1/a/c/o', method='POST')
|
req = swift.common.swob.Request.blank('/v1/a/c/o', method='POST')
|
||||||
head_resp = [200] * self.obj_ring.replicas + \
|
get_resp = [200] * self.obj_ring.replicas + \
|
||||||
[404] * self.obj_ring.max_more_nodes
|
[404] * self.obj_ring.max_more_nodes
|
||||||
put_resp = [201] * self.obj_ring.replicas
|
put_resp = [201] * self.obj_ring.replicas
|
||||||
codes = head_resp + put_resp
|
codes = get_resp + put_resp
|
||||||
with set_http_connect(*codes):
|
with set_http_connect(*codes):
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
self.assertEquals(resp.status_int, 202)
|
self.assertEquals(resp.status_int, 202)
|
||||||
|
self.assertEquals(req.environ['QUERY_STRING'], '')
|
||||||
|
self.assertTrue('swift.post_as_copy' in req.environ)
|
||||||
|
|
||||||
|
def test_POST_as_COPY_static_large_object(self):
|
||||||
|
req = swift.common.swob.Request.blank('/v1/a/c/o', method='POST')
|
||||||
|
get_resp = [200] * self.obj_ring.replicas + \
|
||||||
|
[404] * self.obj_ring.max_more_nodes
|
||||||
|
put_resp = [201] * self.obj_ring.replicas
|
||||||
|
codes = get_resp + put_resp
|
||||||
|
slo_headers = \
|
||||||
|
[{'X-Static-Large-Object': True}] * self.obj_ring.replicas
|
||||||
|
get_headers = slo_headers + [{}] * (len(codes) - len(slo_headers))
|
||||||
|
headers = {'headers': get_headers}
|
||||||
|
with set_http_connect(*codes, **headers):
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEquals(resp.status_int, 202)
|
||||||
|
self.assertEquals(req.environ['QUERY_STRING'], '')
|
||||||
|
self.assertTrue('swift.post_as_copy' in req.environ)
|
||||||
|
|
||||||
def test_POST_delete_at(self):
|
def test_POST_delete_at(self):
|
||||||
t = str(int(time.time() + 100))
|
t = str(int(time.time() + 100))
|
||||||
@@ -624,6 +642,9 @@ class TestReplicatedObjController(BaseObjectControllerMixin,
|
|||||||
with set_http_connect(*codes, give_connect=capture_headers):
|
with set_http_connect(*codes, give_connect=capture_headers):
|
||||||
resp = req.get_response(self.app)
|
resp = req.get_response(self.app)
|
||||||
self.assertEquals(resp.status_int, 200)
|
self.assertEquals(resp.status_int, 200)
|
||||||
|
self.assertEquals(req.environ['QUERY_STRING'], '') # sanity
|
||||||
|
self.assertTrue('swift.post_as_copy' in req.environ)
|
||||||
|
|
||||||
for given_headers in post_headers:
|
for given_headers in post_headers:
|
||||||
self.assertEquals(given_headers.get('X-Delete-At'), t)
|
self.assertEquals(given_headers.get('X-Delete-At'), t)
|
||||||
self.assertTrue('X-Delete-At-Host' in given_headers)
|
self.assertTrue('X-Delete-At-Host' in given_headers)
|
||||||
|
Reference in New Issue
Block a user