Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/functional/s3/test_mv_command.py
1567 views
1
#!/usr/bin/env python
2
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License"). You
5
# may not use this file except in compliance with the License. A copy of
6
# the License is located at
7
#
8
# http://aws.amazon.com/apache2.0/
9
#
10
# or in the "license" file accompanying this file. This file is
11
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
# ANY KIND, either express or implied. See the License for the specific
13
# language governing permissions and limitations under the License.
14
from awscli.customizations.s3.utils import S3PathResolver
15
from awscli.compat import BytesIO
16
from tests.functional.s3 import BaseS3TransferCommandTest
17
from tests import requires_crt
18
19
20
class TestMvCommand(BaseS3TransferCommandTest):
21
22
prefix = 's3 mv '
23
24
def test_cant_mv_object_onto_itself(self):
25
cmdline = '%s s3://bucket/key s3://bucket/key' % self.prefix
26
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
27
self.assertIn('Cannot mv a file onto itself', stderr)
28
29
def test_cant_mv_object_with_implied_name(self):
30
# The "key" key name is implied in the dst argument.
31
cmdline = '%s s3://bucket/key s3://bucket/' % self.prefix
32
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
33
self.assertIn('Cannot mv a file onto itself', stderr)
34
35
def test_website_redirect_ignore_paramfile(self):
36
full_path = self.files.create_file('foo.txt', 'mycontent')
37
cmdline = '%s %s s3://bucket/key.txt --website-redirect %s' % \
38
(self.prefix, full_path, 'http://someserver')
39
self.parsed_responses = [{'ETag': '"c8afdb36c52cf4727836669019e69222"'}]
40
self.run_cmd(cmdline, expected_rc=0)
41
self.assertEqual(self.operations_called[0][0].name, 'PutObject')
42
# Make sure that the specified web address is used as opposed to the
43
# contents of the web address.
44
self.assertEqual(
45
self.operations_called[0][1]['WebsiteRedirectLocation'],
46
'http://someserver'
47
)
48
49
def test_metadata_directive_copy(self):
50
self.parsed_responses = [
51
{"ContentLength": "100", "LastModified": "00:00:00Z"},
52
{'ETag': '"foo-1"'},
53
{'ETag': '"foo-2"'}
54
]
55
cmdline = ('%s s3://bucket/key.txt s3://bucket/key2.txt'
56
' --metadata-directive REPLACE' % self.prefix)
57
self.run_cmd(cmdline, expected_rc=0)
58
self.assertEqual(len(self.operations_called), 3,
59
self.operations_called)
60
self.assertEqual(self.operations_called[0][0].name, 'HeadObject')
61
self.assertEqual(self.operations_called[1][0].name, 'CopyObject')
62
self.assertEqual(self.operations_called[2][0].name, 'DeleteObject')
63
self.assertEqual(self.operations_called[1][1]['MetadataDirective'],
64
'REPLACE')
65
66
def test_no_metadata_directive_for_non_copy(self):
67
full_path = self.files.create_file('foo.txt', 'mycontent')
68
cmdline = '%s %s s3://bucket --metadata-directive REPLACE' % \
69
(self.prefix, full_path)
70
self.parsed_responses = \
71
[{'ETag': '"c8afdb36c52cf4727836669019e69222"'}]
72
self.run_cmd(cmdline, expected_rc=0)
73
self.assertEqual(len(self.operations_called), 1,
74
self.operations_called)
75
self.assertEqual(self.operations_called[0][0].name, 'PutObject')
76
self.assertNotIn('MetadataDirective', self.operations_called[0][1])
77
78
def test_download_move_with_request_payer(self):
79
cmdline = '%s s3://mybucket/mykey %s --request-payer' % (
80
self.prefix, self.files.rootdir)
81
82
self.parsed_responses = [
83
# Response for HeadObject
84
{
85
"ContentLength": 100,
86
"LastModified": "00:00:00Z",
87
"ETag": '"foo-1"',
88
},
89
# Response for GetObject
90
{'ETag': '"foo-1"', 'Body': BytesIO(b'foo')},
91
# Response for DeleteObject
92
{}
93
]
94
95
self.run_cmd(cmdline, expected_rc=0)
96
self.assert_operations_called(
97
[
98
('HeadObject', {
99
'Bucket': 'mybucket',
100
'Key': 'mykey',
101
'RequestPayer': 'requester',
102
}),
103
('GetObject', {
104
'Bucket': 'mybucket',
105
'Key': 'mykey',
106
'RequestPayer': 'requester',
107
}),
108
('DeleteObject', {
109
'Bucket': 'mybucket',
110
'Key': 'mykey',
111
'RequestPayer': 'requester',
112
})
113
]
114
)
115
116
def test_copy_move_with_request_payer(self):
117
cmdline = self.prefix
118
cmdline += 's3://sourcebucket/sourcekey s3://mybucket/mykey'
119
cmdline += ' --request-payer'
120
121
self.parsed_responses = [
122
self.head_object_response(),
123
self.copy_object_response(),
124
self.delete_object_response(),
125
]
126
self.run_cmd(cmdline, expected_rc=0)
127
self.assert_operations_called(
128
[
129
self.head_object_request(
130
'sourcebucket', 'sourcekey', RequestPayer='requester'),
131
self.copy_object_request(
132
'sourcebucket', 'sourcekey', 'mybucket', 'mykey',
133
RequestPayer='requester'),
134
self.delete_object_request(
135
'sourcebucket', 'sourcekey', RequestPayer='requester')
136
]
137
)
138
139
def test_upload_with_checksum_algorithm_crc32(self):
140
full_path = self.files.create_file('foo.txt', 'contents')
141
cmdline = f'{self.prefix} {full_path} s3://bucket/key.txt --checksum-algorithm CRC32'
142
self.run_cmd(cmdline, expected_rc=0)
143
self.assertEqual(self.operations_called[0][0].name, 'PutObject')
144
self.assertEqual(self.operations_called[0][1]['ChecksumAlgorithm'], 'CRC32')
145
146
def test_download_with_checksum_mode_crc32(self):
147
self.parsed_responses = [
148
self.head_object_response(),
149
# Mocked GetObject response with a checksum algorithm specified
150
{
151
'ETag': 'foo-1',
152
'ChecksumCRC32': 'checksum',
153
'Body': BytesIO(b'foo')
154
},
155
self.delete_object_response()
156
]
157
cmdline = f'{self.prefix} s3://bucket/foo {self.files.rootdir} --checksum-mode ENABLED'
158
self.run_cmd(cmdline, expected_rc=0)
159
self.assertEqual(self.operations_called[1][0].name, 'GetObject')
160
self.assertEqual(self.operations_called[1][1]['ChecksumMode'], 'ENABLED')
161
162
163
class TestMvCommandWithValidateSameS3Paths(BaseS3TransferCommandTest):
164
165
prefix = 's3 mv '
166
167
def assert_validates_cannot_mv_onto_itself(self, cmd):
168
stderr = self.run_cmd(cmd, expected_rc=255)[1]
169
self.assertIn('Cannot mv a file onto itself', stderr)
170
171
def assert_runs_mv_without_validation(self, cmd):
172
self.parsed_responses = [
173
self.head_object_response(),
174
self.copy_object_response(),
175
self.delete_object_response(),
176
]
177
self.run_cmd(cmd, expected_rc=0)
178
self.assertEqual(len(self.operations_called), 3,
179
self.operations_called)
180
self.assertEqual(self.operations_called[0][0].name, 'HeadObject')
181
self.assertEqual(self.operations_called[1][0].name, 'CopyObject')
182
self.assertEqual(self.operations_called[2][0].name, 'DeleteObject')
183
184
def assert_raises_warning(self, cmd):
185
self.parsed_responses = [
186
self.head_object_response(),
187
self.copy_object_response(),
188
self.delete_object_response(),
189
]
190
stderr = self.run_cmd(cmd, expected_rc=0)[1]
191
self.assertIn('warning: Provided s3 paths may resolve', stderr)
192
193
def test_cant_mv_object_onto_itself_access_point_arn(self):
194
cmdline = (f"{self.prefix}s3://bucket/key "
195
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
196
"myaccesspoint/key "
197
"--validate-same-s3-paths")
198
self.parsed_responses = [
199
{"Bucket": "bucket"}
200
]
201
self.assert_validates_cannot_mv_onto_itself(cmdline)
202
203
def test_cant_mv_object_onto_itself_access_point_arn_as_source(self):
204
cmdline = (f"{self.prefix}s3://arn:aws:s3:us-west-2:123456789012:"
205
"accesspoint/myaccesspoint/key "
206
"s3://bucket/key "
207
"--validate-same-s3-paths")
208
self.parsed_responses = [
209
{"Bucket": "bucket"}
210
]
211
self.assert_validates_cannot_mv_onto_itself(cmdline)
212
213
def test_cant_mv_object_onto_itself_access_point_arn_with_env_var(self):
214
self.environ['AWS_CLI_S3_MV_VALIDATE_SAME_S3_PATHS'] = 'true'
215
cmdline = (f"{self.prefix}s3://bucket/key "
216
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
217
"myaccesspoint/key")
218
self.parsed_responses = [
219
{"Bucket": "bucket"}
220
]
221
self.assert_validates_cannot_mv_onto_itself(cmdline)
222
223
def test_cant_mv_object_onto_itself_access_point_arn_base_key(self):
224
cmdline = (f"{self.prefix}s3://bucket/key "
225
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
226
"myaccesspoint/ "
227
"--validate-same-s3-paths")
228
self.parsed_responses = [
229
{"Bucket": "bucket"}
230
]
231
self.assert_validates_cannot_mv_onto_itself(cmdline)
232
233
def test_cant_mv_object_onto_itself_access_point_arn_base_prefix(self):
234
cmdline = (f"{self.prefix}s3://bucket/prefix/key "
235
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
236
"myaccesspoint/prefix/ "
237
"--validate-same-s3-paths")
238
self.parsed_responses = [
239
{"Bucket": "bucket"}
240
]
241
self.assert_validates_cannot_mv_onto_itself(cmdline)
242
243
def test_cant_mv_object_onto_itself_access_point_alias(self):
244
cmdline = (f"{self.prefix} s3://bucket/key "
245
"s3://myaccesspoint-foobar-s3alias/key "
246
"--validate-same-s3-paths")
247
self.parsed_responses = [
248
{"Account": "123456789012"},
249
{"Bucket": "bucket"}
250
]
251
self.assert_validates_cannot_mv_onto_itself(cmdline)
252
253
def test_cant_mv_object_onto_itself_outpost_access_point_arn(self):
254
cmdline = (f"{self.prefix}s3://bucket/key "
255
"s3://arn:aws:s3-outposts:us-east-1:123456789012:outpost/"
256
"op-foobar/accesspoint/myaccesspoint/key "
257
"--validate-same-s3-paths")
258
self.parsed_responses = [
259
{"Bucket": "bucket"}
260
]
261
self.assert_validates_cannot_mv_onto_itself(cmdline)
262
263
def test_outpost_access_point_alias_raises_error(self):
264
cmdline = (f"{self.prefix} s3://bucket/key "
265
"s3://myaccesspoint-foobar--op-s3/key "
266
"--validate-same-s3-paths")
267
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
268
self.assertIn("Can't resolve underlying bucket name", stderr)
269
270
def test_cant_mv_object_onto_itself_mrap_arn(self):
271
cmdline = (f"{self.prefix} s3://bucket/key "
272
"s3://arn:aws:s3::123456789012:accesspoint/foobar.mrap/key "
273
"--validate-same-s3-paths")
274
self.parsed_responses = [
275
{
276
"AccessPoints": [{
277
"Alias": "foobar.mrap",
278
"Regions": [
279
{"Bucket": "differentbucket"},
280
{"Bucket": "bucket"}
281
]
282
}]
283
}
284
]
285
self.assert_validates_cannot_mv_onto_itself(cmdline)
286
287
def test_get_mrap_buckets_raises_if_alias_not_found(self):
288
cmdline = (f"{self.prefix} s3://bucket/key "
289
"s3://arn:aws:s3::123456789012:accesspoint/foobar.mrap/key "
290
"--validate-same-s3-paths")
291
self.parsed_responses = [
292
{
293
"AccessPoints": [{
294
"Alias": "baz.mrap",
295
"Regions": [
296
{"Bucket": "differentbucket"},
297
{"Bucket": "bucket"}
298
]
299
}]
300
}
301
]
302
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
303
self.assertEqual(
304
"\nCouldn't find multi-region access point with alias foobar.mrap "
305
"in account 123456789012\n",
306
stderr
307
)
308
309
def test_mv_works_if_access_point_arn_resolves_to_different_bucket(self):
310
cmdline = (f"{self.prefix}s3://bucket/key "
311
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
312
"myaccesspoint/key "
313
"--validate-same-s3-paths")
314
self.parsed_responses = [
315
{"Bucket": "differentbucket"},
316
self.head_object_response(),
317
self.copy_object_response(),
318
self.delete_object_response(),
319
]
320
self.run_cmd(cmdline, expected_rc=0)
321
self.assertEqual(len(self.operations_called), 4,
322
self.operations_called)
323
self.assertEqual(self.operations_called[0][0].name, 'GetAccessPoint')
324
self.assertEqual(self.operations_called[1][0].name, 'HeadObject')
325
self.assertEqual(self.operations_called[2][0].name, 'CopyObject')
326
self.assertEqual(self.operations_called[3][0].name, 'DeleteObject')
327
328
def test_mv_works_if_access_point_alias_resolves_to_different_bucket(self):
329
cmdline = (f"{self.prefix} s3://bucket/key "
330
"s3://myaccesspoint-foobar-s3alias/key "
331
"--validate-same-s3-paths")
332
self.parsed_responses = [
333
{"Account": "123456789012"},
334
{"Bucket": "differentbucket"},
335
self.head_object_response(),
336
self.copy_object_response(),
337
self.delete_object_response(),
338
]
339
self.run_cmd(cmdline, expected_rc=0)
340
self.assertEqual(len(self.operations_called), 5,
341
self.operations_called)
342
self.assertEqual(self.operations_called[0][0].name, 'GetCallerIdentity')
343
self.assertEqual(self.operations_called[1][0].name, 'GetAccessPoint')
344
self.assertEqual(self.operations_called[2][0].name, 'HeadObject')
345
self.assertEqual(self.operations_called[3][0].name, 'CopyObject')
346
self.assertEqual(self.operations_called[4][0].name, 'DeleteObject')
347
348
def test_mv_works_if_outpost_access_point_arn_resolves_to_different_bucket(self):
349
cmdline = (f"{self.prefix}s3://bucket/key "
350
"s3://arn:aws:s3-outposts:us-east-1:123456789012:outpost/"
351
"op-foobar/accesspoint/myaccesspoint/key "
352
"--validate-same-s3-paths")
353
self.parsed_responses = [
354
{"Bucket": "differentbucket"},
355
self.head_object_response(),
356
self.copy_object_response(),
357
self.delete_object_response(),
358
]
359
self.run_cmd(cmdline, expected_rc=0)
360
self.assertEqual(len(self.operations_called), 4,
361
self.operations_called)
362
self.assertEqual(self.operations_called[0][0].name, 'GetAccessPoint')
363
self.assertEqual(self.operations_called[1][0].name, 'HeadObject')
364
self.assertEqual(self.operations_called[2][0].name, 'CopyObject')
365
self.assertEqual(self.operations_called[3][0].name, 'DeleteObject')
366
367
@requires_crt
368
def test_mv_works_if_mrap_arn_resolves_to_different_bucket(self):
369
cmdline = (f"{self.prefix} s3://bucket/key "
370
"s3://arn:aws:s3::123456789012:accesspoint/foobar.mrap/key "
371
"--validate-same-s3-paths")
372
self.parsed_responses = [
373
{
374
"AccessPoints": [{
375
"Alias": "foobar.mrap",
376
"Regions": [
377
{"Bucket": "differentbucket"},
378
]
379
}]
380
},
381
self.head_object_response(),
382
self.copy_object_response(),
383
self.delete_object_response(),
384
]
385
self.run_cmd(cmdline, expected_rc=0)
386
self.assertEqual(len(self.operations_called), 4,
387
self.operations_called)
388
self.assertEqual(self.operations_called[0][0].name, 'ListMultiRegionAccessPoints')
389
self.assertEqual(self.operations_called[1][0].name, 'HeadObject')
390
self.assertEqual(self.operations_called[2][0].name, 'CopyObject')
391
self.assertEqual(self.operations_called[3][0].name, 'DeleteObject')
392
393
def test_skips_validation_if_keys_are_different_accesspoint_arn(self):
394
cmdline = (f"{self.prefix}s3://bucket/key "
395
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
396
"myaccesspoint/key2 "
397
"--validate-same-s3-paths")
398
self.assert_runs_mv_without_validation(cmdline)
399
400
def test_skips_validation_if_prefixes_are_different_accesspoint_arn(self):
401
cmdline = (f"{self.prefix}s3://bucket/key "
402
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
403
"myaccesspoint/prefix/ "
404
"--validate-same-s3-paths")
405
self.assert_runs_mv_without_validation(cmdline)
406
407
def test_skips_validation_if_keys_are_different_accesspoint_alias(self):
408
cmdline = (f"{self.prefix} s3://bucket/key "
409
"s3://myaccesspoint-foobar-s3alias/key2 "
410
"--validate-same-s3-paths")
411
self.assert_runs_mv_without_validation(cmdline)
412
413
def test_skips_validation_if_keys_are_different_outpost_arn(self):
414
cmdline = (f"{self.prefix}s3://bucket/key "
415
"s3://arn:aws:s3-outposts:us-east-1:123456789012:outpost/"
416
"op-foobar/accesspoint/myaccesspoint/key2 "
417
"--validate-same-s3-paths")
418
self.assert_runs_mv_without_validation(cmdline)
419
420
def test_skips_validation_if_keys_are_different_outpost_alias(self):
421
cmdline = (f"{self.prefix} s3://bucket/key "
422
"s3://myaccesspoint-foobar--op-s3/key2 "
423
"--validate-same-s3-paths")
424
self.assert_runs_mv_without_validation(cmdline)
425
426
@requires_crt
427
def test_skips_validation_if_keys_are_different_mrap_arn(self):
428
cmdline = (f"{self.prefix} s3://bucket/key "
429
"s3://arn:aws:s3::123456789012:accesspoint/foobar.mrap/key2 "
430
"--validate-same-s3-paths")
431
self.assert_runs_mv_without_validation(cmdline)
432
433
def test_raises_warning_if_validation_not_set(self):
434
cmdline = (f"{self.prefix}s3://bucket/key "
435
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
436
"myaccesspoint/key")
437
self.assert_raises_warning(cmdline)
438
439
def test_raises_warning_if_validation_not_set_source(self):
440
cmdline = (f"{self.prefix}"
441
"s3://arn:aws:s3:us-west-2:123456789012:accesspoint/"
442
"myaccesspoint/key "
443
"s3://bucket/key")
444
self.assert_raises_warning(cmdline)
445
446