From 3a07ad2ce89a46a62350d61c1e6b28731a37acd9 Mon Sep 17 00:00:00 2001 From: Gogen120 Date: Tue, 19 Jul 2022 12:50:18 +0300 Subject: [PATCH 1/4] Add datastore type dependent DBaaS resources - Add datastore, databases, extension resources for specific datastore type: selectel_dbaas_postgresql_datastore_v1, selectel_dbaas_mysql_datastore_v1, selectel_dbaas_redis_datastore_v1, selectel_dbaas_postgresql_database_v1, selectel_dbaas_mysql_database_v1, selectel_dbaas_postgresql_extension_v1 - Mark old datastore, databases, extension that were available for all types as deprecated --- selectel/dbaas.go | 433 +++++++++++++++++ selectel/dbaas_postgresql_utils.go | 85 ++++ selectel/dbaas_redis_utils.go | 56 +++ ..._selectel_dbaas_mysql_datastore_v1_test.go | 32 ++ ...ctel_dbaas_postgresql_datastore_v1_test.go | 32 ++ ..._selectel_dbaas_redis_datastore_v1_test.go | 33 ++ selectel/provider.go | 12 +- .../resource_selectel_dbaas_database_v1.go | 65 --- ...esource_selectel_dbaas_database_v1_test.go | 139 ++++++ .../resource_selectel_dbaas_datastore_v1.go | 386 +--------------- ...source_selectel_dbaas_datastore_v1_test.go | 133 ++++++ ...source_selectel_dbaas_mysql_database_v1.go | 176 +++++++ ...e_selectel_dbaas_mysql_database_v1_test.go | 83 ++++ ...ource_selectel_dbaas_mysql_datastore_v1.go | 359 +++++++++++++++ ..._selectel_dbaas_mysql_datastore_v1_test.go | 351 ++++++++++++++ ...e_selectel_dbaas_postgresql_database_v1.go | 244 ++++++++++ ...ectel_dbaas_postgresql_database_v1_test.go | 235 ++++++++++ ..._selectel_dbaas_postgresql_datastore_v1.go | 396 ++++++++++++++++ ...ctel_dbaas_postgresql_datastore_v1_test.go | 434 ++++++++++++++++++ ..._selectel_dbaas_postgresql_extension_v1.go | 186 ++++++++ ...ctel_dbaas_postgresql_extension_v1_test.go | 113 +++++ ...ource_selectel_dbaas_redis_datastore_v1.go | 352 ++++++++++++++ ..._selectel_dbaas_redis_datastore_v1_test.go | 280 +++++++++++ selectel/resource_selectel_dbaas_user_v1.go | 56 --- .../docs/r/dbaas_database_v1.html.markdown | 2 + .../docs/r/dbaas_datastore_v1.html.markdown | 2 + .../docs/r/dbaas_extension_v1.html.markdown | 2 + .../r/dbaas_mysql_database_v1.html.markdown | 84 ++++ .../r/dbaas_mysql_datastore_v1.html.markdown | 111 +++++ ...dbaas_postgresql_database_v1.html.markdown | 105 +++++ ...baas_postgresql_datastore_v1.html.markdown | 124 +++++ ...baas_postgresql_extension_v1.html.markdown | 120 +++++ .../r/dbaas_redis_datastore_v1.html.markdown | 108 +++++ website/selectel.erb | 18 + 34 files changed, 4840 insertions(+), 507 deletions(-) create mode 100644 selectel/dbaas_postgresql_utils.go create mode 100644 selectel/dbaas_redis_utils.go create mode 100644 selectel/import_selectel_dbaas_mysql_datastore_v1_test.go create mode 100644 selectel/import_selectel_dbaas_postgresql_datastore_v1_test.go create mode 100644 selectel/import_selectel_dbaas_redis_datastore_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_mysql_database_v1.go create mode 100644 selectel/resource_selectel_dbaas_mysql_database_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_mysql_datastore_v1.go create mode 100644 selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_database_v1.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_database_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_datastore_v1.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_datastore_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_extension_v1.go create mode 100644 selectel/resource_selectel_dbaas_postgresql_extension_v1_test.go create mode 100644 selectel/resource_selectel_dbaas_redis_datastore_v1.go create mode 100644 selectel/resource_selectel_dbaas_redis_datastore_v1_test.go create mode 100644 website/docs/r/dbaas_mysql_database_v1.html.markdown create mode 100644 website/docs/r/dbaas_mysql_datastore_v1.html.markdown create mode 100644 website/docs/r/dbaas_postgresql_database_v1.html.markdown create mode 100644 website/docs/r/dbaas_postgresql_datastore_v1.html.markdown create mode 100644 website/docs/r/dbaas_postgresql_extension_v1.html.markdown create mode 100644 website/docs/r/dbaas_redis_datastore_v1.html.markdown diff --git a/selectel/dbaas.go b/selectel/dbaas.go index a5708f2e..e679931a 100644 --- a/selectel/dbaas.go +++ b/selectel/dbaas.go @@ -3,6 +3,7 @@ package selectel import ( "context" "crypto/md5" + "errors" "fmt" "log" "math/rand" @@ -12,6 +13,7 @@ import ( "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/selectel/dbaas-go" @@ -138,3 +140,434 @@ func convertFieldToStringByType(field interface{}) string { func RandomWithPrefix(name string) string { return fmt.Sprintf("%s_%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) } + +func flavorSchema() *schema.Resource { + return resourceDBaaSDatastoreV1().Schema["flavor"].Elem.(*schema.Resource) +} + +func flavorHashSetFunc() schema.SchemaSetFunc { + return schema.HashResource(flavorSchema()) +} + +func convertFieldFromStringToType(fieldValue string) interface{} { + if val, err := strconv.Atoi(fieldValue); err == nil { + return val + } else if val, err := strconv.ParseFloat(fieldValue, 64); err == nil { + return val + } else if val, err := strconv.ParseFloat(fieldValue, 32); err == nil { + return val + } else if val, err := strconv.ParseBool(fieldValue); err == nil { + return val + } else { + return fieldValue + } +} + +func waitForDBaaSDatastoreV1ActiveState( + ctx context.Context, client *dbaas.API, datastoreID string, timeout time.Duration) error { + pending := []string{ + string(dbaas.StatusPendingCreate), + string(dbaas.StatusPendingUpdate), + string(dbaas.StatusResizing), + } + target := []string{ + string(dbaas.StatusActive), + } + + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: dbaasDatastoreV1StateRefreshFunc(ctx, client, datastoreID), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "error waiting for the datastore %s to become 'ACTIVE': %s", + datastoreID, err) + } + + return nil +} + +func dbaasDatastoreV1StateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.Datastore(ctx, datastoreID) + if err != nil { + return nil, "", err + } + + return d, string(d.Status), nil + } +} + +func dbaasDatastoreV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.Datastore(ctx, datastoreID) + if err != nil { + var dbaasError *dbaas.DBaaSAPIError + if errors.As(err, &dbaasError) { + return d, strconv.Itoa(dbaasError.StatusCode()), nil + } + + return nil, "", err + } + + return d, strconv.Itoa(200), err + } +} + +func resourceDBaaSDatastoreV1FlavorFromSet(flavorSet *schema.Set) (*dbaas.Flavor, error) { + if flavorSet.Len() == 0 { + return nil, nil + } + var resourceVcpusRaw, resourceRAMRaw, resourceDiskRaw interface{} + var ok bool + + resourceFlavorMap := flavorSet.List()[0].(map[string]interface{}) + if resourceVcpusRaw, ok = resourceFlavorMap["vcpus"]; !ok { + return &dbaas.Flavor{}, errors.New("flavor.vcpus value isn't provided") + } + if resourceRAMRaw, ok = resourceFlavorMap["ram"]; !ok { + return &dbaas.Flavor{}, errors.New("flavor.ram value isn't provided") + } + if resourceDiskRaw, ok = resourceFlavorMap["disk"]; !ok { + return &dbaas.Flavor{}, errors.New("flavor.disk value isn't provided") + } + + resourceVcpus := resourceVcpusRaw.(int) + resourceRAM := resourceRAMRaw.(int) + resourceDisk := resourceDiskRaw.(int) + + flavor := &dbaas.Flavor{ + Vcpus: resourceVcpus, + RAM: resourceRAM, + Disk: resourceDisk, + } + + return flavor, nil +} + +func resourceDBaaSDatastoreV1FlavorToSet(flavor dbaas.Flavor) *schema.Set { + flavorSet := &schema.Set{ + F: flavorHashSetFunc(), + } + + flavorSet.Add(map[string]interface{}{ + "vcpus": flavor.Vcpus, + "ram": flavor.RAM, + "disk": flavor.Disk, + }) + + return flavorSet +} + +func resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet *schema.Set) (dbaas.DatastoreFirewallOpts, error) { + if firewallSet.Len() == 0 { + return dbaas.DatastoreFirewallOpts{IPs: []string{}}, nil + } + + var resourceIPsRaw interface{} + var ok bool + + resourceFirewallRaw := firewallSet.List()[0].(map[string]interface{}) + if resourceIPsRaw, ok = resourceFirewallRaw["ips"]; !ok { + return dbaas.DatastoreFirewallOpts{}, errors.New("firewall.ips value isn't provided") + } + resourceIPRaw := resourceIPsRaw.([]interface{}) + var firewall dbaas.DatastoreFirewallOpts + for _, ip := range resourceIPRaw { + firewall.IPs = append(firewall.IPs, ip.(string)) + } + + return firewall, nil +} + +func resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet *schema.Set) (*dbaas.Restore, error) { + if restoreSet.Len() == 0 { + return nil, nil + } + var resourceDatastoreIDRaw, resourceTargetTimeRaw interface{} + var ok bool + + resourceRestoreMap := restoreSet.List()[0].(map[string]interface{}) + if resourceDatastoreIDRaw, ok = resourceRestoreMap["datastore_id"]; !ok { + return &dbaas.Restore{}, errors.New("restore.datastore_id value isn't provided") + } + if resourceTargetTimeRaw, ok = resourceRestoreMap["target_time"]; !ok { + return &dbaas.Restore{}, errors.New("restore.target_time value isn't provided") + } + + resourceDatastoreID := resourceDatastoreIDRaw.(string) + resourceTargetTime := resourceTargetTimeRaw.(string) + + restore := &dbaas.Restore{ + DatastoreID: resourceDatastoreID, + TargetTime: resourceTargetTime, + } + + return restore, nil +} + +func updateDatastoreName(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + var updateOpts dbaas.DatastoreUpdateOpts + updateOpts.Name = d.Get("name").(string) + + log.Print(msgUpdate(objectDatastore, d.Id(), updateOpts)) + _, err := client.UpdateDatastore(ctx, d.Id(), updateOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutUpdate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} + +func updateDatastoreFirewall(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + firewallSet := d.Get("firewall").(*schema.Set) + firewallOpts, err := resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet) + if err != nil { + return errParseDatastoreV1Firewall(err) + } + + log.Print(msgUpdate(objectDatastore, d.Id(), firewallOpts)) + _, err = client.FirewallDatastore(ctx, d.Id(), firewallOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutUpdate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} + +func updateDatastoreConfig(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + var configOpts dbaas.DatastoreConfigOpts + datastore, err := client.Datastore(ctx, d.Id()) + if err != nil { + return err + } + configMap := d.Get("config").(map[string]interface{}) + config := make(map[string]interface{}) + for paramName, paramValue := range configMap { + paramValueStr := paramValue.(string) + config[paramName] = convertFieldFromStringToType(paramValueStr) + } + + for param := range datastore.Config { + if _, ok := config[param]; !ok { + config[param] = nil + } + } + + configOpts.Config = config + + log.Print(msgUpdate(objectDatastore, d.Id(), configOpts)) + _, err = client.ConfigDatastore(ctx, d.Id(), configOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutUpdate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} + +func resizeDatastore(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + var resizeOpts dbaas.DatastoreResizeOpts + nodeCount := d.Get("node_count").(int) + resizeOpts.NodeCount = nodeCount + + flavorID := d.Get("flavor_id") + flavorRaw := d.Get("flavor") + + flavorSet := flavorRaw.(*schema.Set) + flavor, err := resourceDBaaSDatastoreV1FlavorFromSet(flavorSet) + if err != nil { + return errParseDatastoreV1Resize(err) + } + + typeID := d.Get("type_id").(string) + datastoreType, err := client.DatastoreType(ctx, typeID) + if err != nil { + return errors.New("Couldnt get datastore type with id" + typeID) + } + if datastoreType.Engine == "redis" { + resizeOpts.Flavor = nil + resizeOpts.FlavorID = flavorID.(string) + } else { + resizeOpts.Flavor = flavor + resizeOpts.FlavorID = flavorID.(string) + } + + log.Print(msgUpdate(objectDatastore, d.Id(), resizeOpts)) + _, err = client.ResizeDatastore(ctx, d.Id(), resizeOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} + +func validateDatastoreType(ctx context.Context, expectedDatastoreTypeEngine string, typeID string, client *dbaas.API) diag.Diagnostics { + datastoreType, err := client.DatastoreType(ctx, typeID) + if err != nil { + return diag.FromErr(errors.New("Couldnt get datastore type with id" + typeID)) + } + if datastoreType.Engine != expectedDatastoreTypeEngine { + return diag.FromErr(errors.New("Provided datastore type must have a " + expectedDatastoreTypeEngine + " engine, not " + datastoreType.Engine)) + } + + return nil +} + +func waitForDBaaSDatabaseV1ActiveState( + ctx context.Context, client *dbaas.API, databaseID string, timeout time.Duration) error { + pending := []string{ + string(dbaas.StatusPendingCreate), + string(dbaas.StatusPendingUpdate), + } + target := []string{ + string(dbaas.StatusActive), + } + + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: dbaasDatabaseV1StateRefreshFunc(ctx, client, databaseID), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "error waiting for the database %s to become 'ACTIVE': %s", + databaseID, err) + } + + return nil +} + +// Databases + +func dbaasDatabaseV1StateRefreshFunc(ctx context.Context, client *dbaas.API, databaseID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.Database(ctx, databaseID) + if err != nil { + return nil, "", err + } + + return d, string(d.Status), nil + } +} + +func dbaasDatabaseV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.Database(ctx, datastoreID) + if err != nil { + var dbaasError *dbaas.DBaaSAPIError + if errors.As(err, &dbaasError) { + return d, strconv.Itoa(dbaasError.StatusCode()), nil + } + + return nil, "", err + } + + return d, strconv.Itoa(200), err + } +} + +func dbaasDatabaseV1LocaleDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { + // The default locale value - C is the same as null value, so we need to suppress + if old == "C" && new == "" { + return true + } + + return false +} + +// Users + +func waitForDBaaSUserV1ActiveState( + ctx context.Context, client *dbaas.API, userID string, timeout time.Duration) error { + pending := []string{ + string(dbaas.StatusPendingCreate), + string(dbaas.StatusPendingUpdate), + } + target := []string{ + string(dbaas.StatusActive), + } + + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: dbaasUserV1StateRefreshFunc(ctx, client, userID), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "error waiting for the user %s to become 'ACTIVE': %s", + userID, err) + } + + return nil +} + +func dbaasUserV1StateRefreshFunc(ctx context.Context, client *dbaas.API, userID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.User(ctx, userID) + if err != nil { + return nil, "", err + } + + return d, string(d.Status), nil + } +} + +func dbaasUserV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, userID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + d, err := client.User(ctx, userID) + if err != nil { + var dbaasError *dbaas.DBaaSAPIError + if errors.As(err, &dbaasError) { + return d, strconv.Itoa(dbaasError.StatusCode()), nil + } + + return nil, "", err + } + + return d, strconv.Itoa(200), err + } +} diff --git a/selectel/dbaas_postgresql_utils.go b/selectel/dbaas_postgresql_utils.go new file mode 100644 index 00000000..5c9a7eab --- /dev/null +++ b/selectel/dbaas_postgresql_utils.go @@ -0,0 +1,85 @@ +package selectel + +import ( + "context" + "errors" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSPostgreSQLDatastoreV1PoolerFromSet(poolerSet *schema.Set) (*dbaas.Pooler, error) { + if poolerSet.Len() == 0 { + return nil, nil + } + var resourceModeRaw, resourceSizeRaw interface{} + var ok bool + + resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) + if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { + return &dbaas.Pooler{}, errors.New("pooler.mode value isn't provided") + } + if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { + return &dbaas.Pooler{}, errors.New("pooler.size value isn't provided") + } + + resourceMode := resourceModeRaw.(string) + resourceSize := resourceSizeRaw.(int) + + pooler := &dbaas.Pooler{ + Mode: resourceMode, + Size: resourceSize, + } + + return pooler, nil +} + +func resourceDBaaSPostgreSQLDatastoreV1PoolerOptsFromSet(poolerSet *schema.Set) (dbaas.DatastorePoolerOpts, error) { + if poolerSet.Len() == 0 { + return dbaas.DatastorePoolerOpts{}, nil + } + var resourceModeRaw, resourceSizeRaw interface{} + var ok bool + + resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) + if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { + return dbaas.DatastorePoolerOpts{}, errors.New("pooler.mode value isn't provided") + } + if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { + return dbaas.DatastorePoolerOpts{}, errors.New("pooler.size value isn't provided") + } + + resourceMode := resourceModeRaw.(string) + resourceSize := resourceSizeRaw.(int) + + pooler := dbaas.DatastorePoolerOpts{ + Mode: resourceMode, + Size: resourceSize, + } + + return pooler, nil +} + +func updatePostgreSQLDatastorePooler(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + poolerSet := d.Get("pooler").(*schema.Set) + poolerOpts, err := resourceDBaaSPostgreSQLDatastoreV1PoolerOptsFromSet(poolerSet) + if err != nil { + return errParseDatastoreV1Pooler(err) + } + + log.Print(msgUpdate(objectDatastore, d.Id(), poolerOpts)) + _, err = client.PoolerDatastore(ctx, d.Id(), poolerOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutUpdate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} diff --git a/selectel/dbaas_redis_utils.go b/selectel/dbaas_redis_utils.go new file mode 100644 index 00000000..3791ee45 --- /dev/null +++ b/selectel/dbaas_redis_utils.go @@ -0,0 +1,56 @@ +package selectel + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/selectel/dbaas-go" +) + +func updateRedisDatastorePassword(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + passwordOpts := dbaas.DatastorePasswordOpts{ + RedisPassword: d.Get("redis_password").(string), + } + + log.Print(msgUpdate(objectDatastore, d.Id(), passwordOpts)) + _, err := client.PasswordDatastore(ctx, d.Id(), passwordOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutUpdate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} + +func resizeRedisDatastore(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { + var resizeOpts dbaas.DatastoreResizeOpts + nodeCount := d.Get("node_count").(int) + resizeOpts.NodeCount = nodeCount + + flavorID := d.Get("flavor_id") + + resizeOpts.Flavor = nil + resizeOpts.FlavorID = flavorID.(string) + + log.Print(msgUpdate(objectDatastore, d.Id(), resizeOpts)) + _, err := client.ResizeDatastore(ctx, d.Id(), resizeOpts) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) + if err != nil { + return errUpdatingObject(objectDatastore, d.Id(), err) + } + + return nil +} diff --git a/selectel/import_selectel_dbaas_mysql_datastore_v1_test.go b/selectel/import_selectel_dbaas_mysql_datastore_v1_test.go new file mode 100644 index 00000000..9ed47306 --- /dev/null +++ b/selectel/import_selectel_dbaas_mysql_datastore_v1_test.go @@ -0,0 +1,32 @@ +package selectel + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDBaaSMySQLDatastoreV1ImportBasic(t *testing.T) { + resourceName := "selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1" + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSMySQLDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: testAccCheckSelectelImportEnv(resourceName), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/selectel/import_selectel_dbaas_postgresql_datastore_v1_test.go b/selectel/import_selectel_dbaas_postgresql_datastore_v1_test.go new file mode 100644 index 00000000..0b861019 --- /dev/null +++ b/selectel/import_selectel_dbaas_postgresql_datastore_v1_test.go @@ -0,0 +1,32 @@ +package selectel + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDBaaSPostgreSQLDatastoreV1ImportBasic(t *testing.T) { + resourceName := "selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1" + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSPostgreSQLDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: testAccCheckSelectelImportEnv(resourceName), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/selectel/import_selectel_dbaas_redis_datastore_v1_test.go b/selectel/import_selectel_dbaas_redis_datastore_v1_test.go new file mode 100644 index 00000000..8bae1310 --- /dev/null +++ b/selectel/import_selectel_dbaas_redis_datastore_v1_test.go @@ -0,0 +1,33 @@ +package selectel + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDBaaSRedisDatastoreV1ImportBasic(t *testing.T) { + resourceName := "selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1" + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSRedisDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: testAccCheckSelectelImportEnv(resourceName), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"redis_password"}, + }, + }, + }) +} diff --git a/selectel/provider.go b/selectel/provider.go index 29d34260..c36fa9dd 100644 --- a/selectel/provider.go +++ b/selectel/provider.go @@ -96,11 +96,17 @@ func Provider() *schema.Provider { "selectel_mks_nodegroup_v1": resourceMKSNodegroupV1(), "selectel_domains_domain_v1": resourceDomainsDomainV1(), "selectel_domains_record_v1": resourceDomainsRecordV1(), - "selectel_dbaas_datastore_v1": resourceDBaaSDatastoreV1(), + "selectel_dbaas_datastore_v1": resourceDBaaSDatastoreV1(), // DEPRECATED + "selectel_dbaas_postgresql_datastore_v1": resourceDBaaSPostgreSQLDatastoreV1(), + "selectel_dbaas_mysql_datastore_v1": resourceDBaaSMySQLDatastoreV1(), + "selectel_dbaas_redis_datastore_v1": resourceDBaaSRedisDatastoreV1(), "selectel_dbaas_user_v1": resourceDBaaSUserV1(), - "selectel_dbaas_database_v1": resourceDBaaSDatabaseV1(), + "selectel_dbaas_database_v1": resourceDBaaSDatabaseV1(), // DEPRECATED + "selectel_dbaas_postgresql_database_v1": resourceDBaaSPostgreSQLDatabaseV1(), + "selectel_dbaas_mysql_database_v1": resourceDBaaSMySQLDatabaseV1(), "selectel_dbaas_grant_v1": resourceDBaaSGrantV1(), - "selectel_dbaas_extension_v1": resourceDBaaSExtensionV1(), + "selectel_dbaas_extension_v1": resourceDBaaSExtensionV1(), // DEPRECATED + "selectel_dbaas_postgresql_extension_v1": resourceDBaaSPostgreSQLExtensionV1(), "selectel_dbaas_prometheus_metric_token_v1": resourceDBaaSPrometheusMetricTokenV1(), }, ConfigureContextFunc: configureProvider, diff --git a/selectel/resource_selectel_dbaas_database_v1.go b/selectel/resource_selectel_dbaas_database_v1.go index df2fc2bf..933b652f 100644 --- a/selectel/resource_selectel_dbaas_database_v1.go +++ b/selectel/resource_selectel_dbaas_database_v1.go @@ -242,68 +242,3 @@ func resourceDBaaSDatabaseV1ImportState(_ context.Context, d *schema.ResourceDat return []*schema.ResourceData{d}, nil } - -func waitForDBaaSDatabaseV1ActiveState( - ctx context.Context, client *dbaas.API, databaseID string, timeout time.Duration) error { - pending := []string{ - string(dbaas.StatusPendingCreate), - string(dbaas.StatusPendingUpdate), - } - target := []string{ - string(dbaas.StatusActive), - } - - stateConf := &resource.StateChangeConf{ - Pending: pending, - Target: target, - Refresh: dbaasDatabaseV1StateRefreshFunc(ctx, client, databaseID), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 3 * time.Second, - } - - _, err := stateConf.WaitForState() - if err != nil { - return fmt.Errorf( - "error waiting for the database %s to become 'ACTIVE': %s", - databaseID, err) - } - - return nil -} - -func dbaasDatabaseV1StateRefreshFunc(ctx context.Context, client *dbaas.API, databaseID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.Database(ctx, databaseID) - if err != nil { - return nil, "", err - } - - return d, string(d.Status), nil - } -} - -func dbaasDatabaseV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.Database(ctx, datastoreID) - if err != nil { - var dbaasError *dbaas.DBaaSAPIError - if errors.As(err, &dbaasError) { - return d, strconv.Itoa(dbaasError.StatusCode()), nil - } - - return nil, "", err - } - - return d, strconv.Itoa(200), err - } -} - -func dbaasDatabaseV1LocaleDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { - // The default locale value - C is the same as null value, so we need to suppress - if old == "C" && new == "" { - return true - } - - return false -} diff --git a/selectel/resource_selectel_dbaas_database_v1_test.go b/selectel/resource_selectel_dbaas_database_v1_test.go index 9213a332..8ec2ae1a 100644 --- a/selectel/resource_selectel_dbaas_database_v1_test.go +++ b/selectel/resource_selectel_dbaas_database_v1_test.go @@ -22,6 +22,7 @@ func TestAccDBaaSDatabaseV1Basic(t *testing.T) { projectName := acctest.RandomWithPrefix("tf-acc") datastoreName := acctest.RandomWithPrefix("tf-acc-ds") userName := RandomWithPrefix("tf_acc_user") + newUserName := RandomWithPrefix("tf_acc_new_user") userPassword := acctest.RandomWithPrefix("tf-acc-pass") databaseName := RandomWithPrefix("tf_acc_db") nodeCount := 1 @@ -42,6 +43,28 @@ func TestAccDBaaSDatabaseV1Basic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), ), }, + { + Config: testAccDBaaSDatabaseV1UpdateLocale(projectName, datastoreName, userName, userPassword, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "lc_collate", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "lc_ctype", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, + { + Config: testAccDBaaSDatabaseV1UpdateOwnerID(projectName, datastoreName, userName, userPassword, newUserName, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "lc_collate", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "lc_ctype", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, }, }) } @@ -126,3 +149,119 @@ resource "selectel_dbaas_database_v1" "database_tf_acc_test_1" { owner_id = "${selectel_dbaas_user_v1.user_tf_acc_test_1.id}" }`, projectName, datastoreName, nodeCount, userName, userPassword, databaseName) } + +func testAccDBaaSDatabaseV1UpdateLocale(projectName, datastoreName, userName, userPassword, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.user_tf_acc_test_1.id}" + lc_collate = "ru_RU.utf8" + lc_ctype = "ru_RU.utf8" +}`, projectName, datastoreName, nodeCount, userName, userPassword, databaseName) +} + +func testAccDBaaSDatabaseV1UpdateOwnerID(projectName, datastoreName, userName, userPassword, newUserName, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_user_v1" "new_user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" + } + +resource "selectel_dbaas_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.new_user_tf_acc_test_1.id}" + lc_collate = "ru_RU.utf8" + lc_ctype = "ru_RU.utf8" +}`, projectName, datastoreName, nodeCount, userName, userPassword, newUserName, userPassword, databaseName) +} diff --git a/selectel/resource_selectel_dbaas_datastore_v1.go b/selectel/resource_selectel_dbaas_datastore_v1.go index bae6d4d6..e6c5e65e 100644 --- a/selectel/resource_selectel_dbaas_datastore_v1.go +++ b/selectel/resource_selectel_dbaas_datastore_v1.go @@ -209,7 +209,7 @@ func resourceDBaaSDatastoreV1Create(ctx context.Context, d *schema.ResourceData, } poolerSet := d.Get("pooler").(*schema.Set) - pooler, err := resourceDBaaSDatastoreV1PoolerFromSet(poolerSet) + pooler, err := resourceDBaaSPostgreSQLDatastoreV1PoolerFromSet(poolerSet) if err != nil { return diag.FromErr(errParseDatastoreV1Pooler(err)) } @@ -327,7 +327,7 @@ func resourceDBaaSDatastoreV1Update(ctx context.Context, d *schema.ResourceData, } } if d.HasChange("pooler") { - err := updateDatastorePooler(ctx, d, dbaasClient) + err := updatePostgreSQLDatastorePooler(ctx, d, dbaasClient) if err != nil { return diag.FromErr(err) } @@ -351,7 +351,7 @@ func resourceDBaaSDatastoreV1Update(ctx context.Context, d *schema.ResourceData, } } if d.HasChange("redis_password") { - err := updateDatastorePassword(ctx, d, dbaasClient) + err := updateRedisDatastorePassword(ctx, d, dbaasClient) if err != nil { return diag.FromErr(err) } @@ -404,383 +404,3 @@ func resourceDBaaSDatastoreV1ImportState(_ context.Context, d *schema.ResourceDa return []*schema.ResourceData{d}, nil } - -func waitForDBaaSDatastoreV1ActiveState( - ctx context.Context, client *dbaas.API, datastoreID string, timeout time.Duration) error { - pending := []string{ - string(dbaas.StatusPendingCreate), - string(dbaas.StatusPendingUpdate), - string(dbaas.StatusResizing), - } - target := []string{ - string(dbaas.StatusActive), - } - - stateConf := &resource.StateChangeConf{ - Pending: pending, - Target: target, - Refresh: dbaasDatastoreV1StateRefreshFunc(ctx, client, datastoreID), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 3 * time.Second, - } - - _, err := stateConf.WaitForState() - if err != nil { - return fmt.Errorf( - "error waiting for the datastore %s to become 'ACTIVE': %s", - datastoreID, err) - } - - return nil -} - -func dbaasDatastoreV1StateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.Datastore(ctx, datastoreID) - if err != nil { - return nil, "", err - } - - return d, string(d.Status), nil - } -} - -func dbaasDatastoreV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, datastoreID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.Datastore(ctx, datastoreID) - if err != nil { - var dbaasError *dbaas.DBaaSAPIError - if errors.As(err, &dbaasError) { - return d, strconv.Itoa(dbaasError.StatusCode()), nil - } - - return nil, "", err - } - - return d, strconv.Itoa(200), err - } -} - -func resourceDBaaSDatastoreV1FlavorFromSet(flavorSet *schema.Set) (*dbaas.Flavor, error) { - if flavorSet.Len() == 0 { - return nil, nil - } - var resourceVcpusRaw, resourceRAMRaw, resourceDiskRaw interface{} - var ok bool - - resourceFlavorMap := flavorSet.List()[0].(map[string]interface{}) - if resourceVcpusRaw, ok = resourceFlavorMap["vcpus"]; !ok { - return &dbaas.Flavor{}, errors.New("flavor.vcpus value isn't provided") - } - if resourceRAMRaw, ok = resourceFlavorMap["ram"]; !ok { - return &dbaas.Flavor{}, errors.New("flavor.ram value isn't provided") - } - if resourceDiskRaw, ok = resourceFlavorMap["disk"]; !ok { - return &dbaas.Flavor{}, errors.New("flavor.disk value isn't provided") - } - - resourceVcpus := resourceVcpusRaw.(int) - resourceRAM := resourceRAMRaw.(int) - resourceDisk := resourceDiskRaw.(int) - - flavor := &dbaas.Flavor{ - Vcpus: resourceVcpus, - RAM: resourceRAM, - Disk: resourceDisk, - } - - return flavor, nil -} - -func resourceDBaaSDatastoreV1FlavorToSet(flavor dbaas.Flavor) *schema.Set { - flavorSet := &schema.Set{ - F: flavorHashSetFunc(), - } - - flavorSet.Add(map[string]interface{}{ - "vcpus": flavor.Vcpus, - "ram": flavor.RAM, - "disk": flavor.Disk, - }) - - return flavorSet -} - -func flavorSchema() *schema.Resource { - return resourceDBaaSDatastoreV1().Schema["flavor"].Elem.(*schema.Resource) -} - -func flavorHashSetFunc() schema.SchemaSetFunc { - return schema.HashResource(flavorSchema()) -} - -func resourceDBaaSDatastoreV1PoolerFromSet(poolerSet *schema.Set) (*dbaas.Pooler, error) { - if poolerSet.Len() == 0 { - return nil, nil - } - var resourceModeRaw, resourceSizeRaw interface{} - var ok bool - - resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) - if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { - return &dbaas.Pooler{}, errors.New("pooler.mode value isn't provided") - } - if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { - return &dbaas.Pooler{}, errors.New("pooler.size value isn't provided") - } - - resourceMode := resourceModeRaw.(string) - resourceSize := resourceSizeRaw.(int) - - pooler := &dbaas.Pooler{ - Mode: resourceMode, - Size: resourceSize, - } - - return pooler, nil -} - -func resourceDBaaSDatastoreV1PoolerOptsFromSet(poolerSet *schema.Set) (dbaas.DatastorePoolerOpts, error) { - if poolerSet.Len() == 0 { - return dbaas.DatastorePoolerOpts{}, nil - } - var resourceModeRaw, resourceSizeRaw interface{} - var ok bool - - resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) - if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { - return dbaas.DatastorePoolerOpts{}, errors.New("pooler.mode value isn't provided") - } - if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { - return dbaas.DatastorePoolerOpts{}, errors.New("pooler.size value isn't provided") - } - - resourceMode := resourceModeRaw.(string) - resourceSize := resourceSizeRaw.(int) - - pooler := dbaas.DatastorePoolerOpts{ - Mode: resourceMode, - Size: resourceSize, - } - - return pooler, nil -} - -func resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet *schema.Set) (dbaas.DatastoreFirewallOpts, error) { - if firewallSet.Len() == 0 { - return dbaas.DatastoreFirewallOpts{IPs: []string{}}, nil - } - - var resourceIPsRaw interface{} - var ok bool - - resourceFirewallRaw := firewallSet.List()[0].(map[string]interface{}) - if resourceIPsRaw, ok = resourceFirewallRaw["ips"]; !ok { - return dbaas.DatastoreFirewallOpts{}, errors.New("firewall.ips value isn't provided") - } - resourceIPRaw := resourceIPsRaw.([]interface{}) - var firewall dbaas.DatastoreFirewallOpts - for _, ip := range resourceIPRaw { - firewall.IPs = append(firewall.IPs, ip.(string)) - } - - return firewall, nil -} - -func resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet *schema.Set) (*dbaas.Restore, error) { - if restoreSet.Len() == 0 { - return nil, nil - } - var resourceDatastoreIDRaw, resourceTargetTimeRaw interface{} - var ok bool - - resourceRestoreMap := restoreSet.List()[0].(map[string]interface{}) - if resourceDatastoreIDRaw, ok = resourceRestoreMap["datastore_id"]; !ok { - return &dbaas.Restore{}, errors.New("restore.datastore_id value isn't provided") - } - if resourceTargetTimeRaw, ok = resourceRestoreMap["target_time"]; !ok { - return &dbaas.Restore{}, errors.New("restore.target_time value isn't provided") - } - - resourceDatastoreID := resourceDatastoreIDRaw.(string) - resourceTargetTime := resourceTargetTimeRaw.(string) - - restore := &dbaas.Restore{ - DatastoreID: resourceDatastoreID, - TargetTime: resourceTargetTime, - } - - return restore, nil -} - -func convertFieldFromStringToType(fieldValue string) interface{} { - if val, err := strconv.Atoi(fieldValue); err == nil { - return val - } else if val, err := strconv.ParseFloat(fieldValue, 64); err == nil { - return val - } else if val, err := strconv.ParseFloat(fieldValue, 32); err == nil { - return val - } else if val, err := strconv.ParseBool(fieldValue); err == nil { - return val - } else { - return fieldValue - } -} - -func updateDatastoreName(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - var updateOpts dbaas.DatastoreUpdateOpts - updateOpts.Name = d.Get("name").(string) - - log.Print(msgUpdate(objectDatastore, d.Id(), updateOpts)) - _, err := client.UpdateDatastore(ctx, d.Id(), updateOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutUpdate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} - -func updateDatastorePooler(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - poolerSet := d.Get("pooler").(*schema.Set) - poolerOpts, err := resourceDBaaSDatastoreV1PoolerOptsFromSet(poolerSet) - if err != nil { - return errParseDatastoreV1Pooler(err) - } - - log.Print(msgUpdate(objectDatastore, d.Id(), poolerOpts)) - _, err = client.PoolerDatastore(ctx, d.Id(), poolerOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutUpdate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} - -func updateDatastoreFirewall(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - firewallSet := d.Get("firewall").(*schema.Set) - firewallOpts, err := resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet) - if err != nil { - return errParseDatastoreV1Firewall(err) - } - - log.Print(msgUpdate(objectDatastore, d.Id(), firewallOpts)) - _, err = client.FirewallDatastore(ctx, d.Id(), firewallOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutUpdate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} - -func updateDatastoreConfig(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - var configOpts dbaas.DatastoreConfigOpts - datastore, err := client.Datastore(ctx, d.Id()) - if err != nil { - return err - } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } - - for param := range datastore.Config { - if _, ok := config[param]; !ok { - config[param] = nil - } - } - - configOpts.Config = config - - log.Print(msgUpdate(objectDatastore, d.Id(), configOpts)) - _, err = client.ConfigDatastore(ctx, d.Id(), configOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutUpdate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} - -func updateDatastorePassword(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - passwordOpts := dbaas.DatastorePasswordOpts{ - RedisPassword: d.Get("redis_password").(string), - } - - log.Print(msgUpdate(objectDatastore, d.Id(), passwordOpts)) - _, err := client.PasswordDatastore(ctx, d.Id(), passwordOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutUpdate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} - -func resizeDatastore(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { - var resizeOpts dbaas.DatastoreResizeOpts - nodeCount := d.Get("node_count").(int) - resizeOpts.NodeCount = nodeCount - - flavorID := d.Get("flavor_id") - flavorRaw := d.Get("flavor") - - flavorSet := flavorRaw.(*schema.Set) - flavor, err := resourceDBaaSDatastoreV1FlavorFromSet(flavorSet) - if err != nil { - return errParseDatastoreV1Resize(err) - } - - resizeOpts.Flavor = flavor - resizeOpts.FlavorID = flavorID.(string) - - log.Print(msgUpdate(objectDatastore, d.Id(), resizeOpts)) - _, err = client.ResizeDatastore(ctx, d.Id(), resizeOpts) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", d.Id()) - timeout := d.Timeout(schema.TimeoutCreate) - err = waitForDBaaSDatastoreV1ActiveState(ctx, client, d.Id(), timeout) - if err != nil { - return errUpdatingObject(objectDatastore, d.Id(), err) - } - - return nil -} diff --git a/selectel/resource_selectel_dbaas_datastore_v1_test.go b/selectel/resource_selectel_dbaas_datastore_v1_test.go index 785cbd56..f225adb7 100644 --- a/selectel/resource_selectel_dbaas_datastore_v1_test.go +++ b/selectel/resource_selectel_dbaas_datastore_v1_test.go @@ -180,6 +180,7 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { projectName := acctest.RandomWithPrefix("tf-acc") datastoreName := acctest.RandomWithPrefix("tf-acc-ds") nodeCount := 1 + resizeNodeCount := 2 resource.Test(t, resource.TestCase{ PreCheck: func() { testAccSelectelPreCheck(t) }, @@ -224,6 +225,44 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), ), }, + { + Config: testAccDBaaSDatastoreV1UpdateRedisPassword(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), + resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSDatastoreV1RedisResize(projectName, datastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), + resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), + resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, }, }) } @@ -672,3 +711,97 @@ resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" { redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6" }`, projectName, datastoreName, nodeCount) } + +func testAccDBaaSDatastoreV1UpdateRedisPassword(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } +} + +resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" + config = { + maxmemory-policy = "noeviction" + } + firewall { + ips = [ "127.0.0.1", "127.0.0.2" ] + } + redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6123" +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSDatastoreV1RedisResize(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } + } + + resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[1].id}" + config = { + maxmemory-policy = "noeviction" + } + firewall { + ips = [ "127.0.0.1", "127.0.0.2" ] + } + redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6123" + }`, projectName, datastoreName, nodeCount) +} diff --git a/selectel/resource_selectel_dbaas_mysql_database_v1.go b/selectel/resource_selectel_dbaas_mysql_database_v1.go new file mode 100644 index 00000000..87470878 --- /dev/null +++ b/selectel/resource_selectel_dbaas_mysql_database_v1.go @@ -0,0 +1,176 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSMySQLDatabaseV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSMySQLDatabaseV1Create, + ReadContext: resourceDBaaSMySQLDatabaseV1Read, + DeleteContext: resourceDBaaSMySQLDatabaseV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSMySQLDatabaseV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "datastore_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDBaaSMySQLDatabaseV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + databaseCreateOpts := dbaas.DatabaseCreateOpts{ + DatastoreID: datastoreID, + Name: d.Get("name").(string), + } + + log.Print(msgCreate(objectDatabase, databaseCreateOpts)) + database, err := dbaasClient.CreateDatabase(ctx, databaseCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatabase, err)) + } + + log.Printf("[DEBUG] waiting for database %s to become 'ACTIVE'", database.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatabaseV1ActiveState(ctx, dbaasClient, database.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatabase, err)) + } + + d.SetId(database.ID) + + return resourceDBaaSMySQLDatabaseV1Read(ctx, d, meta) +} + +func resourceDBaaSMySQLDatabaseV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectDatabase, d.Id())) + database, err := dbaasClient.Database(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectDatabase, d.Id(), err)) + } + d.Set("datastore_id", database.DatastoreID) + d.Set("name", database.Name) + d.Set("status", database.Status) + + return nil +} + +func resourceDBaaSMySQLDatabaseV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + ownerIDRaw, ownerIDOk := d.GetOk("owner_id") + if ownerIDOk { + ownerID := ownerIDRaw.(string) + selMutexKV.Lock(ownerID) + defer selMutexKV.Unlock(ownerID) + } + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectDatabase, d.Id())) + err := dbaasClient.DeleteDatabase(ctx, d.Id()) + if err != nil { + return diag.FromErr(errDeletingObject(objectDatabase, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasDatabaseV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for database %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the database %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSMySQLDatabaseV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_mysql_database_v1_test.go b/selectel/resource_selectel_dbaas_mysql_database_v1_test.go new file mode 100644 index 00000000..996de93a --- /dev/null +++ b/selectel/resource_selectel_dbaas_mysql_database_v1_test.go @@ -0,0 +1,83 @@ +package selectel + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSMySQLDatabaseV1Basic(t *testing.T) { + var ( + dbaasDatabase dbaas.Database + project projects.Project + ) + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + databaseName := RandomWithPrefix("tf_acc_db") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSMySQLDatabaseV1Basic(projectName, datastoreName, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_mysql_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, + }, + }) +} + +func testAccDBaaSMySQLDatabaseV1Basic(projectName, datastoreName, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_mysql_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" +}`, projectName, datastoreName, nodeCount, databaseName) +} diff --git a/selectel/resource_selectel_dbaas_mysql_datastore_v1.go b/selectel/resource_selectel_dbaas_mysql_datastore_v1.go new file mode 100644 index 00000000..2964db7f --- /dev/null +++ b/selectel/resource_selectel_dbaas_mysql_datastore_v1.go @@ -0,0 +1,359 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSMySQLDatastoreV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSMySQLDatastoreV1Create, + ReadContext: resourceDBaaSMySQLDatastoreV1Read, + UpdateContext: resourceDBaaSMySQLDatastoreV1Update, + DeleteContext: resourceDBaaSMySQLDatastoreV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSMySQLDatastoreV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "type_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "flavor_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: false, + ConflictsWith: []string{"flavor"}, + }, + "node_count": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "connections": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "flavor": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: false, + ConflictsWith: []string{"flavor_id"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "vcpus": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "ram": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "disk": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + }, + }, + }, + "firewall": { + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ips": { + Type: schema.TypeList, + Required: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "restore": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "datastore_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "target_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + }, + }, + "config": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceDBaaSMySQLDatastoreV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + flavorID, flavorIDOk := d.GetOk("flavor_id") + flavorRaw, flavorOk := d.GetOk("flavor") + + if flavorIDOk == flavorOk { + return diag.FromErr(errors.New("either 'flavor' or 'flavor_id' must be provided")) + } + + typeID := d.Get("type_id").(string) + diagErr = validateDatastoreType(ctx, "mysql", typeID, dbaasClient) + if diagErr != nil { + return diagErr + } + + restoreSet := d.Get("restore").(*schema.Set) + restore, err := resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Restore(err)) + } + + configMap := d.Get("config").(map[string]interface{}) + config := make(map[string]interface{}) + for paramName, paramValue := range configMap { + paramValueStr := paramValue.(string) + config[paramName] = convertFieldFromStringToType(paramValueStr) + } + + datastoreCreateOpts := dbaas.DatastoreCreateOpts{ + Name: d.Get("name").(string), + TypeID: typeID, + SubnetID: d.Get("subnet_id").(string), + NodeCount: d.Get("node_count").(int), + Restore: restore, + Config: config, + } + + if flavorOk { + flavorSet := flavorRaw.(*schema.Set) + flavor, err := resourceDBaaSDatastoreV1FlavorFromSet(flavorSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Flavor(err)) + } + + datastoreCreateOpts.Flavor = flavor + } + + if flavorIDOk { + datastoreCreateOpts.FlavorID = flavorID.(string) + } + + log.Print(msgCreate(objectDatastore, datastoreCreateOpts)) + datastore, err := dbaasClient.CreateDatastore(ctx, datastoreCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", datastore.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, dbaasClient, datastore.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + d.SetId(datastore.ID) + + return resourceDBaaSMySQLDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSMySQLDatastoreV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectDatastore, d.Id())) + datastore, err := dbaasClient.Datastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectDatastore, d.Id(), err)) + } + d.Set("name", datastore.Name) + d.Set("status", datastore.Status) + d.Set("project_id", datastore.ProjectID) + d.Set("subnet_id", datastore.SubnetID) + d.Set("type_id", datastore.TypeID) + d.Set("node_count", datastore.NodeCount) + d.Set("enabled", datastore.Enabled) + d.Set("flavor_id", datastore.FlavorID) + + flavor := resourceDBaaSDatastoreV1FlavorToSet(datastore.Flavor) + if err := d.Set("flavor", flavor); err != nil { + log.Print(errSettingComplexAttr("flavor", err)) + } + + if err := d.Set("connections", datastore.Connection); err != nil { + log.Print(errSettingComplexAttr("connections", err)) + } + + configMap := make(map[string]string) + for key, value := range datastore.Config { + configMap[key] = convertFieldToStringByType(value) + } + if err := d.Set("config", configMap); err != nil { + log.Print(errSettingComplexAttr("config", err)) + } + + return nil +} + +func resourceDBaaSMySQLDatastoreV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + if d.HasChange("name") { + err := updateDatastoreName(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("firewall") { + err := updateDatastoreFirewall(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("node_count") || d.HasChange("flavor") || d.HasChange("flavor_id") { + err := resizeDatastore(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("config") { + err := updateDatastoreConfig(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + + return resourceDBaaSMySQLDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSMySQLDatastoreV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectDatastore, d.Id())) + err := dbaasClient.DeleteDatastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errDeletingObject(objectDatastore, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasDatastoreV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for datastore %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the datastore %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSMySQLDatastoreV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go b/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go new file mode 100644 index 00000000..58186487 --- /dev/null +++ b/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go @@ -0,0 +1,351 @@ +package selectel + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSMySQLDatastoreV1Basic(t *testing.T) { + var ( + dbaasDatastore dbaas.Datastore + project projects.Project + ) + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + resizeNodeCount := 3 + + updatedDatastoreName := acctest.RandomWithPrefix("tf-acc-ds-updated") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSMySQLDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.innodb_checksum_algorithm", "strict_innodb"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.auto_increment_offset", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.autocommit", "false"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSMySQLDatastoreV1UpdateName(projectName, updatedDatastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.innodb_checksum_algorithm", "strict_innodb"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.auto_increment_offset", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.autocommit", "false"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSMySQLDatastoreV1UpdateFirewall(projectName, updatedDatastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.innodb_checksum_algorithm", "strict_innodb"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.auto_increment_offset", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.autocommit", "false"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSMySQLDatastoreV1Resize(projectName, updatedDatastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(8192)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.innodb_checksum_algorithm", "strict_innodb"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.auto_increment_offset", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.autocommit", "false"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSMySQLDatastoreV1UpdateConfig(projectName, updatedDatastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(8192)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.innodb_checksum_algorithm", "strict_innodb"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.auto_increment_offset", strconv.Itoa(4)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "config.autocommit", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + }, + }) +} + +func testAccDBaaSMySQLDatastoreV1Basic(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 2 + autocommit = false + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSMySQLDatastoreV1UpdateName(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 2 + autocommit = false + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSMySQLDatastoreV1UpdateFirewall(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 2 + autocommit = false + } + firewall { + ips = [ "127.0.0.1", "127.0.0.2" ] + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSMySQLDatastoreV1Resize(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 8192 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 2 + autocommit = false + } + firewall { + ips = [ "127.0.0.1", "127.0.0.2" ] + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSMySQLDatastoreV1UpdateConfig(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 8192 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 4 + autocommit = true + } + firewall { + ips = [ "127.0.0.1", "127.0.0.2" ] + } +}`, projectName, datastoreName, nodeCount) +} diff --git a/selectel/resource_selectel_dbaas_postgresql_database_v1.go b/selectel/resource_selectel_dbaas_postgresql_database_v1.go new file mode 100644 index 00000000..92691fde --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_database_v1.go @@ -0,0 +1,244 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSPostgreSQLDatabaseV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSPostgreSQLDatabaseV1Create, + ReadContext: resourceDBaaSPostgreSQLDatabaseV1Read, + UpdateContext: resourceDBaaSPostgreSQLDatabaseV1Update, + DeleteContext: resourceDBaaSPostgreSQLDatabaseV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSPostgreSQLDatabaseV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "datastore_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "owner_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "lc_collate": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: dbaasDatabaseV1LocaleDiffSuppressFunc, + }, + "lc_ctype": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: dbaasDatabaseV1LocaleDiffSuppressFunc, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDBaaSPostgreSQLDatabaseV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + databaseCreateOpts := dbaas.DatabaseCreateOpts{ + DatastoreID: datastoreID, + Name: d.Get("name").(string), + OwnerID: d.Get("owner_id").(string), + LcCollate: d.Get("lc_collate").(string), + LcCtype: d.Get("lc_ctype").(string), + } + + log.Print(msgCreate(objectDatabase, databaseCreateOpts)) + database, err := dbaasClient.CreateDatabase(ctx, databaseCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatabase, err)) + } + + log.Printf("[DEBUG] waiting for database %s to become 'ACTIVE'", database.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatabaseV1ActiveState(ctx, dbaasClient, database.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatabase, err)) + } + + d.SetId(database.ID) + + return resourceDBaaSPostgreSQLDatabaseV1Read(ctx, d, meta) +} + +func resourceDBaaSPostgreSQLDatabaseV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectDatabase, d.Id())) + database, err := dbaasClient.Database(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectDatabase, d.Id(), err)) + } + d.Set("datastore_id", database.DatastoreID) + d.Set("name", database.Name) + d.Set("status", database.Status) + if database.OwnerID != "" { + d.Set("owner_id", database.OwnerID) + } + if database.LcCollate != "" { + d.Set("lc_collate", database.LcCollate) + } + if database.LcCtype != "" { + d.Set("lc_ctype", database.LcCtype) + } + + return nil +} + +func resourceDBaaSPostgreSQLDatabaseV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + if d.HasChange("owner_id") { + ownerID := d.Get("owner_id").(string) + + selMutexKV.Lock(ownerID) + defer selMutexKV.Unlock(ownerID) + + updateOpts := dbaas.DatabaseUpdateOpts{ + OwnerID: ownerID, + } + + log.Print(msgUpdate(objectDatastore, d.Id(), updateOpts)) + _, err := dbaasClient.UpdateDatabase(ctx, d.Id(), updateOpts) + if err != nil { + return diag.FromErr(errUpdatingObject(objectDatabase, d.Id(), err)) + } + + log.Printf("[DEBUG] waiting for database %s to become 'ACTIVE'", d.Id()) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatabaseV1ActiveState(ctx, dbaasClient, d.Id(), timeout) + if err != nil { + return diag.FromErr(errUpdatingObject(objectDatabase, d.Id(), err)) + } + } + + return resourceDBaaSPostgreSQLDatabaseV1Read(ctx, d, meta) +} + +func resourceDBaaSPostgreSQLDatabaseV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + ownerIDRaw, ownerIDOk := d.GetOk("owner_id") + if ownerIDOk { + ownerID := ownerIDRaw.(string) + selMutexKV.Lock(ownerID) + defer selMutexKV.Unlock(ownerID) + } + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectDatabase, d.Id())) + err := dbaasClient.DeleteDatabase(ctx, d.Id()) + if err != nil { + return diag.FromErr(errDeletingObject(objectDatabase, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasDatabaseV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for database %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the database %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSPostgreSQLDatabaseV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_postgresql_database_v1_test.go b/selectel/resource_selectel_dbaas_postgresql_database_v1_test.go new file mode 100644 index 00000000..42fb3e8e --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_database_v1_test.go @@ -0,0 +1,235 @@ +package selectel + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSPostgreSQLDatabaseV1Basic(t *testing.T) { + var ( + dbaasDatabase dbaas.Database + project projects.Project + ) + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + userName := RandomWithPrefix("tf_acc_user") + newUserName := RandomWithPrefix("tf_acc_new_user") + userPassword := acctest.RandomWithPrefix("tf-acc-pass") + databaseName := RandomWithPrefix("tf_acc_db") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSPostgreSQLDatabaseV1Basic(projectName, datastoreName, userName, userPassword, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_collate", "C"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_ctype", "C"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatabaseV1UpdateLocale(projectName, datastoreName, userName, userPassword, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_collate", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_ctype", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatabaseV1UpdateOwnerID(projectName, datastoreName, userName, userPassword, newUserName, databaseName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatabaseV1Exists("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", &dbaasDatabase), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "name", databaseName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_collate", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "lc_ctype", "ru_RU.utf8"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1", "status", string(dbaas.StatusActive)), + ), + }, + }, + }) +} + +func testAccDBaaSPostgreSQLDatabaseV1Basic(projectName, datastoreName, userName, userPassword, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_postgresql_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.user_tf_acc_test_1.id}" +}`, projectName, datastoreName, nodeCount, userName, userPassword, databaseName) +} + +func testAccDBaaSPostgreSQLDatabaseV1UpdateLocale(projectName, datastoreName, userName, userPassword, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_postgresql_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.user_tf_acc_test_1.id}" + lc_collate = "ru_RU.utf8" + lc_ctype = "ru_RU.utf8" +}`, projectName, datastoreName, nodeCount, userName, userPassword, databaseName) +} + +func testAccDBaaSPostgreSQLDatabaseV1UpdateOwnerID(projectName, datastoreName, userName, userPassword, newUserName, databaseName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_user_v1" "new_user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" + } + +resource "selectel_dbaas_postgresql_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.new_user_tf_acc_test_1.id}" + lc_collate = "ru_RU.utf8" + lc_ctype = "ru_RU.utf8" +}`, projectName, datastoreName, nodeCount, userName, userPassword, newUserName, userPassword, databaseName) +} diff --git a/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go b/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go new file mode 100644 index 00000000..7d874699 --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go @@ -0,0 +1,396 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSPostgreSQLDatastoreV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSPostgreSQLDatastoreV1Create, + ReadContext: resourceDBaaSPostgreSQLDatastoreV1Read, + UpdateContext: resourceDBaaSPostgreSQLDatastoreV1Update, + DeleteContext: resourceDBaaSPostgreSQLDatastoreV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSPostgreSQLDatastoreV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "type_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "flavor_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: false, + ConflictsWith: []string{"flavor"}, + }, + "node_count": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "connections": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "flavor": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: false, + ConflictsWith: []string{"flavor_id"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "vcpus": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "ram": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "disk": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + }, + }, + }, + "pooler": { + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + ValidateFunc: validation.StringInSlice([]string{ + "session", + "transaction", + "statement", + }, false), + }, + "size": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + }, + }, + }, + "firewall": { + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ips": { + Type: schema.TypeList, + Required: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "restore": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "datastore_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "target_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + }, + }, + "config": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceDBaaSPostgreSQLDatastoreV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + flavorID, flavorIDOk := d.GetOk("flavor_id") + flavorRaw, flavorOk := d.GetOk("flavor") + + if flavorIDOk == flavorOk { + return diag.FromErr(errors.New("either 'flavor' or 'flavor_id' must be provided")) + } + + typeID := d.Get("type_id").(string) + diagErr = validateDatastoreType(ctx, "postgresql", typeID, dbaasClient) + if diagErr != nil { + return diagErr + } + + poolerSet := d.Get("pooler").(*schema.Set) + pooler, err := resourceDBaaSPostgreSQLDatastoreV1PoolerFromSet(poolerSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Pooler(err)) + } + + restoreSet := d.Get("restore").(*schema.Set) + restore, err := resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Restore(err)) + } + + configMap := d.Get("config").(map[string]interface{}) + config := make(map[string]interface{}) + for paramName, paramValue := range configMap { + paramValueStr := paramValue.(string) + config[paramName] = convertFieldFromStringToType(paramValueStr) + } + + datastoreCreateOpts := dbaas.DatastoreCreateOpts{ + Name: d.Get("name").(string), + TypeID: typeID, + SubnetID: d.Get("subnet_id").(string), + NodeCount: d.Get("node_count").(int), + Pooler: pooler, + Restore: restore, + Config: config, + } + + if flavorOk { + flavorSet := flavorRaw.(*schema.Set) + flavor, err := resourceDBaaSDatastoreV1FlavorFromSet(flavorSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Flavor(err)) + } + + datastoreCreateOpts.Flavor = flavor + } + + if flavorIDOk { + datastoreCreateOpts.FlavorID = flavorID.(string) + } + + log.Print(msgCreate(objectDatastore, datastoreCreateOpts)) + datastore, err := dbaasClient.CreateDatastore(ctx, datastoreCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", datastore.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, dbaasClient, datastore.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + d.SetId(datastore.ID) + + return resourceDBaaSPostgreSQLDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSPostgreSQLDatastoreV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectDatastore, d.Id())) + datastore, err := dbaasClient.Datastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectDatastore, d.Id(), err)) + } + d.Set("name", datastore.Name) + d.Set("status", datastore.Status) + d.Set("project_id", datastore.ProjectID) + d.Set("subnet_id", datastore.SubnetID) + d.Set("type_id", datastore.TypeID) + d.Set("node_count", datastore.NodeCount) + d.Set("enabled", datastore.Enabled) + d.Set("flavor_id", datastore.FlavorID) + + flavor := resourceDBaaSDatastoreV1FlavorToSet(datastore.Flavor) + if err := d.Set("flavor", flavor); err != nil { + log.Print(errSettingComplexAttr("flavor", err)) + } + + if err := d.Set("connections", datastore.Connection); err != nil { + log.Print(errSettingComplexAttr("connections", err)) + } + + configMap := make(map[string]string) + for key, value := range datastore.Config { + configMap[key] = convertFieldToStringByType(value) + } + if err := d.Set("config", configMap); err != nil { + log.Print(errSettingComplexAttr("config", err)) + } + + return nil +} + +func resourceDBaaSPostgreSQLDatastoreV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + if d.HasChange("name") { + err := updateDatastoreName(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("pooler") { + err := updatePostgreSQLDatastorePooler(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("firewall") { + err := updateDatastoreFirewall(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("node_count") || d.HasChange("flavor") || d.HasChange("flavor_id") { + err := resizeDatastore(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("config") { + err := updateDatastoreConfig(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + + return resourceDBaaSPostgreSQLDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSPostgreSQLDatastoreV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectDatastore, d.Id())) + err := dbaasClient.DeleteDatastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errDeletingObject(objectDatastore, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasDatastoreV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for datastore %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the datastore %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSPostgreSQLDatastoreV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_postgresql_datastore_v1_test.go b/selectel/resource_selectel_dbaas_postgresql_datastore_v1_test.go new file mode 100644 index 00000000..b7f520a4 --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_datastore_v1_test.go @@ -0,0 +1,434 @@ +package selectel + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSPostgreSQLDatastoreV1Basic(t *testing.T) { + var ( + dbaasDatastore dbaas.Datastore + project projects.Project + ) + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + resizeNodeCount := 2 + + updatedDatastoreName := acctest.RandomWithPrefix("tf-acc-ds-updated") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSPostgreSQLDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(128)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(25)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.transform_null_equals", "true"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatastoreV1UpdateName(projectName, updatedDatastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(128)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(25)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.transform_null_equals", "true"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatastoreV1UpdatePooler(projectName, updatedDatastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(128)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(25)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.transform_null_equals", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.mode", "session"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.size", strconv.Itoa(50)), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatastoreV1UpdateFirewall(projectName, updatedDatastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(4096)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.mode", "session"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.size", strconv.Itoa(50)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(128)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(25)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.transform_null_equals", "true"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatastoreV1Resize(projectName, updatedDatastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(8192)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.mode", "session"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.size", strconv.Itoa(50)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(128)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(25)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.transform_null_equals", "true"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSPostgreSQLDatastoreV1UpdateConfig(projectName, updatedDatastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(8192)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(32)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.mode", "session"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "pooler.0.size", strconv.Itoa(50)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.xmloption", "content"), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.work_mem", strconv.Itoa(256)), + resource.TestCheckResourceAttr("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "config.vacuum_cost_delay", strconv.Itoa(20)), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + }, + }) +} + +func testAccDBaaSPostgreSQLDatastoreV1Basic(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 128 + vacuum_cost_delay = 25 + transform_null_equals = true + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSPostgreSQLDatastoreV1UpdateName(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 128 + vacuum_cost_delay = 25 + transform_null_equals = true + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSPostgreSQLDatastoreV1UpdatePooler(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 128 + vacuum_cost_delay = 25 + transform_null_equals = true + } + pooler { + mode = "session" + size = 50 + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSPostgreSQLDatastoreV1UpdateFirewall(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 128 + vacuum_cost_delay = 25 + transform_null_equals = true + } + pooler { + mode = "session" + size = 50 + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSPostgreSQLDatastoreV1Resize(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 8192 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 128 + vacuum_cost_delay = 25 + transform_null_equals = true + } + pooler { + mode = "session" + size = 50 + } +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSPostgreSQLDatastoreV1UpdateConfig(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "13" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 8192 + disk = 32 + } + config = { + xmloption = "content" + work_mem = 256 + vacuum_cost_delay = 20 + } + pooler { + mode = "session" + size = 50 + } +}`, projectName, datastoreName, nodeCount) +} diff --git a/selectel/resource_selectel_dbaas_postgresql_extension_v1.go b/selectel/resource_selectel_dbaas_postgresql_extension_v1.go new file mode 100644 index 00000000..e78a0b37 --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_extension_v1.go @@ -0,0 +1,186 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSPostgreSQLExtensionV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSPostgreSQLExtensionV1Create, + ReadContext: resourceDBaaSPostgreSQLExtensionV1Read, + DeleteContext: resourceDBaaSPostgreSQLExtensionV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSPostgreSQLExtensionV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "available_extension_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "datastore_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "database_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDBaaSPostgreSQLExtensionV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + databaseID := d.Get("database_id").(string) + + selMutexKV.Lock(databaseID) + defer selMutexKV.Unlock(databaseID) + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + extensionCreateOpts := dbaas.ExtensionCreateOpts{ + AvailableExtensionID: d.Get("available_extension_id").(string), + DatastoreID: datastoreID, + DatabaseID: databaseID, + } + + log.Print(msgCreate(objectExtension, extensionCreateOpts)) + extension, err := dbaasClient.CreateExtension(ctx, extensionCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectExtension, err)) + } + + log.Printf("[DEBUG] waiting for extension %s to become 'ACTIVE'", extension.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSExtensionV1ActiveState(ctx, dbaasClient, extension.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectExtension, err)) + } + + d.SetId(extension.ID) + + return resourceDBaaSPostgreSQLExtensionV1Read(ctx, d, meta) +} + +func resourceDBaaSPostgreSQLExtensionV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectExtension, d.Id())) + extension, err := dbaasClient.Extension(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectExtension, d.Id(), err)) + } + + d.Set("available_extension_id", extension.AvailableExtensionID) + d.Set("datastore_id", extension.DatastoreID) + d.Set("database_id", extension.DatabaseID) + + return nil +} + +func resourceDBaaSPostgreSQLExtensionV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + datastoreID := d.Get("datastore_id").(string) + + selMutexKV.Lock(datastoreID) + defer selMutexKV.Unlock(datastoreID) + + databaseID := d.Get("database_id").(string) + + selMutexKV.Lock(databaseID) + defer selMutexKV.Unlock(databaseID) + + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectExtension, d.Id())) + err := dbaasClient.DeleteExtension(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectExtension, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasExtensionV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for extension %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the extension %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSPostgreSQLExtensionV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_postgresql_extension_v1_test.go b/selectel/resource_selectel_dbaas_postgresql_extension_v1_test.go new file mode 100644 index 00000000..9bdb7aa2 --- /dev/null +++ b/selectel/resource_selectel_dbaas_postgresql_extension_v1_test.go @@ -0,0 +1,113 @@ +package selectel + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSPostgreSQLExtensionV1Basic(t *testing.T) { + var ( + dbaasExtension dbaas.Extension + project projects.Project + ) + + const extensionName = "hstore" + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + userName := RandomWithPrefix("tf_acc_user") + userPassword := acctest.RandomWithPrefix("tf-acc-pass") + databaseName := RandomWithPrefix("tf_acc_db") + nodeCount := 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSPostgreSQLExtensionV1Basic(projectName, datastoreName, userName, userPassword, databaseName, extensionName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSExtensionV1Exists("selectel_dbaas_postgresql_extension_v1.extension_tf_acc_test_1", &dbaasExtension), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_extension_v1.extension_tf_acc_test_1", "available_extension_id"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_extension_v1.extension_tf_acc_test_1", "datastore_id"), + resource.TestCheckResourceAttrSet("selectel_dbaas_postgresql_extension_v1.extension_tf_acc_test_1", "database_id"), + ), + }, + }, + }) +} + +func testAccDBaaSPostgreSQLExtensionV1Basic(projectName, datastoreName, userName, userPassword, databaseName, extensionName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor { + vcpus = 2 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_user_v1" "user_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + password = "%s" +} + +resource "selectel_dbaas_postgresql_database_v1" "database_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + name = "%s" + owner_id = "${selectel_dbaas_user_v1.user_tf_acc_test_1.id}" +} + +data "selectel_dbaas_available_extension_v1" "ae" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + name = "%s" + } +} + +resource "selectel_dbaas_postgresql_extension_v1" "extension_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + available_extension_id = "${data.selectel_dbaas_available_extension_v1.ae.available_extensions[0].id}" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_tf_acc_test_1.id}" + database_id = "${selectel_dbaas_postgresql_database_v1.database_tf_acc_test_1.id}" +}`, projectName, datastoreName, nodeCount, userName, userPassword, databaseName, extensionName) +} diff --git a/selectel/resource_selectel_dbaas_redis_datastore_v1.go b/selectel/resource_selectel_dbaas_redis_datastore_v1.go new file mode 100644 index 00000000..eba78a73 --- /dev/null +++ b/selectel/resource_selectel_dbaas_redis_datastore_v1.go @@ -0,0 +1,352 @@ +package selectel + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/selectel/dbaas-go" +) + +func resourceDBaaSRedisDatastoreV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDBaaSRedisDatastoreV1Create, + ReadContext: resourceDBaaSRedisDatastoreV1Read, + UpdateContext: resourceDBaaSRedisDatastoreV1Update, + DeleteContext: resourceDBaaSRedisDatastoreV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: resourceDBaaSRedisDatastoreV1ImportState, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ru1Region, + ru2Region, + ru3Region, + ru7Region, + ru8Region, + ru9Region, + }, false), + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "type_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "flavor_id": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "node_count": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "connections": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "flavor": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "vcpus": { + Type: schema.TypeInt, + Computed: true, + }, + "ram": { + Type: schema.TypeInt, + Computed: true, + }, + "disk": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "firewall": { + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ips": { + Type: schema.TypeList, + Required: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "restore": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "datastore_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "target_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + }, + }, + "config": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "redis_password": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceDBaaSRedisDatastoreV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + flavorID, flavorIDOk := d.GetOk("flavor_id") + + typeID := d.Get("type_id").(string) + diagErr = validateDatastoreType(ctx, "redis", typeID, dbaasClient) + if diagErr != nil { + return diagErr + } + + restoreSet := d.Get("restore").(*schema.Set) + restore, err := resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet) + if err != nil { + return diag.FromErr(errParseDatastoreV1Restore(err)) + } + + configMap := d.Get("config").(map[string]interface{}) + config := make(map[string]interface{}) + for paramName, paramValue := range configMap { + paramValueStr := paramValue.(string) + config[paramName] = convertFieldFromStringToType(paramValueStr) + } + + datastoreCreateOpts := dbaas.DatastoreCreateOpts{ + Name: d.Get("name").(string), + TypeID: typeID, + SubnetID: d.Get("subnet_id").(string), + NodeCount: d.Get("node_count").(int), + Restore: restore, + Config: config, + } + + if flavorIDOk { + datastoreCreateOpts.FlavorID = flavorID.(string) + } + + redisPassword, redisPasswordOk := d.GetOk("redis_password") + if redisPasswordOk { + datastoreCreateOpts.RedisPassword = redisPassword.(string) + } + + log.Print(msgCreate(objectDatastore, datastoreCreateOpts)) + datastore, err := dbaasClient.CreateDatastore(ctx, datastoreCreateOpts) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", datastore.ID) + timeout := d.Timeout(schema.TimeoutCreate) + err = waitForDBaaSDatastoreV1ActiveState(ctx, dbaasClient, datastore.ID, timeout) + if err != nil { + return diag.FromErr(errCreatingObject(objectDatastore, err)) + } + + d.SetId(datastore.ID) + + return resourceDBaaSRedisDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSRedisDatastoreV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectDatastore, d.Id())) + datastore, err := dbaasClient.Datastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectDatastore, d.Id(), err)) + } + d.Set("name", datastore.Name) + d.Set("status", datastore.Status) + d.Set("project_id", datastore.ProjectID) + d.Set("subnet_id", datastore.SubnetID) + d.Set("type_id", datastore.TypeID) + d.Set("node_count", datastore.NodeCount) + d.Set("enabled", datastore.Enabled) + d.Set("flavor_id", datastore.FlavorID) + + flavor := resourceDBaaSDatastoreV1FlavorToSet(datastore.Flavor) + if err := d.Set("flavor", flavor); err != nil { + log.Print(errSettingComplexAttr("flavor", err)) + } + + if err := d.Set("connections", datastore.Connection); err != nil { + log.Print(errSettingComplexAttr("connections", err)) + } + + configMap := make(map[string]string) + for key, value := range datastore.Config { + configMap[key] = convertFieldToStringByType(value) + } + if err := d.Set("config", configMap); err != nil { + log.Print(errSettingComplexAttr("config", err)) + } + + return nil +} + +func resourceDBaaSRedisDatastoreV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + if d.HasChange("name") { + err := updateDatastoreName(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("firewall") { + err := updateDatastoreFirewall(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("node_count") || d.HasChange("flavor_id") { + err := resizeRedisDatastore(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("config") { + err := updateDatastoreConfig(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("redis_password") { + err := updateRedisDatastorePassword(ctx, d, dbaasClient) + if err != nil { + return diag.FromErr(err) + } + } + + return resourceDBaaSRedisDatastoreV1Read(ctx, d, meta) +} + +func resourceDBaaSRedisDatastoreV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + dbaasClient, diagErr := getDBaaSClient(ctx, d, meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectDatastore, d.Id())) + err := dbaasClient.DeleteDatastore(ctx, d.Id()) + if err != nil { + return diag.FromErr(errDeletingObject(objectDatastore, d.Id(), err)) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{strconv.Itoa(http.StatusOK)}, + Target: []string{strconv.Itoa(http.StatusNotFound)}, + Refresh: dbaasDatastoreV1DeleteStateRefreshFunc(ctx, dbaasClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + log.Printf("[DEBUG] waiting for datastore %s to become deleted", d.Id()) + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for the datastore %s to become deleted: %s", d.Id(), err)) + } + + return nil +} + +func resourceDBaaSRedisDatastoreV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if config.ProjectID == "" { + return nil, errors.New("SEL_PROJECT_ID must be set for the resource import") + } + if config.Region == "" { + return nil, errors.New("SEL_REGION must be set for the resource import") + } + + d.Set("project_id", config.ProjectID) + d.Set("region", config.Region) + + return []*schema.ResourceData{d}, nil +} diff --git a/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go b/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go new file mode 100644 index 00000000..ff6ff347 --- /dev/null +++ b/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go @@ -0,0 +1,280 @@ +package selectel + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/selectel/dbaas-go" + "github.com/selectel/go-selvpcclient/selvpcclient/resell/v2/projects" +) + +func TestAccDBaaSRedisDatastoreV1Basic(t *testing.T) { + var ( + dbaasDatastore dbaas.Datastore + project projects.Project + ) + + projectName := acctest.RandomWithPrefix("tf-acc") + datastoreName := acctest.RandomWithPrefix("tf-acc-ds") + nodeCount := 1 + resizeNodeCount := 2 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckVPCV2ProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDBaaSRedisDatastoreV1Basic(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "volatile-lru"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSRedisDatastoreV1UpdateConfig(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSRedisDatastoreV1UpdatePassword(projectName, datastoreName, nodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + { + Config: testAccDBaaSRedisDatastoreV1Resize(projectName, datastoreName, resizeNodeCount), + Check: resource.ComposeTestCheckFunc( + testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), + testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "name", datastoreName), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), + resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), + resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), + ), + }, + }, + }) +} + +func testAccDBaaSRedisDatastoreV1Basic(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } +} + +resource "selectel_dbaas_redis_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" + config = { + maxmemory-policy = "volatile-lru" + } + redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6" +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSRedisDatastoreV1UpdateConfig(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } +} + +resource "selectel_dbaas_redis_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" + config = { + maxmemory-policy = "noeviction" + } + redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6" +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSRedisDatastoreV1UpdatePassword(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } +} + +resource "selectel_dbaas_redis_datastore_v1" "datastore_tf_acc_test_1" { + name = "%s" + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" + node_count = "%d" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" + config = { + maxmemory-policy = "noeviction" + } + redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6123" +}`, projectName, datastoreName, nodeCount) +} + +func testAccDBaaSRedisDatastoreV1Resize(projectName, datastoreName string, nodeCount int) string { + return fmt.Sprintf(` +resource "selectel_vpc_project_v2" "project_tf_acc_test_1" { + name = "%s" + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" + region = "ru-3" + filter { + datastore_type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" + } + } + +resource "selectel_dbaas_redis_datastore_v1" "datastore_tf_acc_test_1" { +name = "%s" +project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}" +region = "ru-3" +type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" +subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" +node_count = "%d" +flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[1].id}" +config = { + maxmemory-policy = "noeviction" +} +redis_password = "quie7Hoh7ohTo[i0bae3Leeb4mai7ca6123" +}`, projectName, datastoreName, nodeCount) +} diff --git a/selectel/resource_selectel_dbaas_user_v1.go b/selectel/resource_selectel_dbaas_user_v1.go index 75a73455..6c2f5e08 100644 --- a/selectel/resource_selectel_dbaas_user_v1.go +++ b/selectel/resource_selectel_dbaas_user_v1.go @@ -208,59 +208,3 @@ func resourceDBaaSUserV1ImportState(_ context.Context, d *schema.ResourceData, m return []*schema.ResourceData{d}, nil } - -func waitForDBaaSUserV1ActiveState( - ctx context.Context, client *dbaas.API, userID string, timeout time.Duration) error { - pending := []string{ - string(dbaas.StatusPendingCreate), - string(dbaas.StatusPendingUpdate), - } - target := []string{ - string(dbaas.StatusActive), - } - - stateConf := &resource.StateChangeConf{ - Pending: pending, - Target: target, - Refresh: dbaasUserV1StateRefreshFunc(ctx, client, userID), - Timeout: timeout, - Delay: 10 * time.Second, - MinTimeout: 3 * time.Second, - } - - _, err := stateConf.WaitForState() - if err != nil { - return fmt.Errorf( - "error waiting for the user %s to become 'ACTIVE': %s", - userID, err) - } - - return nil -} - -func dbaasUserV1StateRefreshFunc(ctx context.Context, client *dbaas.API, userID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.User(ctx, userID) - if err != nil { - return nil, "", err - } - - return d, string(d.Status), nil - } -} - -func dbaasUserV1DeleteStateRefreshFunc(ctx context.Context, client *dbaas.API, userID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - d, err := client.User(ctx, userID) - if err != nil { - var dbaasError *dbaas.DBaaSAPIError - if errors.As(err, &dbaasError) { - return d, strconv.Itoa(dbaasError.StatusCode()), nil - } - - return nil, "", err - } - - return d, strconv.Itoa(200), err - } -} diff --git a/website/docs/r/dbaas_database_v1.html.markdown b/website/docs/r/dbaas_database_v1.html.markdown index a49a3415..776cf774 100644 --- a/website/docs/r/dbaas_database_v1.html.markdown +++ b/website/docs/r/dbaas_database_v1.html.markdown @@ -8,6 +8,8 @@ description: |- # selectel\_dbaas\_database\_v1 +**WARNING**: This resource is deprecated and is going to be removed soon. You should use database resource for specific datastore type. + Manages a V1 database resource within Selectel Managed Databases Service. ## Example usage diff --git a/website/docs/r/dbaas_datastore_v1.html.markdown b/website/docs/r/dbaas_datastore_v1.html.markdown index b4dbdf2f..223bf400 100644 --- a/website/docs/r/dbaas_datastore_v1.html.markdown +++ b/website/docs/r/dbaas_datastore_v1.html.markdown @@ -8,6 +8,8 @@ description: |- # selectel\_dbaas\_datastore\_v1 +**WARNING**: This resource is deprecated and is going to be removed soon. You should use datastore resource for specific datastore type. + Manages a V1 datastore resource within Selectel Managed Databases Service. ## Example usage diff --git a/website/docs/r/dbaas_extension_v1.html.markdown b/website/docs/r/dbaas_extension_v1.html.markdown index 19045632..412c4d47 100644 --- a/website/docs/r/dbaas_extension_v1.html.markdown +++ b/website/docs/r/dbaas_extension_v1.html.markdown @@ -8,6 +8,8 @@ description: |- # selectel\_dbaas\_extension\_v1 +**WARNING**: This resource is deprecated and is going to be removed soon. You should use extension resource for specific datastore type. + Manages a V1 extension resource within Selectel Managed Databases Service. Can be installed only for PostgreSQL datastores. ## Example usage diff --git a/website/docs/r/dbaas_mysql_database_v1.html.markdown b/website/docs/r/dbaas_mysql_database_v1.html.markdown new file mode 100644 index 00000000..fddcb8aa --- /dev/null +++ b/website/docs/r/dbaas_mysql_database_v1.html.markdown @@ -0,0 +1,84 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_mysql_database_v1" +sidebar_current: "docs-selectel-resource-dbaas-mysql-database-v1" +description: |- + Manages a V1 MySQL database resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_mysql\_database\_v1 + +Manages a V1 MySQL database resource within Selectel Managed Databases Service. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor { + vcpus = 4 + ram = 4096 + disk = 32 + } +} + +resource "selectel_dbaas_mysql_database_v1" "database_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_mysql_datastore_v1.datastore_1.id}" + name = "db" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A name of the database. + Changing this creates a new database. + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new database. + +* `region` - (Required) A Selectel VPC region of where the database is located. + Changing this creates a new database. + +* `datastore_id` - (Required) An associated datastore. + Changing this creates a new database. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the database. + +## Import + +Database can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_database_v1.database_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/docs/r/dbaas_mysql_datastore_v1.html.markdown b/website/docs/r/dbaas_mysql_datastore_v1.html.markdown new file mode 100644 index 00000000..b39b2b74 --- /dev/null +++ b/website/docs/r/dbaas_mysql_datastore_v1.html.markdown @@ -0,0 +1,111 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_mysql_datastore_v1" +sidebar_current: "docs-selectel-resource-dbaas-mysql-datastore-v1" +description: |- + Manages a V1 MySQL datastore resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_mysql\_datastore\_v1 + +Manages a V1 MySQL datastore resource within Selectel Managed Databases Service. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "mysql" + version = "8" + } +} + +resource "selectel_dbaas_mysql_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor { + vcpus = 4 + ram = 4096 + disk = 32 + } + config = { + innodb_checksum_algorithm = "strict_innodb" + auto_increment_offset = 2 + autocommit = false + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A name of the datastore. + Changing this creates a new datastore. + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new datastore. + +* `region` - (Required) A Selectel VPC region of where the datastore is located. + Changing this creates a new datastore. + +* `subnet_id` - (Required) Associated OpenStack Networking service subnet ID. + Changing this creates a new datastore. + +* `type_id` - (Required) The datastore type for the datastore. + Changing this creates a new datastore. + +* `node_count` - (Required) Number of nodes to create for the datastore. + +* `flavor_id` - (Optional) Flavor identifier for the datastore. It can be omitted in cases when `flavor` is set. + +* `flavor` - (Optional) Flavor configuration for the datastore. It's a complex value. See description below. + +* `firewall` - (Optional) List of the ips to allow access from. + +* `restore` - (Optional) Restore parameters for the datastore. It's a complex value. See description below. + Changing this creates a new datastore. + +* `config` - (Optional) Configuration parameters for the datastore. + +**flavor** + +- `vcpus` - (Required) CPU count for the flavor. +- `ram` - (Required) RAM count for the flavor. +- `disk` - (Required) Disk size for the flavor. + +**restore** + +- `datastore_id` - (Optional) - Datastore ID to restore from. +- `target_time` - (Optional) - Restore by the target time. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the datastore. + +* `connections` - Shows DNS connection strings for the datastore. + +## Import + +Datastore can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_datastore_v1.datastore_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/docs/r/dbaas_postgresql_database_v1.html.markdown b/website/docs/r/dbaas_postgresql_database_v1.html.markdown new file mode 100644 index 00000000..d626795b --- /dev/null +++ b/website/docs/r/dbaas_postgresql_database_v1.html.markdown @@ -0,0 +1,105 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_postgresql_database_v1" +sidebar_current: "docs-selectel-resource-dbaas-postgresql-database-v1" +description: |- + Manages a V1 PostgreSQL database resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_postgresql\_database\_v1 + +Manages a V1 PostgreSQL database resource within Selectel Managed Databases Service. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor { + vcpus = 4 + ram = 4096 + disk = 32 + } + pooler { + mode = "transaction" + size = 50 + } +} + +resource "selectel_dbaas_user_v1" "user_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_1.id}" + name = "user" + password = "secret" +} + +resource "selectel_dbaas_postgresql_database_v1" "database_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_postgresql_datastore_v1.datastore_1.id}" + owner_id = "${selectel_dbaas_user_v1.user_1.id}" + name = "db" + lc_ctype = "ru_RU.utf8" + lc_collate = "ru_RU.utf8" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A name of the database. + Changing this creates a new database. + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new database. + +* `region` - (Required) A Selectel VPC region of where the database is located. + Changing this creates a new database. + +* `datastore_id` - (Required) An associated datastore. + Changing this creates a new database. + +* `owner_id` - (Optional) Owner of the database. Required only for the PostgreSQL datastore. Can be omitted for the MySQL datastore. + +* `lc_collate` - (Optional) A lc_collate option for the PostreSQL datastore. + +* `lc_ctype` - (Optional) A lc_ctype option for the PostreSQL datastore. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the database. + +## Import + +Database can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_database_v1.database_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/docs/r/dbaas_postgresql_datastore_v1.html.markdown b/website/docs/r/dbaas_postgresql_datastore_v1.html.markdown new file mode 100644 index 00000000..356a03ec --- /dev/null +++ b/website/docs/r/dbaas_postgresql_datastore_v1.html.markdown @@ -0,0 +1,124 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_postgresql_datastore_v1" +sidebar_current: "docs-selectel-resource-dbaas-postgresql-datastore-v1" +description: |- + Manages a V1 PostgreSQL datastore resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_postgresql\_datastore\_v1 + +Manages a V1 PostgreSQL datastore resource within Selectel Managed Databases Service. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_postgresql_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor { + vcpus = 4 + ram = 4096 + disk = 32 + } + pooler { + mode = "transaction" + size = 50 + } + config = { + xmloption = "content" + work_mem = 512 + session_replication_role = "replica" + vacuum_cost_delay = 25 + transform_null_equals = false + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A name of the datastore. + Changing this creates a new datastore. + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new datastore. + +* `region` - (Required) A Selectel VPC region of where the datastore is located. + Changing this creates a new datastore. + +* `subnet_id` - (Required) Associated OpenStack Networking service subnet ID. + Changing this creates a new datastore. + +* `type_id` - (Required) The datastore type for the datastore. + Changing this creates a new datastore. + +* `node_count` - (Required) Number of nodes to create for the datastore. + +* `flavor_id` - (Optional) Flavor identifier for the datastore. It can be omitted in cases when `flavor` is set. + +* `flavor` - (Optional) Flavor configuration for the datastore. It's a complex value. See description below. + +* `pooler` - (Optional) Pooler configuration for the datastore (only for PostgreSQL datastore). It's a complex value. See description below. + +* `firewall` - (Optional) List of the ips to allow access from. + +* `restore` - (Optional) Restore parameters for the datastore. It's a complex value. See description below. + Changing this creates a new datastore. + +* `config` - (Optional) Configuration parameters for the datastore. + +**flavor** + +- `vcpus` - (Required) CPU count for the flavor. +- `ram` - (Required) RAM count for the flavor. +- `disk` - (Required) Disk size for the flavor. + +**pooler** + +- `mode` - (Required) Mode for the pooler. Valid values: ["session", "transaction", "statement"]. +- `size` - (Required) Size of the pooler. + +**restore** + +- `datastore_id` - (Optional) - Datastore ID to restore from. +- `target_time` - (Optional) - Restore by the target time. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the datastore. + +* `connections` - Shows DNS connection strings for the datastore. + +## Import + +Datastore can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_datastore_v1.datastore_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/docs/r/dbaas_postgresql_extension_v1.html.markdown b/website/docs/r/dbaas_postgresql_extension_v1.html.markdown new file mode 100644 index 00000000..e059713f --- /dev/null +++ b/website/docs/r/dbaas_postgresql_extension_v1.html.markdown @@ -0,0 +1,120 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_extension_v1" +sidebar_current: "docs-selectel-resource-dbaas-extension-v1" +description: |- + Manages a V1 extension resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_extension\_v1 + +**WARNING**: This resource is deprecated. You should use extension resource for specific datastore type. + +Manages a V1 extension resource within Selectel Managed Databases Service. Can be installed only for PostgreSQL datastores. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "postgresql" + version = "12" + } +} + +resource "selectel_dbaas_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor { + vcpus = 4 + ram = 4096 + disk = 32 + } + pooler { + mode = "transaction" + size = 50 + } +} + +resource "selectel_dbaas_user_v1" "user_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_1.id}" + name = "user" + password = "secret" +} + +resource "selectel_dbaas_database_v1" "database_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_1.id}" + owner_id = "${selectel_dbaas_user_v1.user_1.id}" + name = "db" + lc_ctype = "ru_RU.utf8" + lc_collate = "ru_RU.utf8" +} + +data "selectel_dbaas_available_extension_v1" "ae" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + name = "hstore" + } +} + +resource "selectel_dbaas_extension_v1" "extension_1" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + datastore_id = "${selectel_dbaas_datastore_v1.datastore_1.id}" + database_id = "${selectel_dbaas_database_v1.database_1.id}" + available_extension_id = data.selectel_dbaas_available_extension_v1.ae.available_extensions[0].id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new extension. + +* `region` - (Required) A Selectel VPC region of where the database is located. + Changing this creates a new extension. + +* `datastore_id` - (Required) An associated datastore. + Changing this creates a new extension. + +* `database_id` - (Required) An associated database. + Changing this creates a new extension. + +* `available_extension_id` - (Required) An associated available extension. + Changing this creates a new extension. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the extension. + +## Import + +Extension can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_extension_v1.extension_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/docs/r/dbaas_redis_datastore_v1.html.markdown b/website/docs/r/dbaas_redis_datastore_v1.html.markdown new file mode 100644 index 00000000..d911aec1 --- /dev/null +++ b/website/docs/r/dbaas_redis_datastore_v1.html.markdown @@ -0,0 +1,108 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_dbaas_redis_datastore_v1" +sidebar_current: "docs-selectel-resource-dbaas-redis-datastore-v1" +description: |- + Manages a V1 Redis datastore resource within Selectel Managed Databases Service. +--- + +# selectel\_dbaas\_redis\_datastore\_v1 + +Manages a V1 Redis datastore resource within Selectel Managed Databases Service. + +## Example usage + +```hcl +resource "selectel_vpc_project_v2" "project_1" { + auto_quotas = true +} + +resource "selectel_vpc_subnet_v2" "subnet" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" +} + +data "selectel_dbaas_datastore_type_v1" "dt" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + engine = "redis" + version = "6" + } +} + +data "selectel_dbaas_flavor_v1" "flavor" { + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + filter { + datastore_type_id = data.selectel_dbaas_datastore_type_v1.dt_redis.datastore_types[0].id + } +} + +resource "selectel_dbaas_redis_datastore_v1" "datastore_1" { + name = "datastore-1" + project_id = "${selectel_vpc_project_v2.project_1.id}" + region = "ru-3" + type_id = data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id + subnet_id = "${selectel_vpc_subnet_v2.subnet.subnet_id}" + node_count = 3 + flavor_id = data.selectel_dbaas_flavor_v1.flavor.flavors[0].id + config = { + maxmemory-policy = "allkeys-lru" + } + redis_password = "secret" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A name of the datastore. + Changing this creates a new datastore. + +* `project_id` - (Required) An associated Selectel VPC project. + Changing this creates a new datastore. + +* `region` - (Required) A Selectel VPC region of where the datastore is located. + Changing this creates a new datastore. + +* `subnet_id` - (Required) Associated OpenStack Networking service subnet ID. + Changing this creates a new datastore. + +* `type_id` - (Required) The datastore type for the datastore. + Changing this creates a new datastore. + +* `node_count` - (Required) Number of nodes to create for the datastore. + +* `flavor_id` - (Required) Flavor identifier for the datastore. + +* `firewall` - (Optional) List of the ips to allow access from. + +* `restore` - (Optional) Restore parameters for the datastore. It's a complex value. See description below. + Changing this creates a new datastore. + +* `config` - (Optional) Configuration parameters for the datastore. + +* `redis_password` - (Required) Password for the Redis datastore + +**restore** + +- `datastore_id` - (Optional) - Datastore ID to restore from. +- `target_time` - (Optional) - Restore by the target time. + +## Attributes Reference + +The following attributes are exported: + +* `status` - Shows the current status of the datastore. + +* `connections` - Shows DNS connection strings for the datastore. + +## Import + +Datastore can be imported using the `id`, e.g. + +```shell +$ env SEL_TOKEN=SELECTEL_API_TOKEN SEL_PROJECT_ID=SELECTEL_VPC_PROJECT_ID SEL_REGION=SELECTEL_VPC_REGION terraform import selectel_dbaas_datastore_v1.datastore_1 b311ce58-2658-46b5-b733-7a0f418703f2 +``` diff --git a/website/selectel.erb b/website/selectel.erb index c582be5b..a8b0b5b1 100644 --- a/website/selectel.erb +++ b/website/selectel.erb @@ -103,12 +103,30 @@ > selectel_dbaas_database_v1 + > + selectel_dbaas_database_v1 + + > + selectel_dbaas_database_v1 + + > + selectel_dbaas_datastore_v1 + + > + selectel_dbaas_datastore_v1 + + > + selectel_dbaas_datastore_v1 + > selectel_dbaas_datastore_v1 > selectel_dbaas_extension_v1 + > + selectel_dbaas_extension_v1 + > selectel_dbaas_grant_v1 From 318dec2781df3e59a615fa21ec602955daced2a2 Mon Sep 17 00:00:00 2001 From: Gogen120 Date: Wed, 21 Sep 2022 16:36:18 +0300 Subject: [PATCH 2/4] Code review fixes - Add nl-1 endpoint to the regions - Use dbaas-go v0.6.0 with config parser --- go.mod | 12 ++--- go.sum | 30 +++++++----- ...e_selectel_dbaas_available_extension_v1.go | 1 + ...lectel_dbaas_configuration_parameter_v1.go | 1 + ...source_selectel_dbaas_datastore_type_v1.go | 1 + .../data_source_selectel_dbaas_flavor_v1.go | 1 + ...lectel_dbaas_prometheus_metric_token_v1.go | 1 + selectel/dbaas.go | 24 ++------- selectel/dbaas_postgresql_utils.go | 49 +++++++++---------- .../resource_selectel_dbaas_database_v1.go | 1 + .../resource_selectel_dbaas_datastore_v1.go | 10 +--- .../resource_selectel_dbaas_extension_v1.go | 1 + selectel/resource_selectel_dbaas_grant_v1.go | 1 + ...source_selectel_dbaas_mysql_database_v1.go | 1 + ...ource_selectel_dbaas_mysql_datastore_v1.go | 12 ++--- ...e_selectel_dbaas_postgresql_database_v1.go | 1 + ..._selectel_dbaas_postgresql_datastore_v1.go | 12 ++--- ..._selectel_dbaas_postgresql_extension_v1.go | 1 + ...lectel_dbaas_prometheus_metric_token_v1.go | 1 + ...ource_selectel_dbaas_redis_datastore_v1.go | 12 ++--- selectel/resource_selectel_dbaas_user_v1.go | 1 + 21 files changed, 74 insertions(+), 100 deletions(-) diff --git a/go.mod b/go.mod index 59812191..8f33e358 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/hashicorp/go-retryablehttp v0.6.6 github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 - github.com/selectel/dbaas-go v0.5.0 + github.com/selectel/dbaas-go v0.7.0 github.com/selectel/domains-go v0.3.0 github.com/selectel/go-selvpcclient v1.12.0 github.com/selectel/mks-go v0.12.0 @@ -21,7 +21,7 @@ require ( github.com/fatih/color v1.7.0 // indirect github.com/golang/protobuf v1.4.2 // indirect github.com/google/go-cmp v0.5.6 // indirect - github.com/gophercloud/gophercloud v0.17.0 // indirect + github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -52,13 +52,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/zclconf/go-cty v1.9.1 // indirect - golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect + golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/text v0.3.6 // indirect google.golang.org/appengine v1.6.6 // indirect google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect google.golang.org/grpc v1.32.0 // indirect google.golang.org/protobuf v1.25.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a2135b07..6e479bd2 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gophercloud/gophercloud v0.17.0 h1:BgVw0saxyeHWH5us/SQe1ltp0GRnytjmOLXDA8pO77E= -github.com/gophercloud/gophercloud v0.17.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= +github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k= +github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= @@ -218,8 +218,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= -github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= +github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= @@ -257,6 +257,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -289,8 +290,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= -github.com/selectel/dbaas-go v0.5.0 h1:bDiRSBGG6x34vAaHhbSkmAgnnpbiyiHZHkzieRVjBIo= -github.com/selectel/dbaas-go v0.5.0/go.mod h1:H2NC+UrVQzKea92LYyHZj51uuX43sUiAc+iyx/yTCGw= +github.com/selectel/dbaas-go v0.7.0 h1:IBEU7EAPlkQ7wGjtIz9bppthpTZLGn3E6TVX7JWMtWk= +github.com/selectel/dbaas-go v0.7.0/go.mod h1:8D945oFzpx94v08zIb4s1bRTPCdPoNVnBu4umMYFJrQ= github.com/selectel/domains-go v0.3.0 h1:0shjqQmpkWc6eM1SwgKqbTTNiT5G2BOEnvS7JvuF96g= github.com/selectel/domains-go v0.3.0/go.mod h1:AhXhwyMSTkpEWFiBLUvzFP76W+WN+ZblwmjLJLt7y58= github.com/selectel/go-selvpcclient v1.12.0 h1:LsT074HOVF1dWYapsAWjaaJDQhmDPpcsVjSwQ1r1fj0= @@ -342,11 +343,11 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -409,8 +410,9 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -440,7 +442,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -458,10 +459,10 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -469,8 +470,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -607,8 +609,10 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/selectel/data_source_selectel_dbaas_available_extension_v1.go b/selectel/data_source_selectel_dbaas_available_extension_v1.go index 7adb51d6..7cec7121 100644 --- a/selectel/data_source_selectel_dbaas_available_extension_v1.go +++ b/selectel/data_source_selectel_dbaas_available_extension_v1.go @@ -31,6 +31,7 @@ func dataSourceDBaaSAvailableExtensionV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "filter": { diff --git a/selectel/data_source_selectel_dbaas_configuration_parameter_v1.go b/selectel/data_source_selectel_dbaas_configuration_parameter_v1.go index efa64ab0..7e8d0a26 100644 --- a/selectel/data_source_selectel_dbaas_configuration_parameter_v1.go +++ b/selectel/data_source_selectel_dbaas_configuration_parameter_v1.go @@ -32,6 +32,7 @@ func dataSourceDBaaSConfigurationParameterV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "filter": { diff --git a/selectel/data_source_selectel_dbaas_datastore_type_v1.go b/selectel/data_source_selectel_dbaas_datastore_type_v1.go index db113d16..afcdd87d 100644 --- a/selectel/data_source_selectel_dbaas_datastore_type_v1.go +++ b/selectel/data_source_selectel_dbaas_datastore_type_v1.go @@ -32,6 +32,7 @@ func dataSourceDBaaSDatastoreTypeV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "filter": { diff --git a/selectel/data_source_selectel_dbaas_flavor_v1.go b/selectel/data_source_selectel_dbaas_flavor_v1.go index 2527ae25..e2814cd0 100644 --- a/selectel/data_source_selectel_dbaas_flavor_v1.go +++ b/selectel/data_source_selectel_dbaas_flavor_v1.go @@ -34,6 +34,7 @@ func dataSourceDBaaSFlavorV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "flavors": { diff --git a/selectel/data_source_selectel_dbaas_prometheus_metric_token_v1.go b/selectel/data_source_selectel_dbaas_prometheus_metric_token_v1.go index a5e3b73e..b828472e 100644 --- a/selectel/data_source_selectel_dbaas_prometheus_metric_token_v1.go +++ b/selectel/data_source_selectel_dbaas_prometheus_metric_token_v1.go @@ -29,6 +29,7 @@ func dataSourceDBaaSPrometheusMetricTokenV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "prometheus_metrics_tokens": { diff --git a/selectel/dbaas.go b/selectel/dbaas.go index e679931a..529f9be8 100644 --- a/selectel/dbaas.go +++ b/selectel/dbaas.go @@ -27,6 +27,7 @@ const ( ru7DBaaSV1Endpoint = "https://ru-7.dbaas.selcloud.ru/v1" ru8DBaaSV1Endpoint = "https://ru-8.dbaas.selcloud.ru/v1" ru9DBaaSV1Endpoint = "https://ru-9.dbaas.selcloud.ru/v1" + nl1DBaaSV1Endpoint = "https://nl-1.dbaas.selcloud.ru/v1" ) func getDBaaSV1Endpoint(region string) (endpoint string) { @@ -43,6 +44,8 @@ func getDBaaSV1Endpoint(region string) (endpoint string) { endpoint = ru8DBaaSV1Endpoint case ru9Region: endpoint = ru9DBaaSV1Endpoint + case nl1Region: + endpoint = nl1DBaaSV1Endpoint } return @@ -149,20 +152,6 @@ func flavorHashSetFunc() schema.SchemaSetFunc { return schema.HashResource(flavorSchema()) } -func convertFieldFromStringToType(fieldValue string) interface{} { - if val, err := strconv.Atoi(fieldValue); err == nil { - return val - } else if val, err := strconv.ParseFloat(fieldValue, 64); err == nil { - return val - } else if val, err := strconv.ParseFloat(fieldValue, 32); err == nil { - return val - } else if val, err := strconv.ParseBool(fieldValue); err == nil { - return val - } else { - return fieldValue - } -} - func waitForDBaaSDatastoreV1ActiveState( ctx context.Context, client *dbaas.API, datastoreID string, timeout time.Duration) error { pending := []string{ @@ -361,12 +350,7 @@ func updateDatastoreConfig(ctx context.Context, d *schema.ResourceData, client * if err != nil { return err } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } + config := d.Get("config").(map[string]interface{}) for param := range datastore.Config { if _, ok := config[param]; !ok { diff --git a/selectel/dbaas_postgresql_utils.go b/selectel/dbaas_postgresql_utils.go index 5c9a7eab..6f853e31 100644 --- a/selectel/dbaas_postgresql_utils.go +++ b/selectel/dbaas_postgresql_utils.go @@ -9,27 +9,33 @@ import ( "github.com/selectel/dbaas-go" ) -func resourceDBaaSPostgreSQLDatastoreV1PoolerFromSet(poolerSet *schema.Set) (*dbaas.Pooler, error) { - if poolerSet.Len() == 0 { - return nil, nil - } +func parsePoolerSet(poolerSet *schema.Set) (string, int, error) { var resourceModeRaw, resourceSizeRaw interface{} var ok bool resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { - return &dbaas.Pooler{}, errors.New("pooler.mode value isn't provided") + return "", 0, errors.New("pooler.mode value isn't provided") } if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { - return &dbaas.Pooler{}, errors.New("pooler.size value isn't provided") + return "", 0, errors.New("pooler.size value isn't provided") } - resourceMode := resourceModeRaw.(string) - resourceSize := resourceSizeRaw.(int) + return resourceModeRaw.(string), resourceSizeRaw.(int), nil +} + +func resourceDBaaSPostgreSQLDatastoreV1PoolerFromSet(poolerSet *schema.Set) (*dbaas.Pooler, error) { + if poolerSet.Len() == 0 { + return nil, nil + } + poolerMode, poolerSize, err := parsePoolerSet(poolerSet) + if err != nil { + return nil, errParseDatastoreV1Pooler(err) + } pooler := &dbaas.Pooler{ - Mode: resourceMode, - Size: resourceSize, + Mode: poolerMode, + Size: poolerSize, } return pooler, nil @@ -39,26 +45,17 @@ func resourceDBaaSPostgreSQLDatastoreV1PoolerOptsFromSet(poolerSet *schema.Set) if poolerSet.Len() == 0 { return dbaas.DatastorePoolerOpts{}, nil } - var resourceModeRaw, resourceSizeRaw interface{} - var ok bool - - resourcePoolerMap := poolerSet.List()[0].(map[string]interface{}) - if resourceModeRaw, ok = resourcePoolerMap["mode"]; !ok { - return dbaas.DatastorePoolerOpts{}, errors.New("pooler.mode value isn't provided") - } - if resourceSizeRaw, ok = resourcePoolerMap["size"]; !ok { - return dbaas.DatastorePoolerOpts{}, errors.New("pooler.size value isn't provided") + poolerMode, poolerSize, err := parsePoolerSet(poolerSet) + if err != nil { + return dbaas.DatastorePoolerOpts{}, errParseDatastoreV1Pooler(err) } - resourceMode := resourceModeRaw.(string) - resourceSize := resourceSizeRaw.(int) - - pooler := dbaas.DatastorePoolerOpts{ - Mode: resourceMode, - Size: resourceSize, + poolerOpts := dbaas.DatastorePoolerOpts{ + Mode: poolerMode, + Size: poolerSize, } - return pooler, nil + return poolerOpts, nil } func updatePostgreSQLDatastorePooler(ctx context.Context, d *schema.ResourceData, client *dbaas.API) error { diff --git a/selectel/resource_selectel_dbaas_database_v1.go b/selectel/resource_selectel_dbaas_database_v1.go index 933b652f..7f23eddd 100644 --- a/selectel/resource_selectel_dbaas_database_v1.go +++ b/selectel/resource_selectel_dbaas_database_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSDatabaseV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "datastore_id": { diff --git a/selectel/resource_selectel_dbaas_datastore_v1.go b/selectel/resource_selectel_dbaas_datastore_v1.go index e6c5e65e..0c4d4b2e 100644 --- a/selectel/resource_selectel_dbaas_datastore_v1.go +++ b/selectel/resource_selectel_dbaas_datastore_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSDatastoreV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "subnet_id": { @@ -220,13 +221,6 @@ func resourceDBaaSDatastoreV1Create(ctx context.Context, d *schema.ResourceData, return diag.FromErr(errParseDatastoreV1Restore(err)) } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } - datastoreCreateOpts := dbaas.DatastoreCreateOpts{ Name: d.Get("name").(string), TypeID: d.Get("type_id").(string), @@ -234,7 +228,7 @@ func resourceDBaaSDatastoreV1Create(ctx context.Context, d *schema.ResourceData, NodeCount: d.Get("node_count").(int), Pooler: pooler, Restore: restore, - Config: config, + Config: d.Get("config").(map[string]interface{}), } if flavorOk { diff --git a/selectel/resource_selectel_dbaas_extension_v1.go b/selectel/resource_selectel_dbaas_extension_v1.go index 60b8f536..0e61febd 100644 --- a/selectel/resource_selectel_dbaas_extension_v1.go +++ b/selectel/resource_selectel_dbaas_extension_v1.go @@ -46,6 +46,7 @@ func resourceDBaaSExtensionV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "available_extension_id": { diff --git a/selectel/resource_selectel_dbaas_grant_v1.go b/selectel/resource_selectel_dbaas_grant_v1.go index b845c05f..d2ce4328 100644 --- a/selectel/resource_selectel_dbaas_grant_v1.go +++ b/selectel/resource_selectel_dbaas_grant_v1.go @@ -46,6 +46,7 @@ func resourceDBaaSGrantV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "datastore_id": { diff --git a/selectel/resource_selectel_dbaas_mysql_database_v1.go b/selectel/resource_selectel_dbaas_mysql_database_v1.go index 87470878..734d9dc0 100644 --- a/selectel/resource_selectel_dbaas_mysql_database_v1.go +++ b/selectel/resource_selectel_dbaas_mysql_database_v1.go @@ -51,6 +51,7 @@ func resourceDBaaSMySQLDatabaseV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "datastore_id": { diff --git a/selectel/resource_selectel_dbaas_mysql_datastore_v1.go b/selectel/resource_selectel_dbaas_mysql_datastore_v1.go index 2964db7f..638b4b7c 100644 --- a/selectel/resource_selectel_dbaas_mysql_datastore_v1.go +++ b/selectel/resource_selectel_dbaas_mysql_datastore_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSMySQLDatastoreV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "subnet_id": { @@ -142,7 +143,7 @@ func resourceDBaaSMySQLDatastoreV1() *schema.Resource { Schema: map[string]*schema.Schema{ "datastore_id": { Type: schema.TypeString, - Optional: true, + Required: true, ForceNew: false, }, "target_time": { @@ -191,20 +192,13 @@ func resourceDBaaSMySQLDatastoreV1Create(ctx context.Context, d *schema.Resource return diag.FromErr(errParseDatastoreV1Restore(err)) } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } - datastoreCreateOpts := dbaas.DatastoreCreateOpts{ Name: d.Get("name").(string), TypeID: typeID, SubnetID: d.Get("subnet_id").(string), NodeCount: d.Get("node_count").(int), Restore: restore, - Config: config, + Config: d.Get("config").(map[string]interface{}), } if flavorOk { diff --git a/selectel/resource_selectel_dbaas_postgresql_database_v1.go b/selectel/resource_selectel_dbaas_postgresql_database_v1.go index 92691fde..a2d29e20 100644 --- a/selectel/resource_selectel_dbaas_postgresql_database_v1.go +++ b/selectel/resource_selectel_dbaas_postgresql_database_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSPostgreSQLDatabaseV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "datastore_id": { diff --git a/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go b/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go index 7d874699..3f344b0a 100644 --- a/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go +++ b/selectel/resource_selectel_dbaas_postgresql_datastore_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSPostgreSQLDatastoreV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "subnet_id": { @@ -166,7 +167,7 @@ func resourceDBaaSPostgreSQLDatastoreV1() *schema.Resource { Schema: map[string]*schema.Schema{ "datastore_id": { Type: schema.TypeString, - Optional: true, + Required: true, ForceNew: false, }, "target_time": { @@ -221,13 +222,6 @@ func resourceDBaaSPostgreSQLDatastoreV1Create(ctx context.Context, d *schema.Res return diag.FromErr(errParseDatastoreV1Restore(err)) } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } - datastoreCreateOpts := dbaas.DatastoreCreateOpts{ Name: d.Get("name").(string), TypeID: typeID, @@ -235,7 +229,7 @@ func resourceDBaaSPostgreSQLDatastoreV1Create(ctx context.Context, d *schema.Res NodeCount: d.Get("node_count").(int), Pooler: pooler, Restore: restore, - Config: config, + Config: d.Get("config").(map[string]interface{}), } if flavorOk { diff --git a/selectel/resource_selectel_dbaas_postgresql_extension_v1.go b/selectel/resource_selectel_dbaas_postgresql_extension_v1.go index e78a0b37..d95f64e9 100644 --- a/selectel/resource_selectel_dbaas_postgresql_extension_v1.go +++ b/selectel/resource_selectel_dbaas_postgresql_extension_v1.go @@ -46,6 +46,7 @@ func resourceDBaaSPostgreSQLExtensionV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "available_extension_id": { diff --git a/selectel/resource_selectel_dbaas_prometheus_metric_token_v1.go b/selectel/resource_selectel_dbaas_prometheus_metric_token_v1.go index eae97d1a..282be0ab 100644 --- a/selectel/resource_selectel_dbaas_prometheus_metric_token_v1.go +++ b/selectel/resource_selectel_dbaas_prometheus_metric_token_v1.go @@ -45,6 +45,7 @@ func resourceDBaaSPrometheusMetricTokenV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "name": { diff --git a/selectel/resource_selectel_dbaas_redis_datastore_v1.go b/selectel/resource_selectel_dbaas_redis_datastore_v1.go index eba78a73..c0d37490 100644 --- a/selectel/resource_selectel_dbaas_redis_datastore_v1.go +++ b/selectel/resource_selectel_dbaas_redis_datastore_v1.go @@ -52,6 +52,7 @@ func resourceDBaaSRedisDatastoreV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "subnet_id": { @@ -134,7 +135,7 @@ func resourceDBaaSRedisDatastoreV1() *schema.Resource { Schema: map[string]*schema.Schema{ "datastore_id": { Type: schema.TypeString, - Optional: true, + Required: true, ForceNew: false, }, "target_time": { @@ -183,20 +184,13 @@ func resourceDBaaSRedisDatastoreV1Create(ctx context.Context, d *schema.Resource return diag.FromErr(errParseDatastoreV1Restore(err)) } - configMap := d.Get("config").(map[string]interface{}) - config := make(map[string]interface{}) - for paramName, paramValue := range configMap { - paramValueStr := paramValue.(string) - config[paramName] = convertFieldFromStringToType(paramValueStr) - } - datastoreCreateOpts := dbaas.DatastoreCreateOpts{ Name: d.Get("name").(string), TypeID: typeID, SubnetID: d.Get("subnet_id").(string), NodeCount: d.Get("node_count").(int), Restore: restore, - Config: config, + Config: d.Get("config").(map[string]interface{}), } if flavorIDOk { diff --git a/selectel/resource_selectel_dbaas_user_v1.go b/selectel/resource_selectel_dbaas_user_v1.go index 6c2f5e08..f709ca07 100644 --- a/selectel/resource_selectel_dbaas_user_v1.go +++ b/selectel/resource_selectel_dbaas_user_v1.go @@ -47,6 +47,7 @@ func resourceDBaaSUserV1() *schema.Resource { ru7Region, ru8Region, ru9Region, + nl1Region, }, false), }, "name": { From 71528d6e340246031e4348073af002148529caa0 Mon Sep 17 00:00:00 2001 From: Gogen120 Date: Tue, 18 Oct 2022 11:24:32 +0300 Subject: [PATCH 3/4] Fix DBaaS acceptance tests --- .../data_source_selectel_dbaas_flavor_v1_test.go | 1 - .../resource_selectel_dbaas_datastore_v1_test.go | 14 +------------- ...ource_selectel_dbaas_redis_datastore_v1_test.go | 14 +------------- website/selectel.erb | 2 +- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/selectel/data_source_selectel_dbaas_flavor_v1_test.go b/selectel/data_source_selectel_dbaas_flavor_v1_test.go index dffd5db2..ce95b329 100644 --- a/selectel/data_source_selectel_dbaas_flavor_v1_test.go +++ b/selectel/data_source_selectel_dbaas_flavor_v1_test.go @@ -34,7 +34,6 @@ func TestAccDBaaSFlavorsV1Basic(t *testing.T) { resource.TestCheckResourceAttrSet("data.selectel_dbaas_flavor_v1.flavor_tf_acc_test_1", "flavors.0.vcpus"), resource.TestCheckResourceAttrSet("data.selectel_dbaas_flavor_v1.flavor_tf_acc_test_1", "flavors.0.ram"), resource.TestCheckResourceAttrSet("data.selectel_dbaas_flavor_v1.flavor_tf_acc_test_1", "flavors.0.disk"), - resource.TestCheckResourceAttr("data.selectel_dbaas_flavor_v1.flavor_tf_acc_test_1", "flavors.0.datastore_type_ids.#", "9"), ), }, { diff --git a/selectel/resource_selectel_dbaas_datastore_v1_test.go b/selectel/resource_selectel_dbaas_datastore_v1_test.go index f225adb7..c13241e7 100644 --- a/selectel/resource_selectel_dbaas_datastore_v1_test.go +++ b/selectel/resource_selectel_dbaas_datastore_v1_test.go @@ -197,9 +197,6 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "volatile-lru"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), @@ -216,9 +213,6 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), @@ -235,9 +229,6 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), @@ -254,9 +245,6 @@ func TestAccDBaaSDatastoreV1RedisBasic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttr("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "firewall.0.ips.#", "2"), resource.TestCheckResourceAttrSet("selectel_dbaas_datastore_v1.datastore_tf_acc_test_1", "connections.master"), @@ -795,7 +783,7 @@ data "selectel_dbaas_flavor_v1" "flavor" { type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" node_count = "%d" - flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[1].id}" + flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" config = { maxmemory-policy = "noeviction" } diff --git a/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go b/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go index ff6ff347..f454f0b5 100644 --- a/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go +++ b/selectel/resource_selectel_dbaas_redis_datastore_v1_test.go @@ -37,9 +37,6 @@ func TestAccDBaaSRedisDatastoreV1Basic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "volatile-lru"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), @@ -55,9 +52,6 @@ func TestAccDBaaSRedisDatastoreV1Basic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), @@ -73,9 +67,6 @@ func TestAccDBaaSRedisDatastoreV1Basic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), @@ -91,9 +82,6 @@ func TestAccDBaaSRedisDatastoreV1Basic(t *testing.T) { resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(1)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.ram", strconv.Itoa(2048)), - resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "flavor.0.disk", strconv.Itoa(20)), resource.TestCheckResourceAttr("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "config.maxmemory-policy", "noeviction"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.master"), resource.TestCheckResourceAttrSet("selectel_dbaas_redis_datastore_v1.datastore_tf_acc_test_1", "connections.MASTER"), @@ -271,7 +259,7 @@ region = "ru-3" type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}" subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}" node_count = "%d" -flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[1].id}" +flavor_id = "${data.selectel_dbaas_flavor_v1.flavor.flavors[0].id}" config = { maxmemory-policy = "noeviction" } diff --git a/website/selectel.erb b/website/selectel.erb index a8b0b5b1..4304e946 100644 --- a/website/selectel.erb +++ b/website/selectel.erb @@ -112,7 +112,7 @@ > selectel_dbaas_datastore_v1 - > + > selectel_dbaas_datastore_v1 > From c5e44feb0bff6b3a538aa3045d7f556ecd3d85f7 Mon Sep 17 00:00:00 2001 From: Gogen120 Date: Wed, 2 Nov 2022 16:43:46 +0300 Subject: [PATCH 4/4] Fix MySQL datastore resource tests --- .../resource_selectel_dbaas_mysql_datastore_v1_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go b/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go index 58186487..8bc7bfd7 100644 --- a/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go +++ b/selectel/resource_selectel_dbaas_mysql_datastore_v1_test.go @@ -20,7 +20,6 @@ func TestAccDBaaSMySQLDatastoreV1Basic(t *testing.T) { projectName := acctest.RandomWithPrefix("tf-acc") datastoreName := acctest.RandomWithPrefix("tf-acc-ds") nodeCount := 1 - resizeNodeCount := 3 updatedDatastoreName := acctest.RandomWithPrefix("tf-acc-ds-updated") @@ -91,13 +90,13 @@ func TestAccDBaaSMySQLDatastoreV1Basic(t *testing.T) { ), }, { - Config: testAccDBaaSMySQLDatastoreV1Resize(projectName, updatedDatastoreName, resizeNodeCount), + Config: testAccDBaaSMySQLDatastoreV1Resize(projectName, updatedDatastoreName, nodeCount), Check: resource.ComposeTestCheckFunc( testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), - resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)), @@ -112,13 +111,13 @@ func TestAccDBaaSMySQLDatastoreV1Basic(t *testing.T) { ), }, { - Config: testAccDBaaSMySQLDatastoreV1UpdateConfig(projectName, updatedDatastoreName, resizeNodeCount), + Config: testAccDBaaSMySQLDatastoreV1UpdateConfig(projectName, updatedDatastoreName, nodeCount), Check: resource.ComposeTestCheckFunc( testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project), testAccCheckDBaaSDatastoreV1Exists("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", &dbaasDatastore), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "name", updatedDatastoreName), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "region", "ru-3"), - resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(resizeNodeCount)), + resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "node_count", strconv.Itoa(nodeCount)), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "enabled", "true"), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "status", string(dbaas.StatusActive)), resource.TestCheckResourceAttr("selectel_dbaas_mysql_datastore_v1.datastore_tf_acc_test_1", "flavor.0.vcpus", strconv.Itoa(2)),