fixed tf suite: Route53HostedZone (#5223)

This commit is contained in:
Macwan Nevil 2022-06-14 15:44:15 +05:30 committed by GitHub
parent 1698c52740
commit 631e887b5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 169 additions and 24 deletions

View File

@ -89,6 +89,28 @@ class HostedZoneNotEmpty(Route53ClientError):
self.content_type = "text/xml"
class PublicZoneVPCAssociation(Route53ClientError):
"""Public hosted zone can't be associated."""
code = 400
def __init__(self):
message = "You're trying to associate a VPC with a public hosted zone. Amazon Route 53 doesn't support associating a VPC with a public hosted zone."
super().__init__("PublicZoneVPCAssociation", message)
self.content_type = "text/xml"
class LastVPCAssociation(Route53ClientError):
"""Last VPC can't be disassociate."""
code = 400
def __init__(self):
message = "The VPC that you're trying to disassociate from the private hosted zone is the last VPC that is associated with the hosted zone. Amazon Route 53 doesn't support disassociating the last VPC from a hosted zone."
super().__init__("LastVPCAssociation", message)
self.content_type = "text/xml"
class NoSuchQueryLoggingConfig(Route53ClientError):
"""Query log config does not exist."""

View File

@ -11,11 +11,13 @@ from jinja2 import Template
from moto.route53.exceptions import (
HostedZoneNotEmpty,
InvalidInput,
LastVPCAssociation,
NoSuchCloudWatchLogsLogGroup,
NoSuchDelegationSet,
NoSuchHealthCheck,
NoSuchHostedZone,
NoSuchQueryLoggingConfig,
PublicZoneVPCAssociation,
QueryLoggingConfigAlreadyExists,
)
from moto.core import BaseBackend, BaseModel, CloudFormationModel, get_account_id
@ -236,19 +238,14 @@ class FakeZone(CloudFormationModel):
name,
id_,
private_zone,
vpcid=None,
vpcregion=None,
comment=None,
delegation_set=None,
):
self.name = name
self.id = id_
self.vpcs = []
if comment is not None:
self.comment = comment
if vpcid is not None:
self.vpcid = vpcid
if vpcregion is not None:
self.vpcregion = vpcregion
self.private_zone = private_zone
self.rrsets = []
self.delegation_set = delegation_set
@ -287,6 +284,19 @@ class FakeZone(CloudFormationModel):
if record_set.set_identifier != set_identifier
]
def add_vpc(self, vpc_id, vpc_region):
vpc = {}
if vpc_id is not None:
vpc["vpc_id"] = vpc_id
if vpc_region is not None:
vpc["vpc_region"] = vpc_region
if vpc_id or vpc_region:
self.vpcs.append(vpc)
return vpc
def delete_vpc(self, vpc_id):
self.vpcs = [vpc for vpc in self.vpcs if vpc["vpc_id"] != vpc_id]
def get_record_sets(self, start_type, start_name):
def predicate(rrset):
rrset_name_reversed = reverse_domain_name(rrset.name)
@ -420,8 +430,6 @@ class Route53Backend(BaseBackend):
name,
new_id,
private_zone=private_zone,
vpcid=vpcid,
vpcregion=vpcregion,
comment=comment,
delegation_set=delegation_set,
)
@ -433,6 +441,7 @@ class Route53Backend(BaseBackend):
"Type": "NS",
}
new_zone.add_rrset(record_set)
new_zone.add_vpc(vpcid, vpcregion)
self.zones[new_id] = new_zone
return new_zone
@ -440,6 +449,20 @@ class Route53Backend(BaseBackend):
# check if hosted zone exists
self.get_hosted_zone(zone_id)
def associate_vpc(self, zone_id, vpcid, vpcregion):
zone = self.get_hosted_zone(zone_id)
if not zone.private_zone:
raise PublicZoneVPCAssociation()
zone.add_vpc(vpcid, vpcregion)
return zone
def disassociate_vpc(self, zone_id, vpcid):
zone = self.get_hosted_zone(zone_id)
if len(zone.vpcs) <= 1:
raise LastVPCAssociation()
zone.delete_vpc(vpcid)
return zone
def change_tags_for_resource(self, resource_id, tags):
if "Tag" in tags:
if isinstance(tags["Tag"], list):
@ -545,15 +568,16 @@ class Route53Backend(BaseBackend):
for zone in self.list_hosted_zones():
if zone.private_zone is True:
this_zone = self.get_hosted_zone(zone.id)
if this_zone.vpcid == vpc_id:
this_id = f"/hostedzone/{zone.id}"
zone_list.append(
{
"HostedZoneId": this_id,
"Name": zone.name,
"Owner": {"OwningAccount": get_account_id()},
}
)
for vpc in this_zone.vpcs:
if vpc["vpc_id"] == vpc_id:
this_id = f"/hostedzone/{zone.id}"
zone_list.append(
{
"HostedZoneId": this_id,
"Name": zone.name,
"Owner": {"OwningAccount": get_account_id()},
}
)
return zone_list
@ -581,6 +605,11 @@ class Route53Backend(BaseBackend):
raise HostedZoneNotEmpty()
return self.zones.pop(id_.replace("/hostedzone/", ""), None)
def update_hosted_zone_comment(self, id_, comment):
zone = self.get_hosted_zone(id_)
zone.comment = comment
return zone
def create_health_check(self, caller_reference, health_check_args):
health_check_id = str(uuid.uuid4())
health_check = HealthCheck(health_check_id, caller_reference, health_check_args)

