Merge "s3api: Fix multi_delete with object names using non-ASCII characters"
This commit is contained in:
		@@ -18,6 +18,7 @@ import json
 | 
			
		||||
 | 
			
		||||
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH
 | 
			
		||||
from swift.common.http import HTTP_NO_CONTENT
 | 
			
		||||
from swift.common.swob import str_to_wsgi
 | 
			
		||||
from swift.common.utils import public, StreamingPile
 | 
			
		||||
from swift.common.registry import get_swift_info
 | 
			
		||||
 | 
			
		||||
@@ -113,7 +114,7 @@ class MultiObjectDeleteController(Controller):
 | 
			
		||||
        def do_delete(base_req, key, version):
 | 
			
		||||
            req = copy.copy(base_req)
 | 
			
		||||
            req.environ = copy.copy(base_req.environ)
 | 
			
		||||
            req.object_name = key
 | 
			
		||||
            req.object_name = str_to_wsgi(key)
 | 
			
		||||
            if version:
 | 
			
		||||
                req.params = {'version-id': version, 'symlink': 'get'}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import unittest
 | 
			
		||||
import os
 | 
			
		||||
import test.functional as tf
 | 
			
		||||
@@ -56,14 +57,17 @@ class TestS3ApiMultiDelete(S3ApiBase):
 | 
			
		||||
 | 
			
		||||
        return tostring(elem, use_s3ns=False)
 | 
			
		||||
 | 
			
		||||
    def test_delete_multi_objects(self):
 | 
			
		||||
    def _test_delete_multi_objects(self, with_non_ascii=False):
 | 
			
		||||
        bucket = 'bucket'
 | 
			
		||||
        put_objects = ['obj%s' % var for var in range(4)]
 | 
			
		||||
        if with_non_ascii:
 | 
			
		||||
            put_objects = [u'\N{SNOWMAN}obj%s' % var for var in range(4)]
 | 
			
		||||
        else:
 | 
			
		||||
            put_objects = ['obj%s' % var for var in range(4)]
 | 
			
		||||
        self._prepare_test_delete_multi_objects(bucket, put_objects)
 | 
			
		||||
        query = 'delete'
 | 
			
		||||
 | 
			
		||||
        # Delete an object via MultiDelete API
 | 
			
		||||
        req_objects = ['obj0']
 | 
			
		||||
        req_objects = put_objects[:1]
 | 
			
		||||
        xml = self._gen_multi_delete_xml(req_objects)
 | 
			
		||||
        content_md5 = calculate_md5(xml)
 | 
			
		||||
        status, headers, body = \
 | 
			
		||||
@@ -78,10 +82,13 @@ class TestS3ApiMultiDelete(S3ApiBase):
 | 
			
		||||
        resp_objects = elem.findall('Deleted')
 | 
			
		||||
        self.assertEqual(len(resp_objects), len(req_objects))
 | 
			
		||||
        for o in resp_objects:
 | 
			
		||||
            self.assertTrue(o.find('Key').text in req_objects)
 | 
			
		||||
            key = o.find('Key').text
 | 
			
		||||
            if six.PY2:
 | 
			
		||||
                key = key.decode('utf-8')
 | 
			
		||||
            self.assertTrue(key in req_objects)
 | 
			
		||||
 | 
			
		||||
        # Delete 2 objects via MultiDelete API
 | 
			
		||||
        req_objects = ['obj1', 'obj2']
 | 
			
		||||
        req_objects = put_objects[1:3]
 | 
			
		||||
        xml = self._gen_multi_delete_xml(req_objects)
 | 
			
		||||
        content_md5 = calculate_md5(xml)
 | 
			
		||||
        status, headers, body = \
 | 
			
		||||
@@ -93,10 +100,17 @@ class TestS3ApiMultiDelete(S3ApiBase):
 | 
			
		||||
        resp_objects = elem.findall('Deleted')
 | 
			
		||||
        self.assertEqual(len(resp_objects), len(req_objects))
 | 
			
		||||
        for o in resp_objects:
 | 
			
		||||
            self.assertTrue(o.find('Key').text in req_objects)
 | 
			
		||||
            key = o.find('Key').text
 | 
			
		||||
            if six.PY2:
 | 
			
		||||
                key = key.decode('utf-8')
 | 
			
		||||
            self.assertTrue(key in req_objects)
 | 
			
		||||
 | 
			
		||||
        if with_non_ascii:
 | 
			
		||||
            fake_objs = [u'\N{SNOWMAN}obj%s' % var for var in range(4, 6)]
 | 
			
		||||
        else:
 | 
			
		||||
            fake_objs = ['obj%s' % var for var in range(4, 6)]
 | 
			
		||||
        # Delete 2 objects via MultiDelete API but one (obj4) doesn't exist.
 | 
			
		||||
        req_objects = ['obj3', 'obj4']
 | 
			
		||||
        req_objects = [put_objects[-1], fake_objs[0]]
 | 
			
		||||
        xml = self._gen_multi_delete_xml(req_objects)
 | 
			
		||||
        content_md5 = calculate_md5(xml)
 | 
			
		||||
        status, headers, body = \
 | 
			
		||||
