Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RDS: Proxy Target Groups #8237

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
183 changes: 178 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,71 @@
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.endpoint = endpoint
self.rds_resource_id = resource_id
self.type = type
self.role = ""


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 +795,7 @@
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 +816,11 @@
].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 +1621,7 @@
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 +1648,9 @@
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 +1661,30 @@
)
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 +2644,13 @@
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 +2962,86 @@
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 not in db_cluster_identifiers
and t.rds_resource_id not 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 3001 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3001

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

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

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3007

Added line #L3007 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 3033 in moto/rds/models.py

View check run for this annotation

Codecov / codecov/patch

moto/rds/models.py#L3033

Added line #L3033 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"]
return target_group


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