Skip to content

Commit 6913625

Browse files
author
timothy-welsh_ramp
committed
Support Valkey upgrades on elasticache_global_replication_group
Once a replication group is added to a global datastore, all of it's engine upgrades need to be controlled on the global datastore. This resource did not support setting an explicit engine value on global datastores, preventing upgrades (#41618). Further, if creating a Valkey global datastore by inheriting version from the primary (or manually upgrading the engine of the global datastore in AWS Console), then you could not continue manage the secondary replication groups in terraform. A Valkey engine would cause an incorrect destroy & re-create plan (if using defaults for engine) or a conflict (if trying to set the engine correctly). We remove the default engine value, mark it as computed and add DiffSuppressFunc to engine, engine_version and parmeter_group_name to ignore changes to these attributes in plans when the resource belongs to a global datastore (primary or secondary). Fixes #40786 and #41713. This also has the benefit of removing the need to add explicit ignore_changes lifecycle blocks to attempt to work around these plans. We update existing documentation on around this.
1 parent 229b588 commit 6913625

File tree

7 files changed

+362
-78
lines changed

7 files changed

+362
-78
lines changed

internal/service/elasticache/engine_version.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/YakDriver/regexache"
1414
"github.com/aws/aws-sdk-go-v2/aws"
1515
gversion "github.com/hashicorp/go-version"
16+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1617
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1718
"github.com/hashicorp/terraform-provider-aws/names"
1819
)
@@ -109,6 +110,22 @@ func customizeDiffEngineVersionForceNewOnDowngrade(_ context.Context, diff *sche
109110
return engineVersionForceNewOnDowngrade(diff)
110111
}
111112

