Skip to content

Commit

Permalink
Merge pull request #34600 from hashicorp/b-elasticache_replication_gr…
Browse files Browse the repository at this point in the history
…oup-regression

r/aws_elasticache_replication_group: v2 state upgrader
  • Loading branch information
jar-b committed Nov 28, 2023
2 parents 2dd059b + bc70fa7 commit c114d40
Show file tree
Hide file tree
Showing 4 changed files with 385 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/34600.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_elaticache_replication_group: Fix regression caused by the introduction of the `auth_token_update_strategy` argument with a default value
```
15 changes: 13 additions & 2 deletions internal/service/elasticache/replication_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ func ResourceReplicationGroup() *schema.Resource {
Optional: true,
ValidateFunc: validation.StringInSlice(elasticache.AuthTokenUpdateStrategyType_Values(), true),
Default: elasticache.AuthTokenUpdateStrategyTypeRotate,
RequiredWith: []string{"auth_token"},
},
"auto_minor_version_upgrade": {
Type: nullable.TypeNullableBool,
Expand Down Expand Up @@ -349,14 +348,26 @@ func ResourceReplicationGroup() *schema.Resource {
},
},

SchemaVersion: 1,
SchemaVersion: 2,
// SchemaVersion: 1 did not include any state changes via MigrateState.
// Perform a no-operation state upgrade for Terraform 0.12 compatibility.
// Future state migrations should be performed with StateUpgraders.
MigrateState: func(v int, inst *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
return inst, nil
},

StateUpgraders: []schema.StateUpgrader{
// v5.27.0 introduced the auth_token_update_strategy argument with a default
// value required to preserve backward compatibility. In order to prevent
// differences and attempted modifications on upgrade, the default value
// must be written to state via a state upgrader.
{
Type: resourceReplicationGroupConfigV1().CoreConfigSchema().ImpliedType(),
Upgrade: replicationGroupStateUpgradeV1,
Version: 1,
},
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(ReplicationGroupDefaultCreatedTimeout),
Delete: schema.DefaultTimeout(ReplicationGroupDefaultDeletedTimeout),
Expand Down
330 changes: 330 additions & 0 deletions internal/service/elasticache/replication_group_migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package elasticache

import (
"context"
"strings"

"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/types/nullable"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

func replicationGroupStateUpgradeV1(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
if rawState == nil {
rawState = map[string]interface{}{}
}

// Set auth_token_update_strategy to new default value
rawState["auth_token_update_strategy"] = elasticache.AuthTokenUpdateStrategyTypeRotate

return rawState, nil
}

func resourceReplicationGroupConfigV1() *schema.Resource {
//lintignore:R011
return &schema.Resource{
Schema: map[string]*schema.Schema{
"apply_immediately": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"at_rest_encryption_enabled": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Computed: true,
},
"auth_token": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
ValidateFunc: validReplicationGroupAuthToken,
ConflictsWith: []string{"user_group_ids"},
},
"auto_minor_version_upgrade": {
Type: nullable.TypeNullableBool,
Optional: true,
Computed: true,
ValidateFunc: nullable.ValidateTypeStringNullableBool,
},
"automatic_failover_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"cluster_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"configuration_endpoint_address": {
Type: schema.TypeString,
Computed: true,
},
"data_tiering_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
ForceNew: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"engine": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: engineRedis,
ValidateFunc: validation.StringInSlice([]string{engineRedis}, true),
},
"engine_version": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validRedisVersionString,
},
"engine_version_actual": {
Type: schema.TypeString,
Computed: true,
},
"global_replication_group_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ConflictsWith: []string{
"num_node_groups",
"parameter_group_name",
"engine",
"engine_version",
"node_type",
"security_group_names",
"transit_encryption_enabled",
"at_rest_encryption_enabled",
"snapshot_arns",
"snapshot_name",
},
},
"ip_discovery": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(elasticache.IpDiscovery_Values(), false),
},
"log_delivery_configuration": {
Type: schema.TypeSet,
Optional: true,
MaxItems: 2,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"destination_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(elasticache.DestinationType_Values(), false),
},
"destination": {
Type: schema.TypeString,
Required: true,
},
"log_format": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(elasticache.LogFormat_Values(), false),
},
"log_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(elasticache.LogType_Values(), false),
},
},
},
},
"maintenance_window": {
Type: schema.TypeString,
Optional: true,
Computed: true,
StateFunc: func(val interface{}) string {
// ElastiCache always changes the maintenance to lowercase
return strings.ToLower(val.(string))
},
ValidateFunc: verify.ValidOnceAWeekWindowFormat,
},
"member_clusters": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"multi_az_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"network_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(elasticache.NetworkType_Values(), false),
},
"node_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"notification_topic_arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidARN,
},
"num_cache_clusters": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
ConflictsWith: []string{"num_node_groups"},
},
"num_node_groups": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ConflictsWith: []string{"num_cache_clusters", "global_replication_group_id"},
},
"parameter_group_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return strings.HasPrefix(old, "global-datastore-")
},
},
"port": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
// Suppress default Redis ports when not defined
if !d.IsNewResource() && new == "0" && old == defaultRedisPort {
return true
}
return false
},
},
"preferred_cache_cluster_azs": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"primary_endpoint_address": {
Type: schema.TypeString,
Computed: true,
},
"reader_endpoint_address": {
Type: schema.TypeString,
Computed: true,
},
"replicas_per_node_group": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"replication_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateReplicationGroupID,
StateFunc: func(val interface{}) string {
return strings.ToLower(val.(string))
},
},
"security_group_names": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"security_group_ids": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"snapshot_arns": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
// Note: Unlike aws_elasticache_cluster, this does not have a limit of 1 item.
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.All(
verify.ValidARN,
validation.StringDoesNotContainAny(","),
),
},
Set: schema.HashString,
},
"snapshot_retention_limit": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntAtMost(35),
},
"snapshot_window": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: verify.ValidOnceADayWindowFormat,
},
"snapshot_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"subnet_group_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"transit_encryption_enabled": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Computed: true,
},
"user_group_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
ConflictsWith: []string{"auth_token"},
},
"kms_key_id": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"final_snapshot_identifier": {
Type: schema.TypeString,
Optional: true,
},
},
}
}

0 comments on commit c114d40

Please sign in to comment.