Skip to content

Commit 6e4b9eb

Browse files
authored
fix: cynos - support serverless cluster pause/resume (#1429)
* fix: cynos - support serverless cluster pause/resume * changelog 1429
1 parent fa32e3a commit 6e4b9eb

File tree

6 files changed

+260
-23
lines changed

6 files changed

+260
-23
lines changed

.changelog/1429.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
resource/tencentcloud_cynosdb_cluster: fix: cynos - support serverless cluster pause/resume
3+
```

tencentcloud/extension_cynosdb.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,17 @@ func TencentCynosdbClusterBaseInfo() map[string]*schema.Schema {
372372
Optional: true,
373373
Description: "Specify auto-pause delay in second while `db_mode` is `SERVERLESS`. Value range: `[600, 691200]`. Default: `600`.",
374374
},
375+
"serverless_status_flag": {
376+
Type: schema.TypeString,
377+
Optional: true,
378+
ValidateFunc: validateAllowedStringValue([]string{"resume", "pause"}),
379+
Description: "Specify whether to pause or resume serverless cluster. values: `resume`, `pause`.",
380+
},
381+
"serverless_status": {
382+
Type: schema.TypeString,
383+
Computed: true,
384+
Description: "Serverless cluster status. NOTE: This is a readonly attribute, to modify, please set `serverless_status_flag`.",
385+
},
375386
}
376387

377388
for k, v := range TencentCynosdbInstanceBaseInfo() {

tencentcloud/resource_tc_cynosdb_cluster.go

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ func resourceTencentCloudCynosdbClusterCreate(d *schema.ResourceData, meta inter
167167
return fmt.Errorf("`instance_memory_size` is required while creating non-serverless cluster")
168168
}
169169

170+
if _, ok := d.GetOk("serverless_status_flag"); ok && !isServerless {
171+
return fmt.Errorf("cannot set `serverless_status_flag` while creating non-serverless cluster")
172+
}
173+
170174
var chargeType int64 = 0
171175
if v, ok := d.GetOk("charge_type"); ok {
172176
if v == CYNOSDB_CHARGE_TYPE_PREPAID {
@@ -321,6 +325,15 @@ func resourceTencentCloudCynosdbClusterCreate(d *schema.ResourceData, meta inter
321325
}
322326
}
323327

328+
// serverless status
329+
if v, ok := d.GetOk("serverless_status_flag"); ok {
330+
resume := v.(string) == "resume"
331+
err := cynosdbService.SwitchServerlessCluster(ctx, id, resume)
332+
if err != nil {
333+
return err
334+
}
335+
}
336+
324337
return resourceTencentCloudCynosdbClusterRead(d, meta)
325338
}
326339

@@ -358,6 +371,12 @@ func resourceTencentCloudCynosdbClusterRead(d *schema.ResourceData, meta interfa
358371
_ = d.Set("create_time", cluster.CreateTime)
359372
_ = d.Set("storage_used", *cluster.UsedStorage/1000/1000)
360373
_ = d.Set("auto_renew_flag", *item.RenewFlag)
374+
_ = d.Set("serverless_status", cluster.ServerlessStatus)
375+
376+
if _, ok := d.GetOk("serverless_status_flag"); ok && *item.DbMode == CYNOSDB_SERVERLESS {
377+
status := *item.ServerlessStatus
378+
_ = d.Set("serverless_status_flag", status)
379+
}
361380

362381
if _, ok := d.GetOk("db_mode"); ok || *item.DbMode == CYNOSDB_SERVERLESS {
363382
_ = d.Set("db_mode", item.DbMode)
@@ -472,31 +491,34 @@ func resourceTencentCloudCynosdbClusterRead(d *schema.ResourceData, meta interfa
472491
}
473492
}
474493

475-
currentParamMap := make(map[string]*cynosdb.ParamInfo)
476-
params, err := cynosdbService.DescribeClusterParams(ctx, id)
477-
if err != nil {
478-
return err
479-
}
480-
for _, param := range params {
481-
currentParamMap[*param.ParamName] = param
482-
}
483-
resultParamItems := make([]map[string]string, 0)
484-
if v, ok := d.GetOk("param_items"); ok {
485-
paramItems := v.([]interface{})
486-
for _, paramItem := range paramItems {
487-
item := paramItem.(map[string]interface{})
488-
name := item["name"].(string)
489-
oldValue := item["old_value"].(string)
490-
currentParamItem := make(map[string]string)
491-
currentParamItem["name"] = name
492-
currentParamItem["current_value"] = *currentParamMap[name].CurrentValue
493-
if oldValue != "" {
494-
currentParamItem["old_value"] = oldValue
494+
isServerlessPaused := *item.DbMode == CYNOSDB_SERVERLESS && *item.ServerlessStatus == "pause"
495+
if !isServerlessPaused {
496+
currentParamMap := make(map[string]*cynosdb.ParamInfo)
497+
params, err := cynosdbService.DescribeClusterParams(ctx, id)
498+
if err != nil {
499+
return err
500+
}
501+
for _, param := range params {
502+
currentParamMap[*param.ParamName] = param
503+
}
504+
resultParamItems := make([]map[string]string, 0)
505+
if v, ok := d.GetOk("param_items"); ok {
506+
paramItems := v.([]interface{})
507+
for _, paramItem := range paramItems {
508+
item := paramItem.(map[string]interface{})
509+
name := item["name"].(string)
510+
oldValue := item["old_value"].(string)
511+
currentParamItem := make(map[string]string)
512+
currentParamItem["name"] = name
513+
currentParamItem["current_value"] = *currentParamMap[name].CurrentValue
514+
if oldValue != "" {
515+
currentParamItem["old_value"] = oldValue
516+
}
517+
resultParamItems = append(resultParamItems, currentParamItem)
495518
}
496-
resultParamItems = append(resultParamItems, currentParamItem)
497519
}
520+
_ = d.Set("param_items", resultParamItems)
498521
}
499-
_ = d.Set("param_items", resultParamItems)
500522

501523
return nil
502524
}
@@ -690,6 +712,17 @@ func resourceTencentCloudCynosdbClusterUpdate(d *schema.ResourceData, meta inter
690712
}
691713
}
692714

715+
// update serverless status
716+
717+
// serverless status
718+
if d.HasChange("serverless_status_flag") {
719+
resume := d.Get("serverless_status_flag").(string) == "resume"
720+
err := cynosdbService.SwitchServerlessCluster(ctx, clusterId, resume)
721+
if err != nil {
722+
return err
723+
}
724+
}
725+
693726
d.Partial(false)
694727

695728
return resourceTencentCloudCynosdbClusterRead(d, meta)

tencentcloud/resource_tc_cynosdb_cluster_test.go

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func init() {
4949
})
5050
}
5151

52-
func TestAccTencentCloudCynosdbClusterResource(t *testing.T) {
52+
func TestAccTencentCloudCynosdbClusterResourceBasic(t *testing.T) {
5353
t.Parallel()
5454
resource.Test(t, resource.TestCase{
5555
PreCheck: func() { testAccPreCheck(t) },
@@ -156,6 +156,24 @@ func TestAccTencentCloudCynosdbClusterResourceServerless(t *testing.T) {
156156
"auto_pause_delay",
157157
},
158158
},
159+
{
160+
Config: testAccCynosdbClusterServerlessPause,
161+
Check: resource.ComposeTestCheckFunc(
162+
testAccCheckCynosdbClusterExists("tencentcloud_cynosdb_cluster.foo"),
163+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "db_mode", "SERVERLESS"),
164+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "serverless_status", "pause"),
165+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "serverless_status_flag", "pause"),
166+
),
167+
},
168+
{
169+
Config: testAccCynosdbClusterServerlessResume,
170+
Check: resource.ComposeTestCheckFunc(
171+
testAccCheckCynosdbClusterExists("tencentcloud_cynosdb_cluster.foo"),
172+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "db_mode", "SERVERLESS"),
173+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "serverless_status", "resume"),
174+
resource.TestCheckResourceAttr("tencentcloud_cynosdb_cluster.foo", "serverless_status_flag", "resume"),
175+
),
176+
},
159177
},
160178
})
161179
}
@@ -356,3 +374,59 @@ resource "tencentcloud_cynosdb_cluster" "foo" {
356374
357375
force_delete = true
358376
}`
377+
const testAccCynosdbClusterServerlessPause = testAccCynosdbBasic + `
378+
resource "tencentcloud_cynosdb_cluster" "foo" {
379+
available_zone = var.availability_zone
380+
vpc_id = var.my_vpc
381+
subnet_id = var.my_subnet
382+
db_type = "MYSQL"
383+
db_version = "5.7"
384+
cluster_name = "tf-cynosdb-s"
385+
password = "cynos@123"
386+
db_mode = "SERVERLESS"
387+
min_cpu = 0.25
388+
max_cpu = 1
389+
auto_pause = "yes"
390+
auto_pause_delay = 1000
391+
instance_maintain_duration = 3600
392+
instance_maintain_start_time = 10800
393+
instance_maintain_weekdays = [
394+
"Fri",
395+
"Mon",
396+
"Sat",
397+
"Sun",
398+
"Thu",
399+
"Wed",
400+
"Tue",
401+
]
402+
serverless_status_flag = "pause"
403+
force_delete = true
404+
}`
405+
const testAccCynosdbClusterServerlessResume = testAccCynosdbBasic + `
406+
resource "tencentcloud_cynosdb_cluster" "foo" {
407+
available_zone = var.availability_zone
408+
vpc_id = var.my_vpc
409+
subnet_id = var.my_subnet
410+
db_type = "MYSQL"
411+
db_version = "5.7"
412+
cluster_name = "tf-cynosdb-s"
413+
password = "cynos@123"
414+
db_mode = "SERVERLESS"
415+
min_cpu = 0.25
416+
max_cpu = 1
417+
auto_pause = "yes"
418+
auto_pause_delay = 1000
419+
instance_maintain_duration = 3600
420+
instance_maintain_start_time = 10800
421+
instance_maintain_weekdays = [
422+
"Fri",
423+
"Mon",
424+
"Sat",
425+
"Sun",
426+
"Thu",
427+
"Wed",
428+
"Tue",
429+
]
430+
serverless_status_flag = "resume"
431+
force_delete = true
432+
}`

tencentcloud/service_tencentcloud_cynosdb.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,121 @@ func (me *CynosdbService) DescribeClusterParams(ctx context.Context, clusterId s
519519
}
520520
return nil
521521
})
522+
if errRet != nil {
523+
return
524+
}
522525
items = response.Response.Items
523526

