diff --git a/moto/rds/exceptions.py b/moto/rds/exceptions.py
index cc78be6d0..678d1a78c 100644
--- a/moto/rds/exceptions.py
+++ b/moto/rds/exceptions.py
@@ -218,3 +218,35 @@ class InvalidDBInstanceEngine(InvalidParameterCombination):
f"The engine name requested for your DB instance ({instance_engine}) doesn't match "
f"the engine name of your DB cluster ({cluster_engine})."
)
+
+
+class InvalidSubnet(RDSClientError):
+ def __init__(self, subnet_identifier: str):
+ super().__init__(
+ "InvalidSubnet",
+ f"The requested subnet {subnet_identifier} is invalid, or multiple subnets were requested that are not all in a common VPC.",
+ )
+
+
+class DBProxyAlreadyExistsFault(RDSClientError):
+ def __init__(self, db_proxy_identifier: str):
+ super().__init__(
+ "DBProxyAlreadyExistsFault",
+ f"Cannot create the DBProxy because a DBProxy with the identifier {db_proxy_identifier} already exists.",
+ )
+
+
+class DBProxyQuotaExceededFault(RDSClientError):
+ def __init__(self) -> None:
+ super().__init__(
+ "DBProxyQuotaExceeded",
+ "The request cannot be processed because it would exceed the maximum number of DBProxies.",
+ )
+
+
+class DBProxyNotFoundFault(RDSClientError):
+ def __init__(self, db_proxy_identifier: str):
+ super().__init__(
+ "DBProxyNotFoundFault",
+ f"The specified proxy name {db_proxy_identifier} doesn't correspond to a proxy owned by your Amazon Web Services account in the specified Amazon Web Services Region.",
+ )
diff --git a/moto/rds/models.py b/moto/rds/models.py
index 460360be5..18ffab32f 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -24,6 +24,9 @@ from .exceptions import (
DBClusterToBeDeletedHasActiveMembers,
DBInstanceNotFoundError,
DBParameterGroupNotFoundError,
+ DBProxyAlreadyExistsFault,
+ DBProxyNotFoundFault,
+ DBProxyQuotaExceededFault,
DBSecurityGroupNotFoundError,
DBSnapshotAlreadyExistsError,
DBSnapshotNotFoundError,
@@ -39,6 +42,7 @@ from .exceptions import (
InvalidGlobalClusterStateFault,
InvalidParameterCombination,
InvalidParameterValue,
+ InvalidSubnet,
OptionGroupNotFoundFaultError,
RDSClientError,
SnapshotQuotaExceededError,
@@ -1573,11 +1577,128 @@ class SubnetGroup(CloudFormationModel):
backend.delete_subnet_group(self.subnet_name)
+class DBProxy(BaseModel):
+ def __init__(
+ self,
+ db_proxy_name: str,
+ engine_family: str,
+ auth: List[Dict[str, str]],
+ role_arn: str,
+ vpc_subnet_ids: List[str],
+ region_name: str,
+ account_id: str,
+ vpc_security_group_ids: Optional[List[str]],
+ require_tls: Optional[bool] = False,
+ idle_client_timeout: Optional[int] = 1800,
+ debug_logging: Optional[bool] = False,
+ tags: Optional[List[Dict[str, str]]] = None,
+ ):
+ self.db_proxy_name = db_proxy_name
+ self.engine_family = engine_family
+ if self.engine_family not in ["MYSQL", "POSTGRESQ", "SQLSERVER"]:
+ raise InvalidParameterValue("Provided EngineFamily is not valid.")
+ self.auth = auth
+ self.role_arn = role_arn
+ self.vpc_subnet_ids = vpc_subnet_ids
+ self.vpc_security_group_ids = vpc_security_group_ids
+ self.require_tls = require_tls
+ if idle_client_timeout is None:
+ self.idle_client_timeout = 1800
+ else:
+ if int(idle_client_timeout) < 1:
+ self.idle_client_timeout = 1
+ elif int(idle_client_timeout) > 28800:
+ self.idle_client_timeout = 28800
+ else:
+ self.idle_client_timeout = idle_client_timeout
+ self.debug_logging = debug_logging
+ self.created_date = iso_8601_datetime_with_milliseconds()
+ self.updated_date = iso_8601_datetime_with_milliseconds()
+ if tags is None:
+ self.tags = []
+ else:
+ self.tags = tags
+ self.region_name = region_name
+ self.account_id = account_id
+ self.db_proxy_arn = f"arn:aws:rds:{self.region_name}:{self.account_id}:db-proxy:{self.db_proxy_name}"
+ self.arn = self.db_proxy_arn
+ ec2_backend = ec2_backends[self.account_id][self.region_name]
+ subnets = ec2_backend.describe_subnets(subnet_ids=self.vpc_subnet_ids)
+ vpcs = []
+ for subnet in subnets:
+ vpcs.append(subnet.vpc_id)
+ if subnet.vpc_id != vpcs[0]:
+ raise InvalidSubnet(subnet_identifier=subnet.id)
+
+ self.vpc_id = ec2_backend.describe_subnets(subnet_ids=[self.vpc_subnet_ids[0]])[
+ 0
+ ].vpc_id
+ self.status = "availible"
+ self.url_identifier = "".join(
+ random.choice(string.ascii_lowercase + string.digits) for _ in range(12)
+ )
+ self.endpoint = f"{self.db_proxy_name}.db-proxy-{self.url_identifier}.{self.region_name}.rds.amazonaws.com"
+
+ def get_tags(self) -> List[Dict[str, str]]:
+ return self.tags
+
+ def add_tags(self, tags: List[Dict[str, str]]) -> List[Dict[str, str]]:
+ new_keys = [tag_set["Key"] for tag_set in tags]
+ self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in new_keys]
+ self.tags.extend(tags)
+ return self.tags
+
+ def remove_tags(self, tag_keys: List[str]) -> None:
+ self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
+
+ def to_xml(self) -> str:
+ template = Template(
+ """
+ {{ dbproxy.require_tls }}
+
+ {% if dbproxy.VpcSecurityGroupIds %}
+ {% for vpcsecuritygroupid in dbproxy.VpcSecurityGroupIds %}
+ {{ vpcsecuritygroupid }}
+ {% endfor %}
+ {% endif %}
+
+
+ {% for auth in dbproxy.auth %}
+
+ {{ auth["UserName"] }}
+ {{ auth["AuthScheme"] }}
+ {{ auth["SecretArn"] }}
+ {{ auth["IAMAuth"] }}
+ {{ auth["ClientPasswordAuthType"] }}
+
+ {% endfor %}
+
+ {{ dbproxy.engine_family }}
+ {{ dbproxy.updated_date }}
+ {{ dbproxy.db_proxy_name }}
+ {{ dbproxy.idle_client_timeout }}
+ {{ dbproxy.endpoint }}
+ {{ dbproxy.created_date }}
+ {{ dbproxy.role_arn }}
+ {{ dbproxy.debug_logging }}
+ {{ dbproxy.vpc_id }}
+ {{ dbproxy.db_proxy_arn }}
+
+ {% for vpcsubnetid in dbproxy.vpc_subnet_ids %}
+ {{ vpcsubnetid }}
+ {% endfor %}
+
+ {{ dbproxy.status }}
+ """
+ )
+ return template.render(dbproxy=self)
+
+
class RDSBackend(BaseBackend):
def __init__(self, region_name: str, account_id: str):
super().__init__(region_name, account_id)
self.arn_regex = re_compile(
- r"^arn:aws:rds:.*:[0-9]*:(db|cluster|es|og|pg|ri|secgrp|snapshot|cluster-snapshot|subgrp):.*$"
+ r"^arn:aws:rds:.*:[0-9]*:(db|cluster|es|og|pg|ri|secgrp|snapshot|cluster-snapshot|subgrp|db-proxy):.*$"
)
self.clusters: Dict[str, Cluster] = OrderedDict()
self.global_clusters: Dict[str, GlobalCluster] = OrderedDict()
@@ -1592,6 +1713,7 @@ class RDSBackend(BaseBackend):
self.security_groups: Dict[str, SecurityGroup] = {}
self.subnet_groups: Dict[str, SubnetGroup] = {}
self._db_cluster_options: Optional[List[Dict[str, Any]]] = None
+ self.db_proxies: Dict[str, DBProxy] = OrderedDict()
def reset(self) -> None:
self.neptune.reset()
@@ -2586,6 +2708,9 @@ class RDSBackend(BaseBackend):
elif resource_type == "subgrp": # DB subnet group
if resource_name in self.subnet_groups:
return self.subnet_groups[resource_name].get_tags()
+ elif resource_type == "db-proxy": # DB Proxy
+ if resource_name in self.db_proxies:
+ return self.db_proxies[resource_name].get_tags()
else:
raise RDSClientError(
"InvalidParameterValue", f"Invalid resource name: {arn}"
@@ -2628,6 +2753,9 @@ class RDSBackend(BaseBackend):
elif resource_type == "subgrp": # DB subnet group
if resource_name in self.subnet_groups:
self.subnet_groups[resource_name].remove_tags(tag_keys)
+ elif resource_type == "db-proxy": # DB Proxy
+ if resource_name in self.db_proxies:
+ self.db_proxies[resource_name].remove_tags(tag_keys)
else:
raise RDSClientError(
"InvalidParameterValue", f"Invalid resource name: {arn}"
@@ -2669,6 +2797,9 @@ class RDSBackend(BaseBackend):
elif resource_type == "subgrp": # DB subnet group
if resource_name in self.subnet_groups:
return self.subnet_groups[resource_name].add_tags(tags)
+ elif resource_type == "db-proxy": # DB Proxy
+ if resource_name in self.db_proxies:
+ return self.db_proxies[resource_name].add_tags(tags)
else:
raise RDSClientError(
"InvalidParameterValue", f"Invalid resource name: {arn}"
@@ -2910,6 +3041,56 @@ class RDSBackend(BaseBackend):
)
return snapshot.attributes
+ def create_db_proxy(
+ self,
+ db_proxy_name: str,
+ engine_family: str,
+ auth: List[Dict[str, str]],
+ role_arn: str,
+ vpc_subnet_ids: List[str],
+ vpc_security_group_ids: Optional[List[str]],
+ require_tls: Optional[bool],
+ idle_client_timeout: Optional[int],
+ debug_logging: Optional[bool],
+ tags: Optional[List[Dict[str, str]]],
+ ) -> DBProxy:
+ self._validate_db_identifier(db_proxy_name)
+ if db_proxy_name in self.db_proxies:
+ raise DBProxyAlreadyExistsFault(db_proxy_name)
+ if len(self.db_proxies) >= int(os.environ.get("MOTO_RDS_PROXY_LIMIT", "100")):
+ raise DBProxyQuotaExceededFault()
+ db_proxy = DBProxy(
+ db_proxy_name,
+ engine_family,
+ auth,
+ role_arn,
+ vpc_subnet_ids,
+ self.region_name,
+ self.account_id,
+ vpc_security_group_ids,
+ require_tls,
+ idle_client_timeout,
+ debug_logging,
+ tags,
+ )
+ self.db_proxies[db_proxy_name] = db_proxy
+ return db_proxy
+
+ def describe_db_proxies(
+ self,
+ db_proxy_name: Optional[str],
+ filters: Optional[List[Dict[str, Any]]] = None,
+ ) -> List[DBProxy]:
+ """
+ The filters-argument is not yet supported
+ """
+ db_proxies = list(self.db_proxies.values())
+ if db_proxy_name and db_proxy_name in self.db_proxies.keys():
+ db_proxies = [self.db_proxies[db_proxy_name]]
+ if db_proxy_name and db_proxy_name not in self.db_proxies.keys():
+ raise DBProxyNotFoundFault(db_proxy_name)
+ return db_proxies
+
class OptionGroup:
def __init__(
diff --git a/moto/rds/responses.py b/moto/rds/responses.py
index e3dbbe1cc..29cc66509 100644
--- a/moto/rds/responses.py
+++ b/moto/rds/responses.py
@@ -882,6 +882,46 @@ class RDSResponse(BaseResponse):
db_cluster_snapshot_identifier=db_cluster_snapshot_identifier,
)
+ def describe_db_proxies(self) -> str:
+ params = self._get_params()
+ db_proxy_name = params.get("DBProxyName")
+ # filters = params.get("Filters")
+ marker = params.get("Marker")
+ db_proxies = self.backend.describe_db_proxies(
+ db_proxy_name=db_proxy_name,
+ # filters=filters,
+ )
+ template = self.response_template(DESCRIBE_DB_PROXIES_TEMPLATE)
+ rendered = template.render(dbproxies=db_proxies, marker=marker)
+ return rendered
+
+ def create_db_proxy(self) -> str:
+ params = self._get_params()
+ db_proxy_name = params["DBProxyName"]
+ engine_family = params["EngineFamily"]
+ auth = params["Auth"]
+ role_arn = params["RoleArn"]
+ vpc_subnet_ids = params["VpcSubnetIds"]
+ vpc_security_group_ids = params.get("VpcSecurityGroupIds")
+ require_tls = params.get("RequireTLS")
+ idle_client_timeout = params.get("IdleClientTimeout")
+ debug_logging = params.get("DebugLogging")
+ tags = self.unpack_list_params("Tags", "Tag")
+ db_proxy = self.backend.create_db_proxy(
+ db_proxy_name=db_proxy_name,
+ engine_family=engine_family,
+ auth=auth,
+ role_arn=role_arn,
+ vpc_subnet_ids=vpc_subnet_ids,
+ vpc_security_group_ids=vpc_security_group_ids,
+ require_tls=require_tls,
+ idle_client_timeout=idle_client_timeout,
+ debug_logging=debug_logging,
+ tags=tags,
+ )
+ template = self.response_template(CREATE_DB_PROXY_TEMPLATE)
+ return template.render(dbproxy=db_proxy)
+
CREATE_DATABASE_TEMPLATE = """
@@ -1630,3 +1670,30 @@ DESCRIBE_DB_CLUSTER_SNAPSHOT_ATTRIBUTES_TEMPLATE = """1549581b-12b7-11e3-895e-1334a
"""
+
+CREATE_DB_PROXY_TEMPLATE = """
+
+
+ {{ dbproxy.to_xml() }}
+
+
+
+ 1549581b-12b7-11e3-895e-1334aEXAMPLE
+
+"""
+
+DESCRIBE_DB_PROXIES_TEMPLATE = """
+
+
+ {% for dbproxy in dbproxies %}
+
+ {{ dbproxy.to_xml() }}
+
+ {% endfor %}
+
+
+
+ 1549581b-12b7-11e3-895e-1334a
+
+
+"""
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index db2002a96..f54213ceb 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -456,6 +456,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
"rds:db": self.rds_backend.databases,
"rds:snapshot": self.rds_backend.database_snapshots,
"rds:cluster-snapshot": self.rds_backend.cluster_snapshots,
+ "rds:db-proxy": self.rds_backend.db_proxies,
}
for resource_type, resource_source in resource_map.items():
if (
diff --git a/tests/test_rds/test_rds_proxy.py b/tests/test_rds/test_rds_proxy.py
new file mode 100644
index 000000000..314337836
--- /dev/null
+++ b/tests/test_rds/test_rds_proxy.py
@@ -0,0 +1,266 @@
+import boto3
+import pytest
+from botocore.exceptions import ClientError
+
+from moto import mock_aws
+from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+
+DEFAULT_REGION = "us-west-2"
+
+
+@mock_aws
+def test_create_db_proxy():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ ec2_client = boto3.client("ec2", region_name=DEFAULT_REGION)
+ vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ subnet_id = ec2_client.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ subnet_id_2 = ec2_client.create_subnet(CidrBlock="10.0.2.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ resp = rds_client.create_db_proxy(
+ DBProxyName="testrdsproxy",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[{"Key": "TestKey", "Value": "TestValue"}],
+ )
+ db_proxy = resp["DBProxy"]
+ assert db_proxy["DBProxyName"] == "testrdsproxy"
+ assert (
+ db_proxy["DBProxyArn"]
+ == f"arn:aws:rds:us-west-2:{ACCOUNT_ID}:db-proxy:testrdsproxy"
+ )
+ assert db_proxy["Status"] == "availible"
+ assert db_proxy["EngineFamily"] == "MYSQL"
+ assert db_proxy["VpcId"] == vpc_id
+ assert db_proxy["VpcSecurityGroupIds"] == []
+ assert db_proxy["VpcSubnetIds"] == [subnet_id, subnet_id_2]
+ assert db_proxy["Auth"] == [
+ {
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ }
+ ]
+ assert db_proxy["RoleArn"] == "TestArn"
+ assert db_proxy["RequireTLS"] is True
+ assert db_proxy["IdleClientTimeout"] == 1800
+ assert db_proxy["DebugLogging"] is False
+
+
+@mock_aws
+def test_describe_db_proxies():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ ec2_client = boto3.client("ec2", region_name=DEFAULT_REGION)
+ vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ subnet_id = ec2_client.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ subnet_id_2 = ec2_client.create_subnet(CidrBlock="10.0.2.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ rds_client.create_db_proxy(
+ DBProxyName="testrdsproxydescribe",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[
+ {"Key": "TestKey", "Value": "TestValue"},
+ {"Key": "aaa", "Value": "bbb"},
+ ],
+ )
+ response = rds_client.describe_db_proxies(DBProxyName="testrdsproxydescribe")
+ db_proxy = response["DBProxies"][0]
+ assert db_proxy["DBProxyName"] == "testrdsproxydescribe"
+ assert (
+ db_proxy["DBProxyArn"]
+ == f"arn:aws:rds:us-west-2:{ACCOUNT_ID}:db-proxy:testrdsproxydescribe"
+ )
+ assert db_proxy["Status"] == "availible"
+ assert db_proxy["EngineFamily"] == "MYSQL"
+ assert db_proxy["VpcId"] == vpc_id
+ assert db_proxy["VpcSecurityGroupIds"] == []
+ assert db_proxy["VpcSubnetIds"] == [subnet_id, subnet_id_2]
+ assert db_proxy["Auth"] == [
+ {
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ }
+ ]
+ assert db_proxy["RoleArn"] == "TestArn"
+ assert db_proxy["RequireTLS"] is True
+ assert db_proxy["IdleClientTimeout"] == 1800
+ assert db_proxy["DebugLogging"] is False
+
+
+@mock_aws
+def test_list_tags_db_proxy():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ ec2_client = boto3.client("ec2", region_name=DEFAULT_REGION)
+ vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ subnet_id = ec2_client.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ subnet_id_2 = ec2_client.create_subnet(CidrBlock="10.0.2.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ resp = rds_client.create_db_proxy(
+ DBProxyName="testrdsproxydescribe",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[
+ {"Key": "TestKey", "Value": "TestValue"},
+ {"Key": "aaa", "Value": "bbb"},
+ ],
+ )
+ arn = resp["DBProxy"]["DBProxyArn"]
+ resp = rds_client.list_tags_for_resource(ResourceName=arn)
+ assert resp["TagList"] == [
+ {"Value": "TestValue", "Key": "TestKey"},
+ {"Value": "bbb", "Key": "aaa"},
+ ]
+
+
+@mock_aws
+def test_create_db_proxy_invalid_subnet():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ ec2_client = boto3.client("ec2", region_name=DEFAULT_REGION)
+ vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ vpc_id_2 = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ subnet_id = ec2_client.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ subnet_id_2 = ec2_client.create_subnet(CidrBlock="10.0.2.0/24", VpcId=vpc_id_2)[
+ "Subnet"
+ ]["SubnetId"]
+ with pytest.raises(ClientError) as ex:
+ rds_client.create_db_proxy(
+ DBProxyName="testrdsproxy",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[{"Key": "TestKey", "Value": "TestValue"}],
+ )
+ err = ex.value.response["Error"]
+ assert err["Code"] == "InvalidSubnet"
+
+
+@mock_aws
+def test_create_db_proxy_duplicate_name():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ ec2_client = boto3.client("ec2", region_name=DEFAULT_REGION)
+ vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
+ subnet_id = ec2_client.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ subnet_id_2 = ec2_client.create_subnet(CidrBlock="10.0.2.0/24", VpcId=vpc_id)[
+ "Subnet"
+ ]["SubnetId"]
+ rds_client.create_db_proxy(
+ DBProxyName="testrdsproxy",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[{"Key": "TestKey", "Value": "TestValue"}],
+ )
+ with pytest.raises(ClientError) as ex:
+ rds_client.create_db_proxy(
+ DBProxyName="testrdsproxy",
+ EngineFamily="MYSQL",
+ Auth=[
+ {
+ "Description": "Test Description",
+ "UserName": "Test Username",
+ "AuthScheme": "SECRETS",
+ "SecretArn": "TestSecretARN",
+ "IAMAuth": "ENABLED",
+ "ClientPasswordAuthType": "MYSQL_NATIVE_PASSWORD",
+ },
+ ],
+ RoleArn="TestArn",
+ VpcSubnetIds=[subnet_id, subnet_id_2],
+ RequireTLS=True,
+ Tags=[{"Key": "TestKey", "Value": "TestValue"}],
+ )
+ err = ex.value.response["Error"]
+ assert err["Code"] == "DBProxyAlreadyExistsFault"
+ assert (
+ err["Message"]
+ == "Cannot create the DBProxy because a DBProxy with the identifier testrdsproxy already exists."
+ )
+
+
+@mock_aws
+def test_describe_db_proxies_not_found():
+ rds_client = boto3.client("rds", region_name=DEFAULT_REGION)
+ with pytest.raises(ClientError) as ex:
+ rds_client.describe_db_proxies(DBProxyName="testrdsproxydescribe")
+ err = ex.value.response["Error"]
+ assert err["Code"] == "DBProxyNotFoundFault"
+ assert (
+ err["Message"]
+ == "The specified proxy name testrdsproxydescribe doesn't correspond to a proxy owned by your Amazon Web Services account in the specified Amazon Web Services Region."
+ )