From 4f232a51305fa2dc2bce0dd6567e39c9e6ee8e6d Mon Sep 17 00:00:00 2001 From: Matthew Long Date: Fri, 7 Nov 2025 15:42:38 +0000 Subject: [PATCH 1/5] revert: activeactive database reverted to 2.7.2 behaviour --- .github/workflows/terraform_provider_pr.yml | 72 ++- .../enable_default_user_all_explicit.tf | 54 -- ...e_default_user_global_false_region_true.tf | 53 -- ...enable_default_user_global_true_inherit.tf | 52 -- .../enable_default_user_mixed_overrides.tf | 53 -- ...tasource_rediscloud_essentials_database.go | 1 - .../pro/resource_rediscloud_pro_database.go | 24 +- ...ase_enable_default_user_acceptance_test.go | 116 ----- .../rediscloud_active_active_database_test.go | 1 - ...ource_rediscloud_active_active_database.go | 173 +++---- ...ctive_database_enable_default_user_test.go | 178 ------- ...discloud_active_active_database_helpers.go | 473 ------------------ .../resource_rediscloud_pro_database_test.go | 1 - 13 files changed, 119 insertions(+), 1132 deletions(-) delete mode 100644 provider/activeactive/testdata/enable_default_user_all_explicit.tf delete mode 100644 provider/activeactive/testdata/enable_default_user_global_false_region_true.tf delete mode 100644 provider/activeactive/testdata/enable_default_user_global_true_inherit.tf delete mode 100644 provider/activeactive/testdata/enable_default_user_mixed_overrides.tf delete mode 100644 provider/rediscloud_active_active_database_enable_default_user_acceptance_test.go delete mode 100644 provider/resource_rediscloud_active_active_database_enable_default_user_test.go delete mode 100644 provider/resource_rediscloud_active_active_database_helpers.go diff --git a/.github/workflows/terraform_provider_pr.yml b/.github/workflows/terraform_provider_pr.yml index bed3bffd..9d002ab5 100644 --- a/.github/workflows/terraform_provider_pr.yml +++ b/.github/workflows/terraform_provider_pr.yml @@ -56,28 +56,6 @@ jobs: name: go build run: go build -o terraform-plugin-dir/registry.terraform.io/RedisLabs/rediscloud/99.99.99/$(go env GOOS)_$(go env GOARCH)/terraform-provider-rediscloud . - go_unit_test: - name: go unit test - needs: [go_build] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: go.mod - - run: go test ./... -run="^TestUnit" # Runs tests starting with TestUnit - - tfproviderlint: - name: tfproviderlint - needs: [go_build] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: go.mod - - run: make tfproviderlint - terraform_providers_schema: name: terraform providers schema needs: [go_build] @@ -124,7 +102,6 @@ jobs: mkdir terraform-providers-schema terraform providers schema -json > terraform-providers-schema/schema.json - # All acceptance tests run in parallel after quick tests go_test_smoke_aa_tgw_attachment: if: false # Temporarily disabled - TGW tests not ready yet name: go test smoke aa tgw attachment @@ -137,20 +114,9 @@ jobs: go-version-file: go.mod - run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAccResourceRedisCloudActiveActiveTransitGatewayAttachment_CRUDI"' - go_test_smoke_aa_enable_default_user: - name: go test smoke aa enable default user - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: go.mod - - run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAccResourceRedisCloudActiveActiveDatabase_enableDefaultUser"' - go_test_smoke_aa_db: name: go test smoke aa db - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [go_build] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -161,7 +127,7 @@ jobs: go_test_smoke_essentials_sub: name: go test smoke essentials sub - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [go_build] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -185,7 +151,7 @@ jobs: go_test_smoke_pro_db: name: go test smoke pro db - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [go_build] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -197,7 +163,7 @@ jobs: go_test_smoke_misc: name: go test smoke misc - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [ go_build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -208,7 +174,7 @@ jobs: go_test_pro_db_upgrade: name: go test smoke pro db upgrade - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [ go_build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -219,7 +185,7 @@ jobs: go_test_privatelink: name: go test smoke privatelink - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [ go_build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -231,7 +197,7 @@ jobs: go_test_block_public_endpoints: name: go test smoke public endpoints - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [ go_build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -242,7 +208,7 @@ jobs: go_test_smoke_qpf: name: go test smoke query performance factor - needs: [go_unit_test, tfproviderlint, terraform_providers_schema] + needs: [ go_build ] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -250,3 +216,25 @@ jobs: with: go-version-file: go.mod - run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAccResourceRedisCloudProDatabase_qpf"' + + go_unit_test: + name: go unit test + needs: [go_build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: go.mod + - run: go test ./... -run="^TestUnit" # Runs tests starting with TestUnit + + tfproviderlint: + name: tfproviderlint + needs: [go_build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: go.mod + - run: make tfproviderlint diff --git a/provider/activeactive/testdata/enable_default_user_all_explicit.tf b/provider/activeactive/testdata/enable_default_user_all_explicit.tf deleted file mode 100644 index a78fe0e8..00000000 --- a/provider/activeactive/testdata/enable_default_user_all_explicit.tf +++ /dev/null @@ -1,54 +0,0 @@ -# Template signature: fmt.Sprintf(template, subscription_name, database_name, password) -locals { - subscription_name = "%s" - database_name = "%s" - password = "%s" -} - -data "rediscloud_payment_method" "card" { - card_type = "Visa" - last_four_numbers = "5556" -} - -resource "rediscloud_active_active_subscription" "test" { - name = local.subscription_name - payment_method_id = data.rediscloud_payment_method.card.id - cloud_provider = "AWS" - - creation_plan { - dataset_size_in_gb = 1 - quantity = 1 - region { - region = "us-east-1" - networking_deployment_cidr = "192.168.0.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - region { - region = "us-east-2" - networking_deployment_cidr = "10.0.1.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - } -} - -resource "rediscloud_active_active_subscription_database" "test" { - subscription_id = rediscloud_active_active_subscription.test.id - name = local.database_name - dataset_size_in_gb = 1 - global_password = local.password - global_enable_default_user = true - - # us-east-1: explicitly true - override_region { - name = "us-east-1" - enable_default_user = true - } - - # us-east-2: explicitly false - override_region { - name = "us-east-2" - enable_default_user = false - } -} diff --git a/provider/activeactive/testdata/enable_default_user_global_false_region_true.tf b/provider/activeactive/testdata/enable_default_user_global_false_region_true.tf deleted file mode 100644 index e26cff9b..00000000 --- a/provider/activeactive/testdata/enable_default_user_global_false_region_true.tf +++ /dev/null @@ -1,53 +0,0 @@ -# Template signature: fmt.Sprintf(template, subscription_name, database_name, password) -locals { - subscription_name = "%s" - database_name = "%s" - password = "%s" -} - -data "rediscloud_payment_method" "card" { - card_type = "Visa" - last_four_numbers = "5556" -} - -resource "rediscloud_active_active_subscription" "test" { - name = local.subscription_name - payment_method_id = data.rediscloud_payment_method.card.id - cloud_provider = "AWS" - - creation_plan { - dataset_size_in_gb = 1 - quantity = 1 - region { - region = "us-east-1" - networking_deployment_cidr = "192.168.0.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - region { - region = "us-east-2" - networking_deployment_cidr = "10.0.1.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - } -} - -resource "rediscloud_active_active_subscription_database" "test" { - subscription_id = rediscloud_active_active_subscription.test.id - name = local.database_name - dataset_size_in_gb = 1 - global_password = local.password - global_enable_default_user = false - - # us-east-1: explicitly enable (override global false) - override_region { - name = "us-east-1" - enable_default_user = true - } - - # us-east-2: inherit global=false - override_region { - name = "us-east-2" - } -} diff --git a/provider/activeactive/testdata/enable_default_user_global_true_inherit.tf b/provider/activeactive/testdata/enable_default_user_global_true_inherit.tf deleted file mode 100644 index a9aa9c2e..00000000 --- a/provider/activeactive/testdata/enable_default_user_global_true_inherit.tf +++ /dev/null @@ -1,52 +0,0 @@ -# Template signature: fmt.Sprintf(template, subscription_name, database_name, password) -locals { - subscription_name = "%s" - database_name = "%s" - password = "%s" -} - -data "rediscloud_payment_method" "card" { - card_type = "Visa" - last_four_numbers = "5556" -} - -resource "rediscloud_active_active_subscription" "test" { - name = local.subscription_name - payment_method_id = data.rediscloud_payment_method.card.id - cloud_provider = "AWS" - - creation_plan { - dataset_size_in_gb = 1 - quantity = 1 - region { - region = "us-east-1" - networking_deployment_cidr = "192.168.0.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - region { - region = "us-east-2" - networking_deployment_cidr = "10.0.1.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - } -} - -resource "rediscloud_active_active_subscription_database" "test" { - subscription_id = rediscloud_active_active_subscription.test.id - name = local.database_name - dataset_size_in_gb = 1 - global_password = local.password - global_enable_default_user = true - - # Both regions should inherit global=true - # Before fix: would send enableDefaultUser=false to API - # After fix: should NOT send enableDefaultUser at all - override_region { - name = "us-east-1" - } - override_region { - name = "us-east-2" - } -} diff --git a/provider/activeactive/testdata/enable_default_user_mixed_overrides.tf b/provider/activeactive/testdata/enable_default_user_mixed_overrides.tf deleted file mode 100644 index ce676d50..00000000 --- a/provider/activeactive/testdata/enable_default_user_mixed_overrides.tf +++ /dev/null @@ -1,53 +0,0 @@ -# Template signature: fmt.Sprintf(template, subscription_name, database_name, password) -locals { - subscription_name = "%s" - database_name = "%s" - password = "%s" -} - -data "rediscloud_payment_method" "card" { - card_type = "Visa" - last_four_numbers = "5556" -} - -resource "rediscloud_active_active_subscription" "test" { - name = local.subscription_name - payment_method_id = data.rediscloud_payment_method.card.id - cloud_provider = "AWS" - - creation_plan { - dataset_size_in_gb = 1 - quantity = 1 - region { - region = "us-east-1" - networking_deployment_cidr = "192.168.0.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - region { - region = "us-east-2" - networking_deployment_cidr = "10.0.1.0/24" - write_operations_per_second = 1000 - read_operations_per_second = 1000 - } - } -} - -resource "rediscloud_active_active_subscription_database" "test" { - subscription_id = rediscloud_active_active_subscription.test.id - name = local.database_name - dataset_size_in_gb = 1 - global_password = local.password - global_enable_default_user = true - - # us-east-1: explicitly disable (override global) - override_region { - name = "us-east-1" - enable_default_user = false - } - - # us-east-2: inherit global=true - override_region { - name = "us-east-2" - } -} diff --git a/provider/datasource_rediscloud_essentials_database.go b/provider/datasource_rediscloud_essentials_database.go index 14b02a87..10bcc762 100644 --- a/provider/datasource_rediscloud_essentials_database.go +++ b/provider/datasource_rediscloud_essentials_database.go @@ -2,7 +2,6 @@ package provider import ( "context" - "github.com/RedisLabs/rediscloud-go-api/redis" fixedDatabases "github.com/RedisLabs/rediscloud-go-api/service/fixed/databases" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" diff --git a/provider/pro/resource_rediscloud_pro_database.go b/provider/pro/resource_rediscloud_pro_database.go index d0d92ab6..9e04c16d 100644 --- a/provider/pro/resource_rediscloud_pro_database.go +++ b/provider/pro/resource_rediscloud_pro_database.go @@ -298,8 +298,10 @@ func ResourceRedisCloudProDatabase() *schema.Resource { Default: true, }, "auto_minor_version_upgrade": { - Type: schema.TypeBool, - Optional: true, + Description: "When 'true', enables auto minor version upgrades for this database. Default: 'true'", + Type: schema.TypeBool, + Optional: true, + Default: true, }, "port": { Description: "TCP port on which the database is available", @@ -757,12 +759,13 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource Value: utils.GetInt(d, "throughput_measurement_value"), }, - DataPersistence: utils.GetString(d, "data_persistence"), - DataEvictionPolicy: utils.GetString(d, "data_eviction"), - SourceIP: utils.SetToStringSlice(d.Get("source_ips").(*schema.Set)), - Alerts: &alerts, - RemoteBackup: BuildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), - EnableDefaultUser: utils.GetBool(d, "enable_default_user"), + DataPersistence: utils.GetString(d, "data_persistence"), + DataEvictionPolicy: utils.GetString(d, "data_eviction"), + SourceIP: utils.SetToStringSlice(d.Get("source_ips").(*schema.Set)), + Alerts: &alerts, + RemoteBackup: BuildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), + EnableDefaultUser: utils.GetBool(d, "enable_default_user"), + AutoMinorVersionUpgrade: utils.GetBool(d, "auto_minor_version_upgrade"), } // One of the following fields must be set, validation is handled in the schema (ExactlyOneOf) @@ -807,11 +810,6 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource update.Password = redis.String(d.Get("password").(string)) } - // Only send auto_minor_version_upgrade if explicitly set - if v, ok := d.GetOk("auto_minor_version_upgrade"); ok { - update.AutoMinorVersionUpgrade = redis.Bool(v.(bool)) - } - update.ReplicaOf = utils.SetToStringSlice(d.Get("replica_of").(*schema.Set)) if update.ReplicaOf == nil { update.ReplicaOf = make([]*string, 0) diff --git a/provider/rediscloud_active_active_database_enable_default_user_acceptance_test.go b/provider/rediscloud_active_active_database_enable_default_user_acceptance_test.go deleted file mode 100644 index db9d66bf..00000000 --- a/provider/rediscloud_active_active_database_enable_default_user_acceptance_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package provider - -import ( - "fmt" - "testing" - - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -// TestAccResourceRedisCloudActiveActiveDatabase_enableDefaultUser tests the enable_default_user -// field behavior at both global and regional levels, ensuring proper three-state logic -func TestAccResourceRedisCloudActiveActiveDatabase_enableDefaultUser(t *testing.T) { - utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") - - subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + "-enable-default-user" - databaseName := "test-enable-default-user" - password := acctest.RandString(20) - - const resourceName = "rediscloud_active_active_subscription_database.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, - ProviderFactories: providerFactories, - CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, - Steps: []resource.TestStep{ - // Step 1: Create with global=true, regions inherit - { - Config: fmt.Sprintf( - utils.GetTestConfig(t, "./activeactive/testdata/enable_default_user_global_true_inherit.tf"), - subscriptionName, databaseName, password), - Check: resource.ComposeAggregateTestCheckFunc( - // Verify Terraform state - resource.TestCheckResourceAttr(resourceName, "name", databaseName), - resource.TestCheckResourceAttr(resourceName, "global_enable_default_user", "true"), - resource.TestCheckResourceAttr(resourceName, "override_region.#", "2"), - - // Both regions inherit global (no explicit enable_default_user set) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-1", - }), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-2", - }), - ), - }, - - // Step 2: Update to mixed overrides - some regions inherit, some override - { - Config: fmt.Sprintf( - utils.GetTestConfig(t, "./activeactive/testdata/enable_default_user_mixed_overrides.tf"), - subscriptionName, databaseName, password), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "global_enable_default_user", "true"), - resource.TestCheckResourceAttr(resourceName, "override_region.#", "2"), - - // us-east-1: explicitly false (override) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-1", - "enable_default_user": "false", - }), - - // us-east-2: inherits global (no enable_default_user set) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-2", - }), - ), - }, - - // Step 3: Global false, specific regions enable - { - Config: fmt.Sprintf( - utils.GetTestConfig(t, "./activeactive/testdata/enable_default_user_global_false_region_true.tf"), - subscriptionName, databaseName, password), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "global_enable_default_user", "false"), - resource.TestCheckResourceAttr(resourceName, "override_region.#", "2"), - - // us-east-1: explicitly true (override global false) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-1", - "enable_default_user": "true", - }), - - // us-east-2: inherits global false (no enable_default_user set) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-2", - }), - ), - }, - - // Step 4: Mixed - one region overrides to false - { - Config: fmt.Sprintf( - utils.GetTestConfig(t, "./activeactive/testdata/enable_default_user_all_explicit.tf"), - subscriptionName, databaseName, password), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "global_enable_default_user", "true"), - resource.TestCheckResourceAttr(resourceName, "override_region.#", "2"), - - // us-east-1: explicitly true (but matches global, so won't be in state) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-1", - }), - - // us-east-2: explicitly false (differs from global, so IS in state) - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "override_region.*", map[string]string{ - "name": "us-east-2", - "enable_default_user": "false", - }), - ), - }, - }, - }) -} diff --git a/provider/rediscloud_active_active_database_test.go b/provider/rediscloud_active_active_database_test.go index 1f2d0096..0510f685 100644 --- a/provider/rediscloud_active_active_database_test.go +++ b/provider/rediscloud_active_active_database_test.go @@ -516,7 +516,6 @@ resource "rediscloud_active_active_subscription_database" "example" { ` func TestAccResourceRedisCloudActiveActiveDatabase_autoMinorVersionUpgrade(t *testing.T) { - t.Skip("auto_minor_version_upgrade feature not yet available") utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") diff --git a/provider/resource_rediscloud_active_active_database.go b/provider/resource_rediscloud_active_active_database.go index 0905006f..d1e7d5f3 100644 --- a/provider/resource_rediscloud_active_active_database.go +++ b/provider/resource_rediscloud_active_active_database.go @@ -12,7 +12,6 @@ import ( "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" - "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -206,11 +205,12 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Description: "When 'true', enables connecting to the database with the 'default' user across all regions. Default: 'true'", Type: schema.TypeBool, Optional: true, - Default: true, }, "auto_minor_version_upgrade": { - Type: schema.TypeBool, - Optional: true, + Description: "When 'true', enables auto minor version upgrades for this database. Default: 'true'", + Type: schema.TypeBool, + Optional: true, + Default: true, }, "override_region": { Description: "Region-specific configuration parameters to override the global configuration", @@ -526,11 +526,10 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R return diag.FromErr(err) } - if _, ok := d.GetOk("global_data_persistence"); ok { - if db.GlobalDataPersistence != nil { - if err := d.Set("global_data_persistence", redis.StringValue(db.GlobalDataPersistence)); err != nil { - return diag.FromErr(err) - } + // Read global_data_persistence from API response + if db.GlobalDataPersistence != nil { + if err := d.Set("global_data_persistence", redis.StringValue(db.GlobalDataPersistence)); err != nil { + return diag.FromErr(err) } } @@ -571,34 +570,79 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R var regionDbConfigs []map[string]interface{} publicEndpointConfig := make(map[string]interface{}) privateEndpointConfig := make(map[string]interface{}) - - // Build API region lookup map - apiRegions := make(map[string]*databases.CrdbDatabase) for _, regionDb := range db.CrdbDatabases { region := redis.StringValue(regionDb.Region) - apiRegions[region] = regionDb - // Set the endpoints for all regions + // Set the endpoints for the region publicEndpointConfig[region] = redis.StringValue(regionDb.PublicEndpoint) privateEndpointConfig[region] = redis.StringValue(regionDb.PrivateEndpoint) - } + // Check if the region is in the state as an override + stateOverrideRegion := getStateOverrideRegion(d, region) + if stateOverrideRegion == nil { + continue + } + regionDbConfig := map[string]interface{}{ + "name": region, + } - // Iterate through STATE override_region blocks (not API regions) to preserve Set ordering/hashing - stateOverrideRegions := d.Get("override_region").(*schema.Set).List() + // Handle source_ips based on subscription's public_endpoint_access settings + // When public_endpoint_access is false and source_ips is empty, API returns private IP ranges + // When public_endpoint_access is true and source_ips is empty, API returns ["0.0.0.0/0"] + // When source_ips is explicitly set by user, API returns the user's input + // This is to prevent drift in terraform state as API response will differ from what terraform sees + var sourceIPs []string + privateIPRanges := []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10"} + + // Check if the returned source_ips matches default private IP ranges (when public access is blocked) + isPrivateIPRange := len(regionDb.Security.SourceIPs) == len(privateIPRanges) + if isPrivateIPRange { + for i, ip := range regionDb.Security.SourceIPs { + if redis.StringValue(ip) != privateIPRanges[i] { + isPrivateIPRange = false + break + } + } + } - for _, stateRegion := range stateOverrideRegions { - stateRegionMap := stateRegion.(map[string]interface{}) - region := stateRegionMap["name"].(string) + // Check if the returned source_ips is the default public access ["0.0.0.0/0"] + isDefaultPublicAccess := len(regionDb.Security.SourceIPs) == 1 && redis.StringValue(regionDb.Security.SourceIPs[0]) == "0.0.0.0/0" - // Look up corresponding API data - regionDb, exists := apiRegions[region] - if !exists { - tflog.Warn(ctx, "Read: Region in state not found in API response", map[string]interface{}{ - "region": region, - }) - continue + // Only set source_ips if they were explicitly configured by the user (not defaults) + if !isDefaultPublicAccess && !isPrivateIPRange { + sourceIPs = redis.StringSliceValue(regionDb.Security.SourceIPs...) + } + + if stateSourceIPs := getStateOverrideRegion(d, region)["override_global_source_ips"]; stateSourceIPs != nil { + if len(stateSourceIPs.(*schema.Set).List()) > 0 { + regionDbConfig["override_global_source_ips"] = sourceIPs + } + } + + if stateDataPersistence := getStateOverrideRegion(d, region)["override_global_data_persistence"]; stateDataPersistence != nil { + if stateDataPersistence.(string) != "" { + regionDbConfig["override_global_data_persistence"] = regionDb.DataPersistence + } + } + + if stateOverridePassword := getStateOverrideRegion(d, region)["override_global_password"]; stateOverridePassword != "" { + if *regionDb.Security.Password == d.Get("global_password").(string) { + regionDbConfig["override_global_password"] = "" + } else { + regionDbConfig["override_global_password"] = redis.StringValue(regionDb.Security.Password) + } + } + + stateOverrideAlerts := getStateAlertsFromDbRegion(getStateOverrideRegion(d, region)) + if len(stateOverrideAlerts) > 0 { + regionDbConfig["override_global_alert"] = pro.FlattenAlerts(regionDb.Alerts) + } + + regionDbConfig["remote_backup"] = pro.FlattenBackupPlan(regionDb.Backup, getStateRemoteBackup(d, region), "") + + // Only set enable_default_user if it was explicitly configured in the override_region + if stateEnableDefaultUser := getStateOverrideRegion(d, region)["enable_default_user"]; stateEnableDefaultUser != nil { + regionDbConfig["enable_default_user"] = redis.BoolValue(regionDb.Security.EnableDefaultUser) } - regionDbConfig := buildRegionConfigFromAPIAndState(ctx, d, db, region, regionDb, stateRegionMap) regionDbConfigs = append(regionDbConfigs, regionDbConfig) } @@ -623,16 +667,13 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R return diag.FromErr(err) } - if _, ok := d.GetOk("auto_minor_version_upgrade"); ok { - if err := d.Set("auto_minor_version_upgrade", redis.BoolValue(db.AutoMinorVersionUpgrade)); err != nil { - return diag.FromErr(err) - } + if err := d.Set("auto_minor_version_upgrade", redis.BoolValue(db.AutoMinorVersionUpgrade)); err != nil { + return diag.FromErr(err) } // Read global_enable_default_user from API response if db.GlobalEnableDefaultUser != nil { - globalValue := redis.BoolValue(db.GlobalEnableDefaultUser) - if err := d.Set("global_enable_default_user", globalValue); err != nil { + if err := d.Set("global_enable_default_user", redis.BoolValue(db.GlobalEnableDefaultUser)); err != nil { return diag.FromErr(err) } } @@ -709,24 +750,11 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema // Make a list of region-specific configurations var regions []*databases.LocalRegionProperties - - tflog.Debug(ctx, "Update: Starting to build region configurations", map[string]interface{}{ - "regionCount": len(d.Get("override_region").(*schema.Set).List()), - "globalAlertsCount": len(updateAlerts), - "globalAlerts": updateAlerts, - }) - for _, region := range d.Get("override_region").(*schema.Set).List() { dbRegion := region.(map[string]interface{}) overrideAlerts := getStateAlertsFromDbRegion(getStateOverrideRegion(d, dbRegion["name"].(string))) - tflog.Debug(ctx, "Update: Parsed alerts for region", map[string]interface{}{ - "region": dbRegion["name"].(string), - "overrideAlertsLen": len(overrideAlerts), - "overrideAlerts": overrideAlerts, - }) - // Make a list of region-specific source IPs for use in the regions list below var overrideSourceIps []*string for _, sourceIp := range dbRegion["override_global_source_ips"].(*schema.Set).List() { @@ -737,60 +765,15 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema Region: redis.String(dbRegion["name"].(string)), } - // Handle enable_default_user with three-state logic: - // - Not set in config -> don't send to API (inherit from global) - // - Explicitly true -> send true - // - Explicitly false -> send false - regionName := dbRegion["name"].(string) - enableDefaultUser := dbRegion["enable_default_user"].(bool) - wasExplicitlySet := isEnableDefaultUserExplicitlySetInConfig(d, regionName) - - tflog.Debug(ctx, "Checking enable_default_user for region", map[string]interface{}{ - "region": regionName, - "value": enableDefaultUser, - "wasExplicitlySet": wasExplicitlySet, - }) - - if wasExplicitlySet { - // Field was explicitly set in Terraform config, send the value - tflog.Debug(ctx, "Sending enable_default_user to API", map[string]interface{}{ - "region": regionName, - "value": enableDefaultUser, - }) - regionProps.EnableDefaultUser = redis.Bool(enableDefaultUser) - } else { - tflog.Debug(ctx, "Not sending enable_default_user (inherit from global)", map[string]interface{}{ - "region": regionName, - }) + // Only set EnableDefaultUser if it was explicitly configured in the override_region + if val, exists := dbRegion["enable_default_user"]; exists && val != nil { + regionProps.EnableDefaultUser = redis.Bool(val.(bool)) } - // If not explicitly set, don't send - let it inherit from global if len(overrideAlerts) > 0 { regionProps.Alerts = &overrideAlerts - tflog.Debug(ctx, "Update: Setting region alerts from override", map[string]interface{}{ - "region": regionName, - "alertCount": len(overrideAlerts), - "alertValues": overrideAlerts, - }) } else if len(updateAlerts) > 0 { regionProps.Alerts = &updateAlerts - tflog.Debug(ctx, "Update: Setting region alerts from global", map[string]interface{}{ - "region": regionName, - "alertCount": len(updateAlerts), - "alertValues": updateAlerts, - }) - } else { - // Explicitly send empty array to remove alerts from this region - // A pointer to a nil-slice is omitted from the json payload, which means the API keeps the existing value - //goland:noinspection GoPreferNilSlice - emptyAlerts := []*databases.Alert{} - regionProps.Alerts = &emptyAlerts - tflog.Debug(ctx, "Update: Setting empty alerts array to remove alerts", map[string]interface{}{ - "region": regionName, - "overrideAlertsLen": len(overrideAlerts), - "updateAlertsLen": len(updateAlerts), - "sendingEmptyArray": true, - }) } if len(overrideSourceIps) > 0 { regionProps.SourceIP = overrideSourceIps diff --git a/provider/resource_rediscloud_active_active_database_enable_default_user_test.go b/provider/resource_rediscloud_active_active_database_enable_default_user_test.go deleted file mode 100644 index e423f7a8..00000000 --- a/provider/resource_rediscloud_active_active_database_enable_default_user_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package provider - -import ( - "testing" - - "github.com/hashicorp/go-cty/cty" - "github.com/stretchr/testify/assert" -) - -// TestUnitIsEnableDefaultUserExplicitlySetInConfig tests the helper function logic -// that determines if enable_default_user was explicitly set in the Terraform config -func TestUnitIsEnableDefaultUserExplicitlySetInConfig(t *testing.T) { - tests := []struct { - name string - rawConfig cty.Value - regionName string - expected bool - description string - }{ - { - name: "field_explicitly_set_to_true", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - "enable_default_user": cty.True, - }), - }), - }), - regionName: "us-east-1", - expected: true, - description: "When enable_default_user is explicitly true, should return true", - }, - { - name: "field_explicitly_set_to_false", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - "enable_default_user": cty.False, - }), - }), - }), - regionName: "us-east-1", - expected: true, - description: "When enable_default_user is explicitly false, should return true", - }, - { - name: "field_not_set", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - // enable_default_user not present - }), - }), - }), - regionName: "us-east-1", - expected: false, - description: "When enable_default_user is not in config, should return false", - }, - { - name: "field_is_null", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - "enable_default_user": cty.NullVal(cty.Bool), - }), - }), - }), - regionName: "us-east-1", - expected: false, - description: "When enable_default_user is null, should return false", - }, - { - name: "multiple_regions_one_has_field", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - "enable_default_user": cty.True, - }), - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-2"), - "enable_default_user": cty.NullVal(cty.Bool), // Null means not set - }), - }), - }), - regionName: "us-east-2", - expected: false, - description: "When checking region without field (null), should return false", - }, - { - name: "region_not_found", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("us-east-1"), - }), - }), - }), - regionName: "eu-west-1", - expected: false, - description: "When region not found in config, should return false", - }, - { - name: "override_region_null", - rawConfig: cty.ObjectVal(map[string]cty.Value{ - "override_region": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "name": cty.String, - "enable_default_user": cty.Bool, - }))), - }), - regionName: "us-east-1", - expected: false, - description: "When override_region is null, should return false", - }, - { - name: "raw_config_null", - rawConfig: cty.NullVal(cty.Object(map[string]cty.Type{ - "override_region": cty.Set(cty.Object(map[string]cty.Type{ - "name": cty.String, - "enable_default_user": cty.Bool, - })), - })), - regionName: "us-east-1", - expected: false, - description: "When raw config is null (test environment), should return false", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Test the cty.Value parsing logic that powers the actual function - result := testIsEnableDefaultUserExplicitlySetFromRawConfig(tt.rawConfig, tt.regionName) - assert.Equal(t, tt.expected, result, tt.description) - }) - } -} - -// testIsEnableDefaultUserExplicitlySetFromRawConfig replicates the logic of -// isEnableDefaultUserExplicitlySetInConfig for testing purposes with direct cty.Value input -func testIsEnableDefaultUserExplicitlySetFromRawConfig(rawConfig cty.Value, regionName string) bool { - // Same logic as the actual function - if rawConfig.IsNull() { - return false - } - - if !rawConfig.Type().HasAttribute("override_region") { - return false - } - - overrideRegionAttr := rawConfig.GetAttr("override_region") - if overrideRegionAttr.IsNull() { - return false - } - - if overrideRegionAttr.Type().IsSetType() { - iter := overrideRegionAttr.ElementIterator() - for iter.Next() { - _, regionVal := iter.Element() - - if regionVal.Type().HasAttribute("name") { - nameAttr := regionVal.GetAttr("name") - if !nameAttr.IsNull() && nameAttr.AsString() == regionName { - if regionVal.Type().HasAttribute("enable_default_user") { - eduAttr := regionVal.GetAttr("enable_default_user") - return !eduAttr.IsNull() - } - return false - } - } - } - } - - return false -} diff --git a/provider/resource_rediscloud_active_active_database_helpers.go b/provider/resource_rediscloud_active_active_database_helpers.go deleted file mode 100644 index 4f128581..00000000 --- a/provider/resource_rediscloud_active_active_database_helpers.go +++ /dev/null @@ -1,473 +0,0 @@ -package provider - -import ( - "context" - "fmt" - "sort" - - "github.com/RedisLabs/rediscloud-go-api/redis" - "github.com/RedisLabs/rediscloud-go-api/service/databases" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" - "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -// findRegionFieldInCtyValue navigates through a cty.Value structure to find a field -// in the override_region Set for a specific region. -// -// This generic helper is used to check both raw config (GetRawConfig) and raw state (GetRawState) -// without triggering SDK v2's TypeSet materialization that adds all schema fields with zero-values. -// -// Parameters: -// - ctyVal: The cty.Value to search (from GetRawConfig or GetRawState) -// - regionName: The name of the region to find (e.g., "us-east-1") -// - fieldName: The field name to check within the region (e.g., "enable_default_user") -// -// Returns: -// - fieldValue: The cty.Value of the field if found -// - exists: true if the field was found and is not null, false otherwise -func findRegionFieldInCtyValue(ctyVal cty.Value, regionName string, fieldName string) (cty.Value, bool) { - if ctyVal.IsNull() { - return cty.NilVal, false - } - - if !ctyVal.Type().HasAttribute("override_region") { - return cty.NilVal, false - } - - overrideRegionAttr := ctyVal.GetAttr("override_region") - if overrideRegionAttr.IsNull() { - return cty.NilVal, false - } - - if !overrideRegionAttr.Type().IsSetType() { - return cty.NilVal, false - } - - iter := overrideRegionAttr.ElementIterator() - for iter.Next() { - _, regionVal := iter.Element() - - // Check if this is the region we're looking for - if regionVal.Type().HasAttribute("name") { - nameAttr := regionVal.GetAttr("name") - if !nameAttr.IsNull() && nameAttr.AsString() == regionName { - // Found matching region, check for field - if regionVal.Type().HasAttribute(fieldName) { - fieldAttr := regionVal.GetAttr(fieldName) - if !fieldAttr.IsNull() { - // For Set types, check if they have any elements (not just that attribute exists) - if fieldAttr.Type().IsSetType() || fieldAttr.Type().IsListType() { - // Empty sets/lists mean field was not explicitly set - if fieldAttr.LengthInt() == 0 { - return cty.NilVal, false - } - } - return fieldAttr, true - } - } - return cty.NilVal, false - } - } - } - - return cty.NilVal, false -} - -// isEnableDefaultUserExplicitlySetInConfig checks if enable_default_user was explicitly -// set in the Terraform configuration for a specific region in the override_region block. -// -// This is used by the Update function to determine whether to send the field to the API. -// We only need this for Update operations where GetRawConfig() is available. -func isEnableDefaultUserExplicitlySetInConfig(d *schema.ResourceData, regionName string) bool { - _, exists := findRegionFieldInCtyValue(d.GetRawConfig(), regionName, "enable_default_user") - return exists -} - -// isOverrideGlobalAlertExplicitlySetInConfig checks if override_global_alert was explicitly -// set in the Terraform configuration for a specific region in the override_region block. -func isOverrideGlobalAlertExplicitlySetInConfig(d *schema.ResourceData, regionName string) bool { - _, exists := findRegionFieldInCtyValue(d.GetRawConfig(), regionName, "override_global_alert") - return exists -} - -// isOverrideGlobalAlertInActualPersistedState checks if override_global_alert was in the ACTUAL -// persisted Terraform state (not the materialized Go map) for a specific region. -func isOverrideGlobalAlertInActualPersistedState(d *schema.ResourceData, regionName string) bool { - _, exists := findRegionFieldInCtyValue(d.GetRawState(), regionName, "override_global_alert") - return exists -} - -// isEnableDefaultUserInActualPersistedState checks if enable_default_user was in the ACTUAL -// persisted Terraform state (not the materialized Go map) for a specific region. -// Uses GetRawState to bypass TypeSet materialization that adds all fields with zero-values. -func isEnableDefaultUserInActualPersistedState(d *schema.ResourceData, regionName string) bool { - _, exists := findRegionFieldInCtyValue(d.GetRawState(), regionName, "enable_default_user") - return exists -} - -// enableDefaultUserDecision encapsulates the decision result for whether to include -// enable_default_user in the region config. -type enableDefaultUserDecision struct { - shouldInclude bool - reason string -} - -// decideEnableDefaultUserInclusion determines whether to include enable_default_user -// in the region state based on config/state context and API values. -// -// This implements the hybrid GetRawConfig/GetRawState strategy: -// - During Apply/Update (when GetRawConfig available): Check if explicitly set in config -// - During Refresh (when GetRawConfig unavailable): Check if was in persisted state -// -// Parameters: -// - d: ResourceData containing config and state -// - region: The region name (e.g., "us-east-1") -// - regionValue: The enable_default_user value from the API for this region -// - globalValue: The global_enable_default_user value from the API -// -// Returns: -// - Decision indicating whether to include the field and why -func decideEnableDefaultUserInclusion( - d *schema.ResourceData, - region string, - regionValue bool, - globalValue bool, -) enableDefaultUserDecision { - rawConfig := d.GetRawConfig() - valuesDiffer := regionValue != globalValue - - // Try config-based detection first (available during Apply/Update) - if !rawConfig.IsNull() { - if isEnableDefaultUserExplicitlySetInConfig(d, region) { - return enableDefaultUserDecision{ - shouldInclude: true, - reason: "explicitly set in config", - } - } - if valuesDiffer { - return enableDefaultUserDecision{ - shouldInclude: true, - reason: "differs from global (API override)", - } - } - return enableDefaultUserDecision{ - shouldInclude: false, - reason: "matches global (inherited)", - } - } - - // Fall back to state-based detection (during Refresh) - wasInState := isEnableDefaultUserInActualPersistedState(d, region) - - if wasInState { - reason := "was in state, preserving (user explicit)" - if valuesDiffer { - reason = "was in state, differs from global" - } - return enableDefaultUserDecision{ - shouldInclude: true, - reason: reason, - } - } - - if valuesDiffer { - return enableDefaultUserDecision{ - shouldInclude: true, - reason: "not in state, but differs from global", - } - } - - return enableDefaultUserDecision{ - shouldInclude: false, - reason: "not in state, matches global (inherited)", - } -} - -// filterDefaultSourceIPs removes default source IP values that should not be in state. -// Returns empty slice if IPs are default values (private ranges or 0.0.0.0/0). -// -// The API returns different defaults based on public_endpoint_access: -// - When public access disabled: Returns private IP ranges -// - When public access enabled: Returns ["0.0.0.0/0"] -// - When explicitly set by user: Returns user's values -// -// This filtering prevents drift from API-generated defaults. -func filterDefaultSourceIPs(apiSourceIPs []*string) []string { - privateIPRanges := []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10"} - - // Check for default public access ["0.0.0.0/0"] - if len(apiSourceIPs) == 1 && redis.StringValue(apiSourceIPs[0]) == "0.0.0.0/0" { - return []string{} - } - - // Check for default private IP ranges - if len(apiSourceIPs) == len(privateIPRanges) { - isPrivateDefault := true - for i, ip := range apiSourceIPs { - if redis.StringValue(ip) != privateIPRanges[i] { - isPrivateDefault = false - break - } - } - if isPrivateDefault { - return []string{} - } - } - - return redis.StringSliceValue(apiSourceIPs...) -} - -// addSourceIPsIfOverridden adds override_global_source_ips to region config if it differs from global. -func addSourceIPsIfOverridden(ctx context.Context, regionDbConfig map[string]interface{}, d *schema.ResourceData, regionDb *databases.CrdbDatabase, region string) { - sourceIPs := filterDefaultSourceIPs(regionDb.Security.SourceIPs) - - if len(sourceIPs) == 0 { - return - } - - globalSourceIPsPtrs := utils.SetToStringSlice(d.Get("global_source_ips").(*schema.Set)) - globalSourceIPs := redis.StringSliceValue(globalSourceIPsPtrs...) - - shouldAdd := !stringSlicesEqual(sourceIPs, globalSourceIPs) - - if shouldAdd { - regionDbConfig["override_global_source_ips"] = sourceIPs - } -} - -// addDataPersistenceIfOverridden adds override_global_data_persistence to region config if it differs from global. -func addDataPersistenceIfOverridden( - ctx context.Context, - regionDbConfig map[string]interface{}, - db *databases.ActiveActiveDatabase, - regionDb *databases.CrdbDatabase, - region string, -) { - if regionDb.DataPersistence != nil && db.GlobalDataPersistence != nil { - regionValue := redis.StringValue(regionDb.DataPersistence) - globalValue := redis.StringValue(db.GlobalDataPersistence) - - if regionValue != globalValue { - regionDbConfig["override_global_data_persistence"] = regionDb.DataPersistence - } - } -} - -// addPasswordIfOverridden adds override_global_password to region config if it differs from global. -func addPasswordIfOverridden( - ctx context.Context, - regionDbConfig map[string]interface{}, - db *databases.ActiveActiveDatabase, - regionDb *databases.CrdbDatabase, - region string, -) { - if regionDb.Security.Password != nil && db.GlobalPassword != nil { - regionValue := *regionDb.Security.Password - globalValue := redis.StringValue(db.GlobalPassword) - - if regionValue != globalValue { - regionDbConfig["override_global_password"] = regionValue - } - } -} - -// addAlertsIfOverridden adds override_global_alert to region config using hybrid GetRawConfig/GetRawState logic. -// This prevents drift when alerts are removed from config but API still has stale values. -func addAlertsIfOverridden( - ctx context.Context, - regionDbConfig map[string]interface{}, - d *schema.ResourceData, - regionDb *databases.CrdbDatabase, - region string, -) { - globalAlerts := d.Get("global_alert").(*schema.Set).List() - regionAlerts := pro.FlattenAlerts(regionDb.Alerts) - rawConfig := d.GetRawConfig() - alertsDiffer := !alertsEqualContent(globalAlerts, regionAlerts) - - var shouldAdd bool - var reason string - - // Hybrid GetRawConfig/GetRawState strategy (same as enable_default_user) - if !rawConfig.IsNull() { - // During Apply/Update: Check if explicitly set in config - if isOverrideGlobalAlertExplicitlySetInConfig(d, region) { - shouldAdd = true - reason = "explicitly set in config" - } else { - // Not in config - don't add even if API differs (user removed it or never set it) - shouldAdd = false - reason = "not in config (inherited or removed)" - } - } else { - // During Refresh: Check if was in persisted state - wasInState := isOverrideGlobalAlertInActualPersistedState(d, region) - if wasInState { - shouldAdd = true - if alertsDiffer { - reason = "was in state, differs from global" - } else { - reason = "was in state, preserving (user explicit)" - } - } else { - // NOT in state - don't add even if API differs - // The Update function now explicitly removes alerts, so API should be correct. - // If API differs here, it's either: (1) stale data that will converge, or - // (2) out-of-band change that we'll detect on next refresh after convergence - shouldAdd = false - if alertsDiffer { - reason = "not in state, not adding despite API difference (inherited or stale)" - } else { - reason = "not in state, matches global (inherited)" - } - } - } - - tflog.Debug(ctx, "Read: Alerts comparison", map[string]interface{}{ - "region": region, - "globalAlertsCount": len(globalAlerts), - "globalAlerts": globalAlerts, - "regionAlertsCount": len(regionAlerts), - "regionAlerts": regionAlerts, - "alertsDiffer": alertsDiffer, - "getRawConfigAvailable": !rawConfig.IsNull(), - "shouldAdd": shouldAdd, - "reason": reason, - }) - - if shouldAdd { - regionDbConfig["override_global_alert"] = regionAlerts - } -} - -// alertsEqualContent compares two alert lists for equality by comparing their content. -// Takes []interface{} (from state) and []map[string]interface{} (from API flatten) for comparison. -func alertsEqualContent(globalAlerts []interface{}, regionAlerts []map[string]interface{}) bool { - if len(globalAlerts) != len(regionAlerts) { - return false - } - - // Convert global alerts to a set of "name:value" strings - globalSet := make(map[string]bool, len(globalAlerts)) - for _, alert := range globalAlerts { - alertMap := alert.(map[string]interface{}) - name := alertMap["name"].(string) - value := alertMap["value"] - key := fmt.Sprintf("%s:%v", name, value) - globalSet[key] = true - } - - // Check if all region alerts exist in global set - for _, alertMap := range regionAlerts { - name := alertMap["name"].(string) - value := alertMap["value"] - key := fmt.Sprintf("%s:%v", name, value) - if !globalSet[key] { - return false - } - } - - return true -} - -// addRemoteBackupIfConfigured adds remote_backup to region config if it exists in both API and state. -func addRemoteBackupIfConfigured( - ctx context.Context, - regionDbConfig map[string]interface{}, - regionDb *databases.CrdbDatabase, - stateOverrideRegion map[string]interface{}, - region string, -) { - if regionDb.Backup == nil { - return - } - - stateRemoteBackup := stateOverrideRegion["remote_backup"] - if stateRemoteBackup == nil { - return - } - - stateRemoteBackupList := stateRemoteBackup.([]interface{}) - if len(stateRemoteBackupList) > 0 { - regionDbConfig["remote_backup"] = pro.FlattenBackupPlan(regionDb.Backup, stateRemoteBackupList, "") - } -} - -// addEnableDefaultUserIfNeeded applies hybrid GetRawConfig/GetRawState logic -// to determine if enable_default_user should be in state. -func addEnableDefaultUserIfNeeded( - ctx context.Context, - regionDbConfig map[string]interface{}, - d *schema.ResourceData, - db *databases.ActiveActiveDatabase, - region string, - regionDb *databases.CrdbDatabase, -) { - if regionDb.Security.EnableDefaultUser == nil || db.GlobalEnableDefaultUser == nil { - return - } - - regionEnableDefaultUser := redis.BoolValue(regionDb.Security.EnableDefaultUser) - globalEnableDefaultUser := redis.BoolValue(db.GlobalEnableDefaultUser) - - decision := decideEnableDefaultUserInclusion(d, region, regionEnableDefaultUser, globalEnableDefaultUser) - - if decision.shouldInclude { - regionDbConfig["enable_default_user"] = regionEnableDefaultUser - } - - tflog.Debug(ctx, "Read: enable_default_user decision", map[string]interface{}{ - "region": region, - "getRawConfigAvailable": !d.GetRawConfig().IsNull(), - "shouldInclude": decision.shouldInclude, - "value": regionEnableDefaultUser, - "reason": decision.reason, - }) -} - -// buildRegionConfigFromAPIAndState orchestrates building region config from API and state. -// Each override field is handled by a dedicated helper function for clarity and maintainability. -func buildRegionConfigFromAPIAndState(ctx context.Context, d *schema.ResourceData, db *databases.ActiveActiveDatabase, region string, regionDb *databases.CrdbDatabase, stateOverrideRegion map[string]interface{}) map[string]interface{} { - regionDbConfig := map[string]interface{}{ - "name": region, - } - - // Handle each override field using dedicated helper functions - addSourceIPsIfOverridden(ctx, regionDbConfig, d, regionDb, region) - addDataPersistenceIfOverridden(ctx, regionDbConfig, db, regionDb, region) - addPasswordIfOverridden(ctx, regionDbConfig, db, regionDb, region) - addAlertsIfOverridden(ctx, regionDbConfig, d, regionDb, region) - addRemoteBackupIfConfigured(ctx, regionDbConfig, regionDb, stateOverrideRegion, region) - addEnableDefaultUserIfNeeded(ctx, regionDbConfig, d, db, region, regionDb) - - return regionDbConfig -} - -// stringSlicesEqual compares two string slices for equality (order-insensitive). -// This is used for comparing source IP lists where order doesn't matter. -func stringSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - // Make copies to avoid modifying the original slices - aCopy := make([]string, len(a)) - bCopy := make([]string, len(b)) - copy(aCopy, a) - copy(bCopy, b) - - // Sort both copies - sort.Strings(aCopy) - sort.Strings(bCopy) - - // Compare sorted slices - for i := range aCopy { - if aCopy[i] != bCopy[i] { - return false - } - } - return true -} diff --git a/provider/resource_rediscloud_pro_database_test.go b/provider/resource_rediscloud_pro_database_test.go index e09c5f48..aef20b2f 100644 --- a/provider/resource_rediscloud_pro_database_test.go +++ b/provider/resource_rediscloud_pro_database_test.go @@ -276,7 +276,6 @@ func TestAccResourceRedisCloudProDatabase_respversion(t *testing.T) { } func TestAccResourceRedisCloudProDatabase_autoMinorVersionUpgrade(t *testing.T) { - t.Skip("auto_minor_version_upgrade feature not yet available") utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") From 306286fd1280e3771c7934a0b0c2f3dc03471e13 Mon Sep 17 00:00:00 2001 From: Matthew Long Date: Fri, 7 Nov 2025 16:39:16 +0000 Subject: [PATCH 2/5] feat: enable_default_user is now set to true globally and true for regional overrides --- CHANGELOG.md | 7 +++++++ ...d_active_active_database_block_public_endpoints_test.go | 5 +++++ provider/rediscloud_active_active_database_test.go | 2 ++ provider/rediscloud_active_active_subscription_test.go | 3 +++ provider/resource_rediscloud_active_active_database.go | 4 +++- provider/resource_rediscloud_active_active_subscription.go | 6 +++--- 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ecb4d64..eeee7b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) +# Unreleased + +## Changed +- Reverted global/regional override rework from v2.7.3 due to regressions. Active-Active database global configuration behavior has been restored to v2.7.2 state. Transit Gateway improvements from v2.7.3 have been preserved. +- `global_enable_default_user` now have a default override of `true`. This is due to technical limitations in the current Terraform SDK framework. If you wish to turn off the default user per region you have to explicitly do so. + + # 2.7.3 (6th November 2025) ## Changed diff --git a/provider/rediscloud_active_active_database_block_public_endpoints_test.go b/provider/rediscloud_active_active_database_block_public_endpoints_test.go index a0c528e2..bd320efa 100644 --- a/provider/rediscloud_active_active_database_block_public_endpoints_test.go +++ b/provider/rediscloud_active_active_database_block_public_endpoints_test.go @@ -41,17 +41,20 @@ func TestAccActiveActiveSubscriptionDatabase_BlockPublicEndpoints(t *testing.T) // Global source IPs should be explicitly set resource.TestCheckResourceAttr(databaseResource, "global_source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "global_source_ips.*", "192.168.0.0/16"), + resource.TestCheckResourceAttr(databaseResource, "global_enable_default_user", "true"), // Override region 0 (us-east-2) has explicit override of source_ips resource.TestCheckResourceAttr(databaseResource, "override_region.0.override_global_source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "override_region.0.override_global_source_ips.*", "172.16.0.0/16"), resource.TestCheckResourceAttr(databaseResource, "override_region.0.source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "override_region.0.source_ips.*", "172.16.0.0/16"), + resource.TestCheckResourceAttr(databaseResource, "override_region.0.enable_default_user", "true"), // Override region 1 (us-east-1) has no override, should inherit global resource.TestCheckResourceAttr(databaseResource, "override_region.1.override_global_source_ips.#", "0"), resource.TestCheckResourceAttr(databaseResource, "override_region.1.source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "override_region.1.source_ips.*", "192.168.0.0/16"), + resource.TestCheckResourceAttr(databaseResource, "override_region.1.enable_default_user", "true"), // Data source checks resource.TestCheckResourceAttr(datasourceName, "name", subscriptionName), @@ -71,11 +74,13 @@ func TestAccActiveActiveSubscriptionDatabase_BlockPublicEndpoints(t *testing.T) // Global source IPs should be the same (explicitly set in testdata) resource.TestCheckResourceAttr(databaseResource, "global_source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "global_source_ips.*", "192.168.0.0/16"), + resource.TestCheckResourceAttr(databaseResource, "global_enable_default_user", "true"), // Override regions should have the same overrides resource.TestCheckResourceAttr(databaseResource, "override_region.0.override_global_source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "override_region.0.override_global_source_ips.*", "172.16.0.0/16"), resource.TestCheckResourceAttr(databaseResource, "override_region.0.source_ips.#", "1"), resource.TestCheckTypeSetElemAttr(databaseResource, "override_region.0.source_ips.*", "172.16.0.0/16"), + resource.TestCheckResourceAttr(databaseResource, "override_region.0.enable_default_user", "true"), // Data source checks after update resource.TestCheckResourceAttr(datasourceName, "name", subscriptionName), resource.TestCheckResourceAttr(datasourceName, "global_source_ips.#", "1"), diff --git a/provider/rediscloud_active_active_database_test.go b/provider/rediscloud_active_active_database_test.go index 0510f685..18fc32f0 100644 --- a/provider/rediscloud_active_active_database_test.go +++ b/provider/rediscloud_active_active_database_test.go @@ -182,6 +182,7 @@ func TestAccResourceRedisCloudActiveActiveDatabase_CRUDI(t *testing.T) { resource.TestCheckResourceAttr(databaseResourceName, "global_alert.#", "0"), resource.TestCheckResourceAttr(databaseResourceName, "global_modules.#", "1"), resource.TestCheckResourceAttr(databaseResourceName, "global_modules.0", "RedisJSON"), + resource.TestCheckResourceAttr(databaseResourceName, "global_enable_default_user", "true"), resource.TestCheckResourceAttr(databaseResourceName, "override_region.#", "1"), resource.TestCheckResourceAttr(databaseResourceName, "override_region.0.name", "us-east-1"), @@ -239,6 +240,7 @@ func TestAccResourceRedisCloudActiveActiveDatabase_optionalAttributes(t *testing Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveDatabaseOptionalAttributes, subscriptionName, name, password, portNumber), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "port", strconv.Itoa(portNumber)), + resource.TestCheckResourceAttr(resourceName, "global_enable_default_user", "true"), ), }, }, diff --git a/provider/rediscloud_active_active_subscription_test.go b/provider/rediscloud_active_active_subscription_test.go index 907b1aeb..f579d15e 100644 --- a/provider/rediscloud_active_active_subscription_test.go +++ b/provider/rediscloud_active_active_subscription_test.go @@ -199,6 +199,9 @@ func TestAccResourceRedisCloudActiveActiveSubscription_CRUDI(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.write_operations_per_second", "1000"), resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.read_operations_per_second", "1000"), + // Check database enable_default_user settings + resource.TestCheckResourceAttr("rediscloud_active_active_subscription_database.example", "global_enable_default_user", "false"), + // also checks user has removed default user //resource.TestCheckResourceAttr(resourceName, "regions.1.enable_default_user", "false"), diff --git a/provider/resource_rediscloud_active_active_database.go b/provider/resource_rediscloud_active_active_database.go index d1e7d5f3..282f926b 100644 --- a/provider/resource_rediscloud_active_active_database.go +++ b/provider/resource_rediscloud_active_active_database.go @@ -205,6 +205,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Description: "When 'true', enables connecting to the database with the 'default' user across all regions. Default: 'true'", Type: schema.TypeBool, Optional: true, + Default: true, }, "auto_minor_version_upgrade": { Description: "When 'true', enables auto minor version upgrades for this database. Default: 'true'", @@ -265,9 +266,10 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Optional: true, }, "enable_default_user": { - Description: "When 'true', enables connecting to the database with the 'default' user. If not set, the global setting will be used.", + Description: "When 'true', enables connecting to the database with the 'default' user. Default: 'true'", Type: schema.TypeBool, Optional: true, + Default: true, }, "remote_backup": { Description: "An object that specifies the backup options for the database in this region", diff --git a/provider/resource_rediscloud_active_active_subscription.go b/provider/resource_rediscloud_active_active_subscription.go index 1f314f4c..b33883ee 100644 --- a/provider/resource_rediscloud_active_active_subscription.go +++ b/provider/resource_rediscloud_active_active_subscription.go @@ -4,9 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "regexp" "strconv" "time" @@ -14,6 +11,9 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/maintenance" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" From a10e247b0cadfda4ae25fa76c93b3ac64e62be0c Mon Sep 17 00:00:00 2001 From: Matthew Long Date: Fri, 7 Nov 2025 16:40:05 +0000 Subject: [PATCH 3/5] chore: remove auto_minor_version_upgrade from terraform schema and CRUD operations --- .../pro/resource_rediscloud_pro_database.go | 13 ------------- .../rediscloud_active_active_database_test.go | 1 + ...esource_rediscloud_active_active_database.go | 17 +---------------- .../resource_rediscloud_pro_database_test.go | 1 + 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/provider/pro/resource_rediscloud_pro_database.go b/provider/pro/resource_rediscloud_pro_database.go index 9e04c16d..317120e6 100644 --- a/provider/pro/resource_rediscloud_pro_database.go +++ b/provider/pro/resource_rediscloud_pro_database.go @@ -297,12 +297,6 @@ func ResourceRedisCloudProDatabase() *schema.Resource { Optional: true, Default: true, }, - "auto_minor_version_upgrade": { - Description: "When 'true', enables auto minor version upgrades for this database. Default: 'true'", - Type: schema.TypeBool, - Optional: true, - Default: true, - }, "port": { Description: "TCP port on which the database is available", Type: schema.TypeInt, @@ -453,9 +447,6 @@ func resourceRedisCloudProDatabaseCreate(ctx context.Context, d *schema.Resource createDatabase.RespVersion = s }) - utils.SetBool(d, "auto_minor_version_upgrade", func(b *bool) { - createDatabase.AutoMinorVersionUpgrade = b - }) // Confirm sub is ready to accept a db request if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil { @@ -679,9 +670,6 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa } } - if err := d.Set("auto_minor_version_upgrade", redis.BoolValue(db.AutoMinorVersionUpgrade)); err != nil { - return diag.FromErr(err) - } if err := ReadTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) @@ -765,7 +753,6 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource Alerts: &alerts, RemoteBackup: BuildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), EnableDefaultUser: utils.GetBool(d, "enable_default_user"), - AutoMinorVersionUpgrade: utils.GetBool(d, "auto_minor_version_upgrade"), } // One of the following fields must be set, validation is handled in the schema (ExactlyOneOf) diff --git a/provider/rediscloud_active_active_database_test.go b/provider/rediscloud_active_active_database_test.go index 18fc32f0..b38c3120 100644 --- a/provider/rediscloud_active_active_database_test.go +++ b/provider/rediscloud_active_active_database_test.go @@ -520,6 +520,7 @@ resource "rediscloud_active_active_subscription_database" "example" { func TestAccResourceRedisCloudActiveActiveDatabase_autoMinorVersionUpgrade(t *testing.T) { utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + t.Skip("auto_minor_version_upgrade feature temporarily removed") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + "-subscription" name := acctest.RandomWithPrefix(testResourcePrefix) + "-database" diff --git a/provider/resource_rediscloud_active_active_database.go b/provider/resource_rediscloud_active_active_database.go index 282f926b..8d6ed45f 100644 --- a/provider/resource_rediscloud_active_active_database.go +++ b/provider/resource_rediscloud_active_active_database.go @@ -205,12 +205,6 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Description: "When 'true', enables connecting to the database with the 'default' user across all regions. Default: 'true'", Type: schema.TypeBool, Optional: true, - Default: true, - }, - "auto_minor_version_upgrade": { - Description: "When 'true', enables auto minor version upgrades for this database. Default: 'true'", - Type: schema.TypeBool, - Optional: true, Default: true, }, "override_region": { @@ -269,7 +263,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Description: "When 'true', enables connecting to the database with the 'default' user. Default: 'true'", Type: schema.TypeBool, Optional: true, - Default: true, + Default: true, }, "remote_backup": { Description: "An object that specifies the backup options for the database in this region", @@ -455,9 +449,6 @@ func resourceRedisCloudActiveActiveDatabaseCreate(ctx context.Context, d *schema createDatabase.RedisVersion = s }) - utils.SetBool(d, "auto_minor_version_upgrade", func(b *bool) { - createDatabase.AutoMinorVersionUpgrade = b - }) // Confirm Subscription Active status before creating database err = utils.WaitForSubscriptionToBeActive(ctx, subId, api) @@ -669,9 +660,6 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R return diag.FromErr(err) } - if err := d.Set("auto_minor_version_upgrade", redis.BoolValue(db.AutoMinorVersionUpgrade)); err != nil { - return diag.FromErr(err) - } // Read global_enable_default_user from API response if db.GlobalEnableDefaultUser != nil { @@ -836,9 +824,6 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema update.GlobalEnableDefaultUser = redis.Bool(v.(bool)) } - if v, ok := d.GetOk("auto_minor_version_upgrade"); ok { - update.AutoMinorVersionUpgrade = redis.Bool(v.(bool)) - } if v, ok := d.GetOk("support_oss_cluster_api"); ok { update.SupportOSSClusterAPI = redis.Bool(v.(bool)) diff --git a/provider/resource_rediscloud_pro_database_test.go b/provider/resource_rediscloud_pro_database_test.go index aef20b2f..a16c2e9e 100644 --- a/provider/resource_rediscloud_pro_database_test.go +++ b/provider/resource_rediscloud_pro_database_test.go @@ -278,6 +278,7 @@ func TestAccResourceRedisCloudProDatabase_respversion(t *testing.T) { func TestAccResourceRedisCloudProDatabase_autoMinorVersionUpgrade(t *testing.T) { utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + t.Skip("auto_minor_version_upgrade feature temporarily removed") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_subscription_database.example" From 40bf54023d20b711893c0d9c08911c27e1ba43e2 Mon Sep 17 00:00:00 2001 From: Matthew Long Date: Fri, 7 Nov 2025 16:51:08 +0000 Subject: [PATCH 4/5] docs: documenting changes to enable default user behaviour --- CHANGELOG.md | 3 +-- .../rediscloud_active_active_subscription_database.md | 1 + .../rediscloud_active_active_subscription_database.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeee7b6d..ecb7f003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,7 @@ See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) ## Changed - Reverted global/regional override rework from v2.7.3 due to regressions. Active-Active database global configuration behavior has been restored to v2.7.2 state. Transit Gateway improvements from v2.7.3 have been preserved. -- `global_enable_default_user` now have a default override of `true`. This is due to technical limitations in the current Terraform SDK framework. If you wish to turn off the default user per region you have to explicitly do so. - +- `rediscloud_active_active_subscription_database`: Both `global_enable_default_user` and the region-level `enable_default_user` (in `override_region` blocks) now default to `true`. To disable the default user in a specific region, you must explicitly set `enable_default_user = false` in that region's `override_region` block. # 2.7.3 (6th November 2025) diff --git a/docs/data-sources/rediscloud_active_active_subscription_database.md b/docs/data-sources/rediscloud_active_active_subscription_database.md index 6b5e9965..ca2deff7 100644 --- a/docs/data-sources/rediscloud_active_active_subscription_database.md +++ b/docs/data-sources/rediscloud_active_active_subscription_database.md @@ -50,6 +50,7 @@ data "rediscloud_active_active_subscription_database" "example" { * `enable_tls` - Enable TLS for database. * `tls_certificate` - TLS certificate used for authentication. * `global_source_ips` - Set of CIDR addresses to allow access to the database. +* `global_enable_default_user` - Whether the 'default' user is enabled for connecting to the database across all regions. * `data_eviction` - The data items eviction policy. * `global_modules` - A list of modules to be enabled on all deployments of this database. * `public_endpoint` - Public endpoint to access the database. diff --git a/docs/resources/rediscloud_active_active_subscription_database.md b/docs/resources/rediscloud_active_active_subscription_database.md index 9e95b61f..23ac10ea 100644 --- a/docs/resources/rediscloud_active_active_subscription_database.md +++ b/docs/resources/rediscloud_active_active_subscription_database.md @@ -103,7 +103,7 @@ The following arguments are supported: * `global_alert` - (Optional) A block defining Redis database alert of regions that don't override global settings, documented below, can be specified multiple times. (either: 'dataset-size', 'datasets-size', 'throughput-higher-than', 'throughput-lower-than', 'latency', 'syncsource-error', 'syncsource-lag' or 'connections-limit') * `global_modules` - (Optional) A list of modules to be enabled on all deployments of this database. Supported modules: `RedisJSON`, `RediSearch`. Ignored after database creation. * `global_source_ips` - (Optional) List of source IP addresses or subnet masks that are allowed to connect to the database across all regions that don't override this setting (example: ['192.168.10.0/32', '192.168.12.0/24']). When not specified, the default behavior depends on the subscription's `public_endpoint_access` setting: if `false`, defaults to RFC1918 private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10); if `true`, defaults to 0.0.0.0/0 (unrestricted public access) -* `global_enable_default_user` - (Optional) When 'true', enables connecting to the database with the 'default' user across all regions. Default: 'true' +* `global_enable_default_user` - (Optional) When 'true', enables connecting to the database with the 'default' user across all regions. Default: 'true'. To disable, explicitly set to 'false' * `global_resp_version` - (Optional) Either 'resp2' or 'resp3'. Resp version for Crdb databases within the AA database. Must be compatible with Redis version. * `port` - (Optional) TCP port on which the database is available - must be between 10000 and 19999. **Modifying this attribute will force creation of a new resource.** * `override_region` - (Optional) Override region specific configuration, documented below @@ -117,7 +117,7 @@ The `override_region` block supports: * `override_global_source_ips` - (Optional) List of source IP addresses or subnet masks that are allowed to connect to the database in this specific region, overriding the global `global_source_ips` setting (example: ['192.168.10.0/32', '192.168.12.0/24']). If not specified, the global `global_source_ips` setting applies to this region * `override_global_data_persistence` - (Optional) Regional instance of an Active-Active database data persistence rate (in persistent storage) * `remote_backup` - (Optional) Specifies the backup options for the database in this region, documented below -* `enable_default_user` - (Optional) Whether the default user should be enabled or not. True by default. +* `enable_default_user` - (Optional) Whether the default user should be enabled for this specific region. Default: 'true'. To disable the default user for this region, you must explicitly set this to 'false' The `override_global_alert` block supports: From 671fa5ad487c5d36725213ca7676a25e9e5f2fa0 Mon Sep 17 00:00:00 2001 From: Matthew Long Date: Fri, 7 Nov 2025 16:58:35 +0000 Subject: [PATCH 5/5] chore: updating changelog version and unit test order --- .github/workflows/terraform_provider_pr.yml | 38 ++++++++++----------- CHANGELOG.md | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/terraform_provider_pr.yml b/.github/workflows/terraform_provider_pr.yml index 9d002ab5..ec1b318d 100644 --- a/.github/workflows/terraform_provider_pr.yml +++ b/.github/workflows/terraform_provider_pr.yml @@ -102,6 +102,17 @@ jobs: mkdir terraform-providers-schema terraform providers schema -json > terraform-providers-schema/schema.json + go_unit_test: + name: go unit test + needs: [go_build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: go.mod + - run: go test ./... -run="^TestUnit" # Runs tests starting with TestUnit + go_test_smoke_aa_tgw_attachment: if: false # Temporarily disabled - TGW tests not ready yet name: go test smoke aa tgw attachment @@ -116,7 +127,7 @@ jobs: go_test_smoke_aa_db: name: go test smoke aa db - needs: [go_build] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -127,7 +138,7 @@ jobs: go_test_smoke_essentials_sub: name: go test smoke essentials sub - needs: [go_build] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -151,7 +162,7 @@ jobs: go_test_smoke_pro_db: name: go test smoke pro db - needs: [go_build] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -163,7 +174,7 @@ jobs: go_test_smoke_misc: name: go test smoke misc - needs: [ go_build ] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -174,7 +185,7 @@ jobs: go_test_pro_db_upgrade: name: go test smoke pro db upgrade - needs: [ go_build ] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -185,7 +196,7 @@ jobs: go_test_privatelink: name: go test smoke privatelink - needs: [ go_build ] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -197,7 +208,7 @@ jobs: go_test_block_public_endpoints: name: go test smoke public endpoints - needs: [ go_build ] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -208,7 +219,7 @@ jobs: go_test_smoke_qpf: name: go test smoke query performance factor - needs: [ go_build ] + needs: [go_unit_test, tfproviderlint, terraform_providers_schema] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -217,17 +228,6 @@ jobs: go-version-file: go.mod - run: EXECUTE_TESTS=true make testacc TESTARGS='-run="TestAccResourceRedisCloudProDatabase_qpf"' - go_unit_test: - name: go unit test - needs: [go_build] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: go.mod - - run: go test ./... -run="^TestUnit" # Runs tests starting with TestUnit - tfproviderlint: name: tfproviderlint needs: [go_build] diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb7f003..7f528226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) -# Unreleased +# 2.7.4 (7th November 2025) ## Changed - Reverted global/regional override rework from v2.7.3 due to regressions. Active-Active database global configuration behavior has been restored to v2.7.2 state. Transit Gateway improvements from v2.7.3 have been preserved.