Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_elasticache_global_replication_group: Allow configuring shards #27500

Merged
merged 9 commits into from
Oct 27, 2022
Merged
7 changes: 7 additions & 0 deletions .changelog/27500.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_elasticache_global_replication_group: Add `global_node_groups` and `num_node_groups` arguments
```

```release-note:enhancement
resource/aws_elasticache_global_replication_group: Add timeouts.
```
206 changes: 188 additions & 18 deletions internal/service/elasticache/global_replication_group.go

Large diffs are not rendered by default.

323 changes: 318 additions & 5 deletions internal/service/elasticache/global_replication_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func TestAccElastiCacheGlobalReplicationGroup_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "global_replication_group_id_suffix", rName),
resource.TestMatchResourceAttr(resourceName, "global_replication_group_id", regexp.MustCompile(tfelasticache.GlobalReplicationGroupRegionPrefixFormat+rName)),
resource.TestCheckResourceAttr(resourceName, "global_replication_group_description", tfelasticache.EmptyDescription),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "0"),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "0"),
resource.TestCheckResourceAttr(resourceName, "primary_replication_group_id", primaryReplicationGroupId),
resource.TestCheckResourceAttr(resourceName, "transit_encryption_enabled", "false"),
),
Expand Down Expand Up @@ -501,7 +503,7 @@ func TestAccElastiCacheGlobalReplicationGroup_ReplaceSecondary_differentRegion(t
})
}

func TestAccElastiCacheGlobalReplicationGroup_clusterMode(t *testing.T) {
func TestAccElastiCacheGlobalReplicationGroup_clusterMode_basic(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}
Expand All @@ -527,6 +529,263 @@ func TestAccElastiCacheGlobalReplicationGroup_clusterMode(t *testing.T) {
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttrPair(resourceName, "automatic_failover_enabled", primaryReplicationGroupResourceName, "automatic_failover_enabled"),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttrPair(resourceName, "global_node_groups.#", primaryReplicationGroupResourceName, "num_node_groups"),
resource.TestCheckResourceAttrPair(resourceName, "num_node_groups", primaryReplicationGroupResourceName, "num_node_groups"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnCreate_NoChange(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var globalReplicationGroup elasticache.GlobalReplicationGroup
var primaryReplicationGroup elasticache.ReplicationGroup

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resourceName := "aws_elasticache_global_replication_group.test"
primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckGlobalReplicationGroup(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGlobalReplicationGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups(rName, 2, 2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "2"),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "2"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnCreate_Increase(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var globalReplicationGroup elasticache.GlobalReplicationGroup
var primaryReplicationGroup elasticache.ReplicationGroup

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resourceName := "aws_elasticache_global_replication_group.test"
primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckGlobalReplicationGroup(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGlobalReplicationGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups(rName, 2, 3),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "3"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0002$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0003$", rName)),
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "3"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnCreate_Decrease(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var globalReplicationGroup elasticache.GlobalReplicationGroup
var primaryReplicationGroup elasticache.ReplicationGroup

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resourceName := "aws_elasticache_global_replication_group.test"
primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckGlobalReplicationGroup(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGlobalReplicationGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups(rName, 3, 1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "1"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
"slots": regexp.MustCompile("^0-16383$"), // all slots
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "1"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "3"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnUpdate_Increase(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var globalReplicationGroup elasticache.GlobalReplicationGroup
var primaryReplicationGroup elasticache.ReplicationGroup

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resourceName := "aws_elasticache_global_replication_group.test"
primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckGlobalReplicationGroup(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGlobalReplicationGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups_inherit(rName, 2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "2"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0002$", rName)),
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "2"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups(rName, 2, 3),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "3"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0002$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0003$", rName)),
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "3"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccElastiCacheGlobalReplicationGroup_SetNumNodeGroupsOnUpdate_Decrease(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var globalReplicationGroup elasticache.GlobalReplicationGroup
var primaryReplicationGroup elasticache.ReplicationGroup

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resourceName := "aws_elasticache_global_replication_group.test"
primaryReplicationGroupResourceName := "aws_elasticache_replication_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckGlobalReplicationGroup(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGlobalReplicationGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups_inherit(rName, 2),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "2"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
}),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0002$", rName)),
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "2"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
Config: testAccGlobalReplicationGroupConfig_numNodeGroups(rName, 2, 1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckGlobalReplicationGroupExists(resourceName, &globalReplicationGroup),
testAccCheckReplicationGroupExists(primaryReplicationGroupResourceName, &primaryReplicationGroup),
resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "global_node_groups.#", "1"),
resource.TestMatchTypeSetElemNestedAttrs(resourceName, "global_node_groups.*", map[string]*regexp.Regexp{
"global_node_group_id": regexp.MustCompile(fmt.Sprintf("^[a-z]+-%s-0001$", rName)),
"slots": regexp.MustCompile("^0-16383$"), // all slots
}),
resource.TestCheckResourceAttr(resourceName, "num_node_groups", "1"),
resource.TestCheckResourceAttr(primaryReplicationGroupResourceName, "num_node_groups", "2"),
),
},
{
Expand Down Expand Up @@ -1601,14 +1860,68 @@ resource "aws_elasticache_replication_group" "test" {

parameter_group_name = "default.redis6.x.cluster.on"
automatic_failover_enabled = true
cluster_mode {
num_node_groups = 2
replicas_per_node_group = 1
}
num_node_groups = 2
replicas_per_node_group = 1
}
`, rName)
}

