Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions localstack-core/localstack/services/dynamodb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

from localstack.aws.api.dynamodb import (
AttributeMap,
BackupDetails,
Key,
RegionName,
ReplicaDescription,
StreamViewType,
TableDescription,
TableName,
TimeToLiveSpecification,
)
Expand All @@ -16,6 +18,7 @@
CrossRegionAttribute,
LocalAttribute,
)
from localstack.utils.tagging import TaggingService


@dataclasses.dataclass
Expand Down Expand Up @@ -91,6 +94,13 @@ class TableRecords(TypedDict):
RecordsMap = dict[TableName, TableRecords]


@dataclasses.dataclass
class Backup:
backup_detail: BackupDetails
backup_file: str
table_name: str


class DynamoDBStore(BaseStore):
# maps global table names to configurations (for the legacy v.2017 tables)
GLOBAL_TABLES: dict[str, dict] = CrossRegionAttribute(default=dict)
Expand All @@ -104,10 +114,10 @@ class DynamoDBStore(BaseStore):
)

# cache table taggings - maps table ARN to tags dict
TABLE_TAGS: dict[str, dict] = CrossRegionAttribute(default=dict)
TAGS: TaggingService = CrossRegionAttribute(default=TaggingService)

# maps table names to cached table definitions
table_definitions: dict[str, dict] = LocalAttribute(default=dict)
table_definitions: dict[str, TableDescription] = LocalAttribute(default=dict)

# maps table names to additional table properties that are not stored upstream (e.g., ReplicaUpdates)
table_properties: dict[str, dict] = LocalAttribute(default=dict)
Expand All @@ -116,7 +126,7 @@ class DynamoDBStore(BaseStore):
ttl_specifications: dict[str, TimeToLiveSpecification] = LocalAttribute(default=dict)

# maps backups
backups: dict[str, dict] = LocalAttribute(default=dict)
backups: dict[str, Backup] = LocalAttribute(default=dict)