524527
return
525528
}
529+
530+
func (me *CynosdbService) ResumeServerless(ctx context.Context, request *cynosdb.ResumeServerlessRequest) (errRet error) {
531+
logId := getLogId(ctx)
532+
defer func() {
533+
if errRet != nil {
534+
log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n",
535+
logId, request.GetAction(), request.ToJsonString(), errRet.Error())
536+
}
537+
}()
538+
539+
ratelimit.Check(request.GetAction())
540+
response, err := me.client.UseCynosdbClient().ResumeServerless(request)
541+
542+
if err != nil {
543+
errRet = err
544+
return
545+
}
546+
547+
log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n",
548+
logId, request.GetAction(), request.ToJsonString(), response.ToJsonString())
549+
550+
return
551+
}
552+
553+
func (me *CynosdbService) PauseServerless(ctx context.Context, request *cynosdb.PauseServerlessRequest) (errRet error) {
554+
logId := getLogId(ctx)
555+
defer func() {
556+
if errRet != nil {
557+
log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n",
558+
logId, request.GetAction(), request.ToJsonString(), errRet.Error())
559+
}
560+
}()
561+
562+
ratelimit.Check(request.GetAction())
563+
response, err := me.client.UseCynosdbClient().PauseServerless(request)
564+
565+
if err != nil {
566+
errRet = err
567+
return
568+
}
569+
570+
log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n",
571+
logId, request.GetAction(), request.ToJsonString(), response.ToJsonString())
572+
573+
return
574+
}
575+
576+
func (me *CynosdbService) SwitchServerlessCluster(ctx context.Context, clusterId string, resume bool) error {
577+
pause := !resume
578+
_, detail, _, err := me.DescribeClusterById(ctx, clusterId)
579+
if err != nil {
580+
return err
581+
}
582+
st := detail.ServerlessStatus
583+
if st == nil {
584+
return nil
585+
}
586+
err = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
587+
if resume && *st == "resuming" || pause && *st == "pausing" {
588+
return resource.RetryableError(fmt.Errorf("waiting for status %s finish", *st))
589+
}
590+
if resume && *st == "resume" || pause && *st == "pause" {
591+
return nil
592+
}
593+
if resume && *st == "pause" {
594+
request := cynosdb.NewResumeServerlessRequest()
595+
request.ClusterId = &clusterId
596+
err := me.ResumeServerless(ctx, request)
597+
if err != nil {
598+
return retryError(err, cynosdb.OPERATIONDENIED_SERVERLESSCLUSTERSTATUSDENIED)
599+
}
600+
return nil
601+
}
602+
if pause && *st == "resume" {
603+
request := cynosdb.NewPauseServerlessRequest()
604+
request.ClusterId = &clusterId
605+
err := me.PauseServerless(ctx, request)
606+
if err != nil {
607+
return retryError(err, cynosdb.OPERATIONDENIED_SERVERLESSCLUSTERSTATUSDENIED)
608+
}
609+
}
610+
return nil
611+
})
612+
613+
if err != nil {
614+
return err
615+
}
616+
statusChangeRetry := 5
617+
return resource.Retry(readRetryTimeout*5, func() *resource.RetryError {
618+
_, detail, _, err = me.DescribeClusterById(ctx, clusterId)
619+
if err != nil {
620+
return retryError(err)
621+
}
622+
st := detail.ServerlessStatus
623+
if st == nil {
624+
return resource.NonRetryableError(fmt.Errorf("cannot read serverless cluster status"))
625+
}
626+
if resume && *st == "pause" || pause && *st == "resume" {
627+
if statusChangeRetry > 0 {
628+
statusChangeRetry -= 1
629+
return resource.RetryableError(fmt.Errorf("waiting for status change, retry %d", statusChangeRetry))
630+
}
631+
return resource.NonRetryableError(fmt.Errorf("api action invoked but status still %s", *st))
632+
}
633+
if resume && *st == "resuming" || pause && *st == "pausing" {
634+
statusChangeRetry = 0
635+
return resource.RetryableError(fmt.Errorf("waiting for status %s finished", *st))
636+
}
637+
return nil
638+
})
639+
}

