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

tests/resource/aws_api_gateway_domain_name: Remove hardcoded environment variable handling, create public ACM certificate, improve state value checks #16139

Merged
merged 1 commit into from
Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
118 changes: 118 additions & 0 deletions aws/api_gateway_domain_name_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package aws

import (
"context"
"fmt"
"sync"
"testing"

"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

// API Gateway Edge-Optimized Domain Name can only be created with ACM Certificates in specific regions.

// testAccApigatewayEdgeDomainNameRegion is the chosen API Gateway Domain Name testing region
//
// Cached to prevent issues should multiple regions become available.
var testAccApigatewayEdgeDomainNameRegion string

// testAccProviderApigatewayEdgeDomainName is the API Gateway Domain Name provider instance
//
// This Provider can be used in testing code for API calls without requiring
// the use of saving and referencing specific ProviderFactories instances.
//
// testAccPreCheckApigatewayEdgeDomainName(t) must be called before using this provider instance.
var testAccProviderApigatewayEdgeDomainName *schema.Provider

// testAccProviderApigatewayEdgeDomainNameConfigure ensures the provider is only configured once
var testAccProviderApigatewayEdgeDomainNameConfigure sync.Once

// testAccPreCheckApigatewayEdgeDomainName verifies AWS credentials and that API Gateway Domain Name is supported
func testAccPreCheckApigatewayEdgeDomainName(t *testing.T) {
testAccPartitionHasServicePreCheck(apigateway.EndpointsID, t)

// Since we are outside the scope of the Terraform configuration we must
// call Configure() to properly initialize the provider configuration.
testAccProviderApigatewayEdgeDomainNameConfigure.Do(func() {
testAccProviderApigatewayEdgeDomainName = Provider()

region := testAccGetApigatewayEdgeDomainNameRegion()

if region == "" {
t.Skip("API Gateway Domain Name not available in this AWS Partition")
}

config := map[string]interface{}{
"region": region,
}

diags := testAccProviderApigatewayEdgeDomainName.Configure(context.Background(), terraform.NewResourceConfigRaw(config))

if diags != nil && diags.HasError() {
for _, d := range diags {
if d.Severity == diag.Error {
t.Fatalf("error configuring API Gateway Domain Name provider: %s", d.Summary)
}
}
}
})
}

// testAccApigatewayEdgeDomainNameRegionProviderConfig is the Terraform provider configuration for API Gateway Domain Name region testing
//
// Testing API Gateway Domain Name assumes no other provider configurations
// are necessary and overwrites the "aws" provider configuration.
func testAccApigatewayEdgeDomainNameRegionProviderConfig() string {
return testAccRegionalProviderConfig(testAccGetApigatewayEdgeDomainNameRegion())
}

// testAccGetApigatewayEdgeDomainNameRegion returns the API Gateway Domain Name region for testing
func testAccGetApigatewayEdgeDomainNameRegion() string {
if testAccApigatewayEdgeDomainNameRegion != "" {
return testAccApigatewayEdgeDomainNameRegion
}

// AWS Commercial: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html
// AWS GovCloud (US) - edge custom domain names not supported: https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-abp.html
// AWS China - edge custom domain names not supported: https://docs.amazonaws.cn/en_us/aws/latest/userguide/api-gateway.html
switch testAccGetPartition() {
case endpoints.AwsPartitionID:
testAccApigatewayEdgeDomainNameRegion = endpoints.UsEast1RegionID
}

return testAccApigatewayEdgeDomainNameRegion
}

// testAccCheckResourceAttrRegionalARNApigatewayEdgeDomainName ensures the Terraform state exactly matches the expected API Gateway Edge Domain Name format
func testAccCheckResourceAttrRegionalARNApigatewayEdgeDomainName(resourceName, attributeName, arnService string, domain string) resource.TestCheckFunc {
return func(s *terraform.State) error {
attributeValue := arn.ARN{
Partition: testAccGetPartition(),
Region: testAccGetApigatewayEdgeDomainNameRegion(),
Resource: fmt.Sprintf("/domainnames/%s", domain),
Service: arnService,
}.String()

return resource.TestCheckResourceAttr(resourceName, attributeName, attributeValue)(s)
}
}

// testAccCheckResourceAttrRegionalARNApigatewayRegionalDomainName ensures the Terraform state exactly matches the expected API Gateway Regional Domain Name format
func testAccCheckResourceAttrRegionalARNApigatewayRegionalDomainName(resourceName, attributeName, arnService string, domain string) resource.TestCheckFunc {
return func(s *terraform.State) error {
attributeValue := arn.ARN{
Partition: testAccGetPartition(),
Region: testAccGetRegion(),
Resource: fmt.Sprintf("/domainnames/%s", domain),
Service: arnService,
}.String()

return resource.TestCheckResourceAttr(resourceName, attributeName, attributeValue)(s)
}
}
104 changes: 72 additions & 32 deletions aws/resource_aws_api_gateway_domain_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,34 @@ import (
)

func TestAccAWSAPIGatewayDomainName_CertificateArn(t *testing.T) {
certificateArn := os.Getenv("AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_ARN")
if certificateArn == "" {
t.Skip(
"Environment variable AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_ARN is not set. " +
"This environment variable must be set to the ARN of " +
"an ISSUED ACM certificate in us-east-1 to enable this test.")
}

// This test must always run in us-east-1
// BadRequestException: Invalid certificate ARN: arn:aws:acm:us-west-2:123456789012:certificate/xxxxx. Certificate must be in 'us-east-1'.
oldvar := os.Getenv("AWS_DEFAULT_REGION")
os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
defer os.Setenv("AWS_DEFAULT_REGION", oldvar)
rootDomain := testAccAwsAcmCertificateDomainFromEnv(t)
domain := testAccAwsAcmCertificateRandomSubDomain(rootDomain)

var domainName apigateway.DomainName
acmCertificateResourceName := "aws_acm_certificate.test"
resourceName := "aws_api_gateway_domain_name.test"
rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8))

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy,
PreCheck: func() { testAccPreCheck(t); testAccPreCheckApigatewayEdgeDomainName(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayDomainNameConfig_CertificateArn(rName, certificateArn),
Config: testAccAWSAPIGatewayDomainNameConfig_CertificateArn(rootDomain, domain),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName),
testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)),
resource.TestCheckResourceAttrSet(resourceName, "cloudfront_domain_name"),
testAccCheckResourceAttrRegionalARNApigatewayEdgeDomainName(resourceName, "arn", "apigateway", domain),
resource.TestCheckResourceAttrPair(resourceName, "certificate_arn", acmCertificateResourceName, "arn"),
resource.TestMatchResourceAttr(resourceName, "cloudfront_domain_name", regexp.MustCompile(`[a-z0-9]+.cloudfront.net`)),
resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", "Z2FDTNDATAQYW2"),
resource.TestCheckResourceAttr(resourceName, "domain_name", rName),
resource.TestCheckResourceAttrPair(resourceName, "domain_name", acmCertificateResourceName, "domain_name"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
Expand Down Expand Up @@ -131,9 +126,9 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateArn(t *testing.T) {
Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(rName, key, certificate),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName),
testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)),
testAccCheckResourceAttrRegionalARNApigatewayRegionalDomainName(resourceName, "arn", "apigateway", rName),
resource.TestCheckResourceAttr(resourceName, "domain_name", rName),
resource.TestMatchResourceAttr(resourceName, "regional_domain_name", regexp.MustCompile(`.*\.execute-api\..*`)),
testAccMatchResourceAttrRegionalHostname(resourceName, "regional_domain_name", "execute-api", regexp.MustCompile(`d-[a-z0-9]+`)),
resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexp.MustCompile(`^Z`)),
),
},
Expand Down Expand Up @@ -174,14 +169,14 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) {
Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(rName, key, certificate, caCertificate),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName),
testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)),
testAccCheckResourceAttrRegionalARNApigatewayRegionalDomainName(resourceName, "arn", "apigateway", rName),
resource.TestCheckResourceAttr(resourceName, "certificate_body", certificate),
resource.TestCheckResourceAttr(resourceName, "certificate_chain", caCertificate),
resource.TestCheckResourceAttr(resourceName, "certificate_name", "tf-acc-apigateway-domain-name"),
resource.TestCheckResourceAttr(resourceName, "certificate_private_key", key),
resource.TestCheckResourceAttrSet(resourceName, "certificate_upload_date"),
resource.TestCheckResourceAttr(resourceName, "domain_name", rName),
resource.TestMatchResourceAttr(resourceName, "regional_domain_name", regexp.MustCompile(`.*\.execute-api\..*`)),
testAccMatchResourceAttrRegionalHostname(resourceName, "regional_domain_name", "execute-api", regexp.MustCompile(`d-[a-z0-9]+`)),
resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexp.MustCompile(`^Z`)),
),
},
Expand All @@ -206,7 +201,6 @@ func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) {
Config: testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(rName, key, certificate, apigateway.SecurityPolicyTls12),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName),
testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)),
resource.TestCheckResourceAttr(resourceName, "security_policy", apigateway.SecurityPolicyTls12),
),
},
Expand Down Expand Up @@ -236,7 +230,6 @@ func TestAccAWSAPIGatewayDomainName_Tags(t *testing.T) {
Config: testAccAWSAPIGatewayDomainNameConfigTags1(rName, key, certificate, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName),
testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)),
resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
),
Expand Down Expand Up @@ -348,17 +341,64 @@ func testAccCheckAWSAPIGatewayDomainNameDestroy(s *terraform.State) error {
return nil
}

