Fix S3 backend operations with VersionId (#2055)
* fix s3 issues * fix merge conflict * fix and add test cases
This commit is contained in:
parent
f408709ef9
commit
7271fb9391
@ -912,12 +912,11 @@ class S3Backend(BaseBackend):
|
|||||||
return multipart.set_part(part_id, value)
|
return multipart.set_part(part_id, value)
|
||||||
|
|
||||||
def copy_part(self, dest_bucket_name, multipart_id, part_id,
|
def copy_part(self, dest_bucket_name, multipart_id, part_id,
|
||||||
src_bucket_name, src_key_name, start_byte, end_byte):
|
src_bucket_name, src_key_name, src_version_id, start_byte, end_byte):
|
||||||
src_key_name = clean_key_name(src_key_name)
|
|
||||||
src_bucket = self.get_bucket(src_bucket_name)
|
|
||||||
dest_bucket = self.get_bucket(dest_bucket_name)
|
dest_bucket = self.get_bucket(dest_bucket_name)
|
||||||
multipart = dest_bucket.multiparts[multipart_id]
|
multipart = dest_bucket.multiparts[multipart_id]
|
||||||
src_value = src_bucket.keys[src_key_name].value
|
|
||||||
|
src_value = self.get_key(src_bucket_name, src_key_name, version_id=src_version_id).value
|
||||||
if start_byte is not None:
|
if start_byte is not None:
|
||||||
src_value = src_value[start_byte:end_byte + 1]
|
src_value = src_value[start_byte:end_byte + 1]
|
||||||
return multipart.set_part(part_id, src_value)
|
return multipart.set_part(part_id, src_value)
|
||||||
|
@ -707,6 +707,8 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
|||||||
if 'x-amz-copy-source' in request.headers:
|
if 'x-amz-copy-source' in request.headers:
|
||||||
src = unquote(request.headers.get("x-amz-copy-source")).lstrip("/")
|
src = unquote(request.headers.get("x-amz-copy-source")).lstrip("/")
|
||||||
src_bucket, src_key = src.split("/", 1)
|
src_bucket, src_key = src.split("/", 1)
|
||||||
|
|
||||||
|
src_key, src_version_id = src_key.split("?versionId=") if "?versionId=" in src_key else (src_key, None)
|
||||||
src_range = request.headers.get(
|
src_range = request.headers.get(
|
||||||
'x-amz-copy-source-range', '').split("bytes=")[-1]
|
'x-amz-copy-source-range', '').split("bytes=")[-1]
|
||||||
|
|
||||||
@ -716,9 +718,13 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
start_byte, end_byte = None, None
|
start_byte, end_byte = None, None
|
||||||
|
|
||||||
|
if self.backend.get_key(src_bucket, src_key, version_id=src_version_id):
|
||||||
key = self.backend.copy_part(
|
key = self.backend.copy_part(
|
||||||
bucket_name, upload_id, part_number, src_bucket,
|
bucket_name, upload_id, part_number, src_bucket,
|
||||||
src_key, start_byte, end_byte)
|
src_key, src_version_id, start_byte, end_byte)
|
||||||
|
else:
|
||||||
|
return 404, response_headers, ""
|
||||||
|
|
||||||
template = self.response_template(S3_MULTIPART_UPLOAD_RESPONSE)
|
template = self.response_template(S3_MULTIPART_UPLOAD_RESPONSE)
|
||||||
response = template.render(part=key)
|
response = template.render(part=key)
|
||||||
else:
|
else:
|
||||||
@ -757,8 +763,13 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
|||||||
lstrip("/").split("/", 1)
|
lstrip("/").split("/", 1)
|
||||||
src_version_id = parse_qs(src_key_parsed.query).get(
|
src_version_id = parse_qs(src_key_parsed.query).get(
|
||||||
'versionId', [None])[0]
|
'versionId', [None])[0]
|
||||||
|
|
||||||
|
if self.backend.get_key(src_bucket, src_key, version_id=src_version_id):
|
||||||
self.backend.copy_key(src_bucket, src_key, bucket_name, key_name,
|
self.backend.copy_key(src_bucket, src_key, bucket_name, key_name,
|
||||||
storage=storage_class, acl=acl, src_version_id=src_version_id)
|
storage=storage_class, acl=acl, src_version_id=src_version_id)
|
||||||
|
else:
|
||||||
|
return 404, response_headers, ""
|
||||||
|
|
||||||
new_key = self.backend.get_key(bucket_name, key_name)
|
new_key = self.backend.get_key(bucket_name, key_name)
|
||||||
mdirective = request.headers.get('x-amz-metadata-directive')
|
mdirective = request.headers.get('x-amz-metadata-directive')
|
||||||
if mdirective is not None and mdirective == 'REPLACE':
|
if mdirective is not None and mdirective == 'REPLACE':
|
||||||
|
@ -1529,6 +1529,28 @@ def test_boto3_copy_object_with_versioning():
|
|||||||
# Version should be different to previous version
|
# Version should be different to previous version
|
||||||
obj2_version_new.should_not.equal(obj2_version)
|
obj2_version_new.should_not.equal(obj2_version)
|
||||||
|
|
||||||
|
client.copy_object(CopySource={'Bucket': 'blah', 'Key': 'test2', 'VersionId': obj2_version}, Bucket='blah', Key='test3')
|
||||||
|
obj3_version_new = client.get_object(Bucket='blah', Key='test3')['VersionId']
|
||||||
|
obj3_version_new.should_not.equal(obj2_version_new)
|
||||||
|
|
||||||
|
# Copy file that doesn't exist
|
||||||
|
with assert_raises(ClientError) as e:
|
||||||
|
client.copy_object(CopySource={'Bucket': 'blah', 'Key': 'test4', 'VersionId': obj2_version}, Bucket='blah', Key='test5')
|
||||||
|
e.exception.response['Error']['Code'].should.equal('404')
|
||||||
|
|
||||||
|
response = client.create_multipart_upload(Bucket='blah', Key='test4')
|
||||||
|
upload_id = response['UploadId']
|
||||||
|
response = client.upload_part_copy(Bucket='blah', Key='test4', CopySource={'Bucket': 'blah', 'Key': 'test3', 'VersionId': obj3_version_new},
|
||||||
|
UploadId=upload_id, PartNumber=1)
|
||||||
|
etag = response["CopyPartResult"]["ETag"]
|
||||||
|
client.complete_multipart_upload(
|
||||||
|
Bucket='blah', Key='test4', UploadId=upload_id,
|
||||||
|
MultipartUpload={'Parts': [{'ETag': etag, 'PartNumber': 1}]})
|
||||||
|
|
||||||
|
response = client.get_object(Bucket='blah', Key='test4')
|
||||||
|
data = response["Body"].read()
|
||||||
|
data.should.equal(b'test2')
|
||||||
|
|
||||||
|
|
||||||
@mock_s3
|
@mock_s3
|
||||||
def test_boto3_copy_object_from_unversioned_to_versioned_bucket():
|
def test_boto3_copy_object_from_unversioned_to_versioned_bucket():
|
||||||
@ -2762,6 +2784,7 @@ def test_boto3_multiple_delete_markers():
|
|||||||
latest['Key'].should.equal('key-with-versions-and-unicode-ó')
|
latest['Key'].should.equal('key-with-versions-and-unicode-ó')
|
||||||
oldest['Key'].should.equal('key-with-versions-and-unicode-ó')
|
oldest['Key'].should.equal('key-with-versions-and-unicode-ó')
|
||||||
|
|
||||||
|
|
||||||
@mock_s3
|
@mock_s3
|
||||||
def test_get_stream_gzipped():
|
def test_get_stream_gzipped():
|
||||||
payload = b"this is some stuff here"
|
payload = b"this is some stuff here"
|
||||||
|
Loading…
Reference in New Issue
Block a user