diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
index 52d8faced..3997b0afe 100644
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -1,9 +1,40 @@
from __future__ import unicode_literals
+from moto.core.exceptions import RESTError
-class BucketAlreadyExists(Exception):
+ERROR_WITH_BUCKET_NAME = """{% extends 'error' %}
+{% block extra %}{{ bucket }}{% endblock %}
+"""
+
+
+class S3ClientError(RESTError):
pass
-class MissingBucket(Exception):
- pass
+class BucketError(S3ClientError):
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('template', 'bucket_error')
+ self.templates['bucket_error'] = ERROR_WITH_BUCKET_NAME
+ super(BucketError, self).__init__(*args, **kwargs)
+
+
+class BucketAlreadyExists(BucketError):
+ code = 409
+
+ def __init__(self, *args, **kwargs):
+ super(BucketAlreadyExists, self).__init__(
+ "BucketAlreadyExists",
+ ("The requested bucket name is not available. The bucket "
+ "namespace is shared by all users of the system. Please "
+ "select a different name and try again"),
+ *args, **kwargs)
+
+
+class MissingBucket(BucketError):
+ code = 404
+
+ def __init__(self, *args, **kwargs):
+ super(MissingBucket, self).__init__(
+ "NoSuchBucket",
+ "The specified bucket does not exist",
+ *args, **kwargs)
diff --git a/moto/s3/models.py b/moto/s3/models.py
index 2a78a8003..0f88f76d4 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -191,7 +191,7 @@ class S3Backend(BaseBackend):
def create_bucket(self, bucket_name, region_name):
if bucket_name in self.buckets:
- raise BucketAlreadyExists()
+ raise BucketAlreadyExists(bucket=bucket_name)
new_bucket = FakeBucket(name=bucket_name, region_name=region_name)
self.buckets[bucket_name] = new_bucket
return new_bucket
@@ -203,7 +203,7 @@ class S3Backend(BaseBackend):
try:
return self.buckets[bucket_name]
except KeyError:
- raise MissingBucket()
+ raise MissingBucket(bucket=bucket_name)
def delete_bucket(self, bucket_name):
bucket = self.get_bucket(bucket_name)
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index 750bf68ba..d9ef63012 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -7,7 +7,7 @@ from six.moves.urllib.parse import parse_qs, urlparse
from moto.core.responses import _TemplateEnvironmentMixin
-from .exceptions import BucketAlreadyExists, MissingBucket
+from .exceptions import BucketAlreadyExists, S3ClientError
from .models import s3_backend
from .utils import bucket_name_from_url, metadata_from_headers
from xml.dom import minidom
@@ -35,8 +35,8 @@ class ResponseObject(_TemplateEnvironmentMixin):
def bucket_response(self, request, full_url, headers):
try:
response = self._bucket_response(request, full_url, headers)
- except MissingBucket:
- return 404, headers, ""
+ except S3ClientError as s3error:
+ response = s3error.code, headers, s3error.description
if isinstance(response, six.string_types):
return 200, headers, response.encode("utf-8")
@@ -72,12 +72,8 @@ class ResponseObject(_TemplateEnvironmentMixin):
raise NotImplementedError("Method {0} has not been impelemented in the S3 backend yet".format(method))
def _bucket_response_head(self, bucket_name, headers):
- try:
- self.backend.get_bucket(bucket_name)
- except MissingBucket:
- return 404, headers, ""
- else:
- return 200, headers, ""
+ self.backend.get_bucket(bucket_name)
+ return 200, headers, ""
def _bucket_response_get(self, bucket_name, querystring, headers):
if 'uploads' in querystring:
@@ -127,11 +123,7 @@ class ResponseObject(_TemplateEnvironmentMixin):
is_truncated='false',
)
- try:
- bucket = self.backend.get_bucket(bucket_name)
- except MissingBucket:
- return 404, headers, ""
-
+ bucket = self.backend.get_bucket(bucket_name)
prefix = querystring.get('prefix', [None])[0]
delimiter = querystring.get('delimiter', [None])[0]
result_keys, result_folders = self.backend.prefix_query(bucket, prefix, delimiter)
@@ -161,17 +153,12 @@ class ResponseObject(_TemplateEnvironmentMixin):
# us-east-1 has different behavior
new_bucket = self.backend.get_bucket(bucket_name)
else:
- return 409, headers, ""
+ raise
template = self.response_template(S3_BUCKET_CREATE_RESPONSE)
return 200, headers, template.render(bucket=new_bucket)
def _bucket_response_delete(self, bucket_name, headers):
- try:
- removed_bucket = self.backend.delete_bucket(bucket_name)
- except MissingBucket:
- # Non-existant bucket
- template = self.response_template(S3_DELETE_NON_EXISTING_BUCKET)
- return 404, headers, template.render(bucket_name=bucket_name)
+ removed_bucket = self.backend.delete_bucket(bucket_name)
if removed_bucket:
# Bucket exists
@@ -231,8 +218,8 @@ class ResponseObject(_TemplateEnvironmentMixin):
def key_response(self, request, full_url, headers):
try:
response = self._key_response(request, full_url, headers)
- except MissingBucket:
- return 404, headers, ""
+ except S3ClientError as s3error:
+ response = s3error.code, headers, s3error.description
if isinstance(response, six.string_types):
return 200, headers, response
@@ -461,14 +448,6 @@ S3_DELETE_BUCKET_SUCCESS = """
-NoSuchBucket
-The specified bucket does not exist
-{{ bucket_name }}
-asdfasdfsadf
-asfasdfsfsafasdf
-"""
-
S3_DELETE_BUCKET_WITH_ITEMS_ERROR = """
BucketNotEmpty
The bucket you tried to delete is not empty