diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py index 3abda1f78..e8a2c4c9b 100644 --- a/moto/s3/exceptions.py +++ b/moto/s3/exceptions.py @@ -63,6 +63,8 @@ class BucketAlreadyExists(BucketError): code = 409 def __init__(self, *args, **kwargs): + kwargs.setdefault("template", "bucket_error") + self.templates["bucket_error"] = ERROR_WITH_BUCKET_NAME super(BucketAlreadyExists, self).__init__( "BucketAlreadyExists", ( diff --git a/moto/s3/responses.py b/moto/s3/responses.py index aa867918b..215fb0a13 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -811,11 +811,16 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): try: new_bucket = self.backend.create_bucket(bucket_name, region_name) except BucketAlreadyExists: - if region_name == DEFAULT_REGION_NAME: - # us-east-1 has different behavior - new_bucket = self.backend.get_bucket(bucket_name) + new_bucket = self.backend.get_bucket(bucket_name) + if ( + new_bucket.region_name == DEFAULT_REGION_NAME + and region_name == DEFAULT_REGION_NAME + ): + # us-east-1 has different behavior - creating a bucket there is an idempotent operation + pass else: - raise + template = self.response_template(S3_DUPLICATE_BUCKET_ERROR) + return 409, {}, template.render(bucket_name=bucket_name) if "x-amz-acl" in request.headers: # TODO: Support the XML-based ACL format @@ -2662,3 +2667,13 @@ S3_BUCKET_LOCK_CONFIGURATION = """ # """ + +S3_DUPLICATE_BUCKET_ERROR = """ + + BucketAlreadyOwnedByYou + Your previous request to create the named bucket succeeded and you already own it. + {{ bucket_name }} + 44425877V1D0A2F9 + 9Gjjt1m+cjU4OPvX9O9/8RuvnG41MRb/18Oux2o5H5MY7ISNTlXN+Dz9IG62/ILVxhAGI0qyPfg= + +""" diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 540be8239..ffa758595 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -2106,19 +2106,6 @@ def test_boto3_bucket_create(): ) -@mock_s3 -def test_bucket_create_duplicate(): - s3 = boto3.resource("s3", region_name="us-west-2") - s3.create_bucket( - Bucket="blah", CreateBucketConfiguration={"LocationConstraint": "us-west-2"} - ) - with pytest.raises(ClientError) as exc: - s3.create_bucket( - Bucket="blah", CreateBucketConfiguration={"LocationConstraint": "us-west-2"} - ) - exc.value.response["Error"]["Code"].should.equal("BucketAlreadyExists") - - @mock_s3 def test_bucket_create_force_us_east_1(): s3 = boto3.resource("s3", region_name=DEFAULT_REGION_NAME) @@ -5353,3 +5340,74 @@ def test_get_object_versions_with_prefix(): versions = s3_client.list_object_versions(Bucket=bucket_name, Prefix="file") versions["Versions"].should.have.length_of(3) versions["Prefix"].should.equal("file") + + +@mock_s3 +def test_create_bucket_duplicate(): + bucket_name = "same-bucket-test-1371" + alternate_region = "eu-north-1" + # Create it in the default region + default_client = boto3.client("s3", region_name=DEFAULT_REGION_NAME) + default_client.create_bucket(Bucket=bucket_name) + + # Create it again in the same region - should just return that same bucket + default_client.create_bucket(Bucket=bucket_name) + + # Create the bucket in a different region - should return an error + diff_client = boto3.client("s3", region_name=alternate_region) + with pytest.raises(ClientError) as ex: + diff_client.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={"LocationConstraint": alternate_region}, + ) + err = ex.value.response["Error"] + err["Code"].should.equal("BucketAlreadyOwnedByYou") + err["Message"].should.equal( + "Your previous request to create the named bucket succeeded and you already own it." + ) + err["BucketName"].should.equal(bucket_name) + + # Try this again - but creating the bucket in a non-default region in the first place + bucket_name = "same-bucket-nondefault-region-test-1371" + diff_client.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={"LocationConstraint": alternate_region}, + ) + + # Recreating the bucket in the same non-default region should fail + with pytest.raises(ClientError) as ex: + diff_client.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={"LocationConstraint": alternate_region}, + ) + err = ex.value.response["Error"] + err["Code"].should.equal("BucketAlreadyOwnedByYou") + err["Message"].should.equal( + "Your previous request to create the named bucket succeeded and you already own it." + ) + err["BucketName"].should.equal(bucket_name) + + # Recreating the bucket in the default region should fail + diff_client = boto3.client("s3", region_name=DEFAULT_REGION_NAME) + with pytest.raises(ClientError) as ex: + diff_client.create_bucket(Bucket=bucket_name) + err = ex.value.response["Error"] + err["Code"].should.equal("BucketAlreadyOwnedByYou") + err["Message"].should.equal( + "Your previous request to create the named bucket succeeded and you already own it." + ) + err["BucketName"].should.equal(bucket_name) + + # Recreating the bucket in a third region should fail + diff_client = boto3.client("s3", region_name="ap-northeast-1") + with pytest.raises(ClientError) as ex: + diff_client.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={"LocationConstraint": "ap-northeast-1"}, + ) + err = ex.value.response["Error"] + err["Code"].should.equal("BucketAlreadyOwnedByYou") + err["Message"].should.equal( + "Your previous request to create the named bucket succeeded and you already own it." + ) + err["BucketName"].should.equal(bucket_name)