Skip to content

Commit

Permalink
Support Route53 create_hosted_zone raising ClientError ConflictingDom…
Browse files Browse the repository at this point in the history
…ainExists (#7249)
  • Loading branch information
codeocelot authored Jan 27, 2024
1 parent 9153f7b commit 24b94fc
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 1 deletion.
15 changes: 14 additions & 1 deletion moto/route53/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Exceptions raised by the Route53 service."""
from typing import Any
from typing import Any, Optional

from moto.core.exceptions import RESTError

Expand All @@ -12,6 +12,19 @@ def __init__(self, *args: Any, **kwargs: Any):
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):
"""Malformed ARN for the CloudWatch log group."""

Expand Down
21 changes: 21 additions & 0 deletions moto/route53/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from moto.core import BackendDict, BaseBackend, BaseModel, CloudFormationModel
from moto.moto_api._internal import mock_random as random
from moto.route53.exceptions import (
ConflictingDomainExists,
DnsNameInvalidForZone,
HostedZoneNotEmpty,
InvalidActionValue,
Expand Down Expand Up @@ -532,6 +533,24 @@ def __init__(self, region_name: str, account_id: str):
self.query_logging_configs: Dict[str, QueryLoggingConfig] = {}
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(
self,
name: str,
Expand All @@ -542,6 +561,8 @@ def create_hosted_zone(
comment: Optional[str] = None,
delegation_set_id: Optional[str] = None,
) -> FakeZone:
if self._has_prev_conflicting_domain(name, delegation_set_id):
raise ConflictingDomainExists(name, delegation_set_id)
new_id = create_route53_zone_id()
caller_reference = caller_reference or create_route53_caller_reference()
delegation_set = self.create_reusable_delegation_set(
Expand Down
51 changes: 51 additions & 0 deletions tests/test_route53/test_route53.py
Original file line number Diff line number Diff line change
Expand Up @@ -1516,3 +1516,54 @@ def test_get_dns_sec():
)["HostedZone"]["Id"]
dns_sec = client.get_dnssec(HostedZoneId=hosted_zone_id)
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",
)

0 comments on commit 24b94fc

Please sign in to comment.