From 447710c6a68e7d5ea7ad6d7df93c663de32ac7f1 Mon Sep 17 00:00:00 2001 From: Xiaodong Hu Date: Mon, 13 Nov 2023 19:24:32 +0900 Subject: [PATCH] RDS: Add validation for engine parameter before creating db_instance (#7002) --- moto/rds/models.py | 5 ++ moto/rds/utils.py | 28 +++++++++ .../rds_mysql_with_db_parameter_group.py | 2 +- .../fixtures/rds_mysql_with_read_replica.py | 2 +- tests/test_rds/test_rds.py | 60 ++++++++++++------- tests/test_rds/test_server.py | 2 +- 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/moto/rds/models.py b/moto/rds/models.py index 899ff6456..82a7c0755 100644 --- a/moto/rds/models.py +++ b/moto/rds/models.py @@ -49,6 +49,7 @@ from .utils import ( merge_filters, validate_filters, valid_preferred_maintenance_window, + DbInstanceEngine, ClusterEngine, ) @@ -568,6 +569,10 @@ class Database(CloudFormationModel): self.account_id: str = kwargs["account_id"] self.region_name: str = kwargs["region"] self.engine = kwargs.get("engine") + if self.engine not in DbInstanceEngine.valid_db_instance_engine(): + raise InvalidParameterValue( + f"Value {self.engine} for parameter Engine is invalid. Reason: engine {self.engine} not supported" + ) self.engine_version = kwargs.get("engine_version", None) if not self.engine_version and self.engine in self.default_engine_versions: self.engine_version = self.default_engine_versions[self.engine] diff --git a/moto/rds/utils.py b/moto/rds/utils.py index 6c26c96d1..b890ff9b5 100644 --- a/moto/rds/utils.py +++ b/moto/rds/utils.py @@ -23,6 +23,34 @@ FilterDef = namedtuple( ) +class DbInstanceEngine(str, Enum): + + # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds/client/create_db_instance.html + # 2023-11-08 + AURORA_MYSQL = "aurora-mysql" + AURORA_POSTGRESQL = "aurora-postgresql" + CUSTOM_ORACLE_EE = "custom-oracle-ee" + CUSTOM_ORACLE_EE_CDB = "custom-oracle-ee-cdb" + CUSTOM_SQLSERVER_EE = "custom-sqlserver-ee" + CUSTOM_SQLSERVER_SE = "custom-sqlserver-se" + CUSTOM_SQLSERVER_WEB = "custom-sqlserver-web" + MARIADB = "mariadb" + MYSQL = "mysql" + ORACLE_EE = "oracle-ee" + ORACLE_EE_CDB = "oracle-ee-cdb" + ORACLE_SE2 = "oracle-se2" + ORACLE_SE2_CDB = "oracle-se2-cdb" + POSTGRES = "postgres" + SQLSERVER_EE = "sqlserver-ee" + SQLSERVER_SE = "sqlserver-se" + SQLSERVER_EX = "sqlserver-ex" + SQLSERVER_WEB = "sqlserver-web" + + @classmethod + def valid_db_instance_engine(self) -> List[str]: + return sorted([item.value for item in DbInstanceEngine]) + + class ClusterEngine(str, Enum): AURORA_POSTGRESQL = "aurora-postgresql" AURORA_MYSQL = "aurora-mysql" diff --git a/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py b/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py index b0dac055b..71ac3fe4f 100644 --- a/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py +++ b/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py @@ -150,7 +150,7 @@ template = { "DBName": {"Ref": "DBName"}, "AllocatedStorage": {"Ref": "DBAllocatedStorage"}, "DBInstanceClass": {"Ref": "DBInstanceClass"}, - "Engine": "MySQL", + "Engine": "mysql", "DBSubnetGroupName": { "Fn::If": [ "Is-EC2-VPC", diff --git a/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py b/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py index a21589966..25f4537a6 100644 --- a/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py +++ b/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py @@ -142,7 +142,7 @@ template = { "DBName": {"Ref": "DBName"}, "AllocatedStorage": {"Ref": "DBAllocatedStorage"}, "DBInstanceClass": {"Ref": "DBInstanceClass"}, - "Engine": "MySQL", + "Engine": "mysql", "DBSubnetGroupName": { "Fn::If": [ "Is-EC2-VPC", diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py index 4fafa9d40..613a5118c 100644 --- a/tests/test_rds/test_rds.py +++ b/tests/test_rds/test_rds.py @@ -482,7 +482,7 @@ def test_get_databases(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -492,7 +492,7 @@ def test_get_databases(): DBInstanceIdentifier="db-master-2", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -554,7 +554,7 @@ def test_modify_db_instance(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -586,7 +586,7 @@ def test_modify_db_instance_not_existent_db_parameter_group_name(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -608,7 +608,7 @@ def test_modify_db_instance_valid_preferred_maintenance_window(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -632,7 +632,7 @@ def test_modify_db_instance_valid_preferred_maintenance_window_uppercase(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -656,7 +656,7 @@ def test_modify_db_instance_invalid_preferred_maintenance_window_more_than_24_ho DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -679,7 +679,7 @@ def test_modify_db_instance_invalid_preferred_maintenance_window_less_than_30_mi DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -702,7 +702,7 @@ def test_modify_db_instance_invalid_preferred_maintenance_window_value(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -725,7 +725,7 @@ def test_modify_db_instance_invalid_preferred_maintenance_window_format(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -751,7 +751,7 @@ def test_modify_db_instance_maintenance_backup_window_no_spill(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -777,7 +777,7 @@ def test_modify_db_instance_maintenance_backup_window_maintenance_spill(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -803,7 +803,7 @@ def test_modify_db_instance_maintenance_backup_window_backup_spill(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -829,7 +829,7 @@ def test_modify_db_instance_maintenance_backup_window_both_spill(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -855,7 +855,7 @@ def test_rename_db_instance(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -892,7 +892,7 @@ def test_reboot_db_instance(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -1484,7 +1484,7 @@ def test_list_tags_db(): DBInstanceIdentifier="db-with-tags", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -1507,7 +1507,7 @@ def test_add_tags_db(): DBInstanceIdentifier="db-without-tags", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -1535,7 +1535,7 @@ def test_remove_tags_db(): DBInstanceIdentifier="db-with-tags", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -1807,7 +1807,7 @@ def test_add_security_group_to_database(): DBInstanceIdentifier="db-master-1", AllocatedStorage=10, DBInstanceClass="postgres", - Engine="db.m1.small", + Engine="postgres", MasterUsername="root", MasterUserPassword="hunter2", Port=1234, @@ -2629,6 +2629,26 @@ def test_validate_db_identifier(): validation_helper(exc) +@mock_rds +def test_createdb_instance_engine_with_invalid_value(): + client = boto3.client("rds", region_name=DEFAULT_REGION) + with pytest.raises(ClientError) as exc: + client.create_db_instance( + DBInstanceIdentifier="test-db-instance", + Engine="invalid-engine", + DBName="staging-postgres", + DBInstanceClass="db.m1.small", + ) + + err = exc.value.response["Error"] + + assert err["Code"] == "InvalidParameterValue" + assert ( + err["Message"] + == "Value invalid-engine for parameter Engine is invalid. Reason: engine invalid-engine not supported" + ) + + def validation_helper(exc): err = exc.value.response["Error"] assert err["Code"] == "InvalidParameterValue" diff --git a/tests/test_rds/test_server.py b/tests/test_rds/test_server.py index 9f6a0fbcd..82dab5db2 100644 --- a/tests/test_rds/test_server.py +++ b/tests/test_rds/test_server.py @@ -19,7 +19,7 @@ def test_create_db_instance(): "Action": "CreateDBInstance", "DBInstanceIdentifier": "hi", "DBInstanceClass": "db.m4.large", - "Engine": "aurora", + "Engine": "aurora-mysql", "StorageType": "standard", "Port": 3306, }