113+
func customizeDiffEngineForceNewOnDowngrade() schema.CustomizeDiffFunc {
114+
return customdiff.ForceNewIf(names.AttrEngine, func(_ context.Context, diff *schema.ResourceDiff, meta any) bool {
115+
if _, is_global := diff.GetOk("global_replication_group_id"); is_global {
116+
return false
117+
}
118+
119+
if !diff.HasChange(names.AttrEngine) {
120+
return false
121+
}
122+
if old, new := diff.GetChange(names.AttrEngine); old.(string) == engineRedis && new.(string) == engineValkey {
123+
return false
124+
}
125+
return true
126+
})
127+
}
128+
112129
type getChangeDiffer interface {
113130
Get(key string) any
114131
GetChange(key string) (any, any)
@@ -151,12 +168,17 @@ func engineVersionIsDowngrade(diff getChangeDiffer) (bool, error) {
151168
type forceNewDiffer interface {
152169
Id() string
153170
Get(key string) any
171+
GetOk(key string) (any, bool)
154172
GetChange(key string) (any, any)
155173
HasChange(key string) bool
156174
ForceNew(key string) error
157175
}
158176

159177
func engineVersionForceNewOnDowngrade(diff forceNewDiffer) error {
178+
if _, is_global := diff.GetOk("global_replication_group_id"); is_global {
179+
return nil
180+
}
181+
160182
if diff.Id() == "" || !diff.HasChange(names.AttrEngineVersion) {
161183
return nil
162184
}

internal/service/elasticache/engine_version_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ func (d *mockForceNewDiffer) Get(key string) any {
513513
return d.old
514514
}
515515

516+
func (d *mockForceNewDiffer) GetOk(key string) (any, bool) {
517+
return d.old, d.old != ""
518+
}
519+
516520
func (d *mockForceNewDiffer) HasChange(key string) bool {
517521
return d.hasChange || d.old != d.new
518522
}

internal/service/elasticache/exports_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var (
4141
EngineVersionIsDowngrade = engineVersionIsDowngrade
4242
GlobalReplicationGroupRegionPrefixFormat = globalReplicationGroupRegionPrefixFormat
4343
NormalizeEngineVersion = normalizeEngineVersion
44-
ParamGroupNameRequiresMajorVersionUpgrade = paramGroupNameRequiresMajorVersionUpgrade
44+
ParamGroupNameRequiresMajorVersionUpgrade = paramGroupNameRequiresEngineOrMajorVersionUpgrade
4545
ValidateClusterEngineVersion = validateClusterEngineVersion
4646
ValidMemcachedVersionString = validMemcachedVersionString
4747
ValidRedisVersionString = validRedisVersionString

internal/service/elasticache/global_replication_group.go

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,10 @@ func resourceGlobalReplicationGroup() *schema.Resource {
9090
Computed: true,
9191
},
9292
names.AttrEngine: {
93-
Type: schema.TypeString,
94-
Computed: true,
93+
Type: schema.TypeString,
94+
Optional: true,
95+
Computed: true,
96+
ValidateFunc: validation.StringInSlice([]string{engineRedis, engineValkey}, true),
9597
},
9698
names.AttrEngineVersion: {
9799
Type: schema.TypeString,
@@ -210,7 +212,8 @@ func resourceGlobalReplicationGroup() *schema.Resource {
210212

211213
CustomizeDiff: customdiff.All(
212214
customizeDiffGlobalReplicationGroupEngineVersionErrorOnDowngrade,
213-
customizeDiffGlobalReplicationGroupParamGroupNameRequiresMajorVersionUpgrade,
215+
customizeDiffEngineForceNewOnDowngrade(),
216+
customizeDiffGlobalReplicationGroupParamGroupNameRequiresEngineOrMajorVersionUpgrade,
214217
customdiff.ComputedIf("global_node_groups", diffHasChange("num_node_groups")),
215218
),
216219
}
@@ -233,18 +236,23 @@ of the Global Replication Group and all Replication Group members. The AWS provi
233236
Please use the "-replace" option on the terraform plan and apply commands (see https://www.terraform.io/cli/commands/plan#replace-address).`, diff.Id())
234237
}
235238

236-
func customizeDiffGlobalReplicationGroupParamGroupNameRequiresMajorVersionUpgrade(_ context.Context, diff *schema.ResourceDiff, _ any) error {
237-
return paramGroupNameRequiresMajorVersionUpgrade(diff)
239+
func customizeDiffGlobalReplicationGroupParamGroupNameRequiresEngineOrMajorVersionUpgrade(_ context.Context, diff *schema.ResourceDiff, _ any) error {
240+
return paramGroupNameRequiresEngineOrMajorVersionUpgrade(diff)
238241
}
239242

240243
// parameter_group_name can only be set when doing a major update,
241244
// but we also should allow it to stay set afterwards
242-
func paramGroupNameRequiresMajorVersionUpgrade(diff sdkv2.ResourceDiffer) error {
245+
func paramGroupNameRequiresEngineOrMajorVersionUpgrade(diff sdkv2.ResourceDiffer) error {
243246
o, n := diff.GetChange(names.AttrParameterGroupName)
244247
if o.(string) == n.(string) {
245248
return nil
246249
}
247250

251+
// param group must be able to change on Redis 7.1 to Valkey 7.2 upgrade
252+
if diff.HasChange(names.AttrEngine) {
253+
return nil
254+
}
255+
248256
if diff.Id() == "" {
249257
if !diff.HasChange(names.AttrEngineVersion) {
250258
return errors.New("cannot change parameter group name without upgrading major engine version")
@@ -262,7 +270,7 @@ func paramGroupNameRequiresMajorVersionUpgrade(diff sdkv2.ResourceDiffer) error
262270
if vDiff[0] == 0 && vDiff[1] == 0 {
263271
return errors.New("cannot change parameter group name without upgrading major engine version")
264272
}
265-
if vDiff[0] != 1 {
273+
if vDiff[0] == 0 {
266274
return fmt.Errorf("cannot change parameter group name on minor engine version upgrade, upgrading from %s to %s", oldVersion.String(), newVersion.String())
267275
}
268276
}
@@ -318,9 +326,25 @@ func resourceGlobalReplicationGroupCreate(ctx context.Context, d *schema.Resourc
318326
}
319327
}
320328

321-
if v, ok := d.GetOk(names.AttrEngineVersion); ok {
329+
if e, ok := d.GetOk(names.AttrEngine); ok {
330+
if e.(string) == aws.ToString(globalReplicationGroup.Engine) {
331+
log.Printf("[DEBUG] Not updating ElastiCache Global Replication Group (%s) engine: no change from %q", d.Id(), e)
332+
} else {
333+
version := d.Get(names.AttrEngineVersion).(string)
334+
p := d.Get(names.AttrParameterGroupName).(string)
335+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(e.(string), version, p), "engine", d.Timeout(schema.TimeoutUpdate)); err != nil {
336+
return sdkdiag.AppendFromErr(diags, err)
337+
}
338+
}
339+
} else if v, ok := d.GetOk(names.AttrEngineVersion); ok {
322340
requestedVersion, _ := normalizeEngineVersion(v.(string))
323341

342+
// backwards-compatibility; imply redis engine if just given engine version
343+
engine, ok := d.GetOk(names.AttrEngine)
344+
if !ok {
345+
engine = engineRedis
346+
}
347+
324348
engineVersion, err := gversion.NewVersion(aws.ToString(globalReplicationGroup.EngineVersion))
325349
if err != nil {
326350
return sdkdiag.AppendErrorf(diags, "updating ElastiCache Global Replication Group (%s) engine version on creation: error reading engine version: %s", d.Id(), err)
@@ -335,15 +359,15 @@ func resourceGlobalReplicationGroupCreate(ctx context.Context, d *schema.Resourc
335359
p := d.Get(names.AttrParameterGroupName).(string)
336360

337361
if diff[0] == 1 {
338-
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(v.(string), p), "engine version (major)", d.Timeout(schema.TimeoutCreate)); err != nil {
362+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(engine.(string), v.(string), p), "engine version (major)", d.Timeout(schema.TimeoutCreate)); err != nil {
339363
return sdkdiag.AppendFromErr(diags, err)
340364
}
341365
} else if diff[1] == 1 {
342366
if p != "" {
343367
return sdkdiag.AppendErrorf(diags, "cannot change parameter group name on minor engine version upgrade, upgrading from %s to %s", engineVersion.String(), requestedVersion.String())
344368
}
345369
if t, _ := regexp.MatchString(`[6-9]\.x`, v.(string)); !t {
346-
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMinorUpdater(v.(string)), "engine version (minor)", d.Timeout(schema.TimeoutCreate)); err != nil {
370+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMinorUpdater(engine.(string), v.(string)), "engine version (minor)", d.Timeout(schema.TimeoutCreate)); err != nil {
347371
return sdkdiag.AppendFromErr(diags, err)
348372
}
349373
}
@@ -440,20 +464,32 @@ func resourceGlobalReplicationGroupUpdate(ctx context.Context, d *schema.Resourc
440464
}
441465
}
442466

443-
if d.HasChange(names.AttrEngineVersion) {
467+
if d.HasChange(names.AttrEngine) {
468+
engine := d.Get(names.AttrEngine).(string)
469+
version := d.Get(names.AttrEngineVersion).(string)
470+
p := d.Get(names.AttrParameterGroupName).(string)
471+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(engine, version, p), "engine version (major)", d.Timeout(schema.TimeoutUpdate)); err != nil {
472+
return sdkdiag.AppendFromErr(diags, err)
473+
}
474+
} else if d.HasChange(names.AttrEngineVersion) {
444475
o, n := d.GetChange(names.AttrEngineVersion)
445476

446477
newVersion, _ := normalizeEngineVersion(n.(string))
447478
oldVersion, _ := gversion.NewVersion(o.(string))
479+
// backwards-compatibility; imply redis engine if just given engine version
480+
engine, ok := d.GetOk(names.AttrEngine)
481+
if !ok {
482+
engine = engineRedis
483+
}
448484

449485
diff := diffVersion(newVersion, oldVersion)
450486
if diff[0] == 1 {
451487
p := d.Get(names.AttrParameterGroupName).(string)
452-
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(n.(string), p), "engine version (major)", d.Timeout(schema.TimeoutUpdate)); err != nil {
488+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMajorUpdater(engine.(string), n.(string), p), "engine version (major)", d.Timeout(schema.TimeoutUpdate)); err != nil {
453489
return sdkdiag.AppendFromErr(diags, err)
454490
}
455491
} else if diff[1] == 1 {
456-
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMinorUpdater(n.(string)), "engine version (minor)", d.Timeout(schema.TimeoutUpdate)); err != nil {
492+
if err := updateGlobalReplicationGroup(ctx, conn, d.Id(), globalReplicationGroupEngineVersionMinorUpdater(engine.(string), n.(string)), "engine version (minor)", d.Timeout(schema.TimeoutUpdate)); err != nil {
457493
return sdkdiag.AppendFromErr(diags, err)
458494
}
459495
}
@@ -509,14 +545,16 @@ func globalReplicationGroupDescriptionUpdater(description string) globalReplicat
509545
}
510546
}
511547

512-
func globalReplicationGroupEngineVersionMinorUpdater(version string) globalReplicationGroupUpdater {
548+
func globalReplicationGroupEngineVersionMinorUpdater(engine, version string) globalReplicationGroupUpdater {
513549
return func(input *elasticache.ModifyGlobalReplicationGroupInput) {
550+
input.Engine = aws.String(engine)
514551
input.EngineVersion = aws.String(version)
515552
}
516553
}
517554

518-
func globalReplicationGroupEngineVersionMajorUpdater(version, paramGroupName string) globalReplicationGroupUpdater {
555+
func globalReplicationGroupEngineVersionMajorUpdater(engine, version, paramGroupName string) globalReplicationGroupUpdater {
519556
return func(input *elasticache.ModifyGlobalReplicationGroupInput) {
557+
input.Engine = aws.String(engine)
520558
input.EngineVersion = aws.String(version)
521559
input.CacheParameterGroupName = aws.String(paramGroupName)
522560
}

0 commit comments

Comments
 (0)