Route53 - Error on batch changes that are too large (#4674)
This commit is contained in:
parent
7776668a5a
commit
3d105361ef
@ -72,3 +72,12 @@ class QueryLoggingConfigAlreadyExists(Route53ClientError):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
message = "A query logging configuration already exists for this hosted zone"
|
message = "A query logging configuration already exists for this hosted zone"
|
||||||
super().__init__("QueryLoggingConfigAlreadyExists", message)
|
super().__init__("QueryLoggingConfigAlreadyExists", message)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidChangeBatch(Route53ClientError):
|
||||||
|
|
||||||
|
code = 400
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
message = "Number of records limit of 1000 exceeded."
|
||||||
|
super().__init__("InvalidChangeBatch", message)
|
||||||
|
@ -6,7 +6,7 @@ from jinja2 import Template
|
|||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from moto.route53.exceptions import Route53ClientError
|
from moto.route53.exceptions import Route53ClientError, InvalidChangeBatch
|
||||||
from moto.route53.models import route53_backend
|
from moto.route53.models import route53_backend
|
||||||
|
|
||||||
XMLNS = "https://route53.amazonaws.com/doc/2013-04-01/"
|
XMLNS = "https://route53.amazonaws.com/doc/2013-04-01/"
|
||||||
@ -112,6 +112,23 @@ class Route53(BaseResponse):
|
|||||||
]["Change"]
|
]["Change"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Enforce quotas https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-changeresourcerecordsets
|
||||||
|
# - A request cannot contain more than 1,000 ResourceRecord elements. When the value of the Action element is UPSERT, each ResourceRecord element is counted twice.
|
||||||
|
effective_rr_count = 0
|
||||||
|
for value in change_list:
|
||||||
|
record_set = value["ResourceRecordSet"]
|
||||||
|
if (
|
||||||
|
"ResourceRecords" not in record_set
|
||||||
|
or not record_set["ResourceRecords"]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
resource_records = list(record_set["ResourceRecords"].values())[0]
|
||||||
|
effective_rr_count += len(resource_records)
|
||||||
|
if value["Action"] == "UPSERT":
|
||||||
|
effective_rr_count += len(resource_records)
|
||||||
|
if effective_rr_count > 1000:
|
||||||
|
raise InvalidChangeBatch
|
||||||
|
|
||||||
error_msg = route53_backend.change_resource_record_sets(zoneid, change_list)
|
error_msg = route53_backend.change_resource_record_sets(zoneid, change_list)
|
||||||
if error_msg:
|
if error_msg:
|
||||||
return 400, headers, error_msg
|
return 400, headers, error_msg
|
||||||
|
@ -1472,3 +1472,124 @@ def test_get_change():
|
|||||||
|
|
||||||
response["ChangeInfo"]["Id"].should.equal(change_id)
|
response["ChangeInfo"]["Id"].should.equal(change_id)
|
||||||
response["ChangeInfo"]["Status"].should.equal("INSYNC")
|
response["ChangeInfo"]["Status"].should.equal("INSYNC")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_route53
|
||||||
|
def test_change_resource_record_sets_records_limit():
|
||||||
|
conn = boto3.client("route53", region_name="us-east-1")
|
||||||
|
conn.create_hosted_zone(
|
||||||
|
Name="db.",
|
||||||
|
CallerReference=str(hash("foo")),
|
||||||
|
HostedZoneConfig=dict(PrivateZone=True, Comment="db"),
|
||||||
|
)
|
||||||
|
|
||||||
|
zones = conn.list_hosted_zones_by_name(DNSName="db.")
|
||||||
|
len(zones["HostedZones"]).should.equal(1)
|
||||||
|
zones["HostedZones"][0]["Name"].should.equal("db.")
|
||||||
|
hosted_zone_id = zones["HostedZones"][0]["Id"]
|
||||||
|
|
||||||
|
# Changes creating exactly 1,000 resource records.
|
||||||
|
changes = []
|
||||||
|
for ci in range(4):
|
||||||
|
resourcerecords = []
|
||||||
|
for rri in range(250):
|
||||||
|
resourcerecords.append({"Value": "127.0.0.%d" % (rri)})
|
||||||
|
changes.append(
|
||||||
|
{
|
||||||
|
"Action": "CREATE",
|
||||||
|
"ResourceRecordSet": {
|
||||||
|
"Name": "foo%d.db." % (ci),
|
||||||
|
"Type": "A",
|
||||||
|
"TTL": 10,
|
||||||
|
"ResourceRecords": resourcerecords,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
create_1000_resource_records_payload = {
|
||||||
|
"Comment": "Create four records with 250 resource records each",
|
||||||
|
"Changes": changes,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.change_resource_record_sets(
|
||||||
|
HostedZoneId=hosted_zone_id, ChangeBatch=create_1000_resource_records_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Changes creating over 1,000 resource records.
|
||||||
|
too_many_changes = create_1000_resource_records_payload["Changes"].copy()
|
||||||
|
too_many_changes.append(
|
||||||
|
{
|
||||||
|
"Action": "CREATE",
|
||||||
|
"ResourceRecordSet": {
|
||||||
|
"Name": "toomany.db.",
|
||||||
|
"Type": "A",
|
||||||
|
"TTL": 10,
|
||||||
|
"ResourceRecords": [{"Value": "127.0.0.1"}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
create_1001_resource_records_payload = {
|
||||||
|
"Comment": "Create four records with 250 resource records each, plus one more",
|
||||||
|
"Changes": too_many_changes,
|
||||||
|
}
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
conn.change_resource_record_sets(
|
||||||
|
HostedZoneId=hosted_zone_id,
|
||||||
|
ChangeBatch=create_1001_resource_records_payload,
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("InvalidChangeBatch")
|
||||||
|
|
||||||
|
# Changes upserting exactly 500 resource records.
|
||||||
|
changes = []
|
||||||
|
for ci in range(2):
|
||||||
|
resourcerecords = []
|
||||||
|
for rri in range(250):
|
||||||
|
resourcerecords.append({"Value": "127.0.0.%d" % (rri)})
|
||||||
|
changes.append(
|
||||||
|
{
|
||||||
|
"Action": "UPSERT",
|
||||||
|
"ResourceRecordSet": {
|
||||||
|
"Name": "foo%d.db." % (ci),
|
||||||
|
"Type": "A",
|
||||||
|
"TTL": 10,
|
||||||
|
"ResourceRecords": resourcerecords,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
upsert_500_resource_records_payload = {
|
||||||
|
"Comment": "Upsert two records with 250 resource records each",
|
||||||
|
"Changes": changes,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.change_resource_record_sets(
|
||||||
|
HostedZoneId=hosted_zone_id, ChangeBatch=upsert_500_resource_records_payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Changes upserting over 1,000 resource records.
|
||||||
|
too_many_changes = upsert_500_resource_records_payload["Changes"].copy()
|
||||||
|
too_many_changes.append(
|
||||||
|
{
|
||||||
|
"Action": "UPSERT",
|
||||||
|
"ResourceRecordSet": {
|
||||||
|
"Name": "toomany.db.",
|
||||||
|
"Type": "A",
|
||||||
|
"TTL": 10,
|
||||||
|
"ResourceRecords": [{"Value": "127.0.0.1"}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
upsert_501_resource_records_payload = {
|
||||||
|
"Comment": "Upsert two records with 250 resource records each, plus one more",
|
||||||
|
"Changes": too_many_changes,
|
||||||
|
}
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
conn.change_resource_record_sets(
|
||||||
|
HostedZoneId=hosted_zone_id, ChangeBatch=upsert_501_resource_records_payload
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("InvalidChangeBatch")
|
||||||
|
err["Message"].should.equal("Number of records limit of 1000 exceeded.")
|
||||||
|
Loading…
Reference in New Issue
Block a user