View File

@ -114,6 +114,14 @@ class Route53(BaseResponse):
elif request.method == "DELETE":
route53_backend.delete_hosted_zone(zoneid)
return 200, headers, DELETE_HOSTED_ZONE_RESPONSE
elif request.method == "POST":
elements = xmltodict.parse(self.body)
comment = elements.get("UpdateHostedZoneCommentRequest", {}).get(
"Comment", None
)
zone = route53_backend.update_hosted_zone_comment(zoneid, comment)
template = Template(UPDATE_HOSTED_ZONE_COMMENT_RESPONSE)
return 200, headers, template.render(zone=zone)
def get_dnssec_response(self, request, full_url, headers):
# returns static response
@ -129,6 +137,43 @@ class Route53(BaseResponse):
route53_backend.get_dnssec(zoneid)
return 200, headers, GET_DNSSEC
def associate_vpc_response(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
parsed_url = urlparse(full_url)
zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
elements = xmltodict.parse(self.body)
comment = vpc = elements.get("AssociateVPCWithHostedZoneRequest", {}).get(
"Comment", {}
)
vpc = elements.get("AssociateVPCWithHostedZoneRequest", {}).get("VPC", {})
vpcid = vpc.get("VPCId", None)
vpcregion = vpc.get("VPCRegion", None)
route53_backend.associate_vpc(zoneid, vpcid, vpcregion)
template = Template(ASSOCIATE_VPC_RESPONSE)
return 200, headers, template.render(comment=comment)
def disassociate_vpc_response(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
parsed_url = urlparse(full_url)
zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
elements = xmltodict.parse(self.body)
comment = vpc = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get(
"Comment", {}
)
vpc = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get("VPC", {})
vpcid = vpc.get("VPCId", None)
route53_backend.disassociate_vpc(zoneid, vpcid)
template = Template(DISASSOCIATE_VPC_RESPONSE)
return 200, headers, template.render(comment)
def rrset_response(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
@ -544,10 +589,12 @@ GET_HOSTED_ZONE_RESPONSE = """<GetHostedZoneResponse xmlns="https://route53.amaz
{% endif %}
{% if zone.private_zone %}
<VPCs>
{% for vpc in zone.vpcs %}
<VPC>
<VPCId>{{zone.vpcid}}</VPCId>
<VPCRegion>{{zone.vpcregion}}</VPCRegion>
<VPCId>{{vpc.vpc_id}}</VPCId>
<VPCRegion>{{vpc.vpc_region}}</VPCRegion>
</VPC>
{% endfor %}
</VPCs>
{% endif %}
</GetHostedZoneResponse>"""
@ -758,3 +805,41 @@ GET_HEALTH_CHECK_RESPONSE = """<?xml version="1.0"?>
{{ health_check.to_xml() }}
</GetHealthCheckResponse>
"""
UPDATE_HOSTED_ZONE_COMMENT_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<UpdateHostedZoneCommentResponse>
<HostedZone>
<Config>
{% if zone.comment %}
<Comment>{{ zone.comment }}</Comment>
{% endif %}
<PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
</Config>
<Id>/hostedzone/{{ zone.id }}</Id>
<Name>{{ zone.name }}</Name>
<ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
</HostedZone>
</UpdateHostedZoneCommentResponse>
"""
ASSOCIATE_VPC_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<AssociateVPCWithHostedZoneResponse>
<ChangeInfo>
<Comment>{{ comment }}</Comment>
<Id>/change/a1b2c3d4</Id>
<Status>INSYNC</Status>
<SubmittedAt>2017-03-31T01:36:41.958Z</SubmittedAt>
</ChangeInfo>
</AssociateVPCWithHostedZoneResponse>
"""
DISASSOCIATE_VPC_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<DisassociateVPCFromHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
<ChangeInfo>
<Comment>{{ comment }}</Comment>
<Id>/change/a1b2c3d4</Id>
<Status>INSYNC</Status>
<SubmittedAt>2017-03-31T01:36:41.958Z</SubmittedAt>
</ChangeInfo>
</DisassociateVPCFromHostedZoneResponse>
"""

View File

@ -17,6 +17,8 @@ url_paths = {
r"{0}/(?P<api_version>[\d_-]+)/hostedzone/(?P<zone_id>[^/]+)$": Route53().get_or_delete_hostzone_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzone/(?P<zone_id>[^/]+)/rrset/?$": Route53().rrset_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzone/(?P<zone_id>[^/]+)/dnssec/?$": Route53().get_dnssec_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzone/(?P<zone_id>[^/]+)/associatevpc/?$": Route53().associate_vpc_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzone/(?P<zone_id>[^/]+)/disassociatevpc/?$": Route53().disassociate_vpc_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzonesbyname": Route53().list_hosted_zones_by_name_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzonesbyvpc": Route53().list_hosted_zones_by_vpc_response,
r"{0}/(?P<api_version>[\d_-]+)/hostedzonecount": Route53().get_hosted_zone_count_response,

View File

@ -163,6 +163,17 @@ route53:
- TestAccRoute53Record_longTXTrecord
- TestAccRoute53Record_doNotAllowOverwrite
- TestAccRoute53Record_allowOverwrite
- TestAccRoute53Zone_basic
- TestAccRoute53Zone_disappears
- TestAccRoute53Zone_multiple
- TestAccRoute53Zone_comment
- TestAccRoute53Zone_delegationSetID
- TestAccRoute53Zone_forceDestroy
- TestAccRoute53Zone_ForceDestroy_trailingPeriod
- TestAccRoute53Zone_tags
- TestAccRoute53Zone_VPC_single
- TestAccRoute53Zone_VPC_multiple
- TestAccRoute53Zone_VPC_updates
s3:
- TestAccS3BucketPolicy
- TestAccS3BucketPublicAccessBlock

View File

@ -506,11 +506,7 @@ def test_hosted_zone_private_zone_preserved():
hosted_zone = conn.get_hosted_zone(Id=zone_id)
hosted_zone["HostedZone"]["Config"]["PrivateZone"].should.equal(True)
hosted_zone.should.have.key("VPCs")
hosted_zone["VPCs"].should.have.length_of(1)
hosted_zone["VPCs"][0].should.have.key("VPCId")
hosted_zone["VPCs"][0].should.have.key("VPCRegion")
hosted_zone["VPCs"][0]["VPCId"].should.equal("")
hosted_zone["VPCs"][0]["VPCRegion"].should.equal("")
hosted_zone["VPCs"].should.have.length_of(0)
hosted_zones = conn.list_hosted_zones()
hosted_zones["HostedZones"].should.have.length_of(2)