diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py index 1ae6900aa..aec003f0e 100644 --- a/moto/s3/exceptions.py +++ b/moto/s3/exceptions.py @@ -26,6 +26,10 @@ ERROR_WITH_RANGE = """{% extends 'single_error' %} {{ range_requested }}{% endblock %} """ +ERROR_WITH_STORAGE_CLASS = """{% extends 'single_error' %} +{% block extra %}{{ storage_class }}{% endblock %} +""" + class S3ClientError(RESTError): # S3 API uses as the XML tag in response messages @@ -501,6 +505,20 @@ class InvalidContinuationToken(S3ClientError): ) +class InvalidObjectState(BucketError): + code = 400 + + def __init__(self, storage_class, **kwargs): + kwargs.setdefault("template", "storage_error") + self.templates["storage_error"] = ERROR_WITH_STORAGE_CLASS + super(BucketError, self).__init__( + error_type="InvalidObjectState", + message="The operation is not valid for the object's storage class", + storage_class=storage_class, + **kwargs, + ) + + class LockNotEnabled(S3ClientError): code = 400 diff --git a/moto/s3/responses.py b/moto/s3/responses.py index d47273655..7dc682758 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -46,6 +46,7 @@ from .exceptions import ( IllegalLocationConstraintException, InvalidNotificationARN, InvalidNotificationEvent, + InvalidObjectState, ObjectNotInActiveTierError, NoSystemTags, PreconditionFailed, @@ -1356,6 +1357,8 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): elif key is None: raise MissingVersion() + if key.storage_class == "GLACIER": + raise InvalidObjectState(storage_class="GLACIER") if if_unmodified_since: if_unmodified_since = str_to_rfc_1123_datetime(if_unmodified_since) if key.last_modified > if_unmodified_since: diff --git a/tests/test_s3/test_s3_storageclass.py b/tests/test_s3/test_s3_storageclass.py index 10e6ff7d8..8eac2e0d2 100644 --- a/tests/test_s3/test_s3_storageclass.py +++ b/tests/test_s3/test_s3_storageclass.py @@ -247,3 +247,22 @@ def test_s3_copy_object_for_deep_archive_storage_class_restored(): s3.head_object(Bucket="Bucket2", Key="Second_Object").should.not_have.property( "Restore" ) + + +@mock_s3 +def test_s3_get_object_from_glacier(): + s3 = boto3.client("s3", region_name="us-east-1") + bucket_name = "tests3getobjectfromglacier" + s3.create_bucket(Bucket=bucket_name) + + s3.put_object( + Bucket=bucket_name, Key="test.txt", Body="contents", StorageClass="GLACIER" + ) + with pytest.raises(ClientError) as exc: + s3.get_object(Bucket=bucket_name, Key="test.txt") + err = exc.value.response["Error"] + err["Code"].should.equal("InvalidObjectState") + err["Message"].should.equal( + "The operation is not valid for the object's storage class" + ) + err["StorageClass"].should.equal("GLACIER")