Added restore table to point in time DynamoDB method (#4627)
This commit is contained in:
parent
4f2e7f706d
commit
7d44df5266
@ -61,7 +61,7 @@ dynamodb
|
||||
- [X] put_item
|
||||
- [X] query
|
||||
- [X] restore_table_from_backup
|
||||
- [ ] restore_table_to_point_in_time
|
||||
- [X] restore_table_to_point_in_time
|
||||
- [X] scan
|
||||
- [X] tag_resource
|
||||
- [X] transact_get_items
|
||||
|
@ -1020,6 +1020,36 @@ class RestoredTable(Table):
|
||||
return result
|
||||
|
||||
|
||||
class RestoredPITTable(Table):
|
||||
def __init__(self, name, source):
|
||||
params = self._parse_params_from_table(source)
|
||||
super(RestoredPITTable, self).__init__(name, **params)
|
||||
self.indexes = copy.deepcopy(source.indexes)
|
||||
self.global_indexes = copy.deepcopy(source.global_indexes)
|
||||
self.items = copy.deepcopy(source.items)
|
||||
# Restore Attrs
|
||||
self.source_table_arn = source.table_arn
|
||||
self.restore_date_time = self.created_at
|
||||
|
||||
@staticmethod
|
||||
def _parse_params_from_table(table):
|
||||
params = {
|
||||
"schema": copy.deepcopy(table.schema),
|
||||
"attr": copy.deepcopy(table.attr),
|
||||
"throughput": copy.deepcopy(table.throughput),
|
||||
}
|
||||
return params
|
||||
|
||||
def describe(self, base_key="TableDescription"):
|
||||
result = super(RestoredPITTable, self).describe(base_key=base_key)
|
||||
result[base_key]["RestoreSummary"] = {
|
||||
"SourceTableArn": self.source_table_arn,
|
||||
"RestoreDateTime": unix_time(self.restore_date_time),
|
||||
"RestoreInProgress": False,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
class Backup(object):
|
||||
def __init__(
|
||||
self, backend, name, table, status=None, type_=None,
|
||||
@ -1700,6 +1730,22 @@ class DynamoDBBackend(BaseBackend):
|
||||
self.tables[target_table_name] = new_table
|
||||
return new_table
|
||||
|
||||
"""
|
||||
Currently this only accepts the source and target table elements, and will
|
||||
copy all items from the source without respect to other arguments.
|
||||
"""
|
||||
|
||||
def restore_table_to_point_in_time(self, target_table_name, source_table_name):
|
||||
source = self.get_table(source_table_name)
|
||||
if source is None:
|
||||
raise KeyError()
|
||||
existing_table = self.get_table(target_table_name)
|
||||
if existing_table is not None:
|
||||
raise ValueError()
|
||||
new_table = RestoredPITTable(target_table_name, source)
|
||||
self.tables[target_table_name] = new_table
|
||||
return new_table
|
||||
|
||||
######################
|
||||
# LIST of methods where the logic completely resides in responses.py
|
||||
# Duplicated here so that the implementation coverage script is aware
|
||||
|
@ -1203,3 +1203,19 @@ class DynamoHandler(BaseResponse):
|
||||
except ValueError:
|
||||
er = "com.amazonaws.dynamodb.v20111205#TableAlreadyExistsException"
|
||||
return self.error(er, "Table already exists: %s" % target_table_name)
|
||||
|
||||
def restore_table_to_point_in_time(self):
|
||||
body = self.body
|
||||
target_table_name = body.get("TargetTableName")
|
||||
source_table_name = body.get("SourceTableName")
|
||||
try:
|
||||
restored_table = self.dynamodb_backend.restore_table_to_point_in_time(
|
||||
target_table_name, source_table_name
|
||||
)
|
||||
return dynamo_json_dump(restored_table.describe())
|
||||
except KeyError:
|
||||
er = "com.amazonaws.dynamodb.v20111205#SourceTableNotFoundException"
|
||||
return self.error(er, "Source table not found: %s" % source_table_name)
|
||||
except ValueError:
|
||||
er = "com.amazonaws.dynamodb.v20111205#TableAlreadyExistsException"
|
||||
return self.error(er, "Table already exists: %s" % target_table_name)
|
||||
|
@ -5524,6 +5524,76 @@ def test_restore_table_from_backup():
|
||||
summary.should.have.key("RestoreInProgress").should.equal(False)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_restore_table_to_point_in_time():
|
||||
client = boto3.client("dynamodb", "us-east-1")
|
||||
table_name = "test-table"
|
||||
resp = client.create_table(
|
||||
TableName=table_name,
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
|
||||
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
||||
)
|
||||
table = resp.get("TableDescription")
|
||||
for i in range(5):
|
||||
client.put_item(TableName=table_name, Item={"id": {"S": "item %d" % i}})
|
||||
|
||||
restored_table_name = "restored-from-pit"
|
||||
restored = client.restore_table_to_point_in_time(
|
||||
TargetTableName=restored_table_name, SourceTableName=table_name
|
||||
).get("TableDescription")
|
||||
restored.should.have.key("TableName").should.equal(restored_table_name)
|
||||
restored.should.have.key("KeySchema").should.equal(table["KeySchema"])
|
||||
restored.should.have.key("TableStatus")
|
||||
restored.should.have.key("ItemCount").should.equal(5)
|
||||
restored.should.have.key("TableArn").should.contain(restored_table_name)
|
||||
restored.should.have.key("RestoreSummary").should.be.a(dict)
|
||||
summary = restored.get("RestoreSummary")
|
||||
summary.should.have.key("SourceTableArn").should.equal(table["TableArn"])
|
||||
summary.should.have.key("RestoreDateTime").should.be.a(datetime)
|
||||
summary.should.have.key("RestoreInProgress").should.equal(False)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_restore_table_to_point_in_time_raises_error_when_source_not_exist():
|
||||
client = boto3.client("dynamodb", "us-east-1")
|
||||
table_name = "test-table"
|
||||
restored_table_name = "restored-from-pit"
|
||||
with pytest.raises(ClientError) as ex:
|
||||
client.restore_table_to_point_in_time(
|
||||
TargetTableName=restored_table_name, SourceTableName=table_name
|
||||
)
|
||||
error = ex.value.response["Error"]
|
||||
error["Code"].should.equal("SourceTableNotFoundException")
|
||||
error["Message"].should.equal("Source table not found: %s" % table_name)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_restore_table_to_point_in_time_raises_error_when_dest_exist():
|
||||
client = boto3.client("dynamodb", "us-east-1")
|
||||
table_name = "test-table"
|
||||
restored_table_name = "restored-from-pit"
|
||||
client.create_table(
|
||||
TableName=table_name,
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
|
||||
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
||||
)
|
||||
client.create_table(
|
||||
TableName=restored_table_name,
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
|
||||
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
client.restore_table_to_point_in_time(
|
||||
TargetTableName=restored_table_name, SourceTableName=table_name
|
||||
)
|
||||
error = ex.value.response["Error"]
|
||||
error["Code"].should.equal("TableAlreadyExistsException")
|
||||
error["Message"].should.equal("Table already exists: %s" % restored_table_name)
|
||||
|
||||
|
||||
@mock_dynamodb2
|
||||
def test_delete_non_existent_backup_raises_error():
|
||||
client = boto3.client("dynamodb", "us-east-1")
|
||||
|
Loading…
x
Reference in New Issue
Block a user