Skip to content

Commit

Permalink
Merge pull request #40958 from hashicorp/b-lambda_invocation-createon…
Browse files Browse the repository at this point in the history
…ly-update
  • Loading branch information
jar-b authored Jan 16, 2025
2 parents b9843ef + ada03a1 commit 0ae0f39
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changelog/40958.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:bug
resource/aws_lambda_invocation: Prevent a new invocation when upgrading from a version less than `v5.1.0` with no configuration changes
```
```release-note:bug
resource/aws_lambda_invocation: Fix failed input transformations when upgrading from a version less than `v5.1.0` with an `input` that cannot be marshaled into a `map[string]interface{}`
```
9 changes: 9 additions & 0 deletions internal/service/lambda/invocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ func resourceInvocation() *schema.Resource {
UpdateWithoutTimeout: resourceInvocationUpdate,
DeleteWithoutTimeout: resourceInvocationDelete,

SchemaVersion: 1,
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceInvocationConfigV0().CoreConfigSchema().ImpliedType(),
Upgrade: invocationStateUpgradeV0,
Version: 0,
},
},

Schema: map[string]*schema.Schema{
"function_name": {
Type: schema.TypeString,
Expand Down
81 changes: 81 additions & 0 deletions internal/service/lambda/invocation_migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package lambda

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/names"
)

func resourceInvocationConfigV0() *schema.Resource {
// Resource with v0 schema (provider v5.83.0 and below)
return &schema.Resource{
Schema: map[string]*schema.Schema{
"function_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"input": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsJSON,
},
"lifecycle_scope": {
Type: schema.TypeString,
Optional: true,
Default: lifecycleScopeCreateOnly,
ValidateDiagFunc: enum.Validate[lifecycleScope](),
},
"qualifier": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: FunctionVersionLatest,
},
"result": {
Type: schema.TypeString,
Computed: true,
},
"terraform_key": {
Type: schema.TypeString,
Optional: true,
Default: "tf",
},
names.AttrTriggers: {
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}

// This upgrader _should have_ been implemented in v5.1.0 alongside the
// additions of the lifecycle_scope and terraform_key arguments with
// default values, but was overlooked at the time. Because we cannot
// reasonably go back and patch the schemas for all versions in between,
// we instead handle both pre-v5.1.0 and v5.1.0-v5.83.0 versions of the
// previous state.
func invocationStateUpgradeV0(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
if rawState == nil {
rawState = map[string]interface{}{}
}

// If upgrading from a version < v5.1.0, these values will be unset and
// should be set to the defaults added in v5.1.0
if rawState["lifecycle_scope"] == nil {
rawState["lifecycle_scope"] = lifecycleScopeCreateOnly
}
if rawState["terraform_key"] == nil {
rawState["terraform_key"] = "tf"
}

return rawState, nil
}
96 changes: 96 additions & 0 deletions internal/service/lambda/invocation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,102 @@ func TestAccLambdaInvocation_terraformKey(t *testing.T) {
})
}

// Tests the state upgrader coming from a version < v5.1.0 where the default values
// from the arguments added in 5.1.0 are not yet present.
//
// This causes unintentional invocations and/or issues processing input which is
// valid type for CREATE_ONLY lifecycle_scope.
//
// https://github.com/hashicorp/terraform-provider-aws/issues/40954
// https://github.com/hashicorp/terraform-provider-aws/issues/31786
func TestAccLambdaInvocation_UpgradeState_Pre_v5_1_0(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lambda_invocation.test"
fName := "lambda_invocation"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
testData := "value3"
inputJSON := `{"key1":"value1","key2":"value2"}`
resultJSON := fmt.Sprintf(`{"key1":"value1","key2":"value2","key3":%q}`, testData)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.LambdaServiceID),
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
ExternalProviders: map[string]resource.ExternalProvider{
"aws": {
Source: "hashicorp/aws",
VersionConstraint: "4.65.0",
},
},
Config: acctest.ConfigCompose(
testAccInvocationConfig_function(fName, rName, testData),
testAccInvocationConfig_invocation(inputJSON, ""),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckInvocationResult(resourceName, resultJSON),
),
},
{
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Config: acctest.ConfigCompose(
testAccInvocationConfig_function(fName, rName, testData),
testAccInvocationConfig_invocation(inputJSON, ""),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckInvocationResult(resourceName, resultJSON),
),
},
},
})
}

// Tests the state upgrader in cases where the default values from the arguments added
// in v5.1.0 are already present
func TestAccLambdaInvocation_UpgradeState_v5_83_0(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lambda_invocation.test"
fName := "lambda_invocation"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
testData := "value3"
inputJSON := `{"key1":"value1","key2":"value2"}`
resultJSON := fmt.Sprintf(`{"key1":"value1","key2":"value2","key3":%q}`, testData)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.LambdaServiceID),
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
ExternalProviders: map[string]resource.ExternalProvider{
"aws": {
Source: "hashicorp/aws",
VersionConstraint: "5.83.0",
},
},
Config: acctest.ConfigCompose(
testAccInvocationConfig_function(fName, rName, testData),
testAccInvocationConfig_invocation(inputJSON, ""),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckInvocationResult(resourceName, resultJSON),
),
},
{
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Config: acctest.ConfigCompose(
testAccInvocationConfig_function(fName, rName, testData),
testAccInvocationConfig_invocation(inputJSON, ""),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckInvocationResult(resourceName, resultJSON),
),
},
},
})
}

// testAccCheckCRUDDestroyResult verifies that when CRUD lifecycle is active that a destroyed resource
// triggers the lambda.
//
Expand Down

0 comments on commit 0ae0f39

Please sign in to comment.