Skip to content

Commit

Permalink
Disable S3 Express support for s3 sync command
Browse files Browse the repository at this point in the history
  • Loading branch information
kdaily committed Jan 23, 2024
1 parent 9bb4095 commit 99b79a0
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changes/next-release/bugfix-s3sync-90191.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "``s3 sync``",
"description": "Disable S3 Express support for s3 sync command"
}
16 changes: 16 additions & 0 deletions awscli/customizations/s3/subcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sys

from botocore.client import Config
from botocore.utils import is_s3express_bucket
from dateutil.parser import parse
from dateutil.tz import tzlocal

Expand Down Expand Up @@ -1166,6 +1167,21 @@ def add_paths(self, paths):
self._validate_streaming_paths()
self._validate_path_args()
self._validate_sse_c_args()
self._validate_not_s3_express_bucket_for_sync()

def _validate_not_s3_express_bucket_for_sync(self):
if self.cmd == 'sync' and \
(self._is_s3express_path(self.parameters['src']) or
self._is_s3express_path(self.parameters['dest'])):
raise ValueError(
"Cannot use sync command with a directory bucket."
)

def _is_s3express_path(self, path):
if path.startswith("s3://"):
bucket = split_s3_bucket_key(path)[0]
return is_s3express_bucket(bucket)
return False

def _validate_streaming_paths(self):
self.parameters['is_stream'] = False
Expand Down
10 changes: 10 additions & 0 deletions awscli/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -997,3 +997,13 @@ def wait(self, check, *args, **kwargs):
def _fail_message(self, attempts, successes):
format_args = (attempts, successes)
return 'Failed after %s attempts, only had %s successes' % format_args


@contextlib.contextmanager
def cd(path):
try:
original_dir = os.getcwd()
os.chdir(path)
yield
finally:
os.chdir(original_dir)
40 changes: 39 additions & 1 deletion tests/functional/s3/test_sync_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import os

from awscli.compat import six
from awscli.testutils import mock
from awscli.testutils import mock, cd
from tests.functional.s3 import BaseS3TransferCommandTest


Expand Down Expand Up @@ -287,3 +287,41 @@ def test_with_accesspoint_arn(self):
self.get_object_request(accesspoint_arn, 'mykey')
]
)


class TestSyncCommandWithS3Express(BaseS3TransferCommandTest):

prefix = 's3 sync '

def test_incompatible_with_sync_upload(self):
cmdline = '%s localdirectory/ s3://testdirectorybucket--usw2-az1--x-s3/' % self.prefix
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
self.assertIn('Cannot use sync command with a directory bucket.', stderr)

def test_incompatible_with_sync_download(self):
cmdline = '%s s3://testdirectorybucket--usw2-az1--x-s3/ localdirectory/' % self.prefix
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
self.assertIn('Cannot use sync command with a directory bucket.', stderr)

def test_incompatible_with_sync_copy(self):
cmdline = '%s s3://bucket/ s3://testdirectorybucket--usw2-az1--x-s3/' % self.prefix
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
self.assertIn('Cannot use sync command with a directory bucket.', stderr)

def test_incompatible_with_sync_with_delete(self):
cmdline = '%s s3://bucket/ s3://testdirectorybucket--usw2-az1--x-s3/ --delete' % self.prefix
stderr = self.run_cmd(cmdline, expected_rc=255)[1]
self.assertIn('Cannot use sync command with a directory bucket.', stderr)

def test_compatible_with_sync_with_local_directory_like_directory_bucket(self):
self.parsed_responses = [
{'Contents': []}
]

cmdline = '%s s3://bucket/ testdirectorybucket--usw2-az1--x-s3/' % self.prefix
with cd(self.files.rootdir):
_, stderr, _ = self.run_cmd(cmdline)

# Just asserting that command validated and made an API call
self.assertEqual(len(self.operations_called), 1)
self.assertEqual(self.operations_called[0][0].name, 'ListObjectsV2')

0 comments on commit 99b79a0

Please sign in to comment.