diff --git a/.gitignore b/.gitignore index 80ff7108..890391ad 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ CLAUDE.md # Keep windows files with windows line endings *.winfile eol=crlf +.claude diff --git a/provider/datasource_rediscloud_acl_rule_test.go b/provider/datasource_rediscloud_acl_rule_test.go index 418e6bdc..ff2f20b5 100644 --- a/provider/datasource_rediscloud_acl_rule_test.go +++ b/provider/datasource_rediscloud_acl_rule_test.go @@ -2,14 +2,16 @@ package provider import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "regexp" "testing" + + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccDataSourceRedisCloudAclRule_ForDefaultRule(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") // This rule already exists const testName = "Read-Write" diff --git a/provider/datasource_rediscloud_active_active_database.go b/provider/datasource_rediscloud_active_active_database.go index ef67a0ab..a4a91384 100644 --- a/provider/datasource_rediscloud_active_active_database.go +++ b/provider/datasource_rediscloud_active_active_database.go @@ -3,10 +3,12 @@ package provider import ( "context" "fmt" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/databases" + "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" ) @@ -347,7 +349,7 @@ func dataSourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestBackupStatus, err := parseLatestBackupStatus(latestBackupStatus) + parsedLatestBackupStatus, err := utils.ParseLatestBackupStatus(latestBackupStatus) if err != nil { return diag.FromErr(err) } @@ -365,7 +367,7 @@ func dataSourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestImportStatus, err = parseLatestImportStatus(latestImportStatus) + parsedLatestImportStatus, err = utils.ParseLatestImportStatus(latestImportStatus) if err != nil { return diag.FromErr(err) } @@ -374,7 +376,7 @@ func dataSourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema return diag.FromErr(err) } - if err := readTags(ctx, api, subId, dbId, d); err != nil { + if err := pro.ReadTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } diff --git a/provider/datasource_rediscloud_active_active_subscription.go b/provider/datasource_rediscloud_active_active_subscription.go index 0093c4c3..416802bf 100644 --- a/provider/datasource_rediscloud_active_active_subscription.go +++ b/provider/datasource_rediscloud_active_active_subscription.go @@ -5,6 +5,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "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/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "strconv" @@ -167,7 +168,7 @@ func dataSourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *sc }) } - subs = filterSubscriptions(subs, filters) + subs = pro.FilterSubscriptions(subs, filters) if len(subs) == 0 { return diag.Errorf("Your query returned no results. Please change your search criteria and try again.") @@ -219,7 +220,7 @@ func dataSourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *sc if err != nil { return diag.FromErr(err) } - if err := d.Set("maintenance_windows", flattenMaintenance(m)); err != nil { + if err := d.Set("maintenance_windows", pro.FlattenMaintenance(m)); err != nil { return diag.FromErr(err) } @@ -227,7 +228,7 @@ func dataSourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *sc if err != nil { return diag.FromErr(err) } - if err := d.Set("pricing", flattenPricing(pricingList)); err != nil { + if err := d.Set("pricing", pro.FlattenPricing(pricingList)); err != nil { return diag.FromErr(err) } diff --git a/provider/datasource_rediscloud_active_active_subscription_regions.go b/provider/datasource_rediscloud_active_active_subscription_regions.go index 784de251..c7d604c1 100644 --- a/provider/datasource_rediscloud_active_active_subscription_regions.go +++ b/provider/datasource_rediscloud_active_active_subscription_regions.go @@ -6,6 +6,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "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/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -107,7 +108,7 @@ func dataSourceRedisCloudActiveActiveRegionsRead(ctx context.Context, d *schema. }) } - subs = filterSubscriptions(subs, filters) + subs = pro.FilterSubscriptions(subs, filters) if len(subs) == 0 { return diag.Errorf("Your query returned no results. Please change your search criteria and try again.") diff --git a/provider/datasource_rediscloud_active_active_transit_gateway.go b/provider/datasource_rediscloud_active_active_transit_gateway.go index 733bc41b..46f644cb 100644 --- a/provider/datasource_rediscloud_active_active_transit_gateway.go +++ b/provider/datasource_rediscloud_active_active_transit_gateway.go @@ -5,6 +5,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/transit_gateway/attachments" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "strconv" @@ -110,7 +111,7 @@ func dataSourceActiveActiveTransitGatewayRead(ctx context.Context, d *schema.Res tgw := tgws[0] tgwId := redis.IntValue(tgw.Id) - d.SetId(buildResourceId(subId, tgwId)) + d.SetId(utils.BuildResourceId(subId, tgwId)) if err := d.Set("tgw_id", tgwId); err != nil { return diag.FromErr(err) } diff --git a/provider/datasource_rediscloud_cloud_account_test.go b/provider/datasource_rediscloud_cloud_account_test.go index 40037e57..fdba3da6 100644 --- a/provider/datasource_rediscloud_cloud_account_test.go +++ b/provider/datasource_rediscloud_cloud_account_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "testing" @@ -11,7 +12,7 @@ import ( func TestAccDataSourceRedisCloudCloudAccount_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") diff --git a/provider/datasource_rediscloud_data_persistence_test.go b/provider/datasource_rediscloud_data_persistence_test.go index a01c85c1..0933dff4 100644 --- a/provider/datasource_rediscloud_data_persistence_test.go +++ b/provider/datasource_rediscloud_data_persistence_test.go @@ -1,6 +1,7 @@ package provider import ( + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -8,7 +9,7 @@ import ( func TestAccDataSourceRedisCloudDataPersistence_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") const dataPersistenceFoo = "data.rediscloud_data_persistence.foo" resource.ParallelTest(t, resource.TestCase{ diff --git a/provider/datasource_rediscloud_database_modules.go b/provider/datasource_rediscloud_database_modules.go index f893d1d4..0f733b9d 100644 --- a/provider/datasource_rediscloud_database_modules.go +++ b/provider/datasource_rediscloud_database_modules.go @@ -10,7 +10,7 @@ import ( func dataSourceRedisCloudDatabaseModules() *schema.Resource { return &schema.Resource{ - Description: "The Database data source allows access to the details of an existing database within your Redis Enterprise Cloud account.", + Description: "The Database modules data source allows access to the details of existing database modules within your Redis Enterprise Cloud account.", ReadContext: dataSourceRedisCloudDatabaseModulesRead, Schema: map[string]*schema.Schema{ diff --git a/provider/datasource_rediscloud_database_modules_test.go b/provider/datasource_rediscloud_database_modules_test.go index db1d82a9..ce911496 100644 --- a/provider/datasource_rediscloud_database_modules_test.go +++ b/provider/datasource_rediscloud_database_modules_test.go @@ -1,6 +1,7 @@ package provider import ( + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -8,7 +9,7 @@ import ( func TestAccDataSourceRedisCloudDatabaseModules_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/provider/datasource_rediscloud_essentials_database.go b/provider/datasource_rediscloud_essentials_database.go index f8284a78..10bcc762 100644 --- a/provider/datasource_rediscloud_essentials_database.go +++ b/provider/datasource_rediscloud_essentials_database.go @@ -5,6 +5,8 @@ import ( "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" + "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" @@ -408,7 +410,7 @@ func dataSourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.R databaseId := redis.IntValue(db.DatabaseId) - d.SetId(buildResourceId(subId, databaseId)) + d.SetId(utils.BuildResourceId(subId, databaseId)) if err := d.Set("db_id", redis.IntValue(db.DatabaseId)); err != nil { return diag.FromErr(err) @@ -483,10 +485,10 @@ func dataSourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.R if err := d.Set("enable_default_user", redis.Bool(*db.Security.EnableDefaultUser)); err != nil { return diag.FromErr(err) } - if err := d.Set("alert", flattenAlerts(*db.Alerts)); err != nil { + if err := d.Set("alert", pro.FlattenAlerts(*db.Alerts)); err != nil { return diag.FromErr(err) } - if err := d.Set("modules", flattenModules(*db.Modules)); err != nil { + if err := d.Set("modules", pro.FlattenModules(*db.Modules)); err != nil { return diag.FromErr(err) } @@ -495,7 +497,7 @@ func dataSourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.R if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestBackupStatus, err = parseLatestBackupStatus(latestBackupStatus) + parsedLatestBackupStatus, err = utils.ParseLatestBackupStatus(latestBackupStatus) if err != nil { return diag.FromErr(err) } @@ -509,7 +511,7 @@ func dataSourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.R if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestImportStatus, err = parseLatestImportStatus(latestImportStatus) + parsedLatestImportStatus, err = utils.ParseLatestImportStatus(latestImportStatus) if err != nil { return diag.FromErr(err) } @@ -540,7 +542,7 @@ func dataSourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.R if err := d.Set("enable_database_clustering", redis.BoolValue(db.Clustering.Enabled)); err != nil { return diag.FromErr(err) } - if err := d.Set("regex_rules", flattenRegexRules(db.Clustering.RegexRules)); err != nil { + if err := d.Set("regex_rules", pro.FlattenRegexRules(db.Clustering.RegexRules)); err != nil { return diag.FromErr(err) } } diff --git a/provider/datasource_rediscloud_essentials_plan_test.go b/provider/datasource_rediscloud_essentials_plan_test.go index ee1dae1a..f905bf9c 100644 --- a/provider/datasource_rediscloud_essentials_plan_test.go +++ b/provider/datasource_rediscloud_essentials_plan_test.go @@ -1,6 +1,7 @@ package provider import ( + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "regexp" "testing" @@ -8,7 +9,7 @@ import ( func TestAccDataSourceRedisCloudEssentialsPlan_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") const basicPlan = "data.rediscloud_essentials_plan.basic" @@ -50,7 +51,7 @@ func TestAccDataSourceRedisCloudEssentialsPlan_basic(t *testing.T) { func TestAccDataSourceRedisCloudEssentialsPlan_azure(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") const azurePlan = "data.rediscloud_essentials_plan.azure" @@ -92,7 +93,7 @@ func TestAccDataSourceRedisCloudEssentialsPlan_azure(t *testing.T) { func TestAccDataSourceRedisCloudEssentialsPlan_subscriptionId(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") const examplePlan = "data.rediscloud_essentials_plan.example" @@ -136,7 +137,7 @@ func TestAccDataSourceRedisCloudEssentialsPlan_subscriptionId(t *testing.T) { func TestAccDataSourceRedisCloudEssentialsPlan_ambiguous(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -153,7 +154,7 @@ func TestAccDataSourceRedisCloudEssentialsPlan_ambiguous(t *testing.T) { func TestAccDataSourceRedisCloudEssentialsPlan_impossible(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/provider/datasource_rediscloud_payment_method_test.go b/provider/datasource_rediscloud_payment_method_test.go index bc0f572e..7ab8996c 100644 --- a/provider/datasource_rediscloud_payment_method_test.go +++ b/provider/datasource_rediscloud_payment_method_test.go @@ -1,6 +1,7 @@ package provider import ( + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "regexp" "testing" @@ -8,7 +9,7 @@ import ( func TestAccDataSourceRedisCloudPaymentMethod_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/provider/datasource_rediscloud_pro_database_test.go b/provider/datasource_rediscloud_pro_database_test.go index a3a1c0d0..25bd8093 100644 --- a/provider/datasource_rediscloud_pro_database_test.go +++ b/provider/datasource_rediscloud_pro_database_test.go @@ -5,19 +5,24 @@ import ( "os" "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" ) func TestAccDataSourceRedisCloudProDatabase_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") const dataSourceById = "data.rediscloud_database.example-by-id" const dataSourceByName = "data.rediscloud_database.example-by-name" password := acctest.RandString(20) - config := getRedisProDbDatasourceConfig(t, password) + testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + + content := utils.GetTestConfig(t, "./pro/testdata/pro_database_data_source.tf") + config := fmt.Sprintf(content, testCloudAccountName, subscriptionName, password) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, @@ -67,15 +72,3 @@ func TestAccDataSourceRedisCloudProDatabase_basic(t *testing.T) { }) } - -func getRedisProDbDatasourceConfig(t *testing.T, password string) string { - testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") - subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) - - content, err := os.ReadFile("./pro/testdata/testAccDatasourceRedisCloudProDatabase.tf") - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return fmt.Sprintf(string(content), testCloudAccountName, subscriptionName, password) -} diff --git a/provider/datasource_rediscloud_pro_subscription_test.go b/provider/datasource_rediscloud_pro_subscription_test.go index 22a83ee1..5900c250 100644 --- a/provider/datasource_rediscloud_pro_subscription_test.go +++ b/provider/datasource_rediscloud_pro_subscription_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "testing" @@ -10,9 +11,15 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +const ( + proSubscriptionConfigPath = "./pro/testdata/pro_subscription_boilerplate.tf" + proSubscriptionDataSourceConfigPath = "./pro/testdata/pro_subscription_data_source.tf" + AADatabaseProDatasourceConfigPath = "./pro/testdata/active_active_database_with_pro_data_source.tf" +) + func TestAccDataSourceRedisCloudProSubscription_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix("tf-test") @@ -20,19 +27,25 @@ func TestAccDataSourceRedisCloudProSubscription_basic(t *testing.T) { const dataSourceName = "data.rediscloud_subscription.example" testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + proSubConfig := utils.GetTestConfig(t, proSubscriptionConfigPath) + proSubConfig = fmt.Sprintf(proSubConfig, testCloudAccountName, name) + + proSubDataConfig := utils.GetTestConfig(t, proSubscriptionDataSourceConfigPath) + proSubDataConfig = fmt.Sprintf(proSubDataConfig, name) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, ProviderFactories: providerFactories, CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccDatasourceRedisCloudProSubscription, testCloudAccountName, name), + Config: proSubConfig, Check: resource.ComposeAggregateTestCheckFunc( resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(name)), ), }, { - Config: fmt.Sprintf(testAccDatasourceRedisCloudProSubscriptionDataSource, name) + fmt.Sprintf(testAccDatasourceRedisCloudProSubscription, testCloudAccountName, name), + Config: proSubDataConfig + proSubConfig, Check: resource.ComposeAggregateTestCheckFunc( resource.TestMatchResourceAttr(dataSourceName, "name", regexp.MustCompile(name)), resource.TestCheckResourceAttr(dataSourceName, "payment_method", "credit-card"), @@ -61,134 +74,23 @@ func TestAccDataSourceRedisCloudProSubscription_basic(t *testing.T) { func TestAccDataSourceRedisCloudProSubscription_ignoresAA(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) + config := utils.GetTestConfig(t, AADatabaseProDatasourceConfigPath) + config = fmt.Sprintf(config, name+"-subscription", name+"-database", password) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, ProviderFactories: providerFactories, CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccDatasourceRedisCloudAADatabaseWithProDataSource, name+"-subscription", name+"-database", password), + Config: config, ExpectError: regexp.MustCompile("Your query returned no results. Please change your search criteria and try again."), }, }, }) } - -const testAccDatasourceRedisCloudProSubscription = ` -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 = "%s" -} -resource "rediscloud_subscription" "example" { - name = "%s" - payment_method = "credit-card" - 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 - support_oss_cluster_api = false - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - modules = [] - } -} -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "tf-database" - protocol = "redis" - memory_limit_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 -} -` - -const testAccDatasourceRedisCloudProSubscriptionDataSource = ` -data "rediscloud_subscription" "example" { - name = "%s" -} -` - -const testAccDatasourceRedisCloudAADatabaseWithProDataSource = ` -data "rediscloud_payment_method" "card" { - card_type = "Visa" - last_four_numbers = "5556" -} - -resource "rediscloud_active_active_subscription" "example" { - name = "%s" - payment_method_id = data.rediscloud_payment_method.card.id - cloud_provider = "AWS" - creation_plan { - memory_limit_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" "example" { - subscription_id = rediscloud_active_active_subscription.example.id - name = "%s" - memory_limit_in_gb = 3 - support_oss_cluster_api = false - external_endpoint_for_oss_cluster_api = false - enable_tls = false - - global_data_persistence = "none" - global_password = "%s" - global_source_ips = ["192.168.0.0/16", "192.170.0.0/16"] - global_alert { - name = "dataset-size" - value = 1 - } - override_region { - name = "us-east-1" - override_global_data_persistence = "aof-every-write" - override_global_source_ips = ["192.175.0.0/16"] - override_global_password = "region-specific-password" - override_global_alert { - name = "dataset-size" - value = 42 - } - } - override_region { - name = "us-east-2" - } -} -data "rediscloud_database" "example" { - subscription_id = rediscloud_active_active_subscription.example.id - name = rediscloud_active_active_subscription_database.example.name -} -` diff --git a/provider/datasource_rediscloud_regions_test.go b/provider/datasource_rediscloud_regions_test.go index d9736f4b..f0e91582 100644 --- a/provider/datasource_rediscloud_regions_test.go +++ b/provider/datasource_rediscloud_regions_test.go @@ -1,6 +1,7 @@ package provider import ( + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -8,7 +9,7 @@ import ( func TestAccDataSourceRedisCloudRegions_all(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,7 +45,7 @@ func TestAccDataSourceRedisCloudRegions_all(t *testing.T) { func TestAccDataSourceRedisCloudRegions_AWS(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -71,7 +72,7 @@ func TestAccDataSourceRedisCloudRegions_AWS(t *testing.T) { func TestAccDataSourceRedisCloudRegions_GCP(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/provider/datasource_rediscloud_subscription_peerings_test.go b/provider/datasource_rediscloud_subscription_peerings_test.go index 30ef98d2..85e2bd29 100644 --- a/provider/datasource_rediscloud_subscription_peerings_test.go +++ b/provider/datasource_rediscloud_subscription_peerings_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "testing" @@ -12,7 +13,7 @@ import ( func TestAccDataSourceRedisCloudSubscriptionPeerings_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) diff --git a/provider/datasource_rediscloud_transit_gateway.go b/provider/datasource_rediscloud_transit_gateway.go index 288604b8..462695c8 100644 --- a/provider/datasource_rediscloud_transit_gateway.go +++ b/provider/datasource_rediscloud_transit_gateway.go @@ -5,6 +5,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/transit_gateway/attachments" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "strconv" @@ -104,7 +105,7 @@ func dataSourceTransitGatewayRead(ctx context.Context, d *schema.ResourceData, m tgw := tgws[0] tgwId := redis.IntValue(tgw.Id) - d.SetId(buildResourceId(subId, tgwId)) + d.SetId(utils.BuildResourceId(subId, tgwId)) if err := d.Set("tgw_id", tgwId); err != nil { return diag.FromErr(err) } diff --git a/provider/privatelink/testdata/active_active_private_link.tf b/provider/privatelink/testdata/active_active_private_link.tf index 056b09e0..1ec5dc51 100644 --- a/provider/privatelink/testdata/active_active_private_link.tf +++ b/provider/privatelink/testdata/active_active_private_link.tf @@ -74,16 +74,3 @@ data "rediscloud_active_active_private_link" "aa_private_link" { # data "rediscloud_private_link_endpoint_script" "endpoint_script" { # subscription_id = rediscloud_private_link.private_link.subscription_id # } - - -output "resource_aa_private_link" { - value = rediscloud_active_active_private_link.aa_private_link -} - -output "data_aa_private_link" { - value = data.rediscloud_active_active_private_link.aa_private_link -} - -# output "endpoint_script" { -# value = data.rediscloud_private_link_endpoint_script.endpoint_script.endpoint_script -# } diff --git a/provider/privatelink/testdata/pro_private_link.tf b/provider/privatelink/testdata/pro_private_link.tf index 629658a7..304bb3d8 100644 --- a/provider/privatelink/testdata/pro_private_link.tf +++ b/provider/privatelink/testdata/pro_private_link.tf @@ -75,9 +75,8 @@ data "rediscloud_private_link" "pro_private_link" { subscription_id = rediscloud_private_link.pro_private_link.subscription_id } -# not working yet # data "rediscloud_private_link_endpoint_script" "endpoint_script" { -# subscription_id = rediscloud_private_link.private_link.subscription_id +# subscription_id = rediscloud_private_link.pro_private_link.subscription_id # } # # output "endpoint_script" { diff --git a/provider/datasource_rediscloud_pro_database.go b/provider/pro/datasource_rediscloud_pro_database.go similarity index 96% rename from provider/datasource_rediscloud_pro_database.go rename to provider/pro/datasource_rediscloud_pro_database.go index 09c1da82..4b1c6b2c 100644 --- a/provider/datasource_rediscloud_pro_database.go +++ b/provider/pro/datasource_rediscloud_pro_database.go @@ -1,20 +1,21 @@ -package provider +package pro import ( "context" "fmt" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "regexp" "strconv" "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" ) -func dataSourceRedisCloudProDatabase() *schema.Resource { +func DataSourceRedisCloudProDatabase() *schema.Resource { return &schema.Resource{ Description: "The Pro Database data source allows access to the details of an existing database within your Redis Enterprise Cloud account.", ReadContext: dataSourceRedisCloudProDatabaseRead, @@ -437,15 +438,15 @@ func dataSourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.Resource return diag.FromErr(err) } } - if err := d.Set("alert", flattenAlerts(db.Alerts)); err != nil { + if err := d.Set("alert", FlattenAlerts(db.Alerts)); err != nil { return diag.FromErr(err) } - if err := d.Set("module", flattenModules(db.Modules)); err != nil { + if err := d.Set("module", FlattenModules(db.Modules)); err != nil { return diag.FromErr(err) } if db.Clustering != nil { - if err := d.Set("hashing_policy", flattenRegexRules(db.Clustering.RegexRules)); err != nil { + if err := d.Set("hashing_policy", FlattenRegexRules(db.Clustering.RegexRules)); err != nil { return diag.FromErr(err) } } @@ -460,7 +461,7 @@ func dataSourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.Resource if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestBackupStatus, err = parseLatestBackupStatus(latestBackupStatus) + parsedLatestBackupStatus, err = utils.ParseLatestBackupStatus(latestBackupStatus) if err != nil { return diag.FromErr(err) } @@ -474,7 +475,7 @@ func dataSourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.Resource if err != nil { // Forgive errors here, sometimes we just can't get a latest status } else { - parsedLatestImportStatus, err = parseLatestImportStatus(latestImportStatus) + parsedLatestImportStatus, err = utils.ParseLatestImportStatus(latestImportStatus) if err != nil { return diag.FromErr(err) } @@ -483,7 +484,7 @@ func dataSourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - if err := readTags(ctx, api, subId, dbId, d); err != nil { + if err := ReadTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } diff --git a/provider/datasource_rediscloud_pro_subscription.go b/provider/pro/datasource_rediscloud_pro_subscription.go similarity index 96% rename from provider/datasource_rediscloud_pro_subscription.go rename to provider/pro/datasource_rediscloud_pro_subscription.go index 3ab75fee..f87013d9 100644 --- a/provider/datasource_rediscloud_pro_subscription.go +++ b/provider/pro/datasource_rediscloud_pro_subscription.go @@ -1,16 +1,17 @@ -package provider +package pro import ( "context" + "strconv" + "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "strconv" ) -func dataSourceRedisCloudProSubscription() *schema.Resource { +func DataSourceRedisCloudProSubscription() *schema.Resource { return &schema.Resource{ Description: "The Pro Subscription data source allows access to the details of an existing pro subscription within your Redis Enterprise Cloud account.", ReadContext: dataSourceRedisCloudProSubscriptionRead, @@ -243,7 +244,7 @@ func dataSourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Reso }) } - subs = filterSubscriptions(subs, filters) + subs = FilterSubscriptions(subs, filters) if len(subs) == 0 { return diag.Errorf("Your query returned no results. Please change your search criteria and try again.") @@ -274,7 +275,7 @@ func dataSourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Reso if err := d.Set("number_of_databases", redis.IntValue(sub.NumberOfDatabases)); err != nil { return diag.FromErr(err) } - if err := d.Set("cloud_provider", flattenCloudDetails(sub.CloudDetails, false)); err != nil { + if err := d.Set("cloud_provider", FlattenCloudDetails(sub.CloudDetails, false)); err != nil { return diag.FromErr(err) } if err := d.Set("status", redis.StringValue(sub.Status)); err != nil { @@ -287,7 +288,7 @@ func dataSourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Reso if err != nil { return diag.FromErr(err) } - if err := d.Set("maintenance_windows", flattenMaintenance(m)); err != nil { + if err := d.Set("maintenance_windows", FlattenMaintenance(m)); err != nil { return diag.FromErr(err) } @@ -295,7 +296,7 @@ func dataSourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Reso if err != nil { return diag.FromErr(err) } - if err := d.Set("pricing", flattenPricing(pricingList)); err != nil { + if err := d.Set("pricing", FlattenPricing(pricingList)); err != nil { return diag.FromErr(err) } @@ -304,7 +305,7 @@ func dataSourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Reso return diags } -func filterSubscriptions(subs []*subscriptions.Subscription, filters []func(sub *subscriptions.Subscription) bool) []*subscriptions.Subscription { +func FilterSubscriptions(subs []*subscriptions.Subscription, filters []func(sub *subscriptions.Subscription) bool) []*subscriptions.Subscription { var filteredSubs []*subscriptions.Subscription for _, sub := range subs { if filterSub(sub, filters) { diff --git a/provider/pro/flatten.go b/provider/pro/flatten.go new file mode 100644 index 00000000..97c0953e --- /dev/null +++ b/provider/pro/flatten.go @@ -0,0 +1,217 @@ +package pro + +import ( + "context" + "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "strconv" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/RedisLabs/rediscloud-go-api/service/maintenance" + "github.com/RedisLabs/rediscloud-go-api/service/pricing" + "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func flattenSubscriptionAllowlist(ctx context.Context, subId int, api *client.ApiClient) ([]map[string]interface{}, error) { + allowlist, err := api.Client.Subscription.GetCIDRAllowlist(ctx, subId) + if err != nil { + return nil, err + } + + if !isNil(allowlist.Errors) { + return nil, fmt.Errorf("unable to read allowlist for subscription %d: %v", subId, allowlist.Errors) + } + + var cidrs []string + for _, cidr := range allowlist.CIDRIPs { + cidrs = append(cidrs, redis.StringValue(cidr)) + } + var sgs []string + for _, sg := range allowlist.SecurityGroupIDs { + sgs = append(sgs, redis.StringValue(sg)) + } + + tfs := map[string]interface{}{} + + if len(cidrs) != 0 { + tfs["cidrs"] = cidrs + } + if len(sgs) != 0 { + tfs["security_group_ids"] = sgs + } + if len(tfs) == 0 { + return nil, nil + } + + return []map[string]interface{}{tfs}, nil +} + +func isNil(i interface{}) bool { + if i == nil { + return true + } + + if l, ok := i.([]interface{}); ok { + if len(l) == 0 { + return true + } + } + + if m, ok := i.(map[string]interface{}); ok { + if len(m) == 0 { + return true + } + } + + return false +} + +func FlattenCloudDetails(cloudDetails []*subscriptions.CloudDetail, isResource bool) []map[string]interface{} { + var cdl []map[string]interface{} + + for _, currentCloudDetail := range cloudDetails { + + var regions []interface{} + for _, currentRegion := range currentCloudDetail.Regions { + + regionMapString := map[string]interface{}{ + "region": currentRegion.Region, + "multiple_availability_zones": currentRegion.MultipleAvailabilityZones, + "preferred_availability_zones": currentRegion.PreferredAvailabilityZones, + "networks": flattenNetworks(currentRegion.Networking), + } + + if isResource { + if len(currentRegion.Networking) > 0 && !redis.BoolValue(currentRegion.MultipleAvailabilityZones) { + regionMapString["networking_deployment_cidr"] = currentRegion.Networking[0].DeploymentCIDR + } else { + regionMapString["networking_deployment_cidr"] = "" + } + } + + regions = append(regions, regionMapString) + } + + cdlMapString := map[string]interface{}{ + "provider": currentCloudDetail.Provider, + "cloud_account_id": strconv.Itoa(redis.IntValue(currentCloudDetail.CloudAccountID)), + "region": regions, + } + cdl = append(cdl, cdlMapString) + } + + return cdl +} + +func flattenNetworks(networks []*subscriptions.Networking) []map[string]interface{} { + var cdl []map[string]interface{} + + for _, currentNetwork := range networks { + + networkMapString := map[string]interface{}{ + "networking_deployment_cidr": currentNetwork.DeploymentCIDR, + "networking_vpc_id": currentNetwork.VPCId, + "networking_subnet_id": currentNetwork.SubnetID, + } + + cdl = append(cdl, networkMapString) + } + + return cdl +} + +func FlattenAlerts(alerts []*databases.Alert) []map[string]interface{} { + var tfs = make([]map[string]interface{}, 0) + + for _, alert := range alerts { + tf := map[string]interface{}{ + "name": redis.StringValue(alert.Name), + "value": redis.IntValue(alert.Value), + } + tfs = append(tfs, tf) + } + + return tfs +} + +func FlattenModules(modules []*databases.Module) []map[string]interface{} { + + var tfs = make([]map[string]interface{}, 0) + for _, module := range modules { + + tf := map[string]interface{}{ + "name": redis.StringValue(module.Name), + } + tfs = append(tfs, tf) + } + + return tfs +} + +func FlattenRegexRules(rules []*databases.RegexRule) []string { + ret := make([]string, len(rules)) + for _, rule := range rules { + ret[rule.Ordinal] = rule.Pattern + } + + if len(ret) == 2 && ret[0] == ".*\\{(?.*)\\}.*" && ret[1] == "(?.*)" { + // This is the default regex rules - https://docs.redislabs.com/latest/rc/concepts/clustering/#custom-hashing-policy + return []string{} + } + + return ret +} + +func ReadPaymentMethodID(d *schema.ResourceData) (*int, error) { + pmID := d.Get("payment_method_id").(string) + if pmID != "" { + pmID, err := strconv.Atoi(pmID) + if err != nil { + return nil, err + } + return redis.Int(pmID), nil + } + return nil, nil +} + +func FlattenPricing(pricing []*pricing.Pricing) []map[string]interface{} { + var tfs = make([]map[string]interface{}, 0) + for _, p := range pricing { + + tf := map[string]interface{}{ + "database_name": p.DatabaseName, + "type": p.Type, + "type_details": p.TypeDetails, + "quantity": p.Quantity, + "quantity_measurement": p.QuantityMeasurement, + "price_per_unit": p.PricePerUnit, + "price_currency": p.PriceCurrency, + "price_period": p.PricePeriod, + "region": p.Region, + } + tfs = append(tfs, tf) + } + + return tfs +} + +func FlattenMaintenance(m *maintenance.Maintenance) []map[string]interface{} { + var windows []map[string]interface{} + for _, w := range m.Windows { + tfw := map[string]interface{}{ + "start_hour": w.StartHour, + "duration_in_hours": w.DurationInHours, + "days": w.Days, + } + windows = append(windows, tfw) + } + + tf := map[string]interface{}{ + "mode": m.Mode, + "window": windows, + } + + return []map[string]interface{}{tf} +} diff --git a/provider/resource_rediscloud_pro_database.go b/provider/pro/resource_rediscloud_pro_database.go similarity index 87% rename from provider/resource_rediscloud_pro_database.go rename to provider/pro/resource_rediscloud_pro_database.go index f77d34e1..b3230812 100644 --- a/provider/resource_rediscloud_pro_database.go +++ b/provider/pro/resource_rediscloud_pro_database.go @@ -1,27 +1,24 @@ -package provider +package pro import ( "context" "fmt" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "log" "regexp" "strconv" "strings" "time" - redisTags "github.com/RedisLabs/rediscloud-go-api/service/tags" - "github.com/hashicorp/go-cty/cty" - "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" ) -func resourceRedisCloudProDatabase() *schema.Resource { +func ResourceRedisCloudProDatabase() *schema.Resource { return &schema.Resource{ Description: "Creates database resource within a pro subscription in your Redis Enterprise Cloud Account.", CreateContext: resourceRedisCloudProDatabaseCreate, @@ -31,7 +28,7 @@ func resourceRedisCloudProDatabase() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - subId, dbId, err := toDatabaseId(d.Id()) + subId, dbId, err := ToDatabaseId(d.Id()) if err != nil { return nil, err } @@ -41,7 +38,7 @@ func resourceRedisCloudProDatabase() *schema.Resource { if err := d.Set("db_id", dbId); err != nil { return nil, err } - d.SetId(buildResourceId(subId, dbId)) + d.SetId(utils.BuildResourceId(subId, dbId)) return []*schema.ResourceData{d}, nil }, }, @@ -74,7 +71,7 @@ func resourceRedisCloudProDatabase() *schema.Resource { ValidateDiagFunc: validation.ToDiagFunc(validation.StringLenBetween(0, 40)), }, "protocol": { - Description: "The protocol that will be used to access the database, (either ‘redis’ or 'memcached’) ", + Description: "The protocol that will be used to access the database (either ‘redis’ or 'memcached’)", Type: schema.TypeString, ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(databases.ProtocolValues(), false)), Optional: true, @@ -245,12 +242,12 @@ func resourceRedisCloudProDatabase() *schema.Resource { }, }, "modules": { - Description: "Modules to be provisioned in the database", + Description: "Modules to be provisioned in the database. Note: NOT supported for Redis 8.0 and higher as modules are bundled by default.", Type: schema.TypeSet, - // In TF <0.12 List of objects is not supported, so we need to opt-in to use this old behaviour. + // In TF <0.12 List of objects is not supported, so we need to opt in to use this old behaviour. ConfigMode: schema.SchemaConfigModeAttr, Optional: true, - // The API doesn't allow to update/delete modules. Unless we recreate the database. + // The API doesn't allow updating/delete modules. Unless we recreate the database. ForceNew: true, MinItems: 1, Elem: &schema.Resource{ @@ -322,7 +319,7 @@ func resourceRedisCloudProDatabase() *schema.Resource { Description: "Defines the hour automatic backups are made - only applicable when interval is `every-12-hours` or `every-24-hours`", Type: schema.TypeString, Optional: true, - ValidateDiagFunc: isTime(), + ValidateDiagFunc: utils.IsTime(), DiffSuppressFunc: skipDiffIfIntervalIs12And12HourTimeDiff, }, "storage_type": { @@ -346,7 +343,7 @@ func resourceRedisCloudProDatabase() *schema.Resource { Type: schema.TypeString, }, Optional: true, - ValidateDiagFunc: validateTagsfunc, + ValidateDiagFunc: ValidateTagsfunc, }, }, } @@ -401,7 +398,7 @@ func resourceRedisCloudProDatabaseCreate(ctx context.Context, d *schema.Resource }, Modules: createModules, Alerts: createAlerts, - RemoteBackup: buildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), + RemoteBackup: BuildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), } utils.SetStringIfNotEmpty(d, "query_performance_factor", func(s *string) { @@ -447,7 +444,7 @@ func resourceRedisCloudProDatabaseCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - d.SetId(buildResourceId(subId, dbId)) + d.SetId(utils.BuildResourceId(subId, dbId)) // Confirm db + sub active status if err := utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil { @@ -470,7 +467,7 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics - subId, dbId, err := toDatabaseId(d.Id()) + subId, dbId, err := ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -545,11 +542,24 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - if err := d.Set("modules", flattenModules(db.Modules)); err != nil { - return diag.FromErr(err) + // For Redis 8.0+, modules are bundled by default and returned by the API + // Only set modules in state if they were explicitly defined in the config + redisVersion := redis.StringValue(db.RedisVersion) + if redisVersion >= "8.0" { + // Only set modules if they were explicitly configured by the user + if _, ok := d.GetOk("modules"); ok { + if err := d.Set("modules", FlattenModules(db.Modules)); err != nil { + return diag.FromErr(err) + } + } + } else { + // For Redis < 8.0, always set modules from API response + if err := d.Set("modules", FlattenModules(db.Modules)); err != nil { + return diag.FromErr(err) + } } - if err := d.Set("alert", flattenAlerts(db.Alerts)); err != nil { + if err := d.Set("alert", FlattenAlerts(db.Alerts)); err != nil { return diag.FromErr(err) } @@ -596,7 +606,7 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - if err := d.Set("hashing_policy", flattenRegexRules(db.Clustering.RegexRules)); err != nil { + if err := d.Set("hashing_policy", FlattenRegexRules(db.Clustering.RegexRules)); err != nil { return diag.FromErr(err) } @@ -609,11 +619,11 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa } tlsAuthEnabled := *db.Security.TLSClientAuthentication - if err := applyCertificateHints(tlsAuthEnabled, d); err != nil { + if err := utils.ApplyCertificateHints(tlsAuthEnabled, d); err != nil { return diag.FromErr(err) } - if err := d.Set("remote_backup", flattenBackupPlan(db.Backup, d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path").(string))); err != nil { + if err := d.Set("remote_backup", FlattenBackupPlan(db.Backup, d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path").(string))); err != nil { return diag.FromErr(err) } @@ -623,7 +633,7 @@ func resourceRedisCloudProDatabaseRead(ctx context.Context, d *schema.ResourceDa } } - if err := readTags(ctx, api, subId, dbId, d); err != nil { + if err := ReadTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } @@ -637,7 +647,7 @@ func resourceRedisCloudProDatabaseDelete(ctx context.Context, d *schema.Resource var diags diag.Diagnostics subId := d.Get("subscription_id").(int) - _, dbId, err := toDatabaseId(d.Id()) + _, dbId, err := ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -667,7 +677,7 @@ func resourceRedisCloudProDatabaseDelete(ctx context.Context, d *schema.Resource func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { api := meta.(*client.ApiClient) - _, dbId, err := toDatabaseId(d.Id()) + _, dbId, err := ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -700,9 +710,9 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource DataPersistence: utils.GetString(d, "data_persistence"), DataEvictionPolicy: utils.GetString(d, "data_eviction"), - SourceIP: setToStringSlice(d.Get("source_ips").(*schema.Set)), + SourceIP: utils.SetToStringSlice(d.Get("source_ips").(*schema.Set)), Alerts: &alerts, - RemoteBackup: buildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), + RemoteBackup: BuildBackupPlan(d.Get("remote_backup").([]interface{}), d.Get("periodic_backup_path")), EnableDefaultUser: utils.GetBool(d, "enable_default_user"), } @@ -716,7 +726,7 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource } // The below fields are optional and will only be sent in the request if they are present in the Terraform configuration - if len(setToStringSlice(d.Get("source_ips").(*schema.Set))) == 0 { + if len(utils.SetToStringSlice(d.Get("source_ips").(*schema.Set))) == 0 { update.SourceIP = []*string{redis.String("0.0.0.0/0")} } @@ -729,14 +739,14 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource update.Password = redis.String(d.Get("password").(string)) } - update.ReplicaOf = setToStringSlice(d.Get("replica_of").(*schema.Set)) + update.ReplicaOf = utils.SetToStringSlice(d.Get("replica_of").(*schema.Set)) if update.ReplicaOf == nil { update.ReplicaOf = make([]*string, 0) } // The cert validation is done by the API (HTTP 400 is returned if it's invalid). clientSSLCertificate := d.Get("client_ssl_certificate").(string) - clientTLSCertificates := interfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) + clientTLSCertificates := utils.InterfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) enableTLS := d.Get("enable_tls").(bool) if enableTLS { update.EnableTls = redis.Bool(enableTLS) @@ -762,7 +772,7 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource regex := d.Get("hashing_policy").([]interface{}) if len(regex) != 0 { - update.RegexRules = interfaceToStringSlice(regex) + update.RegexRules = utils.InterfaceToStringSlice(regex) } backupPath := d.Get("periodic_backup_path").(string) @@ -822,7 +832,7 @@ func resourceRedisCloudProDatabaseUpdate(ctx context.Context, d *schema.Resource } // The Tags API is synchronous so we shouldn't have to wait for anything - if err := writeTags(ctx, api, subId, dbId, d); err != nil { + if err := WriteTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } @@ -844,16 +854,20 @@ func upgradeRedisVersion(ctx context.Context, api *client.ApiClient, subId int, log.Printf("[INFO] Redis version change request to %s accepted by API", newVersion) - // wait for upgrade if err := utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil { utils.SubscriptionMutex.Unlock(subId) return diag.FromErr(err), true } + if err := utils.WaitForSubscriptionToBeActive(ctx, subId, api); err != nil { + utils.SubscriptionMutex.Unlock(subId) + return diag.FromErr(err), true + } + return nil, false } -func buildBackupPlan(data interface{}, periodicBackupPath interface{}) *databases.DatabaseBackupConfig { +func BuildBackupPlan(data interface{}, periodicBackupPath interface{}) *databases.DatabaseBackupConfig { var d map[string]interface{} switch v := data.(type) { @@ -884,7 +898,7 @@ func buildBackupPlan(data interface{}, periodicBackupPath interface{}) *database return &config } -func flattenBackupPlan(backup *databases.Backup, existing []interface{}, periodicBackupPath string) []map[string]interface{} { +func FlattenBackupPlan(backup *databases.Backup, existing []interface{}, periodicBackupPath string) []map[string]interface{} { if backup == nil || !redis.BoolValue(backup.Enabled) || periodicBackupPath != "" { return nil } @@ -905,7 +919,7 @@ func flattenBackupPlan(backup *databases.Backup, existing []interface{}, periodi } } -func toDatabaseId(id string) (int, int, error) { +func ToDatabaseId(id string) (int, int, error) { parts := strings.Split(id, "/") if len(parts) > 2 { @@ -961,49 +975,16 @@ func skipDiffIfIntervalIs12And12HourTimeDiff(k, oldValue, newValue string, d *sc func customizeDiff() schema.CustomizeDiffFunc { return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { - if err := validateQueryPerformanceFactor()(ctx, diff, meta); err != nil { + if err := validateModulesForRedis8()(ctx, diff, meta); err != nil { return err } - if err := remoteBackupIntervalSetCorrectly("remote_backup")(ctx, diff, meta); err != nil { + if err := RemoteBackupIntervalSetCorrectly("remote_backup")(ctx, diff, meta); err != nil { return err } return nil } } -func validateQueryPerformanceFactor() schema.CustomizeDiffFunc { - return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { - // Check if "query_performance_factor" is set - qpf, qpfExists := diff.GetOk("query_performance_factor") - - // Ensure "modules" is explicitly defined in the HCL - _, modulesExists := diff.GetOkExists("modules") - - if qpfExists && qpf.(string) != "" { - if !modulesExists { - return fmt.Errorf(`"query_performance_factor" requires the "modules" key to be explicitly defined in HCL`) - } - - // Retrieve modules as a slice of interfaces - rawModules := diff.Get("modules").(*schema.Set).List() - - // Convert modules to []map[string]interface{} - var modules []map[string]interface{} - for _, rawModule := range rawModules { - if moduleMap, ok := rawModule.(map[string]interface{}); ok { - modules = append(modules, moduleMap) - } - } - - // Check if "RediSearch" exists - if !containsDBModule(modules, "RediSearch") { - return fmt.Errorf(`"query_performance_factor" requires the "modules" list to contain "RediSearch"`) - } - } - return nil - } -} - // Helper function to check if a module exists func containsDBModule(modules []map[string]interface{}, moduleName string) bool { for _, module := range modules { @@ -1014,7 +995,26 @@ func containsDBModule(modules []map[string]interface{}, moduleName string) bool return false } -func remoteBackupIntervalSetCorrectly(key string) schema.CustomizeDiffFunc { +func validateModulesForRedis8() schema.CustomizeDiffFunc { + return func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { + redisVersion, versionExists := diff.GetOk("redis_version") + modules, modulesExists := diff.GetOkExists("modules") + + 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) + } + } + } + return nil + } +} + +func RemoteBackupIntervalSetCorrectly(key string) schema.CustomizeDiffFunc { // Validate multiple attributes - https://github.com/hashicorp/terraform-plugin-sdk/issues/233 return func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error { @@ -1035,48 +1035,3 @@ func remoteBackupIntervalSetCorrectly(key string) schema.CustomizeDiffFunc { } } - -func readTags(ctx context.Context, api *client.ApiClient, subId int, databaseId int, d *schema.ResourceData) error { - tags := make(map[string]string) - tagResponse, err := api.Client.Tags.Get(ctx, subId, databaseId) - if err != nil { - return err - } - if tagResponse.Tags != nil { - for _, t := range *tagResponse.Tags { - tags[redis.StringValue(t.Key)] = redis.StringValue(t.Value) - } - } - return d.Set("tags", tags) -} - -func writeTags(ctx context.Context, api *client.ApiClient, subId int, databaseId int, d *schema.ResourceData) error { - tags := make([]*redisTags.Tag, 0) - tState := d.Get("tags").(map[string]interface{}) - for k, v := range tState { - tags = append(tags, &redisTags.Tag{ - Key: redis.String(k), - Value: redis.String(v.(string)), - }) - } - return api.Client.Tags.Put(ctx, subId, databaseId, redisTags.AllTags{Tags: &tags}) -} - -func validateTagsfunc(tagsRaw interface{}, _ cty.Path) diag.Diagnostics { - tags := tagsRaw.(map[string]interface{}) - invalid := make([]string, 0) - for k, v := range tags { - if k != strings.ToLower(k) { - invalid = append(invalid, k) - } - vStr := v.(string) - if vStr != strings.ToLower(vStr) { - invalid = append(invalid, vStr) - } - } - - if len(invalid) > 0 { - return diag.Errorf("tag keys and values must be lower case, invalid entries: %s", strings.Join(invalid, ", ")) - } - return nil -} diff --git a/provider/resource_rediscloud_pro_subscription.go b/provider/pro/resource_rediscloud_pro_subscription.go similarity index 86% rename from provider/resource_rediscloud_pro_subscription.go rename to provider/pro/resource_rediscloud_pro_subscription.go index 3fc61ebf..0aca593c 100644 --- a/provider/resource_rediscloud_pro_subscription.go +++ b/provider/pro/resource_rediscloud_pro_subscription.go @@ -1,4 +1,4 @@ -package provider +package pro import ( "bytes" @@ -14,7 +14,6 @@ import ( "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" "github.com/RedisLabs/rediscloud-go-api/service/databases" "github.com/RedisLabs/rediscloud-go-api/service/maintenance" - "github.com/RedisLabs/rediscloud-go-api/service/pricing" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" @@ -35,7 +34,7 @@ func containsModule(modules []interface{}, requiredModule string) bool { return false } -func resourceRedisCloudProSubscription() *schema.Resource { +func ResourceRedisCloudProSubscription() *schema.Resource { return &schema.Resource{ CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { @@ -581,7 +580,7 @@ func resourceRedisCloudProSubscriptionCreate(ctx context.Context, d *schema.Reso name := d.Get("name").(string) paymentMethod := d.Get("payment_method").(string) - paymentMethodID, err := readPaymentMethodID(d) + paymentMethodID, err := ReadPaymentMethodID(d) if err != nil { return diag.FromErr(err) } @@ -593,7 +592,7 @@ func resourceRedisCloudProSubscriptionCreate(ctx context.Context, d *schema.Reso // Create creation-plan databases planMap := plan[0].(map[string]interface{}) - dbs, diags := buildSubscriptionCreatePlanDatabases(memoryStorage, planMap) + dbs, diags := BuildSubscriptionCreatePlanDatabases(memoryStorage, planMap) if diags.HasError() { return diags } @@ -628,7 +627,7 @@ func resourceRedisCloudProSubscriptionCreate(ctx context.Context, d *schema.Reso // If in a CMK flow, verify the pending state if cmkEnabled { - err = waitForSubscriptionToBeEncryptionKeyPending(ctx, subId, api) + err = WaitForSubscriptionToBeEncryptionKeyPending(ctx, subId, api) if err != nil { return append(diags, diag.FromErr(err)...) } @@ -709,7 +708,7 @@ func resourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Resour return diag.FromErr(err) } - if err := d.Set("cloud_provider", flattenCloudDetails(subscription.CloudDetails, true)); err != nil { + if err := d.Set("cloud_provider", FlattenCloudDetails(subscription.CloudDetails, true)); err != nil { return diag.FromErr(err) } @@ -735,7 +734,7 @@ func resourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Resour if err != nil { return diag.FromErr(err) } - if err := d.Set("maintenance_windows", flattenMaintenance(m)); err != nil { + if err := d.Set("maintenance_windows", FlattenMaintenance(m)); err != nil { return diag.FromErr(err) } @@ -743,7 +742,7 @@ func resourceRedisCloudProSubscriptionRead(ctx context.Context, d *schema.Resour if err != nil { return diag.FromErr(err) } - if err := d.Set("pricing", flattenPricing(pricingList)); err != nil { + if err := d.Set("pricing", FlattenPricing(pricingList)); err != nil { return diag.FromErr(err) } } @@ -784,8 +783,8 @@ func resourceRedisCloudProSubscriptionUpdate(ctx context.Context, d *schema.Reso } if d.HasChange("allowlist") { - cidrs := setToStringSlice(d.Get("allowlist.0.cidrs").(*schema.Set)) - sgs := setToStringSlice(d.Get("allowlist.0.security_group_ids").(*schema.Set)) + cidrs := utils.SetToStringSlice(d.Get("allowlist.0.cidrs").(*schema.Set)) + sgs := utils.SetToStringSlice(d.Get("allowlist.0.security_group_ids").(*schema.Set)) err := api.Client.Subscription.UpdateCIDRAllowlist(ctx, subId, subscriptions.UpdateCIDRAllowlist{ CIDRIPs: cidrs, @@ -805,7 +804,7 @@ func resourceRedisCloudProSubscriptionUpdate(ctx context.Context, d *schema.Reso } if d.HasChange("payment_method_id") { - paymentMethodID, err := readPaymentMethodID(d) + paymentMethodID, err := ReadPaymentMethodID(d) if err != nil { return diag.FromErr(err) } @@ -834,7 +833,7 @@ func resourceRedisCloudProSubscriptionUpdate(ctx context.Context, d *schema.Reso windows = append(windows, &maintenance.Window{ StartHour: redis.Int(wMap["start_hour"].(int)), DurationInHours: redis.Int(wMap["duration_in_hours"].(int)), - Days: interfaceToStringSlice(wMap["days"].([]interface{})), + Days: utils.InterfaceToStringSlice(wMap["days"].([]interface{})), }) } @@ -943,7 +942,7 @@ func resourceRedisCloudProSubscriptionDelete(ctx context.Context, d *schema.Reso d.SetId("") - err = waitForSubscriptionToBeDeleted(ctx, subId, api) + err = WaitForSubscriptionToBeDeleted(ctx, subId, api) if err != nil { return diag.FromErr(err) } @@ -976,7 +975,7 @@ func buildCreateCloudProviders(providers interface{}) ([]*subscriptions.CreateCl createRegion := subscriptions.CreateRegion{ Region: redis.String(regionStr), MultipleAvailabilityZones: redis.Bool(multipleAvailabilityZones), - PreferredAvailabilityZones: interfaceToStringSlice(preferredAZs), + PreferredAvailabilityZones: utils.InterfaceToStringSlice(preferredAZs), } if v, ok := regionMap["networking_deployment_cidr"]; ok && v != "" { @@ -1008,7 +1007,7 @@ func buildCreateCloudProviders(providers interface{}) ([]*subscriptions.CreateCl return createCloudProviders, nil } -func buildSubscriptionCreatePlanDatabases(memoryStorage string, planMap map[string]interface{}) ([]*subscriptions.CreateDatabase, diag.Diagnostics) { +func BuildSubscriptionCreatePlanDatabases(memoryStorage string, planMap map[string]interface{}) ([]*subscriptions.CreateDatabase, diag.Diagnostics) { createDatabases := make([]*subscriptions.CreateDatabase, 0) @@ -1020,7 +1019,7 @@ func buildSubscriptionCreatePlanDatabases(memoryStorage string, planMap map[stri numDatabases := planMap["quantity"].(int) supportOSSClusterAPI := planMap["support_oss_cluster_api"].(bool) replication := planMap["replication"].(bool) - planModules := interfaceToStringSlice(planMap["modules"].([]interface{})) + planModules := utils.InterfaceToStringSlice(planMap["modules"].([]interface{})) memoryLimitInGB := 0.0 if v, ok := planMap["memory_limit_in_gb"]; ok && v != nil { @@ -1137,7 +1136,7 @@ func createDatabase(dbName string, idx *int, modules []*subscriptions.CreateModu return dbs } -func waitForSubscriptionToBeEncryptionKeyPending(ctx context.Context, id int, api *client.ApiClient) error { +func WaitForSubscriptionToBeEncryptionKeyPending(ctx context.Context, id int, api *client.ApiClient) error { wait := &retry.StateChangeConf{ Pending: []string{subscriptions.SubscriptionStatusPending}, Target: []string{subscriptions.SubscriptionStatusEncryptionKeyPending, subscriptions.SubscriptionStatusActive}, @@ -1163,7 +1162,7 @@ func waitForSubscriptionToBeEncryptionKeyPending(ctx context.Context, id int, ap return nil } -func waitForSubscriptionToBeDeleted(ctx context.Context, id int, api *client.ApiClient) error { +func WaitForSubscriptionToBeDeleted(ctx context.Context, id int, api *client.ApiClient) error { wait := &retry.StateChangeConf{ Pending: []string{subscriptions.SubscriptionStatusDeleting}, Target: []string{"deleted"}, // TODO: update this with deleted field in SDK @@ -1191,205 +1190,3 @@ func waitForSubscriptionToBeDeleted(ctx context.Context, id int, api *client.Api return nil } - -func flattenSubscriptionAllowlist(ctx context.Context, subId int, api *client.ApiClient) ([]map[string]interface{}, error) { - allowlist, err := api.Client.Subscription.GetCIDRAllowlist(ctx, subId) - if err != nil { - return nil, err - } - - if !isNil(allowlist.Errors) { - return nil, fmt.Errorf("unable to read allowlist for subscription %d: %v", subId, allowlist.Errors) - } - - var cidrs []string - for _, cidr := range allowlist.CIDRIPs { - cidrs = append(cidrs, redis.StringValue(cidr)) - } - var sgs []string - for _, sg := range allowlist.SecurityGroupIDs { - sgs = append(sgs, redis.StringValue(sg)) - } - - tfs := map[string]interface{}{} - - if len(cidrs) != 0 { - tfs["cidrs"] = cidrs - } - if len(sgs) != 0 { - tfs["security_group_ids"] = sgs - } - if len(tfs) == 0 { - return nil, nil - } - - return []map[string]interface{}{tfs}, nil -} - -func isNil(i interface{}) bool { - if i == nil { - return true - } - - if l, ok := i.([]interface{}); ok { - if len(l) == 0 { - return true - } - } - - if m, ok := i.(map[string]interface{}); ok { - if len(m) == 0 { - return true - } - } - - return false -} - -func flattenCloudDetails(cloudDetails []*subscriptions.CloudDetail, isResource bool) []map[string]interface{} { - var cdl []map[string]interface{} - - for _, currentCloudDetail := range cloudDetails { - - var regions []interface{} - for _, currentRegion := range currentCloudDetail.Regions { - - regionMapString := map[string]interface{}{ - "region": currentRegion.Region, - "multiple_availability_zones": currentRegion.MultipleAvailabilityZones, - "preferred_availability_zones": currentRegion.PreferredAvailabilityZones, - "networks": flattenNetworks(currentRegion.Networking), - } - - if isResource { - if len(currentRegion.Networking) > 0 && !redis.BoolValue(currentRegion.MultipleAvailabilityZones) { - regionMapString["networking_deployment_cidr"] = currentRegion.Networking[0].DeploymentCIDR - } else { - regionMapString["networking_deployment_cidr"] = "" - } - } - - regions = append(regions, regionMapString) - } - - cdlMapString := map[string]interface{}{ - "provider": currentCloudDetail.Provider, - "cloud_account_id": strconv.Itoa(redis.IntValue(currentCloudDetail.CloudAccountID)), - "region": regions, - } - cdl = append(cdl, cdlMapString) - } - - return cdl -} - -func flattenNetworks(networks []*subscriptions.Networking) []map[string]interface{} { - var cdl []map[string]interface{} - - for _, currentNetwork := range networks { - - networkMapString := map[string]interface{}{ - "networking_deployment_cidr": currentNetwork.DeploymentCIDR, - "networking_vpc_id": currentNetwork.VPCId, - "networking_subnet_id": currentNetwork.SubnetID, - } - - cdl = append(cdl, networkMapString) - } - - return cdl -} - -func flattenAlerts(alerts []*databases.Alert) []map[string]interface{} { - var tfs = make([]map[string]interface{}, 0) - - for _, alert := range alerts { - tf := map[string]interface{}{ - "name": redis.StringValue(alert.Name), - "value": redis.IntValue(alert.Value), - } - tfs = append(tfs, tf) - } - - return tfs -} - -func flattenModules(modules []*databases.Module) []map[string]interface{} { - - var tfs = make([]map[string]interface{}, 0) - for _, module := range modules { - - tf := map[string]interface{}{ - "name": redis.StringValue(module.Name), - } - tfs = append(tfs, tf) - } - - return tfs -} - -func flattenRegexRules(rules []*databases.RegexRule) []string { - ret := make([]string, len(rules)) - for _, rule := range rules { - ret[rule.Ordinal] = rule.Pattern - } - - if len(ret) == 2 && ret[0] == ".*\\{(?.*)\\}.*" && ret[1] == "(?.*)" { - // This is the default regex rules - https://docs.redislabs.com/latest/rc/concepts/clustering/#custom-hashing-policy - return []string{} - } - - return ret -} - -func readPaymentMethodID(d *schema.ResourceData) (*int, error) { - pmID := d.Get("payment_method_id").(string) - if pmID != "" { - pmID, err := strconv.Atoi(pmID) - if err != nil { - return nil, err - } - return redis.Int(pmID), nil - } - return nil, nil -} - -func flattenPricing(pricing []*pricing.Pricing) []map[string]interface{} { - var tfs = make([]map[string]interface{}, 0) - for _, p := range pricing { - - tf := map[string]interface{}{ - "database_name": p.DatabaseName, - "type": p.Type, - "type_details": p.TypeDetails, - "quantity": p.Quantity, - "quantity_measurement": p.QuantityMeasurement, - "price_per_unit": p.PricePerUnit, - "price_currency": p.PriceCurrency, - "price_period": p.PricePeriod, - "region": p.Region, - } - tfs = append(tfs, tf) - } - - return tfs -} - -func flattenMaintenance(m *maintenance.Maintenance) []map[string]interface{} { - var windows []map[string]interface{} - for _, w := range m.Windows { - tfw := map[string]interface{}{ - "start_hour": w.StartHour, - "duration_in_hours": w.DurationInHours, - "days": w.Days, - } - windows = append(windows, tfw) - } - - tf := map[string]interface{}{ - "mode": m.Mode, - "window": windows, - } - - return []map[string]interface{}{tf} -} diff --git a/provider/pro/testdata/active_active_database_with_pro_data_source.tf b/provider/pro/testdata/active_active_database_with_pro_data_source.tf new file mode 100644 index 00000000..656028e1 --- /dev/null +++ b/provider/pro/testdata/active_active_database_with_pro_data_source.tf @@ -0,0 +1,64 @@ +locals { + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%s" +} + +data "rediscloud_payment_method" "card" { + card_type = "Visa" + last_four_numbers = "5556" +} + +resource "rediscloud_active_active_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + creation_plan { + memory_limit_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" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = local.rediscloud_subscription_name + memory_limit_in_gb = 3 + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + enable_tls = false + + global_data_persistence = "none" + global_password = local.rediscloud_database_password + global_source_ips = ["192.168.0.0/16", "192.170.0.0/16"] + global_alert { + name = "dataset-size" + value = 1 + } + override_region { + name = "us-east-1" + override_global_data_persistence = "aof-every-write" + override_global_source_ips = ["192.175.0.0/16"] + override_global_password = "region-specific-password" + override_global_alert { + name = "dataset-size" + value = 42 + } + } + override_region { + name = "us-east-2" + } +} +data "rediscloud_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = rediscloud_active_active_subscription_database.example.name +} diff --git a/provider/pro/testdata/pro_database.tf b/provider/pro/testdata/pro_database.tf new file mode 100644 index 00000000..ed6d15d5 --- /dev/null +++ b/provider/pro/testdata/pro_database.tf @@ -0,0 +1,74 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%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 + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 3 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = local.rediscloud_database_password + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = "7.2" + + alert { + name = "dataset-size" + value = 1 + } + + modules = [ + { + name = "RedisBloom" + } + ] + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} diff --git a/provider/pro/testdata/testAccDatasourceRedisCloudProDatabase.tf b/provider/pro/testdata/pro_database_data_source.tf similarity index 100% rename from provider/pro/testdata/testAccDatasourceRedisCloudProDatabase.tf rename to provider/pro/testdata/pro_database_data_source.tf diff --git a/provider/pro/testdata/pro_database_invalid_time_utc.tf b/provider/pro/testdata/pro_database_invalid_time_utc.tf new file mode 100644 index 00000000..03a02928 --- /dev/null +++ b/provider/pro/testdata/pro_database_invalid_time_utc.tf @@ -0,0 +1,52 @@ +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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + 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 { + dataset_size_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 = "example-no-protocol" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + remote_backup { + interval = "every-6-hours" + time_utc = "16:00" + storage_type = "aws-s3" + storage_path = "uri://interval.not.12.or.24.hours.test" + } +} diff --git a/provider/pro/testdata/pro_database_multi_modules.tf b/provider/pro/testdata/pro_database_multi_modules.tf new file mode 100644 index 00000000..a4048219 --- /dev/null +++ b/provider/pro/testdata/pro_database_multi_modules.tf @@ -0,0 +1,66 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + database_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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + memory_storage = "ram" + allowlist { + cidrs = ["192.168.0.0/16"] + security_group_ids = [] + } + 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 { + dataset_size_in_gb = 1 + quantity = 1 + replication = false + support_oss_cluster_api = false + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + modules = ["RedisJSON", "RedisBloom"] + } +} + +resource "rediscloud_subscription_database" "example" { + subscription_id = rediscloud_subscription.example.id + name = local.database_name + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + modules = [ + { + name = "RedisJSON" + }, + { + name = "RedisBloom" + } + ] + alert { + name = "latency" + value = 11 + } +} diff --git a/provider/pro/testdata/pro_database_no_password.tf b/provider/pro/testdata/pro_database_no_password.tf new file mode 100644 index 00000000..85c3a581 --- /dev/null +++ b/provider/pro/testdata/pro_database_no_password.tf @@ -0,0 +1,47 @@ +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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + 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 { + dataset_size_in_gb = 1 + quantity = 1 + replication = false + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + } +} + +resource "rediscloud_subscription_database" "no_password_database" { + subscription_id = rediscloud_subscription.example.id + name = "example-no-password" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 +} diff --git a/provider/pro/testdata/pro_database_optional_attributes.tf b/provider/pro/testdata/pro_database_optional_attributes.tf new file mode 100644 index 00000000..70b45209 --- /dev/null +++ b/provider/pro/testdata/pro_database_optional_attributes.tf @@ -0,0 +1,48 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + port_number = %d +} + +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 + 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 { + dataset_size_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 = "example-no-protocol" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + port = local.port_number +} diff --git a/provider/pro/testdata/pro_database_redis_7.tf b/provider/pro/testdata/pro_database_redis_7.tf new file mode 100644 index 00000000..5a5d5266 --- /dev/null +++ b/provider/pro/testdata/pro_database_redis_7.tf @@ -0,0 +1,68 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%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 + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = local.rediscloud_database_password + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = "7.2" + + alert { + name = "dataset-size" + value = 1 + } + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} diff --git a/provider/pro/testdata/pro_database_redis_8.tf b/provider/pro/testdata/pro_database_redis_8.tf new file mode 100644 index 00000000..9f967e5c --- /dev/null +++ b/provider/pro/testdata/pro_database_redis_8.tf @@ -0,0 +1,68 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%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 + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 3 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = local.rediscloud_database_password + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = "8.0" + + alert { + name = "dataset-size" + value = 1 + } + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} diff --git a/provider/pro/testdata/pro_database_redis_8_with_modules.tf b/provider/pro/testdata/pro_database_redis_8_with_modules.tf new file mode 100644 index 00000000..8b9ed6a7 --- /dev/null +++ b/provider/pro/testdata/pro_database_redis_8_with_modules.tf @@ -0,0 +1,56 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%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 + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = local.rediscloud_database_password + redis_version = "8.0" + + modules = [ + { + name = "RedisBloom" + } + ] +} diff --git a/provider/pro/testdata/pro_database_resp_versions.tf b/provider/pro/testdata/pro_database_resp_versions.tf new file mode 100644 index 00000000..4d7c6d82 --- /dev/null +++ b/provider/pro/testdata/pro_database_resp_versions.tf @@ -0,0 +1,50 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + port_number = %d + resp_version = "%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 + 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 { + dataset_size_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 = "example" + dataset_size_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + port = local.port_number + resp_version = local.resp_version +} diff --git a/provider/pro/testdata/pro_database_update.tf b/provider/pro/testdata/pro_database_update.tf new file mode 100644 index 00000000..453e7ac1 --- /dev/null +++ b/provider/pro/testdata/pro_database_update.tf @@ -0,0 +1,66 @@ +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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + 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 { + dataset_size_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 = "example-updated" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "aof-every-write" + data_eviction = "volatile-lru" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 2000 + password = "updated-password" + support_oss_cluster_api = true + external_endpoint_for_oss_cluster_api = true + replication = true + average_item_size_in_bytes = 0 + enable_default_user = true + redis_version = "7.2" + + alert { + name = "dataset-size" + value = 80 + } + + modules = [ + { + name = "RedisBloom" + } + ] +} diff --git a/provider/pro/testdata/pro_database_update_destroy_alerts.tf b/provider/pro/testdata/pro_database_update_destroy_alerts.tf new file mode 100644 index 00000000..2f580b6a --- /dev/null +++ b/provider/pro/testdata/pro_database_update_destroy_alerts.tf @@ -0,0 +1,59 @@ +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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method_id = data.rediscloud_payment_method.card.id + 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 { + dataset_size_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 = "example-updated" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "aof-every-write" + data_eviction = "volatile-lru" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 2000 + password = "updated-password" + support_oss_cluster_api = true + external_endpoint_for_oss_cluster_api = true + replication = true + average_item_size_in_bytes = 0 + + modules = [ + { + name = "RedisBloom" + } + ] +} diff --git a/provider/pro/testdata/testAccResourceRedisCloudProDatabaseUpgrade.tf b/provider/pro/testdata/pro_database_upgrade.tf similarity index 100% rename from provider/pro/testdata/testAccResourceRedisCloudProDatabaseUpgrade.tf rename to provider/pro/testdata/pro_database_upgrade.tf diff --git a/provider/pro/testdata/pro_database_with_replica.tf b/provider/pro/testdata/pro_database_with_replica.tf new file mode 100644 index 00000000..eabe9bfc --- /dev/null +++ b/provider/pro/testdata/pro_database_with_replica.tf @@ -0,0 +1,84 @@ +locals { + rediscloud_cloud_account = "%s" + rediscloud_subscription_name = "%s" + rediscloud_database_password = "%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 + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = local.rediscloud_database_password + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = "7.2" + + alert { + name = "dataset-size" + value = 1 + } + + modules = [ + { + name = "RedisBloom" + } + ] + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} + +resource "rediscloud_subscription_database" "example_replica" { + subscription_id = rediscloud_subscription.example.id + name = "example-replica" + protocol = "redis" + dataset_size_in_gb = 1 + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + replica_of = ["redis://${rediscloud_subscription_database.example.public_endpoint}"] +} diff --git a/provider/pro/testdata/pro_subscription_boilerplate.tf b/provider/pro/testdata/pro_subscription_boilerplate.tf new file mode 100644 index 00000000..9ec75260 --- /dev/null +++ b/provider/pro/testdata/pro_subscription_boilerplate.tf @@ -0,0 +1,34 @@ +locals { + rediscloud_subscription_name = "%s" +} + + +resource "rediscloud_subscription_database" "example" { + subscription_id = rediscloud_subscription.example.id + name = local.rediscloud_subscription_name + protocol = "redis" + dataset_size_in_gb = 1 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = "%s" + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = 7.2 + + alert { + name = "dataset-size" + value = 1 + } + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} diff --git a/provider/pro/testdata/pro_subscription_data_source.tf b/provider/pro/testdata/pro_subscription_data_source.tf new file mode 100644 index 00000000..abc724bd --- /dev/null +++ b/provider/pro/testdata/pro_subscription_data_source.tf @@ -0,0 +1,48 @@ +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_subscription" "example" { + name = local.rediscloud_subscription_name + payment_method = "credit-card" + 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 + support_oss_cluster_api = false + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + modules = [] + } +} +resource "rediscloud_subscription_database" "example" { + subscription_id = rediscloud_subscription.example.id + name = "tf-database" + protocol = "redis" + memory_limit_in_gb = 1 + data_persistence = "none" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 +} diff --git a/provider/pro/utils.go b/provider/pro/utils.go new file mode 100644 index 00000000..4488d90c --- /dev/null +++ b/provider/pro/utils.go @@ -0,0 +1,58 @@ +package pro + +import ( + "context" + "strings" + + "github.com/RedisLabs/rediscloud-go-api/redis" + redisTags "github.com/RedisLabs/rediscloud-go-api/service/tags" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ReadTags(ctx context.Context, api *client.ApiClient, subId int, databaseId int, d *schema.ResourceData) error { + tags := make(map[string]string) + tagResponse, err := api.Client.Tags.Get(ctx, subId, databaseId) + if err != nil { + return err + } + if tagResponse.Tags != nil { + for _, t := range *tagResponse.Tags { + tags[redis.StringValue(t.Key)] = redis.StringValue(t.Value) + } + } + return d.Set("tags", tags) +} + +func WriteTags(ctx context.Context, api *client.ApiClient, subId int, databaseId int, d *schema.ResourceData) error { + tags := make([]*redisTags.Tag, 0) + tState := d.Get("tags").(map[string]interface{}) + for k, v := range tState { + tags = append(tags, &redisTags.Tag{ + Key: redis.String(k), + Value: redis.String(v.(string)), + }) + } + return api.Client.Tags.Put(ctx, subId, databaseId, redisTags.AllTags{Tags: &tags}) +} + +func ValidateTagsfunc(tagsRaw interface{}, _ cty.Path) diag.Diagnostics { + tags := tagsRaw.(map[string]interface{}) + invalid := make([]string, 0) + for k, v := range tags { + if k != strings.ToLower(k) { + invalid = append(invalid, k) + } + vStr := v.(string) + if vStr != strings.ToLower(vStr) { + invalid = append(invalid, vStr) + } + } + + if len(invalid) > 0 { + return diag.Errorf("tag keys and values must be lower case, invalid entries: %s", strings.Join(invalid, ", ")) + } + return nil +} diff --git a/provider/provider.go b/provider/provider.go index 0f6cfcf8..1e44c3ea 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -3,11 +3,12 @@ package provider import ( "context" "fmt" - client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/privatelink" "log" "strings" + client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/privatelink" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -48,8 +49,8 @@ func New(version string) func() *schema.Provider { // Note the difference in public data-source name and the file/method name. // This is to help the developer relate their changes to what they would see happening in the Redis Console. // == flexible == pro - "rediscloud_subscription": dataSourceRedisCloudProSubscription(), - "rediscloud_database": dataSourceRedisCloudProDatabase(), + "rediscloud_subscription": pro.DataSourceRedisCloudProSubscription(), + "rediscloud_database": pro.DataSourceRedisCloudProDatabase(), "rediscloud_database_modules": dataSourceRedisCloudDatabaseModules(), "rediscloud_payment_method": dataSourceRedisCloudPaymentMethod(), "rediscloud_regions": dataSourceRedisCloudRegions(), @@ -83,8 +84,8 @@ func New(version string) func() *schema.Provider { "rediscloud_essentials_database": resourceRedisCloudEssentialsDatabase(), // Note the difference in public resource name and the file/method name. // == flexible == pro - "rediscloud_subscription": resourceRedisCloudProSubscription(), - "rediscloud_subscription_database": resourceRedisCloudProDatabase(), + "rediscloud_subscription": pro.ResourceRedisCloudProSubscription(), + "rediscloud_subscription_database": pro.ResourceRedisCloudProDatabase(), "rediscloud_subscription_peering": resourceRedisCloudSubscriptionPeering(), "rediscloud_private_service_connect": resourceRedisCloudPrivateServiceConnect(), "rediscloud_private_service_connect_endpoint": resourceRedisCloudPrivateServiceConnectEndpoint(), diff --git a/provider/rediscloud_acl_role_test.go b/provider/rediscloud_acl_role_test.go index ee253f55..97458432 100644 --- a/provider/rediscloud_acl_role_test.go +++ b/provider/rediscloud_acl_role_test.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/RedisLabs/rediscloud-go-api/redis" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -16,7 +17,7 @@ import ( func TestAccResourceRedisCloudAclRole_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") prefix := acctest.RandomWithPrefix(testResourcePrefix) exampleCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") @@ -27,11 +28,14 @@ func TestAccResourceRedisCloudAclRole_CRUDI(t *testing.T) { testRoleName := prefix + "-test-role" testRoleNameUpdated := testRoleName + "-updated" - testCreateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + proSubBoilerPlate := utils.GetTestConfig(t, "./pro/testdata/pro_subscription_boilerplate.tf") + proSubBoilerPlateFormatted := fmt.Sprintf(proSubBoilerPlate, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testCreateTerraform := proSubBoilerPlateFormatted + testAccResourceRedisCloudProDatabaseAcl + fmt.Sprintf(referencableRule, exampleRuleName) + fmt.Sprintf(testRole, testRoleName) - testUpdateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testUpdateTerraform := proSubBoilerPlateFormatted + testAccResourceRedisCloudProDatabaseAcl + fmt.Sprintf(referencableRule, exampleRuleName) + fmt.Sprintf(testRole, testRoleNameUpdated) diff --git a/provider/rediscloud_acl_user_test.go b/provider/rediscloud_acl_user_test.go index d09f0ef9..f81e50d3 100644 --- a/provider/rediscloud_acl_user_test.go +++ b/provider/rediscloud_acl_user_test.go @@ -3,20 +3,22 @@ package provider import ( "context" "fmt" - "github.com/RedisLabs/rediscloud-go-api/redis" - client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "os" "regexp" "strconv" "testing" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccResourceRedisCloudAclUser_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") prefix := acctest.RandomWithPrefix(testResourcePrefix) exampleCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") @@ -31,20 +33,20 @@ func TestAccResourceRedisCloudAclUser_CRUDI(t *testing.T) { testUserPassword := prefix + "aA.1" testUserPasswordUpdated := testUserPassword + "-updated" - testCreateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testCreateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabaseAcl, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + fmt.Sprintf(referencableRole, exampleRoleName) + fmt.Sprintf(testUser, testUserName, testUserPassword) // The User will be updated because the Role's name will have changed - testUpdateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testUpdateTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabaseAcl, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + fmt.Sprintf(referencableRole, exampleRoleNameUpdated) + fmt.Sprintf(testUser, testUserName, testUserPassword) - testNewNameTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testNewNameTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabaseAcl, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + fmt.Sprintf(referencableRole, exampleRoleNameUpdated) + fmt.Sprintf(testUser, testUserNameUpdated, testUserPassword) - testNewPasswordTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabase, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + + testNewPasswordTerraform := fmt.Sprintf(testAccResourceRedisCloudProDatabaseAcl, exampleCloudAccountName, exampleSubscriptionName, exampleDatabasePassword) + fmt.Sprintf(referencableRole, exampleRoleNameUpdated) + fmt.Sprintf(testUser, testUserNameUpdated, testUserPasswordUpdated) @@ -82,8 +84,8 @@ func TestAccResourceRedisCloudAclUser_CRUDI(t *testing.T) { return fmt.Errorf("couldn't parse the role ID: %s", redis.StringValue(&r.Primary.ID)) } - client := testProvider.Meta().(*client2.ApiClient) - user, err := client.Client.Users.Get(context.TODO(), id) + apiClient := testProvider.Meta().(*client.ApiClient) + user, err := apiClient.Client.Users.Get(context.TODO(), id) if err != nil { return err } @@ -194,7 +196,7 @@ data "rediscloud_acl_user" "test" { ` func testAccCheckAclUserDestroy(s *terraform.State) error { - client := testProvider.Meta().(*client2.ApiClient) + apiClient := testProvider.Meta().(*client.ApiClient) for _, r := range s.RootModule().Resources { if r.Type != "rediscloud_acl_user" { @@ -206,7 +208,7 @@ func testAccCheckAclUserDestroy(s *terraform.State) error { return err } - roles, err := client.Client.Users.List(context.TODO()) + roles, err := apiClient.Client.Users.List(context.TODO()) if err != nil { return err } @@ -220,3 +222,74 @@ func testAccCheckAclUserDestroy(s *terraform.State) error { return nil } + +const testAccResourceRedisCloudProDatabaseAcl = ` +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 = "%s" +} + +resource "rediscloud_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + 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 { + dataset_size_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 = "example" + protocol = "redis" + dataset_size_in_gb = 3 + data_persistence = "none" + data_eviction = "allkeys-random" + throughput_measurement_by = "operations-per-second" + throughput_measurement_value = 1000 + password = "%s" + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + replication = false + average_item_size_in_bytes = 0 + client_ssl_certificate = "" + periodic_backup_path = "" + enable_default_user = true + redis_version = "7.2" + + alert { + name = "dataset-size" + value = 1 + } + + modules = [ + { + name = "RedisBloom" + } + ] + + tags = { + "market" = "emea" + "material" = "cardboard" + } +} +` diff --git a/provider/rediscloud_active_active_database_test.go b/provider/rediscloud_active_active_database_test.go index 07e33597..b9623cac 100644 --- a/provider/rediscloud_active_active_database_test.go +++ b/provider/rediscloud_active_active_database_test.go @@ -10,6 +10,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -221,7 +222,7 @@ func TestAccResourceRedisCloudActiveActiveDatabase_CRUDI(t *testing.T) { func TestAccResourceRedisCloudActiveActiveDatabase_optionalAttributes(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") // Test that attributes can be optional, either by setting them or not having them set when compared to CRUDI test subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + "-subscription" @@ -247,7 +248,7 @@ func TestAccResourceRedisCloudActiveActiveDatabase_optionalAttributes(t *testing func TestAccResourceRedisCloudActiveActiveDatabase_timeUtcRequiresValidInterval(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") diff --git a/provider/rediscloud_active_active_private_link_test.go b/provider/rediscloud_active_active_private_link_test.go index a233a0df..93620660 100644 --- a/provider/rediscloud_active_active_private_link_test.go +++ b/provider/rediscloud_active_active_private_link_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "testing" @@ -13,7 +14,7 @@ const testActiveActivePrivateLinkConfigFile = "../privatelink/testdata/active_ac func TestAccResourceRedisCloudActiveActivePrivateLink_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") password := acctest.RandString(20) @@ -65,11 +66,6 @@ func TestAccResourceRedisCloudActiveActivePrivateLink_CRUDI(t *testing.T) { func getRedisActiveActivePrivateLinkConfig(t *testing.T, testFile, shareName, password string) string { subName := acctest.RandomWithPrefix(testResourcePrefix) + "-aa-private-link" exampleCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") - - content, err := os.ReadFile(testFile) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return fmt.Sprintf(string(content), subName, exampleCloudAccountName, shareName, password) + content := utils.GetTestConfig(t, testFile) + return fmt.Sprintf(content, subName, exampleCloudAccountName, shareName, password) } diff --git a/provider/rediscloud_active_active_private_service_connect_endpoint_accepter_test.go b/provider/rediscloud_active_active_private_service_connect_endpoint_accepter_test.go index f57faaa4..027df5bd 100644 --- a/provider/rediscloud_active_active_private_service_connect_endpoint_accepter_test.go +++ b/provider/rediscloud_active_active_private_service_connect_endpoint_accepter_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "testing" @@ -16,7 +17,7 @@ import ( func TestAccResourceRedisCloudActiveActivePrivateServiceConnectEndpointAccepter_Create(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-pscea" diff --git a/provider/rediscloud_active_active_private_service_connect_endpoint_test.go b/provider/rediscloud_active_active_private_service_connect_endpoint_test.go index 4938de57..2ab23e8a 100644 --- a/provider/rediscloud_active_active_private_service_connect_endpoint_test.go +++ b/provider/rediscloud_active_active_private_service_connect_endpoint_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "strings" "testing" @@ -12,7 +13,7 @@ import ( func TestAccResourceRedisCloudActiveActivePrivateServiceConnectEndpoint_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-psce" diff --git a/provider/rediscloud_active_active_private_service_connect_test.go b/provider/rediscloud_active_active_private_service_connect_test.go index cdfaa98c..1829b327 100644 --- a/provider/rediscloud_active_active_private_service_connect_test.go +++ b/provider/rediscloud_active_active_private_service_connect_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -10,7 +11,7 @@ import ( func TestAccResourceRedisCloudActiveActivePrivateServiceConnect_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-psc" diff --git a/provider/rediscloud_active_active_subscription_test.go b/provider/rediscloud_active_active_subscription_test.go index a74cd424..2f70b013 100644 --- a/provider/rediscloud_active_active_subscription_test.go +++ b/provider/rediscloud_active_active_subscription_test.go @@ -4,13 +4,13 @@ import ( "context" "flag" "fmt" - "os" "regexp" "strconv" "testing" "github.com/RedisLabs/rediscloud-go-api/redis" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -26,7 +26,7 @@ var activeActiveMarketplaceFlag = flag.Bool("activeActiveMarketplace", false, // Also checks active-active subscription regions. func TestAccResourceRedisCloudActiveActiveSubscription_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_active_active_subscription.example" @@ -244,7 +244,7 @@ func TestAccResourceRedisCloudActiveActiveSubscription_CRUDI(t *testing.T) { func TestAccResourceRedisCloudActiveActiveSubscription_createUpdateContractPayment(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if !*activeActiveContractFlag { t.Skip("The '-activeActiveContract' parameter wasn't provided in the test command.") @@ -284,7 +284,7 @@ func TestAccResourceRedisCloudActiveActiveSubscription_createUpdateContractPayme func TestAccResourceRedisCloudActiveActiveSubscription_createUpdateMarketplacePayment(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if !*activeActiveMarketplaceFlag { t.Skip("The '-activeActiveMarketplace' parameter wasn't provided in the test command.") @@ -455,21 +455,11 @@ resource "rediscloud_active_active_subscription" "example" { ` func testAccResourceRedisCloudActiveActiveSubscription(t *testing.T, subscriptionName string) string { - - content, err := os.ReadFile("./activeactive/testdata//testAccResourceRedisCloudActiveActiveSubscription.tf") - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return fmt.Sprintf(string(content), subscriptionName) + content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscription.tf") + return fmt.Sprintf(content, subscriptionName) } func testAccResourceRedisCloudActiveActiveSubscriptionUpdate(t *testing.T, subscriptionName string, cloudProvider string) string { - - content, err := os.ReadFile("./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscriptionUpdate.tf") - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return fmt.Sprintf(string(content), subscriptionName, cloudProvider) + content := utils.GetTestConfig(t, "./activeactive/testdata/testAccResourceRedisCloudActiveActiveSubscriptionUpdate.tf") + return fmt.Sprintf(content, subscriptionName, cloudProvider) } diff --git a/provider/rediscloud_essentials_subscription_test.go b/provider/rediscloud_essentials_subscription_test.go index d5f28ba0..1971eb2f 100644 --- a/provider/rediscloud_essentials_subscription_test.go +++ b/provider/rediscloud_essentials_subscription_test.go @@ -10,6 +10,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -40,7 +41,7 @@ func testAccPreCheckEssentialsSubscription(t *testing.T) { func TestAccResourceRedisCloudEssentialsSubscription_Free_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) subscriptionNameUpdated := subscriptionName + "-updated" @@ -105,7 +106,7 @@ func TestAccResourceRedisCloudEssentialsSubscription_Free_CRUDI(t *testing.T) { func TestAccResourceRedisCloudEssentialsSubscription_Paid_CreditCard_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) subscriptionNameUpdated := subscriptionName + "-updated" @@ -171,7 +172,7 @@ func TestAccResourceRedisCloudEssentialsSubscription_Paid_CreditCard_CRUDI(t *te func TestAccResourceRedisCloudEssentialsSubscription_Paid_NoPaymentType_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) subscriptionNameUpdated := subscriptionName + "-updated" @@ -238,7 +239,7 @@ func TestAccResourceRedisCloudEssentialsSubscription_Paid_NoPaymentType_CRUDI(t func TestAccResourceRedisCloudEssentialsSubscription_Paid_Marketplace_CRUDI(t *testing.T) { // Only the qa environment has access to the marketplace, so this test will normally fail. // Leaving this in the test suite for manual runs - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if !*essentialsMarketplaceFlag { t.Skip("The '-essentialsMarketplace' parameter wasn't provided in the test command.") @@ -308,7 +309,7 @@ func TestAccResourceRedisCloudEssentialsSubscription_Paid_Marketplace_CRUDI(t *t } func TestAccResourceRedisCloudEssentialsSubscription_Incorrect_PaymentIdForType(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) diff --git a/provider/rediscloud_private_link_test.go b/provider/rediscloud_private_link_test.go index 51cbadc6..68cd5e25 100644 --- a/provider/rediscloud_private_link_test.go +++ b/provider/rediscloud_private_link_test.go @@ -5,6 +5,7 @@ import ( "os" "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" ) @@ -13,8 +14,8 @@ const testPrivateLinkConfigFile = "./privatelink/testdata/pro_private_link.tf" func TestAccResourceRedisCloudPrivateLink_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") - testAccRequiresEnvVar(t, "AWS_TEST_CLOUD_ACCOUNT_NAME") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "AWS_TEST_CLOUD_ACCOUNT_NAME") const resourceName = "rediscloud_private_link.pro_private_link" const datasourceName = "data.rediscloud_private_link.pro_private_link" @@ -40,7 +41,7 @@ func TestAccResourceRedisCloudPrivateLink_CRUDI(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "resource_configuration_arn"), resource.TestCheckResourceAttrSet(resourceName, "share_arn"), resource.TestCheckResourceAttrSet(resourceName, "connections.#"), - resource.TestCheckResourceAttr(resourceName, "databases.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "databases.#"), resource.TestCheckResourceAttrSet(datasourceName, "id"), resource.TestCheckResourceAttrSet(datasourceName, "subscription_id"), @@ -50,7 +51,7 @@ func TestAccResourceRedisCloudPrivateLink_CRUDI(t *testing.T) { resource.TestCheckResourceAttrSet(datasourceName, "resource_configuration_arn"), resource.TestCheckResourceAttrSet(datasourceName, "share_arn"), resource.TestCheckResourceAttrSet(datasourceName, "connections.#"), - resource.TestCheckResourceAttr(datasourceName, "databases.#", "1"), + resource.TestCheckResourceAttrSet(datasourceName, "databases.#"), //resource.TestCheckResourceAttrSet(datasourceScriptName, "id"), //resource.TestCheckResourceAttrSet(datasourceScriptName, "subscription_id"), @@ -70,8 +71,7 @@ func getRedisPrivateLinkConfig(t *testing.T, shareName string) string { subName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-private-link" exampleCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") - content := getTestConfig(t, testPrivateLinkConfigFile) password := acctest.RandString(20) - + content := utils.GetTestConfig(t, testPrivateLinkConfigFile) return fmt.Sprintf(content, subName, exampleCloudAccountName, shareName, password) } diff --git a/provider/rediscloud_private_service_connect_endpoint_accepter_test.go b/provider/rediscloud_private_service_connect_endpoint_accepter_test.go index b493fcc1..0e238fb3 100644 --- a/provider/rediscloud_private_service_connect_endpoint_accepter_test.go +++ b/provider/rediscloud_private_service_connect_endpoint_accepter_test.go @@ -3,12 +3,13 @@ package provider import ( "context" "fmt" - client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "os" "testing" "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/psc" + client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -16,7 +17,7 @@ import ( func TestAccResourceRedisCloudPrivateServiceConnectEndpointAccepter_Create(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-pscea" diff --git a/provider/rediscloud_private_service_connect_endpoint_test.go b/provider/rediscloud_private_service_connect_endpoint_test.go index b48ec619..d4438975 100644 --- a/provider/rediscloud_private_service_connect_endpoint_test.go +++ b/provider/rediscloud_private_service_connect_endpoint_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "strings" "testing" @@ -12,7 +13,7 @@ import ( func TestAccResourceRedisCloudPrivateServiceConnectEndpoint_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-psce" diff --git a/provider/rediscloud_private_service_connect_test.go b/provider/rediscloud_private_service_connect_test.go index 52fdeb95..01c53870 100644 --- a/provider/rediscloud_private_service_connect_test.go +++ b/provider/rediscloud_private_service_connect_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -10,7 +11,7 @@ import ( func TestAccResourceRedisCloudPrivateServiceConnect_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") baseName := acctest.RandomWithPrefix(testResourcePrefix) + "-pro-psc" diff --git a/provider/rediscloud_transit_gateway_attachment_test.go b/provider/rediscloud_transit_gateway_attachment_test.go index f882cab1..01e3c2e0 100644 --- a/provider/rediscloud_transit_gateway_attachment_test.go +++ b/provider/rediscloud_transit_gateway_attachment_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "testing" @@ -12,7 +13,7 @@ import ( func TestAccResourceRedisCloudTransitGatewayAttachment_Pro(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") testTgwId := os.Getenv("AWS_TEST_TGW_ID") diff --git a/provider/resource_rediscloud_acl_role.go b/provider/resource_rediscloud_acl_role.go index 2e6bb1a8..2f3d7151 100644 --- a/provider/resource_rediscloud_acl_role.go +++ b/provider/resource_rediscloud_acl_role.go @@ -6,6 +6,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/access_control_lists/roles" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "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" @@ -259,7 +260,7 @@ func extractRules(d *schema.ResourceData) []*roles.CreateRuleInRoleRequest { var regions []*string = nil if databaseMap["regions"] != nil { - regions = setToStringSlice(databaseMap["regions"].(*schema.Set)) + regions = utils.SetToStringSlice(databaseMap["regions"].(*schema.Set)) } createDatabaseAssociation := roles.CreateDatabaseInRuleInRoleRequest{ diff --git a/provider/resource_rediscloud_acl_rule_test.go b/provider/resource_rediscloud_acl_rule_test.go index f94742d9..604ac64c 100644 --- a/provider/resource_rediscloud_acl_rule_test.go +++ b/provider/resource_rediscloud_acl_rule_test.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/RedisLabs/rediscloud-go-api/redis" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -15,7 +16,7 @@ import ( func TestAccResourceRedisCloudAclRule_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") prefix := acctest.RandomWithPrefix(testResourcePrefix) testName := prefix + "-test-rule" diff --git a/provider/resource_rediscloud_active_active_database.go b/provider/resource_rediscloud_active_active_database.go index 3e9756fe..de5951aa 100644 --- a/provider/resource_rediscloud_active_active_database.go +++ b/provider/resource_rediscloud_active_active_database.go @@ -10,6 +10,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/databases" "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/retry" @@ -27,7 +28,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - subId, dbId, err := toDatabaseId(d.Id()) + subId, dbId, err := pro.ToDatabaseId(d.Id()) if err != nil { return nil, err } @@ -37,7 +38,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { if err := d.Set("db_id", dbId); err != nil { return nil, err } - d.SetId(buildResourceId(subId, dbId)) + d.SetId(utils.BuildResourceId(subId, dbId)) return []*schema.ResourceData{d}, nil }, }, @@ -58,7 +59,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { } for _, key := range keys { - if err := remoteBackupIntervalSetCorrectly(key)(ctx, diff, i); err != nil { + if err := pro.RemoteBackupIntervalSetCorrectly(key)(ctx, diff, i); err != nil { return err } } @@ -281,7 +282,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Description: "Defines the hour automatic backups are made - only applicable when interval is `every-12-hours` or `every-24-hours`", Type: schema.TypeString, Optional: true, - ValidateDiagFunc: isTime(), + ValidateDiagFunc: utils.IsTime(), }, "storage_type": { Description: "Defines the provider of the storage location", @@ -345,7 +346,7 @@ func resourceRedisCloudActiveActiveDatabase() *schema.Resource { Type: schema.TypeString, }, Optional: true, - ValidateDiagFunc: validateTagsfunc, + ValidateDiagFunc: pro.ValidateTagsfunc, }, }, } @@ -360,7 +361,7 @@ func resourceRedisCloudActiveActiveDatabaseCreate(ctx context.Context, d *schema name := d.Get("name").(string) supportOSSClusterAPI := d.Get("support_oss_cluster_api").(bool) useExternalEndpointForOSSClusterAPI := d.Get("external_endpoint_for_oss_cluster_api").(bool) - globalSourceIp := setToStringSlice(d.Get("global_source_ips").(*schema.Set)) + globalSourceIp := utils.SetToStringSlice(d.Get("global_source_ips").(*schema.Set)) createAlerts := make([]*databases.Alert, 0) alerts := d.Get("global_alert").(*schema.Set) @@ -379,7 +380,7 @@ func resourceRedisCloudActiveActiveDatabaseCreate(ctx context.Context, d *schema } createModules := make([]*databases.Module, 0) - planModules := interfaceToStringSlice(d.Get("global_modules").([]interface{})) + planModules := utils.InterfaceToStringSlice(d.Get("global_modules").([]interface{})) for _, module := range planModules { createModule := &databases.Module{ Name: module, @@ -461,7 +462,7 @@ func resourceRedisCloudActiveActiveDatabaseCreate(ctx context.Context, d *schema return diag.FromErr(err) } - d.SetId(buildResourceId(subId, dbId)) + d.SetId(utils.BuildResourceId(subId, dbId)) // Confirm Database Active status err = utils.WaitForDatabaseToBeActive(ctx, subId, dbId, api) @@ -486,7 +487,7 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R var diags diag.Diagnostics - subId, dbId, err := toDatabaseId(d.Id()) + subId, dbId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -587,10 +588,10 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R stateOverrideAlerts := getStateAlertsFromDbRegion(getStateOverrideRegion(d, region)) if len(stateOverrideAlerts) > 0 { - regionDbConfig["override_global_alert"] = flattenAlerts(regionDb.Alerts) + regionDbConfig["override_global_alert"] = pro.FlattenAlerts(regionDb.Alerts) } - regionDbConfig["remote_backup"] = flattenBackupPlan(regionDb.Backup, getStateRemoteBackup(d, region), "") + regionDbConfig["remote_backup"] = pro.FlattenBackupPlan(regionDb.Backup, getStateRemoteBackup(d, region), "") regionDbConfig["enable_default_user"] = redis.BoolValue(regionDb.Security.EnableDefaultUser) @@ -619,11 +620,11 @@ func resourceRedisCloudActiveActiveDatabaseRead(ctx context.Context, d *schema.R } tlsAuthEnabled := *db.CrdbDatabases[0].Security.TLSClientAuthentication - if err := applyCertificateHints(tlsAuthEnabled, d); err != nil { + if err := utils.ApplyCertificateHints(tlsAuthEnabled, d); err != nil { return diag.FromErr(err) } - if err := readTags(ctx, api, subId, dbId, d); err != nil { + if err := pro.ReadTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } @@ -637,7 +638,7 @@ func resourceRedisCloudActiveActiveDatabaseDelete(ctx context.Context, d *schema var diags diag.Diagnostics subId := d.Get("subscription_id").(int) - _, dbId, err := toDatabaseId(d.Id()) + _, dbId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -664,7 +665,7 @@ func resourceRedisCloudActiveActiveDatabaseDelete(ctx context.Context, d *schema func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { api := meta.(*client.ApiClient) - _, dbId, err := toDatabaseId(d.Id()) + _, dbId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -686,7 +687,7 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema }) } - globalSourceIps := setToStringSlice(d.Get("global_source_ips").(*schema.Set)) + globalSourceIps := utils.SetToStringSlice(d.Get("global_source_ips").(*schema.Set)) // Make a list of region-specific configurations var regions []*databases.LocalRegionProperties @@ -732,7 +733,7 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema } } - regionProps.RemoteBackup = buildBackupPlan(dbRegion["remote_backup"], nil) + regionProps.RemoteBackup = pro.BuildBackupPlan(dbRegion["remote_backup"], nil) regions = append(regions, regionProps) } @@ -784,7 +785,7 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema //The cert validation is done by the API (HTTP 400 is returned if it's invalid). clientSSLCertificate := d.Get("client_ssl_certificate").(string) - clientTLSCertificates := interfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) + clientTLSCertificates := utils.InterfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) enableTLS := d.Get("enable_tls").(bool) if enableTLS { update.EnableTls = redis.Bool(enableTLS) @@ -821,7 +822,7 @@ func resourceRedisCloudActiveActiveDatabaseUpdate(ctx context.Context, d *schema } // The Tags API is synchronous so we shouldn't have to wait for anything - if err := writeTags(ctx, api, subId, dbId, d); err != nil { + if err := pro.WriteTags(ctx, api, subId, dbId, d); err != nil { return diag.FromErr(err) } diff --git a/provider/resource_rediscloud_active_active_subscription.go b/provider/resource_rediscloud_active_active_subscription.go index fc9d3e43..2ebb2e02 100644 --- a/provider/resource_rediscloud_active_active_subscription.go +++ b/provider/resource_rediscloud_active_active_subscription.go @@ -5,6 +5,7 @@ import ( "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" @@ -347,7 +348,7 @@ func resourceRedisCloudActiveActiveSubscriptionCreate(ctx context.Context, d *sc name := d.Get("name").(string) paymentMethod := d.Get("payment_method").(string) - paymentMethodID, err := readPaymentMethodID(d) + paymentMethodID, err := pro.ReadPaymentMethodID(d) if err != nil { return diag.FromErr(err) } @@ -379,7 +380,7 @@ func resourceRedisCloudActiveActiveSubscriptionCreate(ctx context.Context, d *sc // If in a CMK flow, verify the pending state if cmkEnabled { - err = waitForSubscriptionToBeEncryptionKeyPending(ctx, subId, api) + err = pro.WaitForSubscriptionToBeEncryptionKeyPending(ctx, subId, api) if err != nil { return diag.FromErr(err) } @@ -432,7 +433,7 @@ func resourceRedisCloudActiveActiveSubscriptionCreate(ctx context.Context, d *sc windows = append(windows, &maintenance.Window{ StartHour: redis.Int(wMap["start_hour"].(int)), DurationInHours: redis.Int(wMap["duration_in_hours"].(int)), - Days: interfaceToStringSlice(wMap["days"].([]interface{})), + Days: utils.InterfaceToStringSlice(wMap["days"].([]interface{})), }) } @@ -498,7 +499,7 @@ func resourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *sche if err != nil { return diag.FromErr(err) } - if err := d.Set("maintenance_windows", flattenMaintenance(m)); err != nil { + if err := d.Set("maintenance_windows", pro.FlattenMaintenance(m)); err != nil { return diag.FromErr(err) } @@ -506,7 +507,7 @@ func resourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *sche if err != nil { return diag.FromErr(err) } - if err := d.Set("pricing", flattenPricing(pricingList)); err != nil { + if err := d.Set("pricing", pro.FlattenPricing(pricingList)); err != nil { return diag.FromErr(err) } } @@ -556,7 +557,7 @@ func resourceRedisCloudActiveActiveSubscriptionUpdate(ctx context.Context, d *sc } if d.HasChange("payment_method_id") { - paymentMethodID, err := readPaymentMethodID(d) + paymentMethodID, err := pro.ReadPaymentMethodID(d) if err != nil { return diag.FromErr(err) } @@ -585,7 +586,7 @@ func resourceRedisCloudActiveActiveSubscriptionUpdate(ctx context.Context, d *sc windows = append(windows, &maintenance.Window{ StartHour: redis.Int(wMap["start_hour"].(int)), DurationInHours: redis.Int(wMap["duration_in_hours"].(int)), - Days: interfaceToStringSlice(wMap["days"].([]interface{})), + Days: utils.InterfaceToStringSlice(wMap["days"].([]interface{})), }) } @@ -678,7 +679,7 @@ func resourceRedisCloudActiveActiveSubscriptionDelete(ctx context.Context, d *sc d.SetId("") - err = waitForSubscriptionToBeDeleted(ctx, subId, api) + err = pro.WaitForSubscriptionToBeDeleted(ctx, subId, api) if err != nil { return diag.FromErr(err) } @@ -698,7 +699,7 @@ func newCreateSubscription(name string, paymentMethodID *int, paymentMethod stri } if cmkEnabled { - req.PersistentStorageEncryptionType = redis.String(CMK_ENABLED_STRING) + req.PersistentStorageEncryptionType = redis.String(pro.CMK_ENABLED_STRING) } return req @@ -766,7 +767,7 @@ func buildSubscriptionCreatePlanAADatabases(planMap map[string]interface{}) []*s } createModules := make([]*subscriptions.CreateModules, 0) - planModules := interfaceToStringSlice(planMap["modules"].([]interface{})) + planModules := utils.InterfaceToStringSlice(planMap["modules"].([]interface{})) for _, module := range planModules { createModule := &subscriptions.CreateModules{ Name: module, diff --git a/provider/resource_rediscloud_active_active_subscription_cmk_test.go b/provider/resource_rediscloud_active_active_subscription_cmk_test.go index 5f2e5984..6dacfda9 100644 --- a/provider/resource_rediscloud_active_active_subscription_cmk_test.go +++ b/provider/resource_rediscloud_active_active_subscription_cmk_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "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" "os" @@ -12,8 +13,8 @@ import ( // to give the CMK the necessary permissions. func TestAccResourceRedisCloudActiveActiveSubscription_CMK(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") - testAccRequiresEnvVar(t, "GCP_CMK_RESOURCE_NAME") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "GCP_CMK_RESOURCE_NAME") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_active_active_subscription.example" diff --git a/provider/resource_rediscloud_active_active_subscription_peering.go b/provider/resource_rediscloud_active_active_subscription_peering.go index bb8d46d6..a03342e6 100644 --- a/provider/resource_rediscloud_active_active_subscription_peering.go +++ b/provider/resource_rediscloud_active_active_subscription_peering.go @@ -2,8 +2,6 @@ package provider import ( "context" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "log" "regexp" "strconv" @@ -13,6 +11,8 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "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" @@ -189,7 +189,7 @@ func resourceRedisCloudSubscriptionActiveActivePeeringCreate(ctx context.Context if vpcCIDR, ok := d.GetOk("vpc_cidr"); ok { peeringRequest.VPCCidr = redis.String(vpcCIDR.(string)) } else if vpcCIDRs, ok := d.GetOk("vpc_cidrs"); ok { - peeringRequest.VPCCidrs = setToStringSlice(vpcCIDRs.(*schema.Set)) + peeringRequest.VPCCidrs = utils.SetToStringSlice(vpcCIDRs.(*schema.Set)) } else { return diag.Errorf("`vpc_cidr` or `vpc_cidrs` must be set when `provider_name` is `AWS`") } @@ -228,7 +228,7 @@ func resourceRedisCloudSubscriptionActiveActivePeeringCreate(ctx context.Context return diag.FromErr(err) } - d.SetId(buildResourceId(subId, peering)) + d.SetId(utils.BuildResourceId(subId, peering)) err = waitForActiveActivePeeringToBeInitiated(ctx, subId, peering, api) if err != nil { diff --git a/provider/resource_rediscloud_active_active_subscription_peering_test.go b/provider/resource_rediscloud_active_active_subscription_peering_test.go index 8afda00a..194a6927 100644 --- a/provider/resource_rediscloud_active_active_subscription_peering_test.go +++ b/provider/resource_rediscloud_active_active_subscription_peering_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "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" "os" @@ -11,7 +12,7 @@ import ( func TestAccResourceRedisCloudActiveActiveSubscriptionPeering_aws(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_PEERING") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_PEERING") name := acctest.RandomWithPrefix(testResourcePrefix) @@ -73,7 +74,7 @@ func TestAccResourceRedisCloudActiveActiveSubscriptionPeering_aws(t *testing.T) func TestAccResourceRedisCloudActiveActiveSubscriptionPeering_gcp(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_PEERING") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_PEERING") name := acctest.RandomWithPrefix(testResourcePrefix) diff --git a/provider/resource_rediscloud_active_active_subscription_regions_test.go b/provider/resource_rediscloud_active_active_subscription_regions_test.go index c5cb1732..c1ecd04b 100644 --- a/provider/resource_rediscloud_active_active_subscription_regions_test.go +++ b/provider/resource_rediscloud_active_active_subscription_regions_test.go @@ -8,6 +8,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -15,7 +16,7 @@ import ( func TestAccResourceRedisCloudActiveActiveSubscriptionRegions_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUB_ACTIVE_ACTIVE") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUB_ACTIVE_ACTIVE") subName := acctest.RandomWithPrefix(testResourcePrefix) + "-regions-test" dbName := acctest.RandomWithPrefix(testResourcePrefix) + "-regions" + "-db" diff --git a/provider/resource_rediscloud_active_active_transit_gateway_attachment.go b/provider/resource_rediscloud_active_active_transit_gateway_attachment.go index 5e61afd8..24f6326a 100644 --- a/provider/resource_rediscloud_active_active_transit_gateway_attachment.go +++ b/provider/resource_rediscloud_active_active_transit_gateway_attachment.go @@ -5,6 +5,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/transit_gateway/attachments" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "strconv" @@ -95,7 +96,7 @@ func resourceRedisCloudActiveActiveTransitGatewayAttachmentCreate(ctx context.Co } // At this point, cidrs has to be empty. We cannot honour the user's configuration until the invitation has been accepted - cidrs := interfaceToStringSlice(d.Get("cidrs").([]interface{})) + cidrs := utils.InterfaceToStringSlice(d.Get("cidrs").([]interface{})) if len(cidrs) > 0 { return diag.Errorf("Attachment cannot be created with Cidrs provided, it must be accepted first. This resource may then be updated with Cidrs.") } @@ -143,7 +144,7 @@ func resourceRedisCloudActiveActiveTransitGatewayAttachmentRead(ctx context.Cont } tgw := tgws[0] - d.SetId(buildResourceId(subId, tgwId)) + d.SetId(utils.BuildResourceId(subId, tgwId)) if err := d.Set("aws_tgw_uid", redis.StringValue(tgw.AwsTgwUid)); err != nil { return diag.FromErr(err) } @@ -176,7 +177,7 @@ func resourceRedisCloudActiveActiveTransitGatewayAttachmentUpdate(ctx context.Co return diag.FromErr(err) } - cidrs := interfaceToStringSlice(d.Get("cidrs").([]interface{})) + cidrs := utils.InterfaceToStringSlice(d.Get("cidrs").([]interface{})) if len(cidrs) == 0 { cidrs = make([]*string, 0) } diff --git a/provider/resource_rediscloud_cloud_account_test.go b/provider/resource_rediscloud_cloud_account_test.go index 6d9b47af..113605e6 100644 --- a/provider/resource_rediscloud_cloud_account_test.go +++ b/provider/resource_rediscloud_cloud_account_test.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/RedisLabs/rediscloud-go-api/redis" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -16,7 +17,7 @@ import ( func TestAccResourceRedisCloudCloudAccount_basic(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if testing.Short() { t.Skip("Required environment variables currently not available under CI") diff --git a/provider/resource_rediscloud_essentials_database.go b/provider/resource_rediscloud_essentials_database.go index 6d7cf9ef..65d855ab 100644 --- a/provider/resource_rediscloud_essentials_database.go +++ b/provider/resource_rediscloud_essentials_database.go @@ -2,20 +2,22 @@ package provider import ( "context" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" - "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" + "fmt" "log" + "strings" "time" "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/databases" fixedDatabases "github.com/RedisLabs/rediscloud-go-api/service/fixed/databases" "github.com/RedisLabs/rediscloud-go-api/service/tags" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - - "github.com/RedisLabs/rediscloud-go-api/service/databases" + "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/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func resourceRedisCloudEssentialsDatabase() *schema.Resource { @@ -28,7 +30,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - subId, dbId, err := toDatabaseId(d.Id()) + subId, dbId, err := pro.ToDatabaseId(d.Id()) if err != nil { return nil, err } @@ -38,7 +40,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { if err := d.Set("db_id", dbId); err != nil { return nil, err } - d.SetId(buildResourceId(subId, dbId)) + d.SetId(utils.BuildResourceId(subId, dbId)) return []*schema.ResourceData{d}, nil }, }, @@ -50,6 +52,8 @@ 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", @@ -220,7 +224,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { }, }, "modules": { - Description: "Modules to be provisioned in the database", + Description: "Modules to be provisioned in the database. Note: Not supported for Redis 8.0 and higher as modules are bundled by default.", Type: schema.TypeSet, // In TF <0.12 List of objects is not supported, so we need to opt-in to use this old behaviour. ConfigMode: schema.SchemaConfigModeAttr, @@ -292,7 +296,7 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource { Type: schema.TypeString, }, Optional: true, - ValidateDiagFunc: validateTagsfunc, + ValidateDiagFunc: pro.ValidateTagsfunc, }, }, } @@ -323,7 +327,7 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R createDatabaseRequest.RespVersion = redis.String(respVersion) } - sourceIps := interfaceToStringSlice(d.Get("source_ips").([]interface{})) + sourceIps := utils.InterfaceToStringSlice(d.Get("source_ips").([]interface{})) if len(sourceIps) == 0 { createDatabaseRequest.SourceIPs = []*string{redis.String("0.0.0.0/0")} } else { @@ -350,7 +354,7 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R createDatabaseRequest.Replica = createReplica } - tlsCertificates := interfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) + tlsCertificates := utils.InterfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) if len(tlsCertificates) > 0 { createCertificates := make([]*fixedDatabases.DatabaseCertificate, 0) for _, cert := range tlsCertificates { @@ -403,7 +407,7 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R createDatabaseRequest.SupportOSSClusterAPI = redis.Bool(d.Get("support_oss_cluster_api").(bool)) createDatabaseRequest.UseExternalEndpointForOSSClusterAPI = redis.Bool(d.Get("external_endpoint_for_oss_cluster_api").(bool)) createDatabaseRequest.EnableDatabaseClustering = redis.Bool(d.Get("enable_database_clustering").(bool)) - createDatabaseRequest.RegexRules = interfaceToStringSlice(d.Get("regex_rules").([]interface{})) + createDatabaseRequest.RegexRules = utils.InterfaceToStringSlice(d.Get("regex_rules").([]interface{})) createDatabaseRequest.EnableTls = redis.Bool(d.Get("enable_tls").(bool)) } @@ -413,7 +417,7 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R return diag.FromErr(err) } - d.SetId(buildResourceId(subId, databaseId)) + d.SetId(utils.BuildResourceId(subId, databaseId)) // Confirm Subscription Active status err = waitForEssentialsDatabaseToBeActive(ctx, subId, databaseId, api) @@ -434,7 +438,7 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res var diags diag.Diagnostics - subId, databaseId, err := toDatabaseId(d.Id()) + subId, databaseId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -453,7 +457,7 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res return diag.FromErr(err) } - d.SetId(buildResourceId(subId, databaseId)) + d.SetId(utils.BuildResourceId(subId, databaseId)) if err := d.Set("db_id", redis.IntValue(db.DatabaseId)); err != nil { return diag.FromErr(err) @@ -534,10 +538,10 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res if err := d.Set("enable_default_user", redis.Bool(*db.Security.EnableDefaultUser)); err != nil { return diag.FromErr(err) } - if err := d.Set("alert", flattenAlerts(*db.Alerts)); err != nil { + if err := d.Set("alert", pro.FlattenAlerts(*db.Alerts)); err != nil { return diag.FromErr(err) } - if err := d.Set("modules", flattenModules(*db.Modules)); err != nil { + if err := d.Set("modules", pro.FlattenModules(*db.Modules)); err != nil { return diag.FromErr(err) } @@ -563,7 +567,7 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res if err := d.Set("enable_database_clustering", redis.BoolValue(db.Clustering.Enabled)); err != nil { return diag.FromErr(err) } - if err := d.Set("regex_rules", flattenRegexRules(db.Clustering.RegexRules)); err != nil { + if err := d.Set("regex_rules", pro.FlattenRegexRules(db.Clustering.RegexRules)); err != nil { return diag.FromErr(err) } } @@ -588,7 +592,7 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res func resourceRedisCloudEssentialsDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { api := meta.(*client.ApiClient) - _, databaseId, err := toDatabaseId(d.Id()) + _, databaseId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -611,7 +615,7 @@ func resourceRedisCloudEssentialsDatabaseUpdate(ctx context.Context, d *schema.R updateDatabaseRequest.RespVersion = redis.String(respVersion) } - sourceIps := interfaceToStringSlice(d.Get("source_ips").([]interface{})) + sourceIps := utils.InterfaceToStringSlice(d.Get("source_ips").([]interface{})) if len(sourceIps) == 0 { updateDatabaseRequest.SourceIPs = []*string{redis.String("0.0.0.0/0")} } else { @@ -638,7 +642,7 @@ func resourceRedisCloudEssentialsDatabaseUpdate(ctx context.Context, d *schema.R updateDatabaseRequest.Replica = createReplica } - tlsCertificates := interfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) + tlsCertificates := utils.InterfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) if len(tlsCertificates) > 0 { createCertificates := make([]*fixedDatabases.DatabaseCertificate, 0) for _, cert := range tlsCertificates { @@ -681,7 +685,7 @@ func resourceRedisCloudEssentialsDatabaseUpdate(ctx context.Context, d *schema.R updateDatabaseRequest.SupportOSSClusterAPI = redis.Bool(d.Get("support_oss_cluster_api").(bool)) updateDatabaseRequest.UseExternalEndpointForOSSClusterAPI = redis.Bool(d.Get("external_endpoint_for_oss_cluster_api").(bool)) updateDatabaseRequest.EnableDatabaseClustering = redis.Bool(d.Get("enable_database_clustering").(bool)) - updateDatabaseRequest.RegexRules = interfaceToStringSlice(d.Get("regex_rules").([]interface{})) + updateDatabaseRequest.RegexRules = utils.InterfaceToStringSlice(d.Get("regex_rules").([]interface{})) updateDatabaseRequest.EnableTls = redis.Bool(d.Get("enable_tls").(bool)) } @@ -713,7 +717,7 @@ func resourceRedisCloudEssentialsDatabaseDelete(ctx context.Context, d *schema.R var diags diag.Diagnostics subId := d.Get("subscription_id").(int) - _, databaseId, err := toDatabaseId(d.Id()) + _, databaseId, err := pro.ToDatabaseId(d.Id()) if err != nil { return diag.FromErr(err) } @@ -815,3 +819,30 @@ func writeFixedTags(ctx context.Context, api *client.ApiClient, subId int, datab } return api.Client.Tags.PutFixed(ctx, subId, databaseId, tags.AllTags{Tags: &t}) } + +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") + 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) + } + } + } + return nil + } +} diff --git a/provider/resource_rediscloud_essentials_database_test.go b/provider/resource_rediscloud_essentials_database_test.go index 29b88d28..b95c562b 100644 --- a/provider/resource_rediscloud_essentials_database_test.go +++ b/provider/resource_rediscloud_essentials_database_test.go @@ -1,6 +1,7 @@ package provider import ( + "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" @@ -11,7 +12,7 @@ import ( func TestAccResourceRedisCloudEssentialsDatabase_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) databaseName := subscriptionName + "-db" @@ -268,7 +269,7 @@ data "rediscloud_essentials_database" "example" { // there was a bug where removing the default user would cause issues with passwords func TestAccResourceRedisCloudEssentialsDatabase_DisableDefaultUser(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) databaseName := subscriptionName + "-db" diff --git a/provider/resource_rediscloud_pro_database_redis_8_test.go b/provider/resource_rediscloud_pro_database_redis_8_test.go new file mode 100644 index 00000000..6df2325a --- /dev/null +++ b/provider/resource_rediscloud_pro_database_redis_8_test.go @@ -0,0 +1,223 @@ +package provider + +import ( + "context" + "fmt" + "os" + "regexp" + "strconv" + "testing" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// checks that a redis 8 database can be provisioned +func TestAccResourceRedisCloudProDatabase_Redis8(t *testing.T) { + + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + + name := acctest.RandomWithPrefix(testResourcePrefix) + password := acctest.RandString(20) + const resourceName = "rediscloud_subscription_database.example" + const subscriptionResourceName = "rediscloud_subscription.example" + testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + + var subId int + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckProSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: getRedis8DatabaseConfig(t, testCloudAccountName, name, password), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "example"), + resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), + resource.TestCheckResourceAttr(resourceName, "dataset_size_in_gb", "3"), + resource.TestCheckResourceAttr(resourceName, "replication", "false"), + resource.TestCheckResourceAttr(resourceName, "support_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "resp_version", "resp3"), + resource.TestCheckResourceAttr(resourceName, "throughput_measurement_by", "operations-per-second"), + resource.TestCheckResourceAttr(resourceName, "throughput_measurement_value", "1000"), + resource.TestCheckResourceAttr(resourceName, "data_persistence", "none"), + resource.TestCheckResourceAttr(resourceName, "data_eviction", "allkeys-random"), + resource.TestCheckResourceAttr(resourceName, "average_item_size_in_bytes", "0"), + resource.TestCheckResourceAttr(resourceName, "client_ssl_certificate", ""), + resource.TestCheckResourceAttr(resourceName, "periodic_backup_path", ""), + resource.TestCheckResourceAttr(resourceName, "external_endpoint_for_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "password", password), + resource.TestCheckResourceAttr(resourceName, "alert.#", "1"), + resource.TestCheckResourceAttr(resourceName, "alert.0.name", "dataset-size"), + resource.TestCheckResourceAttr(resourceName, "alert.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "enable_default_user", "true"), + resource.TestCheckResourceAttr(resourceName, "redis_version", "8.0"), + + resource.TestCheckResourceAttr(resourceName, "tags.market", "emea"), + resource.TestCheckResourceAttr(resourceName, "tags.material", "cardboard"), + + // Test databases exist + func(s *terraform.State) error { + r := s.RootModule().Resources[subscriptionResourceName] + + var err error + subId, err = strconv.Atoi(r.Primary.ID) + if err != nil { + return fmt.Errorf("couldn't parse the subscription ID: %s", redis.StringValue(&r.Primary.ID)) + } + + apiClient := testProvider.Meta().(*client.ApiClient) + sub, err := apiClient.Client.Subscription.Get(context.TODO(), subId) + if err != nil { + return err + } + + if redis.StringValue(sub.Name) != name { + return fmt.Errorf("unexpected name value: %s", redis.StringValue(sub.Name)) + } + + listDb := apiClient.Client.Database.List(context.TODO(), subId) + if listDb.Next() != true { + return fmt.Errorf("no database found: %s", listDb.Err()) + } + if listDb.Err() != nil { + return listDb.Err() + } + + return nil + }, + ), + }, + }, + }) +} + +// Checks that users can upgrade from 7.2 to 8.0 +func TestAccResourceRedisCloudProDatabase_Redis8_Upgrade(t *testing.T) { + + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + + name := acctest.RandomWithPrefix(testResourcePrefix) + password := acctest.RandString(20) + const resourceName = "rediscloud_subscription_database.example" + const subscriptionResourceName = "rediscloud_subscription.example" + testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") + + var subId int + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckProSubscriptionDestroy, + Steps: []resource.TestStep{ + // Test database and replica database creation with Redis 7.2 + { + Config: getRedis7DatabaseConfig(t, testCloudAccountName, name, password), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "example"), + resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), + resource.TestCheckResourceAttr(resourceName, "dataset_size_in_gb", "3"), + resource.TestCheckResourceAttr(resourceName, "replication", "false"), + resource.TestCheckResourceAttr(resourceName, "support_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "resp_version", "resp3"), + resource.TestCheckResourceAttr(resourceName, "throughput_measurement_by", "operations-per-second"), + resource.TestCheckResourceAttr(resourceName, "throughput_measurement_value", "1000"), + resource.TestCheckResourceAttr(resourceName, "data_persistence", "none"), + resource.TestCheckResourceAttr(resourceName, "data_eviction", "allkeys-random"), + resource.TestCheckResourceAttr(resourceName, "average_item_size_in_bytes", "0"), + resource.TestCheckResourceAttr(resourceName, "client_ssl_certificate", ""), + resource.TestCheckResourceAttr(resourceName, "periodic_backup_path", ""), + resource.TestCheckResourceAttr(resourceName, "external_endpoint_for_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "password", password), + resource.TestCheckResourceAttr(resourceName, "alert.#", "1"), + resource.TestCheckResourceAttr(resourceName, "alert.0.name", "dataset-size"), + resource.TestCheckResourceAttr(resourceName, "alert.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "enable_default_user", "true"), + resource.TestCheckResourceAttr(resourceName, "redis_version", "7.2"), + + resource.TestCheckResourceAttr(resourceName, "tags.market", "emea"), + resource.TestCheckResourceAttr(resourceName, "tags.material", "cardboard"), + + // Test databases exist + func(s *terraform.State) error { + r := s.RootModule().Resources[subscriptionResourceName] + + var err error + subId, err = strconv.Atoi(r.Primary.ID) + if err != nil { + return fmt.Errorf("couldn't parse the subscription ID: %s", redis.StringValue(&r.Primary.ID)) + } + + apiClient := testProvider.Meta().(*client.ApiClient) + sub, err := apiClient.Client.Subscription.Get(context.TODO(), subId) + if err != nil { + return err + } + + if redis.StringValue(sub.Name) != name { + return fmt.Errorf("unexpected name value: %s", redis.StringValue(sub.Name)) + } + + listDb := apiClient.Client.Database.List(context.TODO(), subId) + if listDb.Next() != true { + return fmt.Errorf("no database found: %s", listDb.Err()) + } + if listDb.Err() != nil { + return listDb.Err() + } + + return nil + }, + ), + }, + // Test database is updated successfully to Redis 8.0 + { + Config: getRedis8DatabaseConfig(t, testCloudAccountName, name, password), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "redis_version", "8.0"), + ), + }, + }, + }) +} + +// Test that modules cannot be set on Redis 8.x +func TestAccResourceRedisCloudProDatabase_Redis8_ModulesBlocked(t *testing.T) { + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + + name := acctest.RandomWithPrefix(testResourcePrefix) + password := acctest.RandString(20) + 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{ + { + Config: getRedis8WithModulesConfig(t, testCloudAccountName, name, password), + ExpectError: regexp.MustCompile(`"modules" cannot be explicitly set for Redis version 8\.0 as modules are bundled by default`), + }, + }, + }) +} + +func getRedis7DatabaseConfig(t *testing.T, cloudAccountName, subscriptionName, password string) string { + content := utils.GetTestConfig(t, "./pro/testdata/pro_database_redis_7.tf") + return fmt.Sprintf(content, cloudAccountName, subscriptionName, password) +} + +func getRedis8DatabaseConfig(t *testing.T, cloudAccountName, subscriptionName, password string) string { + content := utils.GetTestConfig(t, "./pro/testdata/pro_database_redis_8.tf") + return fmt.Sprintf(content, cloudAccountName, subscriptionName, password) +} + +func getRedis8WithModulesConfig(t *testing.T, cloudAccountName, subscriptionName, password string) string { + content := utils.GetTestConfig(t, "./pro/testdata/pro_database_redis_8_with_modules.tf") + return fmt.Sprintf(content, cloudAccountName, subscriptionName, password) +} diff --git a/provider/resource_rediscloud_pro_database_test.go b/provider/resource_rediscloud_pro_database_test.go index 38db80b8..dec45939 100644 --- a/provider/resource_rediscloud_pro_database_test.go +++ b/provider/resource_rediscloud_pro_database_test.go @@ -3,13 +3,14 @@ package provider import ( "context" "fmt" - client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" "os" "regexp" "strconv" "testing" "github.com/RedisLabs/rediscloud-go-api/redis" + client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -18,7 +19,7 @@ import ( // Checks CRUDI (CREATE, READ, UPDATE, IMPORT) operations on the database resource. func TestAccResourceRedisCloudProDatabase_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -36,7 +37,7 @@ func TestAccResourceRedisCloudProDatabase_CRUDI(t *testing.T) { Steps: []resource.TestStep{ // Test database and replica database creation { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabase, testCloudAccountName, name, password) + testAccResourceRedisCloudProDatabaseReplica, + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_with_replica.tf"), testCloudAccountName, name, password), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", "example"), resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), @@ -104,7 +105,7 @@ func TestAccResourceRedisCloudProDatabase_CRUDI(t *testing.T) { }, // Test database is updated successfully { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseUpdate, testCloudAccountName, name), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_update.tf"), testCloudAccountName, name), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", "example-updated"), resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), @@ -132,14 +133,14 @@ func TestAccResourceRedisCloudProDatabase_CRUDI(t *testing.T) { }, // Test that alerts are deleted { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseUpdateDestroyAlerts, testCloudAccountName, name), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_update_destroy_alerts.tf"), testCloudAccountName, name), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "alert.#", "0"), ), }, // Test that a 32-character password is generated when no password is provided { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseNoPassword, testCloudAccountName, name), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_no_password.tf"), testCloudAccountName, name), Check: resource.ComposeAggregateTestCheckFunc( func(s *terraform.State) error { is := s.RootModule().Resources["rediscloud_subscription_database.no_password_database"].Primary @@ -162,7 +163,7 @@ func TestAccResourceRedisCloudProDatabase_CRUDI(t *testing.T) { func TestAccResourceRedisCloudProDatabase_optionalAttributes(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") // Test that attributes can be optional, either by setting them or not having them set when compared to CRUDI test name := acctest.RandomWithPrefix(testResourcePrefix) @@ -176,7 +177,7 @@ func TestAccResourceRedisCloudProDatabase_optionalAttributes(t *testing.T) { CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseOptionalAttributes, testCloudAccountName, name, portNumber), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_optional_attributes.tf"), testCloudAccountName, name, portNumber), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), resource.TestCheckResourceAttr(resourceName, "port", strconv.Itoa(portNumber)), @@ -188,7 +189,7 @@ func TestAccResourceRedisCloudProDatabase_optionalAttributes(t *testing.T) { func TestAccResourceRedisCloudProDatabase_timeUtcRequiresValidInterval(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") @@ -199,7 +200,7 @@ func TestAccResourceRedisCloudProDatabase_timeUtcRequiresValidInterval(t *testin CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseInvalidTimeUtc, testCloudAccountName, name), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_invalid_time_utc.tf"), testCloudAccountName, name), ExpectError: regexp.MustCompile("unexpected value at remote_backup\\.0\\.time_utc - time_utc can only be set when interval is either every-24-hours or every-12-hours"), }, }, @@ -209,7 +210,7 @@ func TestAccResourceRedisCloudProDatabase_timeUtcRequiresValidInterval(t *testin // Tests the multi-modules feature in a database resource. func TestAccResourceRedisCloudProDatabase_MultiModules(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) dbName := "db-multi-modules" @@ -222,7 +223,7 @@ func TestAccResourceRedisCloudProDatabase_MultiModules(t *testing.T) { CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseMultiModules, testCloudAccountName, name, dbName), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_multi_modules.tf"), testCloudAccountName, name, dbName), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", dbName), resource.TestCheckResourceAttr(resourceName, "modules.#", "2"), @@ -241,7 +242,7 @@ func TestAccResourceRedisCloudProDatabase_MultiModules(t *testing.T) { func TestAccResourceRedisCloudProDatabase_respversion(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") // Test that attributes can be optional, either by setting them or not having them set when compared to CRUDI test name := acctest.RandomWithPrefix(testResourcePrefix) @@ -255,294 +256,21 @@ func TestAccResourceRedisCloudProDatabase_respversion(t *testing.T) { CheckDestroy: testAccCheckProSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseRespVersions, testCloudAccountName, name, portNumber, "resp2"), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_resp_versions.tf"), testCloudAccountName, name, portNumber, "resp2"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "resp_version", "resp2"), ), }, { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseRespVersions, testCloudAccountName, name, portNumber, "resp3"), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_resp_versions.tf"), testCloudAccountName, name, portNumber, "resp3"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "resp_version", "resp3"), ), }, { - Config: fmt.Sprintf(testAccResourceRedisCloudProDatabaseRespVersions, testCloudAccountName, name, portNumber, "best_resp_100"), + Config: fmt.Sprintf(utils.GetTestConfig(t, "./pro/testdata/pro_database_resp_versions.tf"), testCloudAccountName, name, portNumber, "best_resp_100"), ExpectError: regexp.MustCompile("Bad Request: JSON parameter contains unsupported fields / values. JSON parse error: Cannot deserialize value of type `mappings.RespVersion` from String \"best_resp_100\": not one of the values accepted for Enum class: \\[resp2, resp3]"), }, }, }) } - -const proSubscriptionBoilerplate = ` -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 = "%s" -} - -resource "rediscloud_subscription" "example" { - - name = "%s" - payment_method_id = data.rediscloud_payment_method.card.id - memory_storage = "ram" - - allowlist { - cidrs = ["192.168.0.0/16"] - security_group_ids = [] - } - - 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 { - dataset_size_in_gb = 1 - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - quantity = 1 - replication=false - support_oss_cluster_api=false - modules = [] - } -} -` - -const multiModulesProSubscriptionBoilerplate = ` -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 = "%s" -} -resource "rediscloud_subscription" "example" { - name = "%s" - payment_method_id = data.rediscloud_payment_method.card.id - memory_storage = "ram" - allowlist { - cidrs = ["192.168.0.0/16"] - security_group_ids = [] - } - 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 { - dataset_size_in_gb = 1 - quantity = 1 - replication = false - support_oss_cluster_api = false - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - modules = ["RedisJSON", "RedisBloom"] - } -} -` - -// Create and Read tests -// TF config for provisioning a new database -const testAccResourceRedisCloudProDatabase = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example" - protocol = "redis" - dataset_size_in_gb = 1 - data_persistence = "none" - data_eviction = "allkeys-random" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - password = "%s" - support_oss_cluster_api = false - external_endpoint_for_oss_cluster_api = false - replication = false - average_item_size_in_bytes = 0 - client_ssl_certificate = "" - periodic_backup_path = "" - enable_default_user = true - redis_version = 7.2 - - alert { - name = "dataset-size" - value = 1 - } - - modules = [ - { - name = "RedisBloom" - } - ] - - tags = { - "market" = "emea" - "material" = "cardboard" - } -} -` - -const testAccResourceRedisCloudProDatabaseOptionalAttributes = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example-no-protocol" - dataset_size_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - port = %d -} -` - -const testAccResourceRedisCloudProDatabaseInvalidTimeUtc = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example-no-protocol" - dataset_size_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - remote_backup { - interval = "every-6-hours" - time_utc = "16:00" - storage_type = "aws-s3" - storage_path = "uri://interval.not.12.or.24.hours.test" - } -} -` - -// TF config for provisioning a database where the password is not specified -const testAccResourceRedisCloudProDatabaseNoPassword = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "no_password_database" { - subscription_id = rediscloud_subscription.example.id - name = "example-no-password" - protocol = "redis" - dataset_size_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 -} -` - -// TF config for provisioning a database which is a replica of an existing database -const testAccResourceRedisCloudProDatabaseReplica = ` -resource "rediscloud_subscription_database" "example_replica" { - subscription_id = rediscloud_subscription.example.id - name = "example-replica" - protocol = "redis" - dataset_size_in_gb = 1 - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - replica_of = [format("redis://%s", rediscloud_subscription_database.example.public_endpoint)] -} -` - -// TF config for updating a database -const testAccResourceRedisCloudProDatabaseUpdate = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example-updated" - protocol = "redis" - dataset_size_in_gb = 1 - data_persistence = "aof-every-write" - data_eviction = "volatile-lru" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 2000 - password = "updated-password" - support_oss_cluster_api = true - external_endpoint_for_oss_cluster_api = true - replication = true - average_item_size_in_bytes = 0 - enable_default_user = true - redis_version = 7.2 - - alert { - name = "dataset-size" - value = 80 - } - - modules = [ - { - name = "RedisBloom" - } - ] -} -` - -const testAccResourceRedisCloudProDatabaseUpdateDestroyAlerts = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example-updated" - protocol = "redis" - dataset_size_in_gb = 1 - data_persistence = "aof-every-write" - data_eviction = "volatile-lru" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 2000 - password = "updated-password" - support_oss_cluster_api = true - external_endpoint_for_oss_cluster_api = true - replication = true - average_item_size_in_bytes = 0 - - modules = [ - { - name = "RedisBloom" - } - ] -} -` - -const testAccResourceRedisCloudProDatabaseMultiModules = multiModulesProSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "%s" - protocol = "redis" - dataset_size_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - modules = [ - { - name = "RedisJSON" - }, - { - name = "RedisBloom" - } - ] - alert { - name = "latency" - value = 11 - } -} -` - -const testAccResourceRedisCloudProDatabaseRespVersions = proSubscriptionBoilerplate + ` -resource "rediscloud_subscription_database" "example" { - subscription_id = rediscloud_subscription.example.id - name = "example" - dataset_size_in_gb = 1 - data_persistence = "none" - throughput_measurement_by = "operations-per-second" - throughput_measurement_value = 1000 - port = %d - resp_version = "%s" -} -` diff --git a/provider/resource_rediscloud_pro_database_upgrade_test.go b/provider/resource_rediscloud_pro_database_upgrade_test.go deleted file mode 100644 index 074f08e2..00000000 --- a/provider/resource_rediscloud_pro_database_upgrade_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package provider - -import ( - "fmt" - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -const testFileName = "./pro/testdata/testAccResourceRedisCloudProDatabaseUpgrade.tf" - -func TestAccResourceRedisCloudProDatabase_Upgrade(t *testing.T) { - - testAccRequiresEnvVar(t, "EXECUTE_TESTS") - - const resourceName = "rediscloud_subscription_database.example" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, - ProviderFactories: providerFactories, - CheckDestroy: testAccCheckProSubscriptionDestroy, - Steps: []resource.TestStep{ - // Test database and replica database creation - { - Config: getRedisCloudUpgradeConfig(t, testFileName, "7.2"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "name", "example"), - resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), - resource.TestCheckResourceAttr(resourceName, "redis_version", "7.2"), - ), - }, - // Test database is updated successfully - { - Config: getRedisCloudUpgradeConfig(t, testFileName, "7.4"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "name", "example"), - resource.TestCheckResourceAttr(resourceName, "protocol", "redis"), - resource.TestCheckResourceAttr(resourceName, "redis_version", "7.4"), - ), - }, - }, - }) -} - -func getRedisCloudUpgradeConfig(t *testing.T, testFileName string, redisVersion string) string { - testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") - name := acctest.RandomWithPrefix(testResourcePrefix) - - content, err := os.ReadFile(testFileName) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return fmt.Sprintf(string(content), testCloudAccountName, name, redisVersion) -} diff --git a/provider/resource_rediscloud_pro_subscription_cmk_test.go b/provider/resource_rediscloud_pro_subscription_cmk_test.go index d20395a8..633cbf80 100644 --- a/provider/resource_rediscloud_pro_subscription_cmk_test.go +++ b/provider/resource_rediscloud_pro_subscription_cmk_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "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" "os" @@ -13,8 +14,8 @@ import ( // TODO: integrate the GCP provider and set up these permissions automatically func TestAccResourceRedisCloudProSubscription_CMK(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") - testAccRequiresEnvVar(t, "GCP_CMK_RESOURCE_NAME") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "GCP_CMK_RESOURCE_NAME") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_subscription.example" diff --git a/provider/resource_rediscloud_pro_subscription_test.go b/provider/resource_rediscloud_pro_subscription_test.go index dceae2de..15744b17 100644 --- a/provider/resource_rediscloud_pro_subscription_test.go +++ b/provider/resource_rediscloud_pro_subscription_test.go @@ -5,6 +5,8 @@ import ( "flag" "fmt" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/pro" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "strconv" @@ -28,7 +30,7 @@ var marketplaceFlag = flag.Bool("marketplace", false, // Checks CRUDI (CREATE,READ,UPDATE,IMPORT) operations on the subscription resource. func TestAccResourceRedisCloudProSubscription_CRUDI(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_subscription.example" @@ -137,7 +139,7 @@ func TestAccResourceRedisCloudProSubscription_CRUDI(t *testing.T) { func TestAccResourceRedisCloudProSubscription_preferredAZsModulesOptional(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) const resourceName = "rediscloud_subscription.example" @@ -161,7 +163,7 @@ func TestAccResourceRedisCloudProSubscription_preferredAZsModulesOptional(t *tes func TestAccResourceRedisCloudProSubscription_createUpdateContractPayment(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if !*contractFlag { t.Skip("The '-contract' parameter wasn't provided in the test command.") @@ -200,7 +202,7 @@ func TestAccResourceRedisCloudProSubscription_createUpdateContractPayment(t *tes func TestAccResourceRedisCloudProSubscription_createUpdateMarketplacePayment(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") if !*marketplaceFlag { t.Skip("The '-marketplace' parameter wasn't provided in the test command.") @@ -237,7 +239,7 @@ func TestAccResourceRedisCloudProSubscription_createUpdateMarketplacePayment(t * func TestAccResourceRedisCloudProSubscription_RedisVersion(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) testCloudAccountName := os.Getenv("AWS_TEST_CLOUD_ACCOUNT_NAME") @@ -285,7 +287,7 @@ func TestAccResourceRedisCloudProSubscription_RedisVersion(t *testing.T) { func TestAccResourceRedisCloudProSubscription_MaintenanceWindows(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) + "-mw" resourceName := "rediscloud_subscription.example" @@ -439,7 +441,7 @@ func TestAccResourceRedisCloudProSubscription_MaintenanceWindows(t *testing.T) { // Checks that modules are allocated correctly into each creation-plan db if there are multiple modules, including "RedisGraph" and the number of databases is one. func TestFlexSubModulesAllocationWhenGraphAndQuantityIsOne(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") numDatabases := 1 planMap := map[string]interface{}{ @@ -452,7 +454,7 @@ func TestFlexSubModulesAllocationWhenGraphAndQuantityIsOne(t *testing.T) { "throughput_measurement_by": "operations-per-second", "throughput_measurement_value": 10000, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRamAndFlash, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRamAndFlash, planMap) assert.Empty(t, diags) otherDatabases := 0 graphDatabases := 0 @@ -477,7 +479,7 @@ func TestFlexSubModulesAllocationWhenGraphAndQuantityIsOne(t *testing.T) { // Checks that modules are allocated correctly into each creation-plan db if there are multiple modules, including "RedisGraph" and the number of databases is greater than one. func TestFlexSubModulesAllocationWhenGraphAndQuantityMoreThanOne(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") numDatabases := 5 planMap := map[string]interface{}{ @@ -490,7 +492,7 @@ func TestFlexSubModulesAllocationWhenGraphAndQuantityMoreThanOne(t *testing.T) { "throughput_measurement_by": "operations-per-second", "throughput_measurement_value": 10000, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Empty(t, diags) graphDatabases := 0 otherDatabases := 0 @@ -514,7 +516,7 @@ func TestFlexSubModulesAllocationWhenGraphAndQuantityMoreThanOne(t *testing.T) { // Checks that modules are allocated correctly into each creation-plan db if the only module is "RedisGraph". func TestFlexSubModulesAllocationWhenOnlyGraphModule(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") numDatabases := 5 planMap := map[string]interface{}{ @@ -527,7 +529,7 @@ func TestFlexSubModulesAllocationWhenOnlyGraphModule(t *testing.T) { "throughput_measurement_by": "operations-per-second", "throughput_measurement_value": 10000, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Len(t, createDbs, numDatabases) assert.Empty(t, diags) for _, createDb := range createDbs { @@ -539,7 +541,7 @@ func TestFlexSubModulesAllocationWhenOnlyGraphModule(t *testing.T) { // Checks that modules are allocated correctly into the creation-plan dbs if "RedisGraph" is not included func TestFlexSubModulesAllocationWhenNoGraph(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") numDatabases := 5 planMap := map[string]interface{}{ @@ -552,7 +554,7 @@ func TestFlexSubModulesAllocationWhenNoGraph(t *testing.T) { "throughput_measurement_by": "number-of-shards", "throughput_measurement_value": 2, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Len(t, createDbs, numDatabases) assert.Empty(t, diags) for _, createDb := range createDbs { @@ -567,7 +569,7 @@ func TestFlexSubModulesAllocationWhenNoGraph(t *testing.T) { func TestFlexSubNoModulesInCreatePlanDatabases(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 0, @@ -579,7 +581,7 @@ func TestFlexSubNoModulesInCreatePlanDatabases(t *testing.T) { "throughput_measurement_by": "operations-per-second", "throughput_measurement_value": 10000, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Len(t, createDbs, 2) assert.Empty(t, diags) for _, createDb := range createDbs { @@ -590,7 +592,7 @@ func TestFlexSubNoModulesInCreatePlanDatabases(t *testing.T) { func TestFlexSubNoAverageItemSizeInBytes(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 0, // 0 is the value that is returned when the field is not present @@ -602,7 +604,7 @@ func TestFlexSubNoAverageItemSizeInBytes(t *testing.T) { "throughput_measurement_by": "operations-per-second", "throughput_measurement_value": 10000, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Len(t, createDbs, 2) assert.Empty(t, diags) for _, createDb := range createDbs { @@ -612,7 +614,7 @@ func TestFlexSubNoAverageItemSizeInBytes(t *testing.T) { func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsFalse(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 0, @@ -624,7 +626,7 @@ func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsFalse(t *testing "throughput_measurement_by": "number-of-shards", "throughput_measurement_value": 2, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Empty(t, diags) createDb := createDbs[0] assert.Equal(t, "number-of-shards", *createDb.ThroughputMeasurement.By) @@ -633,7 +635,7 @@ func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsFalse(t *testing func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsTrue(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 0, @@ -645,7 +647,7 @@ func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsTrue(t *testing. "throughput_measurement_by": "number-of-shards", "throughput_measurement_value": 2, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Empty(t, diags) createDb := createDbs[0] assert.Equal(t, "number-of-shards", *createDb.ThroughputMeasurement.By) @@ -654,7 +656,7 @@ func TestFlexSubRediSearchThroughputMeasurementWhenReplicationIsTrue(t *testing. func TestFlexSubRedisGraphThroughputMeasurementWhenReplicationIsFalse(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 0, @@ -666,7 +668,7 @@ func TestFlexSubRedisGraphThroughputMeasurementWhenReplicationIsFalse(t *testing "throughput_measurement_by": "number-of-shards", "throughput_measurement_value": 2, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Empty(t, diags) createDb := createDbs[0] assert.Equal(t, "operations-per-second", *createDb.ThroughputMeasurement.By) @@ -675,7 +677,7 @@ func TestFlexSubRedisGraphThroughputMeasurementWhenReplicationIsFalse(t *testing func TestFlexSubRedisGraphThroughputMeasurementWhenReplicationIsTrue(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") planMap := map[string]interface{}{ "average_item_size_in_bytes": 1000, @@ -687,7 +689,7 @@ func TestFlexSubRedisGraphThroughputMeasurementWhenReplicationIsTrue(t *testing. "throughput_measurement_by": "number-of-shards", "throughput_measurement_value": 2, } - createDbs, diags := buildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) + createDbs, diags := pro.BuildSubscriptionCreatePlanDatabases(databases.MemoryStorageRam, planMap) assert.Len(t, diags, 1, "Warning should be reported when storage was ram and using `average_item_size_in_bytes`") assert.Equal(t, diag.Warning, diags[0].Severity) createDb := createDbs[0] diff --git a/provider/resource_rediscloud_pro_tls_test.go b/provider/resource_rediscloud_pro_tls_test.go index 77fc2588..edf67bfe 100644 --- a/provider/resource_rediscloud_pro_tls_test.go +++ b/provider/resource_rediscloud_pro_tls_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" client2 "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "os" "regexp" "strconv" @@ -21,7 +22,7 @@ var invalidSslCertificate = "I am not a valid certificate" // enable_tls=true, client_ssl_certificate= func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAndSslCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -128,7 +129,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAn // enable_tls=true, client_ssl_certificate="" func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAndEmptySslCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -207,7 +208,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAn // enable_tls=true, client_ssl_certificate= func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAndInvalidSslCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -232,7 +233,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAn // enable_tls=false, client_ssl_certificate= func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseAndDisabledTlsAndInvalidCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") + utils.AccRequiresEnvVar(t, "EXECUTE_TEST_SUBSCRIPTION") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -257,7 +258,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseAndDisabledTlsAn // enable_tls=false, client_ssl_certificate="", client_tls_certificates=["something"] func TestAccResourceRedisCloudSubscriptionTls_createWithoutEnableTlsAndTlsCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -281,7 +282,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithoutEnableTlsAndTlsCert(t func TestAccResourceRedisCloudSubscriptionTls_createWithSslCertAndTlsCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) @@ -306,7 +307,7 @@ func TestAccResourceRedisCloudSubscriptionTls_createWithSslCertAndTlsCert(t *tes // enable_tls=true, client_ssl_certificate="", client_tls_certificates=["something"] func TestAccResourceRedisCloudSubscriptionTls_createWithDatabaseWithEnabledTlsAndTlsCert(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) password := acctest.RandString(20) diff --git a/provider/resource_rediscloud_subscription_peering.go b/provider/resource_rediscloud_subscription_peering.go index 0b166e85..b5a270c9 100644 --- a/provider/resource_rediscloud_subscription_peering.go +++ b/provider/resource_rediscloud_subscription_peering.go @@ -179,7 +179,7 @@ func resourceRedisCloudSubscriptionPeeringCreate(ctx context.Context, d *schema. if vpcCIDR, ok := d.GetOk("vpc_cidr"); ok { peeringRequest.VPCCidr = redis.String(vpcCIDR.(string)) } else if vpcCIDRs, ok := d.GetOk("vpc_cidrs"); ok { - peeringRequest.VPCCidrs = setToStringSlice(vpcCIDRs.(*schema.Set)) + peeringRequest.VPCCidrs = utils.SetToStringSlice(vpcCIDRs.(*schema.Set)) } else { return diag.Errorf("`vpc_cidr` or `vpc_cidrs` must be set when `provider_name` is `AWS`") } @@ -211,7 +211,7 @@ func resourceRedisCloudSubscriptionPeeringCreate(ctx context.Context, d *schema. return diag.FromErr(err) } - d.SetId(buildResourceId(subId, peering)) + d.SetId(utils.BuildResourceId(subId, peering)) err = waitForPeeringToBeInitiated(ctx, subId, peering, api) if err != nil { diff --git a/provider/resource_rediscloud_subscription_peering_test.go b/provider/resource_rediscloud_subscription_peering_test.go index 4ec71d8c..6862a55b 100644 --- a/provider/resource_rediscloud_subscription_peering_test.go +++ b/provider/resource_rediscloud_subscription_peering_test.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "github.com/RedisLabs/terraform-provider-rediscloud/provider/utils" "net" "os" "regexp" @@ -13,7 +14,7 @@ import ( func TestAccResourceRedisCloudSubscriptionPeering_aws(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) @@ -77,7 +78,7 @@ func TestAccResourceRedisCloudSubscriptionPeering_aws(t *testing.T) { func TestAccResourceRedisCloudSubscriptionPeering_gcp(t *testing.T) { - testAccRequiresEnvVar(t, "EXECUTE_TESTS") + utils.AccRequiresEnvVar(t, "EXECUTE_TESTS") name := acctest.RandomWithPrefix(testResourcePrefix) diff --git a/provider/resource_rediscloud_transit_gateway_attachment.go b/provider/resource_rediscloud_transit_gateway_attachment.go index c5595084..13a170a0 100644 --- a/provider/resource_rediscloud_transit_gateway_attachment.go +++ b/provider/resource_rediscloud_transit_gateway_attachment.go @@ -5,6 +5,7 @@ import ( "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/transit_gateway/attachments" "github.com/RedisLabs/terraform-provider-rediscloud/provider/client" + "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" "strconv" @@ -89,7 +90,7 @@ func resourceRedisCloudTransitGatewayAttachmentCreate(ctx context.Context, d *sc } // At this point, cidrs has to be empty. We cannot honour the user's configuration until the invitation has been accepted - cidrs := interfaceToStringSlice(d.Get("cidrs").([]interface{})) + cidrs := utils.InterfaceToStringSlice(d.Get("cidrs").([]interface{})) if len(cidrs) > 0 { return diag.Errorf("Attachment cannot be created with Cidrs provided, it must be accepted first. This resource may then be updated with Cidrs.") } @@ -133,7 +134,7 @@ func resourceRedisCloudTransitGatewayAttachmentRead(ctx context.Context, d *sche } tgw := tgws[0] - d.SetId(buildResourceId(subId, tgwId)) + d.SetId(utils.BuildResourceId(subId, tgwId)) if err := d.Set("aws_tgw_uid", redis.StringValue(tgw.AwsTgwUid)); err != nil { return diag.FromErr(err) } @@ -165,7 +166,7 @@ func resourceRedisCloudTransitGatewayAttachmentUpdate(ctx context.Context, d *sc return diag.FromErr(err) } - cidrs := interfaceToStringSlice(d.Get("cidrs").([]interface{})) + cidrs := utils.InterfaceToStringSlice(d.Get("cidrs").([]interface{})) if len(cidrs) == 0 { cidrs = make([]*string, 0) } diff --git a/provider/utils.go b/provider/utils.go deleted file mode 100644 index 3d7cc44b..00000000 --- a/provider/utils.go +++ /dev/null @@ -1,162 +0,0 @@ -package provider - -import ( - "fmt" - "time" - - "github.com/RedisLabs/rediscloud-go-api/redis" - "github.com/RedisLabs/rediscloud-go-api/service/latest_backups" - "github.com/RedisLabs/rediscloud-go-api/service/latest_imports" - "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func setToStringSlice(set *schema.Set) []*string { - var ret []*string - for _, s := range set.List() { - ret = append(ret, redis.String(s.(string))) - } - return ret -} - -func interfaceToStringSlice(list []interface{}) []*string { - var ret []*string - for _, i := range list { - if i == nil { - // The user probably entered "" (string's zero-value) but gets read in as nil (interface{}'s zero-value) - ret = append(ret, redis.String("")) - } else { - ret = append(ret, redis.String(i.(string))) - } - } - return ret -} - -// IDs of any resources dependent on a subscription need to be divided by a slash. In this format: /. -func buildResourceId(subId int, id int) string { - return fmt.Sprintf("%d/%d", subId, id) -} - -func isTime() schema.SchemaValidateDiagFunc { - return func(i interface{}, path cty.Path) diag.Diagnostics { - var diags diag.Diagnostics - - v, ok := i.(string) - if !ok { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Value not a string", - Detail: fmt.Sprintf("Value should be a string rather than %T", i), - }) - } else if _, err := time.Parse("15:04", v); err != nil { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Value is not a time", - Detail: fmt.Sprintf("Value should be a valid time, got: %q: %s", i, err), - }) - } - - return diags - } -} - -func parseLatestBackupStatus(latestBackupStatus *latest_backups.LatestBackupStatus) ([]map[string]interface{}, error) { - lbs := map[string]interface{}{ - "response": nil, - "error": nil, - } - - if latestBackupStatus.Response.Resource != nil { - res := map[string]interface{}{ - "status": redis.StringValue(latestBackupStatus.Response.Resource.Status), - "last_backup_time": nil, - "failure_reason": redis.StringValue(latestBackupStatus.Response.Resource.FailureReason), - } - if latestBackupStatus.Response.Resource.LastBackupTime != nil { - res["last_backup_time"] = latestBackupStatus.Response.Resource.LastBackupTime.String() - } - lbs["response"] = []map[string]interface{}{res} - } - - if latestBackupStatus.Response.Error != nil { - err := map[string]interface{}{ - "type": redis.StringValue(latestBackupStatus.Response.Error.Type), - "description": redis.StringValue(latestBackupStatus.Response.Error.Description), - "status": redis.StringValue(latestBackupStatus.Response.Error.Status), - } - lbs["error"] = []map[string]interface{}{err} - } - - return []map[string]interface{}{lbs}, nil -} - -func parseLatestImportStatus(latestImportStatus *latest_imports.LatestImportStatus) ([]map[string]interface{}, error) { - lis := map[string]interface{}{ - "response": nil, - "error": nil, - } - - if latestImportStatus.Response.Resource != nil { - res := map[string]interface{}{ - "status": redis.StringValue(latestImportStatus.Response.Resource.Status), - "last_import_time": nil, - "failure_reason": redis.StringValue(latestImportStatus.Response.Resource.FailureReason), - "failure_reason_params": parseFailureReasonParams(latestImportStatus.Response.Resource.FailureReasonParams), - } - if latestImportStatus.Response.Resource.LastImportTime != nil { - res["last_import_time"] = latestImportStatus.Response.Resource.LastImportTime.String() - } - lis["response"] = []map[string]interface{}{res} - } - - if latestImportStatus.Response.Error != nil { - err := map[string]interface{}{ - "type": redis.StringValue(latestImportStatus.Response.Error.Type), - "description": redis.StringValue(latestImportStatus.Response.Error.Description), - "status": redis.StringValue(latestImportStatus.Response.Error.Status), - } - lis["error"] = []map[string]interface{}{err} - } - - return []map[string]interface{}{lis}, nil -} - -func parseFailureReasonParams(params []*latest_imports.FailureReasonParam) []map[string]interface{} { - writableParams := make([]map[string]interface{}, 0) - for _, param := range params { - writableParams = append(writableParams, map[string]interface{}{ - "key": redis.StringValue(param.Key), - "value": redis.StringValue(param.Value), - }) - } - return writableParams -} - -func applyCertificateHints(tlsAuthEnabled bool, d *schema.ResourceData) error { - sslCertificate := d.Get("client_ssl_certificate").(string) - tlsCertificates := interfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) - if tlsAuthEnabled { - if sslCertificate == "" && len(tlsCertificates) == 0 { - // The resource does have SSL/TLS auth enabled, but it was not certified by this template. - if err := d.Set("client_tls_certificates", []interface{}{"Unknown certificate"}); err != nil { - return err - } - } - } else { - if sslCertificate != "" { - // The resource does not have SSL/TLS auth enabled, but this template provides an SSL certificate - if err := d.Set("client_ssl_certificate", ""); err != nil { - return err - } - } - if len(tlsCertificates) >= 0 { - // The resource does not have SSL/TLS auth enabled, but this template provides TLS certificates. - if err := d.Set("client_tls_certificates", []interface{}{}); err != nil { - return err - } - } - } - - return nil -} diff --git a/provider/utils/get_set.go b/provider/utils/get_set.go new file mode 100644 index 00000000..da65c4a7 --- /dev/null +++ b/provider/utils/get_set.go @@ -0,0 +1,77 @@ +package utils + +import ( + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "time" +) + +// This timeout is an absolute maximum used in some of the waitForStatus operations concerning creation and updating +// Subscriptions and Databases. Reads and Deletions have their own, stricter timeouts because they consistently behave +// well. The Terraform operation-level timeout should kick in way before we hit this and kill the task. +// Unfortunately there's no "time-remaining-before-timeout" utility, or we could use that in the wait blocks. +const SafetyTimeout = 6 * time.Hour + +// GetString safely retrieves a string value from schema.ResourceData. +func GetString(d *schema.ResourceData, key string) *string { + if v, ok := d.GetOk(key); ok { + return redis.String(v.(string)) + } + return redis.String("") +} + +// GetBool safely retrieves a bool value from schema.ResourceData. +func GetBool(d *schema.ResourceData, key string) *bool { + if v, ok := d.GetOk(key); ok { + return redis.Bool(v.(bool)) + } + return redis.Bool(false) +} + +// GetInt safely retrieves an int value from schema.ResourceData. +func GetInt(d *schema.ResourceData, key string) *int { + if v, ok := d.GetOk(key); ok { + return redis.Int(v.(int)) + } + return redis.Int(0) +} + +func SetStringIfNotEmpty(d *schema.ResourceData, key string, setter func(*string)) { + if v, ok := d.GetOk(key); ok { + if s, valid := v.(string); valid && s != "" { + setter(redis.String(s)) + } + } +} + +func SetIntIfPositive(d *schema.ResourceData, key string, setter func(*int)) { + if v, ok := d.GetOk(key); ok { + if i, valid := v.(int); valid && i > 0 { + setter(redis.Int(i)) + } + } +} + +func SetInt(d *schema.ResourceData, key string, setter func(*int)) { + if v, ok := d.GetOk(key); ok { + if i, valid := v.(int); valid { + setter(redis.Int(i)) + } + } +} + +func SetFloat64(d *schema.ResourceData, key string, setter func(*float64)) { + if v, ok := d.GetOk(key); ok { + if f, valid := v.(float64); valid { + setter(redis.Float64(f)) + } + } +} + +func SetBool(d *schema.ResourceData, key string, setter func(*bool)) { + if v, ok := d.GetOk(key); ok { + if b, valid := v.(bool); valid { + setter(redis.Bool(b)) + } + } +} diff --git a/provider/utils/test_utils.go b/provider/utils/test_utils.go new file mode 100644 index 00000000..78422f20 --- /dev/null +++ b/provider/utils/test_utils.go @@ -0,0 +1,23 @@ +package utils + +import ( + "os" + "testing" +) + +func AccRequiresEnvVar(t *testing.T, envVarName string) string { + envVarValue := os.Getenv(envVarName) + if envVarValue == "" || envVarValue == "false" { + t.Skipf("Skipping test because %s is not set.", envVarName) + } + return envVarValue +} + +func GetTestConfig(t *testing.T, testFile string) string { + content, err := os.ReadFile(testFile) + if err != nil { + t.Fatalf("failed to read file: %v", err) + } + + return string(content) +} diff --git a/provider/utils/utils.go b/provider/utils/utils.go index da65c4a7..6c3b99eb 100644 --- a/provider/utils/utils.go +++ b/provider/utils/utils.go @@ -1,77 +1,162 @@ package utils import ( + "fmt" + "time" + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/latest_backups" + "github.com/RedisLabs/rediscloud-go-api/service/latest_imports" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "time" ) -// This timeout is an absolute maximum used in some of the waitForStatus operations concerning creation and updating -// Subscriptions and Databases. Reads and Deletions have their own, stricter timeouts because they consistently behave -// well. The Terraform operation-level timeout should kick in way before we hit this and kill the task. -// Unfortunately there's no "time-remaining-before-timeout" utility, or we could use that in the wait blocks. -const SafetyTimeout = 6 * time.Hour - -// GetString safely retrieves a string value from schema.ResourceData. -func GetString(d *schema.ResourceData, key string) *string { - if v, ok := d.GetOk(key); ok { - return redis.String(v.(string)) +func SetToStringSlice(set *schema.Set) []*string { + var ret []*string + for _, s := range set.List() { + ret = append(ret, redis.String(s.(string))) } - return redis.String("") + return ret } -// GetBool safely retrieves a bool value from schema.ResourceData. -func GetBool(d *schema.ResourceData, key string) *bool { - if v, ok := d.GetOk(key); ok { - return redis.Bool(v.(bool)) +func InterfaceToStringSlice(list []interface{}) []*string { + var ret []*string + for _, i := range list { + if i == nil { + // The user probably entered "" (string's zero-value) but gets read in as nil (interface{}'s zero-value) + ret = append(ret, redis.String("")) + } else { + ret = append(ret, redis.String(i.(string))) + } } - return redis.Bool(false) + return ret } -// GetInt safely retrieves an int value from schema.ResourceData. -func GetInt(d *schema.ResourceData, key string) *int { - if v, ok := d.GetOk(key); ok { - return redis.Int(v.(int)) - } - return redis.Int(0) +// IDs of any resources dependent on a subscription need to be divided by a slash. In this format: /. +func BuildResourceId(subId int, id int) string { + return fmt.Sprintf("%d/%d", subId, id) } -func SetStringIfNotEmpty(d *schema.ResourceData, key string, setter func(*string)) { - if v, ok := d.GetOk(key); ok { - if s, valid := v.(string); valid && s != "" { - setter(redis.String(s)) +func IsTime() schema.SchemaValidateDiagFunc { + return func(i interface{}, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + v, ok := i.(string) + if !ok { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Value not a string", + Detail: fmt.Sprintf("Value should be a string rather than %T", i), + }) + } else if _, err := time.Parse("15:04", v); err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Value is not a time", + Detail: fmt.Sprintf("Value should be a valid time, got: %q: %s", i, err), + }) } + + return diags } } -func SetIntIfPositive(d *schema.ResourceData, key string, setter func(*int)) { - if v, ok := d.GetOk(key); ok { - if i, valid := v.(int); valid && i > 0 { - setter(redis.Int(i)) +func ParseLatestBackupStatus(latestBackupStatus *latest_backups.LatestBackupStatus) ([]map[string]interface{}, error) { + lbs := map[string]interface{}{ + "response": nil, + "error": nil, + } + + if latestBackupStatus.Response.Resource != nil { + res := map[string]interface{}{ + "status": redis.StringValue(latestBackupStatus.Response.Resource.Status), + "last_backup_time": nil, + "failure_reason": redis.StringValue(latestBackupStatus.Response.Resource.FailureReason), } + if latestBackupStatus.Response.Resource.LastBackupTime != nil { + res["last_backup_time"] = latestBackupStatus.Response.Resource.LastBackupTime.String() + } + lbs["response"] = []map[string]interface{}{res} } -} -func SetInt(d *schema.ResourceData, key string, setter func(*int)) { - if v, ok := d.GetOk(key); ok { - if i, valid := v.(int); valid { - setter(redis.Int(i)) + if latestBackupStatus.Response.Error != nil { + err := map[string]interface{}{ + "type": redis.StringValue(latestBackupStatus.Response.Error.Type), + "description": redis.StringValue(latestBackupStatus.Response.Error.Description), + "status": redis.StringValue(latestBackupStatus.Response.Error.Status), } + lbs["error"] = []map[string]interface{}{err} } + + return []map[string]interface{}{lbs}, nil } -func SetFloat64(d *schema.ResourceData, key string, setter func(*float64)) { - if v, ok := d.GetOk(key); ok { - if f, valid := v.(float64); valid { - setter(redis.Float64(f)) +func ParseLatestImportStatus(latestImportStatus *latest_imports.LatestImportStatus) ([]map[string]interface{}, error) { + lis := map[string]interface{}{ + "response": nil, + "error": nil, + } + + if latestImportStatus.Response.Resource != nil { + res := map[string]interface{}{ + "status": redis.StringValue(latestImportStatus.Response.Resource.Status), + "last_import_time": nil, + "failure_reason": redis.StringValue(latestImportStatus.Response.Resource.FailureReason), + "failure_reason_params": parseFailureReasonParams(latestImportStatus.Response.Resource.FailureReasonParams), } + if latestImportStatus.Response.Resource.LastImportTime != nil { + res["last_import_time"] = latestImportStatus.Response.Resource.LastImportTime.String() + } + lis["response"] = []map[string]interface{}{res} } + + if latestImportStatus.Response.Error != nil { + err := map[string]interface{}{ + "type": redis.StringValue(latestImportStatus.Response.Error.Type), + "description": redis.StringValue(latestImportStatus.Response.Error.Description), + "status": redis.StringValue(latestImportStatus.Response.Error.Status), + } + lis["error"] = []map[string]interface{}{err} + } + + return []map[string]interface{}{lis}, nil } -func SetBool(d *schema.ResourceData, key string, setter func(*bool)) { - if v, ok := d.GetOk(key); ok { - if b, valid := v.(bool); valid { - setter(redis.Bool(b)) +func parseFailureReasonParams(params []*latest_imports.FailureReasonParam) []map[string]interface{} { + writableParams := make([]map[string]interface{}, 0) + for _, param := range params { + writableParams = append(writableParams, map[string]interface{}{ + "key": redis.StringValue(param.Key), + "value": redis.StringValue(param.Value), + }) + } + return writableParams +} + +func ApplyCertificateHints(tlsAuthEnabled bool, d *schema.ResourceData) error { + sslCertificate := d.Get("client_ssl_certificate").(string) + tlsCertificates := InterfaceToStringSlice(d.Get("client_tls_certificates").([]interface{})) + if tlsAuthEnabled { + if sslCertificate == "" && len(tlsCertificates) == 0 { + // The resource does have SSL/TLS auth enabled, but it was not certified by this template. + if err := d.Set("client_tls_certificates", []interface{}{"Unknown certificate"}); err != nil { + return err + } + } + } else { + if sslCertificate != "" { + // The resource does not have SSL/TLS auth enabled, but this template provides an SSL certificate + if err := d.Set("client_ssl_certificate", ""); err != nil { + return err + } + } + if len(tlsCertificates) >= 0 { + // The resource does not have SSL/TLS auth enabled, but this template provides TLS certificates. + if err := d.Set("client_tls_certificates", []interface{}{}); err != nil { + return err + } } } + + return nil } diff --git a/provider/utils_test.go b/provider/utils/utils_test.go similarity index 51% rename from provider/utils_test.go rename to provider/utils/utils_test.go index 31dfe503..b5c55f30 100644 --- a/provider/utils_test.go +++ b/provider/utils/utils_test.go @@ -1,8 +1,7 @@ -package provider +package utils import ( "github.com/stretchr/testify/assert" - "os" "testing" ) @@ -22,25 +21,8 @@ func TestIsTime(t *testing.T) { for _, test := range tests { t.Run(test.input, func(t *testing.T) { - actual := isTime()(test.input, nil) + actual := IsTime()(test.input, nil) assert.Equal(t, test.errors, actual.HasError(), "%+v", actual) }) } } - -func testAccRequiresEnvVar(t *testing.T, envVarName string) string { - envVarValue := os.Getenv(envVarName) - if envVarValue == "" || envVarValue == "false" { - t.Skipf("Skipping test because %s is not set.", envVarName) - } - return envVarValue -} - -func getTestConfig(t *testing.T, testFile string) string { - content, err := os.ReadFile(testFile) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - return string(content) -}