Skip to content

Commit d79ce04

Browse files
Merge pull request #693 from RedisLabs/fix-errors
Bugfixes
2 parents d423839 + a47ce34 commit d79ce04

9 files changed

+156
-41
lines changed

.github/workflows/terraform_provider_pr.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ jobs:
126126

127127

128128
go_test_smoke_essentials_db:
129+
if: false # Temporarily disabled - waiting on client fixes
129130
name: go test smoke essentials db
130131
needs: go_test_smoke_essentials_sub
131132
runs-on: ubuntu-latest
@@ -193,6 +194,17 @@ jobs:
193194
go-version-file: go.mod
194195
- run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAcc(RedisCloudProDatabaseBlockPublicEndpoints|ActiveActiveSubscriptionDatabaseBlockPublicEndpoints)"'
195196

197+
go_test_smoke_qpf:
198+
name: go test smoke query performance factor
199+
needs: [ go_build ]
200+
runs-on: ubuntu-latest
201+
steps:
202+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
203+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
204+
with:
205+
go-version-file: go.mod
206+
- run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAccResourceRedisCloudProDatabase_qpf"'
207+
196208
go_unit_test:
197209
name: go unit test
198210
needs: [go_build]

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
All notable changes to this project will be documented in this file.
44
See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/)
55

6+
7+
# 2.7.1 (27th October 2025)
8+
9+
## Fixed
10+
- rediscloud_subscription_database: The query_performance_factor attribute can now be updated in-place without recreating the database. Previously, any changes to this attribute would force resource replacement.
11+
- rediscloud_subscription_database (Redis 8.0+): Fixed drift detection issues where explicitly configured modules would incorrectly show as changes requiring resource replacement after upgrading to Redis 8.0 or higher. Modules are bundled
12+
by default in Redis 8.0+, so configuration differences are now properly suppressed.
13+
- rediscloud_subscription_database (Redis 8.0+): The warning for modules has been made more prominent.
14+
- Support for a new pending status for subscription and database updates.
15+
- Test Suite: Fixed incorrect file path references in acceptance tests.
16+
617
# 2.7.0 (22nd October 2025)
718

819
## Added:

