From 13cf370d07f5317969f5826b3a63bcdd267514da Mon Sep 17 00:00:00 2001 From: stack72 Date: Fri, 23 Sep 2016 12:46:27 +0100 Subject: [PATCH] provider/aws: Add support for tags to aws_cloudfront_distribution Fixes #8959 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSCloudFrontDistribution_S3OriginWithTags' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/23 16:30:31 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSCloudFrontDistribution_S3OriginWithTags -timeout 120m === RUN TestAccAWSCloudFrontDistribution_S3OriginWithTags --- PASS: TestAccAWSCloudFrontDistribution_S3OriginWithTags (1234.66s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1234.680s ``` --- ...nt_distribution_configuration_structure.go | 1 + .../resource_aws_cloudfront_distribution.go | 35 ++++- ...source_aws_cloudfront_distribution_test.go | 142 ++++++++++++++++++ builtin/providers/aws/tagsCloudFront.go | 98 ++++++++++++ 4 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 builtin/providers/aws/tagsCloudFront.go diff --git a/builtin/providers/aws/cloudfront_distribution_configuration_structure.go b/builtin/providers/aws/cloudfront_distribution_configuration_structure.go index 31fcab71f..672c01d03 100644 --- a/builtin/providers/aws/cloudfront_distribution_configuration_structure.go +++ b/builtin/providers/aws/cloudfront_distribution_configuration_structure.go @@ -87,6 +87,7 @@ func expandDistributionConfig(d *schema.ResourceData) *cloudfront.DistributionCo } else { distributionConfig.WebACLId = aws.String("") } + return distributionConfig } diff --git a/builtin/providers/aws/resource_aws_cloudfront_distribution.go b/builtin/providers/aws/resource_aws_cloudfront_distribution.go index f94c0de8d..6ad3077b3 100644 --- a/builtin/providers/aws/resource_aws_cloudfront_distribution.go +++ b/builtin/providers/aws/resource_aws_cloudfront_distribution.go @@ -22,6 +22,10 @@ func resourceAwsCloudFrontDistribution() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "aliases": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -485,17 +489,23 @@ func resourceAwsCloudFrontDistribution() *schema.Resource { Optional: true, Default: false, }, + + "tags": tagsSchema(), }, } } func resourceAwsCloudFrontDistributionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudfrontconn - params := &cloudfront.CreateDistributionInput{ - DistributionConfig: expandDistributionConfig(d), + + params := &cloudfront.CreateDistributionWithTagsInput{ + DistributionConfigWithTags: &cloudfront.DistributionConfigWithTags{ + DistributionConfig: expandDistributionConfig(d), + Tags: tagsFromMapCloudFront(d.Get("tags").(map[string]interface{})), + }, } - resp, err := conn.CreateDistribution(params) + resp, err := conn.CreateDistributionWithTags(params) if err != nil { return err } @@ -530,6 +540,21 @@ func resourceAwsCloudFrontDistributionRead(d *schema.ResourceData, meta interfac d.Set("last_modified_time", aws.String(resp.Distribution.LastModifiedTime.String())) d.Set("in_progress_validation_batches", resp.Distribution.InProgressInvalidationBatches) d.Set("etag", resp.ETag) + d.Set("arn", resp.Distribution.ARN) + + cloudFrontArn := resp.Distribution.ARN + tagResp, tagErr := conn.ListTagsForResource(&cloudfront.ListTagsForResourceInput{ + Resource: cloudFrontArn, + }) + + if tagErr != nil { + log.Printf("[DEBUG] Error retrieving tags for ARN: %s", cloudFrontArn) + } + + if tagResp != nil { + d.Set("tags", tagsToMapCloudFront(tagResp.Tags)) + } + return nil } @@ -545,6 +570,10 @@ func resourceAwsCloudFrontDistributionUpdate(d *schema.ResourceData, meta interf return err } + if err := setTagsCloudFront(conn, d, d.Get("arn").(string)); err != nil { + return err + } + return resourceAwsCloudFrontDistributionRead(d, meta) } diff --git a/builtin/providers/aws/resource_aws_cloudfront_distribution_test.go b/builtin/providers/aws/resource_aws_cloudfront_distribution_test.go index b477c53b2..88e2b3ba5 100644 --- a/builtin/providers/aws/resource_aws_cloudfront_distribution_test.go +++ b/builtin/providers/aws/resource_aws_cloudfront_distribution_test.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -41,6 +42,46 @@ func TestAccAWSCloudFrontDistribution_S3Origin(t *testing.T) { }) } +func TestAccAWSCloudFrontDistribution_S3OriginWithTags(t *testing.T) { + ri := acctest.RandInt() + preConfig := fmt.Sprintf(testAccAWSCloudFrontDistributionS3ConfigWithTags, ri, testAccAWSCloudFrontDistributionRetainConfig()) + postConfig := fmt.Sprintf(testAccAWSCloudFrontDistributionS3ConfigWithTagsUpdated, ri, testAccAWSCloudFrontDistributionRetainConfig()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudFrontDistributionDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontDistributionExistence( + "aws_cloudfront_distribution.s3_distribution", + ), + resource.TestCheckResourceAttr( + "aws_cloudfront_distribution.s3_distribution", "tags.%", "2"), + resource.TestCheckResourceAttr( + "aws_cloudfront_distribution.s3_distribution", "tags.environment", "production"), + resource.TestCheckResourceAttr( + "aws_cloudfront_distribution.s3_distribution", "tags.account", "main"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudFrontDistributionExistence( + "aws_cloudfront_distribution.s3_distribution", + ), + resource.TestCheckResourceAttr( + "aws_cloudfront_distribution.s3_distribution", "tags.%", "1"), + resource.TestCheckResourceAttr( + "aws_cloudfront_distribution.s3_distribution", "tags.environment", "dev"), + ), + }, + }, + }) +} + // TestAccAWSCloudFrontDistribution_customOriginruns an // aws_cloudfront_distribution acceptance test with a single custom origin. // @@ -262,6 +303,107 @@ resource "aws_cloudfront_distribution" "s3_distribution" { } `, rand.New(rand.NewSource(time.Now().UnixNano())).Int(), testAccAWSCloudFrontDistributionRetainConfig()) +var testAccAWSCloudFrontDistributionS3ConfigWithTags = ` +variable rand_id { + default = %d +} + +resource "aws_s3_bucket" "s3_bucket" { + bucket = "mybucket.${var.rand_id}.s3.amazonaws.com" + acl = "public-read" +} + +resource "aws_cloudfront_distribution" "s3_distribution" { + origin { + domain_name = "${aws_s3_bucket.s3_bucket.id}" + origin_id = "myS3Origin" + } + enabled = true + default_root_object = "index.html" + aliases = [ "mysite.${var.rand_id}.example.com", "yoursite.${var.rand_id}.example.com" ] + default_cache_behavior { + allowed_methods = [ "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT" ] + cached_methods = [ "GET", "HEAD" ] + target_origin_id = "myS3Origin" + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + viewer_protocol_policy = "allow-all" + min_ttl = 0 + default_ttl = 3600 + max_ttl = 86400 + } + price_class = "PriceClass_200" + restrictions { + geo_restriction { + restriction_type = "whitelist" + locations = [ "US", "CA", "GB", "DE" ] + } + } + viewer_certificate { + cloudfront_default_certificate = true + } + tags { + environment = "production" + account = "main" + } + %s +} +` + +var testAccAWSCloudFrontDistributionS3ConfigWithTagsUpdated = ` +variable rand_id { + default = %d +} + +resource "aws_s3_bucket" "s3_bucket" { + bucket = "mybucket.${var.rand_id}.s3.amazonaws.com" + acl = "public-read" +} + +resource "aws_cloudfront_distribution" "s3_distribution" { + origin { + domain_name = "${aws_s3_bucket.s3_bucket.id}" + origin_id = "myS3Origin" + } + enabled = true + default_root_object = "index.html" + aliases = [ "mysite.${var.rand_id}.example.com", "yoursite.${var.rand_id}.example.com" ] + default_cache_behavior { + allowed_methods = [ "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT" ] + cached_methods = [ "GET", "HEAD" ] + target_origin_id = "myS3Origin" + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + viewer_protocol_policy = "allow-all" + min_ttl = 0 + default_ttl = 3600 + max_ttl = 86400 + } + price_class = "PriceClass_200" + restrictions { + geo_restriction { + restriction_type = "whitelist" + locations = [ "US", "CA", "GB", "DE" ] + } + } + viewer_certificate { + cloudfront_default_certificate = true + } + tags { + environment = "dev" + } + %s +} +` + var testAccAWSCloudFrontDistributionCustomConfig = fmt.Sprintf(` variable rand_id { default = %d diff --git a/builtin/providers/aws/tagsCloudFront.go b/builtin/providers/aws/tagsCloudFront.go new file mode 100644 index 000000000..d2b60c73c --- /dev/null +++ b/builtin/providers/aws/tagsCloudFront.go @@ -0,0 +1,98 @@ +package aws + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/hashicorp/terraform/helper/schema" +) + +func setTagsCloudFront(conn *cloudfront.CloudFront, d *schema.ResourceData, arn string) error { + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffTagsCloudFront(tagsFromMapCloudFront(o), tagsFromMapCloudFront(n)) + + if len(remove) > 0 { + log.Printf("[DEBUG] Removing tags: %s", remove) + k := make([]*string, 0, len(remove)) + for _, t := range remove { + k = append(k, t.Key) + } + + _, err := conn.UntagResource(&cloudfront.UntagResourceInput{ + Resource: aws.String(arn), + TagKeys: &cloudfront.TagKeys{ + Items: k, + }, + }) + if err != nil { + return err + } + } + + if len(create) > 0 { + log.Printf("[DEBUG] Creating tags: %s", create) + _, err := conn.TagResource(&cloudfront.TagResourceInput{ + Resource: aws.String(arn), + Tags: &cloudfront.Tags{ + Items: create, + }, + }) + if err != nil { + return err + } + } + + } + + return nil +} +func diffTagsCloudFront(oldTags, newTags *cloudfront.Tags) ([]*cloudfront.Tag, []*cloudfront.Tag) { + // First, we're creating everything we have + create := make(map[string]interface{}) + for _, t := range newTags.Items { + create[*t.Key] = *t.Value + } + + // Build the list of what to remove + var remove []*cloudfront.Tag + for _, t := range oldTags.Items { + old, ok := create[*t.Key] + if !ok || old != *t.Value { + // Delete it! + remove = append(remove, t) + } + } + + createTags := tagsFromMapCloudFront(create) + return createTags.Items, remove +} + +func tagsFromMapCloudFront(m map[string]interface{}) *cloudfront.Tags { + result := make([]*cloudfront.Tag, 0, len(m)) + for k, v := range m { + result = append(result, &cloudfront.Tag{ + Key: aws.String(k), + Value: aws.String(v.(string)), + }) + } + + tags := &cloudfront.Tags{ + Items: result, + } + + return tags +} + +func tagsToMapCloudFront(ts *cloudfront.Tags) map[string]string { + result := make(map[string]string) + + for _, t := range ts.Items { + result[*t.Key] = *t.Value + } + + return result +}