diff --git a/swift/common/middleware/s3api/controllers/obj.py b/swift/common/middleware/s3api/controllers/obj.py index d466edd8f2..91abe518c3 100644 --- a/swift/common/middleware/s3api/controllers/obj.py +++ b/swift/common/middleware/s3api/controllers/obj.py @@ -20,7 +20,7 @@ from swift.common.utils import public from swift.common.middleware.s3api.utils import S3Timestamp from swift.common.middleware.s3api.controllers.base import Controller from swift.common.middleware.s3api.s3response import S3NotImplemented, \ - InvalidRange, NoSuchKey, InvalidArgument + InvalidRange, NoSuchKey, InvalidArgument, HTTPNoContent class ObjectController(Controller): @@ -143,5 +143,6 @@ class ObjectController(Controller): except NoSuchKey: # expect to raise NoSuchBucket when the bucket doesn't exist req.get_container_info(self.app) - raise + # else -- it's gone! Success. + return HTTPNoContent() return resp diff --git a/test/functional/s3api/test_object.py b/test/functional/s3api/test_object.py index 783f48ae9a..7424e40060 100644 --- a/test/functional/s3api/test_object.py +++ b/test/functional/s3api/test_object.py @@ -139,6 +139,12 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(status, 204) self.assertCommonResponseHeaders(headers) + # DELETE Non-Existent Object + status, headers, body = \ + self.conn.make_request('DELETE', self.bucket, 'does-not-exist') + self.assertEqual(status, 204) + self.assertCommonResponseHeaders(headers) + def test_put_object_error(self): auth_error_conn = Connection(aws_secret_key='invalid') status, headers, body = \ @@ -238,11 +244,6 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(get_error_code(body), 'SignatureDoesNotMatch') self.assertEqual(headers['content-type'], 'application/xml') - status, headers, body = \ - self.conn.make_request('DELETE', self.bucket, 'invalid') - self.assertEqual(get_error_code(body), 'NoSuchKey') - self.assertEqual(headers['content-type'], 'application/xml') - status, headers, body = \ self.conn.make_request('DELETE', 'invalid', obj) self.assertEqual(get_error_code(body), 'NoSuchBucket') diff --git a/test/unit/common/middleware/s3api/test_multi_delete.py b/test/unit/common/middleware/s3api/test_multi_delete.py index c2ec2e649a..91f8e8d226 100644 --- a/test/unit/common/middleware/s3api/test_multi_delete.py +++ b/test/unit/common/middleware/s3api/test_multi_delete.py @@ -17,7 +17,6 @@ import unittest from datetime import datetime from hashlib import md5 -from six.moves import urllib from swift.common import swob from swift.common.swob import Request @@ -87,11 +86,14 @@ class TestS3ApiMultiDelete(S3ApiTestCase): elem = fromstring(body) self.assertEqual(len(elem.findall('Deleted')), 3) - _, path, _ = self.swift.calls_with_headers[-1] - path, query_string = path.split('?', 1) - self.assertEqual(path, '/v1/AUTH_test/bucket/Key3') - query = dict(urllib.parse.parse_qsl(query_string)) - self.assertEqual(query['multipart-manifest'], 'delete') + self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test/bucket'), + ('HEAD', '/v1/AUTH_test/bucket/Key1'), + ('DELETE', '/v1/AUTH_test/bucket/Key1'), + ('HEAD', '/v1/AUTH_test/bucket/Key2'), + ('HEAD', '/v1/AUTH_test/bucket/Key3'), + ('DELETE', '/v1/AUTH_test/bucket/Key3?multipart-manifest=delete'), + ]) @s3acl def test_object_multi_DELETE_quiet(self): diff --git a/test/unit/common/middleware/s3api/test_obj.py b/test/unit/common/middleware/s3api/test_obj.py index 601cd7cd07..fdddb0ecd4 100644 --- a/test/unit/common/middleware/s3api/test_obj.py +++ b/test/unit/common/middleware/s3api/test_obj.py @@ -767,13 +767,6 @@ class TestS3ApiObj(S3ApiTestCase): swob.HTTPServiceUnavailable) self.assertEqual(code, 'InternalError') - with patch( - 'swift.common.middleware.s3api.s3request.get_container_info', - return_value={'status': 204}): - code = self._test_method_error('DELETE', '/bucket/object', - swob.HTTPNotFound) - self.assertEqual(code, 'NoSuchKey') - with patch( 'swift.common.middleware.s3api.s3request.get_container_info', return_value={'status': 404}): @@ -809,11 +802,27 @@ class TestS3ApiObj(S3ApiTestCase): self.assertIn(('HEAD', '/v1/AUTH_test/bucket/object'), self.swift.calls) - self.assertIn(('DELETE', '/v1/AUTH_test/bucket/object'), - self.swift.calls) + self.assertEqual(('DELETE', '/v1/AUTH_test/bucket/object'), + self.swift.calls[-1]) _, path = self.swift.calls[-1] self.assertEqual(path.count('?'), 0) + @s3acl + def test_object_DELETE_missing(self): + self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', + swob.HTTPNotFound, {}, None) + req = Request.blank('/bucket/object', + environ={'REQUEST_METHOD': 'DELETE'}, + headers={'Authorization': 'AWS test:tester:hmac', + 'Date': self.get_date_header()}) + status, headers, body = self.call_s3api(req) + self.assertEqual(status.split()[0], '204') + + self.assertIn(('HEAD', '/v1/AUTH_test/bucket/object'), + self.swift.calls) + self.assertNotIn(('DELETE', '/v1/AUTH_test/bucket/object'), + self.swift.calls) + @s3acl def test_slo_object_DELETE(self): self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',