@@ -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+
3271Import
3372
3473tcr 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