Skip to content

Commit 2e72516

Browse files
authored
feat: tcr - support replications (#1289)
* feat: tcr - support replications * fix: tcr - update replication doc * fix: tcr - testcase provider env
1 parent 7cac74e commit 2e72516

File tree

8 files changed

+640
-9
lines changed

8 files changed

+640
-9
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.445
3535
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudaudit v1.0.199
3636
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls v1.0.412
37-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500
37+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503
3838
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445
3939
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cynosdb v1.0.488
4040
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dayu v1.0.335
@@ -58,7 +58,7 @@ require (
5858
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.199
5959
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.199
6060
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199
61-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486
61+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503
6262
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268
6363
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tem v1.0.472
6464
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.500

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.493/go.mod
493493
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.494/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
494494
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500 h1:xuc9CRLhkpww61x5/k2cZ1tx8zGTEzE1+pv/1bSWlPI=
495495
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
496+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503 h1:XV1MdaHDMqpGz74EvbkOWr+xlslC7yQHc+9DXmkTXZs=
497+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
496498
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445 h1:Bh7XD0ypNMHYyBOM8hhKsSu+y0VVKUnJVS+YKKhfpGg=
497499
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445/go.mod h1:jMDD351efCFpT1+KVFbcpu6SbmP4TYmp4qkoCfr63nQ=
498500
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cynosdb v1.0.488 h1:A1seXWtMf2atBjSNYvcwxyDoFzCMgqyVnsxnWzhqJEA=
@@ -542,8 +544,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199 h1:i
542544
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199/go.mod h1:PUgbrkzA9IaKBj1urk+W4L6Jr5TuBhQ4xB/96QvLf/U=
543545
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.267 h1:Aqnh1edylmWJnBK9btXtYBtzmfdqyr2pxOYW5oOyrcY=
544546
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.267/go.mod h1:SEUO10oGtg+4AGCfpJDn9ynf47P+ZiyvhzOyXLt0mOY=
545-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486 h1:6HgzeOwD3yeo8a/prCx63bAN0INcP67GwkK+bseY9f0=
546-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486/go.mod h1:Fmyf/a1j8Op6vyl71KtX35Hd0GnYqm5uoYz/RB1H4Wk=
547+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503 h1:wDJnXddBwMCqYDy4mPRcMZpRD5EOoXjktXSyQQUvpwo=
548+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503/go.mod h1:auioaP0mtgitVHdt+NLRN3f87zPsA3M/m+9niqGxKgs=
547549
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268 h1:ez5lvKQVWGQV90BV3m9SeFODaoDbrtkMzw2S0DRMncA=
548550
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268/go.mod h1:fchXZhmqaYaG2c4wTCBTdnW6TFAtxl3D/P/yuuuLMfA=
549551
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tem v1.0.472 h1:9Jzrgx78+5XnZ8myNYjCYZn5ZF+tbSIpF6KWGgWr0uY=

tencentcloud/resource_tc_tcr_instance.go

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,45 @@ resource "tencentcloud_tcr_instance" "foo" {
2929
}
3030
```
3131
32+
Create with Replications
33+
34+
```hcl
35+
36+
resource "tencentcloud_tcr_instance" "foo" {
37+
name = "example"
38+
instance_type = "premium"
39+
replications {
40+
region_id = var.tcr_region_map["ap-guangzhou"] # 1
41+
}
42+
replications {
43+
region_id = var.tcr_region_map["ap-singapore"] # 9
44+
}
45+
}
46+
47+
variable "tcr_region_map" {
48+
default = {
49+
"ap-guangzhou" = 1
50+
"ap-shanghai" = 4
51+
"ap-hongkong" = 5
52+
"ap-beijing" = 8
53+
"ap-singapore" = 9
54+
"na-siliconvalley" = 15
55+
"ap-chengdu" = 16
56+
"eu-frankfurt" = 17
57+
"ap-seoul" = 18
58+
"ap-chongqing" = 19
59+
"ap-mumbai" = 21
60+
"na-ashburn" = 22
61+
"ap-bangkok" = 23
62+
"eu-moscow" = 24
63+
"ap-tokyo" = 25
64+
"ap-nanjing" = 33
65+
"ap-taipei" = 39
66+
"ap-jakarta" = 72
67+
}
68+
}
69+
```
70+
3271
Import
3372
3473
tcr instance can be imported using the id, e.g.
@@ -43,6 +82,11 @@ import (
4382
"context"
4483
"fmt"
4584
"log"
85+
"strings"
86+
"time"
87+
88+
"github.com/hashicorp/go-multierror"
89+
sdkErrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
4690

4791
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
4892
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
@@ -112,6 +156,30 @@ func resourceTencentCloudTcrInstance() *schema.Resource {
112156
},
113157
},
114158
},
159+
"replications": {
160+
Type: schema.TypeList,
161+
Optional: true,
162+
Description: "Specify List of instance Replications, premium only. The available [source region list](https://www.tencentcloud.com/document/api/1051/41101) is here.",
163+
Elem: &schema.Resource{
164+
Schema: map[string]*schema.Schema{
165+
"id": {
166+
Type: schema.TypeString,
167+
Computed: true,
168+
Description: "Replication registry ID (readonly).",
169+
},
170+
"region_id": {
171+
Type: schema.TypeInt,
172+
Optional: true,
173+
Description: "Replication region ID, check the example at the top of page to find out id of region.",
174+
},
175+
"syn_tag": {
176+
Type: schema.TypeBool,
177+
Optional: true,
178+
Description: "Specify whether to sync TCR cloud tags to COS Bucket. NOTE: You have to specify when adding, modifying will be ignored for now.",
179+
},
180+
},
181+
},
182+
},
115183
//Computed values
116184
"status": {
117185
Type: schema.TypeString,
@@ -249,6 +317,13 @@ func resourceTencentCloudTcrInstanceCreate(d *schema.ResourceData, meta interfac
249317
} else if !operation {
250318
log.Printf("[WARN] `open_public_operation` was not opened, skip `security_policy` set.")
251319
}
320+
321+
if _, ok := d.GetOk("replications"); ok {
322+
err := resourceTencentCloudTcrReplicationSet(ctx, d, meta)
323+
if err != nil {
324+
return err
325+
}
326+
}
252327
}
253328

254329
if tags := helper.GetTags(d, "tags"); len(tags) > 0 {
@@ -359,6 +434,39 @@ func resourceTencentCloudTcrInstanceRead(d *schema.ResourceData, meta interface{
359434
return err
360435
}
361436

437+
replicas := d.Get("replications").([]interface{})
438+
439+
err = resource.Retry(readRetryTimeout*3, func() *resource.RetryError {
440+
request := tcr.NewDescribeReplicationInstancesRequest()
441+
request.RegistryId = helper.String(d.Id())
442+
request.Limit = helper.IntInt64(100)
443+
response, err := tcrService.DescribeReplicationInstances(ctx, request)
444+
if err != nil {
445+
return retryError(err)
446+
}
447+
for i := range response {
448+
item := response[i]
449+
if *item.Status != "Running" {
450+
return resource.RetryableError(
451+
fmt.Errorf(
452+
"replica %d of registry %s is now %s, waiting for task finish",
453+
*item.ReplicationRegionId,
454+
*item.RegistryId,
455+
*item.Status))
456+
}
457+
}
458+
replicas = resourceTencentCloudTcrFillReplicas(replicas, response)
459+
return nil
460+
})
461+
462+
if err != nil {
463+
return err
464+
}
465+
466+
if len(replicas) > 0 {
467+
_ = d.Set("replications", replicas)
468+
}
469+
362470
tags := make(map[string]string, len(instance.TagSpecification.Tags))
363471
for _, tag := range instance.TagSpecification.Tags {
364472
tags[*tag.Key] = *tag.Value
@@ -446,6 +554,13 @@ func resourceTencentCloudTcrInstanceUpdate(d *schema.ResourceData, meta interfac
446554
d.SetPartial("security_policy")
447555
}
448556

557+
if d.HasChange("replications") {
558+
err := resourceTencentCloudTcrReplicationSet(ctx, d, meta)
559+
if err != nil {
560+
return err
561+
}
562+
}
563+
449564
if d.HasChange("tags") {
450565
oldTags, newTags := d.GetChange("tags")
451566
replaceTags, deleteTags := diffTags(oldTags.(map[string]interface{}), newTags.(map[string]interface{}))
@@ -496,6 +611,30 @@ func resourceTencentCloudTcrInstanceDelete(d *schema.ResourceData, meta interfac
496611
var inErr, outErr error
497612
var has bool
498613

614+
// Delete replications first
615+
repRequest := tcr.NewDescribeReplicationInstancesRequest()
616+
repRequest.RegistryId = &instanceId
617+
replicas, outErr := tcrService.DescribeReplicationInstances(ctx, repRequest)
618+
619+
if outErr != nil {
620+
return outErr
621+
}
622+
623+
for i := range replicas {
624+
item := replicas[i]
625+
outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
626+
request := tcr.NewDeleteReplicationInstanceRequest()
627+
request.RegistryId = &instanceId
628+
request.ReplicationRegistryId = item.ReplicationRegistryId
629+
request.ReplicationRegionId = item.ReplicationRegionId
630+
err := tcrService.DeleteReplicationInstance(ctx, request)
631+
if err != nil {
632+
return retryError(err, tcr.INTERNALERROR_ERRORCONFLICT)
633+
}
634+
return nil
635+
})
636+
}
637+
499638
outErr = tcrService.DeleteTCRInstance(ctx, instanceId, deleteBucket)
500639
if outErr != nil {
501640
outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
@@ -589,3 +728,114 @@ func resourceTencentCloudTcrSecurityPolicyRemove(d *schema.ResourceData, meta in
589728
}
590729
return nil
591730
}
731+
732+
func resourceTencentCloudTcrReplicationSet(ctx context.Context, d *schema.ResourceData, meta interface{}) error {
733+
var errs multierror.Error
734+
735+
client := meta.(*TencentCloudClient).apiV3Conn
736+
service := TCRService{client}
737+
o, n := d.GetChange("replications")
738+
ov := o.([]interface{})
739+
nv := n.([]interface{})
740+
741+
setFunc := func(v interface{}) int {
742+
item, ok := v.(map[string]interface{})
743+
if !ok {
744+
return 0
745+
}
746+
return item["region_id"].(int)
747+
}
748+
749+
oSet := schema.NewSet(setFunc, ov)
750+
nSet := schema.NewSet(setFunc, nv)
751+
adds := nSet.Difference(oSet)
752+
removes := oSet.Difference(nSet)
753+
754+
log.Printf("[DEBUG] TCR - replicas will be add: %v", adds)
755+
log.Printf("[DEBUG] TCR - replicas will be delete %v", removes)
756+
757+
if list := adds.List(); adds.Len() > 0 {
758+
for i := range list {
759+
request := tcr.NewCreateReplicationInstanceRequest()
760+
replica := list[i].(map[string]interface{})
761+
request.RegistryId = helper.String(d.Id())
762+
request.ReplicationRegionId = helper.IntUint64(replica["region_id"].(int))
763+
if synTag, ok := replica["syn_tag"].(bool); ok {
764+
request.SyncTag = &synTag
765+
}
766+
err := resource.Retry(writeRetryTimeout, func() *resource.RetryError {
767+
_, err := service.CreateReplicationInstance(ctx, request)
768+
if err != nil {
769+
sdkErr, ok := err.(*sdkErrors.TencentCloudSDKError)
770+
if ok {
771+
code := sdkErr.GetCode()
772+
message := sdkErr.GetMessage()
773+
if code == tcr.INTERNALERROR && strings.Contains(message, "409 InvalidBucketState") {
774+
log.Printf("[WARN] Got COS retryable error %s: %s", code, message)
775+
return resource.RetryableError(sdkErr)
776+
}
777+
}
778+
return retryError(err)
779+
}
780+
return nil
781+
})
782+
if err != nil {
783+
errs = *multierror.Append(err)
784+
}
785+
// Buffered for Request Limit: 1 time per sec
786+
time.Sleep(time.Second * 3)
787+
}
788+
}
789+
790+
if list := removes.List(); removes.Len() > 0 {
791+
for i := range list {
792+
replica := list[i].(map[string]interface{})
793+
id, ok := replica["id"].(string)
794+
regionId := replica["region_id"].(int)
795+
if !ok || id == "" {
796+
errs = *multierror.Append(fmt.Errorf("replication region %d has no id", regionId))
797+
continue
798+
}
799+
request := tcr.NewDeleteReplicationInstanceRequest()
800+
request.RegistryId = helper.String(d.Id())
801+
request.ReplicationRegistryId = helper.String(id)
802+
request.ReplicationRegionId = helper.IntUint64(regionId)
803+
err := service.DeleteReplicationInstance(ctx, request)
804+
if err != nil {
805+
errs = *multierror.Append(err)
806+
}
807+
}
808+
}
809+
810+
return errs.ErrorOrNil()
811+
}
812+
813+
func resourceTencentCloudTcrFillReplicas(replicas []interface{}, registries []*tcr.ReplicationRegistry) []interface{} {
814+
replicaRegionIndexes := map[int]int{}
815+
for i := range replicas {
816+
item := replicas[i].(map[string]interface{})
817+
regionId := item["region_id"].(int)
818+
replicaRegionIndexes[regionId] = i
819+
}
820+
821+
var newReplicas []interface{}
822+
for i := range registries {
823+
item := registries[i]
824+
id := *item.ReplicationRegistryId
825+
regionId := *item.ReplicationRegionId
826+
if index, ok := replicaRegionIndexes[int(regionId)]; ok && index >= 0 {
827+
replicas[index].(map[string]interface{})["id"] = id
828+
} else {
829+
newReplicas = append(newReplicas, map[string]interface{}{
830+
"id": id,
831+
"region_id": int(regionId),
832+
})
833+
}
834+
}
835+
836+
if len(newReplicas) > 0 {
837+
replicas = append(replicas, newReplicas...)
838+
}
839+
840+
return replicas
841+
}

0 commit comments

Comments
 (0)