func testAccAWSAPIGatewayDomainNameConfig_CertificateArn(domainName, certificateArn string) string {
return fmt.Sprintf(`
func testAccAWSAPIGatewayDomainNameConfig_CertificateArn(rootDomain string, domain string) string {
return composeConfig(
testAccApigatewayEdgeDomainNameRegionProviderConfig(),
fmt.Sprintf(`
data "aws_route53_zone" "test" {
name = %[1]q
private_zone = false
}

resource "aws_acm_certificate" "test" {
domain_name = %[2]q
validation_method = "DNS"
}

#
# for_each acceptance testing requires:
# https://github.com/hashicorp/terraform-plugin-sdk/issues/536
#
# resource "aws_route53_record" "test" {
# for_each = {
# for dvo in aws_acm_certificate.test.domain_validation_options: dvo.domain_name => {
# name = dvo.resource_record_name
# record = dvo.resource_record_value
# type = dvo.resource_record_type
# }
# }

# allow_overwrite = true
# name = each.value.name
# records = [each.value.record]
# ttl = 60
# type = each.value.type
# zone_id = data.aws_route53_zone.test.zone_id
# }

resource "aws_route53_record" "test" {
allow_overwrite = true
name = tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_name
records = [tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_value]
ttl = 60
type = tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.test.zone_id
}

resource "aws_acm_certificate_validation" "test" {
certificate_arn = aws_acm_certificate.test.arn
validation_record_fqdns = [aws_route53_record.test.fqdn]
}
Comment on lines +348 to +391
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI, in #15258 I have pulled this into a separate function as I will use it in a couple of places: 4a7197b#diff-de07b61dfd6679fbda71d2c219efc2356d9a0757a6366304a5361ee09f86803bR395-R441.
If this PR is merged first I'll refactor testAccAWSAPIGatewayDomainNameConfig_CertificateArn to use this new function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay cool -- feel free to add it to the contributing guide and create a followup technical debt issue. 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Done: #16171.


resource "aws_api_gateway_domain_name" "test" {
domain_name = "%s"
certificate_arn = "%s"
domain_name = aws_acm_certificate.test.domain_name
certificate_arn = aws_acm_certificate_validation.test.certificate_arn

endpoint_configuration {
types = ["EDGE"]
}
}
`, domainName, certificateArn)
`, rootDomain, domain))
}

