From 4e08c8ac2fb56f41de27cfd76ff318884f3ef576 Mon Sep 17 00:00:00 2001 From: Christopher Fulara Date: Wed, 2 Jun 2021 18:16:12 -0400 Subject: [PATCH 1/4] Set etag to development function's etag --- aws/resource_aws_cloudfront_function.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_cloudfront_function.go b/aws/resource_aws_cloudfront_function.go index 49ccf0e99e0..8b8d6ca3e66 100644 --- a/aws/resource_aws_cloudfront_function.go +++ b/aws/resource_aws_cloudfront_function.go @@ -133,9 +133,20 @@ func resourceAwsCloudFrontFunctionRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error describing CloudFront Function (%s/%s): %w", d.Id(), stage, err) } + if d.Get("publish").(bool) { + describeDevelopmentFunctionOutput, err2 := finder.FunctionByNameAndStage(conn, d.Id(), cloudfront.FunctionStageDevelopment) + + if err2 != nil { + return fmt.Errorf("error describing CloudFront Function (%s/%s): %w", d.Id(), cloudfront.FunctionStageDevelopment, err2) + } + + d.Set("etag", describeDevelopmentFunctionOutput.ETag) + } else { + d.Set("etag", describeFunctionOutput.ETag) + } + d.Set("arn", describeFunctionOutput.FunctionSummary.FunctionMetadata.FunctionARN) d.Set("comment", describeFunctionOutput.FunctionSummary.FunctionConfig.Comment) - d.Set("etag", describeFunctionOutput.ETag) d.Set("name", describeFunctionOutput.FunctionSummary.Name) d.Set("runtime", describeFunctionOutput.FunctionSummary.FunctionConfig.Runtime) d.Set("status", describeFunctionOutput.FunctionSummary.Status) From 4e81dcb5cb666b83279daf4cd53592c31f84a6a0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 8 Nov 2021 10:44:24 -0500 Subject: [PATCH 2/4] Add 'TestAccCloudFrontFunction_UpdateCodeAndPublish'. --- internal/service/cloudfront/function_test.go | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/internal/service/cloudfront/function_test.go b/internal/service/cloudfront/function_test.go index d280a393ff6..44325b9a0c0 100644 --- a/internal/service/cloudfront/function_test.go +++ b/internal/service/cloudfront/function_test.go @@ -201,6 +201,45 @@ func TestAccCloudFrontFunction_Update_code(t *testing.T) { }) } +func TestAccCloudFrontFunction_UpdateCodeAndPublish(t *testing.T) { + var conf cloudfront.DescribeFunctionOutput + resourceName := "aws_cloudfront_function.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(cloudfront.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, cloudfront.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckCloudfrontFunctionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPublishConfig(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "etag", "ETVPDKIKX0DER"), + resource.TestCheckResourceAttr(resourceName, "publish", "false"), + resource.TestCheckResourceAttr(resourceName, "status", "UNPUBLISHED"), + ), + }, + { + Config: testAccCodeUpdateConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "etag", "E3UN6WX5RRO2AG"), + resource.TestCheckResourceAttr(resourceName, "publish", "true"), + resource.TestCheckResourceAttr(resourceName, "status", "UNASSOCIATED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"publish"}, + }, + }, + }) +} + func TestAccCloudFrontFunction_Update_comment(t *testing.T) { var conf cloudfront.DescribeFunctionOutput resourceName := "aws_cloudfront_function.test" From 5f7bbeeedd89b544386eaf7ee57e2b7d54ea32c9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 8 Nov 2021 11:46:46 -0500 Subject: [PATCH 3/4] r/aws_cloudfront_function: 'etag' contains the DEVELOPMENT stage value. r/aws_cloudfront_function: Add 'live_stage_etag' with the LIVE stage value. --- internal/service/cloudfront/find.go | 2 +- internal/service/cloudfront/function.go | 67 +++++++------------ internal/service/cloudfront/function_test.go | 4 ++ .../docs/r/cloudfront_function.html.markdown | 3 +- 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/internal/service/cloudfront/find.go b/internal/service/cloudfront/find.go index dd42b6b3033..f8077a88246 100644 --- a/internal/service/cloudfront/find.go +++ b/internal/service/cloudfront/find.go @@ -26,7 +26,7 @@ func FindFunctionByNameAndStage(conn *cloudfront.CloudFront, name, stage string) return nil, err } - if output == nil { + if output == nil || output.FunctionSummary == nil { return nil, &resource.NotFoundError{ Message: "Empty result", LastRequest: input, diff --git a/internal/service/cloudfront/function.go b/internal/service/cloudfront/function.go index 9e165219b7d..2ea9037106b 100644 --- a/internal/service/cloudfront/function.go +++ b/internal/service/cloudfront/function.go @@ -28,40 +28,37 @@ func ResourceFunction() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "code": { Type: schema.TypeString, Required: true, }, - "comment": { Type: schema.TypeString, Optional: true, }, - "etag": { Type: schema.TypeString, Computed: true, }, - + "live_stage_etag": { + Type: schema.TypeString, + Computed: true, + }, "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "publish": { Type: schema.TypeBool, Optional: true, Default: true, }, - "runtime": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice(cloudfront.FunctionRuntime_Values(), false), }, - "status": { Type: schema.TypeString, Computed: true, @@ -70,8 +67,6 @@ func ResourceFunction() *schema.Resource { } } -// resourceAwsCloudFrontFunction maps to: -// CreateFunction in the API / SDK func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CloudFrontConn @@ -85,7 +80,7 @@ func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { Name: aws.String(functionName), } - log.Printf("[DEBUG] Creating CloudFront Function %s", functionName) + log.Printf("[DEBUG] Creating CloudFront Function: %s", functionName) output, err := conn.CreateFunction(input) if err != nil { @@ -100,7 +95,7 @@ func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { IfMatch: output.ETag, } - log.Printf("[DEBUG] Publishing Cloudfront Function: %s", input) + log.Printf("[DEBUG] Publishing CloudFront Function: %s", input) _, err := conn.PublishFunction(input) if err != nil { @@ -111,62 +106,52 @@ func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { return resourceFunctionRead(d, meta) } -// resourceFunctionRead maps to: -// GetFunction in the API / SDK func resourceFunctionRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CloudFrontConn - stage := cloudfront.FunctionStageDevelopment - if d.Get("publish").(bool) { - stage = cloudfront.FunctionStageLive - } - - describeFunctionOutput, err := FindFunctionByNameAndStage(conn, d.Id(), stage) + describeFunctionOutput, err := FindFunctionByNameAndStage(conn, d.Id(), cloudfront.FunctionStageDevelopment) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] CloudFront Function (%s/%s) not found, removing from state", d.Id(), stage) + log.Printf("[WARN] CloudFront Function (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error describing CloudFront Function (%s/%s): %w", d.Id(), stage, err) - } - - if d.Get("publish").(bool) { - describeDevelopmentFunctionOutput, err2 := FindFunctionByNameAndStage(conn, d.Id(), cloudfront.FunctionStageDevelopment) - - if err2 != nil { - return fmt.Errorf("error describing CloudFront Function (%s/%s): %w", d.Id(), cloudfront.FunctionStageDevelopment, err2) - } - - d.Set("etag", describeDevelopmentFunctionOutput.ETag) - } else { - d.Set("etag", describeFunctionOutput.ETag) + return fmt.Errorf("error reading CloudFront Function (%s) DEVELOPMENT stage: %w", d.Id(), err) } d.Set("arn", describeFunctionOutput.FunctionSummary.FunctionMetadata.FunctionARN) d.Set("comment", describeFunctionOutput.FunctionSummary.FunctionConfig.Comment) + d.Set("etag", describeFunctionOutput.ETag) d.Set("name", describeFunctionOutput.FunctionSummary.Name) d.Set("runtime", describeFunctionOutput.FunctionSummary.FunctionConfig.Runtime) d.Set("status", describeFunctionOutput.FunctionSummary.Status) getFunctionOutput, err := conn.GetFunction(&cloudfront.GetFunctionInput{ Name: aws.String(d.Id()), - Stage: aws.String(stage), + Stage: aws.String(cloudfront.FunctionStageDevelopment), }) if err != nil { - return fmt.Errorf("error getting CloudFront Function (%s/%s): %w", d.Id(), stage, err) + return fmt.Errorf("error reading CloudFront Function (%s) DEVELOPMENT stage code: %w", d.Id(), err) } d.Set("code", string(getFunctionOutput.FunctionCode)) + describeFunctionOutput, err = FindFunctionByNameAndStage(conn, d.Id(), cloudfront.FunctionStageLive) + + if tfresource.NotFound(err) { + d.Set("live_stage_etag", "") + } else if err != nil { + return fmt.Errorf("error reading CloudFront Function (%s) LIVE stage: %w", d.Id(), err) + } else { + d.Set("live_stage_etag", describeFunctionOutput.ETag) + } + return nil } -// resourceFunctionUpdate maps to: -// UpdateFunctionCode in the API / SDK func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CloudFrontConn etag := d.Get("etag").(string) @@ -186,7 +171,7 @@ func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { output, err := conn.UpdateFunction(input) if err != nil { - return fmt.Errorf("error updating CloudFront Function (%s) configuration : %w", d.Id(), err) + return fmt.Errorf("error updating CloudFront Function (%s): %w", d.Id(), err) } etag = aws.StringValue(output.ETag) @@ -198,7 +183,7 @@ func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { IfMatch: aws.String(etag), } - log.Printf("[DEBUG] Publishing Cloudfront Function: %s", d.Id()) + log.Printf("[DEBUG] Publishing CloudFront Function: %s", d.Id()) _, err := conn.PublishFunction(input) if err != nil { @@ -209,12 +194,10 @@ func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { return resourceFunctionRead(d, meta) } -// resourceAwsCloudFrontFunction maps to: -// DeleteFunction in the API / SDK func resourceFunctionDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CloudFrontConn - log.Printf("[INFO] Deleting Cloudfront Function: %s", d.Id()) + log.Printf("[INFO] Deleting CloudFront Function: %s", d.Id()) _, err := conn.DeleteFunction(&cloudfront.DeleteFunctionInput{ Name: aws.String(d.Id()), IfMatch: aws.String(d.Get("etag").(string)), diff --git a/internal/service/cloudfront/function_test.go b/internal/service/cloudfront/function_test.go index 44325b9a0c0..cf07227054d 100644 --- a/internal/service/cloudfront/function_test.go +++ b/internal/service/cloudfront/function_test.go @@ -99,6 +99,8 @@ func TestAccCloudFrontFunction_publish(t *testing.T) { Config: testAccPublishConfig(rName, false), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "etag", "ETVPDKIKX0DER"), + resource.TestCheckResourceAttr(resourceName, "live_stage_etag", ""), resource.TestCheckResourceAttr(resourceName, "publish", "false"), resource.TestCheckResourceAttr(resourceName, "status", "UNPUBLISHED"), ), @@ -113,6 +115,8 @@ func TestAccCloudFrontFunction_publish(t *testing.T) { Config: testAccPublishConfig(rName, true), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "etag", "ETVPDKIKX0DER"), + resource.TestCheckResourceAttr(resourceName, "live_stage_etag", "ETVPDKIKX0DER"), resource.TestCheckResourceAttr(resourceName, "publish", "true"), resource.TestCheckResourceAttr(resourceName, "status", "UNASSOCIATED"), ), diff --git a/website/docs/r/cloudfront_function.html.markdown b/website/docs/r/cloudfront_function.html.markdown index ef643469bb6..f9ec4801f23 100644 --- a/website/docs/r/cloudfront_function.html.markdown +++ b/website/docs/r/cloudfront_function.html.markdown @@ -46,7 +46,8 @@ The following arguments are optional: In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) identifying your CloudFront Function. -* `etag` - ETag hash of the function +* `etag` - ETag hash of the function. This is the value for the `DEVELOPMENT` stage of the function. +* `live_stage_etag` - ETag hash of any `LIVE` stage of the function. * `status` - Status of the function. Can be `UNPUBLISHED`, `UNASSOCIATED` or `ASSOCIATED`. ## Import From c48355ceddd891304968ab1ed12b93d0b97801dd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 8 Nov 2021 11:54:10 -0500 Subject: [PATCH 4/4] Add CHANGELOG entry. --- .changelog/19697.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/19697.txt diff --git a/.changelog/19697.txt b/.changelog/19697.txt new file mode 100644 index 00000000000..9adb227ec80 --- /dev/null +++ b/.changelog/19697.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_cloudfront_function: Add `live_etag_version` attribute +``` + +```release-note:bug +resource/aws_cloudfront_function: The `etag` attribute always the `DEVELOPMENT` version's value +``` \ No newline at end of file