Skip to content

Commit

Permalink
RDS: Proxy Target Groups
Browse files Browse the repository at this point in the history
  • Loading branch information
bblommers committed Oct 18, 2024
1 parent 1ace1db commit 3057638
Show file tree
Hide file tree
Showing 9 changed files with 689 additions and 32 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/tests_terraform_examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:3.10-slim /moto/scripts/ci_moto_server.sh &
python scripts/ci_wait_for_server.py
- name: Run tests
if: ${{ matrix.service != 'rds' }}
run: |
mkdir ~/.aws && touch ~/.aws/credentials && echo -e "[default]\naws_access_key_id = test\naws_secret_access_key = test" > ~/.aws/credentials
cd other_langs/terraform/${{ matrix.service }}
Expand All @@ -56,3 +57,11 @@ jobs:
sleep 30
terraform plan -detailed-exitcode
terraform apply -destroy --auto-approve
- name: Run tests
if: ${{ matrix.service == 'rds' }}
run: |
mkdir ~/.aws && touch ~/.aws/credentials && echo -e "[default]\naws_access_key_id = test\naws_secret_access_key = test" > ~/.aws/credentials
cd other_langs/terraform/${{ matrix.service }}
terraform init
terraform apply --auto-approve
terraform apply -destroy --auto-approve
14 changes: 7 additions & 7 deletions IMPLEMENTATION_COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6447,7 +6447,7 @@

## rds
<details>
<summary>38% implemented</summary>
<summary>42% implemented</summary>

- [ ] add_role_to_db_cluster
- [ ] add_role_to_db_instance
Expand Down Expand Up @@ -6492,7 +6492,7 @@
- [X] delete_db_instance
- [ ] delete_db_instance_automated_backup
- [X] delete_db_parameter_group
- [ ] delete_db_proxy
- [X] delete_db_proxy
- [ ] delete_db_proxy_endpoint
- [ ] delete_db_security_group
- [ ] delete_db_shard_group
Expand All @@ -6503,7 +6503,7 @@
- [ ] delete_integration
- [X] delete_option_group
- [ ] delete_tenant_database
- [ ] deregister_db_proxy_targets
- [X] deregister_db_proxy_targets
- [ ] describe_account_attributes
- [ ] describe_blue_green_deployments
- [ ] describe_certificates
Expand All @@ -6523,8 +6523,8 @@
- [ ] describe_db_parameters
- [X] describe_db_proxies
- [ ] describe_db_proxy_endpoints
- [ ] describe_db_proxy_target_groups
- [ ] describe_db_proxy_targets
- [X] describe_db_proxy_target_groups
- [X] describe_db_proxy_targets
- [ ] describe_db_recommendations
- [ ] describe_db_security_groups
- [ ] describe_db_shard_groups
Expand Down Expand Up @@ -6567,7 +6567,7 @@
- [X] modify_db_parameter_group
- [ ] modify_db_proxy
- [ ] modify_db_proxy_endpoint
- [ ] modify_db_proxy_target_group
- [X] modify_db_proxy_target_group
- [ ] modify_db_recommendation
- [ ] modify_db_shard_group
- [ ] modify_db_snapshot
Expand All @@ -6584,7 +6584,7 @@
- [ ] reboot_db_cluster
- [X] reboot_db_instance
- [ ] reboot_db_shard_group
- [ ] register_db_proxy_targets
- [X] register_db_proxy_targets
- [X] remove_from_global_cluster
- [ ] remove_role_from_db_cluster
- [ ] remove_role_from_db_instance
Expand Down
12 changes: 6 additions & 6 deletions docs/docs/services/rds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ rds
- [X] delete_db_instance
- [ ] delete_db_instance_automated_backup
- [X] delete_db_parameter_group
- [ ] delete_db_proxy
- [X] delete_db_proxy
- [ ] delete_db_proxy_endpoint
- [ ] delete_db_security_group
- [ ] delete_db_shard_group
Expand All @@ -68,7 +68,7 @@ rds
- [ ] delete_integration
- [X] delete_option_group
- [ ] delete_tenant_database
- [ ] deregister_db_proxy_targets
- [X] deregister_db_proxy_targets
- [ ] describe_account_attributes
- [ ] describe_blue_green_deployments
- [ ] describe_certificates
Expand All @@ -92,8 +92,8 @@ rds


