RDS: improve global clustering behaviour (#6403)

This commit is contained in:
rafcio19 2023-06-15 22:21:20 +01:00 committed by GitHub
parent 932b7a25d6
commit a15b14085b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 10 deletions

View File

@ -78,7 +78,7 @@ class GlobalCluster(BaseModel):
self.deletion_protection = (
deletion_protection and deletion_protection.lower() == "true"
)
self.members: List[str] = []
self.members: List[Cluster] = []
def to_xml(self) -> str:
template = Template(
@ -92,10 +92,18 @@ class GlobalCluster(BaseModel):
<StorageEncrypted>{{ 'true' if cluster.storage_encrypted else 'false' }}</StorageEncrypted>
<DeletionProtection>{{ 'true' if cluster.deletion_protection else 'false' }}</DeletionProtection>
<GlobalClusterMembers>
{% for cluster_arn in cluster.members %}
{% for cluster_member in cluster.members %}
<GlobalClusterMember>
<DBClusterArn>{{ cluster_arn }}</DBClusterArn>
<IsWriter>true</IsWriter>
<DBClusterArn>{{ cluster_member.db_cluster_arn }}</DBClusterArn>
<IsWriter>{{ 'true' if cluster_member.is_writer else 'false' }}</IsWriter>
{% if not cluster_member.is_writer %}<GlobalWriteForwardingStatus>disabled</GlobalWriteForwardingStatus>{% endif %}
<Readers>
{% if cluster_member.is_writer %}
{% for reader in cluster.members %}
{% if not reader.is_writer %}<Reader>{{ reader.db_cluster_arn }}</Reader>{% endif %}
{% endfor %}
{% endif %}
</Readers>
</GlobalClusterMember>
{% endfor %}
</GlobalClusterMembers>
@ -141,11 +149,15 @@ class Cluster:
engine=self.engine, storage_type=self.storage_type # type: ignore
)
self.master_username = kwargs.get("master_username")
if not self.master_username:
self.global_cluster_identifier = kwargs.get("global_cluster_identifier")
if not self.master_username and self.global_cluster_identifier:
pass
elif not self.master_username:
raise InvalidParameterValue(
"The parameter MasterUsername must be provided and must not be blank."
)
self.master_user_password = kwargs.get("master_user_password") # type: ignore
else:
self.master_user_password = kwargs.get("master_user_password") # type: ignore
self.availability_zones = kwargs.get("availability_zones")
if not self.availability_zones:
@ -194,10 +206,10 @@ class Cluster:
"timeout_action": "RollbackCapacityChange",
"seconds_before_timeout": 300,
}
self.global_cluster_identifier = kwargs.get("global_cluster_identifier")
self.cluster_members: List[str] = list()
self.replication_source_identifier = kwargs.get("replication_source_identifier")
self.read_replica_identifiers: List[str] = list()
self.is_writer: bool = False
@property
def is_multi_az(self) -> bool:
@ -2119,7 +2131,27 @@ class RDSBackend(BaseBackend):
and cluster.global_cluster_identifier in self.global_clusters
):
global_cluster = self.global_clusters[cluster.global_cluster_identifier]
global_cluster.members.append(cluster.db_cluster_arn)
# Main DB cluster, does RW on global cluster
setattr(cluster, "is_writer", True)
# self.clusters[cluster_id] = cluster
global_cluster.members.append(cluster)
# search all backend to check if global cluster named global_cluster_identifier exists
# anywhere else
if (
cluster.global_cluster_identifier
and cluster.global_cluster_identifier not in self.global_clusters
):
for regional_backend in rds_backends[self.account_id]:
if (
cluster.global_cluster_identifier
in rds_backends[self.account_id][regional_backend].global_clusters
):
global_cluster = rds_backends[self.account_id][
regional_backend
].global_clusters[cluster.global_cluster_identifier]
global_cluster.members.append(cluster)
if cluster.replication_source_identifier:
cluster_identifier = cluster.replication_source_identifier
@ -2623,7 +2655,7 @@ class RDSBackend(BaseBackend):
self.global_clusters[global_cluster_identifier] = global_cluster
if source_cluster is not None:
source_cluster.global_cluster_identifier = global_cluster.global_cluster_arn
global_cluster.members.append(source_cluster.db_cluster_arn)
global_cluster.members.append(source_cluster)
return global_cluster
def describe_global_clusters(self) -> List[GlobalCluster]:
@ -2650,7 +2682,7 @@ class RDSBackend(BaseBackend):
cluster = self.describe_db_clusters(
cluster_identifier=db_cluster_identifier
)[0]
global_cluster.members.remove(cluster.db_cluster_arn)
global_cluster.members.remove(cluster)
return global_cluster
except: # noqa: E722 Do not use bare except
pass

View File

@ -91,6 +91,66 @@ def test_create_global_cluster_from_regular_cluster():
assert global_cluster["GlobalClusterMembers"][0]["DBClusterArn"] == cluster_arn
@mock_rds
def test_create_global_cluster_from_regular_cluster_with_reader():
east_client = boto3.client("rds", "eu-west-1")
west_client = boto3.client("rds", "eu-west-2")
# Create global cluster
east_client.create_global_cluster(
GlobalClusterIdentifier="test-global-db",
Engine="aurora-mysql",
DeletionProtection=False,
DatabaseName="test-db",
StorageEncrypted=False,
)
east_client.create_db_cluster(
DBClusterIdentifier="test-primary-cluster",
Engine="aurora-mysql",
GlobalClusterIdentifier="test-global-db",
MasterUsername="testUsername",
MasterUserPassword="testPassword",
)
east_client.create_db_instance(
DBInstanceIdentifier="test-primary-cluster-i1",
DBInstanceClass="db.r5.large",
Engine="aurora-mysql",
PubliclyAccessible=False,
DBClusterIdentifier="test-primary-cluster",
)
west_client.create_db_cluster(
DBClusterIdentifier="test-secondary-cluster",
Engine="aurora-mysql",
GlobalClusterIdentifier="test-global-db",
)
resp = east_client.describe_global_clusters(
GlobalClusterIdentifier="test-global-db"
)
members = resp["GlobalClusters"][0]["GlobalClusterMembers"]
assert len(members) == 2
assert (
members[0]["DBClusterArn"]
== "arn:aws:rds:eu-west-1:123456789012:cluster:test-primary-cluster"
)
assert len(members[0]["Readers"]) == 1
assert (
members[0]["Readers"][0]
== "arn:aws:rds:eu-west-2:123456789012:cluster:test-secondary-cluster"
)
assert members[0]["IsWriter"]
assert (
members[1]["DBClusterArn"]
== "arn:aws:rds:eu-west-2:123456789012:cluster:test-secondary-cluster"
)
assert len(members[1]["Readers"]) == 0
assert not members[1]["IsWriter"]
@mock_rds
def test_create_global_cluster_from_regular_cluster__using_name():
client = boto3.client("rds", "us-east-1")