func testAccAWSAPIGatewayDomainNameConfig_CertificateName(domainName, key, certificate, chainCertificate string) string {
Expand Down
1 change: 0 additions & 1 deletion docs/MAINTAINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,6 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi
| `AWS_ALTERNATE_SECRET_ACCESS_KEY` | AWS secret access key with access to a secondary AWS account for tests requiring multiple accounts. Requires `AWS_ALTERNATE_ACCESS_KEY_ID`. Conflicts with `AWS_ALTERNATE_PROFILE`. |
| `AWS_ALTERNATE_PROFILE` | AWS profile with access to a secondary AWS account for tests requiring multiple accounts. Conflicts with `AWS_ALTERNATE_ACCESS_KEY_ID` and `AWS_ALTERNATE_SECRET_ACCESS_KEY`. |
| `AWS_ALTERNATE_REGION` | Secondary AWS region for tests requiring multiple regions. Defaults to `us-east-1`. |
| `AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_ARN` | Amazon Resource Name of ACM Certificate in `us-east-1` for API Gateway Domain Name testing. |
| `AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_BODY` | Certificate body of publicly trusted certificate for API Gateway Domain Name testing. |
| `AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_CHAIN` | Certificate chain of publicly trusted certificate for API Gateway Domain Name testing. |
| `AWS_API_GATEWAY_DOMAIN_NAME_CERTIFICATE_PRIVATE_KEY` | Private key of publicly trusted certificate for API Gateway Domain Name testing. |
Expand Down