RDS: add cluster engine validation when creating a rds cluster (#7001)

This commit is contained in:
Miki Watanabe 2023-11-09 15:26:49 -05:00 committed by GitHub
parent e43619ae07
commit 3ca46c3c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 23 deletions

View File

@ -49,6 +49,7 @@ from .utils import (
merge_filters, merge_filters,
validate_filters, validate_filters,
valid_preferred_maintenance_window, valid_preferred_maintenance_window,
ClusterEngine,
) )
@ -129,6 +130,17 @@ class Cluster:
self.db_cluster_instance_class = kwargs.get("db_cluster_instance_class") self.db_cluster_instance_class = kwargs.get("db_cluster_instance_class")
self.deletion_protection = kwargs.get("deletion_protection") self.deletion_protection = kwargs.get("deletion_protection")
self.engine = kwargs.get("engine") self.engine = kwargs.get("engine")
if self.engine not in ClusterEngine.list_cluster_engines():
raise InvalidParameterValue(
(
"Engine '{engine}' is not supported "
"to satisfy constraint: Member must satisfy enum value set: "
"{valid_engines}"
).format(
engine=self.engine,
valid_engines=ClusterEngine.list_cluster_engines(),
)
)
self.engine_version = kwargs.get("engine_version") or Cluster.default_engine_version(self.engine) # type: ignore self.engine_version = kwargs.get("engine_version") or Cluster.default_engine_version(self.engine) # type: ignore
self.engine_mode = kwargs.get("engine_mode") or "provisioned" self.engine_mode = kwargs.get("engine_mode") or "provisioned"
self.iops = kwargs.get("iops") self.iops = kwargs.get("iops")
@ -1591,7 +1603,10 @@ class RDSBackend(BaseBackend):
if cluster_id is not None: if cluster_id is not None:
cluster = self.clusters.get(cluster_id) cluster = self.clusters.get(cluster_id)
if cluster is not None: if cluster is not None:
if cluster.engine == "aurora" and cluster.engine_mode == "serverless": if (
cluster.engine in ClusterEngine.serverless_engines()
and cluster.engine_mode == "serverless"
):
raise InvalidParameterValue( raise InvalidParameterValue(
"Instances cannot be added to Aurora Serverless clusters." "Instances cannot be added to Aurora Serverless clusters."
) )

View File

@ -1,6 +1,7 @@
import copy import copy
from collections import namedtuple from collections import namedtuple
from typing import Any, Dict, Tuple, Optional from typing import Any, Dict, Tuple, Optional, List
from enum import Enum
from botocore.utils import merge_dicts from botocore.utils import merge_dicts
@ -22,6 +23,21 @@ FilterDef = namedtuple(
) )
class ClusterEngine(str, Enum):
AURORA_POSTGRESQL = "aurora-postgresql"
AURORA_MYSQL = "aurora-mysql"
RDS_POSTGRESQL = "postgres"
RDS_MYSQL = "mysql"
@classmethod
def list_cluster_engines(self) -> List[str]:
return sorted([item.value for item in ClusterEngine])
@classmethod
def serverless_engines(self) -> List[str]:
return [ClusterEngine.AURORA_MYSQL, ClusterEngine.AURORA_POSTGRESQL]
def get_object_value(obj: Any, attr: str) -> Any: def get_object_value(obj: Any, attr: str) -> Any:
"""Retrieves an arbitrary attribute value from an object. """Retrieves an arbitrary attribute value from an object.

View File

