Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: RDS Snapshot Attributes #7232

Merged
merged 2 commits into from
Jan 20, 2024
Merged
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
100 changes: 99 additions & 1 deletion moto/rds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@
self.replication_source_identifier = kwargs.get("replication_source_identifier")
self.read_replica_identifiers: List[str] = list()
self.is_writer: bool = False
self.storage_encrypted = kwargs.get("storage_encrypted", False)
if self.storage_encrypted:
self.kms_key_id = kwargs.get("kms_key_id", "default_kms_key_id")
else:
self.kms_key_id = kwargs.get("kms_key_id")
if self.engine == "aurora-mysql" or self.engine == "aurora-postgresql":
self.global_write_forwarding_requested = kwargs.get(
"enable_global_write_forwarding"
)

@property
def is_multi_az(self) -> bool:
Expand Down Expand Up @@ -361,7 +370,8 @@
{% endfor %}
</VpcSecurityGroups>
<HostedZoneId>{{ cluster.hosted_zone_id }}</HostedZoneId>
<StorageEncrypted>false</StorageEncrypted>
<StorageEncrypted>{{ 'true' if cluster.storage_encrypted else 'false' }}</StorageEncrypted>
<GlobalWriteForwardingRequested>{{ cluster.global_write_forwarding_requested }}</GlobalWriteForwardingRequested>
<DbClusterResourceId>{{ cluster.resource_id }}</DbClusterResourceId>
<DBClusterArn>{{ cluster.db_cluster_arn }}</DBClusterArn>
<AssociatedRoles></AssociatedRoles>
Expand Down Expand Up @@ -485,6 +495,7 @@
self.tags = tags
self.status = "available"
self.created_at = iso_8601_datetime_with_milliseconds()
self.attributes: List[Dict[str, Any]] = []

@property
def arn(self) -> str:
Expand Down Expand Up @@ -1122,6 +1133,7 @@
self.tags = tags
self.status = "available"
self.created_at = iso_8601_datetime_with_milliseconds()
self.attributes: List[Dict[str, Any]] = []

@property
def arn(self) -> str:
Expand Down Expand Up @@ -2803,6 +2815,92 @@
pass
return None

def describe_db_snapshot_attributes(
self, db_snapshot_identifier: str
) -> List[Dict[str, Any]]:
snapshot = self.describe_db_snapshots(
db_instance_identifier=None, db_snapshot_identifier=db_snapshot_identifier
)[0]
return snapshot.attributes

def modify_db_snapshot_attribute(
self,
db_snapshot_identifier: str,
attribute_name: str,
values_to_add: Optional[Dict[str, Dict[str, str]]] = None,
values_to_remove: Optional[Dict[str, Dict[str, str]]] = None,
) -> List[Dict[str, Any]]:
snapshot = self.describe_db_snapshots(
db_instance_identifier=None, db_snapshot_identifier=db_snapshot_identifier
)[0]
attribute_present = False
for attribute in snapshot.attributes:
if attribute["AttributeName"] == attribute_name:
attribute_present = True
if values_to_add:
attribute["AttributeValues"] = (

Check warning on line 2841 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L2841

Added line #L2841 was not covered by tests
values_to_add["AttributeValue"].values()
+ attribute["AttributeValues"]
)
if values_to_remove:
attribute["AttributeValues"] = [
i
for i in attribute["AttributeValues"]
if i not in values_to_remove["AttributeValue"].values()
]
if not attribute_present and values_to_add:
snapshot.attributes.append(
{
"AttributeName": attribute_name,
"AttributeValues": values_to_add["AttributeValue"].values(),
}
)
return snapshot.attributes

def describe_db_cluster_snapshot_attributes(
self, db_cluster_snapshot_identifier: str
) -> List[Dict[str, Any]]:
snapshot = self.describe_db_cluster_snapshots(
db_cluster_identifier=None,
db_snapshot_identifier=db_cluster_snapshot_identifier,
)[0]
return snapshot.attributes

def modify_db_cluster_snapshot_attribute(
self,
db_cluster_snapshot_identifier: str,
attribute_name: str,
values_to_add: Optional[Dict[str, Dict[str, str]]] = None,
values_to_remove: Optional[Dict[str, Dict[str, str]]] = None,
) -> List[Dict[str, Any]]:
snapshot = self.describe_db_cluster_snapshots(
db_cluster_identifier=None,
db_snapshot_identifier=db_cluster_snapshot_identifier,
)[0]
attribute_present = False
for attribute in snapshot.attributes:
if attribute["AttributeName"] == attribute_name:
attribute_present = True
if values_to_add:
attribute["AttributeValues"] = (

Check warning on line 2885 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L2885

Added line #L2885 was not covered by tests
values_to_add["AttributeValue"].values()
+ attribute["AttributeValues"]
)
if values_to_remove:
attribute["AttributeValues"] = [
i
for i in attribute["AttributeValues"]
if i not in values_to_remove["AttributeValue"].values()
]
if not attribute_present and values_to_add:
snapshot.attributes.append(
{
"AttributeName": attribute_name,
"AttributeValues": values_to_add["AttributeValue"].values(),
}
)
return snapshot.attributes


