RDS: Snapshot attributes, cluster parameters (#7232)

This commit is contained in:
Josh Levy 2024-01-20 05:40:22 -05:00 committed by GitHub
parent 5de7d4df38
commit 792956e959
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 478 additions and 1 deletions

View File

@ -229,6 +229,15 @@ class Cluster:
self.replication_source_identifier = kwargs.get("replication_source_identifier") self.replication_source_identifier = kwargs.get("replication_source_identifier")
self.read_replica_identifiers: List[str] = list() self.read_replica_identifiers: List[str] = list()
self.is_writer: bool = False 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 @property
def is_multi_az(self) -> bool: def is_multi_az(self) -> bool:
@ -361,7 +370,8 @@ class Cluster:
{% endfor %} {% endfor %}
</VpcSecurityGroups> </VpcSecurityGroups>
<HostedZoneId>{{ cluster.hosted_zone_id }}</HostedZoneId> <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> <DbClusterResourceId>{{ cluster.resource_id }}</DbClusterResourceId>
<DBClusterArn>{{ cluster.db_cluster_arn }}</DBClusterArn> <DBClusterArn>{{ cluster.db_cluster_arn }}</DBClusterArn>
<AssociatedRoles></AssociatedRoles> <AssociatedRoles></AssociatedRoles>
@ -485,6 +495,7 @@ class ClusterSnapshot(BaseModel):
self.tags = tags self.tags = tags
self.status = "available" self.status = "available"
self.created_at = iso_8601_datetime_with_milliseconds() self.created_at = iso_8601_datetime_with_milliseconds()
self.attributes: List[Dict[str, Any]] = []
@property @property
def arn(self) -> str: def arn(self) -> str:
@ -1122,6 +1133,7 @@ class DatabaseSnapshot(BaseModel):
self.tags = tags self.tags = tags
self.status = "available" self.status = "available"
self.created_at = iso_8601_datetime_with_milliseconds() self.created_at = iso_8601_datetime_with_milliseconds()
self.attributes: List[Dict[str, Any]] = []
@property @property
def arn(self) -> str: def arn(self) -> str:
@ -2803,6 +2815,92 @@ class RDSBackend(BaseBackend):
pass pass
return None 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"] = (
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"] = (
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: class OptionGroup:
def __init__( def __init__(

View File

@ -195,6 +195,10 @@ class RDSResponse(BaseResponse):
"allocated_storage": self._get_param("AllocatedStorage"), "allocated_storage": self._get_param("AllocatedStorage"),
"global_cluster_identifier": self._get_param("GlobalClusterIdentifier"), "global_cluster_identifier": self._get_param("GlobalClusterIdentifier"),
"iops": self._get_param("Iops"), "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"), "storage_type": self._get_param("StorageType"),
"kms_key_id": self._get_param("KmsKeyId"), "kms_key_id": self._get_param("KmsKeyId"),
"master_username": self._get_param("MasterUsername"), "master_username": self._get_param("MasterUsername"),
@ -818,6 +822,66 @@ class RDSResponse(BaseResponse):
template = self.response_template(PROMOTE_READ_REPLICA_DB_CLUSTER_TEMPLATE) template = self.response_template(PROMOTE_READ_REPLICA_DB_CLUSTER_TEMPLATE)
return template.render(cluster=cluster) 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/"> CREATE_DATABASE_TEMPLATE = """<CreateDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
<CreateDBInstanceResult> <CreateDBInstanceResult>
@ -1474,3 +1538,95 @@ PROMOTE_READ_REPLICA_DB_CLUSTER_TEMPLATE = """<PromoteReadReplicaDBClusterRespon
<RequestId>7369556f-b70d-11c3-faca-6ba18376ea1b</RequestId> <RequestId>7369556f-b70d-11c3-faca-6ba18376ea1b</RequestId>
</ResponseMetadata> </ResponseMetadata>
</PromoteReadReplicaDBClusterResponse>""" </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>"""

View File

@ -2694,6 +2694,106 @@ def test_createdb_instance_engine_with_invalid_value():
) )
@mock_rds
def test_describe_db_snapshot_attributes_default():
client = boto3.client("rds", region_name="us-east-2")
client.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2",
Port=1234,
DBSecurityGroups=["my_sg"],
)
client.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
).get("DBSnapshot")
resp = client.describe_db_snapshot_attributes(DBSnapshotIdentifier="snapshot-1")
assert resp["DBSnapshotAttributesResult"]["DBSnapshotIdentifier"] == "snapshot-1"
assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"] == []
@mock_rds
def test_describe_db_snapshot_attributes():
client = boto3.client("rds", region_name="us-east-2")
client.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2",
Port=1234,
DBSecurityGroups=["my_sg"],
)
client.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
).get("DBSnapshot")
resp = client.modify_db_snapshot_attribute(
DBSnapshotIdentifier="snapshot-1",
AttributeName="restore",
ValuesToAdd=["Test", "Test2"],
)
resp = client.describe_db_snapshot_attributes(DBSnapshotIdentifier="snapshot-1")
assert (
resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0]["AttributeName"]
== "restore"
)
assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0][
"AttributeValues"
] == ["Test", "Test2"]
@mock_rds
def test_modify_db_snapshot_attribute():
client = boto3.client("rds", region_name="us-east-2")
client.create_db_instance(
DBInstanceIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DBName="staging-postgres",
DBInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2",
Port=1234,
DBSecurityGroups=["my_sg"],
)
client.create_db_snapshot(
DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
).get("DBSnapshot")
resp = client.modify_db_snapshot_attribute(
DBSnapshotIdentifier="snapshot-1",
AttributeName="restore",
ValuesToAdd=["Test", "Test2"],
)
resp = client.modify_db_snapshot_attribute(
DBSnapshotIdentifier="snapshot-1",
AttributeName="restore",
ValuesToRemove=["Test"],
)
assert (
resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0]["AttributeName"]
== "restore"
)
assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0][
"AttributeValues"
] == ["Test2"]
def validation_helper(exc): def validation_helper(exc):
err = exc.value.response["Error"] err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterValue" assert err["Code"] == "InvalidParameterValue"

View File

@ -215,6 +215,8 @@ def test_create_db_cluster__verify_default_properties():
assert cluster["TagList"] == [] assert cluster["TagList"] == []
assert "ClusterCreateTime" in cluster assert "ClusterCreateTime" in cluster
assert cluster["EarliestRestorableTime"] >= cluster["ClusterCreateTime"] assert cluster["EarliestRestorableTime"] >= cluster["ClusterCreateTime"]
assert cluster["StorageEncrypted"] is False
assert cluster["GlobalWriteForwardingRequested"] is False
@mock_rds @mock_rds
@ -236,6 +238,8 @@ def test_create_db_cluster_additional_parameters():
KmsKeyId="some:kms:arn", KmsKeyId="some:kms:arn",
NetworkType="IPV4", NetworkType="IPV4",
DBSubnetGroupName="subnetgroupname", DBSubnetGroupName="subnetgroupname",
StorageEncrypted=True,
EnableGlobalWriteForwarding=True,
ScalingConfiguration={ ScalingConfiguration={
"MinCapacity": 5, "MinCapacity": 5,
"AutoPause": True, "AutoPause": True,
@ -260,6 +264,8 @@ def test_create_db_cluster_additional_parameters():
assert cluster["KmsKeyId"] == "some:kms:arn" assert cluster["KmsKeyId"] == "some:kms:arn"
assert cluster["NetworkType"] == "IPV4" assert cluster["NetworkType"] == "IPV4"
assert cluster["DBSubnetGroup"] == "subnetgroupname" assert cluster["DBSubnetGroup"] == "subnetgroupname"
assert cluster["StorageEncrypted"] is True
assert cluster["GlobalWriteForwardingRequested"] is True
assert cluster["ScalingConfigurationInfo"] == {"MinCapacity": 5, "AutoPause": True} assert cluster["ScalingConfigurationInfo"] == {"MinCapacity": 5, "AutoPause": True}
assert cluster["ServerlessV2ScalingConfiguration"] == { assert cluster["ServerlessV2ScalingConfiguration"] == {
"MaxCapacity": 4.0, "MaxCapacity": 4.0,
@ -977,3 +983,120 @@ def test_createdb_instance_engine_mismatch_fail():
== "The engine name requested for your DB instance (mysql) doesn't match " == "The engine name requested for your DB instance (mysql) doesn't match "
"the engine name of your DB cluster (aurora-postgresql)." "the engine name of your DB cluster (aurora-postgresql)."
) )
@mock_rds
def test_describe_db_cluster_snapshot_attributes_default():
conn = boto3.client("rds", region_name="us-west-2")
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DatabaseName="staging-postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
Port=1234,
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="g-1"
).get("DBClusterSnapshot")
resp = conn.describe_db_cluster_snapshot_attributes(
DBClusterSnapshotIdentifier="g-1"
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotIdentifier"]
== "g-1"
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"] == []
)
@mock_rds
def test_describe_db_cluster_snapshot_attributes():
conn = boto3.client("rds", region_name="us-west-2")
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DatabaseName="staging-postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
Port=1234,
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="g-1"
).get("DBClusterSnapshot")
conn.modify_db_cluster_snapshot_attribute(
DBClusterSnapshotIdentifier="g-1",
AttributeName="restore",
ValuesToAdd=["test", "test2"],
)
resp = conn.describe_db_cluster_snapshot_attributes(
DBClusterSnapshotIdentifier="g-1"
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotIdentifier"]
== "g-1"
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"][0][
"AttributeName"
]
== "restore"
)
assert resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"][0][
"AttributeValues"
] == ["test", "test2"]
@mock_rds
def test_modify_db_cluster_snapshot_attribute():
conn = boto3.client("rds", region_name="us-west-2")
conn.create_db_cluster(
DBClusterIdentifier="db-primary-1",
AllocatedStorage=10,
Engine="postgres",
DatabaseName="staging-postgres",
DBClusterInstanceClass="db.m1.small",
MasterUsername="root",
MasterUserPassword="hunter2000",
Port=1234,
)
conn.create_db_cluster_snapshot(
DBClusterIdentifier="db-primary-1", DBClusterSnapshotIdentifier="g-1"
).get("DBClusterSnapshot")
resp = conn.modify_db_cluster_snapshot_attribute(
DBClusterSnapshotIdentifier="g-1",
AttributeName="restore",
ValuesToAdd=["test", "test2"],
)
resp = conn.modify_db_cluster_snapshot_attribute(
DBClusterSnapshotIdentifier="g-1",
AttributeName="restore",
ValuesToRemove=["test"],
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotIdentifier"]
== "g-1"
)
assert (
resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"][0][
"AttributeName"
]
== "restore"
)
assert resp["DBClusterSnapshotAttributesResult"]["DBClusterSnapshotAttributes"][0][
"AttributeValues"
] == ["test2"]