From 2d67818834f1da7e78ebe4f26a3b78c77d26ef8c Mon Sep 17 00:00:00 2001 From: Sasi Date: Fri, 3 Jan 2025 13:18:32 -0500 Subject: [PATCH 1/5] support of valkey engine for elasticache user and usergroup --- internal/service/elasticache/user.go | 7 ++- internal/service/elasticache/user_group.go | 4 +- .../service/elasticache/user_group_test.go | 36 +++++++++++++ internal/service/elasticache/user_test.go | 51 +++++++++++++++++++ website/docs/r/elasticache_user.html.markdown | 2 +- .../r/elasticache_user_group.html.markdown | 2 +- 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/internal/service/elasticache/user.go b/internal/service/elasticache/user.go index 315999095d2..0a893aff1d4 100644 --- a/internal/service/elasticache/user.go +++ b/internal/service/elasticache/user.go @@ -90,8 +90,7 @@ func resourceUser() *schema.Resource { names.AttrEngine: { Type: schema.TypeString, Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"REDIS"}, false), + ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return strings.EqualFold(old, new) }, @@ -249,6 +248,10 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf input.NoPasswordRequired = aws.Bool(d.Get("no_password_required").(bool)) } + if d.HasChange(names.AttrEngine) { + input.Engine = aws.String(d.Get(names.AttrEngine).(string)) + } + if d.HasChange("passwords") { input.Passwords = flex.ExpandStringValueSet(d.Get("passwords").(*schema.Set)) } diff --git a/internal/service/elasticache/user_group.go b/internal/service/elasticache/user_group.go index 49d1b72c4f9..62201443f7a 100644 --- a/internal/service/elasticache/user_group.go +++ b/internal/service/elasticache/user_group.go @@ -50,8 +50,7 @@ func resourceUserGroup() *schema.Resource { names.AttrEngine: { Type: schema.TypeString, Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"REDIS"}, false), + ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return strings.EqualFold(old, new) }, @@ -155,6 +154,7 @@ func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta i if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &elasticache.ModifyUserGroupInput{ UserGroupId: aws.String(d.Get("user_group_id").(string)), + Engine: aws.String(d.Get(names.AttrEngine).(string)), } if d.HasChange("user_ids") { diff --git a/internal/service/elasticache/user_group_test.go b/internal/service/elasticache/user_group_test.go index a316c31aaa2..3e83e4c001d 100644 --- a/internal/service/elasticache/user_group_test.go +++ b/internal/service/elasticache/user_group_test.go @@ -88,6 +88,24 @@ func TestAccElastiCacheUserGroup_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), ), }, + { + Config: testAccUserGroupConfig_engine_valkey(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserGroupExists(ctx, resourceName, &userGroup), + resource.TestCheckResourceAttr(resourceName, "user_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "user_group_id", rName), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + ), + }, + { + Config: testAccUserGroupConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserGroupExists(ctx, resourceName, &userGroup), + resource.TestCheckResourceAttr(resourceName, "user_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "user_group_id", rName), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), + ), + }, }, }) } @@ -260,6 +278,24 @@ resource "aws_elasticache_user_group" "test" { `, rName)) } +func testAccUserGroupConfig_engine_valkey(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +resource "aws_elasticache_user" "test1" { + user_id = "%[1]s-1" + user_name = "default" + access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember" + engine = "REDIS" + passwords = ["password123456789"] +} + +resource "aws_elasticache_user_group" "test" { + user_group_id = %[1]q + engine = "VALKEY" + user_ids = [aws_elasticache_user.test1.user_id] +} +`, rName)) +} + func testAccUserGroupConfig_tags1(rName, tagKey1, tagValue1 string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_elasticache_user" "test1" { diff --git a/internal/service/elasticache/user_test.go b/internal/service/elasticache/user_test.go index 726dcb513eb..a1e3cd2e932 100644 --- a/internal/service/elasticache/user_test.go +++ b/internal/service/elasticache/user_test.go @@ -167,6 +167,45 @@ func TestAccElastiCacheUser_update(t *testing.T) { }) } +func TestAccElastiCacheUser_update_engine(t *testing.T) { + ctx := acctest.Context(t) + var user awstypes.User + rName := sdkacctest.RandomWithPrefix("tf-acc") + resourceName := "aws_elasticache_user.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ElastiCacheServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccUserConfigWithEngine(rName, "VALKEY"), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserExists(ctx, resourceName, &user), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "valkey"), + ), + }, + { + Config: testAccUserConfigWithEngine(rName, "REDIS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserExists(ctx, resourceName, &user), + resource.TestCheckResourceAttr(resourceName, names.AttrEngine, "redis"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "no_password_required", + "passwords", + }, + }, + }, + }) +} + func TestAccElastiCacheUser_update_password_auth_mode(t *testing.T) { ctx := acctest.Context(t) var user awstypes.User @@ -467,6 +506,18 @@ resource "aws_elasticache_user" "test" { `, rName) } +func testAccUserConfigWithEngine(rName, engine string) string { + return fmt.Sprintf(` +resource "aws_elasticache_user" "test" { + user_id = %[1]q + user_name = "username1" + access_string = "on ~* +@all" + engine = %[2]q + passwords = ["password123456789"] +} +`, rName, engine) +} + func testAccUserConfigWithPasswordAuthMode_twoPasswords(rName string, password1 string, password2 string) string { return fmt.Sprintf(` resource "aws_elasticache_user" "test" { diff --git a/website/docs/r/elasticache_user.html.markdown b/website/docs/r/elasticache_user.html.markdown index e41676ebafd..96d63d6bd36 100644 --- a/website/docs/r/elasticache_user.html.markdown +++ b/website/docs/r/elasticache_user.html.markdown @@ -57,7 +57,7 @@ resource "aws_elasticache_user" "test" { The following arguments are required: * `access_string` - (Required) Access permissions string used for this user. See [Specifying Permissions Using an Access String](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Clusters.RBAC.html#Access-string) for more details. -* `engine` - (Required) The current supported value is `REDIS`. +* `engine` - (Required) The current supported values are `REDIS`, `VALKEY`. * `user_id` - (Required) The ID of the user. * `user_name` - (Required) The username of the user. diff --git a/website/docs/r/elasticache_user_group.html.markdown b/website/docs/r/elasticache_user_group.html.markdown index 3a94928644a..ca2deb929ba 100644 --- a/website/docs/r/elasticache_user_group.html.markdown +++ b/website/docs/r/elasticache_user_group.html.markdown @@ -32,7 +32,7 @@ resource "aws_elasticache_user_group" "test" { The following arguments are required: -* `engine` - (Required) The current supported value is `REDIS`. +* `engine` - (Required) The current supported value are `REDIS`, `VALKEY`. * `user_group_id` - (Required) The ID of the user group. The following arguments are optional: From 52e1c187cc83a996b6489a29e19e2d8cb29c6e2f Mon Sep 17 00:00:00 2001 From: Sasi Date: Fri, 3 Jan 2025 13:28:00 -0500 Subject: [PATCH 2/5] changelog file --- .changelog/40764.txt | 4 ++++ internal/service/elasticache/user_group_test.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changelog/40764.txt diff --git a/.changelog/40764.txt b/.changelog/40764.txt new file mode 100644 index 00000000000..90cb50de504 --- /dev/null +++ b/.changelog/40764.txt @@ -0,0 +1,4 @@ +```release-note:enhancement +resource/aws_elasticache_user: Add `VALKEY` as supported value for 'engine' argument. +resource/aws_elasticache_user_group: Add `VALKEY` as supported value for 'engine' argument. +``` \ No newline at end of file diff --git a/internal/service/elasticache/user_group_test.go b/internal/service/elasticache/user_group_test.go index 3e83e4c001d..22dd94d07a3 100644 --- a/internal/service/elasticache/user_group_test.go +++ b/internal/service/elasticache/user_group_test.go @@ -284,7 +284,7 @@ resource "aws_elasticache_user" "test1" { user_id = "%[1]s-1" user_name = "default" access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember" - engine = "REDIS" + engine = "VALKEY" passwords = ["password123456789"] } From ae392f557f1986d64f87e4f671926491c1c07693 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jan 2025 10:50:43 -0500 Subject: [PATCH 3/5] Tweak CHANGELOG entries. --- .changelog/40764.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.changelog/40764.txt b/.changelog/40764.txt index 90cb50de504..d362b3f62c7 100644 --- a/.changelog/40764.txt +++ b/.changelog/40764.txt @@ -1,4 +1,7 @@ ```release-note:enhancement -resource/aws_elasticache_user: Add `VALKEY` as supported value for 'engine' argument. -resource/aws_elasticache_user_group: Add `VALKEY` as supported value for 'engine' argument. +resource/aws_elasticache_user: Add `VALKEY` as supported value for 'engine' argument +``` + +```release-note:enhancement +resource/aws_elasticache_user_group: Add `VALKEY` as supported value for 'engine' argument ``` \ No newline at end of file From 1375c2220f522067d720d1e59786777a87f6a3d2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jan 2025 10:56:19 -0500 Subject: [PATCH 4/5] Cosmetics. --- internal/service/elasticache/user.go | 20 +++++++++---------- internal/service/elasticache/user_group.go | 20 +++++++++---------- .../service/elasticache/user_group_test.go | 4 ---- internal/service/elasticache/user_test.go | 12 ++++------- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/internal/service/elasticache/user.go b/internal/service/elasticache/user.go index 0a893aff1d4..87398d98965 100644 --- a/internal/service/elasticache/user.go +++ b/internal/service/elasticache/user.go @@ -6,7 +6,6 @@ package elasticache import ( "context" "log" - "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -21,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -88,12 +88,10 @@ func resourceUser() *schema.Resource { }, }, names.AttrEngine: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return strings.EqualFold(old, new) - }, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), + DiffSuppressFunc: sdkv2.SuppressEquivalentStringCaseInsensitive, }, "no_password_required": { Type: schema.TypeBool, @@ -244,14 +242,14 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf } } - if d.HasChange("no_password_required") { - input.NoPasswordRequired = aws.Bool(d.Get("no_password_required").(bool)) - } - if d.HasChange(names.AttrEngine) { input.Engine = aws.String(d.Get(names.AttrEngine).(string)) } + if d.HasChange("no_password_required") { + input.NoPasswordRequired = aws.Bool(d.Get("no_password_required").(bool)) + } + if d.HasChange("passwords") { input.Passwords = flex.ExpandStringValueSet(d.Get("passwords").(*schema.Set)) } diff --git a/internal/service/elasticache/user_group.go b/internal/service/elasticache/user_group.go index 62201443f7a..dc74c76ee64 100644 --- a/internal/service/elasticache/user_group.go +++ b/internal/service/elasticache/user_group.go @@ -6,7 +6,6 @@ package elasticache import ( "context" "log" - "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -48,12 +48,10 @@ func resourceUserGroup() *schema.Resource { Computed: true, }, names.AttrEngine: { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return strings.EqualFold(old, new) - }, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"REDIS", "VALKEY"}, false), + DiffSuppressFunc: sdkv2.SuppressEquivalentStringCaseInsensitive, }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), @@ -154,13 +152,15 @@ func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta i if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { input := &elasticache.ModifyUserGroupInput{ UserGroupId: aws.String(d.Get("user_group_id").(string)), - Engine: aws.String(d.Get(names.AttrEngine).(string)), + } + + if d.HasChange(names.AttrEngine) { + input.Engine = aws.String(d.Get(names.AttrEngine).(string)) } if d.HasChange("user_ids") { o, n := d.GetChange("user_ids") - del := o.(*schema.Set).Difference(n.(*schema.Set)) - add := n.(*schema.Set).Difference(o.(*schema.Set)) + add, del := n.(*schema.Set).Difference(o.(*schema.Set)), o.(*schema.Set).Difference(n.(*schema.Set)) if add.Len() > 0 { input.UserIdsToAdd = flex.ExpandStringValueSet(add) diff --git a/internal/service/elasticache/user_group_test.go b/internal/service/elasticache/user_group_test.go index 22dd94d07a3..a9128c738ee 100644 --- a/internal/service/elasticache/user_group_test.go +++ b/internal/service/elasticache/user_group_test.go @@ -208,10 +208,6 @@ func testAccCheckUserGroupExists(ctx context.Context, n string, v *awstypes.User return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ElastiCache User Group ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).ElastiCacheClient(ctx) output, err := tfelasticache.FindUserGroupByID(ctx, conn, rs.Primary.ID) diff --git a/internal/service/elasticache/user_test.go b/internal/service/elasticache/user_test.go index a1e3cd2e932..d27d5b56a55 100644 --- a/internal/service/elasticache/user_test.go +++ b/internal/service/elasticache/user_test.go @@ -56,7 +56,7 @@ func TestAccElastiCacheUser_basic(t *testing.T) { }) } -func TestAccElastiCacheUser_password_auth_mode(t *testing.T) { +func TestAccElastiCacheUser_passwordAuthMode(t *testing.T) { ctx := acctest.Context(t) var user awstypes.User rName := sdkacctest.RandomWithPrefix("tf-acc") @@ -95,7 +95,7 @@ func TestAccElastiCacheUser_password_auth_mode(t *testing.T) { }) } -func TestAccElastiCacheUser_iam_auth_mode(t *testing.T) { +func TestAccElastiCacheUser_iamAuthMode(t *testing.T) { ctx := acctest.Context(t) var user awstypes.User rName := sdkacctest.RandomWithPrefix("tf-acc") @@ -167,7 +167,7 @@ func TestAccElastiCacheUser_update(t *testing.T) { }) } -func TestAccElastiCacheUser_update_engine(t *testing.T) { +func TestAccElastiCacheUser_updateEngine(t *testing.T) { ctx := acctest.Context(t) var user awstypes.User rName := sdkacctest.RandomWithPrefix("tf-acc") @@ -206,7 +206,7 @@ func TestAccElastiCacheUser_update_engine(t *testing.T) { }) } -func TestAccElastiCacheUser_update_password_auth_mode(t *testing.T) { +func TestAccElastiCacheUser_updatePasswordAuthMode(t *testing.T) { ctx := acctest.Context(t) var user awstypes.User rName := sdkacctest.RandomWithPrefix("tf-acc") @@ -420,10 +420,6 @@ func testAccCheckUserExists(ctx context.Context, n string, v *awstypes.User) res return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No ElastiCache User ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).ElastiCacheClient(ctx) output, err := tfelasticache.FindUserByID(ctx, conn, rs.Primary.ID) From a5ee591122116486c1809d80e1df1ed7b37ea68a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Jan 2025 11:47:07 -0500 Subject: [PATCH 5/5] Fix 'testAccUserGroupConfig_engineValkey': 'operation error ElastiCache: ModifyUser, https response error StatusCode: 400, RequestID: a7c03416-8677-419b-a6fa-274072ab6636, InvalidParameterCombination: The user is associated to Redis user group tf-acc-6578360618284093375, please modify the engine of the user group before modifying the engine of the user'. --- internal/service/elasticache/user_group_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/elasticache/user_group_test.go b/internal/service/elasticache/user_group_test.go index a9128c738ee..31957ce2e9f 100644 --- a/internal/service/elasticache/user_group_test.go +++ b/internal/service/elasticache/user_group_test.go @@ -89,7 +89,7 @@ func TestAccElastiCacheUserGroup_update(t *testing.T) { ), }, { - Config: testAccUserGroupConfig_engine_valkey(rName), + Config: testAccUserGroupConfig_engineValkey(rName), Check: resource.ComposeTestCheckFunc( testAccCheckUserGroupExists(ctx, resourceName, &userGroup), resource.TestCheckResourceAttr(resourceName, "user_ids.#", "1"), @@ -274,13 +274,13 @@ resource "aws_elasticache_user_group" "test" { `, rName)) } -func testAccUserGroupConfig_engine_valkey(rName string) string { +func testAccUserGroupConfig_engineValkey(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_elasticache_user" "test1" { user_id = "%[1]s-1" user_name = "default" access_string = "on ~app::* -@all +@read +@hash +@bitmap +@geo -setbit -bitfield -hset -hsetnx -hmset -hincrby -hincrbyfloat -hdel -bitop -geoadd -georadius -georadiusbymember" - engine = "VALKEY" + engine = "REDIS" passwords = ["password123456789"] }