Skip to content

Commit f926330

Browse files
authored
Provide ETag to transfer manager to validate multipart copies (aws#9857)
1 parent 8704e06 commit f926330

5 files changed

Lines changed: 39 additions & 11 deletions

File tree

awscli/customizations/s3/s3handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ def can_submit(self, fileinfo):
433433

434434
def _add_additional_subscribers(self, subscribers, fileinfo):
435435
subscribers.append(ProvideSizeSubscriber(fileinfo.size))
436+
subscribers.append(ProvideETagSubscriber(fileinfo.etag))
436437
if self._should_inject_content_type():
437438
subscribers.append(ProvideCopyContentTypeSubscriber())
438439
if self._cli_params.get('is_move', False):

tests/functional/s3/test_cp_command.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,11 @@ def test_website_redirect_ignore_paramfile(self):
219219

220220
def test_metadata_copy(self):
221221
self.parsed_responses = [
222-
{"ContentLength": "100", "LastModified": "00:00:00Z"},
222+
{
223+
"ContentLength": "100",
224+
"LastModified": "00:00:00Z",
225+
'ETag': '"foo-1"',
226+
},
223227
{'ETag': '"foo-1"'},
224228
]
225229
cmdline = ('%s s3://bucket/key.txt s3://bucket/key2.txt'
@@ -267,7 +271,11 @@ def test_metadata_copy_with_multipart_upload(self):
267271

268272
def test_metadata_directive_copy(self):
269273
self.parsed_responses = [
270-
{"ContentLength": "100", "LastModified": "00:00:00Z"},
274+
{
275+
"ContentLength": "100",
276+
"LastModified": "00:00:00Z",
277+
"ETag": "'foo-1'",
278+
},
271279
{'ETag': '"foo-1"'},
272280
]
273281
cmdline = ('%s s3://bucket/key.txt s3://bucket/key2.txt'
@@ -610,7 +618,11 @@ def test_cp_upload_large_file_with_sse_kms_and_key_id(self):
610618

611619
def test_cp_copy_with_sse_kms_and_key_id(self):
612620
self.parsed_responses = [
613-
{'ContentLength': 5, 'LastModified': '00:00:00Z'}, # HeadObject
621+
{
622+
'ContentLength': 5,
623+
'LastModified': '00:00:00Z',
624+
'ETag': '"foo"',
625+
}, # HeadObject
614626
{} # CopyObject
615627
]
616628
cmdline = (
@@ -636,8 +648,11 @@ def test_cp_copy_with_sse_kms_and_key_id(self):
636648

637649
def test_cp_copy_large_file_with_sse_kms_and_key_id(self):
638650
self.parsed_responses = [
639-
{'ContentLength': 10 * (1024 ** 2),
640-
'LastModified': '00:00:00Z'}, # HeadObject
651+
{
652+
'ContentLength': 10 * (1024 ** 2),
653+
'LastModified': '00:00:00Z',
654+
'ETag': '"foo"',
655+
}, # HeadObject
641656
{'UploadId': 'foo'}, # CreateMultipartUpload
642657
{'CopyPartResult': {'ETag': '"foo"'}}, # UploadPartCopy
643658
{'CopyPartResult': {'ETag': '"foo"'}}, # UploadPartCopy
@@ -669,8 +684,11 @@ def test_cannot_use_recursive_with_stream(self):
669684

670685
def test_upload_unicode_path(self):
671686
self.parsed_responses = [
672-
{'ContentLength': 10,
673-
'LastModified': '00:00:00Z'}, # HeadObject
687+
{
688+
'ContentLength': 10,
689+
'LastModified': '00:00:00Z',
690+
'ETag': '"foo"',
691+
}, # HeadObject
674692
{'ETag': '"foo"'} # PutObject
675693
]
676694
command = u's3 cp s3://bucket/\u2603 s3://bucket/\u2713'
@@ -1128,11 +1146,11 @@ def test_multipart_copy(self):
11281146
self.upload_part_copy_request(
11291147
'sourcebucket', 'sourcekey', 'mybucket', 'mykey',
11301148
upload_id, PartNumber=mock.ANY, RequestPayer='requester',
1131-
CopySourceRange=mock.ANY),
1149+
CopySourceRange=mock.ANY, CopySourceIfMatch='"foo-1"'),
11321150
self.upload_part_copy_request(
11331151
'sourcebucket', 'sourcekey', 'mybucket', 'mykey',
11341152
upload_id, PartNumber=mock.ANY, RequestPayer='requester',
1135-
CopySourceRange=mock.ANY),
1153+
CopySourceRange=mock.ANY, CopySourceIfMatch='"foo-1"'),
11361154
self.complete_mpu_request(
11371155
'mybucket', 'mykey', upload_id, num_parts=2,
11381156
RequestPayer='requester')

tests/functional/s3/test_mv_command.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ def test_website_redirect_ignore_paramfile(self):
4848

4949
def test_metadata_directive_copy(self):
5050
self.parsed_responses = [
51-
{"ContentLength": "100", "LastModified": "00:00:00Z"},
51+
{
52+
"ContentLength": "100",
53+
"LastModified": "00:00:00Z",
54+
"ETag": '"foo-1"',
55+
},
5256
{'ETag': '"foo-1"'},
5357
{'ETag': '"foo-2"'}
5458
]

tests/functional/s3/test_sync_command.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ def test_copy_with_checksum_algorithm_update_sha1(self):
306306
'Key': 'mykey',
307307
'LastModified': '00:00:00Z',
308308
'Size': 100,
309-
'ChecksumAlgorithm': 'SHA1'
309+
'ChecksumAlgorithm': 'SHA1',
310+
'ETag': 'foo'
310311
}
311312
],
312313
'CommonPrefixes': []

tests/unit/customizations/s3/test_s3handler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ def test_submit(self):
630630
# Make sure the subscriber applied are of the correct type and order
631631
ref_subscribers = [
632632
ProvideSizeSubscriber,
633+
ProvideETagSubscriber,
633634
ProvideCopyContentTypeSubscriber,
634635
CopyResultSubscriber
635636
]
@@ -663,6 +664,7 @@ def test_submit_when_content_type_specified(self):
663664
copy_call_kwargs['extra_args'], {'ContentType': 'text/plain'})
664665
ref_subscribers = [
665666
ProvideSizeSubscriber,
667+
ProvideETagSubscriber,
666668
CopyResultSubscriber
667669
]
668670
actual_subscribers = copy_call_kwargs['subscribers']
@@ -680,6 +682,7 @@ def test_submit_when_no_guess_content_mime_type(self):
680682
copy_call_kwargs = self.transfer_manager.copy.call_args[1]
681683
ref_subscribers = [
682684
ProvideSizeSubscriber,
685+
ProvideETagSubscriber,
683686
CopyResultSubscriber
684687
]
685688
actual_subscribers = copy_call_kwargs['subscribers']
@@ -797,6 +800,7 @@ def test_submit_move_adds_delete_source_subscriber(self):
797800
self.transfer_request_submitter.submit(fileinfo)
798801
ref_subscribers = [
799802
ProvideSizeSubscriber,
803+
ProvideETagSubscriber,
800804
ProvideCopyContentTypeSubscriber,
801805
DeleteSourceObjectSubscriber,
802806
CopyResultSubscriber,

0 commit comments

Comments
 (0)