Skip to content

Commit

Permalink
Merge pull request #17301 from hashicorp/elasticache-replication-grou…
Browse files Browse the repository at this point in the history
…p-scale-replicas

resource/aws_elasticache_replication_group: Allow changing replica count in cluster-mode
  • Loading branch information
gdavison committed Jan 28, 2021
2 parents 5f104c8 + b87daf3 commit 90a7313
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .changelog/17301.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_elasticache_replication_group: Allow changing `cluster_mode.replica_count` without re-creation
```
71 changes: 62 additions & 9 deletions aws/resource_aws_elasticache_replication_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,8 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource {
Computed: true,
},
"cluster_mode": {
Type: schema.TypeList,
Optional: true,
// We allow Computed: true here since using number_cache_clusters
// and a cluster mode enabled parameter_group_name will create
// a single shard replication group with number_cache_clusters - 1
// read replicas. Otherwise, the resource is marked ForceNew.
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
ExactlyOneOf: []string{"cluster_mode", "number_cache_clusters"},
Expand All @@ -93,7 +89,6 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource {
"replicas_per_node_group": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"num_node_groups": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -314,7 +309,9 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource {
return nil
},
customdiff.ComputedIf("member_clusters", func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool {
return diff.HasChange("number_cache_clusters") || diff.HasChange("cluster_mode.0.num_node_groups")
return diff.HasChange("number_cache_clusters") ||
diff.HasChange("cluster_mode.0.num_node_groups") ||
diff.HasChange("cluster_mode.0.replicas_per_node_group")
}),
),
}
Expand Down Expand Up @@ -570,7 +567,7 @@ func resourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta int
func resourceAwsElasticacheReplicationGroupUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).elasticacheconn

if d.HasChange("cluster_mode.0.num_node_groups") {
if d.HasChanges("cluster_mode.0.num_node_groups", "cluster_mode.0.replicas_per_node_group") {
err := elasticacheReplicationGroupModifyShardConfiguration(conn, d)
if err != nil {
return fmt.Errorf("error modifying ElastiCache Replication Group (%s) shard configuration: %w", d.Id(), err)
Expand Down Expand Up @@ -761,6 +758,24 @@ func flattenElasticacheNodeGroupsToClusterMode(nodeGroups []*elasticache.NodeGro
}

func elasticacheReplicationGroupModifyShardConfiguration(conn *elasticache.ElastiCache, d *schema.ResourceData) error {
if d.HasChange("cluster_mode.0.num_node_groups") {
err := elasticacheReplicationGroupModifyShardConfigurationNumNodeGroups(conn, d)
if err != nil {
return err
}
}

if d.HasChange("cluster_mode.0.replicas_per_node_group") {
err := elasticacheReplicationGroupModifyShardConfigurationReplicasPerNodeGroup(conn, d)
if err != nil {
return err
}
}

return nil
}

func elasticacheReplicationGroupModifyShardConfigurationNumNodeGroups(conn *elasticache.ElastiCache, d *schema.ResourceData) error {
o, n := d.GetChange("cluster_mode.0.num_node_groups")
oldNumNodeGroups := o.(int)
newNumNodeGroups := n.(int)
Expand Down Expand Up @@ -796,6 +811,44 @@ func elasticacheReplicationGroupModifyShardConfiguration(conn *elasticache.Elast
return nil
}

func elasticacheReplicationGroupModifyShardConfigurationReplicasPerNodeGroup(conn *elasticache.ElastiCache, d *schema.ResourceData) error {
o, n := d.GetChange("cluster_mode.0.replicas_per_node_group")
oldReplicas := o.(int)
newReplicas := n.(int)

if newReplicas > oldReplicas {
input := &elasticache.IncreaseReplicaCountInput{
ApplyImmediately: aws.Bool(true),
NewReplicaCount: aws.Int64(int64(newReplicas)),
ReplicationGroupId: aws.String(d.Id()),
}
_, err := conn.IncreaseReplicaCount(input)
if err != nil {
return fmt.Errorf("error adding ElastiCache Replication Group (%s) replicas: %w", d.Id(), err)
}
_, err = waiter.ReplicationGroupAvailable(conn, d.Id(), d.Timeout(schema.TimeoutUpdate))
if err != nil {
return fmt.Errorf("error waiting for ElastiCache Replication Group (%s) replica addition: %w", d.Id(), err)
}
} else {
input := &elasticache.DecreaseReplicaCountInput{
ApplyImmediately: aws.Bool(true),
NewReplicaCount: aws.Int64(int64(newReplicas)),
ReplicationGroupId: aws.String(d.Id()),
}
_, err := conn.DecreaseReplicaCount(input)
if err != nil {
return fmt.Errorf("error removing ElastiCache Replication Group (%s) replicas: %w", d.Id(), err)
}
_, err = waiter.ReplicationGroupAvailable(conn, d.Id(), d.Timeout(schema.TimeoutUpdate))
if err != nil {
return fmt.Errorf("error waiting for ElastiCache Replication Group (%s) replica removal: %w", d.Id(), err)
}
}

return nil
}

func elasticacheReplicationGroupModifyNumCacheClusters(conn *elasticache.ElastiCache, d *schema.ResourceData) error {
o, n := d.GetChange("number_cache_clusters")
oldNumberCacheClusters := o.(int)
Expand Down
97 changes: 92 additions & 5 deletions aws/resource_aws_elasticache_replication_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ func TestAccAWSElasticacheReplicationGroup_ClusterMode_UpdateNumNodeGroups_Scale
})
}

func TestAccAWSElasticacheReplicationGroup_ClusterMode_ReplicasPerNodeGroup(t *testing.T) {
func TestAccAWSElasticacheReplicationGroup_ClusterMode_UpdateReplicasPerNodeGroup(t *testing.T) {
var rg elasticache.ReplicationGroup
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_elasticache_replication_group.test"
Expand All @@ -628,6 +628,32 @@ func TestAccAWSElasticacheReplicationGroup_ClusterMode_ReplicasPerNodeGroup(t *t
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 2, 1),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x.cluster.on"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "2"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "1"),
resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "4"),
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "4"),
),
},
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 2, 3),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x.cluster.on"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "2"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "3"),
resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "8"),
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "8"),
),
},
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 2, 2),
Check: resource.ComposeTestCheckFunc(
Expand All @@ -641,11 +667,72 @@ func TestAccAWSElasticacheReplicationGroup_ClusterMode_ReplicasPerNodeGroup(t *t
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "6"),
),
},
},
})
}

func TestAccAWSElasticacheReplicationGroup_ClusterMode_UpdateNumNodeGroupsAndReplicasPerNodeGroup_ScaleUp(t *testing.T) {
var rg elasticache.ReplicationGroup
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy,
Steps: []resource.TestStep{
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"apply_immediately"},
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 2, 1),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x.cluster.on"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "2"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "1"),
resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "4"),
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "4"),
),
},
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 3, 2),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x.cluster.on"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "3"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "2"),
resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "9"),
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "9"),
),
},
},
})
}

func TestAccAWSElasticacheReplicationGroup_ClusterMode_UpdateNumNodeGroupsAndReplicasPerNodeGroup_ScaleDown(t *testing.T) {
var rg elasticache.ReplicationGroup
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 3, 2),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists(resourceName, &rg),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x.cluster.on"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.#", "1"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.num_node_groups", "3"),
resource.TestCheckResourceAttr(resourceName, "cluster_mode.0.replicas_per_node_group", "2"),
resource.TestCheckResourceAttr(resourceName, "number_cache_clusters", "9"),
resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "9"),
),
},
{
Config: testAccAWSElasticacheReplicationGroupNativeRedisClusterConfig(rName, 2, 1),
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/elasticache_replication_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Please note that setting a `snapshot_retention_limit` is not supported on cache.

Cluster Mode (`cluster_mode`) supports the following:

* `replicas_per_node_group` - (Required) Specify the number of replica nodes in each node group. Valid values are 0 to 5. Changing this number will force a new resource.
* `replicas_per_node_group` - (Required) Specify the number of replica nodes in each node group. Valid values are 0 to 5. Changing this number will trigger an online resizing operation before other settings modifications.
* `num_node_groups` - (Required) Specify the number of node groups (shards) for this Redis replication group. Changing this number will trigger an online resizing operation before other settings modifications.

## Attributes Reference
Expand Down

0 comments on commit 90a7313

Please sign in to comment.