Support Route53 create_hosted_zone raising ClientError ConflictingDomainExists (#7249)

This commit is contained in:
Joey Gracey 2024-01-27 04:29:00 -08:00 committed by GitHub
parent 9153f7bf73
commit 24b94fc192
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 86 additions and 1 deletions

View File

@ -1,5 +1,5 @@
"""Exceptions raised by the Route53 service.""" """Exceptions raised by the Route53 service."""
from typing import Any from typing import Any, Optional
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
@ -12,6 +12,19 @@ class Route53ClientError(RESTError):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
class ConflictingDomainExists(Route53ClientError):
"""Domain already exists."""
code = 400
def __init__(self, domain_name: str, delegation_set_id: Optional[str]) -> None:
message = (
f"Cannot create hosted zone with DelegationSetId DelegationSetId:{delegation_set_id} as the DNSName"
f"{domain_name} conflicts with existing ones sharing the delegation set"
)
super().__init__("ConflictingDomainExists", message)
class InvalidInput(Route53ClientError): class InvalidInput(Route53ClientError):
"""Malformed ARN for the CloudWatch log group.""" """Malformed ARN for the CloudWatch log group."""

View File

@ -12,6 +12,7 @@ from jinja2 import Template
from moto.core import BackendDict, BaseBackend, BaseModel, CloudFormationModel from moto.core import BackendDict, BaseBackend, BaseModel, CloudFormationModel
from moto.moto_api._internal import mock_random as random from moto.moto_api._internal import mock_random as random
from moto.route53.exceptions import ( from moto.route53.exceptions import (
ConflictingDomainExists,
DnsNameInvalidForZone, DnsNameInvalidForZone,
HostedZoneNotEmpty, HostedZoneNotEmpty,
InvalidActionValue, InvalidActionValue,
@ -532,6 +533,24 @@ class Route53Backend(BaseBackend):
self.query_logging_configs: Dict[str, QueryLoggingConfig] = {} self.query_logging_configs: Dict[str, QueryLoggingConfig] = {}
self.delegation_sets: Dict[str, DelegationSet] = dict() self.delegation_sets: Dict[str, DelegationSet] = dict()
def _has_prev_conflicting_domain(
self, name: str, delegation_set_id: Optional[str]
) -> bool:
"""Check if a conflicting domain exists in the backend"""
if not delegation_set_id:
return False
for zone in self.zones.values():
if not zone.delegation_set or zone.delegation_set.id != delegation_set_id:
# Delegation sets don't match, so can't possibly conflict
continue
if (
zone.name == name
or zone.name.endswith(f".{name}")
or name.endswith(f".{zone.name}")
):
return True
return False
def create_hosted_zone( def create_hosted_zone(
self, self,
name: str, name: str,
@ -542,6 +561,8 @@ class Route53Backend(BaseBackend):
comment: Optional[str] = None, comment: Optional[str] = None,
delegation_set_id: Optional[str] = None, delegation_set_id: Optional[str] = None,
) -> FakeZone: ) -> FakeZone:
if self._has_prev_conflicting_domain(name, delegation_set_id):
raise ConflictingDomainExists(name, delegation_set_id)
new_id = create_route53_zone_id() new_id = create_route53_zone_id()
caller_reference = caller_reference or create_route53_caller_reference() caller_reference = caller_reference or create_route53_caller_reference()
delegation_set = self.create_reusable_delegation_set( delegation_set = self.create_reusable_delegation_set(

View File

@ -1516,3 +1516,54 @@ def test_get_dns_sec():
)["HostedZone"]["Id"] )["HostedZone"]["Id"]
dns_sec = client.get_dnssec(HostedZoneId=hosted_zone_id) dns_sec = client.get_dnssec(HostedZoneId=hosted_zone_id)
assert dns_sec["Status"] == {"ServeSignature": "NOT_SIGNING"} assert dns_sec["Status"] == {"ServeSignature": "NOT_SIGNING"}
@mock_route53
@pytest.mark.parametrize(
"domain1,domain2",
(
["a.com", "a.com"],
["a.b.com", "b.com"],
["b.com", "a.b.com"],
["a.b.com", "a.b.com"],
),
)
def test_conflicting_domain_exists(domain1, domain2):
delegation_set_id = "N10015061S366L6NMTRKQ"
conn = boto3.client("route53", region_name="us-east-1")
conn.create_hosted_zone(
Name=domain1,
CallerReference=str(hash("foo")),
DelegationSetId=delegation_set_id,
)
with pytest.raises(ClientError) as exc_info:
conn.create_hosted_zone(
Name=domain2,
CallerReference=str(hash("bar")),
DelegationSetId=delegation_set_id,
)
assert exc_info.value.response.get("Error").get("Code") == "ConflictingDomainExists"
for string in [delegation_set_id, domain2]:
assert string in exc_info.value.response.get("Error").get("Message")
# Now test that these domains can be created with different delegation set ids
conn.create_hosted_zone(
Name=domain1,
CallerReference=str(hash("foo")),
)
conn.create_hosted_zone(
Name=domain2,
CallerReference=str(hash("bar")),
)
# And, finally, test that these domains can be created with different named delegation sets
conn.create_hosted_zone(
Name=domain1,
CallerReference=str(hash("foo")),
DelegationSetId="1",
)
conn.create_hosted_zone(
Name=domain2,
CallerReference=str(hash("bar")),
DelegationSetId="2",
)