diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 927f4f0d..168337f3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -51,7 +51,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -65,4 +65,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/terraform_provider_main.yml b/.github/workflows/terraform_provider_main.yml index 98bc39cf..5d9eac7e 100644 --- a/.github/workflows/terraform_provider_main.yml +++ b/.github/workflows/terraform_provider_main.yml @@ -185,7 +185,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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 diff --git a/.github/workflows/terraform_provider_pr.yml b/.github/workflows/terraform_provider_pr.yml index 01096abc..1a41b313 100644 --- a/.github/workflows/terraform_provider_pr.yml +++ b/.github/workflows/terraform_provider_pr.yml @@ -199,7 +199,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d1aac4..bbc777cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) +# 2.7.0 (22nd October 2025) + +## Added: +- Add auto_minor_version_upgrade field to Pro and Active-Active database resources (default: true) to allow users to control automatic minor version upgrades. This will NOT affect existing databases. + +## Changed: +- Change Redis 8.0 modules validation from hard error to warning since modules are bundled by default in Redis 8+. + +## Fixed: +- Fix test error message patterns to match updated API error format. +- Fix Redis 8 upgrade test expectation (dataset_size_in_gb: 3→1). + + # 2.6.0 (17th October 2025) ## Added: diff --git a/go.mod b/go.mod index b93c5f4a..17be2d60 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.0 toolchain go1.24.1 require ( - github.com/RedisLabs/rediscloud-go-api v0.38.0 + github.com/RedisLabs/rediscloud-go-api v0.39.0 github.com/bflad/tfproviderlint v0.31.0 github.com/hashicorp/go-cty v1.5.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 diff --git a/go.sum b/go.sum index 92da6608..6aae7c20 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/RedisLabs/rediscloud-go-api v0.38.0 h1:lLOS0E8tQhUzuUfb/H+QWtDaZrC9xNnTvAPBygH4WS8= -github.com/RedisLabs/rediscloud-go-api v0.38.0/go.mod h1:Hkh3i/EsHnyfgV0ijednbofz/EmZC3sFnSNNruF3G6I= +github.com/RedisLabs/rediscloud-go-api v0.39.0 h1:VNI7VyP9V41Z8FP558v60WCJFBB57AGuQQq3QY4t2VU= +github.com/RedisLabs/rediscloud-go-api v0.39.0/go.mod h1:Hkh3i/EsHnyfgV0ijednbofz/EmZC3sFnSNNruF3G6I= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= diff --git a/provider/activeactive/testdata/auto_minor_version_upgrade.tf b/provider/activeactive/testdata/auto_minor_version_upgrade.tf new file mode 100644 index 00000000..1904d6bc --- /dev/null +++ b/provider/activeactive/testdata/auto_minor_version_upgrade.tf @@ -0,0 +1,44 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" +} + +data "rediscloud_payment_method" "card" { + card_type = "Visa" + last_four_numbers = "5556" +} + +data "rediscloud_cloud_account" "account" { + exclude_internal_account = true + provider_type = "AWS" + name = local.rediscloud_cloud_account +} + +resource "rediscloud_active_active_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + storage_encryption = true + + region { + region = "eu-west-1" + networking_deployment_cidr = "10.0.0.0/24" + preferred_availability_zones = ["eu-west-1a"] + } + + region { + region = "us-east-1" + networking_deployment_cidr = "10.1.0.0/24" + preferred_availability_zones = ["us-east-1a"] + } +} + +resource "rediscloud_active_active_subscription_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = "auto-minor-version-upgrade-test" + protocol = "redis" + dataset_size_in_gb = 1 + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + auto_minor_version_upgrade = %s +} diff --git a/provider/pro/resource_rediscloud_pro_database.go b/provider/pro/resource_rediscloud_pro_database.go index dd670907..5e72be5d 100644 --- a/provider/pro/resource_rediscloud_pro_database.go +++ b/provider/pro/resource_rediscloud_pro_database.go @@ -297,6 +297,12 @@ 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, @@ -436,8 +442,13 @@ 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 { + utils.SubscriptionMutex.Unlock(subId) return diag.FromErr(err) } @@ -656,6 +667,10 @@ 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) } @@ -731,12 +746,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) @@ -1037,6 +1053,21 @@ func containsDBModule(modules []map[string]interface{}, moduleName string) bool return false } +// shouldWarnRedis8Modules checks if a warning should be issued for modules in Redis 8.0 or higher +func shouldWarnRedis8Modules(version string, hasModules bool) bool { + if !hasModules { + return false + } + // Extract major version (first character before the dot) + if len(version) > 0 { + majorVersionStr := strings.Split(version, ".")[0] + if majorVersion, err := strconv.Atoi(majorVersionStr); err == nil { + return majorVersion >= 8 + } + } + return false +} + func validateModulesForRedis8() schema.CustomizeDiffFunc { return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { redisVersion, versionExists := diff.GetOk("redis_version") @@ -1044,12 +1075,10 @@ func validateModulesForRedis8() schema.CustomizeDiffFunc { if versionExists && modulesExists { version := redisVersion.(string) - // Check if version is >= 8.0 - if strings.HasPrefix(version, "8.") { - moduleSet := modules.(*schema.Set) - if moduleSet.Len() > 0 { - return fmt.Errorf(`"modules" cannot be explicitly set for Redis version %s as modules are bundled by default. Remove the "modules" field from your configuration`, version) - } + moduleSet := modules.(*schema.Set) + + if shouldWarnRedis8Modules(version, moduleSet.Len() > 0) { + 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) } } return nil diff --git a/provider/pro/resource_rediscloud_pro_database_validation_test.go b/provider/pro/resource_rediscloud_pro_database_validation_test.go new file mode 100644 index 00000000..a1a9c6a4 --- /dev/null +++ b/provider/pro/resource_rediscloud_pro_database_validation_test.go @@ -0,0 +1,67 @@ +package pro + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestUnitShouldWarnRedis8Modules_Redis8WithModules tests that warning is triggered for Redis 8.0 with modules +func TestUnitShouldWarnRedis8Modules_Redis8WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("8.0", true) + assert.True(t, result, "should warn for Redis 8.0 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis80WithModules tests that warning is triggered for Redis 8.0.0 with modules +func TestUnitShouldWarnRedis8Modules_Redis80WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("8.0.0", true) + assert.True(t, result, "should warn for Redis 8.0.0 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis81WithModules tests that warning is triggered for Redis 8.1+ with modules +func TestUnitShouldWarnRedis8Modules_Redis81WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("8.1.0", true) + assert.True(t, result, "should warn for Redis 8.1.0 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis89WithModules tests that warning is triggered for Redis 8.9+ with modules +func TestUnitShouldWarnRedis8Modules_Redis89WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("8.9.9", true) + assert.True(t, result, "should warn for Redis 8.9.9 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis7WithModules tests that no warning for Redis 7.x with modules +func TestUnitShouldWarnRedis8Modules_Redis7WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("7.4", true) + assert.False(t, result, "should not warn for Redis 7.4 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis6WithModules tests that no warning for Redis 6.x with modules +func TestUnitShouldWarnRedis8Modules_Redis6WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("6.2", true) + assert.False(t, result, "should not warn for Redis 6.2 with modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis8NoModules tests that no warning for Redis 8.0 without modules +func TestUnitShouldWarnRedis8Modules_Redis8NoModules(t *testing.T) { + result := shouldWarnRedis8Modules("8.0", false) + assert.False(t, result, "should not warn for Redis 8.0 without modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis7NoModules tests that no warning for Redis 7.x without modules +func TestUnitShouldWarnRedis8Modules_Redis7NoModules(t *testing.T) { + result := shouldWarnRedis8Modules("7.4", false) + assert.False(t, result, "should not warn for Redis 7.4 without modules") +} + +// TestUnitShouldWarnRedis8Modules_Redis9WithModules tests that warning is triggered for Redis 9.x with modules (future-proofing) +func TestUnitShouldWarnRedis8Modules_Redis9WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("9.0", true) + assert.True(t, result, "should warn for Redis 9.0 with modules (modules bundled in 8.0+)") +} + +// TestUnitShouldWarnRedis8Modules_Redis10WithModules tests that warning is triggered for Redis 10.x with modules +func TestUnitShouldWarnRedis8Modules_Redis10WithModules(t *testing.T) { + result := shouldWarnRedis8Modules("10.0.0", true) + assert.True(t, result, "should warn for Redis 10.0.0 with modules (modules bundled in 8.0+)") +} diff --git a/provider/pro/testdata/pro_database_auto_minor_version_upgrade.tf b/provider/pro/testdata/pro_database_auto_minor_version_upgrade.tf new file mode 100644 index 00000000..5ae03dd8 --- /dev/null +++ b/provider/pro/testdata/pro_database_auto_minor_version_upgrade.tf @@ -0,0 +1,50 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + auto_minor_version_upgrade = %s +} + +data "rediscloud_payment_method" "card" { + card_type = "Visa" + last_four_numbers = "5556" +} + +data "rediscloud_cloud_account" "account" { + exclude_internal_account = true + provider_type = "AWS" + name = local.rediscloud_cloud_account +} + +resource "rediscloud_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + memory_storage = "ram" + cloud_provider { + provider = data.rediscloud_cloud_account.account.provider_type + cloud_account_id = data.rediscloud_cloud_account.account.id + region { + region = "eu-west-1" + networking_deployment_cidr = "10.0.0.0/24" + preferred_availability_zones = ["eu-west-1a"] + } + } + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + replication = false + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + } +} + +resource "rediscloud_subscription_database" "example" { + subscription_id = rediscloud_subscription.example.id + name = "auto-minor-version-upgrade-test" + protocol = "redis" + memory_limit_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + auto_minor_version_upgrade = local.auto_minor_version_upgrade +} diff --git a/provider/pro/testdata/pro_database_data_source.tf b/provider/pro/testdata/pro_database_data_source.tf index d462e1ec..73939ae6 100644 --- a/provider/pro/testdata/pro_database_data_source.tf +++ b/provider/pro/testdata/pro_database_data_source.tf @@ -1,40 +1,39 @@ locals { - rediscloud_cloud_account = "%s" + rediscloud_cloud_account = "%s" rediscloud_subscription_name = "%s" - rediscloud_password = "%s" + rediscloud_password = "%s" } data "rediscloud_payment_method" "card" { - card_type = "Visa" + card_type = "Visa" last_four_numbers = "5556" } data "rediscloud_cloud_account" "account" { exclude_internal_account = true - provider_type = "AWS" - name = local.rediscloud_cloud_account + provider_type = "AWS" + name = local.rediscloud_cloud_account } resource "rediscloud_subscription" "example" { - name = local.rediscloud_subscription_name + name = local.rediscloud_subscription_name payment_method_id = data.rediscloud_payment_method.card.id - memory_storage = "ram" + memory_storage = "ram" cloud_provider { - provider = data.rediscloud_cloud_account.account.provider_type + provider = data.rediscloud_cloud_account.account.provider_type cloud_account_id = data.rediscloud_cloud_account.account.id region { - region = "eu-west-1" + region = "eu-west-1" networking_deployment_cidr = "10.0.0.0/24" preferred_availability_zones = ["eu-west-1a"] } } creation_plan { - memory_limit_in_gb = 1 - quantity = 1 - replication=false - support_oss_cluster_api=true - throughput_measurement_by = "operations-per-second" + memory_limit_in_gb = 1 + quantity = 1 + replication = false + support_oss_cluster_api = true + throughput_measurement_by = "operations-per-second" throughput_measurement_value = 1000 - query_performance_factor = "2x" modules = ["RediSearch"] } } @@ -47,24 +46,24 @@ resource "rediscloud_subscription_database" "example" { throughput_measurement_by = "operations-per-second" throughput_measurement_value = 1000 password = local.rediscloud_password - support_oss_cluster_api = true - replication = false - enable_default_user = true - query_performance_factor = "2x" - redis_version = "7.4" + support_oss_cluster_api = true + replication = false + enable_default_user = true + query_performance_factor = "2x" + redis_version = "7.4" modules = [ { - name: "RediSearch" + name : "RediSearch" } ] } data "rediscloud_database" "example-by-id" { subscription_id = rediscloud_subscription.example.id - db_id = rediscloud_subscription_database.example.db_id + db_id = rediscloud_subscription_database.example.db_id } data "rediscloud_database" "example-by-name" { subscription_id = rediscloud_subscription.example.id - name = rediscloud_subscription_database.example.name + name = rediscloud_subscription_database.example.name } diff --git a/provider/rediscloud_active_active_database_test.go b/provider/rediscloud_active_active_database_test.go index 34e355b1..b125257f 100644 --- a/provider/rediscloud_active_active_database_test.go +++ b/provider/rediscloud_active_active_database_test.go @@ -515,3 +515,36 @@ resource "rediscloud_active_active_subscription_database" "example" { } } ` + +func TestAccResourceRedisCloudActiveActiveDatabase_autoMinorVersionUpgrade(t *testing.T) { + + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + + subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + "-subscription" + name := acctest.RandomWithPrefix(testResourcePrefix) + "-database" + const resourceName = "rediscloud_active_active_subscription_database.example" + testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + // Test database creation with auto_minor_version_upgrade set to false + { + Config: fmt.Sprintf(utils.GetTestConfig(t, "./activeactive/testdata/auto_minor_version_upgrade.tf"), testCloudAccountName, subscriptionName, name, "false"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "auto-minor-version-upgrade-test"), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + ), + }, + // Test database update with auto_minor_version_upgrade set to true + { + Config: fmt.Sprintf(utils.GetTestConfig(t, "./activeactive/testdata/auto_minor_version_upgrade.tf"), testCloudAccountName, subscriptionName, name, "true"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "true"), + ), + }, + }, + }) +} diff --git a/provider/resource_rediscloud_active_active_database.go b/provider/resource_rediscloud_active_active_database.go index ebb44b2b..dbfdb478 100644 --- a/provider/resource_rediscloud_active_active_database.go +++ b/provider/resource_rediscloud_active_active_database.go @@ -206,6 +206,12 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Type: schema.TypeBool, Optional: 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": { Description: "Region-specific configuration parameters to override the global configuration", Type: schema.TypeSet, @@ -447,6 +453,10 @@ 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) if err != nil { @@ -643,6 +653,10 @@ 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) + } + tlsAuthEnabled := *db.CrdbDatabases[0].Security.TLSClientAuthentication if err := utils.ApplyCertificateHints(tlsAuthEnabled, d); err != nil { return diag.FromErr(err) @@ -799,6 +813,10 @@ 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_active_active_private_service_connect.go b/provider/resource_rediscloud_active_active_private_service_connect.go index f514d0ee..2e68b381 100644 --- a/provider/resource_rediscloud_active_active_private_service_connect.go +++ b/provider/resource_rediscloud_active_active_private_service_connect.go @@ -64,12 +64,12 @@ func resourceRedisCloudActiveActivePrivateServiceConnectCreate(ctx context.Conte return diag.FromErr(err) } utils.SubscriptionMutex.Lock(subscriptionId) + defer utils.SubscriptionMutex.Unlock(subscriptionId) regionId := d.Get("region_id").(int) pscServiceId, err := api.Client.PrivateServiceConnect.CreateActiveActiveService(ctx, subscriptionId, regionId) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } @@ -79,17 +79,14 @@ func resourceRedisCloudActiveActivePrivateServiceConnectCreate(ctx context.Conte return refreshPrivateServiceConnectServiceActiveActiveStatus(ctx, subscriptionId, regionId, api) }) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } err = utils.WaitForSubscriptionToBeActive(ctx, subscriptionId, api) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } - utils.SubscriptionMutex.Unlock(subscriptionId) return resourceRedisCloudActiveActivePrivateServiceConnectRead(ctx, d, meta) } diff --git a/provider/resource_rediscloud_essentials_database.go b/provider/resource_rediscloud_essentials_database.go index 65d855ab..99735d42 100644 --- a/provider/resource_rediscloud_essentials_database.go +++ b/provider/resource_rediscloud_essentials_database.go @@ -2,9 +2,7 @@ package provider import ( "context" - "fmt" "log" - "strings" "time" "github.com/RedisLabs/rediscloud-go-api/redis" @@ -52,8 +50,6 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { Delete: schema.DefaultTimeout(10 * time.Minute), }, - CustomizeDiff: essentialsCustomizeDiff(), - Schema: map[string]*schema.Schema{ "subscription_id": { Description: "Identifier of the essentials subscription", @@ -138,7 +134,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { Computed: true, }, "source_ips": { - Description: "Set of CIDR addresses to allow access to the database", + Description: "Set of CIDR addresses to allow access to the database.", Type: schema.TypeList, Optional: true, MinItems: 1, @@ -146,6 +142,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { Type: schema.TypeString, ValidateDiagFunc: validation.ToDiagFunc(validation.IsCIDR), }, + DiffSuppressFunc: suppressIfPaygDisabled, }, "replica": { Description: "Details of database replication", @@ -413,6 +410,7 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R databaseId, err := api.Client.FixedDatabases.Create(ctx, subId, createDatabaseRequest) if err != nil { + log.Printf("[ERROR] FixedDatabases.Create failed for subscription %d: %v", subId, err) utils.SubscriptionMutex.Unlock(subId) return diag.FromErr(err) } @@ -731,7 +729,7 @@ func resourceRedisCloudEssentialsDatabaseDelete(ctx context.Context, d *schema.R dbErr := api.Client.FixedDatabases.Delete(ctx, subId, databaseId) if dbErr != nil { - diag.FromErr(dbErr) + return diag.FromErr(dbErr) } return diags } @@ -822,25 +820,14 @@ func writeFixedTags(ctx context.Context, api *client.ApiClient, subId int, datab func essentialsCustomizeDiff() schema.CustomizeDiffFunc { return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { - if err := validateModulesForRedis8Essentials()(ctx, diff, meta); err != nil { - return err - } - return nil - } -} - -func validateModulesForRedis8Essentials() schema.CustomizeDiffFunc { - return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { - redisVersion, versionExists := diff.GetOk("redis_version") + // Check if user is trying to specify modules modules, modulesExists := diff.GetOkExists("modules") - if versionExists && modulesExists { - version := redisVersion.(string) - if strings.HasPrefix(version, "8.") { - moduleSet := modules.(*schema.Set) - if moduleSet.Len() > 0 { - return fmt.Errorf(`"modules" cannot be explicitly set for Redis version %s as modules are bundled by default. Remove the "modules" field from your configuration`, version) - } + if modulesExists { + moduleSet := modules.(*schema.Set) + if moduleSet.Len() > 0 { + // Warn, don't error + log.Printf("[WARN] Modules are explicitly configured. Note that some plans may use Redis 8.0+ where modules are bundled by default. The API will reject invalid configurations.") } } return nil diff --git a/provider/resource_rediscloud_private_service_connect.go b/provider/resource_rediscloud_private_service_connect.go index 3980ef78..0c2f64e3 100644 --- a/provider/resource_rediscloud_private_service_connect.go +++ b/provider/resource_rediscloud_private_service_connect.go @@ -59,10 +59,10 @@ func resourceRedisCloudPrivateServiceConnectCreate(ctx context.Context, d *schem return diag.FromErr(err) } utils.SubscriptionMutex.Lock(subscriptionId) + defer utils.SubscriptionMutex.Unlock(subscriptionId) pscServiceId, err := api.Client.PrivateServiceConnect.CreateService(ctx, subscriptionId) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } @@ -72,17 +72,14 @@ func resourceRedisCloudPrivateServiceConnectCreate(ctx context.Context, d *schem return refreshPrivateServiceConnectServiceStatus(ctx, subscriptionId, api) }) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } err = utils.WaitForSubscriptionToBeActive(ctx, subscriptionId, api) if err != nil { - utils.SubscriptionMutex.Unlock(subscriptionId) return diag.FromErr(err) } - utils.SubscriptionMutex.Unlock(subscriptionId) return resourceRedisCloudPrivateServiceConnectRead(ctx, d, meta) } diff --git a/provider/resource_rediscloud_pro_database_qpf_test.go b/provider/resource_rediscloud_pro_database_qpf_test.go index 94837b3c..4faeb814 100644 --- a/provider/resource_rediscloud_pro_database_qpf_test.go +++ b/provider/resource_rediscloud_pro_database_qpf_test.go @@ -150,7 +150,7 @@ func TestAccResourceRedisCloudProDatabase_qpf_missingModule(t *testing.T) { config := formatDatabaseConfig(name, testCloudAccountName, password, "4x", "") - testErrorCase(t, config, regexp.MustCompile("query_performance_factor\" requires the \"modules\" key to be explicitly defined in HCL")) + testErrorCase(t, config, regexp.MustCompile("DATABASE_QUERY_PERFORMANCE_FACTOR_SEARCH_IS_REQUIRED.*RediSearch.*required")) } func TestAccResourceRedisCloudProDatabase_qpf_missingRediSearchModule(t *testing.T) { @@ -160,7 +160,7 @@ func TestAccResourceRedisCloudProDatabase_qpf_missingRediSearchModule(t *testing config := formatDatabaseConfig(name, testCloudAccountName, password, "4x", `modules = [{ name = "RediBloom" }]`) - testErrorCase(t, config, regexp.MustCompile("query_performance_factor\" requires the \"modules\" list to contain \"RediSearch")) + testErrorCase(t, config, regexp.MustCompile("DATABASE_QUERY_PERFORMANCE_FACTOR_SEARCH_IS_REQUIRED.*RediSearch.*required")) } func TestAccResourceRedisCloudProDatabase_qpf_invalidQueryPerformanceFactors(t *testing.T) { diff --git a/provider/resource_rediscloud_pro_database_redis_8_test.go b/provider/resource_rediscloud_pro_database_redis_8_test.go index 6084f4f4..0de7ec6d 100644 --- a/provider/resource_rediscloud_pro_database_redis_8_test.go +++ b/provider/resource_rediscloud_pro_database_redis_8_test.go @@ -121,7 +121,7 @@ func TestAccResourceRedisCloudProDatabase_Redis8_Upgrade(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", "example"), resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), - resource.TestCheckResourceAttr(resourceName, "dataset_size_in_gb", "3"), + resource.TestCheckResourceAttr(resourceName, "dataset_size_in_gb", "1"), resource.TestCheckResourceAttr(resourceName, "replication", "false"), resource.TestCheckResourceAttr(resourceName, "support_oss_cluster_api", "false"), resource.TestCheckResourceAttr(resourceName, "resp_version", "resp3"), diff --git a/provider/resource_rediscloud_pro_database_test.go b/provider/resource_rediscloud_pro_database_test.go index f0b684dc..aef20b2f 100644 --- a/provider/resource_rediscloud_pro_database_test.go +++ b/provider/resource_rediscloud_pro_database_test.go @@ -274,3 +274,35 @@ func TestAccResourceRedisCloudProDatabase_respversion(t *testing.T) { }, }) } + +func TestAccResourceRedisCloudProDatabase_autoMinorVersionUpgrade(t *testing.T) { + + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + + name := acctest.RandomWithPrefix(testResourcePrefix) + const resourceName = "rediscloud_subscription_database.example" + testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckProSubscriptionDestroy, + Steps: []resource.TestStep{ + // Test database creation with auto_minor_version_upgrade set to false + { + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_auto_minor_version_upgrade.tf"), testCloudAccountName, name, "false"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "auto-minor-version-upgrade-test"), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + ), + }, + // Test database update with auto_minor_version_upgrade set to true + { + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_auto_minor_version_upgrade.tf"), testCloudAccountName, name, "true"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "true"), + ), + }, + }, + }) +}