Add dynamodb continuous backups (#2976)
* remove print statement * Add dynamodb.describe_continuous_backups * Add dynamodb.update_continuous_backups * Fix Python 2 timestamp error
This commit is contained in:
parent
9e7803dc36
commit
65e790c4eb
@ -316,6 +316,12 @@ class Table(BaseModel):
|
||||
}
|
||||
self.set_stream_specification(streams)
|
||||
self.lambda_event_source_mappings = {}
|
||||
self.continuous_backups = {
|
||||
"ContinuousBackupsStatus": "ENABLED", # One of 'ENABLED'|'DISABLED', it's enabled by default
|
||||
"PointInTimeRecoveryDescription": {
|
||||
"PointInTimeRecoveryStatus": "DISABLED" # One of 'ENABLED'|'DISABLED'
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
@ -1246,6 +1252,33 @@ class DynamoDBBackend(BaseBackend):
|
||||
self.tables = original_table_state
|
||||
raise
|
||||
|
||||
def describe_continuous_backups(self, table_name):
|
||||
table = self.get_table(table_name)
|
||||
|
||||
return table.continuous_backups
|
||||
|
||||
def update_continuous_backups(self, table_name, point_in_time_spec):
|
||||
table = self.get_table(table_name)
|
||||
|
||||
if (
|
||||
point_in_time_spec["PointInTimeRecoveryEnabled"]
|
||||
and table.continuous_backups["PointInTimeRecoveryDescription"][
|
||||
"PointInTimeRecoveryStatus"
|
||||
]
|
||||
== "DISABLED"
|
||||
):
|
||||
table.continuous_backups["PointInTimeRecoveryDescription"] = {
|
||||
"PointInTimeRecoveryStatus": "ENABLED",
|
||||
"EarliestRestorableDateTime": unix_time(),
|
||||
"LatestRestorableDateTime": unix_time(),
|
||||
}
|
||||
elif not point_in_time_spec["PointInTimeRecoveryEnabled"]:
|
||||
table.continuous_backups["PointInTimeRecoveryDescription"] = {
|
||||
"PointInTimeRecoveryStatus": "DISABLED"
|
||||
}
|
||||
|
||||
return table.continuous_backups
|
||||
|
||||
|
||||
dynamodb_backends = {}
|
||||
for region in Session().get_available_regions("dynamodb"):
|
||||
|
@ -936,3 +936,32 @@ class DynamoHandler(BaseResponse):
|
||||
)
|
||||
response = {"ConsumedCapacity": [], "ItemCollectionMetrics": {}}
|
||||
return dynamo_json_dump(response)
|
||||
|
||||
def describe_continuous_backups(self):
|
||||
name = self.body["TableName"]
|
||||
|
||||
if self.dynamodb_backend.get_table(name) is None:
|
||||
return self.error(
|
||||
"com.amazonaws.dynamodb.v20111205#TableNotFoundException",
|
||||
"Table not found: {}".format(name),
|
||||
)
|
||||
|
||||
response = self.dynamodb_backend.describe_continuous_backups(name)
|
||||
|
||||
return json.dumps({"ContinuousBackupsDescription": response})
|
||||
|
||||
def update_continuous_backups(self):
|
||||
name = self.body["TableName"]
|
||||
point_in_time_spec = self.body["PointInTimeRecoverySpecification"]
|
||||
|
||||
if self.dynamodb_backend.get_table(name) is None:
|
||||
return self.error(
|
||||
"com.amazonaws.dynamodb.v20111205#TableNotFoundException",
|
||||
"Table not found: {}".format(name),
|
||||
)
|
||||
|
||||
response = self.dynamodb_backend.update_continuous_backups(
|
||||
name, point_in_time_spec
|
||||
)
|
||||
|
||||
return json.dumps({"ContinuousBackupsDescription": response})
|
||||
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
import boto
|
||||
@ -2049,6 +2050,141 @@ def test_set_ttl():
|
||||
resp["TimeToLiveDescription"]["TimeToLiveStatus"].should.equal("DISABLED")
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_describe_continuous_backups():
|
||||
# given
|
||||
client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
table_name = client.create_table(
|
||||
TableName="test",
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "client", "AttributeType": "S"},
|
||||
{"AttributeName": "app", "AttributeType": "S"},
|
||||
],
|
||||
KeySchema=[
|
||||
{"AttributeName": "client", "KeyType": "HASH"},
|
||||
{"AttributeName": "app", "KeyType": "RANGE"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)["TableDescription"]["TableName"]
|
||||
|
||||
# when
|
||||
response = client.describe_continuous_backups(TableName=table_name)
|
||||
|
||||
# then
|
||||
response["ContinuousBackupsDescription"].should.equal(
|
||||
{
|
||||
"ContinuousBackupsStatus": "ENABLED",
|
||||
"PointInTimeRecoveryDescription": {"PointInTimeRecoveryStatus": "DISABLED"},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_describe_continuous_backups_errors():
|
||||
# given
|
||||
client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
|
||||
# when
|
||||
with assert_raises(Exception) as e:
|
||||
client.describe_continuous_backups(TableName="not-existing-table")
|
||||
|
||||
# then
|
||||
ex = e.exception
|
||||
ex.operation_name.should.equal("DescribeContinuousBackups")
|
||||
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
||||
ex.response["Error"]["Code"].should.contain("TableNotFoundException")
|
||||
ex.response["Error"]["Message"].should.equal("Table not found: not-existing-table")
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_update_continuous_backups():
|
||||
# given
|
||||
client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
table_name = client.create_table(
|
||||
TableName="test",
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "client", "AttributeType": "S"},
|
||||
{"AttributeName": "app", "AttributeType": "S"},
|
||||
],
|
||||
KeySchema=[
|
||||
{"AttributeName": "client", "KeyType": "HASH"},
|
||||
{"AttributeName": "app", "KeyType": "RANGE"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)["TableDescription"]["TableName"]
|
||||
|
||||
# when
|
||||
response = client.update_continuous_backups(
|
||||
TableName=table_name,
|
||||
PointInTimeRecoverySpecification={"PointInTimeRecoveryEnabled": True},
|
||||
)
|
||||
|
||||
# then
|
||||
response["ContinuousBackupsDescription"]["ContinuousBackupsStatus"].should.equal(
|
||||
"ENABLED"
|
||||
)
|
||||
point_in_time = response["ContinuousBackupsDescription"][
|
||||
"PointInTimeRecoveryDescription"
|
||||
]
|
||||
earliest_datetime = point_in_time["EarliestRestorableDateTime"]
|
||||
earliest_datetime.should.be.a(datetime)
|
||||
latest_datetime = point_in_time["LatestRestorableDateTime"]
|
||||
latest_datetime.should.be.a(datetime)
|
||||
point_in_time["PointInTimeRecoveryStatus"].should.equal("ENABLED")
|
||||
|
||||
# when
|
||||
# a second update should not change anything
|
||||
response = client.update_continuous_backups(
|
||||
TableName=table_name,
|
||||
PointInTimeRecoverySpecification={"PointInTimeRecoveryEnabled": True},
|
||||
)
|
||||
|
||||
# then
|
||||
response["ContinuousBackupsDescription"]["ContinuousBackupsStatus"].should.equal(
|
||||
"ENABLED"
|
||||
)
|
||||
point_in_time = response["ContinuousBackupsDescription"][
|
||||
"PointInTimeRecoveryDescription"
|
||||
]
|
||||
point_in_time["EarliestRestorableDateTime"].should.equal(earliest_datetime)
|
||||
point_in_time["LatestRestorableDateTime"].should.equal(latest_datetime)
|
||||
point_in_time["PointInTimeRecoveryStatus"].should.equal("ENABLED")
|
||||
|
||||
# when
|
||||
response = client.update_continuous_backups(
|
||||
TableName=table_name,
|
||||
PointInTimeRecoverySpecification={"PointInTimeRecoveryEnabled": False},
|
||||
)
|
||||
|
||||
# then
|
||||
response["ContinuousBackupsDescription"].should.equal(
|
||||
{
|
||||
"ContinuousBackupsStatus": "ENABLED",
|
||||
"PointInTimeRecoveryDescription": {"PointInTimeRecoveryStatus": "DISABLED"},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_update_continuous_backups_errors():
|
||||
# given
|
||||
client = boto3.client("dynamodb", region_name="us-east-1")
|
||||
|
||||
# when
|
||||
with assert_raises(Exception) as e:
|
||||
client.update_continuous_backups(
|
||||
TableName="not-existing-table",
|
||||
PointInTimeRecoverySpecification={"PointInTimeRecoveryEnabled": True},
|
||||
)
|
||||
|
||||
# then
|
||||
ex = e.exception
|
||||
ex.operation_name.should.equal("UpdateContinuousBackups")
|
||||
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
||||
ex.response["Error"]["Code"].should.contain("TableNotFoundException")
|
||||
ex.response["Error"]["Message"].should.equal("Table not found: not-existing-table")
|
||||
|
||||
|
||||
# https://github.com/spulec/moto/issues/1043
|
||||
@mock_dynamodb2
|
||||
def test_query_missing_expr_names():
|
||||
|
@ -324,7 +324,6 @@ def test_get_parameters_errors():
|
||||
", ".join(ssm_parameters.keys())
|
||||
)
|
||||
)
|
||||
print(ex.response["Error"]["Message"])
|
||||
|
||||
|
||||
@mock_ssm
|
||||
|
Loading…
Reference in New Issue
Block a user