class OptionGroup:
def __init__(
Expand Down
156 changes: 156 additions & 0 deletions moto/rds/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ def _get_db_cluster_kwargs(self) -> Dict[str, Any]:
"allocated_storage": self._get_param("AllocatedStorage"),
"global_cluster_identifier": self._get_param("GlobalClusterIdentifier"),
"iops": self._get_param("Iops"),
"storage_encrypted": self._get_param("StorageEncrypted"),
"enable_global_write_forwarding": self._get_param(
"EnableGlobalWriteForwarding"
),
"storage_type": self._get_param("StorageType"),
"kms_key_id": self._get_param("KmsKeyId"),
"master_username": self._get_param("MasterUsername"),
Expand Down Expand Up @@ -818,6 +822,66 @@ def promote_read_replica_db_cluster(self) -> str:
template = self.response_template(PROMOTE_READ_REPLICA_DB_CLUSTER_TEMPLATE)
return template.render(cluster=cluster)

def describe_db_snapshot_attributes(self) -> str:
params = self._get_params()
db_snapshot_identifier = params["DBSnapshotIdentifier"]
db_snapshot_attributes_result = self.backend.describe_db_snapshot_attributes(
db_snapshot_identifier=db_snapshot_identifier,
)
template = self.response_template(DESCRIBE_DB_SNAPSHOT_ATTRIBUTES_TEMPLATE)
return template.render(
db_snapshot_attributes_result=db_snapshot_attributes_result,
db_snapshot_identifier=db_snapshot_identifier,
)

def modify_db_snapshot_attribute(self) -> str:
params = self._get_params()
db_snapshot_identifier = params["DBSnapshotIdentifier"]
db_snapshot_attributes_result = self.backend.modify_db_snapshot_attribute(
db_snapshot_identifier=db_snapshot_identifier,
attribute_name=params["AttributeName"],
values_to_add=params.get("ValuesToAdd"),
values_to_remove=params.get("ValuesToRemove"),
)
template = self.response_template(MODIFY_DB_SNAPSHOT_ATTRIBUTE_TEMPLATE)
return template.render(
db_snapshot_attributes_result=db_snapshot_attributes_result,
db_snapshot_identifier=db_snapshot_identifier,
)

def describe_db_cluster_snapshot_attributes(self) -> str:
params = self._get_params()
db_cluster_snapshot_identifier = params["DBClusterSnapshotIdentifier"]
db_cluster_snapshot_attributes_result = (
self.backend.describe_db_cluster_snapshot_attributes(
db_cluster_snapshot_identifier=db_cluster_snapshot_identifier,
)
)
template = self.response_template(
DESCRIBE_DB_CLUSTER_SNAPSHOT_ATTRIBUTES_TEMPLATE
)
return template.render(
db_cluster_snapshot_attributes_result=db_cluster_snapshot_attributes_result,
db_cluster_snapshot_identifier=db_cluster_snapshot_identifier,
)

def modify_db_cluster_snapshot_attribute(self) -> str:
params = self._get_params()
db_cluster_snapshot_identifier = params["DBClusterSnapshotIdentifier"]
db_cluster_snapshot_attributes_result = (
self.backend.modify_db_cluster_snapshot_attribute(
db_cluster_snapshot_identifier=db_cluster_snapshot_identifier,
attribute_name=params["AttributeName"],
values_to_add=params.get("ValuesToAdd"),
values_to_remove=params.get("ValuesToRemove"),
)
)
template = self.response_template(MODIFY_DB_CLUSTER_SNAPSHOT_ATTRIBUTE_TEMPLATE)
return template.render(
db_cluster_snapshot_attributes_result=db_cluster_snapshot_attributes_result,
db_cluster_snapshot_identifier=db_cluster_snapshot_identifier,
)


CREATE_DATABASE_TEMPLATE = """<CreateDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
<CreateDBInstanceResult>
Expand Down Expand Up @@ -1474,3 +1538,95 @@ def promote_read_replica_db_cluster(self) -> str:
<RequestId>7369556f-b70d-11c3-faca-6ba18376ea1b</RequestId>
</ResponseMetadata>
</PromoteReadReplicaDBClusterResponse>"""

DESCRIBE_DB_SNAPSHOT_ATTRIBUTES_TEMPLATE = """<DescribeDBSnapshotAttributesResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
<DescribeDBSnapshotAttributesResult>
<DBSnapshotAttributesResult>
<DBSnapshotAttributes>
{%- for attribute in db_snapshot_attributes_result -%}
<DBSnapshotAttribute>
<AttributeName>{{ attribute["AttributeName"] }}</AttributeName>
<AttributeValues>
{%- for value in attribute["AttributeValues"] -%}
<AttributeValue>{{ value }}</AttributeValue>
{%- endfor -%}
</AttributeValues>
</DBSnapshotAttribute>
{%- endfor -%}
</DBSnapshotAttributes>
<DBSnapshotIdentifier>{{ db_snapshot_identifier }}</DBSnapshotIdentifier>
</DBSnapshotAttributesResult>
</DescribeDBSnapshotAttributesResult>
<ResponseMetadata>
<RequestId>1549581b-12b7-11e3-895e-1334a</RequestId>
</ResponseMetadata>
</DescribeDBSnapshotAttributesResponse>"""

MODIFY_DB_SNAPSHOT_ATTRIBUTE_TEMPLATE = """<ModifyDBSnapshotAttributeResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
<ModifyDBSnapshotAttributeResult>
<DBSnapshotAttributesResult>
<DBSnapshotAttributes>
{%- for attribute in db_snapshot_attributes_result -%}
<DBSnapshotAttribute>
<AttributeName>{{ attribute["AttributeName"] }}</AttributeName>
<AttributeValues>
{%- for value in attribute["AttributeValues"] -%}
<AttributeValue>{{ value }}</AttributeValue>
{%- endfor -%}
</AttributeValues>
</DBSnapshotAttribute>
{%- endfor -%}
</DBSnapshotAttributes>
<DBSnapshotIdentifier>{{ db_snapshot_identifier }}</DBSnapshotIdentifier>
</DBSnapshotAttributesResult>
</ModifyDBSnapshotAttributeResult>
<ResponseMetadata>
<RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
</ResponseMetadata>
</ModifyDBSnapshotAttributeResponse>"""

MODIFY_DB_CLUSTER_SNAPSHOT_ATTRIBUTE_TEMPLATE = """<ModifyDBClusterSnapshotAttributeResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
<ModifyDBClusterSnapshotAttributeResult>
<DBClusterSnapshotAttributesResult>
<DBClusterSnapshotAttributes>
{%- for attribute in db_cluster_snapshot_attributes_result -%}
<DBClusterSnapshotAttribute>
<AttributeName>{{ attribute["AttributeName"] }}</AttributeName>
<AttributeValues>
{%- for value in attribute["AttributeValues"] -%}
<AttributeValue>{{ value }}</AttributeValue>
{%- endfor -%}
</AttributeValues>
</DBClusterSnapshotAttribute>
{%- endfor -%}
</DBClusterSnapshotAttributes>
<DBClusterSnapshotIdentifier>{{ db_cluster_snapshot_identifier }}</DBClusterSnapshotIdentifier>
</DBClusterSnapshotAttributesResult>
</ModifyDBClusterSnapshotAttributeResult>
<ResponseMetadata>
<RequestId>1549581b-12b7-11e3-895e-1334a</RequestId>
</ResponseMetadata>
</ModifyDBClusterSnapshotAttributeResponse>"""

DESCRIBE_DB_CLUSTER_SNAPSHOT_ATTRIBUTES_TEMPLATE = """<DescribeDBClusterSnapshotAttributesResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
<DescribeDBClusterSnapshotAttributesResult>
<DBClusterSnapshotAttributesResult>
<DBClusterSnapshotAttributes>
{%- for attribute in db_cluster_snapshot_attributes_result -%}
<DBClusterSnapshotAttribute>
<AttributeName>{{ attribute["AttributeName"] }}</AttributeName>
<AttributeValues>
{%- for value in attribute["AttributeValues"] -%}
<AttributeValue>{{ value }}</AttributeValue>
{%- endfor -%}
</AttributeValues>
</DBClusterSnapshotAttribute>
{%- endfor -%}
</DBClusterSnapshotAttributes>
<DBClusterSnapshotIdentifier>{{ db_cluster_snapshot_identifier }}</DBClusterSnapshotIdentifier>
</DBClusterSnapshotAttributesResult>
</DescribeDBClusterSnapshotAttributesResult>
<ResponseMetadata>
<RequestId>1549581b-12b7-11e3-895e-1334a</RequestId>
</ResponseMetadata>
</DescribeDBClusterSnapshotAttributesResponse>"""
Loading