alicloud/resource_alicloud_rds_clone_db_instance.go (1,313 lines of code) (raw):

package alicloud import ( "encoding/json" "fmt" "log" "strings" "time" "github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func resourceAlicloudRdsCloneDbInstance() *schema.Resource { return &schema.Resource{ Create: resourceAlicloudRdsCloneDbInstanceCreate, Read: resourceAlicloudRdsCloneDbInstanceRead, Update: resourceAlicloudRdsCloneDbInstanceUpdate, Delete: resourceAlicloudRdsCloneDbInstanceDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(300 * time.Minute), Update: schema.DefaultTimeout(30 * time.Minute), Delete: schema.DefaultTimeout(20 * time.Minute), }, Schema: map[string]*schema.Schema{ "acl": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"cert", "perfer", "verify-ca", "verify-full"}, false), Computed: true, }, "auto_upgrade_minor_version": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"Auto", "Manual"}, false), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return d.Get("engine").(string) != "MySQL" }, }, "backup_id": { Type: schema.TypeString, Optional: true, }, "backup_type": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"FullBackup", "IncrementalBackup"}, false), }, "ca_type": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"aliyun", "custom"}, false), Computed: true, }, "category": { Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, ValidateFunc: StringInSlice([]string{"AlwaysOn", "Basic", "Finance", "HighAvailability", "serverless_basic", "serverless_standard", "serverless_ha", "cluster"}, false), }, "certificate": { Type: schema.TypeString, Optional: true, }, "client_ca_enabled": { Type: schema.TypeInt, Optional: true, }, "client_ca_cert": { Type: schema.TypeString, Optional: true, }, "client_cert_revocation_list": { Type: schema.TypeString, Optional: true, }, "client_crl_enabled": { Type: schema.TypeInt, Optional: true, }, "connection_string": { Type: schema.TypeString, Computed: true, }, "connection_string_prefix": { Type: schema.TypeString, Optional: true, ValidateFunc: StringLenBetween(8, 64), }, "db_instance_class": { Type: schema.TypeString, Optional: true, Computed: true, }, "db_instance_description": { Type: schema.TypeString, Optional: true, ValidateFunc: StringLenBetween(2, 256), }, "db_instance_storage": { Type: schema.TypeInt, Optional: true, Computed: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if v, ok := d.GetOk("payment_type"); ok && v.(string) == "Serverless" && old != "" && new != "" && old != new { return true } return false }, }, "db_instance_storage_type": { Type: schema.TypeString, Required: true, ValidateFunc: StringInSlice([]string{"cloud_essd", "cloud_essd2", "cloud_essd3", "cloud_ssd", "local_ssd"}, false), }, "db_name": { Type: schema.TypeString, Optional: true, }, "db_names": { Type: schema.TypeString, Optional: true, }, "dedicated_host_group_id": { Type: schema.TypeString, Optional: true, }, "direction": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"Auto", "Down", "TempUpgrade", "Up"}, false), }, "effective_time": { Type: schema.TypeString, Optional: true, }, "encryption_key": { Type: schema.TypeString, Optional: true, }, "engine": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "engine_version": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "pg_hba_conf": { Type: schema.TypeSet, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, Required: true, }, "mask": { Type: schema.TypeString, Optional: true, // if attribute contains Optional feature, need to add Default: "", otherwise when terraform plan is executed, unmodified items wil detect differences. Default: "", }, "database": { Type: schema.TypeString, Required: true, }, "priority_id": { Type: schema.TypeInt, Required: true, }, "address": { Type: schema.TypeString, Required: true, }, "user": { Type: schema.TypeString, Required: true, }, "method": { Type: schema.TypeString, Required: true, }, "option": { Type: schema.TypeString, Optional: true, Default: "", }, }, }, Optional: true, Computed: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return d.Get("engine").(string) != string(PostgreSQL) }, }, "force_restart": { Type: schema.TypeBool, Optional: true, }, "ha_mode": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"RPO", "RTO"}, false), }, "instance_network_type": { Type: schema.TypeString, Optional: true, ForceNew: true, ValidateFunc: StringInSlice([]string{"Classic", "VPC"}, false), Computed: true, }, "maintain_time": { Type: schema.TypeString, Optional: true, Computed: true, }, "password": { Type: schema.TypeString, Optional: true, }, "payment_type": { Type: schema.TypeString, Required: true, ValidateFunc: StringInSlice([]string{"PayAsYouGo", "Subscription", "Serverless"}, false), }, "period": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"Month", "Year"}, false), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return d.Get("payment_type").(string) != "Subscription" }, }, "port": { Type: schema.TypeString, Optional: true, Computed: true, }, "private_ip_address": { Type: schema.TypeString, Optional: true, Computed: true, }, "private_key": { Type: schema.TypeString, Optional: true, }, "released_keep_policy": { Type: schema.TypeString, Optional: true, }, "replication_acl": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"cert", "perfer", "verify-ca", "verify-full"}, false), Computed: true, }, "resource_group_id": { Type: schema.TypeString, Optional: true, }, "restore_table": { Type: schema.TypeString, Optional: true, }, "restore_time": { Type: schema.TypeString, Optional: true, }, "role_arn": { Type: schema.TypeString, Optional: true, }, "security_ips": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, Optional: true, }, "server_cert": { Type: schema.TypeString, Optional: true, Computed: true, }, "server_key": { Type: schema.TypeString, Optional: true, Computed: true, }, "source_biz": { Type: schema.TypeString, Optional: true, }, "source_db_instance_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, "ssl_enabled": { Type: schema.TypeInt, ValidateFunc: IntInSlice([]int{0, 1}), Optional: true, Computed: true, }, "switch_time": { Type: schema.TypeString, Optional: true, }, "sync_mode": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"Async", "Semi-sync", "Sync"}, false), }, "tde_status": { Type: schema.TypeString, Optional: true, }, "table_meta": { Type: schema.TypeString, Optional: true, }, "used_time": { Type: schema.TypeInt, Optional: true, ValidateFunc: IntInSlice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 24, 36}), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return d.Get("payment_type").(string) != "Subscription" }, }, "vpc_id": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "vswitch_id": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { /* If it is not a new resource and is a multi region deployment, and the returned availability zone is consistent with the first input availability zone, then it needs to be suppressed. */ newArray := strings.Split(new, ",") if d.Id() != "" && len(newArray) > 1 && old == newArray[0] { return true } return false }, }, "zone_id": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "zone_id_slave_a": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "zone_id_slave_b": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "parameters": { Type: schema.TypeSet, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Required: true, }, "value": { Type: schema.TypeString, Required: true, }, }, }, Set: parameterToHash, Optional: true, Computed: true, }, "deletion_protection": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return d.Get("payment_type") != "PayAsYouGo" }, }, "tcp_connection_type": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"SHORT", "LONG"}, false), }, "serverless_config": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "max_capacity": { Type: schema.TypeFloat, Required: true, }, "min_capacity": { Type: schema.TypeFloat, Required: true, }, "auto_pause": { Type: schema.TypeBool, Optional: true, }, "switch_force": { Type: schema.TypeBool, Optional: true, }, }, }, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if v, ok := d.GetOk("payment_type"); ok && v.(string) != "Serverless" { return true } return false }, }, }, } } func resourceAlicloudRdsCloneDbInstanceCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) rdsService := RdsService{client} var response map[string]interface{} action := "CloneDBInstance" request := make(map[string]interface{}) request["SourceIp"] = client.SourceIp var err error if v, ok := d.GetOk("backup_id"); ok { request["BackupId"] = v } if v, ok := d.GetOk("backup_type"); ok { request["BackupType"] = v } if v, ok := d.GetOk("category"); ok { request["Category"] = v } if v, ok := d.GetOk("db_instance_class"); ok { request["DBInstanceClass"] = v } if v, ok := d.GetOk("db_instance_storage"); ok { request["DBInstanceStorage"] = v } request["DBInstanceStorageType"] = d.Get("db_instance_storage_type") if v, ok := d.GetOk("db_names"); ok { request["DbNames"] = v } if v, ok := d.GetOk("dedicated_host_group_id"); ok { request["DedicatedHostGroupId"] = v } if v, ok := d.GetOk("instance_network_type"); ok { request["InstanceNetworkType"] = v } request["PayType"] = convertRdsInstancePaymentTypeRequest(d.Get("payment_type")) if v, ok := d.GetOk("period"); ok { request["Period"] = v } if v, ok := d.GetOk("private_ip_address"); ok { request["PrivateIpAddress"] = v } request["RegionId"] = client.RegionId if v, ok := d.GetOk("restore_table"); ok { request["RestoreTable"] = v } if v, ok := d.GetOk("restore_time"); ok { request["RestoreTime"] = v } request["DBInstanceId"] = d.Get("source_db_instance_id") if v, ok := d.GetOk("table_meta"); ok { request["TableMeta"] = v } if v, ok := d.GetOk("used_time"); ok { request["UsedTime"] = v } if v, ok := d.GetOk("zone_id"); ok { request["ZoneId"] = v } if v, ok := d.GetOk("zone_id_slave_a"); ok { request["ZoneIdSlave1"] = v } if v, ok := d.GetOk("zone_id_slave_b"); ok { request["ZoneIdSlave2"] = v } if v, ok := d.GetOk("vpc_id"); ok { request["VPCId"] = v } if v, ok := d.GetOk("vswitch_id"); ok { request["VSwitchId"] = v } instance, err := rdsService.DescribeDBInstance(d.Get("source_db_instance_id").(string)) if err != nil { return WrapError(err) } if request["PayType"] == string(Serverless) { if v, ok := d.GetOk("serverless_config"); ok { v := v.([]interface{})[0].(map[string]interface{}) if string(MySQL) == instance["Engine"] || string(PostgreSQL) == instance["Engine"] { serverlessConfig, err := json.Marshal(struct { MaxCapacity float64 `json:"MaxCapacity"` MinCapacity float64 `json:"MinCapacity"` AutoPause bool `json:"AutoPause"` SwitchForce bool `json:"SwitchForce"` }{ v["max_capacity"].(float64), v["min_capacity"].(float64), v["auto_pause"].(bool), v["switch_force"].(bool), }) if err != nil { return WrapError(err) } request["ServerlessConfig"] = string(serverlessConfig) } else if string(SQLServer) == instance["Engine"] { serverlessConfig, err := json.Marshal(struct { MaxCapacity float64 `json:"MaxCapacity"` MinCapacity float64 `json:"MinCapacity"` }{ v["max_capacity"].(float64), v["min_capacity"].(float64), }) if err != nil { return WrapError(err) } request["ServerlessConfig"] = string(serverlessConfig) } } } wait := incrementalWait(3*time.Second, 5*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, request, false) if err != nil { if IsExpectedErrors(err, []string{"undefined"}) || NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, request) if err != nil { return WrapErrorf(err, DefaultErrorMsg, "alicloud_rds_clone_db_instance", action, AlibabaCloudSdkGoERROR) } d.SetId(fmt.Sprint(response["DBInstanceId"])) // wait instance status change from Creating to running stateConf := BuildStateConf([]string{"Creating"}, []string{"Running"}, d.Timeout(schema.TimeoutCreate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } return resourceAlicloudRdsCloneDbInstanceUpdate(d, meta) } func resourceAlicloudRdsCloneDbInstanceRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) rdsService := RdsService{client} object, err := rdsService.DescribeRdsCloneDbInstance(d.Id()) if err != nil { if NotFoundError(err) { log.Printf("[DEBUG] Resource alicloud_rds_clone_db_instance rdsService.DescribeRdsCloneDbInstance Failed!!! %s", err) d.SetId("") return nil } return WrapError(err) } d.Set("auto_upgrade_minor_version", object["AutoUpgradeMinorVersion"]) d.Set("category", object["Category"]) d.Set("db_instance_class", object["DBInstanceClass"]) d.Set("db_instance_description", object["DBInstanceDescription"]) if v, ok := object["DBInstanceStorage"]; ok && fmt.Sprint(v) != "0" { d.Set("db_instance_storage", formatInt(v)) } d.Set("db_instance_storage_type", object["DBInstanceStorageType"]) d.Set("dedicated_host_group_id", object["DedicatedHostGroupId"]) d.Set("engine", object["Engine"]) d.Set("engine_version", object["EngineVersion"]) d.Set("instance_network_type", object["InstanceNetworkType"]) d.Set("maintain_time", object["MaintainTime"]) d.Set("vswitch_id", object["VSwitchId"]) d.Set("zone_id", object["ZoneId"]) d.Set("vpc_id", object["VpcId"]) slaveZones := object["SlaveZones"].(map[string]interface{})["SlaveZone"].([]interface{}) if len(slaveZones) == 2 { d.Set("zone_id_slave_a", slaveZones[0].(map[string]interface{})["ZoneId"]) d.Set("zone_id_slave_b", slaveZones[1].(map[string]interface{})["ZoneId"]) } else if len(slaveZones) == 1 { d.Set("zone_id_slave_a", slaveZones[0].(map[string]interface{})["ZoneId"]) } payType := convertRdsInstancePaymentTypeResponse(object["PayType"]) d.Set("payment_type", payType) serverlessConfig := make([]map[string]interface{}, 0) slc := object["ServerlessConfig"].(map[string]interface{}) if payType == "Serverless" && (string(MySQL) == object["Engine"] || string(PostgreSQL) == object["Engine"]) { slcMaps := map[string]interface{}{ "max_capacity": slc["ScaleMax"], "min_capacity": slc["ScaleMin"], "auto_pause": slc["AutoPause"], "switch_force": slc["SwitchForce"], } serverlessConfig = append(serverlessConfig, slcMaps) d.Set("serverless_config", serverlessConfig) } else if payType == "Serverless" && string(SQLServer) == object["Engine"] { slcMaps := map[string]interface{}{ "max_capacity": slc["ScaleMax"], "min_capacity": slc["ScaleMin"], } serverlessConfig = append(serverlessConfig, slcMaps) d.Set("serverless_config", serverlessConfig) } d.Set("port", object["Port"]) d.Set("connection_string", object["ConnectionString"]) d.Set("deletion_protection", object["DeletionProtection"]) if err = rdsService.RefreshParameters(d, "parameters"); err != nil { return WrapError(err) } if object["Engine"].(string) == string(PostgreSQL) && object["DBInstanceStorageType"].(string) != "local_ssd" { if err = rdsService.RefreshPgHbaConf(d, "pg_hba_conf"); err != nil { return WrapError(err) } } describeDBInstanceHAConfigObject, err := rdsService.DescribeDBInstanceHAConfig(d.Id()) if err != nil { return WrapError(err) } d.Set("sync_mode", describeDBInstanceHAConfigObject["SyncMode"]) d.Set("ha_mode", describeDBInstanceHAConfigObject["HAMode"]) dbInstanceIpArrayName := "default" describeDBInstanceIPArrayListObject, err := rdsService.GetSecurityIps(d.Id(), dbInstanceIpArrayName) if err != nil { return WrapError(err) } d.Set("security_ips", describeDBInstanceIPArrayListObject) describeDBInstanceNetInfoObject, err := rdsService.DescribeDBInstanceNetInfo(d.Id()) if err != nil { return WrapError(err) } var privateIpAddress string for _, item := range describeDBInstanceNetInfoObject { ipType := item.(map[string]interface{})["IPType"] if ipType == "Private" { privateIpAddress = item.(map[string]interface{})["IPAddress"].(string) break } } d.Set("private_ip_address", privateIpAddress) describeDBInstanceSSLObject, err := rdsService.DescribeDBInstanceSSL(d.Id()) if err != nil { return WrapError(err) } d.Set("acl", describeDBInstanceSSLObject["ACL"]) d.Set("ca_type", describeDBInstanceSSLObject["CAType"]) d.Set("client_ca_cert", describeDBInstanceSSLObject["ClientCACert"]) d.Set("client_cert_revocation_list", describeDBInstanceSSLObject["ClientCertRevocationList"]) d.Set("replication_acl", describeDBInstanceSSLObject["ReplicationACL"]) d.Set("server_cert", describeDBInstanceSSLObject["ServerCert"]) d.Set("server_key", describeDBInstanceSSLObject["ServerKey"]) if v, ok := describeDBInstanceSSLObject["SSLEnabled"]; ok && v.(string) != "" { sslEnabled := 0 if v == "on" { sslEnabled = 1 } d.Set("ssl_enabled'", sslEnabled) } res, err := rdsService.DescribeHADiagnoseConfig(d.Id()) if err != nil { return WrapError(err) } d.Set("tcp_connection_type", res["TcpConnectionType"]) return nil } func resourceAlicloudRdsCloneDbInstanceUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) rdsService := RdsService{client} var response map[string]interface{} var err error d.Partial(true) if d.HasChange("parameters") { if err := rdsService.ModifyParameters(d, "parameters"); err != nil { return WrapError(err) } } if d.HasChange("pg_hba_conf") { err := rdsService.ModifyPgHbaConfig(d, "pg_hba_conf") if err != nil { return WrapError(err) } } if d.HasChange("deletion_protection") && d.Get("payment_type") == "PayAsYouGo" { err := rdsService.ModifyDBInstanceDeletionProtection(d, "deletion_protection") if err != nil { return WrapError(err) } } if d.HasChange("tcp_connection_type") { err := rdsService.ModifyHADiagnoseConfig(d, "tcp_connection_type") if err != nil { return WrapError(err) } } update := false request := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("auto_upgrade_minor_version") { update = true } if v, ok := d.GetOk("auto_upgrade_minor_version"); ok { request["AutoUpgradeMinorVersion"] = v } if update { action := "ModifyDBInstanceAutoUpgradeMinorVersion" request["ClientToken"] = buildClientToken("ModifyDBInstanceAutoUpgradeMinorVersion") wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, request, true) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, request) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } d.SetPartial("auto_upgrade_minor_version") } update = false modifyDBInstanceDescriptionReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("db_instance_description") { update = true } if v, ok := d.GetOk("db_instance_description"); ok { modifyDBInstanceDescriptionReq["DBInstanceDescription"] = v } if update { action := "ModifyDBInstanceDescription" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceDescriptionReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceDescriptionReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } d.SetPartial("db_instance_description") } update = false modifyDBInstanceMaintainTimeReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("maintain_time") { update = true } if v, ok := d.GetOk("maintain_time"); ok { modifyDBInstanceMaintainTimeReq["MaintainTime"] = v } if update { action := "ModifyDBInstanceMaintainTime" var err error request["ClientToken"] = buildClientToken("ModifyDBInstanceMaintainTime") wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceMaintainTimeReq, true) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceMaintainTimeReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } d.SetPartial("maintain_time") } update = false modifyDBInstanceHAConfigReq := map[string]interface{}{ "DbInstanceId": d.Id(), } if d.HasChange("sync_mode") { update = true } if d.HasChange("ha_mode") { update = true } if update { if v, ok := d.GetOk("sync_mode"); ok { modifyDBInstanceHAConfigReq["SyncMode"] = v } if v, ok := d.GetOk("ha_mode"); ok { modifyDBInstanceHAConfigReq["HAMode"] = v } action := "ModifyDBInstanceHAConfig" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceHAConfigReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceHAConfigReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } d.SetPartial("sync_mode") } update = false switchDBInstanceVpcReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if !d.IsNewResource() && d.HasChange("private_ip_address") { update = true if v, ok := d.GetOk("private_ip_address"); ok { switchDBInstanceVpcReq["PrivateIpAddress"] = v } } if !d.IsNewResource() && d.HasChange("vpc_id") { update = true if v, ok := d.GetOk("vpc_id"); ok { switchDBInstanceVpcReq["VPCId"] = v } } if !d.IsNewResource() && d.HasChange("vswitch_id") { update = true if v, ok := d.GetOk("vswitch_id"); ok { switchDBInstanceVpcReq["VSwitchId"] = v } } if update { action := "SwitchDBInstanceVpc" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, switchDBInstanceVpcReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, switchDBInstanceVpcReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } // wait instance status change from Creating to running stateConf := BuildStateConf([]string{}, []string{"Running"}, d.Timeout(schema.TimeoutUpdate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } d.SetPartial("private_ip_address") d.SetPartial("vpc_id") d.SetPartial("vswitch_id") } update = false modifyDBInstanceConnectionStringReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("port") { update = true } if d.HasChange("connection_string_prefix") { update = true } if v, ok := d.GetOk("connection_string"); ok { modifyDBInstanceConnectionStringReq["CurrentConnectionString"] = v } if update { if v, ok := d.GetOk("connection_string_prefix"); ok { modifyDBInstanceConnectionStringReq["ConnectionStringPrefix"] = v } if v, ok := d.GetOk("port"); ok { modifyDBInstanceConnectionStringReq["Port"] = v } action := "ModifyDBInstanceConnectionString" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceConnectionStringReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceConnectionStringReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } // wait instance status change from Creating to running stateConf := BuildStateConf([]string{}, []string{"Running"}, d.Timeout(schema.TimeoutUpdate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } d.SetPartial("connection_string") } update = false modifyDBInstanceTDEReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("encryption_key") { update = true if v, ok := d.GetOk("encryption_key"); ok { modifyDBInstanceTDEReq["EncryptionKey"] = v } } if update { if v, ok := d.GetOk("tde_status"); ok { modifyDBInstanceTDEReq["TDEStatus"] = v } if v, ok := d.GetOk("certificate"); ok { modifyDBInstanceTDEReq["Certificate"] = v } if v, ok := d.GetOk("db_name"); ok { modifyDBInstanceTDEReq["DBName"] = v } if v, ok := d.GetOk("password"); ok { modifyDBInstanceTDEReq["PassWord"] = v } if v, ok := d.GetOk("private_key"); ok { modifyDBInstanceTDEReq["PrivateKey"] = v } if v, ok := d.GetOk("role_arn"); ok { modifyDBInstanceTDEReq["RoleArn"] = v } action := "ModifyDBInstanceTDE" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceTDEReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceTDEReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } // wait instance status change from Creating to running stateConf := BuildStateConf([]string{}, []string{"Running"}, d.Timeout(schema.TimeoutUpdate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } d.SetPartial("encryption_key") } update = false modifySecurityIpsReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if d.HasChange("security_ips") { update = true ipList := expandStringList(d.Get("security_ips").(*schema.Set).List()) ipstr := strings.Join(ipList[:], COMMA_SEPARATED) // default disable connect from outside if ipstr == "" { ipstr = LOCAL_HOST_IP } modifySecurityIpsReq["SecurityIps"] = ipstr } if update { action := "ModifySecurityIps" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifySecurityIpsReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifySecurityIpsReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } d.SetPartial("security_ips") } update = false modifyDBInstanceSSLReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if v, ok := d.GetOk("connection_string"); ok { modifyDBInstanceSSLReq["ConnectionString"] = v } if d.HasChange("acl") { update = true if v, ok := d.GetOk("acl"); ok { modifyDBInstanceSSLReq["ACL"] = v } } if d.HasChange("ca_type") { update = true if v, ok := d.GetOk("ca_type"); ok { modifyDBInstanceSSLReq["CAType"] = v } } if d.HasChange("client_ca_cert") { update = true if v, ok := d.GetOk("client_ca_cert"); ok { modifyDBInstanceSSLReq["ClientCACert"] = v } } if d.HasChange("client_cert_revocation_list") { update = true if v, ok := d.GetOk("client_cert_revocation_list"); ok { modifyDBInstanceSSLReq["ClientCertRevocationList"] = v } } if d.HasChange("replication_acl") { update = true if v, ok := d.GetOk("replication_acl"); ok { modifyDBInstanceSSLReq["ReplicationACL"] = v } } if d.HasChange("server_cert") { update = true if v, ok := d.GetOk("server_cert"); ok { modifyDBInstanceSSLReq["ServerCert"] = v } } if d.HasChange("server_key") { update = true if v, ok := d.GetOk("server_key"); ok { modifyDBInstanceSSLReq["ServerKey"] = v } } if d.HasChange("client_ca_enabled") { update = true if v, ok := d.GetOk("client_ca_enabled"); ok { modifyDBInstanceSSLReq["ClientCAEnabled"] = v } } if d.HasChange("client_crl_enabled") { update = true if v, ok := d.GetOk("client_crl_enabled"); ok { modifyDBInstanceSSLReq["ClientCrlEnabled"] = v } } if d.HasChange("ssl_enabled") { update = true if v, ok := d.GetOk("ssl_enabled"); ok { modifyDBInstanceSSLReq["SSLEnabled"] = v } } if update { action := "ModifyDBInstanceSSL" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceSSLReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceSSLReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } // wait instance status change from Creating to running stateConf := BuildStateConf([]string{}, []string{"Running"}, d.Timeout(schema.TimeoutUpdate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } d.SetPartial("connection_string") d.SetPartial("acl") d.SetPartial("ca_type") d.SetPartial("client_ca_cert") d.SetPartial("client_cert_revocation_list") d.SetPartial("replication_acl") d.SetPartial("server_cert") d.SetPartial("server_key") d.SetPartial("ssl_enabled") } update = false modifyDBInstanceSpecReq := map[string]interface{}{ "DBInstanceId": d.Id(), } if !d.IsNewResource() && d.HasChange("payment_type") { update = true } if v, ok := d.GetOk("payment_type"); ok { modifyDBInstanceSpecReq["PayType"] = convertRdsInstancePaymentTypeRequest(v) } if !d.IsNewResource() && d.HasChange("db_instance_class") { update = true if v, ok := d.GetOk("db_instance_class"); ok { modifyDBInstanceSpecReq["DBInstanceClass"] = v } } if !d.IsNewResource() && d.HasChange("db_instance_storage") { update = true if v, ok := d.GetOk("db_instance_storage"); ok { modifyDBInstanceSpecReq["DBInstanceStorage"] = v } } if !d.IsNewResource() && d.HasChange("db_instance_storage_type") { update = true modifyDBInstanceSpecReq["DBInstanceStorageType"] = d.Get("db_instance_storage_type") } if !d.IsNewResource() && d.HasChange("dedicated_host_group_id") { update = true if v, ok := d.GetOk("dedicated_host_group_id"); ok { modifyDBInstanceSpecReq["DedicatedHostGroupId"] = v } } if d.HasChange("engine_version") { update = true if v, ok := d.GetOk("engine_version"); ok { modifyDBInstanceSpecReq["EngineVersion"] = v } } if !d.IsNewResource() && d.HasChange("zone_id") { update = true if v, ok := d.GetOk("zone_id"); ok { modifyDBInstanceSpecReq["ZoneId"] = v } } instance, err := rdsService.DescribeDBInstance(d.Id()) if err != nil { return WrapError(err) } if d.HasChange("serverless_config") { update = true if v, ok := d.GetOk("serverless_config"); ok { if string(MySQL) == instance["Engine"] || string(PostgreSQL) == instance["Engine"] { v := v.([]interface{})[0].(map[string]interface{}) serverlessConfig, err := json.Marshal(struct { MaxCapacity float64 `json:"MaxCapacity"` MinCapacity float64 `json:"MinCapacity"` AutoPause bool `json:"AutoPause"` SwitchForce bool `json:"SwitchForce"` }{ v["max_capacity"].(float64), v["min_capacity"].(float64), v["auto_pause"].(bool), v["switch_force"].(bool), }) if err != nil { return WrapError(err) } if category, ok := d.GetOk("Category"); ok { modifyDBInstanceSpecReq["Category"] = category } modifyDBInstanceSpecReq["Direction"] = "Serverless" modifyDBInstanceSpecReq["ServerlessConfiguration"] = string(serverlessConfig) } else if string(SQLServer) == instance["Engine"] { v := v.([]interface{})[0].(map[string]interface{}) serverlessConfig, err := json.Marshal(struct { MaxCapacity float64 `json:"MaxCapacity"` MinCapacity float64 `json:"MinCapacity"` }{ v["max_capacity"].(float64), v["min_capacity"].(float64), }) if err != nil { return WrapError(err) } if category, ok := d.GetOk("Category"); ok { modifyDBInstanceSpecReq["Category"] = category } modifyDBInstanceSpecReq["Direction"] = "Serverless" modifyDBInstanceSpecReq["ServerlessConfiguration"] = string(serverlessConfig) } } } if update { if v, ok := d.GetOk("direction"); ok { modifyDBInstanceSpecReq["Direction"] = v } if v, ok := d.GetOk("effective_time"); ok { modifyDBInstanceSpecReq["EffectiveTime"] = v } if v, ok := d.GetOk("resource_group_id"); ok { modifyDBInstanceSpecReq["ResourceGroupId"] = v } if v, ok := d.GetOk("source_biz"); ok { modifyDBInstanceSpecReq["SourceBiz"] = v } if v, ok := d.GetOk("switch_time"); ok { modifyDBInstanceSpecReq["SwitchTime"] = v } if v, ok := d.GetOk("used_time"); ok { modifyDBInstanceSpecReq["UsedTime"] = v } action := "ModifyDBInstanceSpec" wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, modifyDBInstanceSpecReq, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, modifyDBInstanceSpecReq) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } // wait instance status change from Creating to running stateConf := BuildStateConf([]string{}, []string{"Running"}, d.Timeout(schema.TimeoutUpdate), 3*time.Minute, rdsService.RdsDBInstanceStateRefreshFunc(d.Id(), []string{"Deleting"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } d.SetPartial("db_instance_class") d.SetPartial("db_instance_storage") d.SetPartial("db_instance_storage_type") d.SetPartial("dedicated_host_group_id") d.SetPartial("engine_version") d.SetPartial("zone_id") } d.Partial(false) return resourceAlicloudRdsCloneDbInstanceRead(d, meta) } func resourceAlicloudRdsCloneDbInstanceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) action := "DeleteDBInstance" var response map[string]interface{} var err error request := map[string]interface{}{ "DBInstanceId": d.Id(), } request[""] = client.RegionId if v, ok := d.GetOk("released_keep_policy"); ok { request["ReleasedKeepPolicy"] = v } wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { response, err = client.RpcPost("Rds", "2014-08-15", action, nil, request, false) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) addDebug(action, response, request) if err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) } return nil } func convertRdsInstancePaymentTypeRequest(source interface{}) interface{} { switch source { case "PayAsYouGo": return "Postpaid" case "Subscription": return "Prepaid" case "Serverless": return "Serverless" } return source } func convertRdsInstancePaymentTypeResponse(source interface{}) interface{} { switch source { case "Postpaid": return "PayAsYouGo" case "Prepaid": return "Subscription" case "SERVERLESS": return "Serverless" } return source }