From 0a5e2d3e4dac477d703d799e40f00f83d4b5ffa4 Mon Sep 17 00:00:00 2001 From: rafcio19 Date: Mon, 6 Nov 2023 21:22:28 +0100 Subject: [PATCH] RDS: detect rds instance / cluster engine mismatch (#6994) --- moto/rds/exceptions.py | 8 ++ moto/rds/models.py | 5 ++ tests/test_rds/test_rds_clusters.py | 82 +++++++++++++------ .../test_rds_clusters_with_instances.py | 4 +- 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/moto/rds/exceptions.py b/moto/rds/exceptions.py index 1ee93b20b..f6af07161 100644 --- a/moto/rds/exceptions.py +++ b/moto/rds/exceptions.py @@ -201,3 +201,11 @@ class InvalidDBInstanceIdentifier(InvalidParameterValue): "Identifiers must begin with a letter; must contain only ASCII letters, digits, and hyphens; " "and must not end with a hyphen or contain two consecutive hyphens." ) + + +class InvalidDBInstanceEngine(InvalidParameterCombination): + def __init__(self, instance_engine: str, cluster_engine: str) -> None: + super().__init__( + f"The engine name requested for your DB instance ({instance_engine}) doesn't match " + f"the engine name of your DB cluster ({cluster_engine})." + ) diff --git a/moto/rds/models.py b/moto/rds/models.py index c430ab369..5e39aaef4 100644 --- a/moto/rds/models.py +++ b/moto/rds/models.py @@ -27,6 +27,7 @@ from .exceptions import ( DBClusterParameterGroupNotFoundError, OptionGroupNotFoundFaultError, InvalidDBClusterStateFaultError, + InvalidDBInstanceEngine, InvalidDBInstanceStateError, SnapshotQuotaExceededError, DBSnapshotAlreadyExistsError, @@ -1593,6 +1594,10 @@ class RDSBackend(BaseBackend): raise InvalidParameterValue( "Instances cannot be added to Aurora Serverless clusters." ) + if database.engine != cluster.engine: + raise InvalidDBInstanceEngine( + str(database.engine), str(cluster.engine) + ) cluster.cluster_members.append(database_id) self.databases[database_id] = database return database diff --git a/tests/test_rds/test_rds_clusters.py b/tests/test_rds/test_rds_clusters.py index 3190bc8ae..2c261d498 100644 --- a/tests/test_rds/test_rds_clusters.py +++ b/tests/test_rds/test_rds_clusters.py @@ -7,10 +7,12 @@ import pytest from moto import mock_rds from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID +RDS_REGION = "eu-north-1" + @mock_rds def test_describe_db_cluster_initial(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.describe_db_clusters() assert len(resp["DBClusters"]) == 0 @@ -18,7 +20,7 @@ def test_describe_db_cluster_initial(): @mock_rds def test_describe_db_cluster_fails_for_non_existent_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.describe_db_clusters() assert len(resp["DBClusters"]) == 0 @@ -31,7 +33,7 @@ def test_describe_db_cluster_fails_for_non_existent_cluster(): @mock_rds def test_create_db_cluster_needs_master_username(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.create_db_cluster(DBClusterIdentifier="cluster-id", Engine="aurora") @@ -44,7 +46,7 @@ def test_create_db_cluster_needs_master_username(): @mock_rds def test_create_db_cluster_needs_master_user_password(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.create_db_cluster( @@ -59,7 +61,7 @@ def test_create_db_cluster_needs_master_user_password(): @mock_rds def test_create_db_cluster_needs_long_master_user_password(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.create_db_cluster( @@ -78,7 +80,7 @@ def test_create_db_cluster_needs_long_master_user_password(): @mock_rds def test_modify_db_cluster_needs_long_master_user_password(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -102,7 +104,7 @@ def test_modify_db_cluster_needs_long_master_user_password(): @mock_rds def test_modify_db_cluster_new_cluster_identifier(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) old_id = "cluster-id" new_id = "new-cluster-id" @@ -131,7 +133,7 @@ def test_modify_db_cluster_new_cluster_identifier(): @mock_rds def test_create_db_cluster__verify_default_properties(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -198,7 +200,7 @@ def test_create_db_cluster__verify_default_properties(): @mock_rds def test_create_db_cluster_additional_parameters(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.create_db_cluster( AvailabilityZones=["eu-north-1b"], @@ -253,7 +255,7 @@ def test_create_db_cluster_additional_parameters(): @mock_rds def test_describe_db_cluster_after_creation(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id1", @@ -286,7 +288,7 @@ def test_describe_db_cluster_after_creation(): @mock_rds def test_delete_db_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -302,7 +304,7 @@ def test_delete_db_cluster(): @mock_rds def test_delete_db_cluster_do_snapshot(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -323,7 +325,7 @@ def test_delete_db_cluster_do_snapshot(): @mock_rds def test_delete_db_cluster_that_is_protected(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -341,7 +343,7 @@ def test_delete_db_cluster_that_is_protected(): @mock_rds def test_delete_db_cluster_unknown_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.delete_db_cluster(DBClusterIdentifier="cluster-unknown") @@ -352,7 +354,7 @@ def test_delete_db_cluster_unknown_cluster(): @mock_rds def test_start_db_cluster_unknown_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.start_db_cluster(DBClusterIdentifier="cluster-unknown") @@ -363,7 +365,7 @@ def test_start_db_cluster_unknown_cluster(): @mock_rds def test_start_db_cluster_after_stopping(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -380,7 +382,7 @@ def test_start_db_cluster_after_stopping(): @mock_rds def test_start_db_cluster_without_stopping(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -398,7 +400,7 @@ def test_start_db_cluster_without_stopping(): @mock_rds def test_stop_db_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -419,7 +421,7 @@ def test_stop_db_cluster(): @mock_rds def test_stop_db_cluster_already_stopped(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -439,7 +441,7 @@ def test_stop_db_cluster_already_stopped(): @mock_rds def test_stop_db_cluster_unknown_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) with pytest.raises(ClientError) as ex: client.stop_db_cluster(DBClusterIdentifier="cluster-unknown") @@ -807,7 +809,7 @@ def test_add_tags_to_cluster_snapshot(): @mock_rds def test_create_serverless_db_cluster(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -831,7 +833,7 @@ def test_create_serverless_db_cluster(): @mock_rds def test_create_db_cluster_with_enable_http_endpoint_invalid(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) resp = client.create_db_cluster( DBClusterIdentifier="cluster-id", @@ -850,7 +852,7 @@ def test_create_db_cluster_with_enable_http_endpoint_invalid(): @mock_rds def test_describe_db_clusters_filter_by_engine(): - client = boto3.client("rds", region_name="eu-north-1") + client = boto3.client("rds", region_name=RDS_REGION) client.create_db_cluster( DBClusterIdentifier="id1", @@ -922,3 +924,37 @@ def test_replicate_cluster(): replica = us_west.describe_db_clusters()["DBClusters"][0] assert "ReplicationSourceIdentifier" not in replica assert replica["MultiAZ"] is False + + +@mock_rds +def test_createdb_instance_engine_mismatch_fail(): + # Setup + client = boto3.client("rds", "us-east-1") + cluster_name = "test-cluster" + client.create_db_cluster( + DBClusterIdentifier=cluster_name, + Engine="aurora-postgresql", + EngineVersion="12.14", + MasterUsername="testuser", + MasterUserPassword="password", + ) + + # Execute + + with pytest.raises(ClientError) as exc: + client.create_db_instance( + DBClusterIdentifier=cluster_name, + Engine="mysql", + EngineVersion="12.14", + DBInstanceIdentifier="test-instance", + DBInstanceClass="db.t4g.medium", + ) + + # Verify + err = exc.value.response["Error"] + assert err["Code"] == "InvalidParameterCombination" + assert ( + err["Message"] + == "The engine name requested for your DB instance (mysql) doesn't match " + "the engine name of your DB cluster (aurora-postgresql)." + ) diff --git a/tests/test_rds/test_rds_clusters_with_instances.py b/tests/test_rds/test_rds_clusters_with_instances.py index 707999327..09dec8c56 100644 --- a/tests/test_rds/test_rds_clusters_with_instances.py +++ b/tests/test_rds/test_rds_clusters_with_instances.py @@ -21,7 +21,7 @@ def test_add_instance_as_cluster_member(): DBInstanceIdentifier="dbi", DBClusterIdentifier="dbci", DBInstanceClass="db.r5.large", - Engine="aurora-postgresql", + Engine="mysql", ) cluster = client.describe_db_clusters()["DBClusters"][0] @@ -53,7 +53,7 @@ def test_remove_instance_from_cluster(): DBInstanceIdentifier="dbi", DBClusterIdentifier="dbci", DBInstanceClass="db.r5.large", - Engine="aurora-postgresql", + Engine="mysql", ) client.delete_db_instance(