This is an automated email from the ASF dual-hosted git repository.

o-nikolas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 0cb0081d7be Make DMS system-test databases non-public (#71733)
0cb0081d7be is described below

commit 0cb0081d7bec792330b38512045ce3202249964b
Author: Niko Oliveira <[email protected]>
AuthorDate: Mon Aug 17 15:21:11 2026 -0700

    Make DMS system-test databases non-public (#71733)
    
    RdsCreateDbInstanceOperator was called with PubliclyAccessible=True in the
    example_dms and example_dms_serverless system tests. example_dms also
    created its security group with an 0.0.0.0/0 ingress rule.
    
    Make the databases non-public and reachable from within a VPC instead:
    
    - Set PubliclyAccessible=False for both tests.
    - Add optional SUBNET_GROUP / SECURITY_GROUP / REPLICATION_SUBNET_GROUP
      context variables. When provided, the RDS instance and the DMS
      replication instance / serverless replication are placed in those 
(private)
      subnet groups / security group; when absent, behaviour is unchanged
      aside from the instance no longer being public. This mirrors the
      approach used for the Redshift system tests (apache#70705).
    - example_dms: reuse a provided security group (skip create/delete) and,
      when creating its own, scope ingress to the VPC CIDR on 5432 instead of
      0.0.0.0/0.
---
 .../amazon/tests/system/amazon/aws/example_dms.py  | 113 ++++++++++++++++-----
 .../system/amazon/aws/example_dms_serverless.py    |  53 ++++++++--
 2 files changed, 129 insertions(+), 37 deletions(-)

diff --git a/providers/amazon/tests/system/amazon/aws/example_dms.py 
b/providers/amazon/tests/system/amazon/aws/example_dms.py
index 8ecfc07b735..bf15a80237f 100644
--- a/providers/amazon/tests/system/amazon/aws/example_dms.py
+++ b/providers/amazon/tests/system/amazon/aws/example_dms.py
@@ -68,7 +68,19 @@ from system.amazon.aws.utils.ec2 import get_default_vpc_id
 
 DAG_ID = "example_dms"
 
-sys_test_context_task = SystemTestContextBuilder().build()
+# Optional externally fetched variables. When provided, the RDS instance and 
the DMS
+# replication instance are placed in the given subnet groups / security group.
+SUBNET_GROUP_KEY = "SUBNET_GROUP"
+SECURITY_GROUP_KEY = "SECURITY_GROUP"
+REPLICATION_SUBNET_GROUP_KEY = "REPLICATION_SUBNET_GROUP"
+
+sys_test_context_task = (
+    SystemTestContextBuilder()
+    .add_variable(SUBNET_GROUP_KEY, optional=True)
+    .add_variable(SECURITY_GROUP_KEY, optional=True)
+    .add_variable(REPLICATION_SUBNET_GROUP_KEY, optional=True)
+    .build()
+)
 
 # Config values for setting up the RDS databases.
 RDS_ENGINE = "postgres"
@@ -84,11 +96,6 @@ SAMPLE_DATA = [
     ("Subversion", "2000"),
     ("NiFi", "2006"),
 ]
-SG_IP_PERMISSION = {
-    "FromPort": 5432,
-    "IpProtocol": "All",
-    "IpRanges": [{"CidrIp": "0.0.0.0/0"}],
-}
 
 
 def _get_rds_instance_endpoint(instance_name: str):
@@ -101,8 +108,12 @@ def _get_rds_instance_endpoint(instance_name: str):
 
 
 @task
-def create_security_group(security_group_name: str, vpc_id: str):
+def create_security_group(security_group_name: str, vpc_id: str, 
existing_security_group: str | None = None):
+    if existing_security_group:
+        print("Using the provided security group, skipping creation.")
+        return existing_security_group
     client = boto3.client("ec2")
+    vpc_cidr = client.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0]["CidrBlock"]
     security_group = client.create_security_group(
         GroupName=security_group_name,
         Description="Created for DMS system test",
@@ -113,12 +124,48 @@ def create_security_group(security_group_name: str, 
vpc_id: str):
     )
     client.authorize_security_group_ingress(
         GroupId=security_group["GroupId"],
-        IpPermissions=[SG_IP_PERMISSION],
+        IpPermissions=[
+            {
+                "FromPort": 5432,
+                "ToPort": 5432,
+                "IpProtocol": "tcp",
+                "IpRanges": [{"CidrIp": vpc_cidr}],
+            }
+        ],
     )
 
     return security_group["GroupId"]
 
 
+@task
+def build_rds_kwargs(
+    db_name: str,
+    engine_version: str,
+    parameter_group_name: str,
+    security_group_id: str,
+    subnet_group: str | None = None,
+) -> dict:
+    """
+    Assemble the kwargs for RdsCreateDbInstanceOperator at runtime.
+
+    When a DB subnet group is provided via the test context, the instance is 
placed in it (e.g. private subnets
+    reachable by the test runner), otherwise it lands in the default VPC.
+    """
+    rds_kwargs = {
+        "DBName": db_name,
+        "AllocatedStorage": 20,
+        "MasterUsername": RDS_USERNAME,
+        "MasterUserPassword": RDS_PASSWORD,
+        "PubliclyAccessible": False,
+        "EngineVersion": engine_version,
+        "DBParameterGroupName": parameter_group_name,
+        "VpcSecurityGroupIds": [security_group_id],
+    }
+    if subnet_group:
+        rds_kwargs["DBSubnetGroupName"] = subnet_group
+    return rds_kwargs
+
+
 @task(multiple_outputs=True)
 def create_db_parameter_group(parameter_group_name: str):
     rds_client = boto3.client("rds")
@@ -210,16 +257,24 @@ def create_dms_assets(
     replication_instance_name: str,
     source_endpoint_identifier: str,
     target_endpoint_identifier: str,
+    replication_subnet_group: str | None = None,
 ):
     print("Creating DMS assets.")
     dms_client = boto3.client("dms")
     rds_instance_endpoint = _get_rds_instance_endpoint(instance_name)
 
     print("Creating replication instance.")
-    instance_arn = dms_client.create_replication_instance(
-        ReplicationInstanceIdentifier=replication_instance_name,
-        ReplicationInstanceClass="dms.t3.small",
-    )["ReplicationInstance"]["ReplicationInstanceArn"]
+    replication_instance_kwargs = {
+        "ReplicationInstanceIdentifier": replication_instance_name,
+        "ReplicationInstanceClass": "dms.t3.small",
+    }
+    if replication_subnet_group:
+        # Place the replication instance in the same subnets as the (non 
publicly
+        # accessible) source database so it can reach its private endpoint.
+        replication_instance_kwargs["ReplicationSubnetGroupIdentifier"] = 
replication_subnet_group
+    instance_arn = 
dms_client.create_replication_instance(**replication_instance_kwargs)[
+        "ReplicationInstance"
+    ]["ReplicationInstanceArn"]
 
     print("Creating DMS source endpoint.")
     source_endpoint_arn = dms_client.create_endpoint(
@@ -290,7 +345,12 @@ def delete_dms_assets(
 
 
 @task(trigger_rule=TriggerRule.ALL_DONE)
-def delete_security_group(security_group_id: str, security_group_name: str):
+def delete_security_group(
+    security_group_id: str, security_group_name: str, existing_security_group: 
str | None = None
+):
+    if existing_security_group:
+        print("Security group was provided externally, skipping deletion.")
+        return
     boto3.client("ec2").delete_security_group(GroupId=security_group_id, 
GroupName=security_group_name)
 
 
@@ -311,6 +371,9 @@ with DAG(
 ) as dag:
     test_context = sys_test_context_task()
     env_id = test_context[ENV_ID_KEY]
+    subnet_group = test_context[SUBNET_GROUP_KEY]
+    security_group = test_context[SECURITY_GROUP_KEY]
+    replication_subnet_group = test_context[REPLICATION_SUBNET_GROUP_KEY]
 
     rds_instance_name = f"{env_id}-instance"
     rds_source_db_name = f"{env_id}_source_database"  # dashes are not allowed 
in db name
@@ -345,25 +408,20 @@ with DAG(
 
     get_vpc_id = get_default_vpc_id()
 
-    create_sg = create_security_group(security_group_name, get_vpc_id)
+    create_sg = create_security_group(security_group_name, get_vpc_id, 
security_group)
 
     create_db_instance = RdsCreateDbInstanceOperator(
         task_id="create_db_instance",
         db_instance_identifier=rds_instance_name,
         db_instance_class="db.t3.micro",
         engine=RDS_ENGINE,
-        rds_kwargs={
-            "DBName": rds_source_db_name,
-            "AllocatedStorage": 20,
-            "MasterUsername": RDS_USERNAME,
-            "MasterUserPassword": RDS_PASSWORD,
-            "PubliclyAccessible": True,
-            "EngineVersion": db_parameter_group["engine_version"],
-            "DBParameterGroupName": db_parameter_group["name"],
-            "VpcSecurityGroupIds": [
-                create_sg,
-            ],
-        },
+        rds_kwargs=build_rds_kwargs(
+            rds_source_db_name,
+            db_parameter_group["engine_version"],
+            db_parameter_group["name"],
+            create_sg,
+            subnet_group,
+        ),
     )
 
     create_target_db = create_target_database(
@@ -379,6 +437,7 @@ with DAG(
         replication_instance_name=dms_replication_instance_name,
         source_endpoint_identifier=source_endpoint_identifier,
         target_endpoint_identifier=target_endpoint_identifier,
+        replication_subnet_group=replication_subnet_group,
     )
 
     # [START howto_operator_dms_create_task]
@@ -538,7 +597,7 @@ with DAG(
         delete_assets,
         delete_db_instance,
         delete_parameter_group,
-        delete_security_group(create_sg, security_group_name),
+        delete_security_group(create_sg, security_group_name, security_group),
     )
 
     from tests_common.test_utils.watcher import watcher
diff --git a/providers/amazon/tests/system/amazon/aws/example_dms_serverless.py 
b/providers/amazon/tests/system/amazon/aws/example_dms_serverless.py
index 770cc43b6fe..3af94147b2e 100644
--- a/providers/amazon/tests/system/amazon/aws/example_dms_serverless.py
+++ b/providers/amazon/tests/system/amazon/aws/example_dms_serverless.py
@@ -68,8 +68,20 @@ documentation.
 
 DAG_ID = "example_dms_serverless"
 ROLE_ARN_KEY = "ROLE_ARN"
-
-sys_test_context_task = 
SystemTestContextBuilder().add_variable(ROLE_ARN_KEY).build()
+# Optional externally fetched variables. When provided, the RDS instance and 
the serverless
+# replication are placed in the given subnet groups / security group.
+SUBNET_GROUP_KEY = "SUBNET_GROUP"
+SECURITY_GROUP_KEY = "SECURITY_GROUP"
+REPLICATION_SUBNET_GROUP_KEY = "REPLICATION_SUBNET_GROUP"
+
+sys_test_context_task = (
+    SystemTestContextBuilder()
+    .add_variable(ROLE_ARN_KEY)
+    .add_variable(SUBNET_GROUP_KEY, optional=True)
+    .add_variable(SECURITY_GROUP_KEY, optional=True)
+    .add_variable(REPLICATION_SUBNET_GROUP_KEY, default_value="default")
+    .build()
+)
 
 # Config values for setting up the "Source" database.
 CA_CERT_ID = "rds-ca-rsa2048-g1"
@@ -97,6 +109,30 @@ def _get_rds_instance_endpoint(instance_name: str):
     return rds_instance_endpoint
 
 
+@task
+def build_rds_kwargs(
+    db_name: str, security_group_id: str | None = None, subnet_group: str | 
None = None
+) -> dict:
+    """
+    Assemble the kwargs for RdsCreateDbInstanceOperator at runtime.
+
+    When a DB subnet group / security group are provided via the test context, 
the instance is placed in
+    them (e.g. private subnets reachable by the test runner), otherwise it 
lands in the default VPC.
+    """
+    rds_kwargs = {
+        "DBName": db_name,
+        "AllocatedStorage": 20,
+        "MasterUsername": RDS_USERNAME,
+        "MasterUserPassword": RDS_PASSWORD,
+        "PubliclyAccessible": False,
+    }
+    if security_group_id:
+        rds_kwargs["VpcSecurityGroupIds"] = [security_group_id]
+    if subnet_group:
+        rds_kwargs["DBSubnetGroupName"] = subnet_group
+    return rds_kwargs
+
+
 @task
 def create_sample_table(instance_name: str, db_name: str, table_name: str):
     print("Creating sample table.")
@@ -199,6 +235,9 @@ with DAG(
     test_context = sys_test_context_task()
     env_id = test_context[ENV_ID_KEY]
     role_arn = test_context[ROLE_ARN_KEY]
+    subnet_group = test_context[SUBNET_GROUP_KEY]
+    security_group = test_context[SECURITY_GROUP_KEY]
+    replication_subnet_group = test_context[REPLICATION_SUBNET_GROUP_KEY]
 
     bucket_name = f"{env_id}-dms-serverless-bucket"
     rds_instance_name = f"{env_id}-instance"
@@ -215,13 +254,7 @@ with DAG(
         db_instance_identifier=rds_instance_name,
         db_instance_class="db.t3.micro",
         engine=RDS_ENGINE,
-        rds_kwargs={
-            "DBName": rds_db_name,
-            "AllocatedStorage": 20,
-            "MasterUsername": RDS_USERNAME,
-            "MasterUserPassword": RDS_PASSWORD,
-            "PubliclyAccessible": True,
-        },
+        rds_kwargs=build_rds_kwargs(rds_db_name, security_group, subnet_group),
     )
 
     # Sample data.
@@ -278,7 +311,7 @@ with DAG(
             "MaxCapacityUnits": 4,
             "MinCapacityUnits": 1,
             "MultiAZ": False,
-            "ReplicationSubnetGroupId": "default",
+            "ReplicationSubnetGroupId": replication_subnet_group,
         },
         replication_type="full-load",
         table_mappings=json.dumps(table_mappings),

Reply via email to