diff --git a/go.mod b/go.mod index a9b861c334..f447e8b880 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.445 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudaudit v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls v1.0.412 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cynosdb v1.0.488 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dayu v1.0.335 @@ -58,7 +58,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tem v1.0.472 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.500 diff --git a/go.sum b/go.sum index fe66fe8ca1..60e1617124 100644 --- a/go.sum +++ b/go.sum @@ -493,6 +493,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.493/go.mod github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.494/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500 h1:xuc9CRLhkpww61x5/k2cZ1tx8zGTEzE1+pv/1bSWlPI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503 h1:XV1MdaHDMqpGz74EvbkOWr+xlslC7yQHc+9DXmkTXZs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445 h1:Bh7XD0ypNMHYyBOM8hhKsSu+y0VVKUnJVS+YKKhfpGg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.445/go.mod h1:jMDD351efCFpT1+KVFbcpu6SbmP4TYmp4qkoCfr63nQ= 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 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199/go.mod h1:PUgbrkzA9IaKBj1urk+W4L6Jr5TuBhQ4xB/96QvLf/U= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.267 h1:Aqnh1edylmWJnBK9btXtYBtzmfdqyr2pxOYW5oOyrcY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.267/go.mod h1:SEUO10oGtg+4AGCfpJDn9ynf47P+ZiyvhzOyXLt0mOY= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486 h1:6HgzeOwD3yeo8a/prCx63bAN0INcP67GwkK+bseY9f0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486/go.mod h1:Fmyf/a1j8Op6vyl71KtX35Hd0GnYqm5uoYz/RB1H4Wk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503 h1:wDJnXddBwMCqYDy4mPRcMZpRD5EOoXjktXSyQQUvpwo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503/go.mod h1:auioaP0mtgitVHdt+NLRN3f87zPsA3M/m+9niqGxKgs= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268 h1:ez5lvKQVWGQV90BV3m9SeFODaoDbrtkMzw2S0DRMncA= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268/go.mod h1:fchXZhmqaYaG2c4wTCBTdnW6TFAtxl3D/P/yuuuLMfA= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tem v1.0.472 h1:9Jzrgx78+5XnZ8myNYjCYZn5ZF+tbSIpF6KWGgWr0uY= diff --git a/tencentcloud/resource_tc_tcr_instance.go b/tencentcloud/resource_tc_tcr_instance.go index fe23baa80b..46b6c0d0ad 100644 --- a/tencentcloud/resource_tc_tcr_instance.go +++ b/tencentcloud/resource_tc_tcr_instance.go @@ -29,6 +29,45 @@ resource "tencentcloud_tcr_instance" "foo" { } ``` +Create with Replications + +```hcl + +resource "tencentcloud_tcr_instance" "foo" { + name = "example" + instance_type = "premium" + replications { + region_id = var.tcr_region_map["ap-guangzhou"] # 1 + } + replications { + region_id = var.tcr_region_map["ap-singapore"] # 9 + } +} + +variable "tcr_region_map" { + default = { + "ap-guangzhou" = 1 + "ap-shanghai" = 4 + "ap-hongkong" = 5 + "ap-beijing" = 8 + "ap-singapore" = 9 + "na-siliconvalley" = 15 + "ap-chengdu" = 16 + "eu-frankfurt" = 17 + "ap-seoul" = 18 + "ap-chongqing" = 19 + "ap-mumbai" = 21 + "na-ashburn" = 22 + "ap-bangkok" = 23 + "eu-moscow" = 24 + "ap-tokyo" = 25 + "ap-nanjing" = 33 + "ap-taipei" = 39 + "ap-jakarta" = 72 + } +} +``` + Import tcr instance can be imported using the id, e.g. @@ -43,6 +82,11 @@ import ( "context" "fmt" "log" + "strings" + "time" + + "github.com/hashicorp/go-multierror" + sdkErrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -112,6 +156,30 @@ func resourceTencentCloudTcrInstance() *schema.Resource { }, }, }, + "replications": { + Type: schema.TypeList, + Optional: true, + Description: "Specify List of instance Replications, premium only. The available [source region list](https://www.tencentcloud.com/document/api/1051/41101) is here.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "Replication registry ID (readonly).", + }, + "region_id": { + Type: schema.TypeInt, + Optional: true, + Description: "Replication region ID, check the example at the top of page to find out id of region.", + }, + "syn_tag": { + Type: schema.TypeBool, + Optional: true, + Description: "Specify whether to sync TCR cloud tags to COS Bucket. NOTE: You have to specify when adding, modifying will be ignored for now.", + }, + }, + }, + }, //Computed values "status": { Type: schema.TypeString, @@ -249,6 +317,13 @@ func resourceTencentCloudTcrInstanceCreate(d *schema.ResourceData, meta interfac } else if !operation { log.Printf("[WARN] `open_public_operation` was not opened, skip `security_policy` set.") } + + if _, ok := d.GetOk("replications"); ok { + err := resourceTencentCloudTcrReplicationSet(ctx, d, meta) + if err != nil { + return err + } + } } if tags := helper.GetTags(d, "tags"); len(tags) > 0 { @@ -359,6 +434,39 @@ func resourceTencentCloudTcrInstanceRead(d *schema.ResourceData, meta interface{ return err } + replicas := d.Get("replications").([]interface{}) + + err = resource.Retry(readRetryTimeout*3, func() *resource.RetryError { + request := tcr.NewDescribeReplicationInstancesRequest() + request.RegistryId = helper.String(d.Id()) + request.Limit = helper.IntInt64(100) + response, err := tcrService.DescribeReplicationInstances(ctx, request) + if err != nil { + return retryError(err) + } + for i := range response { + item := response[i] + if *item.Status != "Running" { + return resource.RetryableError( + fmt.Errorf( + "replica %d of registry %s is now %s, waiting for task finish", + *item.ReplicationRegionId, + *item.RegistryId, + *item.Status)) + } + } + replicas = resourceTencentCloudTcrFillReplicas(replicas, response) + return nil + }) + + if err != nil { + return err + } + + if len(replicas) > 0 { + _ = d.Set("replications", replicas) + } + tags := make(map[string]string, len(instance.TagSpecification.Tags)) for _, tag := range instance.TagSpecification.Tags { tags[*tag.Key] = *tag.Value @@ -446,6 +554,13 @@ func resourceTencentCloudTcrInstanceUpdate(d *schema.ResourceData, meta interfac d.SetPartial("security_policy") } + if d.HasChange("replications") { + err := resourceTencentCloudTcrReplicationSet(ctx, d, meta) + if err != nil { + return err + } + } + if d.HasChange("tags") { oldTags, newTags := d.GetChange("tags") replaceTags, deleteTags := diffTags(oldTags.(map[string]interface{}), newTags.(map[string]interface{})) @@ -496,6 +611,30 @@ func resourceTencentCloudTcrInstanceDelete(d *schema.ResourceData, meta interfac var inErr, outErr error var has bool + // Delete replications first + repRequest := tcr.NewDescribeReplicationInstancesRequest() + repRequest.RegistryId = &instanceId + replicas, outErr := tcrService.DescribeReplicationInstances(ctx, repRequest) + + if outErr != nil { + return outErr + } + + for i := range replicas { + item := replicas[i] + outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + request := tcr.NewDeleteReplicationInstanceRequest() + request.RegistryId = &instanceId + request.ReplicationRegistryId = item.ReplicationRegistryId + request.ReplicationRegionId = item.ReplicationRegionId + err := tcrService.DeleteReplicationInstance(ctx, request) + if err != nil { + return retryError(err, tcr.INTERNALERROR_ERRORCONFLICT) + } + return nil + }) + } + outErr = tcrService.DeleteTCRInstance(ctx, instanceId, deleteBucket) if outErr != nil { outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { @@ -589,3 +728,114 @@ func resourceTencentCloudTcrSecurityPolicyRemove(d *schema.ResourceData, meta in } return nil } + +func resourceTencentCloudTcrReplicationSet(ctx context.Context, d *schema.ResourceData, meta interface{}) error { + var errs multierror.Error + + client := meta.(*TencentCloudClient).apiV3Conn + service := TCRService{client} + o, n := d.GetChange("replications") + ov := o.([]interface{}) + nv := n.([]interface{}) + + setFunc := func(v interface{}) int { + item, ok := v.(map[string]interface{}) + if !ok { + return 0 + } + return item["region_id"].(int) + } + + oSet := schema.NewSet(setFunc, ov) + nSet := schema.NewSet(setFunc, nv) + adds := nSet.Difference(oSet) + removes := oSet.Difference(nSet) + + log.Printf("[DEBUG] TCR - replicas will be add: %v", adds) + log.Printf("[DEBUG] TCR - replicas will be delete %v", removes) + + if list := adds.List(); adds.Len() > 0 { + for i := range list { + request := tcr.NewCreateReplicationInstanceRequest() + replica := list[i].(map[string]interface{}) + request.RegistryId = helper.String(d.Id()) + request.ReplicationRegionId = helper.IntUint64(replica["region_id"].(int)) + if synTag, ok := replica["syn_tag"].(bool); ok { + request.SyncTag = &synTag + } + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + _, err := service.CreateReplicationInstance(ctx, request) + if err != nil { + sdkErr, ok := err.(*sdkErrors.TencentCloudSDKError) + if ok { + code := sdkErr.GetCode() + message := sdkErr.GetMessage() + if code == tcr.INTERNALERROR && strings.Contains(message, "409 InvalidBucketState") { + log.Printf("[WARN] Got COS retryable error %s: %s", code, message) + return resource.RetryableError(sdkErr) + } + } + return retryError(err) + } + return nil + }) + if err != nil { + errs = *multierror.Append(err) + } + // Buffered for Request Limit: 1 time per sec + time.Sleep(time.Second * 3) + } + } + + if list := removes.List(); removes.Len() > 0 { + for i := range list { + replica := list[i].(map[string]interface{}) + id, ok := replica["id"].(string) + regionId := replica["region_id"].(int) + if !ok || id == "" { + errs = *multierror.Append(fmt.Errorf("replication region %d has no id", regionId)) + continue + } + request := tcr.NewDeleteReplicationInstanceRequest() + request.RegistryId = helper.String(d.Id()) + request.ReplicationRegistryId = helper.String(id) + request.ReplicationRegionId = helper.IntUint64(regionId) + err := service.DeleteReplicationInstance(ctx, request) + if err != nil { + errs = *multierror.Append(err) + } + } + } + + return errs.ErrorOrNil() +} + +func resourceTencentCloudTcrFillReplicas(replicas []interface{}, registries []*tcr.ReplicationRegistry) []interface{} { + replicaRegionIndexes := map[int]int{} + for i := range replicas { + item := replicas[i].(map[string]interface{}) + regionId := item["region_id"].(int) + replicaRegionIndexes[regionId] = i + } + + var newReplicas []interface{} + for i := range registries { + item := registries[i] + id := *item.ReplicationRegistryId + regionId := *item.ReplicationRegionId + if index, ok := replicaRegionIndexes[int(regionId)]; ok && index >= 0 { + replicas[index].(map[string]interface{})["id"] = id + } else { + newReplicas = append(newReplicas, map[string]interface{}{ + "id": id, + "region_id": int(regionId), + }) + } + } + + if len(newReplicas) > 0 { + replicas = append(replicas, newReplicas...) + } + + return replicas +} diff --git a/tencentcloud/resource_tc_tcr_instance_test.go b/tencentcloud/resource_tc_tcr_instance_test.go index e80db4cb9a..ceb5726f5d 100644 --- a/tencentcloud/resource_tc_tcr_instance_test.go +++ b/tencentcloud/resource_tc_tcr_instance_test.go @@ -7,6 +7,10 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + tcr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr/v20190924" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" ) @@ -39,6 +43,34 @@ func init() { if isResourcePersist(name, &created) { continue } + + // Delete replicas + + // Delete replications first + repRequest := tcr.NewDescribeReplicationInstancesRequest() + repRequest.RegistryId = &id + replicas, outErr := service.DescribeReplicationInstances(ctx, repRequest) + + if outErr != nil { + return outErr + } + + for i := range replicas { + item := replicas[i] + outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + request := tcr.NewDeleteReplicationInstanceRequest() + request.RegistryId = &id + request.ReplicationRegistryId = item.ReplicationRegistryId + request.ReplicationRegionId = item.ReplicationRegionId + err := service.DeleteReplicationInstance(ctx, request) + if err != nil { + return retryError(err, tcr.INTERNALERROR_ERRORCONFLICT) + } + return nil + }) + } + + // Delete Instance log.Printf("instance %s:%s will delete", id, name) err = service.DeleteTCRInstance(ctx, id, true) if err != nil { @@ -69,7 +101,6 @@ func TestAccTencentCloudTCRInstance_basic_and_update(t *testing.T) { resource.TestCheckResourceAttrSet("tencentcloud_tcr_instance.mytcr_instance", "status"), resource.TestCheckResourceAttrSet("tencentcloud_tcr_instance.mytcr_instance", "public_domain"), ), - Destroy: false, }, { ResourceName: "tencentcloud_tcr_instance.mytcr_instance", @@ -88,7 +119,6 @@ func TestAccTencentCloudTCRInstance_basic_and_update(t *testing.T) { //resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.0.cidr_block", "192.168.1.1/24"), //resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.1.cidr_block", "10.0.0.1/16"), ), - Destroy: false, }, { Config: testAccTCRInstance_basic_update_security, @@ -110,6 +140,134 @@ func TestAccTencentCloudTCRInstance_basic_and_update(t *testing.T) { }) } +func TestAccTencentCloudTCRInstance_replication(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTCRInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTCRInstance_replica, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "name", "tfreplicas"), + resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "replications.#", "2"), + ), + }, + //{ + // ResourceName: "tencentcloud_tcr_instance.mytcr_instance", + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{"delete_bucket"}, + //}, + { + Config: testAccTCRInstance_replica_update, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "name", "tfreplicas"), + resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "replications.#", "3"), + ), + }, + }, + }) +} + +func TestUnitTencentCloudTCRReplicaSet(t *testing.T) { + + inputs := []interface{}{ + map[string]interface{}{ + "region_id": 1, + "sync_cos": true, + }, + map[string]interface{}{ + "region_id": 2, + }, + map[string]interface{}{ + "region_id": 3, + "sync_cos": false, + }, + } + + registries1 := []*tcr.ReplicationRegistry{ + { + ReplicationRegistryId: helper.String("a"), + RegistryId: helper.String("x"), + ReplicationRegionId: helper.IntUint64(1), + }, + { + ReplicationRegistryId: helper.String("b"), + RegistryId: helper.String("x"), + ReplicationRegionId: helper.IntUint64(2), + }, + { + ReplicationRegistryId: helper.String("c"), + RegistryId: helper.String("x"), + ReplicationRegionId: helper.IntUint64(3), + }, + } + + result1 := resourceTencentCloudTcrFillReplicas(inputs, registries1) + expected1 := []interface{}{ + map[string]interface{}{ + "id": "a", + "region_id": 1, + "sync_cos": true, + }, + map[string]interface{}{ + "id": "b", + "region_id": 2, + }, + map[string]interface{}{ + "id": "c", + "region_id": 3, + "sync_cos": false, + }, + } + assert.Equalf(t, expected1, result1, "%s case 1 not equal, expected:\n%v\ngot: \n%v", t.Name(), expected1, result1) + + var registries2 []*tcr.ReplicationRegistry + var registries2Incr = []*tcr.ReplicationRegistry{ + { + ReplicationRegistryId: helper.String("d"), + RegistryId: helper.String("x"), + ReplicationRegionId: helper.IntUint64(4), + }, + { + ReplicationRegistryId: helper.String("e"), + RegistryId: helper.String("x"), + ReplicationRegionId: helper.IntUint64(5), + }, + } + registries2 = append(registries2, registries1...) + registries2 = append(registries2, registries2Incr...) + result2 := resourceTencentCloudTcrFillReplicas(inputs, registries2) + expected2 := []interface{}{ + map[string]interface{}{ + "id": "a", + "region_id": 1, + "sync_cos": true, + }, + map[string]interface{}{ + "id": "b", + "region_id": 2, + }, + map[string]interface{}{ + "id": "c", + "region_id": 3, + "sync_cos": false, + }, + map[string]interface{}{ + "id": "d", + "region_id": 4, + }, + map[string]interface{}{ + "id": "e", + "region_id": 5, + }, + } + + assert.Equalf(t, expected2, result2, "%s case 2 not equal, expected:\n%v\ngot: \n%v", t.Name(), expected2, result2) +} + func testAccCheckTCRInstanceDestroy(s *terraform.State) error { logId := getLogId(contextNil) ctx := context.WithValue(context.TODO(), logIdKey, logId) @@ -166,6 +324,38 @@ resource "tencentcloud_tcr_instance" "mytcr_instance" { } }` +const testAccTCRInstance_replica = ` +resource "tencentcloud_tcr_instance" "mytcr_instance" { + name = "tfreplicas" + instance_type = "premium" + delete_bucket = true + + replications { + region_id = 4 # ap-shanghai + } + replications { + region_id = 5 # ap-hongkong + } +}` + +const testAccTCRInstance_replica_update = ` +resource "tencentcloud_tcr_instance" "mytcr_instance" { + name = "tfreplicas" + instance_type = "premium" + delete_bucket = true + + replications { + region_id = 5 # ap-hongkong + } + replications { + region_id = 8 # ap-beijing + } + replications { + region_id = 16 #ap-chengdu + syn_tag = true + } +}` + const testAccTCRInstance_basic_update_remark = ` resource "tencentcloud_tcr_instance" "mytcr_instance" { name = "testacctcrinstance1" diff --git a/tencentcloud/service_tencentcloud_tcr.go b/tencentcloud/service_tencentcloud_tcr.go index 5d0c616e4e..1ca4a062a5 100644 --- a/tencentcloud/service_tencentcloud_tcr.go +++ b/tencentcloud/service_tencentcloud_tcr.go @@ -5,6 +5,7 @@ import ( "fmt" "log" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" sdkErrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" tcr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr/v20190924" "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/connectivity" @@ -828,3 +829,147 @@ func (me *TCRService) DescribeTcrVpcDnsById(ctx context.Context, instanceId stri has = true return } + +func (me *TCRService) CreateReplicationInstance(ctx context.Context, request *tcr.CreateReplicationInstanceRequest) (id string, errRet error) { + logId := getLogId(ctx) + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseTCRClient().CreateReplicationInstance(request) + + if err != nil { + errRet = err + return + } + + id = *response.Response.ReplicationRegistryId + + startPolling := false + + err = resource.Retry(readRetryTimeout*3, func() *resource.RetryError { + req := tcr.NewDescribeReplicationInstancesRequest() + req.RegistryId = request.RegistryId + req.Limit = helper.IntInt64(100) + replicas, err := me.DescribeReplicationInstances(ctx, req) + if err != nil { + return retryError(err) + } + if len(replicas) == 0 { + return resource.NonRetryableError(fmt.Errorf("no replica found in registry %s", *request.RegistryId)) + } + + for i := range replicas { + item := replicas[i] + if *item.Status == "Running" { + continue + } + startPolling = true + return resource.RetryableError(fmt.Errorf("replica %s is %s, waiting for task finish", *request.RegistryId, *item.Status)) + } + + if !startPolling { + return resource.RetryableError(fmt.Errorf("waiting for polling start")) + } + + return nil + }) + + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} + +func (me *TCRService) DeleteReplicationInstance(ctx context.Context, request *tcr.DeleteReplicationInstanceRequest) (errRet error) { + logId := getLogId(ctx) + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseTCRClient().DeleteReplicationInstance(request) + + if err != nil { + errRet = err + return + } + + startPolling := false + + err = resource.Retry(readRetryTimeout*3, func() *resource.RetryError { + req := tcr.NewDescribeReplicationInstancesRequest() + req.RegistryId = request.RegistryId + req.Limit = helper.IntInt64(100) + replicas, err := me.DescribeReplicationInstances(ctx, req) + if err != nil { + return retryError(err) + } + + if len(replicas) == 0 { + return nil + } + + for i := range replicas { + item := replicas[i] + if *item.Status == "Running" { + continue + } + startPolling = true + return resource.RetryableError(fmt.Errorf("replica %s is %s, waiting for task finish", *request.RegistryId, *item.Status)) + } + + if !startPolling { + return resource.RetryableError(fmt.Errorf("waiting for polling start")) + } + + return nil + }) + + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} + +func (me *TCRService) DescribeReplicationInstances(ctx context.Context, request *tcr.DescribeReplicationInstancesRequest) (list []*tcr.ReplicationRegistry, errRet error) { + logId := getLogId(ctx) + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseTCRClient().DescribeReplicationInstances(request) + + if err != nil { + errRet = err + return + } + + list = response.Response.ReplicationRegistries + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go index a16d03afe5..9e4c71a824 100644 --- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go +++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go @@ -265,7 +265,7 @@ func CompleteCommonParams(request Request, region string, requestClient string) params["Action"] = request.GetAction() params["Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) params["Nonce"] = strconv.Itoa(rand.Int()) - params["RequestClient"] = "SDK_GO_1.0.500" + params["RequestClient"] = "SDK_GO_1.0.503" if requestClient != "" { params["RequestClient"] += ": " + requestClient } diff --git a/vendor/modules.txt b/vendor/modules.txt index 6a7123fe91..ba21355853 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -557,7 +557,7 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudaudit/v20190319 # github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls v1.0.412 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls/v20201016 -# github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.500 +# github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.503 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http @@ -609,7 +609,7 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts/v20180813 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813 # github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcaplusdb/v20190823 -# github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.486 +# github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr v1.0.503 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr/v20190924 # github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq v1.0.268 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tdmq/v20200217 diff --git a/website/docs/r/tcr_instance.html.markdown b/website/docs/r/tcr_instance.html.markdown index 23fdd70cff..b79edeb374 100644 --- a/website/docs/r/tcr_instance.html.markdown +++ b/website/docs/r/tcr_instance.html.markdown @@ -40,6 +40,44 @@ resource "tencentcloud_tcr_instance" "foo" { } ``` +Create with Replications + +```hcl +resource "tencentcloud_tcr_instance" "foo" { + name = "example" + instance_type = "premium" + replications { + region_id = var.tcr_region_map["ap-guangzhou"] # 1 + } + replications { + region_id = var.tcr_region_map["ap-singapore"] # 9 + } +} + +variable "tcr_region_map" { + default = { + "ap-guangzhou" = 1 + "ap-shanghai" = 4 + "ap-hongkong" = 5 + "ap-beijing" = 8 + "ap-singapore" = 9 + "na-siliconvalley" = 15 + "ap-chengdu" = 16 + "eu-frankfurt" = 17 + "ap-seoul" = 18 + "ap-chongqing" = 19 + "ap-mumbai" = 21 + "na-ashburn" = 22 + "ap-bangkok" = 23 + "eu-moscow" = 24 + "ap-tokyo" = 25 + "ap-nanjing" = 33 + "ap-taipei" = 39 + "ap-jakarta" = 72 + } +} +``` + ## Argument Reference The following arguments are supported: @@ -48,9 +86,15 @@ The following arguments are supported: * `name` - (Required, String, ForceNew) Name of the TCR instance. * `delete_bucket` - (Optional, Bool) Indicate to delete the COS bucket which is auto-created with the instance or not. * `open_public_operation` - (Optional, Bool) Control public network access. +* `replications` - (Optional, List) Specify List of instance Replications, premium only. The available [source region list](https://www.tencentcloud.com/document/api/1051/41101) is here. * `security_policy` - (Optional, Set) Public network access allowlist policies of the TCR instance. Only available when `open_public_operation` is `true`. * `tags` - (Optional, Map) The available tags within this TCR instance. +The `replications` object supports the following: + +* `region_id` - (Optional, Int) Replication region ID, check the example at the top of page to find out id of region. +* `syn_tag` - (Optional, Bool) Specify whether to sync TCR cloud tags to COS Bucket. NOTE: You have to specify when adding, modifying will be ignored for now. + The `security_policy` object supports the following: * `cidr_block` - (Optional, String) The public network IP address of the access source.