diff --git a/.travis.yml b/.travis.yml index d7835a0..c93d4e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ script: - | set -e -o pipefail; if [ "$TRAVIS_PULL_REQUEST" != "false" ] - then git diff $TRAVIS_COMMIT_RANGE !(docs) | flake8 --diff; - else git fetch -q origin master:origin/master && git diff origin/master... !(docs) | flake8 --diff + then git diff $TRAVIS_COMMIT_RANGE !(docs) | flake8 --diff --exclude tests/cloudformation/*; + else git fetch -q origin master:origin/master && git diff origin/master... !(docs) | flake8 --diff --exclude tests/cloudformation/* fi # Then run the tests diff --git a/README.rst b/README.rst index b983732..3cd165c 100644 --- a/README.rst +++ b/README.rst @@ -493,6 +493,9 @@ This section defines certificates for the AWS Certificate Manager. For verificat - - validation_domain: # (optional) The domain name the verfication email should go to. The default is the domain name. + domain_validation_options: # (optional) override validation_domain for specific domain names + - domain_name: + validation_domain: tags: : # (optional) Dictionary of keypairs to tag the resource with. @@ -504,8 +507,11 @@ For example, mycert: domain: helloworld.test.dsd.io subject_alternative_names: - - goodbye.test.dsd.io + - goodbye.test.somewhere.io validation_domain: dsd.io + domain_validation_options: + - domain_name: goodbye.test.somewhere.io + validation_domain: somewhere.io tags: site: testsite diff --git a/bootstrap_cfn/config.py b/bootstrap_cfn/config.py index 5e8dfbe..9d370cb 100644 --- a/bootstrap_cfn/config.py +++ b/bootstrap_cfn/config.py @@ -1329,10 +1329,33 @@ def _get_acm_certificate(self, certificate_name): dnv = [DomainValidationOption( DomainName=domain_name, ValidationDomain=validation_domain)] - dnv += [DomainValidationOption( - DomainName=d, - ValidationDomain=validation_domain) - for d in acm_data.get('subject_alternative_names', [])] + # Get all subject_alternative_names and set up domain validation options + # to default to the validation_domain if there is no specific entry in + # domain_validation_options + domain_validation_options = acm_data.get('domain_validation_options', []) + validated_domains = [] + for domain_validation_option in domain_validation_options: + option_domain_name = domain_validation_option.get("domain_name", None) + option_validation_domain = domain_validation_option.get("validation_domain", None) + if option_domain_name is None or option_validation_domain is None: + raise errors.CfnConfigError("domain_validation_options require domain_name and validation_domain set") + # if the domain name validation is overridden in the domain_option, + # warn + if option_domain_name == domain_name: + logging.warning("domain_validation_options overrides the " + "validation option of the main domain. Use " + "validation_domain instead.") + dnv += [DomainValidationOption( + DomainName=option_domain_name, + ValidationDomain=option_validation_domain) + ] + validated_domains.append(option_domain_name) + # If the alternative name is not explicitly set a validation domain, + # use the default + for subject_alternative_name in acm_data.get('subject_alternative_names', []): + if subject_alternative_name not in validated_domains: + dnv += [DomainValidationOption(DomainName=subject_alternative_name, + ValidationDomain=validation_domain)] certificate = Certificate( canonical_certificate_name, diff --git a/tests/cloudformation/sample-project_acm.yaml b/tests/cloudformation/sample-project_acm.yaml index 57d4aa7..f56a1e4 100644 --- a/tests/cloudformation/sample-project_acm.yaml +++ b/tests/cloudformation/sample-project_acm.yaml @@ -1,4 +1,5 @@ # noqa +--- dev: ec2: tags: @@ -35,9 +36,12 @@ dev: mycert: domain: helloworld.test.dsd.io subject_alternative_names: - - goodbye.test.dsd.io - - hello_again.test.dsd.io + - goodbye.test.somewhere.io + - hello_again.subdomain.dsd.io validation_domain: dsd.io + domain_validation_options: + - domain_name: goodbye.test.somewhere.io + validation_domain: somewhere.io tags: test_key1: test_value_1 test_key2: test_value_2 diff --git a/tests/test_acm.py b/tests/test_acm.py index a2acc06..14b0029 100755 --- a/tests/test_acm.py +++ b/tests/test_acm.py @@ -35,29 +35,34 @@ def test_acm(self): {'Key': 'test_key1', 'Value': 'test_value_1'}, {'Key': 'test_key2', 'Value': 'test_value_2'} ] - subject_alternative_names = ['goodbye.test.dsd.io', 'hello_again.test.dsd.io'] + subject_alternative_names = ['goodbye.test.somewhere.io', 'hello_again.subdomain.dsd.io'] + # The main domain should use validation_domain, + # 'goodbye.test.somewhere.io' should use the domain_validation_options + # to validate on somewhere.io. + # 'hello_again.subdomain.dsd.io' has no validation_options so should + # default to the validation_domain domain_validation_options = [ DomainValidationOption( DomainName=domain_name, ValidationDomain=validation_domain ), DomainValidationOption( - DomainName=subject_alternative_names[0], - ValidationDomain=validation_domain + DomainName='goodbye.test.somewhere.io', + ValidationDomain='somewhere.io' ), DomainValidationOption( - DomainName=subject_alternative_names[1], + DomainName='hello_again.subdomain.dsd.io', ValidationDomain=validation_domain ) ] ACMCertificate = Certificate( - certificate_name, - DomainName=domain_name, - SubjectAlternativeNames=subject_alternative_names, - DomainValidationOptions=domain_validation_options, - Tags=tags - ) + certificate_name, + DomainName=domain_name, + SubjectAlternativeNames=subject_alternative_names, + DomainValidationOptions=domain_validation_options, + Tags=tags + ) certificate_cfg = [config_parser._get_acm_certificate(certificate_name)] expected = [ACMCertificate] compare(self._resources_to_dict(expected), @@ -80,12 +85,12 @@ def test_acm_non_alphanumeric(self): ValidationDomain=domain_name ) ACMCertificate = Certificate( - parsed_certificate_name, - DomainName=domain_name, - SubjectAlternativeNames=subject_alternative_names, - DomainValidationOptions=[domain_validation_options], - Tags=tags - ) + parsed_certificate_name, + DomainName=domain_name, + SubjectAlternativeNames=subject_alternative_names, + DomainValidationOptions=[domain_validation_options], + Tags=tags + ) certificate_cfg = [config_parser._get_acm_certificate(certificate_name)] expected = [ACMCertificate] compare(self._resources_to_dict(expected),