provider/pro/resource_rediscloud_pro_database.go

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ func ResourceRedisCloudProDatabase() *schema.Resource {
230230
Type: schema.TypeString,
231231
Optional: true,
232232
Computed: true,
233-
ForceNew: true,
234233
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
235234
v := val.(string)
236235
matched, err := regexp.MatchString(`^([2468])x$`, v)
@@ -251,8 +250,9 @@ func ResourceRedisCloudProDatabase() *schema.Resource {
251250
ConfigMode: schema.SchemaConfigModeAttr,
252251
Optional: true,
253252
// The API doesn't allow updating/delete modules. Unless we recreate the database.
254-
ForceNew: true,
255-
MinItems: 1,
253+
ForceNew: true,
254+
MinItems: 1,
255+
DiffSuppressFunc: modulesDiffSuppressFunc,
256256
Elem: &schema.Resource{
257257
Schema: map[string]*schema.Schema{
258258
"name": {
@@ -418,6 +418,17 @@ func resourceRedisCloudProDatabaseCreate(ctx context.Context, d *schema.Resource
418418
createDatabase.RedisVersion = s
419419
})
420420

421+
// Warn if modules are explicitly configured for Redis 8.0+
422+
var diags diag.Diagnostics
423+
redisVersion := d.Get("redis_version").(string)
424+
if shouldWarnRedis8Modules(redisVersion, len(createModules) > 0) {
425+
diags = append(diags, diag.Diagnostic{
426+
Severity: diag.Warning,
427+
Summary: "Modules are bundled by default in Redis 8.0+",
428+
Detail: fmt.Sprintf("The 'modules' block is deprecated for Redis %s and later versions, as modules (RediSearch, RedisJSON, RedisBloom, RedisTimeSeries) are bundled by default. You should remove the 'modules' block from your configuration.", redisVersion),
429+
})
430+
}
431+
421432
utils.SetStringIfNotEmpty(d, "password", func(s *string) {
422433
createDatabase.Password = s
423434
})
@@ -449,31 +460,32 @@ func resourceRedisCloudProDatabaseCreate(ctx context.Context, d *schema.Resource
449460
// Confirm sub is ready to accept a db request
450461
if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil {
451462
utils.SubscriptionMutex.Unlock(subId)
452-
return diag.FromErr(err)
463+
return append(diags, diag.FromErr(err)...)
453464
}
454465

455466
dbId, err := api.Client.Database.Create(ctx, subId, createDatabase)
456467
if err != nil {
457468
utils.SubscriptionMutex.Unlock(subId)
458-
return diag.FromErr(err)
469+
return append(diags, diag.FromErr(err)...)
459470
}
460471

461472
d.SetId(utils.BuildResourceId(subId, dbId))
462473

463474
// Confirm db + sub active status
464475
if err := utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil {
465476
utils.SubscriptionMutex.Unlock(subId)
466-
return diag.FromErr(err)
477+
return append(diags, diag.FromErr(err)...)
467478
}
468479
if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil {
469480
utils.SubscriptionMutex.Unlock(subId)
470-
return diag.FromErr(err)
481+
return append(diags, diag.FromErr(err)...)
471482
}
472483

473484
// Some attributes on a database are not accessible by the subscription creation API.
474485
// Run the subscription update function to apply any additional changes to the databases, such as password, enableDefaultUser and so on.
475486
utils.SubscriptionMutex.Unlock(subId)
476-
return resourceRedisCloudProDatabaseUpdate(ctx, d, meta)
487+
updateDiags := resourceRedisCloudProDatabaseUpdate(ctx, d, meta)
488+
return append(diags, updateDiags...)
477489
}
478490

479491
func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -559,7 +571,7 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa
559571
// For Redis 8.0+, modules are bundled by default and returned by the API
560572
// Only set modules in state if they were explicitly defined in the config
561573
redisVersion := redis.StringValue(db.RedisVersion)
562-
if redisVersion >= "8.0" {
574+
if shouldSuppressModuleDiffsForRedis8(redisVersion) {
563575
// Only set modules if they were explicitly configured by the user
564576
if _, ok := d.GetOk("modules"); ok {
565577
if err := d.Set("modules", FlattenModules(db.Modules)); err != nil {
@@ -714,6 +726,7 @@ func resourceRedisCloudProDatabaseDelete(ctx context.Context, d *schema.Resource
714726

715727
func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
716728
api := meta.(*client.ApiClient)
729+
var diags diag.Diagnostics
717730

718731
_, dbId, err := ToDatabaseId(d.Id())
719732
if err != nil {
@@ -821,7 +834,7 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource
821834
update.ClientSSLCertificate = redis.String(clientSSLCertificate)
822835
} else if len(clientTLSCertificates) > 0 {
823836
utils.SubscriptionMutex.Unlock(subId)
824-
return diag.Errorf("TLS certificates may not be provided while enable_tls is false")
837+
return append(diags, diag.Errorf("TLS certificates may not be provided while enable_tls is false")...)
825838
} else {
826839
// Default: enable_tls=false, client_ssl_certificate=""
827840
update.EnableTls = redis.Bool(enableTLS)
@@ -845,14 +858,25 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource
845858
update.RespVersion = redis.String(respVersion)
846859
}
847860

861+
// Warn if modules are explicitly configured for Redis 8.0+
862+
redisVersion := d.Get("redis_version").(string)
863+
modules := d.Get("modules").(*schema.Set)
864+
if shouldWarnRedis8Modules(redisVersion, modules.Len() > 0) {
865+
diags = append(diags, diag.Diagnostic{
866+
Severity: diag.Warning,
867+
Summary: "Modules are bundled by default in Redis 8.0+",
868+
Detail: fmt.Sprintf("The 'modules' block is deprecated for Redis %s and later versions, as modules (RediSearch, RedisJSON, RedisBloom, RedisTimeSeries) are bundled by default. You should remove the 'modules' block from your configuration.", redisVersion),
869+
})
870+
}
871+
848872
// Confirm sub + db are ready to accept a db request
849873
if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil {
850874
utils.SubscriptionMutex.Unlock(subId)
851-
return diag.FromErr(err)
875+
return append(diags, diag.FromErr(err)...)
852876
}
853877
if err := utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil {
854878
utils.SubscriptionMutex.Unlock(subId)
855-
return diag.FromErr(err)
879+
return append(diags, diag.FromErr(err)...)
856880
}
857881

858882
// if redis_version has changed, then upgrade first
@@ -863,11 +887,11 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource
863887
// if either version is blank, it could attempt to upgrade unnecessarily.
864888
// only upgrade when a known version goes to another known version
865889
if originalVersion.(string) != "" && newVersion.(string) != "" {
866-
if diags, unlocked := upgradeRedisVersion(ctx, api, subId, dbId, newVersion.(string)); diags != nil {
890+
if upgradeDiags, unlocked := upgradeRedisVersion(ctx, api, subId, dbId, newVersion.(string)); upgradeDiags != nil {
867891
if !unlocked {
868892
utils.SubscriptionMutex.Unlock(subId)
869893
}
870-
return diags
894+
return append(diags, upgradeDiags...)
871895
}
872896
}
873897
}
@@ -876,26 +900,27 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource
876900

877901
if err := api.Client.Database.Update(ctx, subId, dbId, update); err != nil {
878902
utils.SubscriptionMutex.Unlock(subId)
879-
return diag.FromErr(err)
903+
return append(diags, diag.FromErr(err)...)
880904
}
881905

882906
// Confirm db + sub active status
883907
if err := utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil {
884908
utils.SubscriptionMutex.Unlock(subId)
885-
return diag.FromErr(err)
909+
return append(diags, diag.FromErr(err)...)
886910
}
887911
if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil {
888912
utils.SubscriptionMutex.Unlock(subId)
889-
return diag.FromErr(err)
913+
return append(diags, diag.FromErr(err)...)
890914
}
891915

892916
// The Tags API is synchronous so we shouldn't have to wait for anything
893917
if err := WriteTags(ctx, api, subId, dbId, d); err != nil {
894-
return diag.FromErr(err)
918+
return append(diags, diag.FromErr(err)...)
895919
}
896920

897921
utils.SubscriptionMutex.Unlock(subId)
898-
return resourceRedisCloudProDatabaseRead(ctx, d, meta)
922+
readDiags := resourceRedisCloudProDatabaseRead(ctx, d, meta)
923+
return append(diags, readDiags...)
899924
}
900925

901926
func upgradeRedisVersion(ctx context.Context, api *client.ApiClient, subId int, dbId int, newVersion string) (diag.Diagnostics, bool) {
@@ -1068,19 +1093,49 @@ func shouldWarnRedis8Modules(version string, hasModules bool) bool {
10681093
return false
10691094
}
10701095

1096+
// shouldSuppressModuleDiffsForRedis8 checks if module diffs should be suppressed for Redis 8.0 or higher
1097+
// In Redis 8.0+, modules are bundled by default, so we should ignore changes to explicitly configured modules
1098+
func shouldSuppressModuleDiffsForRedis8(version string) bool {
1099+
if len(version) == 0 {
1100+
return false
1101+
}
1102+
majorVersionStr := strings.Split(version, ".")[0]
1103+
if majorVersion, err := strconv.Atoi(majorVersionStr); err == nil {
1104+
return majorVersion >= 8
1105+
}
1106+
return false
1107+
}
1108+
1109+
// modulesDiffSuppressFunc returns a DiffSuppressFunc that suppresses module diffs for Redis 8.0+
1110+
// This prevents Terraform from showing module changes as "forces replacement" when upgrading to Redis 8.0+
1111+
func modulesDiffSuppressFunc(k, oldValue, newValue string, d *schema.ResourceData) bool {
1112+
redisVersion, ok := d.GetOk("redis_version")
1113+
if !ok {
1114+
return false
1115+
}
1116+
version := redisVersion.(string)
1117+
return shouldSuppressModuleDiffsForRedis8(version)
1118+
}
1119+
10711120
func validateModulesForRedis8() schema.CustomizeDiffFunc {
10721121
return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error {
1073-
redisVersion, versionExists := diff.GetOk("redis_version")
1074-
modules, modulesExists := diff.GetOkExists("modules")
1122+
// Check if modules are configured for Redis 8.0+
1123+
redisVersionRaw, ok := diff.GetOk("redis_version")
1124+
if !ok {
1125+
return nil
1126+
}
1127+
redisVersion := redisVersionRaw.(string)
10751128

1076-
if versionExists && modulesExists {
1077-
version := redisVersion.(string)
1078-
moduleSet := modules.(*schema.Set)
1129+
modulesRaw, ok := diff.GetOk("modules")
1130+
if !ok {
1131+
return nil
1132+
}
1133+
modules := modulesRaw.(*schema.Set)
10791134

1080-
if shouldWarnRedis8Modules(version, moduleSet.Len() > 0) {
1081-
log.Printf("[WARN] Modules are bundled by default in Redis %s. You should remove the modules block as it is deprecated for this version.", version)
1082-
}
1135+
if shouldWarnRedis8Modules(redisVersion, modules.Len() > 0) {
1136+
log.Printf("[WARN] Modules are bundled by default in Redis %s and later versions. The 'modules' block is deprecated for Redis 8.0+ as modules (RediSearch, RedisJSON, RedisBloom, RedisTimeSeries) are bundled by default. You should remove the 'modules' block from your configuration.", redisVersion)
10831137
}
1138+
10841139
return nil
10851140
}
10861141
}

provider/pro/resource_rediscloud_pro_database_validation_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,33 @@ func TestUnitShouldWarnRedis8Modules_Redis10WithModules(t *testing.T) {
6565
result := shouldWarnRedis8Modules("10.0.0", true)
6666
assert.True(t, result, "should warn for Redis 10.0.0 with modules (modules bundled in 8.0+)")
6767
}
68+
69+
// TestUnitShouldSuppressModuleDiffsForRedis8_Redis8 tests that module diffs are suppressed for Redis 8.0
70+
func TestUnitShouldSuppressModuleDiffsForRedis8_Redis8(t *testing.T) {
71+
result := shouldSuppressModuleDiffsForRedis8("8.0")
72+
assert.True(t, result, "should suppress module diffs for Redis 8.0")
73+
}
74+
75+
// TestUnitShouldSuppressModuleDiffsForRedis8_Redis82 tests that module diffs are suppressed for Redis 8.2
76+
func TestUnitShouldSuppressModuleDiffsForRedis8_Redis82(t *testing.T) {
77+
result := shouldSuppressModuleDiffsForRedis8("8.2")
78+
assert.True(t, result, "should suppress module diffs for Redis 8.2")
79+
}
80+
81+
// TestUnitShouldSuppressModuleDiffsForRedis8_Redis9 tests that module diffs are suppressed for Redis 9.0
82+
func TestUnitShouldSuppressModuleDiffsForRedis8_Redis9(t *testing.T) {
83+
result := shouldSuppressModuleDiffsForRedis8("9.0")
84+
assert.True(t, result, "should suppress module diffs for Redis 9.0")
85+
}
86+
87+
// TestUnitShouldSuppressModuleDiffsForRedis8_Redis7 tests that module diffs are NOT suppressed for Redis 7.x
88+
func TestUnitShouldSuppressModuleDiffsForRedis8_Redis7(t *testing.T) {
89+
result := shouldSuppressModuleDiffsForRedis8("7.4")
90+
assert.False(t, result, "should not suppress module diffs for Redis 7.4")
91+
}
92+
93+
// TestUnitShouldSuppressModuleDiffsForRedis8_Redis6 tests that module diffs are NOT suppressed for Redis 6.x
94+
func TestUnitShouldSuppressModuleDiffsForRedis8_Redis6(t *testing.T) {
95+
result := shouldSuppressModuleDiffsForRedis8("6.2")
96+
assert.False(t, result, "should not suppress module diffs for Redis 6.2")
97+
}

provider/pro/resource_rediscloud_pro_subscription.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,18 @@ func ResourceRedisCloudProSubscription() *schema.Resource {
371371
return false
372372
}
373373

374-
if old != new {
375-
// The user is requesting a change
376-
return false
374+
// Suppress diff if user removes the deprecated attribute
375+
if new == "" {
376+
return true
377377
}
378378

379-
return true
379+
// Suppress diff if no actual change
380+
if old == new {
381+
return true
382+
}
383+
384+
// User is requesting a version change - don't suppress
385+
return false
380386
},
381387
},
382388
"maintenance_windows": {

provider/rediscloud_active_active_private_link_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1111
)
1212

13-
const testActiveActivePrivateLinkConfigFile = "../privatelink/testdata/active_active_private_link.tf"
13+
const testActiveActivePrivateLinkConfigFile = "./privatelink/testdata/active_active_private_link.tf"
1414

1515
func TestAccResourceRedisCloudActiveActivePrivateLink_CRUDI(t *testing.T) {
1616

provider/rediscloud_active_active_subscription_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -486,21 +486,21 @@ resource "rediscloud_active_active_subscription" "example" {
486486
`
487487

488488
func testAccResourceRedisCloudActiveActiveSubscription(t *testing.T, subscriptionName string) string {
489-
content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscription.tf")
489+
content := utils.GetTestConfig(t, "./activeactive/testdata/active_active_sub.tf")
490490
return fmt.Sprintf(content, subscriptionName)
491491
}
492492

493493
func testAccResourceRedisCloudActiveActiveSubscriptionUpdate(t *testing.T, subscriptionName string, cloudProvider string) string {
494-
content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscriptionUpdate.tf")
494+
content := utils.GetTestConfig(t, "./activeactive/testdata/subscription_update.tf")
495495
return fmt.Sprintf(content, subscriptionName, cloudProvider)
496496
}
497497

498498
func testAccResourceRedisCloudActiveActiveSubscriptionPublicEndpointDisabled(t *testing.T, subscriptionName string) string {
499-
content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscription_PublicEndpointDisabled.tf")
499+
content := utils.GetTestConfig(t, "./activeactive/testdata/public_endpoint_disabled.tf")
500500
return fmt.Sprintf(content, subscriptionName)
501501
}
502502

503503
func testAccResourceRedisCloudActiveActiveSubscriptionPublicEndpointEnabled(t *testing.T, subscriptionName string) string {
504-
content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscription_PublicEndpointEnabled.tf")
504+
content := utils.GetTestConfig(t, "./activeactive/testdata/public_endpoint_enabled.tf")
505505
return fmt.Sprintf(content, subscriptionName)
506506
}

provider/resource_rediscloud_pro_database_qpf_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,11 @@ func TestAccResourceRedisCloudProDatabase_qpf(t *testing.T) {
130130
),
131131
},
132132

133-
// Test plan to ensure query_performance_factor change forces a new resource
133+
// Test that query_performance_factor can be updated without forcing replacement
134134
{
135-
Config: formatDatabaseConfig(name, testCloudAccountName, password, "2x", `modules = [{ name = "RediSearch" }]`),
136-
PlanOnly: true, // Runs terraform plan without applying
137-
ExpectNonEmptyPlan: true, // Ensures that a change is detected
138-
Check: resource.ComposeTestCheckFunc(
135+
Config: formatDatabaseConfig(name, testCloudAccountName, password, "2x", `modules = [{ name = "RediSearch" }]`),
136+
Check: resource.ComposeAggregateTestCheckFunc(
137+
resource.TestCheckResourceAttr("rediscloud_subscription_database.example", "name", "example"),
139138
resource.TestCheckResourceAttr("rediscloud_subscription_database.example", "query_performance_factor", "2x"),
140139
),
141140
},

provider/utils/wait.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ func WaitForDatabaseToBeActive(ctx context.Context, subId, id int, api *client.A
5252
databases.StatusProxyPolicyChangeDraft,
5353
databases.StatusDynamicEndpointsCreationPending,
5454
databases.StatusActiveUpgradePending,
55+
"bdb-update-pending", // Database update in progress.
56+
// TODO replace with api model string in next release
5557
},
5658
Target: []string{databases.StatusActive},
5759
Timeout: SafetyTimeout,

0 commit comments

Comments
 (0)