s3api: Fix (async) multi-delete of MPUs
Change-Id: I2347a73ff23c5c7d415f23d864fc29147e4a1754
This commit is contained in:
@@ -17,6 +17,7 @@ import copy
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH
|
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH
|
||||||
|
from swift.common.http import HTTP_NO_CONTENT
|
||||||
from swift.common.utils import public, StreamingPile, get_swift_info
|
from swift.common.utils import public, StreamingPile, get_swift_info
|
||||||
|
|
||||||
from swift.common.middleware.s3api.controllers.base import Controller, \
|
from swift.common.middleware.s3api.controllers.base import Controller, \
|
||||||
@@ -127,8 +128,11 @@ class MultiObjectDeleteController(Controller):
|
|||||||
|
|
||||||
resp = req.get_response(self.app, method='DELETE', query=query,
|
resp = req.get_response(self.app, method='DELETE', query=query,
|
||||||
headers={'Accept': 'application/json'})
|
headers={'Accept': 'application/json'})
|
||||||
# Have to read the response to actually do the SLO delete
|
# If async segment cleanup is available, we expect to get
|
||||||
if query.get('multipart-manifest'):
|
# back a 204; otherwise, the delete is synchronous and we
|
||||||
|
# have to read the response to actually do the SLO delete
|
||||||
|
if query.get('multipart-manifest') and \
|
||||||
|
resp.status_int != HTTP_NO_CONTENT:
|
||||||
try:
|
try:
|
||||||
delete_result = json.loads(resp.body)
|
delete_result = json.loads(resp.body)
|
||||||
if delete_result['Errors']:
|
if delete_result['Errors']:
|
||||||
|
@@ -62,14 +62,14 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
|||||||
|
|
||||||
@s3acl
|
@s3acl
|
||||||
def test_object_multi_DELETE(self):
|
def test_object_multi_DELETE(self):
|
||||||
self.swift.register('HEAD', '/v1/AUTH_test/bucket/Key3',
|
|
||||||
swob.HTTPOk,
|
|
||||||
{'x-static-large-object': 'True'},
|
|
||||||
None)
|
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key1',
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key1',
|
||||||
swob.HTTPNoContent, {}, None)
|
swob.HTTPNoContent, {}, None)
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key2',
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key2',
|
||||||
swob.HTTPNotFound, {}, None)
|
swob.HTTPNotFound, {}, None)
|
||||||
|
self.swift.register('HEAD', '/v1/AUTH_test/bucket/Key3',
|
||||||
|
swob.HTTPOk,
|
||||||
|
{'x-static-large-object': 'True'},
|
||||||
|
None)
|
||||||
slo_delete_resp = {
|
slo_delete_resp = {
|
||||||
'Number Not Found': 0,
|
'Number Not Found': 0,
|
||||||
'Response Status': '200 OK',
|
'Response Status': '200 OK',
|
||||||
@@ -79,9 +79,16 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
|||||||
}
|
}
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key3',
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key3',
|
||||||
swob.HTTPOk, {}, json.dumps(slo_delete_resp))
|
swob.HTTPOk, {}, json.dumps(slo_delete_resp))
|
||||||
|
self.swift.register('HEAD', '/v1/AUTH_test/bucket/Key4',
|
||||||
|
swob.HTTPOk,
|
||||||
|
{'x-static-large-object': 'True',
|
||||||
|
'x-object-sysmeta-s3api-etag': 'some-etag'},
|
||||||
|
None)
|
||||||
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key4',
|
||||||
|
swob.HTTPNoContent, {}, None)
|
||||||
|
|
||||||
elem = Element('Delete')
|
elem = Element('Delete')
|
||||||
for key in ['Key1', 'Key2', 'Key3']:
|
for key in ['Key1', 'Key2', 'Key3', 'Key4']:
|
||||||
obj = SubElement(elem, 'Object')
|
obj = SubElement(elem, 'Object')
|
||||||
SubElement(obj, 'Key').text = key
|
SubElement(obj, 'Key').text = key
|
||||||
body = tostring(elem, use_s3ns=False)
|
body = tostring(elem, use_s3ns=False)
|
||||||
@@ -99,7 +106,8 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
|||||||
self.assertEqual(status.split()[0], '200')
|
self.assertEqual(status.split()[0], '200')
|
||||||
|
|
||||||
elem = fromstring(body)
|
elem = fromstring(body)
|
||||||
self.assertEqual(len(elem.findall('Deleted')), 3)
|
self.assertEqual(len(elem.findall('Deleted')), 4)
|
||||||
|
self.assertEqual(len(elem.findall('Error')), 0)
|
||||||
self.assertEqual(self.swift.calls, [
|
self.assertEqual(self.swift.calls, [
|
||||||
('HEAD', '/v1/AUTH_test/bucket'),
|
('HEAD', '/v1/AUTH_test/bucket'),
|
||||||
('HEAD', '/v1/AUTH_test/bucket/Key1?symlink=get'),
|
('HEAD', '/v1/AUTH_test/bucket/Key1?symlink=get'),
|
||||||
@@ -108,6 +116,9 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
|||||||
('DELETE', '/v1/AUTH_test/bucket/Key2'),
|
('DELETE', '/v1/AUTH_test/bucket/Key2'),
|
||||||
('HEAD', '/v1/AUTH_test/bucket/Key3?symlink=get'),
|
('HEAD', '/v1/AUTH_test/bucket/Key3?symlink=get'),
|
||||||
('DELETE', '/v1/AUTH_test/bucket/Key3?multipart-manifest=delete'),
|
('DELETE', '/v1/AUTH_test/bucket/Key3?multipart-manifest=delete'),
|
||||||
|
('HEAD', '/v1/AUTH_test/bucket/Key4?symlink=get'),
|
||||||
|
('DELETE',
|
||||||
|
'/v1/AUTH_test/bucket/Key4?async=on&multipart-manifest=delete'),
|
||||||
])
|
])
|
||||||
|
|
||||||
@s3acl
|
@s3acl
|
||||||
|
@@ -1572,7 +1572,7 @@ class TestS3ApiObj(S3ApiTestCase):
|
|||||||
'x-object-sysmeta-s3api-etag': 's3-style-etag'},
|
'x-object-sysmeta-s3api-etag': 's3-style-etag'},
|
||||||
None)
|
None)
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/object',
|
self.swift.register('DELETE', '/v1/AUTH_test/bucket/object',
|
||||||
swob.HTTPOk, {}, '<SLO delete results>')
|
swob.HTTPNoContent, {}, '')
|
||||||
req = Request.blank('/bucket/object',
|
req = Request.blank('/bucket/object',
|
||||||
environ={'REQUEST_METHOD': 'DELETE'},
|
environ={'REQUEST_METHOD': 'DELETE'},
|
||||||
headers={'Authorization': 'AWS test:tester:hmac',
|
headers={'Authorization': 'AWS test:tester:hmac',
|
||||||
|
Reference in New Issue
Block a user