func testAccGlobalReplicationGroupConfig_numNodeGroups_inherit(rName string, numNodeGroups int) string {
return fmt.Sprintf(`
resource "aws_elasticache_global_replication_group" "test" {
global_replication_group_id_suffix = %[1]q
primary_replication_group_id = aws_elasticache_replication_group.test.id
}

resource "aws_elasticache_replication_group" "test" {
replication_group_id = %[1]q
replication_group_description = "test"

engine = "redis"
engine_version = "6.2"
node_type = "cache.m5.large"

parameter_group_name = "default.redis6.x.cluster.on"
automatic_failover_enabled = true
num_node_groups = %[2]d
replicas_per_node_group = 1

lifecycle {
ignore_changes = [member_clusters, num_node_groups]
}
}
`, rName, numNodeGroups)
}

func testAccGlobalReplicationGroupConfig_numNodeGroups(rName string, numNodeGroups, globalNumNodeGroups int) string {
return fmt.Sprintf(`
resource "aws_elasticache_global_replication_group" "test" {
global_replication_group_id_suffix = %[1]q
primary_replication_group_id = aws_elasticache_replication_group.test.id

num_node_groups = %[3]d
}

resource "aws_elasticache_replication_group" "test" {
replication_group_id = %[1]q
replication_group_description = "test"

engine = "redis"
engine_version = "6.2"
node_type = "cache.m5.large"

parameter_group_name = "default.redis6.x.cluster.on"
automatic_failover_enabled = true
num_node_groups = %[2]d
replicas_per_node_group = 1

lifecycle {
ignore_changes = [member_clusters, num_node_groups]
}
}
`, rName, numNodeGroups, globalNumNodeGroups)
}

func testAccGlobalReplicationGroupConfig_engineVersionInherit(rName, primaryReplicationGroupId, repGroupEngineVersion string) string {
return fmt.Sprintf(`
resource "aws_elasticache_global_replication_group" "test" {
Expand Down
2 changes: 1 addition & 1 deletion internal/service/elasticache/replication_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ func resourceReplicationGroupCreate(d *schema.ResourceData, meta interface{}) er
// state, but the global replication group can still be in the "modifying" state. Wait for the replication group
// to be fully added to the global replication group.
// API calls to the global replication group can be made in any region.
if _, err := waitGlobalReplicationGroupAvailable(context.TODO(), conn, v.(string), GlobalReplicationGroupDefaultCreatedTimeout); err != nil {
if _, err := waitGlobalReplicationGroupAvailable(context.TODO(), conn, v.(string), globalReplicationGroupDefaultCreatedTimeout); err != nil {
return fmt.Errorf("error waiting for ElastiCache Global Replication Group (%s) to be available: %w", v, err)
}
}
Expand Down
Loading