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.set_stream_specification(streams)
|
||||||
self.lambda_event_source_mappings = {}
|
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
|
@classmethod
|
||||||
def create_from_cloudformation_json(
|
def create_from_cloudformation_json(
|
||||||
@ -1246,6 +1252,33 @@ class DynamoDBBackend(BaseBackend):
|
|||||||
self.tables = original_table_state
|
self.tables = original_table_state
|
||||||
raise
|
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 = {}
|
dynamodb_backends = {}
|
||||||
for region in Session().get_available_regions("dynamodb"):
|
for region in Session().get_available_regions("dynamodb"):
|
||||||
|
@ -936,3 +936,32 @@ class DynamoHandler(BaseResponse):
|
|||||||
)
|
)
|
||||||
response = {"ConsumedCapacity": [], "ItemCollectionMetrics": {}}
|
response = {"ConsumedCapacity": [], "ItemCollectionMetrics": {}}
|
||||||
return dynamo_json_dump(response)
|
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 __future__ import unicode_literals, print_function
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import boto
|
import boto
|
||||||
@ -2049,6 +2050,141 @@ def test_set_ttl():
|
|||||||
resp["TimeToLiveDescription"]["TimeToLiveStatus"].should.equal("DISABLED")
|
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
|
# https://github.com/spulec/moto/issues/1043
|
||||||
@mock_dynamodb2
|
@mock_dynamodb2
|
||||||
def test_query_missing_expr_names():
|
def test_query_missing_expr_names():
|
||||||
|
@ -324,7 +324,6 @@ def test_get_parameters_errors():
|
|||||||
", ".join(ssm_parameters.keys())
|
", ".join(ssm_parameters.keys())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(ex.response["Error"]["Message"])
|
|
||||||
|
|
||||||
|
|
||||||
@mock_ssm
|
@mock_ssm
|
||||||
|
Loading…
x
Reference in New Issue
Block a user