diff --git a/.changelog/31264.txt b/.changelog/31264.txt new file mode 100644 index 00000000000..db4eb8a39cc --- /dev/null +++ b/.changelog/31264.txt @@ -0,0 +1,11 @@ +```release-note:bug +resource/aws_elasticache_cluster: Correctly supports ElastiCache Redis version 7+ +``` + +```release-note:bug +resource/aws_elasticache_global_replication_group: Correctly supports ElastiCache Redis version 7+ +``` + +```release-note:bug +resource/aws_elasticache_replication_group: Correctly supports ElastiCache Redis version 7+ +``` diff --git a/internal/service/elasticache/engine_version.go b/internal/service/elasticache/engine_version.go index 842665370ea..f374180d67c 100644 --- a/internal/service/elasticache/engine_version.go +++ b/internal/service/elasticache/engine_version.go @@ -30,15 +30,10 @@ func validMemcachedVersionString(v interface{}, k string) (ws []string, errors [ } const ( - redisVersionPreV6RegexpRaw = `[1-5](\.[[:digit:]]+){2}` - redisVersionPostV6RegexpRaw = `(([6-9])\.x)|([6-9]\.[[:digit:]]+)` + redisVersionPreV6RegexpPattern = `^[1-5](\.[[:digit:]]+){2}$` + redisVersionPostV6RegexpPattern = `^((6)\.x)|([6-9]\.[[:digit:]]+)$` - redisVersionRegexpRaw = redisVersionPreV6RegexpRaw + "|" + redisVersionPostV6RegexpRaw -) - -const ( - redisVersionRegexpPattern = "^" + redisVersionRegexpRaw + "$" - redisVersionPostV6RegexpPattern = "^" + redisVersionPostV6RegexpRaw + "$" + redisVersionRegexpPattern = redisVersionPreV6RegexpPattern + "|" + redisVersionPostV6RegexpPattern ) var ( @@ -128,15 +123,14 @@ func engineVersionForceNewOnDowngrade(diff forceNewDiffer) error { return diff.ForceNew("engine_version") } -// normalizeEngineVersion returns a github.com/hashicorp/go-version Version -// that can handle a regular 1.2.3 version number or either the 6.x or 6.0 version number used for -// ElastiCache Redis version 6 and higher. 6.x will sort to 6. +// normalizeEngineVersion returns a github.com/hashicorp/go-version Version from: +// - a regular 1.2.3 version number +// - either the 6.x or 6.0 version number used for ElastiCache Redis version 6. 6.x will sort to 6. +// - a 7.0 version number used from version 7 func normalizeEngineVersion(version string) (*gversion.Version, error) { if matches := redisVersionPostV6Regexp.FindStringSubmatch(version); matches != nil { if matches[1] != "" { version = fmt.Sprintf("%s.%d", matches[2], math.MaxInt) - } else if matches[3] != "" { - version = matches[3] } } return gversion.NewVersion(version) diff --git a/internal/service/elasticache/engine_version_test.go b/internal/service/elasticache/engine_version_test.go index fd096c073d8..bb5867d28e8 100644 --- a/internal/service/elasticache/engine_version_test.go +++ b/internal/service/elasticache/engine_version_test.go @@ -139,6 +139,42 @@ func TestValidRedisVersionString(t *testing.T) { version: "6.y", valid: false, }, + { + version: "7.0", + valid: true, + }, + { + version: "7.2", + valid: true, + }, + { + version: "7.x", + valid: false, + }, + { + version: "7.2.x", + valid: false, + }, + { + version: "7.5.0", + valid: false, + }, + { + version: "7.5.", + valid: false, + }, + { + version: "7.", + valid: false, + }, + { + version: "7", + valid: false, + }, + { + version: "7.y", + valid: false, + }, } for _, testcase := range testcases { @@ -191,6 +227,11 @@ func TestValidateClusterEngineVersion(t *testing.T) { version: "6.0", valid: false, }, + { + engine: "", + version: "7.0", + valid: false, + }, { engine: engineMemcached, @@ -207,6 +248,11 @@ func TestValidateClusterEngineVersion(t *testing.T) { version: "6.0", valid: false, }, + { + engine: engineMemcached, + version: "7.0", + valid: false, + }, { engine: engineRedis, @@ -223,6 +269,11 @@ func TestValidateClusterEngineVersion(t *testing.T) { version: "6.0", valid: true, }, + { + engine: engineRedis, + version: "7.0", + valid: true, + }, } for _, testcase := range testcases { @@ -277,17 +328,17 @@ func TestCustomizeDiffEngineVersionIsDowngrade(t *testing.T) { expected: false, }, - // "upgrade major 6.x": { - // old: "5.0.6", - // new: "6.x", - // expectForceNew: false, - // }, + "upgrade major 6.x": { + old: "5.0.6", + new: "6.x", + expected: false, + }, - // "upgrade major 6.digit": { - // old: "5.0.6", - // new: "6.0", - // expectForceNew: false, - // }, + "upgrade major 6.digit": { + old: "5.0.6", + new: "6.0", + expected: false, + }, "downgrade minor versions": { old: "1.3.5", @@ -319,15 +370,15 @@ func TestCustomizeDiffEngineVersionIsDowngrade(t *testing.T) { expected: false, }, - "downgrade from major 7.x to 6.x": { - old: "7.x", + "downgrade from major 7.digit to 6.x": { + old: "7.2", new: "6.x", expected: true, }, - "downgrade from major 7.digit to 6.x": { + "downgrade from major 7.digit to 6.digit": { old: "7.2", - new: "6.x", + new: "6.2", expected: true, }, } @@ -419,17 +470,23 @@ func TestCustomizeDiffEngineVersionForceNewOnDowngrade(t *testing.T) { expectForceNew: false, }, - // "upgrade major 6.x": { - // old: "5.0.6", - // new: "6.x", - // expectForceNew: false, - // }, + "upgrade major 6.x": { + old: "5.0.6", + new: "6.x", + expectForceNew: false, + }, + + "upgrade major 6.digit": { + old: "5.0.6", + new: "6.0", + expectForceNew: false, + }, - // "upgrade major 6.digit": { - // old: "5.0.6", - // new: "6.0", - // expectForceNew: false, - // }, + "upgrade major 7.digit": { + old: "6.x", + new: "7.0", + expectForceNew: false, + }, "downgrade minor versions": { old: "1.3.5", @@ -461,15 +518,21 @@ func TestCustomizeDiffEngineVersionForceNewOnDowngrade(t *testing.T) { expectForceNew: false, }, - "downgrade from major 7.x to 6.x": { - old: "7.x", + "downgrade from major 7.digit to 6.x": { + old: "7.2", new: "6.x", expectForceNew: true, }, - "downgrade from major 7.digit to 6.x": { + "downgrade from major 7.digit to 6.digit": { old: "7.2", - new: "6.x", + new: "6.2", + expectForceNew: true, + }, + + "downgrade major 7.digit": { + old: "7.2", + new: "7.0", expectForceNew: true, }, } @@ -529,10 +592,19 @@ func TestNormalizeEngineVersion(t *testing.T) { normalized: fmt.Sprintf("6.%d.0", math.MaxInt), valid: true, }, + { + version: "7.2", + normalized: "7.2.0", + valid: true, + }, { version: "5.x", valid: false, }, + { + version: "7.x", + valid: false, + }, } for _, testcase := range testcases { diff --git a/internal/service/elasticache/replication_group_test.go b/internal/service/elasticache/replication_group_test.go index 6b1a39b2961..a309e636295 100644 --- a/internal/service/elasticache/replication_group_test.go +++ b/internal/service/elasticache/replication_group_test.go @@ -136,6 +136,41 @@ func TestAccElastiCacheReplicationGroup_uppercase(t *testing.T) { }) } +func TestAccElastiCacheReplicationGroup_EngineVersion_v7(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var rg elasticache.ReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckReplicationGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccReplicationGroupConfig_v7(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(ctx, resourceName, &rg), + resource.TestCheckResourceAttr(resourceName, "engine", "redis"), + resource.TestCheckResourceAttr(resourceName, "engine_version", "7.0"), + resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexp.MustCompile(`^7\.[[:digit:]]+\.[[:digit:]]+$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately"}, //not in the API + }, + }, + }) +} + func TestAccElastiCacheReplicationGroup_EngineVersion_update(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -2802,6 +2837,17 @@ resource "aws_elasticache_replication_group" "test" { `, rName) } +func testAccReplicationGroupConfig_v7(rName string) string { + return fmt.Sprintf(` +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[1]q + replication_group_description = "test description" + node_type = "cache.t3.small" + engine_version = "7.0" +} +`, rName) +} + func testAccReplicationGroupConfig_uppercase(rName string) string { return acctest.ConfigCompose( acctest.ConfigVPCWithSubnets(rName, 2), diff --git a/website/docs/r/elasticache_cluster.html.markdown b/website/docs/r/elasticache_cluster.html.markdown index 2b711f45bb1..efc6c73d185 100644 --- a/website/docs/r/elasticache_cluster.html.markdown +++ b/website/docs/r/elasticache_cluster.html.markdown @@ -156,7 +156,8 @@ The following arguments are optional: * `engine_version` – (Optional) Version number of the cache engine to be used. If not set, defaults to the latest version. See [Describe Cache Engine Versions](https://docs.aws.amazon.com/cli/latest/reference/elasticache/describe-cache-engine-versions.html) in the AWS Documentation for supported versions. - When `engine` is `redis` and the version is 6 or higher, the major and minor version can be set, e.g., `6.2`, + When `engine` is `redis` and the version is 7 or higher, the major and minor version should be set, e.g., `7.2`. + When the version is 6, the major and minor version can be set, e.g., `6.2`, or the minor version can be unspecified which will use the latest version at creation time, e.g., `6.x`. Otherwise, specify the full version desired, e.g., `5.0.6`. The actual engine version used is returned in the attribute `engine_version_actual`, see [Attributes Reference](#attributes-reference) below. diff --git a/website/docs/r/elasticache_global_replication_group.html.markdown b/website/docs/r/elasticache_global_replication_group.html.markdown index 48bd8a8d14d..1df030be0bd 100644 --- a/website/docs/r/elasticache_global_replication_group.html.markdown +++ b/website/docs/r/elasticache_global_replication_group.html.markdown @@ -110,7 +110,8 @@ The following arguments are supported: When creating, by default the Global Replication Group inherits the version of the primary replication group. If a version is specified, the Global Replication Group and all member replication groups will be upgraded to this version. Cannot be downgraded without replacing the Global Replication Group and all member replication groups. - If the version is 6 or higher, the major and minor version can be set, e.g., `6.2`, + When the version is 7 or higher, the major and minor version should be set, e.g., `7.2`. + When the version is 6, the major and minor version can be set, e.g., `6.2`, or the minor version can be unspecified which will use the latest version at creation time, e.g., `6.x`. The actual engine version used is returned in the attribute `engine_version_actual`, see [Attributes Reference](#attributes-reference) below. * `global_replication_group_id_suffix` – (Required) The suffix name of a Global Datastore. If `global_replication_group_id_suffix` is changed, creates a new resource. diff --git a/website/docs/r/elasticache_replication_group.html.markdown b/website/docs/r/elasticache_replication_group.html.markdown index 25d3c9a3019..ad9e4c27e8f 100644 --- a/website/docs/r/elasticache_replication_group.html.markdown +++ b/website/docs/r/elasticache_replication_group.html.markdown @@ -184,7 +184,8 @@ The following arguments are optional: * `data_tiering_enabled` - (Optional) Enables data tiering. Data tiering is only supported for replication groups using the r6gd node type. This parameter must be set to `true` when using r6gd nodes. * `engine` - (Optional) Name of the cache engine to be used for the clusters in this replication group. The only valid value is `redis`. * `engine_version` - (Optional) Version number of the cache engine to be used for the cache clusters in this replication group. - If the version is 6 or higher, the major and minor version can be set, e.g., `6.2`, + If the version is 7 or higher, the major and minor version should be set, e.g., `7.2`. + If the version is 6, the major and minor version can be set, e.g., `6.2`, or the minor version can be unspecified which will use the latest version at creation time, e.g., `6.x`. Otherwise, specify the full version desired, e.g., `5.0.6`. The actual engine version used is returned in the attribute `engine_version_actual`, see [Attributes Reference](#attributes-reference) below.