Skip to content

Commit 3057f21

Browse files
change: Add bucket owner check (#4150)
1 parent 16bd4bf commit 3057f21

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

src/sagemaker/session.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ def __init__(
233233
self._default_bucket_name_override = default_bucket
234234
# this may also be set again inside :func:`_initialize` if it is None
235235
self.default_bucket_prefix = default_bucket_prefix
236+
self._default_bucket_set_by_sdk = False
236237

237238
self.s3_resource = None
238239
self.s3_client = None
@@ -545,8 +546,12 @@ def default_bucket(self):
545546
default_bucket = self._default_bucket_name_override
546547
if not default_bucket:
547548
default_bucket = generate_default_sagemaker_bucket_name(self.boto_session)
549+
self._default_bucket_set_by_sdk = True
548550

549-
self._create_s3_bucket_if_it_does_not_exist(bucket_name=default_bucket, region=region)
551+
self._create_s3_bucket_if_it_does_not_exist(
552+
bucket_name=default_bucket,
553+
region=region,
554+
)
550555

551556
self._default_bucket = default_bucket
552557

@@ -620,6 +625,28 @@ def _create_s3_bucket_if_it_does_not_exist(self, bucket_name, region):
620625
else:
621626
raise
622627

628+
if self._default_bucket_set_by_sdk:
629+
# make sure the s3 bucket is configured in users account.
630+
expected_bucket_owner_id = self.account_id()
631+
try:
632+
s3.meta.client.head_bucket(
633+
Bucket=bucket_name, ExpectedBucketOwner=expected_bucket_owner_id
634+
)
635+
except ClientError as e:
636+
error_code = e.response["Error"]["Code"]
637+
message = e.response["Error"]["Message"]
638+
if error_code == "403" and message == "Forbidden":
639+
LOGGER.error(
640+
"Since default_bucket param was not set, SageMaker Python SDK tried to use "
641+
"%s bucket. "
642+
"This bucket cannot be configured to use as it is not owned by Account %s. "
643+
"To unblock it's recommended to use custom default_bucket "
644+
"parameter in sagemaker.Session",
645+
bucket_name,
646+
expected_bucket_owner_id,
647+
)
648+
raise
649+
623650
def _append_sagemaker_config_tags(self, tags: list, config_path_to_tags: str):
624651
"""Appends tags specified in the sagemaker_config to the given list of tags.
625652

tests/unit/test_default_bucket.py

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,22 @@
1212
# language governing permissions and limitations under the License.
1313
from __future__ import absolute_import
1414

15+
import datetime
1516
import pytest
1617
from botocore.exceptions import ClientError
17-
from mock import MagicMock, patch
18+
from mock import MagicMock
1819
import sagemaker
1920

2021
ACCOUNT_ID = "123"
2122
REGION = "us-west-2"
2223
DEFAULT_BUCKET_NAME = "sagemaker-{}-{}".format(REGION, ACCOUNT_ID)
2324

2425

26+
@pytest.fixture
27+
def datetime_obj():
28+
return datetime.datetime(2017, 6, 16, 15, 55, 0)
29+
30+
2531
@pytest.fixture()
2632
def sagemaker_session():
2733
boto_mock = MagicMock(name="boto_session", region_name=REGION)
@@ -50,23 +56,53 @@ def test_default_bucket_s3_create_call(sagemaker_session):
5056
assert sagemaker_session._default_bucket == bucket_name
5157

5258

53-
def test_default_bucket_s3_needs_access(sagemaker_session):
54-
with patch("logging.Logger.error") as mocked_error_log:
55-
with pytest.raises(ClientError):
56-
error = ClientError(
57-
error_response={"Error": {"Code": "403", "Message": "Forbidden"}},
58-
operation_name="foo",
59-
)
60-
sagemaker_session.boto_session.resource(
61-
"s3"
62-
).meta.client.head_bucket.side_effect = error
63-
sagemaker_session.default_bucket()
64-
mocked_error_log.assert_called_once_with(
65-
"Bucket %s exists, but access is forbidden. Please try again after "
66-
"adding appropriate access.",
67-
DEFAULT_BUCKET_NAME,
68-
)
69-
assert sagemaker_session._default_bucket is None
59+
def test_default_bucket_s3_needs_access(sagemaker_session, caplog):
60+
with pytest.raises(ClientError):
61+
error = ClientError(
62+
error_response={"Error": {"Code": "403", "Message": "Forbidden"}},
63+
operation_name="foo",
64+
)
65+
sagemaker_session.boto_session.resource("s3").meta.client.head_bucket.side_effect = error
66+
sagemaker_session.default_bucket()
67+
error_message = (
68+
" exists, but access is forbidden. Please try again after adding appropriate access."
69+
)
70+
assert error_message in caplog.text
71+
assert sagemaker_session._default_bucket is None
72+
73+
74+
def test_default_bucket_s3_needs_bucket_owner_access(sagemaker_session, datetime_obj, caplog):
75+
with pytest.raises(ClientError):
76+
error = ClientError(
77+
error_response={"Error": {"Code": "403", "Message": "Forbidden"}},
78+
operation_name="foo",
79+
)
80+
sagemaker_session.boto_session.resource("s3").meta.client.head_bucket.side_effect = error
81+
# bucket exists
82+
sagemaker_session.boto_session.resource("s3").Bucket(
83+
name=DEFAULT_BUCKET_NAME
84+
).creation_date = datetime_obj
85+
sagemaker_session.default_bucket()
86+
87+
error_message = "This bucket cannot be configured to use as it is not owned by Account"
88+
assert error_message in caplog.text
89+
assert sagemaker_session._default_bucket is None
90+
91+
92+
def test_default_bucket_s3_custom_bucket_input(sagemaker_session, datetime_obj, caplog):
93+
sagemaker_session._default_bucket_name_override = "custom-bucket-override"
94+
error = ClientError(
95+
error_response={"Error": {"Code": "403", "Message": "Forbidden"}},
96+
operation_name="foo",
97+
)
98+
sagemaker_session.boto_session.resource("s3").meta.client.head_bucket.side_effect = error
99+
# bucket exists
100+
sagemaker_session.boto_session.resource("s3").Bucket(
101+
name=DEFAULT_BUCKET_NAME
102+
).creation_date = datetime_obj
103+
# This should not raise ClientError as no head_bucket call is expected for custom bucket
104+
sagemaker_session.default_bucket()
105+
assert sagemaker_session._default_bucket == "custom-bucket-override"
70106

71107

72108
def test_default_already_cached(sagemaker_session):

0 commit comments

Comments
 (0)