- [ ] describe_db_proxy_endpoints
- [ ] describe_db_proxy_target_groups
- [ ] describe_db_proxy_targets
- [X] describe_db_proxy_target_groups
- [X] describe_db_proxy_targets
- [ ] describe_db_recommendations
- [ ] describe_db_security_groups
- [ ] describe_db_shard_groups
Expand Down Expand Up @@ -140,7 +140,7 @@ rds
- [X] modify_db_parameter_group
- [ ] modify_db_proxy
- [ ] modify_db_proxy_endpoint
- [ ] modify_db_proxy_target_group
- [X] modify_db_proxy_target_group
- [ ] modify_db_recommendation
- [ ] modify_db_shard_group
- [ ] modify_db_snapshot
Expand All @@ -157,7 +157,7 @@ rds
- [ ] reboot_db_cluster
- [X] reboot_db_instance
- [ ] reboot_db_shard_group
- [ ] register_db_proxy_targets
- [X] register_db_proxy_targets
- [X] remove_from_global_cluster
- [ ] remove_role_from_db_cluster
- [ ] remove_role_from_db_instance
Expand Down
192 changes: 187 additions & 5 deletions moto/rds/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import copy
import math
import os
import re
import string
Expand Down Expand Up @@ -121,6 +122,76 @@ def arn(self) -> str:
return f"arn:{self.partition}:rds:{self.region}:{self.account_id}:{self.resource_type}:{self.name}"


class ProxyTarget(RDSBaseModel):
resource_type = "proxy-target"

def __init__(
self,
backend: "RDSBackend",
resource_id: str,
endpoint: Optional[str],
type: str,
):
super().__init__(backend)
self._name = "trgasdf"
self.endpoint = endpoint
self.rds_resource_id = resource_id
self.type = type
self.role = ""

@property
def name(self) -> str:
return self._name

Check warning on line 144 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L144

Added line #L144 was not covered by tests


class ProxyTargetGroup(RDSBaseModel):
resource_type = "target-group"

def __init__(
self,
backend: "RDSBackend",
name: str,
proxy_name: str,
):
super().__init__(backend)
self._name = f"prx-tg-{random.get_random_string(length=17, lower_case=True)}"
self.group_name = name
self.proxy_name = proxy_name
self.targets: List[ProxyTarget] = []

self.max_connections = 100
self.max_idle_connections = 50
self.borrow_timeout = 120
self.session_pinning_filters: List[str] = []

self.created_date = iso_8601_datetime_with_milliseconds()
self.updated_date = iso_8601_datetime_with_milliseconds()

@property
def name(self) -> str:
return self._name

def to_xml(self) -> str:
template = Template("""<DBProxyName>{{ group.proxy_name }}</DBProxyName>
<TargetGroupName>{{ group.group_name }}</TargetGroupName>
<TargetGroupArn>{{ group.arn }}</TargetGroupArn>
<IsDefault>true</IsDefault>
<Status>available</Status>
<ConnectionPoolConfig>
<MaxConnectionsPercent>{{ group.max_connections }}</MaxConnectionsPercent>
<MaxIdleConnectionsPercent>{{ group.max_idle_connections }}</MaxIdleConnectionsPercent>
<ConnectionBorrowTimeout>{{ group.borrow_timeout }}</ConnectionBorrowTimeout>
<SessionPinningFilters>
{% for filter in group.session_pinning_filters %}
<member>{{ filter }}</member>
{% endfor %}
</SessionPinningFilters>
</ConnectionPoolConfig>
<CreatedDate>{{ group.created_date }}</CreatedDate>
<UpdatedDate>{{ group.updated_date }}</UpdatedDate>""")
return template.render(group=self)


