Skip to content
Draft
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
41 changes: 37 additions & 4 deletions internal/service/elasticache/serverless_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package elasticache
import (
"context"
"fmt"
"strconv"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
Expand All @@ -15,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
Expand Down Expand Up @@ -90,18 +92,23 @@ func (r *serverlessCacheResource) Schema(ctx context.Context, request resource.S
names.AttrEngine: schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplaceIf(
func(ctx context.Context, req planmodifier.StringRequest, resp *stringplanmodifier.RequiresReplaceIfFuncResponse) {
// In-place updates are only supported for redis -> valkey
// In-place update support for redis -> valkey
if req.StateValue.Equal(types.StringValue(engineRedis)) && req.PlanValue.Equal(types.StringValue(engineValkey)) {
return
}
// In-place updates support for valkey -> redis
if req.StateValue.Equal(types.StringValue(engineValkey)) && req.PlanValue.Equal(types.StringValue(engineRedis)) {
return
}

// Any other change will force a replacement
resp.RequiresReplace = true
},
"Engine modifications other than redis to valkey require a replacement",
"Engine modifications other than redis to valkey require a replacement",
"Engine modifications other than redis to valkey or valkey to redis require a replacement",
"Engine modifications other than redis to valkey or valkey to redis require a replacement",
),
},
},
Expand All @@ -120,7 +127,33 @@ func (r *serverlessCacheResource) Schema(ctx context.Context, request resource.S
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
stringplanmodifier.RequiresReplaceIf(
func(ctx context.Context, req planmodifier.StringRequest, resp *stringplanmodifier.RequiresReplaceIfFuncResponse) {
var engineVal types.String
req.Config.GetAttribute(ctx, path.Root(names.AttrEngine), &engineVal)

stateFloatVal, err := strconv.ParseFloat(req.StateValue.ValueString(), 64)
if err != nil {
response.Diagnostics.AddError("incorrect major_engine_version format", err.Error())
return
}

planFloatVal, err := strconv.ParseFloat(req.PlanValue.ValueString(), 64)
if err != nil {
response.Diagnostics.AddError("incorrect major_engine_version format", err.Error())
return
}

if stateFloatVal < planFloatVal && engineVal.Equal(types.StringValue(engineValkey)) {
return
}

// Any other change will force a replacement
resp.RequiresReplace = true
},
"major_engine_version downgrade is not supported for valkey",
"major_engine_version downgrade is not supported for valkey",
),
},
},
names.AttrName: schema.StringAttribute{
Expand Down
71 changes: 71 additions & 0 deletions internal/service/elasticache/serverless_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,67 @@ func TestAccElastiCacheServerlessCache_engine(t *testing.T) {
testAccCheckServerlessCacheExists(ctx, t, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, names.AttrEngine, tfelasticache.EngineRedis),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate),
},
},
},
},
})
}

func TestAccElastiCacheServerlessCache_valkeyMajorEngineVersion(t *testing.T) {
ctx := acctest.Context(t)
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix)
resourceName := "aws_elasticache_serverless_cache.test"
var v awstypes.ServerlessCache

acctest.ParallelTest(ctx, t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: resource.ComposeAggregateTestCheckFunc(
testAccCheckServerlessCacheDestroy(ctx, t),
),
Steps: []resource.TestStep{
{
Config: testAccServerlessCacheConfig_majorEngineVersion(rName, tfelasticache.EngineValkey, "7"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckServerlessCacheExists(ctx, t, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, names.AttrEngine, tfelasticache.EngineValkey),
resource.TestCheckResourceAttr(resourceName, "major_engine_version", "7"),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate),
},
},
},
{
Config: testAccServerlessCacheConfig_majorEngineVersion(rName, tfelasticache.EngineValkey, "8"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckServerlessCacheExists(ctx, t, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, names.AttrEngine, tfelasticache.EngineValkey),
resource.TestCheckResourceAttr(resourceName, "major_engine_version", "8"),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate),
},
},
},
{
Config: testAccServerlessCacheConfig_majorEngineVersion(rName, tfelasticache.EngineValkey, "7"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckServerlessCacheExists(ctx, t, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, names.AttrEngine, tfelasticache.EngineValkey),
resource.TestCheckResourceAttr(resourceName, "major_engine_version", "7"),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionDestroyBeforeCreate),
Expand Down Expand Up @@ -702,6 +763,16 @@ resource "aws_elasticache_serverless_cache" "test" {
`, rName, engine)
}

func testAccServerlessCacheConfig_majorEngineVersion(rName, engine, major_engine_version string) string {
return fmt.Sprintf(`
resource "aws_elasticache_serverless_cache" "test" {
name = %[1]q
engine = %[2]q
major_engine_version = %[3]q
}
`, rName, engine, major_engine_version)
}

func testAccServerlessCacheConfig_full(rName string) string {
return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(`
resource "aws_elasticache_serverless_cache" "test" {
Expand Down