Add state transition for restoring objects from S3 glacier (#5202)

This commit is contained in:
Daniel Birnstiel 2022-06-09 11:41:57 +02:00 committed by GitHub
parent 0bf7057866
commit f282cb03f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 3 deletions

View File

@ -56,6 +56,16 @@ Advancement:
Call `boto3.client("dax").describe_clusters(..)`. Call `boto3.client("dax").describe_clusters(..)`.
Service: S3 (Glacier Restoration)
---------------
**Model**: `s3::keyrestore` :raw-html:`<br />`
Available States:
None --> "IN_PROGRESS" --> "RESTORED"
Transition type: Immediate - transitions immediately
Service: Support Service: Support
------------------ ------------------

View File

@ -32,6 +32,8 @@ from moto.core.utils import (
BackendDict, BackendDict,
) )
from moto.cloudwatch.models import MetricDatum from moto.cloudwatch.models import MetricDatum
from moto.moto_api import state_manager
from moto.moto_api._internal.managed_state_model import ManagedState
from moto.utilities.tagging_service import TaggingService from moto.utilities.tagging_service import TaggingService
from moto.utilities.utils import LowercaseDict, md5_hash from moto.utilities.utils import LowercaseDict, md5_hash
from moto.s3.exceptions import ( from moto.s3.exceptions import (
@ -101,7 +103,7 @@ class FakeDeleteMarker(BaseModel):
return self._version_id return self._version_id
class FakeKey(BaseModel): class FakeKey(BaseModel, ManagedState):
def __init__( def __init__(
self, self,
name, name,
@ -121,6 +123,14 @@ class FakeKey(BaseModel):
lock_until=None, lock_until=None,
s3_backend=None, s3_backend=None,
): ):
ManagedState.__init__(
self,
"s3::keyrestore",
transitions=[
(None, "IN_PROGRESS"),
("IN_PROGRESS", "RESTORED"),
],
)
self.name = name self.name = name
self.last_modified = datetime.datetime.utcnow() self.last_modified = datetime.datetime.utcnow()
self.acl = get_canned_acl("private") self.acl = get_canned_acl("private")
@ -256,8 +266,13 @@ class FakeKey(BaseModel):
if self._storage_class != "STANDARD": if self._storage_class != "STANDARD":
res["x-amz-storage-class"] = self._storage_class res["x-amz-storage-class"] = self._storage_class
if self._expiry is not None: if self._expiry is not None:
rhdr = 'ongoing-request="false", expiry-date="{0}"' if self.status == "IN_PROGRESS":
res["x-amz-restore"] = rhdr.format(self.expiry_date) header = 'ongoing-request="true"'
else:
header = 'ongoing-request="false", expiry-date="{0}"'.format(
self.expiry_date
)
res["x-amz-restore"] = header
if self._is_versioned: if self._is_versioned:
res["x-amz-version-id"] = str(self.version_id) res["x-amz-version-id"] = str(self.version_id)
@ -1380,6 +1395,10 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
self.buckets = {} self.buckets = {}
self.tagger = TaggingService() self.tagger = TaggingService()
state_manager.register_default_transition(
"s3::keyrestore", transition={"progression": "immediate"}
)
@property @property
def _url_module(self): def _url_module(self):
# The urls-property can be different depending on env variables # The urls-property can be different depending on env variables
@ -1741,6 +1760,7 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
key = key.multipart.parts[part_number] key = key.multipart.parts[part_number]
if isinstance(key, FakeKey): if isinstance(key, FakeKey):
key.advance()
return key return key
else: else:
return None return None

View File

@ -15,6 +15,7 @@ from botocore.handlers import disable_signing
from freezegun import freeze_time from freezegun import freeze_time
import requests import requests
from moto.moto_api import state_manager
from moto.s3.responses import DEFAULT_REGION_NAME from moto.s3.responses import DEFAULT_REGION_NAME
from unittest import SkipTest from unittest import SkipTest
import pytest import pytest
@ -549,6 +550,38 @@ def test_restore_key():
) )
@freeze_time("2012-01-01 12:00:00")
@mock_s3
def test_restore_key_transition():
if settings.TEST_SERVER_MODE:
raise SkipTest("Can't set transition directly in ServerMode")
state_manager.set_transition(
model_name="s3::keyrestore", transition={"progression": "manual", "times": 1}
)
s3 = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)
bucket = s3.Bucket("foobar")
bucket.create()
key = bucket.put_object(Key="the-key", Body=b"somedata", StorageClass="GLACIER")
key.restore.should.equal(None)
key.restore_object(RestoreRequest={"Days": 1})
# first call: there should be an ongoing request
key.restore.should.contain('ongoing-request="true"')
# second call: request should be done
key.load()
key.restore.should.contain('ongoing-request="false"')
# third call: request should still be done
key.load()
key.restore.should.contain('ongoing-request="false"')
state_manager.unset_transition(model_name="s3::keyrestore")
@mock_s3 @mock_s3
def test_cannot_restore_standard_class_object(): def test_cannot_restore_standard_class_object():
s3 = boto3.resource("s3", region_name=DEFAULT_REGION_NAME) s3 = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)