diff --git a/swift/common/middleware/s3api/controllers/multi_upload.py b/swift/common/middleware/s3api/controllers/multi_upload.py index 3a30bb091f..fc7615e62b 100644 --- a/swift/common/middleware/s3api/controllers/multi_upload.py +++ b/swift/common/middleware/s3api/controllers/multi_upload.py @@ -332,7 +332,8 @@ class UploadsController(Controller): 'last_modified': object_info['last_modified']} return obj_dict - is_part = re.compile('/[0-9]+$') + is_segment = re.compile('.*/[0-9]+$') + while len(uploads) < maxuploads: try: resp = req.get_response(self.app, container=container, @@ -344,8 +345,8 @@ class UploadsController(Controller): if not objects: break - new_uploads = [object_to_upload(obj) for obj in objects if - is_part.search(obj.get('name', '')) is None] + new_uploads = [object_to_upload(obj) for obj in objects + if not is_segment.match(obj.get('name', ''))] new_prefixes = [] if 'delimiter' in req.params: prefix = get_param(req, 'prefix', '') diff --git a/test/s3api/test_mpu.py b/test/s3api/test_mpu.py new file mode 100644 index 0000000000..e522ba915d --- /dev/null +++ b/test/s3api/test_mpu.py @@ -0,0 +1,100 @@ +# Copyright (c) 2021 Nvidia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from test.s3api import BaseS3TestCase + + +class TestMultiPartUploads(BaseS3TestCase): + + maxDiff = None + + def setUp(self): + self.client = self.get_s3_client(1) + self.bucket_name = self.create_name('test-mpu') + resp = self.client.create_bucket(Bucket=self.bucket_name) + self.assertEqual(200, resp['ResponseMetadata']['HTTPStatusCode']) + + def tearDown(self): + self.clear_bucket(self.client, self.bucket_name) + super(TestMultiPartUploads, self).tearDown() + + def test_basic_upload(self): + key_name = self.create_name('key') + create_mpu_resp = self.client.create_multipart_upload( + Bucket=self.bucket_name, Key=key_name) + self.assertEqual(200, create_mpu_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + upload_id = create_mpu_resp['UploadId'] + parts = [] + for i in range(1, 3): + body = ('%d' % i) * 5 * (2 ** 20) + part_resp = self.client.upload_part( + Body=body, Bucket=self.bucket_name, Key=key_name, + PartNumber=i, UploadId=upload_id) + self.assertEqual(200, part_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + parts.append({ + 'ETag': part_resp['ETag'], + 'PartNumber': i, + }) + list_parts_resp = self.client.list_parts( + Bucket=self.bucket_name, Key=key_name, + UploadId=upload_id, + ) + self.assertEqual(200, list_parts_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + self.assertEqual(parts, [{k: p[k] for k in ('ETag', 'PartNumber')} + for p in list_parts_resp['Parts']]) + complete_mpu_resp = self.client.complete_multipart_upload( + Bucket=self.bucket_name, Key=key_name, + MultipartUpload={ + 'Parts': parts, + }, + UploadId=upload_id, + ) + self.assertEqual(200, complete_mpu_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + + def test_create_list_abort_multipart_uploads(self): + key_name = self.create_name('key') + create_mpu_resp = self.client.create_multipart_upload( + Bucket=self.bucket_name, Key=key_name) + self.assertEqual(200, create_mpu_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + upload_id = create_mpu_resp['UploadId'] + + # our upload is in progress + list_mpu_resp = self.client.list_multipart_uploads( + Bucket=self.bucket_name) + self.assertEqual(200, list_mpu_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + found_uploads = list_mpu_resp.get('Uploads', []) + self.assertEqual(1, len(found_uploads), found_uploads) + self.assertEqual(upload_id, found_uploads[0]['UploadId']) + + abort_resp = self.client.abort_multipart_upload( + Bucket=self.bucket_name, + Key=key_name, + UploadId=upload_id, + ) + self.assertEqual(204, abort_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + + # no more inprogress uploads + list_mpu_resp = self.client.list_multipart_uploads( + Bucket=self.bucket_name) + self.assertEqual(200, list_mpu_resp[ + 'ResponseMetadata']['HTTPStatusCode']) + self.assertEqual([], list_mpu_resp.get('Uploads', []))