website/docs/r/cynosdb_cluster.html.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ The following arguments are supported:
9191
* `project_id` - (Optional, Int, ForceNew) ID of the project. `0` by default.
9292
* `ro_group_sg` - (Optional, List: [`String`]) IDs of security group for `ro_group`.
9393
* `rw_group_sg` - (Optional, List: [`String`]) IDs of security group for `rw_group`.
94+
* `serverless_status_flag` - (Optional, String) Specify whether to pause or resume serverless cluster. values: `resume`, `pause`.
9495
* `storage_limit` - (Optional, Int, ForceNew) Storage limit of CynosDB cluster instance, unit in GB. The maximum storage of a non-serverless instance in GB. NOTE: If db_type is `MYSQL` and charge_type is `PREPAID`, the value cannot exceed the maximum storage corresponding to the CPU and memory specifications, when charge_type is `POSTPAID_BY_HOUR`, this argument is unnecessary.
9596
* `tags` - (Optional, Map) The tags of the CynosDB cluster.
9697

@@ -126,6 +127,7 @@ In addition to all arguments above, the following attributes are exported:
126127
* `rw_group_instances` - List of instances in the read-write instance group.
127128
* `instance_id` - ID of instance.
128129
* `instance_name` - Name of instance.
130+
* `serverless_status` - Serverless cluster status. NOTE: This is a readonly attribute, to modify, please set `serverless_status_flag`.
129131
* `storage_used` - Used storage of CynosDB cluster, unit in MB.
130132

131133

0 commit comments

Comments
 (0)