Skip to content

Commit 6ac9a7d

Browse files
committed
fix: order insensitive string slice comparisons
1 parent 53c44c0 commit 6ac9a7d

File tree

1 file changed

+115
-14
lines changed

1 file changed

+115
-14
lines changed

provider/resource_rediscloud_active_active_database_helpers.go

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package provider
22

33
import (
44
"context"
5+
"sort"
56

67
"github.com/RedisLabs/rediscloud-go-api/redis"
78
"github.com/RedisLabs/rediscloud-go-api/service/databases"
@@ -198,79 +199,162 @@ func filterDefaultSourceIPs(apiSourceIPs []*string) []string {
198199
}
199200

200201
// addSourceIPsIfOverridden adds override_global_source_ips to region config if it differs from global.
201-
func addSourceIPsIfOverridden(regionDbConfig map[string]interface{}, d *schema.ResourceData, regionDb *databases.CrdbDatabase) {
202+
func addSourceIPsIfOverridden(ctx context.Context, regionDbConfig map[string]interface{}, d *schema.ResourceData, regionDb *databases.CrdbDatabase, region string) {
202203
sourceIPs := filterDefaultSourceIPs(regionDb.Security.SourceIPs)
204+
205+
tflog.Debug(ctx, "Read: addSourceIPsIfOverridden", map[string]interface{}{
206+
"region": region,
207+
"apiSourceIPsCount": len(regionDb.Security.SourceIPs),
208+
"filteredIPsCount": len(sourceIPs),
209+
"filteredIPs": sourceIPs,
210+
})
211+
203212
if len(sourceIPs) == 0 {
213+
tflog.Debug(ctx, "Read: Skipping source IPs (filtered to empty)", map[string]interface{}{"region": region})
204214
return
205215
}
206216

207217
globalSourceIPsPtrs := utils.SetToStringSlice(d.Get("global_source_ips").(*schema.Set))
208218
globalSourceIPs := redis.StringSliceValue(globalSourceIPsPtrs...)
209219

210-
if !stringSlicesEqual(sourceIPs, globalSourceIPs) {
220+
shouldAdd := !stringSlicesEqual(sourceIPs, globalSourceIPs)
221+
tflog.Debug(ctx, "Read: Source IPs comparison", map[string]interface{}{
222+
"region": region,
223+
"regionIPs": sourceIPs,
224+
"globalIPs": globalSourceIPs,
225+
"shouldAdd": shouldAdd,
226+
})
227+
228+
if shouldAdd {
211229
regionDbConfig["override_global_source_ips"] = sourceIPs
212230
}
213231
}
214232

215233
// addDataPersistenceIfOverridden adds override_global_data_persistence to region config if it differs from global.
216234
func addDataPersistenceIfOverridden(
235+
ctx context.Context,
217236
regionDbConfig map[string]interface{},
218237
db *databases.ActiveActiveDatabase,
219238
regionDb *databases.CrdbDatabase,
239+
region string,
220240
) {
221241
if regionDb.DataPersistence != nil && db.GlobalDataPersistence != nil {
222-
if redis.StringValue(regionDb.DataPersistence) != redis.StringValue(db.GlobalDataPersistence) {
242+
regionValue := redis.StringValue(regionDb.DataPersistence)
243+
globalValue := redis.StringValue(db.GlobalDataPersistence)
244+
shouldAdd := regionValue != globalValue
245+
246+
tflog.Debug(ctx, "Read: Data persistence comparison", map[string]interface{}{
247+
"region": region,
248+
"regionValue": regionValue,
249+
"globalValue": globalValue,
250+
"shouldAdd": shouldAdd,
251+
})
252+
253+
if shouldAdd {
223254
regionDbConfig["override_global_data_persistence"] = regionDb.DataPersistence
224255
}
256+
} else {
257+
tflog.Debug(ctx, "Read: Skipping data persistence (nil values)", map[string]interface{}{
258+
"region": region,
259+
"regionIsNil": regionDb.DataPersistence == nil,
260+
"globalIsNil": db.GlobalDataPersistence == nil,
261+
})
225262
}
226263
}
227264

228265
// addPasswordIfOverridden adds override_global_password to region config if it differs from global.
229266
func addPasswordIfOverridden(
267+
ctx context.Context,
230268
regionDbConfig map[string]interface{},
231269
db *databases.ActiveActiveDatabase,
232270
regionDb *databases.CrdbDatabase,
271+
region string,
233272
) {
234273
if regionDb.Security.Password != nil && db.GlobalPassword != nil {
235-
if *regionDb.Security.Password != redis.StringValue(db.GlobalPassword) {
236-
regionDbConfig["override_global_password"] = redis.StringValue(regionDb.Security.Password)
274+
regionValue := *regionDb.Security.Password
275+
globalValue := redis.StringValue(db.GlobalPassword)
276+
shouldAdd := regionValue != globalValue
277+
278+
tflog.Debug(ctx, "Read: Password comparison", map[string]interface{}{
279+
"region": region,
280+
"regionValue": "[REDACTED]",
281+
"globalValue": "[REDACTED]",
282+
"valuesDiffer": shouldAdd,
283+
"shouldAdd": shouldAdd,
284+
})
285+
286+
if shouldAdd {
287+
regionDbConfig["override_global_password"] = regionValue
237288
}
289+
} else {
290+
tflog.Debug(ctx, "Read: Skipping password (nil values)", map[string]interface{}{
291+
"region": region,
292+
"regionIsNil": regionDb.Security.Password == nil,
293+
"globalIsNil": db.GlobalPassword == nil,
294+
})
238295
}
239296
}
240297

241298
// addAlertsIfOverridden adds override_global_alert to region config if count differs from global.
242299
// Note: Active-Active API doesn't return global alerts separately, so we compare counts.
243300
func addAlertsIfOverridden(
301+
ctx context.Context,
244302
regionDbConfig map[string]interface{},
245303
d *schema.ResourceData,
246304
regionDb *databases.CrdbDatabase,
305+
region string,
247306
) {
248307
globalAlerts := d.Get("global_alert").(*schema.Set).List()
249308
regionAlerts := pro.FlattenAlerts(regionDb.Alerts)
309+
shouldAdd := len(globalAlerts) != len(regionAlerts)
310+
311+
tflog.Debug(ctx, "Read: Alerts comparison", map[string]interface{}{
312+
"region": region,
313+
"globalAlertsCount": len(globalAlerts),
314+
"globalAlerts": globalAlerts,
315+
"regionAlertsCount": len(regionAlerts),
316+
"regionAlerts": regionAlerts,
317+
"shouldAdd": shouldAdd,
318+
})
250319

251-
if len(globalAlerts) != len(regionAlerts) {
320+
if shouldAdd {
252321
regionDbConfig["override_global_alert"] = regionAlerts
253322
}
254323
}
255324

256325
// addRemoteBackupIfConfigured adds remote_backup to region config if it exists in both API and state.
257326
func addRemoteBackupIfConfigured(
327+
ctx context.Context,
258328
regionDbConfig map[string]interface{},
259329
regionDb *databases.CrdbDatabase,
260330
stateOverrideRegion map[string]interface{},
331+
region string,
261332
) {
333+
tflog.Debug(ctx, "Read: Checking remote backup", map[string]interface{}{
334+
"region": region,
335+
"apiHasBackup": regionDb.Backup != nil,
336+
})
337+
262338
if regionDb.Backup == nil {
339+
tflog.Debug(ctx, "Read: Skipping remote backup (nil in API)", map[string]interface{}{"region": region})
263340
return
264341
}
265342

266343
stateRemoteBackup := stateOverrideRegion["remote_backup"]
267344
if stateRemoteBackup == nil {
345+
tflog.Debug(ctx, "Read: Skipping remote backup (nil in state)", map[string]interface{}{"region": region})
268346
return
269347
}
270348

271349
stateRemoteBackupList := stateRemoteBackup.([]interface{})
350+
tflog.Debug(ctx, "Read: Remote backup state list", map[string]interface{}{
351+
"region": region,
352+
"listCount": len(stateRemoteBackupList),
353+
})
354+
272355
if len(stateRemoteBackupList) > 0 {
273356
regionDbConfig["remote_backup"] = pro.FlattenBackupPlan(regionDb.Backup, stateRemoteBackupList, "")
357+
tflog.Debug(ctx, "Read: Added remote_backup to region config", map[string]interface{}{"region": region})
274358
}
275359
}
276360

@@ -323,30 +407,47 @@ func logRegionConfigBuilt(ctx context.Context, region string, regionDbConfig map
323407
// buildRegionConfigFromAPIAndState orchestrates building region config from API and state.
324408
// Each override field is handled by a dedicated helper function for clarity and maintainability.
325409
func buildRegionConfigFromAPIAndState(ctx context.Context, d *schema.ResourceData, db *databases.ActiveActiveDatabase, region string, regionDb *databases.CrdbDatabase, stateOverrideRegion map[string]interface{}) map[string]interface{} {
410+
tflog.Debug(ctx, "Read: Starting buildRegionConfigFromAPIAndState", map[string]interface{}{
411+
"region": region,
412+
})
413+
326414
regionDbConfig := map[string]interface{}{
327415
"name": region,
328416
}
329417

330418
// Handle each override field using dedicated helper functions
331-
addSourceIPsIfOverridden(regionDbConfig, d, regionDb)
332-
addDataPersistenceIfOverridden(regionDbConfig, db, regionDb)
333-
addPasswordIfOverridden(regionDbConfig, db, regionDb)
334-
addAlertsIfOverridden(regionDbConfig, d, regionDb)
335-
addRemoteBackupIfConfigured(regionDbConfig, regionDb, stateOverrideRegion)
419+
addSourceIPsIfOverridden(ctx, regionDbConfig, d, regionDb, region)
420+
addDataPersistenceIfOverridden(ctx, regionDbConfig, db, regionDb, region)
421+
addPasswordIfOverridden(ctx, regionDbConfig, db, regionDb, region)
422+
addAlertsIfOverridden(ctx, regionDbConfig, d, regionDb, region)
423+
addRemoteBackupIfConfigured(ctx, regionDbConfig, regionDb, stateOverrideRegion, region)
336424
addEnableDefaultUserIfNeeded(ctx, regionDbConfig, d, db, region, regionDb)
337425

338426
logRegionConfigBuilt(ctx, region, regionDbConfig)
339427

340428
return regionDbConfig
341429
}
342430

343-
// stringSlicesEqual compares two string slices for equality (order matters)
431+
// stringSlicesEqual compares two string slices for equality (order-insensitive).
432+
// This is used for comparing source IP lists where order doesn't matter.
344433
func stringSlicesEqual(a, b []string) bool {
345434
if len(a) != len(b) {
346435
return false
347436
}
348-
for i := range a {
349-
if a[i] != b[i] {
437+
438+
// Make copies to avoid modifying the original slices
439+
aCopy := make([]string, len(a))
440+
bCopy := make([]string, len(b))
441+
copy(aCopy, a)
442+
copy(bCopy, b)
443+
444+
// Sort both copies
445+
sort.Strings(aCopy)
446+
sort.Strings(bCopy)
447+
448+
// Compare sorted slices
449+
for i := range aCopy {
450+
if aCopy[i] != bCopy[i] {
350451
return false
351452
}
352453
}

0 commit comments

Comments
 (0)