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

r/aws_cloudfront_function Always store DEVELOPMENT stage etag in state #19697

Merged
merged 7 commits into from
Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 7 additions & 0 deletions .changelog/19697.txt
Original file line number Diff line number Diff line change
@@ -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
```
2 changes: 1 addition & 1 deletion internal/service/cloudfront/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
54 changes: 24 additions & 30 deletions internal/service/cloudfront/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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

Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -111,26 +106,19 @@ 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)
return fmt.Errorf("error reading CloudFront Function (%s) DEVELOPMENT stage: %w", d.Id(), err)
}

d.Set("arn", describeFunctionOutput.FunctionSummary.FunctionMetadata.FunctionARN)
Expand All @@ -142,20 +130,28 @@ func resourceFunctionRead(d *schema.ResourceData, meta interface{}) error {

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)
Expand All @@ -175,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)
Expand All @@ -187,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 {
Expand All @@ -198,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)),
Expand Down
43 changes: 43 additions & 0 deletions internal/service/cloudfront/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
),
Expand All @@ -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"),
),
Expand Down Expand Up @@ -201,6 +205,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"
Expand Down
3 changes: 2 additions & 1 deletion website/docs/r/cloudfront_function.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down