@@ -109,10 +123,13 @@ class TestS3ApiMultiDelete(S3ApiBase):
 | 
			
		||||
        # S3 assumes a NoSuchKey object as deleted.
 | 
			
		||||
        self.assertEqual(len(resp_objects), len(req_objects))
 | 
			
		||||
        for o in resp_objects:
 | 
			
		||||
            self.assertTrue(o.find('Key').text in req_objects)
 | 
			
		||||
            key = o.find('Key').text
 | 
			
		||||
            if six.PY2:
 | 
			
		||||
                key = key.decode('utf-8')
 | 
			
		||||
            self.assertTrue(key in req_objects)
 | 
			
		||||
 | 
			
		||||
        # Delete 2 objects via MultiDelete API but no objects exist
 | 
			
		||||
        req_objects = ['obj4', 'obj5']
 | 
			
		||||
        req_objects = fake_objs[:2]
 | 
			
		||||
        xml = self._gen_multi_delete_xml(req_objects)
 | 
			
		||||
        content_md5 = calculate_md5(xml)
 | 
			
		||||
        status, headers, body = \
 | 
			
		||||
@@ -124,7 +141,16 @@ class TestS3ApiMultiDelete(S3ApiBase):
 | 
			
		||||
        resp_objects = elem.findall('Deleted')
 | 
			
		||||
        self.assertEqual(len(resp_objects), len(req_objects))
 | 
			
		||||
        for o in resp_objects:
 | 
			
		||||
            self.assertTrue(o.find('Key').text in req_objects)
 | 
			
		||||
            key = o.find('Key').text
 | 
			
		||||
            if six.PY2:
 | 
			
		||||
                key = key.decode('utf-8')
 | 
			
		||||
            self.assertTrue(key in req_objects)
 | 
			
		||||
 | 
			
		||||
    def test_delete_multi_objects(self):
 | 
			
		||||
        self._test_delete_multi_objects()
 | 
			
		||||
 | 
			
		||||
    def test_delete_multi_objects_with_non_ascii(self):
 | 
			
		||||
        self._test_delete_multi_objects(with_non_ascii=True)
 | 
			
		||||
 | 
			
		||||
    def test_delete_multi_objects_error(self):
 | 
			
		||||
        bucket = 'bucket'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Copyright (c) 2014 OpenStack Foundation
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
@@ -39,6 +40,9 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
 | 
			
		||||
                            swob.HTTPOk, {}, None)
 | 
			
		||||
        self.swift.register('HEAD', '/v1/AUTH_test/bucket/Key2',
 | 
			
		||||
                            swob.HTTPNotFound, {}, None)
 | 
			
		||||
        self.swift.register('HEAD',
 | 
			
		||||
                            '/v1/AUTH_test/bucket/business/caf\xc3\xa9',
 | 
			
		||||
                            swob.HTTPOk, {}, None)
 | 
			
		||||
        self.ts = make_timestamp_iter()
 | 
			
		||||
 | 
			
		||||
    @s3acl
 | 
			
		||||
@@ -70,6 +74,9 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
 | 
			
		||||
                            swob.HTTPOk,
 | 
			
		||||
                            {'x-static-large-object': 'True'},
 | 
			
		||||
                            None)
 | 
			
		||||
        self.swift.register('DELETE',
 | 
			
		||||
                            '/v1/AUTH_test/bucket/business/caf\xc3\xa9',
 | 
			
		||||
                            swob.HTTPNoContent, {}, None)
 | 
			
		||||
        slo_delete_resp = {
 | 
			
		||||
            'Number Not Found': 0,
 | 
			
		||||
            'Response Status': '200 OK',
 | 
			
		||||
@@ -88,7 +95,7 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
 | 
			
		||||
                            swob.HTTPNoContent, {}, None)
 | 
			
		||||
 | 
			
		||||
        elem = Element('Delete')
 | 
			
		||||
        for key in ['Key1', 'Key2', 'Key3', 'Key4']:
 | 
			
		||||
        for key in ['Key1', 'Key2', 'Key3', 'Key4', 'business/café']:
 | 
			
		||||
            obj = SubElement(elem, 'Object')
 | 
			
		||||
            SubElement(obj, 'Key').text = key
 | 
			
		||||
        body = tostring(elem, use_s3ns=False)
 | 
			
		||||
@@ -106,7 +113,7 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
 | 
			
		||||
        self.assertEqual(status.split()[0], '200')
 | 
			
		||||
 | 
			
		||||
        elem = fromstring(body)
 | 
			
		||||
        self.assertEqual(len(elem.findall('Deleted')), 4)
 | 
			
		||||
        self.assertEqual(len(elem.findall('Deleted')), 5)
 | 
			
		||||
        self.assertEqual(len(elem.findall('Error')), 0)
 | 
			
		||||
        self.assertEqual(self.swift.calls, [
 | 
			
		||||
            ('HEAD', '/v1/AUTH_test/bucket'),
 | 
			
		||||
@@ -119,6 +126,8 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
 | 
			
		||||
            ('HEAD', '/v1/AUTH_test/bucket/Key4?symlink=get'),
 | 
			
		||||
            ('DELETE',
 | 
			
		||||
             '/v1/AUTH_test/bucket/Key4?async=on&multipart-manifest=delete'),
 | 
			
		||||
            ('HEAD', '/v1/AUTH_test/bucket/business/caf\xc3\xa9?symlink=get'),
 | 
			
		||||
            ('DELETE', '/v1/AUTH_test/bucket/business/caf\xc3\xa9'),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    @s3acl
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user