From 5de7d4df3865efa86f67781cb0cf3743acc07103 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Fri, 19 Jan 2024 23:11:19 +0000 Subject: [PATCH] ACM: DomainValidationOptions should be the same for validated certificates (#7233) --- .../workflows/tests_terraform_examples.yml | 5 +++ moto/acm/models.py | 32 +++++++++---------- moto/route53/models.py | 1 + moto/route53/responses.py | 3 ++ tests/test_acm/test_acm.py | 18 +++++++++-- tests/test_route53/test_route53.py | 1 + 6 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.github/workflows/tests_terraform_examples.yml b/.github/workflows/tests_terraform_examples.yml index b1a5f7054..12790048e 100644 --- a/.github/workflows/tests_terraform_examples.yml +++ b/.github/workflows/tests_terraform_examples.yml @@ -33,4 +33,9 @@ jobs: cd other_langs/terraform/${{ matrix.service }} terraform init terraform apply --auto-approve + echo "Verify nothing changes when ACM certificates are validated" + sleep 30 + terraform plan -detailed-exitcode + sleep 30 + terraform plan -detailed-exitcode terraform apply -destroy --auto-approve diff --git a/moto/acm/models.py b/moto/acm/models.py index 8d7bcf4c8..95435983c 100644 --- a/moto/acm/models.py +++ b/moto/acm/models.py @@ -343,24 +343,22 @@ class CertBundle(BaseModel): domain_names = set(sans + [self.common_name]) validation_options = [] - if self.status == "PENDING_VALIDATION": - for san in domain_names: - resource_record = { - "Name": f"_d930b28be6c5927595552b219965053e.{san}.", - "Type": "CNAME", - "Value": "_c9edd76ee4a0e2a74388032f3861cc50.ykybfrwcxw.acm-validations.aws.", + for san in domain_names: + resource_record = { + "Name": f"_d930b28be6c5927595552b219965053e.{san}.", + "Type": "CNAME", + "Value": "_c9edd76ee4a0e2a74388032f3861cc50.ykybfrwcxw.acm-validations.aws.", + } + validation_options.append( + { + "DomainName": san, + "ValidationDomain": san, + "ValidationStatus": self.status, + "ValidationMethod": "DNS", + "ResourceRecord": resource_record, } - validation_options.append( - { - "DomainName": san, - "ValidationDomain": san, - "ValidationStatus": self.status, - "ValidationMethod": "DNS", - "ResourceRecord": resource_record, - } - ) - else: - validation_options = [{"DomainName": name} for name in domain_names] + ) + result["Certificate"]["DomainValidationOptions"] = validation_options if self.type == "IMPORTED": diff --git a/moto/route53/models.py b/moto/route53/models.py index 409d992ff..ccfd5dc4d 100644 --- a/moto/route53/models.py +++ b/moto/route53/models.py @@ -208,6 +208,7 @@ class RecordSet(CloudFormationModel): self.alias_target = kwargs.get("AliasTarget", []) self.failover = kwargs.get("Failover", []) self.geo_location = kwargs.get("GeoLocation", []) + self.multi_value = kwargs.get("MultiValueAnswer") @staticmethod def cloudformation_name_type() -> str: diff --git a/moto/route53/responses.py b/moto/route53/responses.py index 7227f85f5..6350c71bc 100644 --- a/moto/route53/responses.py +++ b/moto/route53/responses.py @@ -562,6 +562,9 @@ LIST_RRSET_RESPONSE = """ {% if record.failover %} {{ record.failover }} {% endif %} + {% if record.multi_value %} + {{ record.multi_value }} + {% endif %} {% if record.geo_location %} {% for geo_key in ['ContinentCode','CountryCode','SubdivisionCode'] %} diff --git a/tests/test_acm/test_acm.py b/tests/test_acm/test_acm.py index d2943d801..c6704c5ed 100644 --- a/tests/test_acm/test_acm.py +++ b/tests/test_acm/test_acm.py @@ -1,5 +1,6 @@ import os import uuid +from time import sleep from unittest import SkipTest, mock import boto3 @@ -180,7 +181,6 @@ def test_describe_certificate(): assert len(resp["Certificate"]["DomainValidationOptions"]) == 1 validation_option = resp["Certificate"]["DomainValidationOptions"][0] assert validation_option["DomainName"] == SERVER_COMMON_NAME - assert "ValidationDomain" not in validation_option @mock_acm @@ -388,7 +388,10 @@ def test_request_certificate(): @mock_acm +@mock.patch("moto.settings.ACM_VALIDATION_WAIT", 1) def test_request_certificate_with_optional_arguments(): + if not settings.TEST_DECORATOR_MODE: + raise SkipTest("Can only change setting in DecoratorMode") client = boto3.client("acm", region_name="eu-central-1") token = str(uuid.uuid4()) @@ -406,12 +409,21 @@ def test_request_certificate_with_optional_arguments(): arn_1 = resp["CertificateArn"] cert = client.describe_certificate(CertificateArn=arn_1)["Certificate"] + assert cert["Status"] == "PENDING_VALIDATION" assert len(cert["SubjectAlternativeNames"]) == 3 - assert len(cert["DomainValidationOptions"]) == 3 - assert {option["DomainName"] for option in cert["DomainValidationOptions"]} == set( + validation_options = cert["DomainValidationOptions"].copy() + assert len(validation_options) == 3 + assert {option["DomainName"] for option in validation_options} == set( cert["SubjectAlternativeNames"] ) + # Verify SAN's are still the same, even after the Certificate is validated + sleep(2) + for opt in validation_options: + opt["ValidationStatus"] = "ISSUED" + cert = client.describe_certificate(CertificateArn=arn_1)["Certificate"] + assert cert["DomainValidationOptions"] == validation_options + resp = client.list_tags_for_certificate(CertificateArn=arn_1) tags = {item["Key"]: item.get("Value", "__NONE__") for item in resp["Tags"]} assert len(tags) == 2 diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py index 5838cc5a1..149e34958 100644 --- a/tests/test_route53/test_route53.py +++ b/tests/test_route53/test_route53.py @@ -809,6 +809,7 @@ def test_change_resource_record_sets_crud_valid_with_special_xml_chars( assert cname_record_detail["ResourceRecords"] == [ {"Value": "SomeInitialValue&NewValue"} ] + assert cname_record_detail.get("MultiValueAnswer") == multi_value_answer # Delete record. delete_payload = {