class GlobalCluster(RDSBaseModel):
resource_type = "global-cluster"

Expand Down Expand Up @@ -729,7 +800,7 @@ def __init__(
self.instance_create_time = iso_8601_datetime_with_milliseconds()
self.publicly_accessible = kwargs.get("publicly_accessible")
if self.publicly_accessible is None:
self.publicly_accessible = True
self.publicly_accessible = False
self.copy_tags_to_snapshot = kwargs.get("copy_tags_to_snapshot")
if self.copy_tags_to_snapshot is None:
self.copy_tags_to_snapshot = False
Expand All @@ -750,6 +821,11 @@ def __init__(
].describe_db_subnet_groups(self.db_subnet_group_name)[0]
self.security_groups = kwargs.get("security_groups", [])
self.vpc_security_group_ids = kwargs.get("vpc_security_group_ids", [])
if not self.vpc_security_group_ids:
ec2_backend = ec2_backends[self.account_id][self.region]
default_vpc = ec2_backend.default_vpc
default_sg = ec2_backend.get_default_security_group(default_vpc.id)
self.vpc_security_group_ids.append(default_sg.id) # type: ignore
self.preferred_maintenance_window = kwargs.get("preferred_maintenance_window")
self.preferred_backup_window = kwargs.get("preferred_backup_window")
msg = valid_preferred_maintenance_window(
Expand Down Expand Up @@ -1550,7 +1626,7 @@ def __init__(
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.vpc_security_group_ids = vpc_security_group_ids or []
self.require_tls = require_tls
if idle_client_timeout is None:
self.idle_client_timeout = 1800
Expand All @@ -1577,6 +1653,9 @@ def __init__(
vpcs.append(subnet.vpc_id)
if subnet.vpc_id != vpcs[0]:
raise InvalidSubnet(subnet_identifier=subnet.id)
if not self.vpc_security_group_ids:
default_sg = ec2_backend.get_default_security_group(vpcs[0])
self.vpc_security_group_ids.append(default_sg.id) # type: ignore

self.vpc_id = ec2_backend.describe_subnets(subnet_ids=[self.vpc_subnet_ids[0]])[
0
Expand All @@ -1587,18 +1666,30 @@ def __init__(
)
self.endpoint = f"{self.db_proxy_name}.db-proxy-{self.url_identifier}.{self.region}.rds.amazonaws.com"

self.proxy_target_groups = {
"default": ProxyTargetGroup(
backend=self.backend, name="default", proxy_name=db_proxy_name
)
}

self.unique_id = f"prx-{random.get_random_string(17, lower_case=True)}"

@property
def name(self) -> str:
return self.db_proxy_name

@property
def arn(self) -> str:
return f"arn:{self.partition}:rds:{self.region}:{self.account_id}:{self.resource_type}:{self.unique_id}"

def to_xml(self) -> str:
template = Template(
"""
<RequireTLS>{{ dbproxy.require_tls }}</RequireTLS>
<VpcSecurityGroupIds>
{% if dbproxy.VpcSecurityGroupIds %}
{% for vpcsecuritygroupid in dbproxy.VpcSecurityGroupIds %}
<member>{{ vpcsecuritygroupid }}</member>
{% if dbproxy.vpc_security_group_ids %}
{% for sg in dbproxy.vpc_security_group_ids %}
<member>{{ sg }}</member>
{% endfor %}
{% endif %}
</VpcSecurityGroupIds>
Expand Down Expand Up @@ -2558,6 +2649,13 @@ def _find_resource(self, resource_type: str, resource_name: str) -> Any:
if resource_type == getattr(resource_class, "resource_type", ""):
if resource_name in resources: # type: ignore
return resources[resource_name] # type: ignore
# The resource_name is the last part of the ARN
# Usually that's the name - but for DBProxies, the last part of the ARN is a random identifier
# So we can't just use the dict-keys - we have to manually check the ARN
if resource_type == "db-proxy":
for resource in self.db_proxies.values():
if resource.arn.endswith(resource_name):
return resource

def list_tags_for_resource(self, arn: str) -> List[Dict[str, str]]:
if self.arn_regex.match(arn):
Expand Down Expand Up @@ -2869,6 +2967,90 @@ def describe_db_proxies(
raise DBProxyNotFoundFault(db_proxy_name)
return db_proxies

def deregister_db_proxy_targets(
self,
db_proxy_name: str,
target_group_name: str,
db_cluster_identifiers: List[str],
db_instance_identifiers: List[str],
) -> None:
db_proxy = self.db_proxies[db_proxy_name]
target_group = db_proxy.proxy_target_groups[target_group_name or "default"]
target_group.targets = [
t
for t in target_group.targets
if t.rds_resource_id in db_cluster_identifiers
]
target_group.targets = [
t
for t in target_group.targets
if t.rds_resource_id in db_instance_identifiers
]

def register_db_proxy_targets(
self,
db_proxy_name: str,
target_group_name: str,
db_cluster_identifiers: List[str],
db_instance_identifiers: List[str],
) -> List[ProxyTarget]:
db_proxy = self.db_proxies[db_proxy_name]
target_group = db_proxy.proxy_target_groups[target_group_name or "default"]
new_targets = []
for cluster_id in db_cluster_identifiers:
cluster = self.clusters[cluster_id]
target = ProxyTarget(
backend=self,
resource_id=cluster_id,
endpoint=cluster.endpoint,
type="TRACKED_CLUSTER",
)
new_targets.append(target)
for instance_id in db_instance_identifiers:
target = ProxyTarget(

Check warning on line 3010 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3010

Added line #L3010 was not covered by tests
backend=self,
resource_id=instance_id,
endpoint=None,
type="RDS_INSTANCE",
)
new_targets.append(target)

Check warning on line 3016 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3016

Added line #L3016 was not covered by tests
target_group.targets.extend(new_targets)
return new_targets

def delete_db_proxy(self, proxy_name: str) -> DBProxy:
return self.db_proxies.pop(proxy_name)

def describe_db_proxy_targets(self, proxy_name: str) -> List[ProxyTarget]:
proxy = self.db_proxies[proxy_name]
target_group = proxy.proxy_target_groups["default"]
return target_group.targets

def describe_db_proxy_target_groups(
self, proxy_name: str
) -> List[ProxyTargetGroup]:
proxy = self.db_proxies[proxy_name]
return list(proxy.proxy_target_groups.values())

def modify_db_proxy_target_group(
self, proxy_name: str, config: Dict[str, Any]
) -> ProxyTargetGroup:
proxy = self.db_proxies[proxy_name]
target_group = proxy.proxy_target_groups["default"]
if max_connections := config.get("MaxConnectionsPercent"):
target_group.max_connections = max_connections
if max_idle := config.get("MaxIdleConnectionsPercent"):
target_group.max_idle_connections = max_idle

Check warning on line 3042 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3042

Added line #L3042 was not covered by tests
else:
target_group.max_idle_connections = math.floor(
int(target_group.max_connections) / 2
)
target_group.borrow_timeout = config.get(
"ConnectionBorrowTimeout", target_group.borrow_timeout
)
if "SessionPinningFilters" in config:
target_group.session_pinning_filters = config["SessionPinningFilters"]

Check warning on line 3051 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3051

Added line #L3051 was not covered by tests
return target_group


class OptionGroup(RDSBaseModel):
resource_type = "og"
Expand Down
Loading

0 comments on commit 3057638

Please sign in to comment.