Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort ACM cert subject alternative names and domain validation opts #8708

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions aws/resource_aws_acm_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"log"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -210,15 +211,16 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err
d.Set("domain_name", resp.Certificate.DomainName)
d.Set("arn", resp.Certificate.CertificateArn)

if err := d.Set("subject_alternative_names", cleanUpSubjectAlternativeNames(resp.Certificate)); err != nil {
sans := cleanUpSubjectAlternativeNames(resp.Certificate)
if err := d.Set("subject_alternative_names", sans); err != nil {
return resource.NonRetryableError(err)
}

domainValidationOptions, emailValidationOptions, err := convertValidationOptions(resp.Certificate)

if err != nil {
return resource.RetryableError(err)
}
sortDomainValidationOptions(d.Get("domain_name").(*string), sans, domainValidationOptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use this patch with terraform-provider-aws v2.23.0, but unfortunately it failed with an error.
I'm not an expert in golang, however it looks to me that d.Get("domain_name") returns just string, not *string.
So changing the type of domainName from *string to string worked for me.


if err := d.Set("domain_validation_options", domainValidationOptions); err != nil {
return resource.NonRetryableError(err)
Expand All @@ -244,6 +246,7 @@ func resourceAwsAcmCertificateRead(d *schema.ResourceData, meta interface{}) err
return nil
})
}

func resourceAwsAcmCertificateGuessValidationMethod(domainValidationOptions []map[string]interface{}, emailValidationOptions []string) string {
// The DescribeCertificate Response doesn't have information on what validation method was used
// so we need to guess from the validation options we see...
Expand Down Expand Up @@ -283,8 +286,8 @@ func cleanUpSubjectAlternativeNames(cert *acm.CertificateDetail) []string {
vs = append(vs, aws.StringValue(v))
}
}
sort.Strings(vs)
return vs

}

func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string]interface{}, []string, error) {
Expand Down Expand Up @@ -315,6 +318,29 @@ func convertValidationOptions(certificate *acm.CertificateDetail) ([]map[string]
return domainValidationResult, emailValidationResult, nil
}

// sortDomainValidationOptions sorts a slice of domain validation options
// to match the order they occur in in the subject alternative names.
func sortDomainValidationOptions(domainName *string, subjectAlterntaiveNames []string, domainValidationOptions []map[string]interface{}) {
sans := append([]string{*domainName}, subjectAlterntaiveNames...)

// validation method is email and domainValidationOptions is empty
if len(sans) != len(domainValidationOptions) {
return
}

// This works because requesting a certificate is either by method email or method dns,
// but not mixed and furthermore the method cannot be changed after creation.
for i, san := range sans {
for j, opt := range domainValidationOptions {
if san == opt["domain_name"].(string) {
domainValidationOptions[i], domainValidationOptions[j] =
domainValidationOptions[j], domainValidationOptions[i]
break
}
}
}
}

func resourceAwsAcmCertificateDelete(d *schema.ResourceData, meta interface{}) error {
acmconn := meta.(*AWSClient).acmconn

Expand Down
73 changes: 73 additions & 0 deletions aws/resource_aws_acm_certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,76 @@ func testAccCheckAcmCertificateDestroy(s *terraform.State) error {

return nil
}

func TestSortDomainValidationOptions(t *testing.T) {
domainName := "www.example.com"

data := []map[string][]string{
{
"domainValidationOptions": {},
"subjectAlternativeNames": {},
"expectedResult": {},
},
{
"domainValidationOptions": {"www.example.com"},
"subjectAlternativeNames": {},
"expectedResult": {"www.example.com"},
},
{
"domainValidationOptions": {"www.example.com"},
"subjectAlternativeNames": {"a.example.com"},
"expectedResult": {"www.example.com"},
},
{
"domainValidationOptions": {"www.example.com", "a.example.com", "b.example.com"},
"subjectAlternativeNames": {"a.example.com", "b.example.com"},
"expectedResult": {"www.example.com", "a.example.com", "b.example.com"},
},
{
"domainValidationOptions": {"a.example.com", "b.example.com", "www.example.com", "c.example.com"},
"subjectAlternativeNames": {"a.example.com", "b.example.com", "c.example.com"},
"expectedResult": {"www.example.com", "a.example.com", "b.example.com", "c.example.com"},
},
{
"domainValidationOptions": {"a.example.com", "b.example.com", "www.example.com", "c.example.com"},
"subjectAlternativeNames": {"c.example.com", "b.example.com", "a.example.com"},
"expectedResult": {"www.example.com", "c.example.com", "b.example.com", "a.example.com"},
},
{
"domainValidationOptions": {"y.example.com", "b.example.com", "www.example.com", "a.example.com"},
"subjectAlternativeNames": {"a.example.com", "b.example.com", "y.example.com"},
"expectedResult": {"www.example.com", "a.example.com", "b.example.com", "y.example.com"},
},
}

for _, testCase := range data {
var options []map[string]interface{}

for _, domainName := range testCase["domainValidationOptions"] {
options = append(options,
map[string]interface{}{
"domain_name": domainName,
},
)
}

subjectAlternativeNames := testCase["subjectAlternativeNames"]
sortDomainValidationOptions(&domainName, subjectAlternativeNames, options)

expected := testCase["expectedResult"]
if len(options) != len(expected) {
t.Errorf("expected same length for input %v and expected output %v\n",
options, expected)
break
}

for i := range options {
actual := options[i]["domain_name"].(string)
if actual != expected[i] {
t.Errorf("input was: %v and %v\ngot: %v\nwant: %v\n",
testCase["domainValidationOptions"], subjectAlternativeNames, options, testCase["exptectedResult"])
break
}
}
}
}