diff --git a/go.sum b/go.sum index d248f038c5..3aec188fc5 100644 --- a/go.sum +++ b/go.sum @@ -513,6 +513,7 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.370 h1:FlmN github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.370/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.376 h1:pXsCDParuAiDUt/SuDBeJ0Fv90kItYMbk37W1cMVHck= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.376/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.377/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.378 h1:o+GKr3tHDivmoWxBXX5oeJC8EmmNV64g/yw5s0RjBdg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.378/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.199 h1:ajgJogYSIQ5u1PIbiV5nsvr5K0fYpm1/T7Dy+mxEM6U= diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 50a2a6846a..094a7a3ab2 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -414,6 +414,9 @@ PostgreSQL Resource tencentcloud_postgresql_instance + tencentcloud_postgresql_readonly_instance + tencentcloud_postgresql_readonly_group + tencentcloud_postgresql_readonly_attachment Redis Data Source @@ -1029,6 +1032,9 @@ func Provider() terraform.ResourceProvider { "tencentcloud_mongodb_standby_instance": resourceTencentCloudMongodbStandbyInstance(), "tencentcloud_elasticsearch_instance": resourceTencentCloudElasticsearchInstance(), "tencentcloud_postgresql_instance": resourceTencentCloudPostgresqlInstance(), + "tencentcloud_postgresql_readonly_instance": resourceTencentCloudPostgresqlReadonlyInstance(), + "tencentcloud_postgresql_readonly_group": resourceTencentCloudPostgresqlReadonlyGroup(), + "tencentcloud_postgresql_readonly_attachment": resourceTencentCloudPostgresqlReadonlyAttachment(), "tencentcloud_sqlserver_instance": resourceTencentCloudSqlserverInstance(), "tencentcloud_sqlserver_db": resourceTencentCloudSqlserverDB(), "tencentcloud_sqlserver_account": resourceTencentCloudSqlserverAccount(), diff --git a/tencentcloud/resource_tc_postgresql_readonly_attachment.go b/tencentcloud/resource_tc_postgresql_readonly_attachment.go new file mode 100644 index 0000000000..65050d784c --- /dev/null +++ b/tencentcloud/resource_tc_postgresql_readonly_attachment.go @@ -0,0 +1,137 @@ +/* +Use this resource to create postgresql readonly attachment. + +Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_attachment" "attach" { + db_instance_id = tencentcloud_postgresql_readonly_instance.foo.id + read_only_group_id = tencentcloud_postgresql_readonly_group.group.id +} +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + postgresql "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/postgres/v20170312" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudPostgresqlReadonlyAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudPostgresqlReadOnlyAttachmentCreate, + Read: resourceTencentCloudPostgresqlReadOnlyAttachmentRead, + //Update: resourceTencentCloudPostgresqlReadOnlyAttachmentUpdate, + Delete: resourceTencentCLoudPostgresqlReadOnlyAttachmentDelete, + //Importer: &schema.ResourceImporter{ + // State: schema.ImportStatePassthrough, + //}, + + Schema: map[string]*schema.Schema{ + "db_instance_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "Read only instance ID.", + }, + "read_only_group_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "Read only group ID.", + }, + }, + } +} + +func resourceTencentCloudPostgresqlReadOnlyAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_attachment.create")() + + logId := getLogId(contextNil) + + var ( + request = postgresql.NewAddDBInstanceToReadOnlyGroupRequest() + dbInstanceId string + groupId string + ) + if v, ok := d.GetOk("db_instance_id"); ok { + dbInstanceId = v.(string) + request.DBInstanceId = helper.String(dbInstanceId) + + } + if v, ok := d.GetOk("read_only_group_id"); ok { + groupId = v.(string) + request.ReadOnlyGroupId = helper.String(groupId) + } + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UsePostgresqlClient().AddDBInstanceToReadOnlyGroup(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + return nil + }) + if err != nil { + return err + } + instanceId := helper.IdFormat(dbInstanceId, groupId) + d.SetId(instanceId) + + return nil +} + +func resourceTencentCloudPostgresqlReadOnlyAttachmentRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_attachment.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + _, err := postgresqlService.DescribePostgresqlReadOnlyGroupById(ctx, d.Id()) + if err != nil { + return err + } + + return nil +} + +func resourceTencentCLoudPostgresqlReadOnlyAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_attachment.delete")() + + logId := getLogId(contextNil) + request := postgresql.NewRemoveDBInstanceFromReadOnlyGroupRequest() + + idSplit := strings.Split(d.Id(), FILED_SP) + if len(idSplit) != 2 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + dbInstanceId := idSplit[0] + groupId := idSplit[1] + request.ReadOnlyGroupId = helper.String(groupId) + request.DBInstanceId = helper.String(dbInstanceId) + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UsePostgresqlClient().RemoveDBInstanceFromReadOnlyGroup(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + return nil + }) + if err != nil { + return err + } + return nil +} diff --git a/tencentcloud/resource_tc_postgresql_readonly_group.go b/tencentcloud/resource_tc_postgresql_readonly_group.go new file mode 100644 index 0000000000..25048109b7 --- /dev/null +++ b/tencentcloud/resource_tc_postgresql_readonly_group.go @@ -0,0 +1,269 @@ +/* +Use this resource to create postgresql readonly group. + +Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_group" "group" { + master_db_instance_id = "postgres-f44wlfdv" + name = "world" + project_id = 0 + vpc_id = "vpc-86v957zb" + subnet_id = "subnet-enm92y0m" + replay_lag_eliminate = 1 + replay_latency_eliminate = 1 + max_replay_lag = 100 + max_replay_latency = 512 + min_delay_eliminate_reserve = 1 +# security_groups_ids = [] +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + postgresql "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/postgres/v20170312" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudPostgresqlReadonlyGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudPostgresqlReadOnlyGroupCreate, + Read: resourceTencentCloudPostgresqlReadOnlyGroupRead, + Update: resourceTencentCloudPostgresqlReadOnlyGroupUpdate, + Delete: resourceTencentCLoudPostgresqlReadOnlyGroupDelete, + //Importer: &schema.ResourceImporter{ + // State: schema.ImportStatePassthrough, + //}, + + Schema: map[string]*schema.Schema{ + "master_db_instance_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "Primary instance ID.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "RO group name.", + }, + "project_id": { + Type: schema.TypeInt, + Required: true, + Description: "Project ID.", + }, + "vpc_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "VPC ID.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + Description: "VPC subnet ID.", + }, + "replay_lag_eliminate": { + Type: schema.TypeInt, + Required: true, + Description: "Whether to remove a read-only replica from an RO group if the delay between the read-only replica " + + "and the primary instance exceeds the threshold. Valid values: 0 (no), 1 (yes).", + }, + "replay_latency_eliminate": { + Type: schema.TypeInt, + Required: true, + Description: "Whether to remove a read-only replica from an RO group if the sync log size difference between " + + "the read-only replica and the primary instance exceeds the threshold. Valid values: 0 (no), 1 (yes).", + }, + "max_replay_lag": { + Type: schema.TypeInt, + Required: true, + Description: "Delay threshold in ms.", + }, + "max_replay_latency": { + Type: schema.TypeInt, + Required: true, + Description: "Delayed log size threshold in MB.", + }, + "min_delay_eliminate_reserve": { + Type: schema.TypeInt, + Required: true, + Description: "The minimum number of read-only replicas that must be retained in an RO group.", + }, + "security_groups_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "ID of security group. If both vpc_id and subnet_id are not set, this argument should not be set either.", + }, + // Computed values + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the postgresql instance.", + }, + }, + } +} + +func resourceTencentCloudPostgresqlReadOnlyGroupCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_group.create")() + + logId := getLogId(contextNil) + + var ( + request = postgresql.NewCreateReadOnlyGroupRequest() + response *postgresql.CreateReadOnlyGroupResponse + ) + if v, ok := d.GetOk("master_db_instance_id"); ok { + request.MasterDBInstanceId = helper.String(v.(string)) + } + if v, ok := d.GetOk("name"); ok { + request.Name = helper.String(v.(string)) + } + if v, ok := d.GetOk("project_id"); ok { + request.ProjectId = helper.IntUint64(v.(int)) + } + if v, ok := d.GetOk("vpc_id"); ok { + request.VpcId = helper.String(v.(string)) + } + if v, ok := d.GetOk("subnet_id"); ok { + request.SubnetId = helper.String(v.(string)) + } + + if v, ok := d.GetOk("replay_lag_eliminate"); ok { + request.ReplayLagEliminate = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("replay_latency_eliminate"); ok { + request.ReplayLatencyEliminate = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("max_replay_lag"); ok { + request.MaxReplayLag = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("max_replay_latency"); ok { + request.MaxReplayLatency = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("min_delay_eliminate_reserve"); ok { + request.MinDelayEliminateReserve = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("security_groups_ids"); ok { + securityGroupsIds := v.(*schema.Set).List() + request.SecurityGroupIds = make([]*string, 0, len(securityGroupsIds)) + for _, item := range securityGroupsIds { + request.SecurityGroupIds = append(request.SecurityGroupIds, helper.String(item.(string))) + } + } + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UsePostgresqlClient().CreateReadOnlyGroup(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + response = result + return nil + }) + if err != nil { + return err + } + instanceId := *response.Response.ReadOnlyGroupId + d.SetId(instanceId) + + //return resourceTencentCloudPostgresqlReadOnlyGroupRead(d, meta) + return nil +} + +func resourceTencentCloudPostgresqlReadOnlyGroupRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_group.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + _, err := postgresqlService.DescribePostgresqlReadOnlyGroupById(ctx, d.Id()) + if err != nil { + return err + } + + return nil +} + +func resourceTencentCloudPostgresqlReadOnlyGroupUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_group.update")() + + logId := getLogId(contextNil) + request := postgresql.NewModifyReadOnlyGroupConfigRequest() + + request.ReadOnlyGroupId = helper.String(d.Id()) + + if d.HasChange("name") { + request.ReadOnlyGroupName = helper.String(d.Get("name").(string)) + } + if d.HasChange("replay_lag_eliminate") { + request.ReplayLagEliminate = helper.IntUint64(d.Get("replay_lag_eliminate").(int)) + } + if d.HasChange("replay_latency_eliminate") { + request.ReplayLatencyEliminate = helper.IntUint64(d.Get("replay_latency_eliminate").(int)) + } + if d.HasChange("max_replay_lag") { + request.MaxReplayLag = helper.IntUint64(d.Get("max_replay_lag").(int)) + } + if d.HasChange("max_replay_latency") { + request.MaxReplayLatency = helper.IntUint64(d.Get("max_replay_latency").(int)) + } + if d.HasChange("min_delay_eliminate_reserve") { + request.MinDelayEliminateReserve = helper.IntUint64(d.Get("min_delay_eliminate_reserve").(int)) + } + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UsePostgresqlClient().ModifyReadOnlyGroupConfig(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + return nil + }) + + if err != nil { + return err + } + return nil +} + +func resourceTencentCLoudPostgresqlReadOnlyGroupDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_group.delete")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + groupId := d.Id() + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + e := postgresqlService.DeletePostgresqlReadOnlyGroupById(ctx, groupId) + if e != nil { + return retryError(e) + } + return nil + }) + if err != nil { + return err + } + + return nil +} diff --git a/tencentcloud/resource_tc_postgresql_readonly_instance.go b/tencentcloud/resource_tc_postgresql_readonly_instance.go new file mode 100644 index 0000000000..2cbed79c2c --- /dev/null +++ b/tencentcloud/resource_tc_postgresql_readonly_instance.go @@ -0,0 +1,512 @@ +/* +Use this resource to create postgresql readonly instance. + +Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_instance" "foo" { + auto_renew_flag = 0 + db_version = "10.4" + instance_charge_type = "POSTPAID_BY_HOUR" + master_db_instance_id = "postgres-j4pm65id" + memory = 4 + name = "hello" + need_support_ipv6 = 0 + project_id = 0 + security_groups_ids = [ + "sg-fefj5n6r", + ] + storage = 250 + subnet_id = "subnet-enm92y0m" + vpc_id = "vpc-86v957zb" + zone = "ap-guangzhou-6" +} +``` + +Import + +postgresql readonly instance can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_postgresql_readonly_instance.foo pgro-bcqx8b9a +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + "strings" + + postgresql "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/postgres/v20170312" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudPostgresqlReadonlyInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudPostgresqlReadOnlyInstanceCreate, + Read: resourceTencentCloudPostgresqlReadOnlyInstanceRead, + Update: resourceTencentCloudPostgresqlReadOnlyInstanceUpdate, + Delete: resourceTencentCLoudPostgresqlReadOnlyInstanceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "db_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "PostgreSQL kernel version, which must be the same as that of the primary instance.", + }, + "storage": { + Type: schema.TypeInt, + Required: true, + Description: "Instance storage capacity in GB.", + }, + "memory": { + Type: schema.TypeInt, + Required: true, + Description: "Memory size(in GB). Allowed value must be larger than `memory` that data source `tencentcloud_postgresql_specinfos` provides.", + }, + "master_db_instance_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "ID of the primary instance to which the read-only replica belongs.", + }, + "zone": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "Availability zone ID, which can be obtained through the Zone field in the returned value of the DescribeZones API.", + }, + "project_id": { + Type: schema.TypeInt, + Required: true, + Description: "Project ID.", + }, + "vpc_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + Description: "VPC ID.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + Description: "VPC subnet ID.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Instance name.", + }, + "security_groups_ids": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "ID of security group.", + }, + "instance_charge_type": { + Type: schema.TypeString, + Optional: true, + Default: COMMON_PAYTYPE_POSTPAID, + ForceNew: true, + ValidateFunc: validateAllowedStringValue(POSTGRESQL_PAYTYPE), + Description: "instance billing mode. Valid values: PREPAID (monthly subscription), POSTPAID_BY_HOUR (pay-as-you-go).", + }, + "auto_renew_flag": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ForceNew: true, + Description: "Renewal flag. Valid values: 0 (manual renewal), 1 (auto-renewal). Default value: 0.", + }, + "need_support_ipv6": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Description: "Whether to support IPv6 address access. Valid values: 1 (yes), 0 (no).", + }, + //"tag_list": { + // Type: schema.TypeMap, + // Optional: true, + // Description: "The information of tags to be associated with instances. This parameter is left empty by default..", + //}, + //"read_only_group_id": { + // Type: schema.TypeString, + // ForceNew: true, + // Optional: true, + // Description: "RO group ID.", + //}, + // Computed values + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the postgresql instance.", + }, + }, + } +} + +func resourceTencentCloudPostgresqlReadOnlyInstanceCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_instance.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + var ( + request = postgresql.NewCreateReadOnlyDBInstanceRequest() + response *postgresql.CreateReadOnlyDBInstanceResponse + postgresqlService = PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + zone string + dbVersion string + memory int + ) + if v, ok := d.GetOk("db_version"); ok { + dbVersion = v.(string) + request.DBVersion = helper.String(dbVersion) + } + if v, ok := d.GetOk("storage"); ok { + request.Storage = helper.IntUint64(v.(int)) + } + if v, ok := d.GetOk("memory"); ok { + memory = v.(int) + } + if v, ok := d.GetOk("master_db_instance_id"); ok { + request.MasterDBInstanceId = helper.String(v.(string)) + } + if v, ok := d.GetOk("zone"); ok { + zone = v.(string) + request.Zone = helper.String(zone) + } + if v, ok := d.GetOk("project_id"); ok { + request.ProjectId = helper.IntUint64(v.(int)) + } + if v, ok := d.GetOk("instance_charge_type"); ok { + request.InstanceChargeType = helper.String(v.(string)) + } + if v, ok := d.GetOk("auto_renew_flag"); ok { + request.AutoRenewFlag = helper.IntInt64(v.(int)) + } + if v, ok := d.GetOk("vpc_id"); ok { + request.VpcId = helper.String(v.(string)) + } + if v, ok := d.GetOk("subnet_id"); ok { + request.SubnetId = helper.String(v.(string)) + } + if v, ok := d.GetOk("name"); ok { + request.Name = helper.String(v.(string)) + } + if v, ok := d.GetOk("need_support_ipv6"); ok { + request.NeedSupportIpv6 = helper.IntUint64(v.(int)) + } + if v, ok := d.GetOk("read_only_group_id"); ok { + request.ReadOnlyGroupId = helper.String(v.(string)) + } + if v, ok := d.GetOk("security_groups_ids"); ok { + securityGroupsIds := v.(*schema.Set).List() + request.SecurityGroupIds = make([]*string, 0, len(securityGroupsIds)) + for _, item := range securityGroupsIds { + request.SecurityGroupIds = append(request.SecurityGroupIds, helper.String(item.(string))) + } + } + //if tags := helper.GetTags(d, "tag_list"); len(tags) > 0 { + // for k, v := range tags { + // request.TagList = &postgresql.Tag{ + // TagKey: &k, + // TagValue: &v, + // } + // } + //} + + // get specCode with db_version and memory + var allowVersion, allowMemory []string + var specVersion, specCode string + err := resource.Retry(readRetryTimeout*5, func() *resource.RetryError { + speccodes, inErr := postgresqlService.DescribeSpecinfos(ctx, zone) + if inErr != nil { + return retryError(inErr) + } + for _, info := range speccodes { + if !IsContains(allowVersion, *info.Version) { + allowVersion = append(allowVersion, *info.Version) + } + if *info.Version == dbVersion { + specVersion = *info.Version + memoryString := fmt.Sprintf("%d", int(*info.Memory)/1024) + if !IsContains(allowMemory, memoryString) { + allowMemory = append(allowMemory, memoryString) + } + if int(*info.Memory)/1024 == memory { + specCode = *info.SpecCode + break + } + } + } + return nil + }) + if err != nil { + return err + } + if specVersion == "" { + return fmt.Errorf(`The "db_version" value: "%s" is invalid, Valid values are one of: "%s"`, dbVersion, strings.Join(allowVersion, `", "`)) + } + if specCode == "" { + return fmt.Errorf(`The "storage" value: %d is invalid, Valid values are one of: %s`, memory, strings.Join(allowMemory, `, `)) + } + request.SpecCode = helper.String(specCode) + + request.InstanceCount = helper.IntUint64(1) + request.Period = helper.IntUint64(1) + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UsePostgresqlClient().CreateReadOnlyDBInstance(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + response = result + return nil + + }) + if err != nil { + return err + } + instanceId := *response.Response.DBInstanceIdSet[0] + d.SetId(instanceId) + + // check creation done + err = resource.Retry(5*readRetryTimeout, func() *resource.RetryError { + instance, has, err := postgresqlService.DescribePostgresqlInstanceById(ctx, instanceId) + if err != nil { + return retryError(err) + } else if has && *instance.DBInstanceStatus == "running" { + return nil + } else if !has { + return resource.NonRetryableError(fmt.Errorf("create postgresql instance fail")) + } else { + return resource.RetryableError(fmt.Errorf("creating readonly postgresql instance %s , status %s ", instanceId, *instance.DBInstanceStatus)) + } + }) + + if err != nil { + return err + } + + return resourceTencentCloudPostgresqlReadOnlyInstanceRead(d, meta) +} + +func resourceTencentCloudPostgresqlReadOnlyInstanceRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_instance.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + instance, has, err := postgresqlService.DescribePostgresqlInstanceById(ctx, d.Id()) + if err != nil { + return err + } + if !has { + d.SetId("") + return nil + } + + _ = d.Set("db_version", instance.DBVersion) + _ = d.Set("storage", instance.DBInstanceStorage) + _ = d.Set("memory", instance.DBInstanceMemory) + _ = d.Set("master_db_instance_id", instance.MasterDBInstanceId) + _ = d.Set("zone", instance.Zone) + _ = d.Set("project_id", instance.ProjectId) + + if *instance.PayType == POSTGRESQL_PAYTYPE_PREPAID || *instance.PayType == COMMON_PAYTYPE_PREPAID { + _ = d.Set("instance_charge_type", COMMON_PAYTYPE_PREPAID) + } else { + _ = d.Set("instance_charge_type", COMMON_PAYTYPE_POSTPAID) + } + + _ = d.Set("auto_renew_flag", instance.AutoRenew) + _ = d.Set("vpc_id", instance.VpcId) + _ = d.Set("subnet_id", instance.SubnetId) + _ = d.Set("name", instance.DBInstanceName) + _ = d.Set("need_support_ipv6", instance.SupportIpv6) + + // security groups + // Only redis service support modify Generic DB instance security groups + redisService := RedisService{client: meta.(*TencentCloudClient).apiV3Conn} + sg, err := redisService.DescribeDBSecurityGroups(ctx, "postgres", d.Id()) + if err != nil { + return err + } + if len(sg) > 0 { + _ = d.Set("security_groups_ids", sg) + } + + //tags := make(map[string]string, len(instance.TagList)) + //for _, tag := range instance.TagList { + // tags[*tag.TagKey] = *tag.TagValue + //} + //_ = d.Set("tag_list", tags) + + // computed + _ = d.Set("create_time", instance.CreateTime) + _ = d.Set("status", instance.DBInstanceStatus) + + return nil +} + +func resourceTencentCloudPostgresqlReadOnlyInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_instance.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + instanceId := d.Id() + d.Partial(true) + + var outErr, inErr, checkErr error + // update name + if d.HasChange("name") { + name := d.Get("name").(string) + outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + inErr = postgresqlService.ModifyPostgresqlInstanceName(ctx, instanceId, name) + if inErr != nil { + return retryError(inErr) + } + return nil + }) + if outErr != nil { + return outErr + } + // check update name done + checkErr = postgresqlService.CheckDBInstanceStatus(ctx, instanceId) + if checkErr != nil { + return checkErr + } + d.SetPartial("name") + } + + // upgrade storage and memory size + if d.HasChange("memory") || d.HasChange("storage") { + memory := d.Get("memory").(int) + storage := d.Get("storage").(int) + outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + inErr = postgresqlService.UpgradePostgresqlInstance(ctx, instanceId, memory, storage) + if inErr != nil { + return retryError(inErr) + } + return nil + }) + if outErr != nil { + return outErr + } + // check update storage and memory done + checkErr = postgresqlService.CheckDBInstanceStatus(ctx, instanceId) + if checkErr != nil { + return checkErr + } + d.SetPartial("memory") + d.SetPartial("storage") + } + + // update project id + if d.HasChange("project_id") { + projectId := d.Get("project_id").(int) + outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + inErr = postgresqlService.ModifyPostgresqlInstanceProjectId(ctx, instanceId, projectId) + if inErr != nil { + return retryError(inErr) + } + return nil + }) + if outErr != nil { + return outErr + } + + // check update project id done + checkErr = postgresqlService.CheckDBInstanceStatus(ctx, instanceId) + if checkErr != nil { + return checkErr + } + d.SetPartial("project_id") + } + + if d.HasChange("security_groups_ids") { + + // Only redis service support modify Generic DB instance security groups + service := RedisService{client: meta.(*TencentCloudClient).apiV3Conn} + ids := d.Get("security_groups_ids").(*schema.Set).List() + var sgIds []*string + for _, id := range ids { + sgIds = append(sgIds, helper.String(id.(string))) + } + err := service.ModifyDBInstanceSecurityGroups(ctx, "postgres", d.Id(), sgIds) + if err != nil { + return err + } + d.SetPartial("security_groups_ids") + } + + //if d.HasChange("tags") { + // + // oldValue, newValue := d.GetChange("tags") + // replaceTags, deleteTags := diffTags(oldValue.(map[string]interface{}), newValue.(map[string]interface{})) + // + // tcClient := meta.(*TencentCloudClient).apiV3Conn + // tagService := &TagService{client: tcClient} + // resourceName := BuildTagResourceName("postgres", "DBInstanceId", tcClient.Region, d.Id()) + // err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) + // if err != nil { + // return err + // } + // d.SetPartial("tags") + //} + + d.Partial(false) + + return resourceTencentCloudPostgresqlReadOnlyInstanceRead(d, meta) +} + +func resourceTencentCLoudPostgresqlReadOnlyInstanceDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_postgresql_readonly_instance.delete")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + instanceId := d.Id() + postgresqlService := PostgresqlService{client: meta.(*TencentCloudClient).apiV3Conn} + + // isolate + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + e := postgresqlService.IsolatePostgresqlInstance(ctx, instanceId) + if e != nil { + return retryError(e) + } + return nil + }) + if err != nil { + return err + } + // delete + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + e := postgresqlService.DeletePostgresqlInstance(ctx, instanceId) + if e != nil { + return retryError(e) + } + return nil + }) + if err != nil { + return err + } + + return nil +} diff --git a/tencentcloud/service_tencentcloud_postgresql.go b/tencentcloud/service_tencentcloud_postgresql.go index 3df8bfa16e..e69a6bc408 100644 --- a/tencentcloud/service_tencentcloud_postgresql.go +++ b/tencentcloud/service_tencentcloud_postgresql.go @@ -586,3 +586,89 @@ func (me *PostgresqlService) DescribeDBInstanceAttribute(ctx context.Context, re return } + +func (me *PostgresqlService) DescribePostgresqlReadOnlyGroups(ctx context.Context, filter []*postgresql.Filter) (instanceList []*postgresql.ReadOnlyGroup, errRet error) { + logId := getLogId(ctx) + request := postgresql.NewDescribeReadOnlyGroupsRequest() + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail,reason[%s]", logId, request.GetAction(), errRet.Error()) + } + }() + + var offset, limit int64 = 1, 10 + + for { + request.PageNumber = &offset + request.PageSize = &limit + request.Filters = filter + ratelimit.Check(request.GetAction()) + response, err := me.client.UsePostgresqlClient().DescribeReadOnlyGroups(request) + if err != nil { + errRet = err + return + } + if response == nil || response.Response == nil { + errRet = fmt.Errorf("TencentCloud SDK return nil response, %s", request.GetAction()) + } + instanceList = append(instanceList, response.Response.ReadOnlyGroupList...) + if len(response.Response.ReadOnlyGroupList) < int(limit) { + return + } + offset += 1 + } +} + +func (me *PostgresqlService) DescribePostgresqlReadOnlyGroupById(ctx context.Context, groupId string) (instanceList []*postgresql.ReadOnlyGroup, errRet error) { + logId := getLogId(ctx) + request := postgresql.NewDescribeReadOnlyGroupsRequest() + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail,reason[%s]", logId, request.GetAction(), errRet.Error()) + } + }() + + request.Filters = append( + request.Filters, + &postgresql.Filter{ + Name: helper.String("db-master-instance-id"), + Values: []*string{&groupId}, + }, + ) + + var offset, limit int64 = 1, 10 + + for { + request.PageNumber = &offset + request.PageSize = &limit + ratelimit.Check(request.GetAction()) + response, err := me.client.UsePostgresqlClient().DescribeReadOnlyGroups(request) + if err != nil { + errRet = err + return + } + if response == nil || response.Response == nil { + errRet = fmt.Errorf("TencentCloud SDK return nil response, %s", request.GetAction()) + } + instanceList = append(instanceList, response.Response.ReadOnlyGroupList...) + if len(response.Response.ReadOnlyGroupList) < int(limit) { + return + } + offset += 1 + } +} + +func (me *PostgresqlService) DeletePostgresqlReadOnlyGroupById(ctx context.Context, groupId string) (errRet error) { + logId := getLogId(ctx) + request := postgresql.NewDeleteReadOnlyGroupRequest() + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail,reason[%s]", logId, request.GetAction(), errRet.Error()) + } + }() + request.ReadOnlyGroupId = &groupId + + ratelimit.Check(request.GetAction()) + _, err := me.client.UsePostgresqlClient().DeleteReadOnlyGroup(request) + return err +} diff --git a/website/docs/r/postgresql_readonly_attachment.html.markdown b/website/docs/r/postgresql_readonly_attachment.html.markdown new file mode 100644 index 0000000000..7419350941 --- /dev/null +++ b/website/docs/r/postgresql_readonly_attachment.html.markdown @@ -0,0 +1,37 @@ +--- +subcategory: "PostgreSQL" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_postgresql_readonly_attachment" +sidebar_current: "docs-tencentcloud-resource-postgresql_readonly_attachment" +description: |- + Use this resource to create postgresql readonly attachment. +--- + +# tencentcloud_postgresql_readonly_attachment + +Use this resource to create postgresql readonly attachment. + +## Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_attachment" "attach" { + db_instance_id = tencentcloud_postgresql_readonly_instance.foo.id + read_only_group_id = tencentcloud_postgresql_readonly_group.group.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `db_instance_id` - (Required, ForceNew) Read only instance ID. +* `read_only_group_id` - (Required, ForceNew) Read only group ID. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. + + + diff --git a/website/docs/r/postgresql_readonly_group.html.markdown b/website/docs/r/postgresql_readonly_group.html.markdown new file mode 100644 index 0000000000..70aff9d3a2 --- /dev/null +++ b/website/docs/r/postgresql_readonly_group.html.markdown @@ -0,0 +1,55 @@ +--- +subcategory: "PostgreSQL" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_postgresql_readonly_group" +sidebar_current: "docs-tencentcloud-resource-postgresql_readonly_group" +description: |- + Use this resource to create postgresql readonly group. +--- + +# tencentcloud_postgresql_readonly_group + +Use this resource to create postgresql readonly group. + +## Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_group" "group" { + master_db_instance_id = "postgres-f44wlfdv" + name = "world" + project_id = 0 + vpc_id = "vpc-86v957zb" + subnet_id = "subnet-enm92y0m" + replay_lag_eliminate = 1 + replay_latency_eliminate = 1 + max_replay_lag = 100 + max_replay_latency = 512 + min_delay_eliminate_reserve = 1 + # security_groups_ids = [] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `master_db_instance_id` - (Required, ForceNew) Primary instance ID. +* `max_replay_lag` - (Required) Delay threshold in ms. +* `max_replay_latency` - (Required) Delayed log size threshold in MB. +* `min_delay_eliminate_reserve` - (Required) The minimum number of read-only replicas that must be retained in an RO group. +* `name` - (Required) RO group name. +* `project_id` - (Required) Project ID. +* `replay_lag_eliminate` - (Required) Whether to remove a read-only replica from an RO group if the delay between the read-only replica and the primary instance exceeds the threshold. Valid values: 0 (no), 1 (yes). +* `replay_latency_eliminate` - (Required) Whether to remove a read-only replica from an RO group if the sync log size difference between the read-only replica and the primary instance exceeds the threshold. Valid values: 0 (no), 1 (yes). +* `subnet_id` - (Required) VPC subnet ID. +* `vpc_id` - (Required, ForceNew) VPC ID. +* `security_groups_ids` - (Optional) ID of security group. If both vpc_id and subnet_id are not set, this argument should not be set either. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `create_time` - Create time of the postgresql instance. + + diff --git a/website/docs/r/postgresql_readonly_instance.html.markdown b/website/docs/r/postgresql_readonly_instance.html.markdown new file mode 100644 index 0000000000..a84e1e7c99 --- /dev/null +++ b/website/docs/r/postgresql_readonly_instance.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "PostgreSQL" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_postgresql_readonly_instance" +sidebar_current: "docs-tencentcloud-resource-postgresql_readonly_instance" +description: |- + Use this resource to create postgresql readonly instance. +--- + +# tencentcloud_postgresql_readonly_instance + +Use this resource to create postgresql readonly instance. + +## Example Usage + +```hcl +resource "tencentcloud_postgresql_readonly_instance" "foo" { + auto_renew_flag = 0 + db_version = "10.4" + instance_charge_type = "POSTPAID_BY_HOUR" + master_db_instance_id = "postgres-j4pm65id" + memory = 4 + name = "hello" + need_support_ipv6 = 0 + project_id = 0 + security_groups_ids = [ + "sg-fefj5n6r", + ] + storage = 250 + subnet_id = "subnet-enm92y0m" + vpc_id = "vpc-86v957zb" + zone = "ap-guangzhou-6" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `db_version` - (Required, ForceNew) PostgreSQL kernel version, which must be the same as that of the primary instance. +* `master_db_instance_id` - (Required, ForceNew) ID of the primary instance to which the read-only replica belongs. +* `memory` - (Required) Memory size(in GB). Allowed value must be larger than `memory` that data source `tencentcloud_postgresql_specinfos` provides. +* `name` - (Required) Instance name. +* `project_id` - (Required) Project ID. +* `security_groups_ids` - (Required) ID of security group. +* `storage` - (Required) Instance storage capacity in GB. +* `subnet_id` - (Required) VPC subnet ID. +* `vpc_id` - (Required, ForceNew) VPC ID. +* `zone` - (Required, ForceNew) Availability zone ID, which can be obtained through the Zone field in the returned value of the DescribeZones API. +* `auto_renew_flag` - (Optional, ForceNew) Renewal flag. Valid values: 0 (manual renewal), 1 (auto-renewal). Default value: 0. +* `instance_charge_type` - (Optional, ForceNew) instance billing mode. Valid values: PREPAID (monthly subscription), POSTPAID_BY_HOUR (pay-as-you-go). +* `need_support_ipv6` - (Optional, ForceNew) Whether to support IPv6 address access. Valid values: 1 (yes), 0 (no). + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `create_time` - Create time of the postgresql instance. + + +## Import + +postgresql readonly instance can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_postgresql_readonly_instance.foo pgro-bcqx8b9a +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 54884fcf14..23938a185c 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -1116,6 +1116,15 @@
  • tencentcloud_postgresql_instance
  • +
  • + tencentcloud_postgresql_readonly_attachment +
  • +
  • + tencentcloud_postgresql_readonly_group +
  • +
  • + tencentcloud_postgresql_readonly_instance +