Route53: change_resource_record_sets(): Relax DELETE validation (#7153)

This commit is contained in:
Bert Blommers 2023-12-21 21:18:44 -01:00 committed by GitHub
parent 9098554903
commit dc18556449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 13 deletions

View File

@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
service: ["acm"]
service: ["acm", "route53"]
steps:
- uses: actions/checkout@v4
@ -30,4 +30,7 @@ jobs:
- name: Run tests
run: |
mkdir ~/.aws && touch ~/.aws/credentials && echo -e "[default]\naws_access_key_id = test\naws_secret_access_key = test" > ~/.aws/credentials
cd other_langs/terraform/${{ matrix.service }} && terraform init && terraform apply --auto-approve
cd other_langs/terraform/${{ matrix.service }}
terraform init
terraform apply --auto-approve
terraform apply -destroy --auto-approve

View File

@ -314,6 +314,27 @@ class ChangeList(List[Dict[str, Any]]):
item["ResourceRecordSet"]["Name"] = item["ResourceRecordSet"]["Name"].strip(".")
return super().__contains__(item)
def has_insert_or_update(self, new_rr_set: Dict[str, Any]) -> bool:
"""
Check if a CREATE or UPSERT record exists where the name and type is the same as the provided record
If the existing record has TTL/ResourceRecords, the new TTL should have the same
"""
for change in self:
if change["Action"] in ["CREATE", "UPSERT"]:
rr_set = change["ResourceRecordSet"]
if (
rr_set["Name"] == new_rr_set["Name"].strip(".")
and rr_set["Type"] == new_rr_set["Type"]
):
if "TTL" in rr_set:
if rr_set["TTL"] == new_rr_set.get("TTL") and rr_set[
"ResourceRecords"
] == new_rr_set.get("ResourceRecords"):
return True
else:
return True
return False
class FakeZone(CloudFormationModel):
def __init__(
@ -632,13 +653,8 @@ class Route53Backend(BaseBackend):
for value in change_list:
if value["Action"] == "DELETE":
# To delete a resource record set, you must specify all the same values that you specified when you created it.
corresponding_create = copy.deepcopy(value)
corresponding_create["Action"] = "CREATE"
corresponding_upsert = copy.deepcopy(value)
corresponding_upsert["Action"] = "UPSERT"
if (
corresponding_create not in the_zone.rr_changes
and corresponding_upsert not in the_zone.rr_changes
if not the_zone.rr_changes.has_insert_or_update(
value["ResourceRecordSet"]
):
msg = f"Invalid request: Expected exactly one of [AliasTarget, all of [TTL, and ResourceRecords], or TrafficPolicyInstanceId], but found none in Change with [Action=DELETE, Name={value['ResourceRecordSet']['Name']}, Type={value['ResourceRecordSet']['Type']}, SetIdentifier={value['ResourceRecordSet'].get('SetIdentifier', 'null')}]"
raise InvalidInput(msg)

View File

@ -0,0 +1,14 @@
provider "aws" {
region = "us-east-1"
s3_use_path_style = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
route53 = "http://localhost:5000"
}
access_key = "my-access-key"
secret_key = "my-secret-key"
}

View File

@ -0,0 +1,13 @@
resource "aws_route53_zone" "test" {
name = "test.co"
}
resource "aws_route53_record" "svc_healtchecked_a_record" {
zone_id = aws_route53_zone.test.id
name = "svc-healthchecked.${aws_route53_zone.test.name}"
type = "A"
ttl = 60
set_identifier = "repl-0"
multivalue_answer_routing_policy = true
records = ["172.31.0.100", "172.31.0.101"]
}

View File

@ -729,7 +729,10 @@ def test_change_resource_record_sets_crud_valid():
@mock_route53
def test_change_resource_record_sets_crud_valid_with_special_xml_chars():
@pytest.mark.parametrize("multi_value_answer", [True, False, None])
def test_change_resource_record_sets_crud_valid_with_special_xml_chars(
multi_value_answer,
):
conn = boto3.client("route53", region_name="us-east-1")
conn.create_hosted_zone(
Name="db.",
@ -757,6 +760,10 @@ def test_change_resource_record_sets_crud_valid_with_special_xml_chars():
}
],
}
if multi_value_answer is not None:
txt_record_endpoint_payload["Changes"][0]["ResourceRecordSet"][
"MultiValueAnswer"
] = multi_value_answer
conn.change_resource_record_sets(
HostedZoneId=hosted_zone_id, ChangeBatch=txt_record_endpoint_payload
)
@ -784,6 +791,10 @@ def test_change_resource_record_sets_crud_valid_with_special_xml_chars():
}
],
}
if multi_value_answer is not None:
txt_record_with_special_char_endpoint_payload["Changes"][0][
"ResourceRecordSet"
]["MultiValueAnswer"] = multi_value_answer
conn.change_resource_record_sets(
HostedZoneId=hosted_zone_id,
ChangeBatch=txt_record_with_special_char_endpoint_payload,
@ -830,7 +841,7 @@ def test_change_resource_record_set__delete_should_match_create():
"HostedZone"
]["Id"]
create_call = client.change_resource_record_sets(
client.change_resource_record_sets(
HostedZoneId=hosted_zone_id,
ChangeBatch={
"Changes": [
@ -846,8 +857,6 @@ def test_change_resource_record_set__delete_should_match_create():
]
},
)
waiter = client.get_waiter("resource_record_sets_changed")
waiter.wait(Id=create_call["ChangeInfo"]["Id"])
with pytest.raises(ClientError) as exc:
client.change_resource_record_sets(