dynamodb_stores = AccountRegionBundle("dynamodb", DynamoDBStore)
36 changes: 14 additions & 22 deletions localstack-core/localstack/services/dynamodb/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
GetItemInput,
GetItemOutput,
GlobalTableAlreadyExistsException,
GlobalTableDescription,
GlobalTableNotFoundException,
KinesisStreamingDestinationOutput,
ListGlobalTablesOutput,
Expand Down Expand Up @@ -751,11 +752,8 @@ def create_table(
if "WarmThroughput" in table_description:
table_description["WarmThroughput"]["Status"] = "UPDATING"

tags = table_definitions.pop("Tags", [])
if tags:
get_store(context.account_id, context.region).TABLE_TAGS[table_arn] = {
tag["Key"]: tag["Value"] for tag in tags
}
if tags := table_definitions.pop("Tags", []):
get_store(context.account_id, context.region).TAGS.tag_resource(table_arn, tags)

# remove invalid attributes from result
table_description.pop("Tags", None)
Expand Down Expand Up @@ -785,7 +783,7 @@ def delete_table(
dynamodbstreams_api.delete_streams(context.account_id, context.region, table_arn)

store = get_store(context.account_id, context.region)
store.TABLE_TAGS.pop(table_arn, None)
store.TAGS.del_resource(table_arn)
store.REPLICAS.pop(table_name, None)

return result
Expand Down Expand Up @@ -1465,10 +1463,8 @@ def execute_statement(
def tag_resource(
self, context: RequestContext, resource_arn: ResourceArnString, tags: TagList, **kwargs
) -> None:
table_tags = get_store(context.account_id, context.region).TABLE_TAGS
if resource_arn not in table_tags:
table_tags[resource_arn] = {}
table_tags[resource_arn].update({tag["Key"]: tag["Value"] for tag in tags})
table_tags = get_store(context.account_id, context.region).TAGS
table_tags.tag_resource(resource_arn, tags)

def untag_resource(
self,
Expand All @@ -1477,10 +1473,8 @@ def untag_resource(
tag_keys: TagKeyList,
**kwargs,
) -> None:
for tag_key in tag_keys or []:
get_store(context.account_id, context.region).TABLE_TAGS.get(resource_arn, {}).pop(
tag_key, None
)
store = get_store(context.account_id, context.region)
store.TAGS.untag_resource(resource_arn, tag_keys)

def list_tags_of_resource(
self,
Expand All @@ -1489,13 +1483,9 @@ def list_tags_of_resource(
next_token: NextTokenString = None,
**kwargs,
) -> ListTagsOfResourceOutput:
result = [
{"Key": k, "Value": v}
for k, v in get_store(context.account_id, context.region)
.TABLE_TAGS.get(resource_arn, {})
.items()
]
return ListTagsOfResourceOutput(Tags=result)
store = get_store(context.account_id, context.region)
tags = store.TAGS.list_tags_for_resource(resource_arn)
return ListTagsOfResourceOutput(Tags=tags.get("Tags"))

#
# TTLs
Expand Down Expand Up @@ -1557,7 +1547,9 @@ def create_global_table(
if global_table_name in global_tables:
raise GlobalTableAlreadyExistsException("Global table with this name already exists")
replication_group = [grp.copy() for grp in replication_group or []]
data = {"GlobalTableName": global_table_name, "ReplicationGroup": replication_group}
data = GlobalTableDescription(
GlobalTableName=global_table_name, ReplicationGroup=replication_group
)
global_tables[global_table_name] = data
for group in replication_group:
group["ReplicaStatus"] = "ACTIVE"
Expand Down
31 changes: 14 additions & 17 deletions localstack-core/localstack/services/dynamodb/v2/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
GetItemInput,
GetItemOutput,
GlobalTableAlreadyExistsException,
GlobalTableDescription,
GlobalTableNotFoundException,
KinesisStreamingDestinationOutput,
ListGlobalTablesOutput,
Expand Down Expand Up @@ -560,6 +561,8 @@ def create_table(
if "NumberOfDecreasesToday" not in table_description["ProvisionedThroughput"]:
table_description["ProvisionedThroughput"]["NumberOfDecreasesToday"] = 0

if tags := table_definitions.pop("Tags", []):
get_store(context.account_id, context.region).TAGS.tag_resource(table_arn, tags)
if "WarmThroughput" in table_description:
table_description["WarmThroughput"]["Status"] = "UPDATING"

Expand Down Expand Up @@ -596,7 +599,7 @@ def delete_table(
table_arn = self.fix_table_arn(context.account_id, context.region, table_arn)

store = get_store(context.account_id, context.region)
store.TABLE_TAGS.pop(table_arn, None)
store.TAGS.del_resource(table_arn)
store.REPLICAS.pop(table_name, None)

return result
Expand Down Expand Up @@ -989,10 +992,8 @@ def execute_statement(
def tag_resource(
self, context: RequestContext, resource_arn: ResourceArnString, tags: TagList, **kwargs
) -> None:
table_tags = get_store(context.account_id, context.region).TABLE_TAGS
if resource_arn not in table_tags:
table_tags[resource_arn] = {}
table_tags[resource_arn].update({tag["Key"]: tag["Value"] for tag in tags})
table_tags = get_store(context.account_id, context.region).TAGS
table_tags.tag_resource(resource_arn, tags)

def untag_resource(
self,
Expand All @@ -1001,10 +1002,8 @@ def untag_resource(
tag_keys: TagKeyList,
**kwargs,
) -> None:
for tag_key in tag_keys or []:
get_store(context.account_id, context.region).TABLE_TAGS.get(resource_arn, {}).pop(
tag_key, None
)
store = get_store(context.account_id, context.region)
store.TAGS.untag_resource(resource_arn, tag_keys)

def list_tags_of_resource(
self,
Expand All @@ -1013,13 +1012,9 @@ def list_tags_of_resource(
next_token: NextTokenString = None,
**kwargs,
) -> ListTagsOfResourceOutput:
result = [
{"Key": k, "Value": v}
for k, v in get_store(context.account_id, context.region)
.TABLE_TAGS.get(resource_arn, {})
.items()
]
return ListTagsOfResourceOutput(Tags=result)
store = get_store(context.account_id, context.region)
tags = store.TAGS.list_tags_for_resource(resource_arn)
return ListTagsOfResourceOutput(Tags=tags.get("Tags"))

#
# TTLs
Expand Down Expand Up @@ -1081,7 +1076,9 @@ def create_global_table(
if global_table_name in global_tables:
raise GlobalTableAlreadyExistsException("Global table with this name already exists")
replication_group = [grp.copy() for grp in replication_group or []]
data = {"GlobalTableName": global_table_name, "ReplicationGroup": replication_group}
data = GlobalTableDescription(
GlobalTableName=global_table_name, ReplicationGroup=replication_group
)
global_tables[global_table_name] = data
for group in replication_group:
group["ReplicaStatus"] = "ACTIVE"
Expand Down
Loading