@ -31,12 +31,29 @@ def test_describe_db_cluster_fails_for_non_existent_cluster():
assert err["Message"] == "DBCluster cluster-id not found." assert err["Message"] == "DBCluster cluster-id not found."
@mock_rds
def test_create_db_cluster_invalid_engine():
client = boto3.client("rds", region_name=RDS_REGION)
with pytest.raises(ClientError) as ex:
client.create_db_cluster(
DBClusterIdentifier="cluster-id", Engine="aurora-postgresql"
)
err = ex.value.response["Error"]
assert err["Code"] == "InvalidParameterValue"
assert err["Message"] == (
"The parameter MasterUsername must be provided and must not be blank."
)
@mock_rds @mock_rds
def test_create_db_cluster_needs_master_username(): def test_create_db_cluster_needs_master_username():
client = boto3.client("rds", region_name=RDS_REGION) client = boto3.client("rds", region_name=RDS_REGION)
with pytest.raises(ClientError) as ex: with pytest.raises(ClientError) as ex:
client.create_db_cluster(DBClusterIdentifier="cluster-id", Engine="aurora") client.create_db_cluster(
DBClusterIdentifier="cluster-id", Engine="aurora-postgresql"
)
err = ex.value.response["Error"] err = ex.value.response["Error"]
assert err["Code"] == "InvalidParameterValue" assert err["Code"] == "InvalidParameterValue"
assert err["Message"] == ( assert err["Message"] == (
@ -50,7 +67,9 @@ def test_create_db_cluster_needs_master_user_password():
with pytest.raises(ClientError) as ex: with pytest.raises(ClientError) as ex:
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", Engine="aurora", MasterUsername="root" DBClusterIdentifier="cluster-id",
Engine="aurora-postgresql",
MasterUsername="root",
) )
err = ex.value.response["Error"] err = ex.value.response["Error"]
assert err["Code"] == "InvalidParameterValue" assert err["Code"] == "InvalidParameterValue"
@ -66,7 +85,7 @@ def test_create_db_cluster_needs_long_master_user_password():
with pytest.raises(ClientError) as ex: with pytest.raises(ClientError) as ex:
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2", MasterUserPassword="hunter2",
) )
@ -84,7 +103,7 @@ def test_modify_db_cluster_needs_long_master_user_password():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter21", MasterUserPassword="hunter21",
) )
@ -110,7 +129,7 @@ def test_modify_db_cluster_new_cluster_identifier():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier=old_id, DBClusterIdentifier=old_id,
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter21", MasterUserPassword="hunter21",
) )
@ -137,7 +156,7 @@ def test_create_db_cluster__verify_default_properties():
resp = client.create_db_cluster( resp = client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-mysql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -169,8 +188,8 @@ def test_create_db_cluster__verify_default_properties():
) )
assert cluster["ReaderEndpoint"] == expected_readonly assert cluster["ReaderEndpoint"] == expected_readonly
assert cluster["MultiAZ"] is False assert cluster["MultiAZ"] is False
assert cluster["Engine"] == "aurora" assert cluster["Engine"] == "aurora-mysql"
assert cluster["EngineVersion"] == "5.6.mysql_aurora.1.22.5" assert cluster["EngineVersion"] == "5.7.mysql_aurora.2.07.2"
assert cluster["Port"] == 3306 assert cluster["Port"] == 3306
assert cluster["MasterUsername"] == "root" assert cluster["MasterUsername"] == "root"
assert cluster["PreferredBackupWindow"] == "01:37-02:07" assert cluster["PreferredBackupWindow"] == "01:37-02:07"
@ -206,7 +225,7 @@ def test_create_db_cluster_additional_parameters():
AvailabilityZones=["eu-north-1b"], AvailabilityZones=["eu-north-1b"],
DatabaseName="users", DatabaseName="users",
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
EngineVersion="8.0.mysql_aurora.3.01.0", EngineVersion="8.0.mysql_aurora.3.01.0",
EngineMode="serverless", EngineMode="serverless",
MasterUsername="root", MasterUsername="root",
@ -232,7 +251,7 @@ def test_create_db_cluster_additional_parameters():
assert cluster["AvailabilityZones"] == ["eu-north-1b"] assert cluster["AvailabilityZones"] == ["eu-north-1b"]
assert cluster["DatabaseName"] == "users" assert cluster["DatabaseName"] == "users"
assert cluster["Engine"] == "aurora" assert cluster["Engine"] == "aurora-postgresql"
assert cluster["EngineVersion"] == "8.0.mysql_aurora.3.01.0" assert cluster["EngineVersion"] == "8.0.mysql_aurora.3.01.0"
assert cluster["EngineMode"] == "serverless" assert cluster["EngineMode"] == "serverless"
assert cluster["Port"] == 1234 assert cluster["Port"] == 1234
@ -259,14 +278,14 @@ def test_describe_db_cluster_after_creation():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id1", DBClusterIdentifier="cluster-id1",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
cluster_arn = client.create_db_cluster( cluster_arn = client.create_db_cluster(
DBClusterIdentifier="cluster-id2", DBClusterIdentifier="cluster-id2",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
)["DBCluster"]["DBClusterArn"] )["DBCluster"]["DBClusterArn"]
@ -292,7 +311,7 @@ def test_delete_db_cluster():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -308,7 +327,7 @@ def test_delete_db_cluster_do_snapshot():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -329,7 +348,7 @@ def test_delete_db_cluster_that_is_protected():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
DeletionProtection=True, DeletionProtection=True,
@ -369,7 +388,7 @@ def test_start_db_cluster_after_stopping():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -386,7 +405,7 @@ def test_start_db_cluster_without_stopping():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -404,7 +423,7 @@ def test_stop_db_cluster():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -425,7 +444,7 @@ def test_stop_db_cluster_already_stopped():
client.create_db_cluster( client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
Engine="aurora", Engine="aurora-postgresql",
MasterUsername="root", MasterUsername="root",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",
) )
@ -838,7 +857,7 @@ def test_create_db_cluster_with_enable_http_endpoint_invalid():
resp = client.create_db_cluster( resp = client.create_db_cluster(
DBClusterIdentifier="cluster-id", DBClusterIdentifier="cluster-id",
DatabaseName="users", DatabaseName="users",
Engine="aurora-mysql", Engine="aurora-postgresql",
EngineMode="serverless", EngineMode="serverless",
EngineVersion="5.7.0", EngineVersion="5.7.0",
MasterUsername="root", MasterUsername="root",

View File

@ -74,7 +74,7 @@ def test_add_instance_to_serverless_cluster():
_ = client.create_db_cluster( _ = client.create_db_cluster(
DBClusterIdentifier="dbci", DBClusterIdentifier="dbci",
Engine="aurora", Engine="aurora-postgresql",
EngineMode="serverless", EngineMode="serverless",
MasterUsername="masterusername", MasterUsername="masterusername",
MasterUserPassword="hunter2_", MasterUserPassword="hunter2_",