From fd6cdedb228c8e8c1c5ac22041b3f69102ebb004 Mon Sep 17 00:00:00 2001 From: Kagashino Date: Wed, 15 Jun 2022 10:05:05 +0800 Subject: [PATCH] feat: clb - support snat pro and snat ips --- tencentcloud/provider.go | 2 + tencentcloud/resource_tc_clb_instance.go | 105 ++++-- tencentcloud/resource_tc_clb_instance_test.go | 51 +++ tencentcloud/resource_tc_clb_snat_ip.go | 321 ++++++++++++++++++ tencentcloud/resource_tc_clb_snat_ip_test.go | 151 ++++++++ tencentcloud/service_tencentcloud_clb.go | 58 ++++ website/docs/r/clb_instance.html.markdown | 7 + website/docs/r/clb_snat_ip.html.markdown | 64 ++++ website/tencentcloud.erb | 3 + 9 files changed, 735 insertions(+), 27 deletions(-) create mode 100644 tencentcloud/resource_tc_clb_snat_ip.go create mode 100644 tencentcloud/resource_tc_clb_snat_ip_test.go create mode 100644 website/docs/r/clb_snat_ip.html.markdown diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index bb0e2a2463..404a0cddfd 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -244,6 +244,7 @@ Cloud Load Balancer(CLB) tencentcloud_clb_log_set tencentcloud_clb_log_topic tencentcloud_clb_customized_config + tencentcloud_clb_snat_ip Cloud Object Storage(COS) Data Source @@ -965,6 +966,7 @@ func Provider() terraform.ResourceProvider { "tencentcloud_clb_log_set": resourceTencentCloudClbLogSet(), "tencentcloud_clb_log_topic": resourceTencentCloudClbLogTopic(), "tencentcloud_clb_customized_config": resourceTencentCloudClbCustomizedConfig(), + "tencentcloud_clb_snat_ip": resourceTencentCloudClbSnatIp(), "tencentcloud_container_cluster": resourceTencentCloudContainerCluster(), "tencentcloud_container_cluster_instance": resourceTencentCloudContainerClusterInstance(), "tencentcloud_kubernetes_cluster": resourceTencentCloudTkeCluster(), diff --git a/tencentcloud/resource_tc_clb_instance.go b/tencentcloud/resource_tc_clb_instance.go index 43c30d9944..8bea973b5d 100644 --- a/tencentcloud/resource_tc_clb_instance.go +++ b/tencentcloud/resource_tc_clb_instance.go @@ -152,6 +152,8 @@ import ( "log" "sync" + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/pkg/errors" @@ -253,6 +255,30 @@ func resourceTencentCloudClbInstance() *schema.Resource { Computed: true, Description: "Vpc information of backend services are attached the CLB instance. Only supports `OPEN` CLBs.", }, + "snat_pro": { + Type: schema.TypeBool, + Optional: true, + Description: "Indicates whether Binding IPs of other VPCs feature switch.", + }, + "snat_ips": { + Type: schema.TypeList, + Optional: true, + Description: "Snat Ip List, required with `snat_pro=true`. NOTE: This argument cannot be read and modified here because dynamic ip is untraceable, please import resource `tencentcloud_clb_snat_ip` to handle fixed ips.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Optional: true, + Description: "Snat IP address, If set to empty will auto allocated.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + Description: "Snat subnet ID.", + }, + }, + }, + }, "tags": { Type: schema.TypeMap, Optional: true, @@ -366,6 +392,24 @@ func resourceTencentCloudClbInstanceCreate(d *schema.ResourceData, meta interfac request.AddressIPVersion = helper.String(v.(string)) } + if v, ok := d.GetOk("snat_pro"); ok { + request.SnatPro = helper.Bool(v.(bool)) + } + + if v, ok := d.Get("snat_ips").([]interface{}); ok && len(v) > 0 { + for i := range v { + item := v[i].(map[string]interface{}) + subnetId := item["subnet_id"].(string) + snatIp := &clb.SnatIp{ + SubnetId: &subnetId, + } + if v, ok := item["ip"].(string); ok && v != "" { + snatIp.Ip = &v + } + request.SnatIps = append(request.SnatIps, snatIp) + } + } + v, ok := d.GetOk("internet_charge_type") bv, bok := d.GetOk("internet_bandwidth_max_out") pv, pok := d.GetOk("bandwidth_package_id") @@ -613,6 +657,10 @@ func resourceTencentCloudClbInstanceRead(d *schema.ResourceData, meta interface{ _ = d.Set("log_set_id", instance.LogSetId) _ = d.Set("log_topic_id", instance.LogTopicId) + if _, ok := d.GetOk("snat_pro"); ok { + _ = d.Set("snat_pro", instance.SnatPro) + } + tcClient := meta.(*TencentCloudClient).apiV3Conn tagService := &TagService{client: tcClient} tags, err := tagService.DescribeResourceTags(ctx, "clb", "clb", tcClient.Region, d.Id()) @@ -631,15 +679,19 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac defer clbActionMu.Unlock() logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) d.Partial(true) clbId := d.Id() + request := clb.NewModifyLoadBalancerAttributesRequest() + request.LoadBalancerId = helper.String(clbId) clbName := "" targetRegionInfo := clb.TargetRegionInfo{} internet := clb.InternetAccessible{} changed := false isLoadBalancerPassToTgt := false + snatPro := d.Get("snat_pro").(bool) if d.HasChange("clb_name") { changed = true @@ -651,6 +703,7 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac if flag { return fmt.Errorf("[CHECK][CLB instance][Update] check: Same CLB name %s exists!", clbName) } + request.LoadBalancerName = helper.String(clbName) } if d.HasChange("target_region_info_region") || d.HasChange("target_region_info_vpc_id") { @@ -664,6 +717,7 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac Region: ®ion, VpcId: &vpcId, } + request.TargetRegionInfo = &targetRegionInfo } if d.HasChange("internet_charge_type") || d.HasChange("internet_bandwidth_max_out") { @@ -679,28 +733,25 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac if bandwidth > 0 { internet.InternetMaxBandwidthOut = helper.IntInt64(bandwidth) } + request.InternetChargeInfo = &internet } if d.HasChange("load_balancer_pass_to_target") { changed = true isLoadBalancerPassToTgt = d.Get("load_balancer_pass_to_target").(bool) + request.LoadBalancerPassToTarget = &isLoadBalancerPassToTgt + } + + if d.HasChange("snat_pro") { + changed = true + request.SnatPro = &snatPro + } + + if d.HasChange("snat_ips") { + return fmt.Errorf("`snat_ips`") } if changed { - request := clb.NewModifyLoadBalancerAttributesRequest() - request.LoadBalancerId = helper.String(clbId) - if d.HasChange("clb_name") { - request.LoadBalancerName = helper.String(clbName) - } - if d.HasChange("target_region_info_region") || d.HasChange("target_region_info_vpc_id") { - request.TargetRegionInfo = &targetRegionInfo - } - if d.HasChange("internet_charge_type") || d.HasChange("internet_bandwidth_max_out") { - request.InternetChargeInfo = &internet - } - if d.HasChange("load_balancer_pass_to_target") { - request.LoadBalancerPassToTarget = &isLoadBalancerPassToTgt - } err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { response, e := meta.(*TencentCloudClient).apiV3Conn.UseClbClient().ModifyLoadBalancerAttributes(request) if e != nil { @@ -720,18 +771,6 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac log.Printf("[CRITAL]%s update CLB instance failed, reason:%+v", logId, err) return err } - if d.HasChange("clb_name") { - d.SetPartial("clb_name") - } - if d.HasChange("clb_vips") { - d.SetPartial("clb_vips") - } - if d.HasChange("target_region_info_region") { - d.SetPartial("target_region_info_region") - } - if d.HasChange("target_region_info_vpc_id") { - d.SetPartial("target_region_info_vpc_id") - } } if d.HasChange("security_groups") { @@ -794,7 +833,7 @@ func resourceTencentCloudClbInstanceUpdate(d *schema.ResourceData, meta interfac return err } } - ctx := context.WithValue(context.TODO(), logIdKey, logId) + if d.HasChange("tags") { oldValue, newValue := d.GetChange("tags") @@ -875,3 +914,15 @@ func checkSameName(name string, meta interface{}) (flag bool, errRet error) { errRet = err return } + +func snatIpSetInitFn(i interface{}) int { + item := i.(map[string]interface{}) + subnet := item["subnet_id"].(string) + allocatedIp := item["allocated_snat_ip"].(string) + ip, ok := item["ip"].(string) + + if !ok || ip == "" { + ip = allocatedIp + } + return hashcode.String(subnet + ip) +} diff --git a/tencentcloud/resource_tc_clb_instance_test.go b/tencentcloud/resource_tc_clb_instance_test.go index c9b88910a4..708d2a5327 100644 --- a/tencentcloud/resource_tc_clb_instance_test.go +++ b/tencentcloud/resource_tc_clb_instance_test.go @@ -13,6 +13,7 @@ import ( ) const BasicClbName = "tf-clb-basic" +const SnatClbName = "tf-clb-snat" const InternalClbName = "tf-clb-internal" const InternalClbNameUpdate = "tf-clb-update-internal" const SingleClbName = "single-open-clb" @@ -136,6 +137,27 @@ func TestAccTencentCloudClbInstance_open(t *testing.T) { }) } +func TestAccTencentCloudClbInstance_snat(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckClbInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClbInstance_snat, + Check: resource.ComposeTestCheckFunc( + testAccCheckClbInstanceExists("tencentcloud_clb_instance.clb_basic"), + resource.TestCheckResourceAttr("tencentcloud_clb_instance.clb_basic", "network_type", "OPEN"), + resource.TestCheckResourceAttr("tencentcloud_clb_instance.clb_basic", "clb_name", SnatClbName), + resource.TestCheckResourceAttr("tencentcloud_clb_instance.clb_basic", "snat_pro", "true"), + ), + }, + }, + }) +} + func TestAccTencentCloudClbInstance_internal(t *testing.T) { t.Parallel() @@ -310,6 +332,35 @@ resource "tencentcloud_clb_instance" "clb_basic" { } ` +const testAccClbInstance_snat = ` +data "tencentcloud_vpc_instances" "gz3vpc" { + name = "Default-" + is_default = true +} + +data "tencentcloud_vpc_subnets" "gz3" { + vpc_id = data.tencentcloud_vpc_instances.gz3vpc.instance_list.0.vpc_id +} + +locals { + keep_clb_subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") if lookup(subnet, "name") == "keep-clb-sub"] + subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") ] + subnet_for_clb_snat = concat(local.keep_clb_subnets, local.subnets) +} + +resource "tencentcloud_clb_instance" "clb_basic" { + network_type = "OPEN" + clb_name = "` + SnatClbName + `" + snat_pro = true + snat_ips { + subnet_id = local.subnet_for_clb_snat.0 + } + snat_ips { + subnet_id = local.subnet_for_clb_snat.1 + } +} +` + const testAccClbInstance_internal = ` variable "availability_zone" { default = "ap-guangzhou-3" diff --git a/tencentcloud/resource_tc_clb_snat_ip.go b/tencentcloud/resource_tc_clb_snat_ip.go new file mode 100644 index 0000000000..1c1f7ec039 --- /dev/null +++ b/tencentcloud/resource_tc_clb_snat_ip.go @@ -0,0 +1,321 @@ +/* +Provide a resource to create a SnatIp of CLB instance. + +~> **NOTE:** Target CLB instance must enable `snat_pro` before creating snat ips. +~> **NOTE:** Dynamic allocate IP doesn't support for now. + +Example Usage + +```hcl +resource "tencentcloud_clb_instance" "snat_test" { + network_type = "OPEN" + clb_name = "tf-clb-snat-test" +} + +resource "tencentcloud_clb_snat_ip" "foo" { + clb_id = tencentcloud_clb_instance.snat_test.id + ips { + subnet_id = "subnet-12345678" + ip = "172.16.0.1" + } + ips { + subnet_id = "subnet-12345678" + ip = "172.16.0.2" + } +} + +``` + + +Import + +ClbSnatIp instance can be imported by clb instance id, e.g. +``` +$ terraform import tencentcloud_clb_snat_ip.test clb_id +``` + +*/ +package tencentcloud + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + clb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317" +) + +func resourceTencentCloudClbSnatIp() *schema.Resource { + return &schema.Resource{ + Read: resourceTencentCloudClbSnatIpRead, + Create: resourceTencentCloudClbSnatIpCreate, + Update: resourceTencentCloudClbSnatIpUpdate, + Delete: resourceTencentCloudClbSnatIpDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "clb_id": { + Type: schema.TypeString, + Required: true, + Description: "CLB instance ID.", + }, + "ips": { + Type: schema.TypeSet, + Optional: true, + Description: "Snat IP address config.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Required: true, + Description: "Snat IP.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + Description: "Subnet ID.", + }, + }, + }, + }, + }, + } +} + +func resourceTencentCloudClbSnatIpRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_clb_snat_ip.read")() + defer inconsistentCheck(d, meta)() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := ClbService{client} + + clbId := d.Id() + + var ( + instance *clb.LoadBalancer + err error + ) + + err = resource.Retry(readRetryTimeout, func() *resource.RetryError { + instance, err = service.DescribeLoadBalancerById(ctx, clbId) + if err != nil { + return retryError(err) + } + return nil + }) + + if err != nil { + d.SetId("") + return err + } + + d.SetId(clbId) + + if ipLen := len(instance.SnatIps); ipLen > 0 { + snatIps := make([]interface{}, 0) + for i := range instance.SnatIps { + item := instance.SnatIps[i] + snatIps = append(snatIps, map[string]interface{}{ + "subnet_id": *item.SubnetId, + "ip": *item.Ip, + }) + } + _ = d.Set("ips", snatIps) + } + + _ = d.Set("clb_id", clbId) + + return nil +} + +func resourceTencentCloudClbSnatIpCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_clb_snat_ip.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := ClbService{client} + + clbId := d.Get("clb_id").(string) + ips := d.Get("ips").(*schema.Set).List() + snatIps := make([]*clb.SnatIp, 0, len(ips)) + for i := range ips { + item := ips[i].(map[string]interface{}) + subnetId := item["subnet_id"].(string) + ip := item["ip"].(string) + snatIp := &clb.SnatIp{ + SubnetId: &subnetId, + Ip: &ip, + } + snatIps = append(snatIps, snatIp) + } + + var taskId string + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + reqId, err := service.CreateLoadBalancerSnatIps(ctx, clbId, snatIps) + if err != nil { + return retryError(err, clb.FAILEDOPERATION) + } + taskId = reqId + return nil + }) + + if err != nil { + return err + } + + if err := waitForTaskFinish(taskId, client.UseClbClient()); err != nil { + return err + } + + d.SetId(clbId) + + return resourceTencentCloudClbSnatIpRead(d, meta) +} + +func resourceTencentCloudClbSnatIpUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_clb_snat_ip.update")() + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + clbId := d.Id() + client := meta.(*TencentCloudClient).apiV3Conn + service := ClbService{client} + + instanceSnatIps := make([]interface{}, 0) + instance, err := service.DescribeLoadBalancerById(ctx, clbId) + if err != nil { + return err + } + + if len(instance.SnatIps) > 0 { + for i := range instance.SnatIps { + subnet := *instance.SnatIps[i].SubnetId + ip := *instance.SnatIps[i].Ip + instanceSnatIps = append(instanceSnatIps, map[string]interface{}{ + "ip": ip, + "subnet_id": subnet, + }) + } + } + + if d.HasChange("ips") { + o, n := d.GetChange("ips") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + add := ns.Difference(os).List() + remove := os.Difference(ns).List() + + for i := range add { + var ( + addIps []*clb.SnatIp + taskId string + err error + ) + item := add[i].(map[string]interface{}) + ip := item["ip"].(string) + subnet := item["subnet_id"].(string) + addIps = append(addIps, &clb.SnatIp{ + SubnetId: &subnet, + Ip: &ip, + }) + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + taskId, err = service.CreateLoadBalancerSnatIps(ctx, clbId, addIps) + if err != nil { + return retryError(err, clb.FAILEDOPERATION) + } + return nil + }) + if err != nil { + return err + } + + err = waitForTaskFinish(taskId, client.UseClbClient()) + if err != nil { + return err + } + } + + if len(remove) > 0 { + var ( + removeIps []*string + taskId string + err error + ) + + for i := range remove { + item := remove[i].(map[string]interface{}) + ip := item["ip"].(string) + removeIps = append(removeIps, &ip) + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + taskId, err = service.DeleteLoadBalancerSnatIps(ctx, clbId, removeIps) + if err != nil { + return retryError(err, clb.FAILEDOPERATION) + } + return nil + }) + if err != nil { + return err + } + + err = waitForTaskFinish(taskId, client.UseClbClient()) + if err != nil { + return err + } + } + + } + + return resourceTencentCloudClbSnatIpRead(d, meta) +} + +func resourceTencentCloudClbSnatIpDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_clb_snat_ip.delete")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := ClbService{client} + + clbId := d.Id() + + instanceSnatIps := make([]*string, 0) + instance, err := service.DescribeLoadBalancerById(ctx, clbId) + if err != nil { + return err + } + + if len(instance.SnatIps) > 0 { + for i := range instance.SnatIps { + ip := instance.SnatIps[i].Ip + instanceSnatIps = append(instanceSnatIps, ip) + } + } + + var taskId string + err = resource.Retry(readRetryTimeout, func() *resource.RetryError { + reqId, err := service.DeleteLoadBalancerSnatIps(ctx, clbId, instanceSnatIps) + if err != nil { + return retryError(err, clb.FAILEDOPERATION) + } + taskId = reqId + return nil + }) + + if err != nil { + return err + } + + if err := waitForTaskFinish(taskId, client.UseClbClient()); err != nil { + return err + } + + d.SetId("") + + return err +} diff --git a/tencentcloud/resource_tc_clb_snat_ip_test.go b/tencentcloud/resource_tc_clb_snat_ip_test.go new file mode 100644 index 0000000000..95e3f1793a --- /dev/null +++ b/tencentcloud/resource_tc_clb_snat_ip_test.go @@ -0,0 +1,151 @@ +package tencentcloud + +import ( + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccTencentCloudClbSnatIp(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccClbSnatIpBasic, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_clb_snat_ip.snat_ips", "id"), + resource.TestCheckResourceAttr("tencentcloud_clb_snat_ip.snat_ips", "ips.#", "3"), + ), + }, + { + PreConfig: func() { + time.Sleep(time.Second * 10) + }, + Config: testAccClbSnatIpBasicUpdate, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_clb_snat_ip.snat_ips", "id"), + resource.TestCheckResourceAttr("tencentcloud_clb_snat_ip.snat_ips", "ips.#", "3"), + ), + }, + { + PreConfig: func() { + time.Sleep(time.Second * 10) + }, + Config: testAccClbSnatIpBasicUpdate2, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_clb_snat_ip.snat_ips", "id"), + resource.TestCheckResourceAttr("tencentcloud_clb_snat_ip.snat_ips", "ips.#", "1"), + ), + }, + }, + }) +} + +const testAccClbSnatIpBasic = ` +data "tencentcloud_vpc_instances" "gz3vpc" { + name = "Default-" + is_default = true +} + +data "tencentcloud_vpc_subnets" "gz3" { + vpc_id = data.tencentcloud_vpc_instances.gz3vpc.instance_list.0.vpc_id +} + +locals { + keep_clb_subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") if lookup(subnet, "name") == "keep-clb-sub"] + subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") ] + subnet_for_clb_snat = concat(local.keep_clb_subnets, local.subnets) +} + +resource "tencentcloud_clb_instance" "foo" { + network_type = "OPEN" + clb_name = "tf-clb-snat-resource-test" +} + +resource "tencentcloud_clb_snat_ip" "snat_ips" { + clb_id = tencentcloud_clb_instance.foo.id + ips { + ip = "172.16.151.17" + subnet_id = local.subnet_for_clb_snat.0 + } + ips { + ip = "172.16.151.15" + subnet_id = local.subnet_for_clb_snat.0 + } + ips { + ip = "172.16.151.138" + subnet_id = local.subnet_for_clb_snat.1 + } +} +` + +const testAccClbSnatIpBasicUpdate = ` +data "tencentcloud_vpc_instances" "gz3vpc" { + name = "Default-" + is_default = true +} + +data "tencentcloud_vpc_subnets" "gz3" { + vpc_id = data.tencentcloud_vpc_instances.gz3vpc.instance_list.0.vpc_id +} + +locals { + keep_clb_subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") if lookup(subnet, "name") == "keep-clb-sub"] + subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") ] + subnet_for_clb_snat = concat(local.keep_clb_subnets, local.subnets) +} + +resource "tencentcloud_clb_instance" "foo" { + network_type = "OPEN" + clb_name = "tf-clb-snat-resource-test" +} + +resource "tencentcloud_clb_snat_ip" "snat_ips" { + clb_id = tencentcloud_clb_instance.foo.id + ips { + ip = "172.16.151.17" + subnet_id = local.subnet_for_clb_snat.0 + } + ips { + ip = "172.16.151.138" + subnet_id = local.subnet_for_clb_snat.1 + } + ips { + ip = "172.16.151.139" + subnet_id = local.subnet_for_clb_snat.1 + } +} +` + +const testAccClbSnatIpBasicUpdate2 = ` +data "tencentcloud_vpc_instances" "gz3vpc" { + name = "Default-" + is_default = true +} + +data "tencentcloud_vpc_subnets" "gz3" { + vpc_id = data.tencentcloud_vpc_instances.gz3vpc.instance_list.0.vpc_id +} + +locals { + keep_clb_subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") if lookup(subnet, "name") == "keep-clb-sub"] + subnets = [for subnet in data.tencentcloud_vpc_subnets.gz3.instance_list: lookup(subnet, "subnet_id") ] + subnet_for_clb_snat = concat(local.keep_clb_subnets, local.subnets) +} + +resource "tencentcloud_clb_instance" "foo" { + network_type = "OPEN" + clb_name = "tf-clb-snat-resource-test" +} + +resource "tencentcloud_clb_snat_ip" "snat_ips" { + clb_id = tencentcloud_clb_instance.foo.id + ips { + ip = "172.16.151.16" + subnet_id = local.subnet_for_clb_snat.0 + } +} +` diff --git a/tencentcloud/service_tencentcloud_clb.go b/tencentcloud/service_tencentcloud_clb.go index 16af3bc9d0..31e4e89f66 100644 --- a/tencentcloud/service_tencentcloud_clb.go +++ b/tencentcloud/service_tencentcloud_clb.go @@ -1506,6 +1506,64 @@ func (me *ClbService) BindOrUnBindCustomizedConfigWithLbId(ctx context.Context, return } +func (me *ClbService) CreateLoadBalancerSnatIps(ctx context.Context, id string, ips []*clb.SnatIp) (taskId string, errRet error) { + logId := getLogId(ctx) + request := clb.NewCreateLoadBalancerSnatIpsRequest() + 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()) + } + }() + + request.LoadBalancerId = &id + request.SnatIps = ips + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseClbClient().CreateLoadBalancerSnatIps(request) + + if err != nil { + errRet = err + return + } + + taskId = *response.Response.RequestId + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} + +func (me *ClbService) DeleteLoadBalancerSnatIps(ctx context.Context, id string, ips []*string) (taskId string, errRet error) { + logId := getLogId(ctx) + request := clb.NewDeleteLoadBalancerSnatIpsRequest() + 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()) + } + }() + + request.LoadBalancerId = &id + request.Ips = ips + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseClbClient().DeleteLoadBalancerSnatIps(request) + + if err != nil { + errRet = err + return + } + + taskId = *response.Response.RequestId + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} + func extractBindClbList(itemlist []*clb.BindDetailItem) (lbList []interface{}) { result := make([]interface{}, 0, len(itemlist)) for _, v := range itemlist { diff --git a/website/docs/r/clb_instance.html.markdown b/website/docs/r/clb_instance.html.markdown index 5f90be69d8..74b56b9ecd 100644 --- a/website/docs/r/clb_instance.html.markdown +++ b/website/docs/r/clb_instance.html.markdown @@ -163,6 +163,8 @@ The following arguments are supported: * `project_id` - (Optional, ForceNew) ID of the project within the CLB instance, `0` - Default Project. * `security_groups` - (Optional) Security groups of the CLB instance. Supports both `OPEN` and `INTERNAL` CLBs. * `slave_zone_id` - (Optional) Setting slave zone id of cross available zone disaster recovery, only applicable to open CLB. this zone will undertake traffic when the master is down. +* `snat_ips` - (Optional) Snat Ip List, required with `snat_pro=true`. NOTE: This argument cannot be read and modified here because dynamic ip is untraceable, please import resource `tencentcloud_clb_snat_ip` to handle fixed ips. +* `snat_pro` - (Optional) Indicates whether Binding IPs of other VPCs feature switch. * `subnet_id` - (Optional, ForceNew) Subnet ID of the CLB. Effective only for CLB within the VPC. Only supports `INTERNAL` CLBs. Default is `ipv4`. * `tags` - (Optional) The available tags within this CLB. * `target_region_info_region` - (Optional) Region information of backend services are attached the CLB instance. Only supports `OPEN` CLBs. @@ -170,6 +172,11 @@ The following arguments are supported: * `vpc_id` - (Optional, ForceNew) VPC ID of the CLB. * `zone_id` - (Optional) Available zone id, only applicable to open CLB. +The `snat_ips` object supports the following: + +* `subnet_id` - (Required) Snat subnet ID. +* `ip` - (Optional) Snat IP address, If set to empty will auto allocated. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: diff --git a/website/docs/r/clb_snat_ip.html.markdown b/website/docs/r/clb_snat_ip.html.markdown new file mode 100644 index 0000000000..e870a9c613 --- /dev/null +++ b/website/docs/r/clb_snat_ip.html.markdown @@ -0,0 +1,64 @@ +--- +subcategory: "Cloud Load Balancer(CLB)" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_clb_snat_ip" +sidebar_current: "docs-tencentcloud-resource-clb_snat_ip" +description: |- + Provide a resource to create a SnatIp of CLB instance. +--- + +# tencentcloud_clb_snat_ip + +Provide a resource to create a SnatIp of CLB instance. + +~> **NOTE:** Target CLB instance must enable `snat_pro` before creating snat ips. +~> **NOTE:** Dynamic allocate IP doesn't support for now. + +## Example Usage + +```hcl +resource "tencentcloud_clb_instance" "snat_test" { + network_type = "OPEN" + clb_name = "tf-clb-snat-test" +} + +resource "tencentcloud_clb_snat_ip" "foo" { + clb_id = tencentcloud_clb_instance.snat_test.id + ips { + subnet_id = "subnet-12345678" + ip = "172.16.0.1" + } + ips { + subnet_id = "subnet-12345678" + ip = "172.16.0.2" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `clb_id` - (Required) CLB instance ID. +* `ips` - (Optional) Snat IP address config. + +The `ips` object supports the following: + +* `ip` - (Required) Snat IP. +* `subnet_id` - (Required) Subnet ID. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. + + + +## Import + +ClbSnatIp instance can be imported by clb instance id, e.g. +``` +$ terraform import tencentcloud_clb_snat_ip.test clb_id +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 2f5abc691b..b12981ecfb 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -592,6 +592,9 @@
  • tencentcloud_clb_redirection
  • +
  • + tencentcloud_clb_snat_ip +
  • tencentcloud_clb_target_group