From 61dd2ee44ac242d64ef412626c03e1e38fec34e9 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 29 Jul 2021 15:04:34 -0700 Subject: [PATCH] s3api: Fix (async) multi-delete of MPUs Change-Id: I2347a73ff23c5c7d415f23d864fc29147e4a1754 --- .../s3api/controllers/multi_delete.py | 8 +++++-- .../middleware/s3api/test_multi_delete.py | 23 ++++++++++++++----- test/unit/common/middleware/s3api/test_obj.py | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/swift/common/middleware/s3api/controllers/multi_delete.py b/swift/common/middleware/s3api/controllers/multi_delete.py index cd02762298..6d1c522b56 100644 --- a/swift/common/middleware/s3api/controllers/multi_delete.py +++ b/swift/common/middleware/s3api/controllers/multi_delete.py @@ -17,6 +17,7 @@ import copy import json 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.middleware.s3api.controllers.base import Controller, \ @@ -127,8 +128,11 @@ class MultiObjectDeleteController(Controller): resp = req.get_response(self.app, method='DELETE', query=query, headers={'Accept': 'application/json'}) - # Have to read the response to actually do the SLO delete - if query.get('multipart-manifest'): + # If async segment cleanup is available, we expect to get + # 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: delete_result = json.loads(resp.body) if delete_result['Errors']: diff --git a/test/unit/common/middleware/s3api/test_multi_delete.py b/test/unit/common/middleware/s3api/test_multi_delete.py index e4c3ecf099..44af595270 100644 --- a/test/unit/common/middleware/s3api/test_multi_delete.py +++ b/test/unit/common/middleware/s3api/test_multi_delete.py @@ -62,14 +62,14 @@ class TestS3ApiMultiDelete(S3ApiTestCase): @s3acl 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', swob.HTTPNoContent, {}, None) self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key2', swob.HTTPNotFound, {}, None) + self.swift.register('HEAD', '/v1/AUTH_test/bucket/Key3', + swob.HTTPOk, + {'x-static-large-object': 'True'}, + None) slo_delete_resp = { 'Number Not Found': 0, 'Response Status': '200 OK', @@ -79,9 +79,16 @@ class TestS3ApiMultiDelete(S3ApiTestCase): } self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key3', 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') - for key in ['Key1', 'Key2', 'Key3']: + for key in ['Key1', 'Key2', 'Key3', 'Key4']: obj = SubElement(elem, 'Object') SubElement(obj, 'Key').text = key body = tostring(elem, use_s3ns=False) @@ -99,7 +106,8 @@ class TestS3ApiMultiDelete(S3ApiTestCase): self.assertEqual(status.split()[0], '200') 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, [ ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/Key1?symlink=get'), @@ -108,6 +116,9 @@ class TestS3ApiMultiDelete(S3ApiTestCase): ('DELETE', '/v1/AUTH_test/bucket/Key2'), ('HEAD', '/v1/AUTH_test/bucket/Key3?symlink=get'), ('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 diff --git a/test/unit/common/middleware/s3api/test_obj.py b/test/unit/common/middleware/s3api/test_obj.py index 92e2482c16..e596934225 100644 --- a/test/unit/common/middleware/s3api/test_obj.py +++ b/test/unit/common/middleware/s3api/test_obj.py @@ -1572,7 +1572,7 @@ class TestS3ApiObj(S3ApiTestCase): 'x-object-sysmeta-s3api-etag': 's3-style-etag'}, None) self.swift.register('DELETE', '/v1/AUTH_test/bucket/object', - swob.HTTPOk, {}, '') + swob.HTTPNoContent, {}, '') req = Request.blank('/bucket/object', environ={'REQUEST_METHOD': 'DELETE'}, headers={'Authorization': 'AWS test:tester:hmac',