Skip to content

Commit 9d95bbf

Browse files
committed
UploadPart: add copy part with encryption
Signed-off-by: Seena Fallah <[email protected]>
1 parent aef70d8 commit 9d95bbf

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

s3tests_boto3/functional/test_s3.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16682,6 +16682,130 @@ def test_copy_enc_1mb(source_mode_key, dest_mode_key):
1668216682
def test_copy_enc_8mb(source_mode_key, dest_mode_key):
1668316683
_test_copy_enc(8*1024*1024, source_mode_key, dest_mode_key)
1668416684

16685+
def _test_copy_part_enc(file_size, source_mode_key, dest_mode_key, source_sc=None, dest_sc=None):
16686+
source_args = _copy_enc_source_modes[source_mode_key]
16687+
dest_args = _copy_enc_dest_modes[dest_mode_key]
16688+
16689+
bucket_name = get_new_bucket()
16690+
client = get_client()
16691+
16692+
# upload original file with source encryption
16693+
data = 'A'*file_size
16694+
args = {key: value() if callable(value) else value for key, value in source_args.get('args', {}).items()}
16695+
if source_sc:
16696+
args['StorageClass'] = source_sc
16697+
response = client.put_object(Bucket=bucket_name, Key='testobj', Body=data, **args)
16698+
assert source_args.get('assert', lambda r: True)(response)
16699+
16700+
# create a multipart upload with source encryption
16701+
dest_bucket_name = get_new_bucket()
16702+
upload_args = {key: value() if callable(value) else value for key, value in dest_args.get('args', {}).items()}
16703+
if dest_sc:
16704+
upload_args['StorageClass'] = dest_sc
16705+
response = client.create_multipart_upload(Bucket=dest_bucket_name, Key='testobj2', **upload_args)
16706+
assert dest_args.get('assert', lambda r: True)(response)
16707+
upload_id = response['UploadId']
16708+
assert len(upload_id)
16709+
16710+
parts = []
16711+
16712+
# copy the object as the part
16713+
copy_args = {key: value() if callable(value) else value for key, value in source_args.get('source_copy_args', {}).items()}
16714+
if dest_mode_key == 'sse-c':
16715+
copy_args.update(upload_args)
16716+
if dest_sc:
16717+
copy_args.pop('StorageClass', None) # StorageClass is not allowed in copy part
16718+
response = client.upload_part_copy(
16719+
Bucket=dest_bucket_name,
16720+
Key='testobj2',
16721+
PartNumber=1,
16722+
UploadId=upload_id,
16723+
CopySource={'Bucket': bucket_name, 'Key': 'testobj'},
16724+
**copy_args
16725+
)
16726+
assert dest_args.get('assert', lambda r: True)(response)
16727+
parts.append({
16728+
'ETag': response['CopyPartResult']['ETag'],
16729+
'PartNumber': 1
16730+
})
16731+
16732+
# add another temporary part to the upload
16733+
complete_args = {}
16734+
if dest_mode_key == 'sse-c':
16735+
complete_args.update(upload_args)
16736+
if dest_sc:
16737+
complete_args.pop('StorageClass', None) # StorageClass is not allowed in complete multipart upload
16738+
temp_part = client.upload_part(
16739+
Bucket=dest_bucket_name,
16740+
Key='testobj2',
16741+
PartNumber=2,
16742+
UploadId=upload_id,
16743+
Body='B'*file_size,
16744+
**complete_args
16745+
)
16746+
assert dest_args.get('assert', lambda r: True)(temp_part)
16747+
parts.append({
16748+
'ETag': temp_part['ETag'],
16749+
'PartNumber': 2
16750+
})
16751+
16752+
# complete the multipart upload
16753+
response = client.complete_multipart_upload(
16754+
Bucket=dest_bucket_name,
16755+
Key='testobj2',
16756+
UploadId=upload_id,
16757+
MultipartUpload={'Parts': parts},
16758+
**complete_args
16759+
)
16760+
assert dest_args.get('assert', lambda r: True)(response)
16761+
# verify the copy is encrypted
16762+
get_args = dest_args.get('get_args', {})
16763+
response = client.get_object(Bucket=dest_bucket_name, Key='testobj2', **get_args)
16764+
assert dest_args.get('assert', lambda r: True)(response)
16765+
body = _get_body(response)
16766+
assert body == (data + 'B'*file_size)
16767+
16768+
def generate_copy_part_enc_params():
16769+
configure()
16770+
sc = configured_storage_classes()
16771+
16772+
obj_sizes = [8*1024*1024] # min multipart is 5MB
16773+
params = []
16774+
for source_key in _copy_enc_source_modes.keys():
16775+
for dest_key in _copy_enc_dest_modes.keys():
16776+
source_marks = _copy_enc_source_modes[source_key].get('marks', [])
16777+
dest_marks = _copy_enc_dest_modes[dest_key].get('marks', [])
16778+
for source_sc in sc:
16779+
for dest_sc in sc:
16780+
additional_marks = []
16781+
if source_sc != 'STANDARD' or dest_sc != 'STANDARD':
16782+
# storage classes are not supported on AWS
16783+
additional_marks.append(pytest.mark.fails_on_aws)
16784+
for obj_size in obj_sizes:
16785+
param = pytest.param(
16786+
source_key,
16787+
dest_key,
16788+
source_sc,
16789+
dest_sc,
16790+
obj_size,
16791+
marks=[*source_marks, *dest_marks, *additional_marks]
16792+
)
16793+
params.append(param)
16794+
return params
16795+
16796+
@pytest.mark.encryption
16797+
@pytest.mark.fails_on_dbstore
16798+
@pytest.mark.parametrize(
16799+
"source_mode_key, dest_mode_key, source_storage_class, dest_storage_class, obj_size",
16800+
generate_copy_part_enc_params()
16801+
)
16802+
def test_copy_part_enc(source_mode_key, dest_mode_key, source_storage_class, dest_storage_class, obj_size):
16803+
print(
16804+
f"Testing copy part from {source_mode_key} to {dest_mode_key} with storage class "
16805+
f"{source_storage_class} -> {dest_storage_class} and object size {obj_size}"
16806+
)
16807+
_test_copy_part_enc(obj_size, source_mode_key, dest_mode_key, source_storage_class, dest_storage_class)
16808+
1668516809
def generate_copy_enc_storage_class_params():
1668616810
configure()
1668716811
sc = configured_storage_classes()

0 commit comments

Comments
 (0)