From: Matt Benjamin Date: Tue, 21 Apr 2026 17:29:23 +0000 (-0400) Subject: rgw/multipart: test idempotent complete-multipart cksum and etag X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=9ca96ba0fc3d3537d947c4157c40abbe7f0fca1e;p=s3-tests.git rgw/multipart: test idempotent complete-multipart cksum and etag Verify that correct etag and checksum (if any) are returned from a complete-multipart operation that is retried. Signed-off-by: Matt Benjamin --- diff --git a/s3tests/functional/test_s3.py b/s3tests/functional/test_s3.py index 5fee6a75..a1ffe55f 100644 --- a/s3tests/functional/test_s3.py +++ b/s3tests/functional/test_s3.py @@ -14676,6 +14676,54 @@ def test_multipart_checksum_sha256(): response = client.head_object(Bucket=bucket, Key=key, ChecksumMode='ENABLED') assert composite_sha256sum == response['ChecksumSHA256'] +@pytest.mark.checksum +@pytest.mark.fails_on_dbstore +def test_multipart_reupload_checksum_and_etag(): + bucket = get_new_bucket() + client = get_client() + + key = "mymultipart" + response = client.create_multipart_upload(Bucket=bucket, Key=key, ChecksumAlgorithm='SHA256') + assert 'SHA256' == response['ChecksumAlgorithm'] + upload_id = response['UploadId'] + + size = 5 * 1024 * 1024 # each part but the last must be at least 5M + + # code to compute checksums for these is in unittest_rgw_cksum + body1 = FakeWriteFile(size, 'A') + body2 = FakeWriteFile(size, 'B') + body3 = FakeWriteFile(size, 'C') + + # known MD5/etag and sha256 checksum values + part1_sha256sum = '275VF5loJr1YYawit0XSHREhkFXYkkPKGuoK0x9VKxI=' + part2_sha256sum = 'mrHwOfjTL5Zwfj74F05HOQGLdUb7E5szdCbxgUSq6NM=' + part3_sha256sum = 'Vw7oB/nKQ5xWb3hNgbyfkvDiivl+U+/Dft48nfJfDow=' + + composite_etag = 'b2add96cc9702bbf4efb0ccdfc6b7747-3' + composite_sha256sum = 'uWBwpe1dxI4Vw8Gf0X9ynOdw/SS6VBzfWm9giiv1sf4=-3' + + response1 = client.upload_part(UploadId=upload_id, Bucket=bucket, Key=key, PartNumber=1, Body=body1, ChecksumAlgorithm='SHA256', ChecksumSHA256=part1_sha256sum) + response2 = client.upload_part(UploadId=upload_id, Bucket=bucket, Key=key, PartNumber=2, Body=body2, ChecksumAlgorithm='SHA256', ChecksumSHA256=part2_sha256sum) + response3 = client.upload_part(UploadId=upload_id, Bucket=bucket, Key=key, PartNumber=3, Body=body3, ChecksumAlgorithm='SHA256', ChecksumSHA256=part3_sha256sum) + + res1 = client.complete_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id, ChecksumSHA256=composite_sha256sum, MultipartUpload={'Parts': [ + {'ETag': response1['ETag'].strip('"'), 'ChecksumSHA256': response1['ChecksumSHA256'], 'PartNumber': 1}, + {'ETag': response2['ETag'].strip('"'), 'ChecksumSHA256': response2['ChecksumSHA256'], 'PartNumber': 2}, + {'ETag': response3['ETag'].strip('"'), 'ChecksumSHA256': response3['ChecksumSHA256'], 'PartNumber': 3}]}) + + assert composite_etag == res1['ETag'].strip('"') + assert composite_sha256sum == res1['ChecksumSHA256'] + + # validate the headers on the (idempotent) attempt to retry completion--they should match for ETag and Checksum (if any) + res2 = client.complete_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id, ChecksumSHA256=composite_sha256sum, MultipartUpload={'Parts': [ + {'ETag': response1['ETag'].strip('"'), 'ChecksumSHA256': response1['ChecksumSHA256'], 'PartNumber': 1}, + {'ETag': response2['ETag'].strip('"'), 'ChecksumSHA256': response2['ChecksumSHA256'], 'PartNumber': 2}, + {'ETag': response3['ETag'].strip('"'), 'ChecksumSHA256': response3['ChecksumSHA256'], 'PartNumber': 3}]}) + + # assert just validated final etag and checksum values match + assert res1['ETag'] == res2['ETag'] + assert res1['ChecksumSHA256'] == res2['ChecksumSHA256'] + def multipart_checksum_3parts_helper(key=None, checksum_algo=None, checksum_type=None, **kwargs): bucket = get_new_bucket()