Route53: list_hosted_zones() now supports pagination (#7328)

This commit is contained in:
Bert Blommers 2024-02-09 22:17:37 +00:00 committed by GitHub
parent 2c3f735e85
commit ff9dda224f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 75 additions and 16 deletions

View File

@ -723,7 +723,11 @@ class Route53Backend(BaseBackend):
the_zone.delete_rrset(record_set)
the_zone.rr_changes.append(original_change)
@paginate(pagination_model=PAGINATION_MODEL) # type: ignore[misc]
def list_hosted_zones(self) -> List[FakeZone]:
"""
The parameters DelegationSetId and HostedZoneType are not yet implemented
"""
return list(self.zones.values())
def list_hosted_zones_by_name(
@ -733,7 +737,7 @@ class Route53Backend(BaseBackend):
dnsname = dnsnames[0]
if dnsname[-1] != ".":
dnsname += "."
zones = [zone for zone in self.list_hosted_zones() if zone.name == dnsname]
zones = [zone for zone in self.zones.values() if zone.name == dnsname]
else:
dnsname = None
# sort by names, but with domain components reversed
@ -745,8 +749,7 @@ class Route53Backend(BaseBackend):
domains = domains[-1:] + domains[:-1]
return ".".join(reversed(domains))
zones = self.list_hosted_zones()
zones = sorted(zones, key=sort_key)
zones = sorted(self.zones.values(), key=sort_key)
return dnsname, zones
def list_hosted_zones_by_vpc(self, vpc_id: str) -> List[Dict[str, Any]]:
@ -754,7 +757,7 @@ class Route53Backend(BaseBackend):
Pagination is not yet implemented
"""
zone_list = []
for zone in self.list_hosted_zones():
for zone in self.zones.values():
if zone.private_zone is True:
this_zone = self.get_hosted_zone(zone.id)
for vpc in this_zone.vpcs:
@ -776,10 +779,10 @@ class Route53Backend(BaseBackend):
return the_zone
def get_hosted_zone_count(self) -> int:
return len(self.list_hosted_zones())
return len(self.zones.values())
def get_hosted_zone_by_name(self, name: str) -> Optional[FakeZone]:
for zone in self.list_hosted_zones():
for zone in self.zones.values():
if zone.name == name:
return zone
return None
@ -875,8 +878,7 @@ class Route53Backend(BaseBackend):
) -> QueryLoggingConfig:
"""Process the create_query_logging_config request."""
# Does the hosted_zone_id exist?
response = self.list_hosted_zones()
zones = list(response) if response else []
zones = list(self.zones.values())
for zone in zones:
if zone.id == hosted_zone_id:
break
@ -940,8 +942,7 @@ class Route53Backend(BaseBackend):
"""Return a list of query logging configs."""
if hosted_zone_id:
# Does the hosted_zone_id exist?
response = self.list_hosted_zones()
zones = list(response) if response else []
zones = list(self.zones.values())
for zone in zones:
if zone.id == hosted_zone_id:
break

View File

@ -81,16 +81,27 @@ class Route53(BaseResponse):
vpcregion=vpcregion,
delegation_set_id=delegation_set_id,
)
template = Template(CREATE_HOSTED_ZONE_RESPONSE)
template = Template(CREATE_HOSTED_ZONE_RESPONSE).render(zone=new_zone)
headers = {
"Location": f"https://route53.amazonaws.com/2013-04-01/hostedzone/{new_zone.id}"
}
return 201, headers, template.render(zone=new_zone)
return 201, headers, template
elif request.method == "GET":
all_zones = self.backend.list_hosted_zones()
template = Template(LIST_HOSTED_ZONES_RESPONSE)
return 200, headers, template.render(zones=all_zones)
max_size = self.querystring.get("maxitems", [None])[0]
if max_size:
max_size = int(max_size)
marker = self.querystring.get("marker", [None])[0]
zone_page, next_marker = self.backend.list_hosted_zones(
marker=marker, max_size=max_size
)
template = Template(LIST_HOSTED_ZONES_RESPONSE).render(
zones=zone_page,
marker=marker,
next_marker=next_marker,
max_items=max_size,
)
return 200, headers, template
def list_hosted_zones_by_name_response(
self, request: Any, full_url: str, headers: Any
@ -704,7 +715,10 @@ LIST_HOSTED_ZONES_RESPONSE = """<ListHostedZonesResponse xmlns="https://route53.
</HostedZone>
{% endfor %}
</HostedZones>
<IsTruncated>false</IsTruncated>
{% if marker %}<Marker>{{ marker }}</Marker>{% endif %}
{%if next_marker %}<NextMarker>{{ next_marker }}</NextMarker>{% endif %}
{%if max_items %}<MaxItems>{{ max_items }}</MaxItems>{% endif %}
<IsTruncated>{{ 'true' if next_marker else 'false'}}</IsTruncated>
</ListHostedZonesResponse>"""
LIST_HOSTED_ZONES_BY_NAME_RESPONSE = """<ListHostedZonesByNameResponse xmlns="{{ xmlns }}">

View File

@ -2,6 +2,12 @@
from .exceptions import InvalidPaginationToken
PAGINATION_MODEL = {
"list_hosted_zones": {
"input_token": "marker",
"limit_key": "max_size",
"limit_default": 100,
"unique_attribute": "id",
},
"list_query_logging_configs": {
"input_token": "next_token",
"limit_key": "max_results",

View File

@ -602,6 +602,44 @@ def test_list_hosted_zones_by_dns_name():
assert zones["HostedZones"][3]["CallerReference"] == str(hash("bar"))
@mock_aws
def test_list_hosted_zones_pagination():
conn = boto3.client("route53", region_name="us-east-1")
for idx in range(150):
conn.create_hosted_zone(
Name=f"test{idx}.com.", CallerReference=str(hash(f"h{idx}"))
)
page1 = conn.list_hosted_zones()
assert "Marker" not in page1
assert page1["IsTruncated"] is True
assert "NextMarker" in page1
assert "MaxItems" not in page1
assert len(page1["HostedZones"]) == 100
page2 = conn.list_hosted_zones(Marker=page1["NextMarker"])
assert page2["Marker"] == page1["NextMarker"]
assert page2["IsTruncated"] is False
assert "NextMarker" not in page2
assert "MaxItems" not in page2
assert len(page2["HostedZones"]) == 50
small_page = conn.list_hosted_zones(MaxItems="75")
assert "Marker" not in small_page
assert small_page["IsTruncated"] is True
assert "NextMarker" in small_page
assert small_page["MaxItems"] == "75"
assert len(small_page["HostedZones"]) == 75
remainer = conn.list_hosted_zones(Marker=small_page["NextMarker"])
assert remainer["Marker"] == small_page["NextMarker"]
assert remainer["IsTruncated"] is False
assert "NextMarker" not in remainer
assert "MaxItems" not in remainer
assert len(remainer["HostedZones"]) == 75
@mock_aws
def test_change_resource_record_sets_crud_valid():
conn = boto3.client("route53", region_name="us-east-1")