diff --git a/.changelog/3043.txt b/.changelog/3043.txt new file mode 100644 index 0000000000..5068e290aa --- /dev/null +++ b/.changelog/3043.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/tencentcloud_eni_attachment: support retry +``` \ No newline at end of file diff --git a/tencentcloud/services/vpc/resource_tc_eni_attachment.go b/tencentcloud/services/vpc/resource_tc_eni_attachment.go index 37983bf3ca..82cc28a8c3 100644 --- a/tencentcloud/services/vpc/resource_tc_eni_attachment.go +++ b/tencentcloud/services/vpc/resource_tc_eni_attachment.go @@ -39,14 +39,16 @@ func ResourceTencentCloudEniAttachment() *schema.Resource { func resourceTencentCloudEniAttachmentCreate(d *schema.ResourceData, m interface{}) error { defer tccommon.LogElapsed("resource.tencentcloud_eni_attachment.create")() - logId := tccommon.GetLogId(tccommon.ContextNil) - ctx := context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + service = VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} + ) eniId := d.Get("eni_id").(string) cvmId := d.Get("instance_id").(string) - service := VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} - if err := service.AttachEniToCvm(ctx, eniId, cvmId); err != nil { return err } @@ -60,8 +62,11 @@ func resourceTencentCloudEniAttachmentRead(d *schema.ResourceData, m interface{} defer tccommon.LogElapsed("resource.tencentcloud_eni_attachment.read")() defer tccommon.InconsistentCheck(d, m)() - logId := tccommon.GetLogId(tccommon.ContextNil) - ctx := context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + service = VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} + ) id := d.Id() split := strings.Split(id, "+") @@ -72,9 +77,6 @@ func resourceTencentCloudEniAttachmentRead(d *schema.ResourceData, m interface{} } eniId := split[0] - - service := VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} - enis, err := service.DescribeEniById(ctx, []string{eniId}) if err != nil { return err @@ -86,7 +88,6 @@ func resourceTencentCloudEniAttachmentRead(d *schema.ResourceData, m interface{} } eni := enis[0] - if eni.Attachment == nil { d.SetId("") return nil @@ -100,8 +101,12 @@ func resourceTencentCloudEniAttachmentRead(d *schema.ResourceData, m interface{} func resourceTencentCloudEniAttachmentDelete(d *schema.ResourceData, m interface{}) error { defer tccommon.LogElapsed("resource.tencentcloud_eni_attachment.delete")() - logId := tccommon.GetLogId(tccommon.ContextNil) - ctx := context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = context.WithValue(context.TODO(), tccommon.LogIdKey, logId) + service = VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} + ) id := d.Id() split := strings.Split(id, "+") @@ -112,8 +117,5 @@ func resourceTencentCloudEniAttachmentDelete(d *schema.ResourceData, m interface } eniId, cvmId := split[0], split[1] - - service := VpcService{client: m.(tccommon.ProviderMeta).GetAPIV3Conn()} - return service.DetachEniFromCvm(ctx, eniId, cvmId) } diff --git a/tencentcloud/services/vpc/resource_tc_eni_attachment.md b/tencentcloud/services/vpc/resource_tc_eni_attachment.md index da5e5c3511..591b7d01fb 100644 --- a/tencentcloud/services/vpc/resource_tc_eni_attachment.md +++ b/tencentcloud/services/vpc/resource_tc_eni_attachment.md @@ -3,33 +3,12 @@ Provides a resource to detailed information of attached backend server to an ENI Example Usage ```hcl -resource "tencentcloud_vpc" "foo" { - name = "ci-test-eni-vpc" - cidr_block = "10.0.0.0/16" -} - -resource "tencentcloud_subnet" "foo" { - availability_zone = "ap-guangzhou-3" - name = "ci-test-eni-subnet" - vpc_id = tencentcloud_vpc.foo.id - cidr_block = "10.0.0.0/16" - is_multicast = false -} - -resource "tencentcloud_eni" "foo" { - name = "ci-test-eni" - vpc_id = tencentcloud_vpc.foo.id - subnet_id = tencentcloud_subnet.foo.id - description = "eni desc" - ipv4_count = 1 -} - -data "tencentcloud_images" "my_favorite_image" { +data "tencentcloud_images" "images" { image_type = ["PUBLIC_IMAGE"] os_name = "centos" } -data "tencentcloud_instance_types" "my_favorite_instance_types" { +data "tencentcloud_instance_types" "instance_types" { filter { name = "instance-family" values = ["S3"] @@ -39,24 +18,44 @@ data "tencentcloud_instance_types" "my_favorite_instance_types" { memory_size = 1 } -data "tencentcloud_availability_zones" "my_favorite_zones" { +data "tencentcloud_availability_zones" "zones" {} + +resource "tencentcloud_vpc" "vpc" { + name = "ci-test-eni-vpc" + cidr_block = "10.0.0.0/16" +} + +resource "tencentcloud_subnet" "subnet" { + availability_zone = "ap-guangzhou-3" + name = "ci-test-eni-subnet" + vpc_id = tencentcloud_vpc.vpc.id + cidr_block = "10.0.0.0/16" + is_multicast = false +} + +resource "tencentcloud_eni" "eni" { + name = "ci-test-eni" + vpc_id = tencentcloud_vpc.vpc.id + subnet_id = tencentcloud_subnet.subnet.id + description = "eni desc" + ipv4_count = 1 } -resource "tencentcloud_instance" "foo" { +resource "tencentcloud_instance" "example" { instance_name = "ci-test-eni-attach" - availability_zone = data.tencentcloud_availability_zones.my_favorite_zones.zones.0.name - image_id = data.tencentcloud_images.my_favorite_image.images.0.image_id - instance_type = data.tencentcloud_instance_types.my_favorite_instance_types.instance_types.0.instance_type + availability_zone = data.tencentcloud_availability_zones.zones.zones.0.name + image_id = data.tencentcloud_images.images.images.0.image_id + instance_type = data.tencentcloud_instance_types.instance_types.instance_types.0.instance_type system_disk_type = "CLOUD_PREMIUM" disable_security_service = true disable_monitor_service = true - vpc_id = tencentcloud_vpc.foo.id - subnet_id = tencentcloud_subnet.foo.id + vpc_id = tencentcloud_vpc.vpc.id + subnet_id = tencentcloud_subnet.subnet.id } -resource "tencentcloud_eni_attachment" "foo" { - eni_id = tencentcloud_eni.foo.id - instance_id = tencentcloud_instance.foo.id +resource "tencentcloud_eni_attachment" "example" { + eni_id = tencentcloud_eni.eni.id + instance_id = tencentcloud_instance.example.id } ``` @@ -65,5 +64,5 @@ Import ENI attachment can be imported using the id, e.g. ``` - $ terraform import tencentcloud_eni_attachment.foo eni-gtlvkjvz+ins-0h3a5new +terraform import tencentcloud_eni_attachment.example eni-gtlvkjvz+ins-0h3a5new ``` \ No newline at end of file diff --git a/tencentcloud/services/vpc/service_tencentcloud_vpc.go b/tencentcloud/services/vpc/service_tencentcloud_vpc.go index c978f652be..6aceb80c4c 100644 --- a/tencentcloud/services/vpc/service_tencentcloud_vpc.go +++ b/tencentcloud/services/vpc/service_tencentcloud_vpc.go @@ -3042,6 +3042,10 @@ func (me *VpcService) describeEnis( return tccommon.RetryError(err) } + if response == nil && response.Response == nil && response.Response.NetworkInterfaceSet == nil { + return resource.NonRetryableError(fmt.Errorf("Read eni list failed, Response is nil.")) + } + eniSet := response.Response.NetworkInterfaceSet count = len(eniSet) enis = append(enis, eniSet...) @@ -3295,24 +3299,36 @@ func (me *VpcService) AttachEniToCvm(ctx context.Context, eniId, cvmId string) e client := me.client.UseVpcClient() attachRequest := vpc.NewAttachNetworkInterfaceRequest() + attachResponse := vpc.NewAttachNetworkInterfaceResponse() attachRequest.NetworkInterfaceId = &eniId attachRequest.InstanceId = &cvmId if err := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { ratelimit.Check(attachRequest.GetAction()) - - if _, err := client.AttachNetworkInterface(attachRequest); err != nil { + result, err := client.AttachNetworkInterface(attachRequest) + if err != nil { log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%v]", logId, attachRequest.GetAction(), attachRequest.ToJsonString(), err) return tccommon.RetryError(err) } + if result == nil || result.Response == nil || result.Response.RequestId == nil { + return resource.NonRetryableError(fmt.Errorf("Attach eni to instance failed, Response is nil.")) + } + + attachResponse = result return nil }); err != nil { log.Printf("[CRITAL]%s attach eni to instance failed, reason: %v", logId, err) return err } + // wait + err := me.DescribeVpcTaskResult(ctx, attachResponse.Response.RequestId) + if err != nil { + return err + } + describeRequest := vpc.NewDescribeNetworkInterfacesRequest() describeRequest.NetworkInterfaceIds = []*string{&eniId} @@ -3390,13 +3406,14 @@ func (me *VpcService) DetachEniFromCvm(ctx context.Context, eniId, cvmId string) client := me.client.UseVpcClient() request := vpc.NewDetachNetworkInterfaceRequest() + response := vpc.NewDetachNetworkInterfaceResponse() request.NetworkInterfaceId = &eniId request.InstanceId = &cvmId if err := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { ratelimit.Check(request.GetAction()) - - if _, err := client.DetachNetworkInterface(request); err != nil { + result, err := client.DetachNetworkInterface(request) + if err != nil { if sdkError, ok := err.(*sdkErrors.TencentCloudSDKError); ok { switch sdkError.Code { case "UnsupportedOperation.InvalidState": @@ -3413,12 +3430,23 @@ func (me *VpcService) DetachEniFromCvm(ctx context.Context, eniId, cvmId string) return tccommon.RetryError(err) } + if result == nil || result.Response == nil || result.Response.RequestId == nil { + return resource.NonRetryableError(fmt.Errorf("Detach eni from instance failed, Response is nil.")) + } + + response = result return nil }); err != nil { log.Printf("[CRITAL]%s detach eni from instance failed, reason: %v", logId, err) return err } + // wait + err := me.DescribeVpcTaskResult(ctx, response.Response.RequestId) + if err != nil { + return err + } + if err := waitEniDetach(ctx, eniId, client); err != nil { log.Printf("[CRITAL]%s detach eni from instance failed, reason: %v", logId, err) return err @@ -5001,7 +5029,6 @@ func (me *VpcService) DescribeVpnGatewayRoutes(ctx context.Context, vpnGatewayId } func (me *VpcService) DescribeVpcTaskResult(ctx context.Context, taskId *string) (err error) { - logId := tccommon.GetLogId(ctx) request := vpc.NewDescribeVpcTaskResultRequest() defer func() { @@ -5009,6 +5036,7 @@ func (me *VpcService) DescribeVpcTaskResult(ctx context.Context, taskId *string) log.Printf("[CRITAL]%s api[%s] fail,reason[%s]", logId, request.GetAction(), err.Error()) } }() + request.TaskId = taskId err = resource.Retry(tccommon.ReadRetryTimeout, func() *resource.RetryError { ratelimit.Check(request.GetAction()) @@ -5016,14 +5044,18 @@ func (me *VpcService) DescribeVpcTaskResult(ctx context.Context, taskId *string) if err != nil { return tccommon.RetryError(err) } + if response.Response.Status != nil && *response.Response.Status == VPN_TASK_STATUS_RUNNING { return resource.RetryableError(errors.New("VPN task is running")) } + return nil }) + if err != nil { return err } + return } diff --git a/website/docs/r/eni_attachment.html.markdown b/website/docs/r/eni_attachment.html.markdown index d40e14046f..eeb49eecb7 100644 --- a/website/docs/r/eni_attachment.html.markdown +++ b/website/docs/r/eni_attachment.html.markdown @@ -14,33 +14,12 @@ Provides a resource to detailed information of attached backend server to an ENI ## Example Usage ```hcl -resource "tencentcloud_vpc" "foo" { - name = "ci-test-eni-vpc" - cidr_block = "10.0.0.0/16" -} - -resource "tencentcloud_subnet" "foo" { - availability_zone = "ap-guangzhou-3" - name = "ci-test-eni-subnet" - vpc_id = tencentcloud_vpc.foo.id - cidr_block = "10.0.0.0/16" - is_multicast = false -} - -resource "tencentcloud_eni" "foo" { - name = "ci-test-eni" - vpc_id = tencentcloud_vpc.foo.id - subnet_id = tencentcloud_subnet.foo.id - description = "eni desc" - ipv4_count = 1 -} - -data "tencentcloud_images" "my_favorite_image" { +data "tencentcloud_images" "images" { image_type = ["PUBLIC_IMAGE"] os_name = "centos" } -data "tencentcloud_instance_types" "my_favorite_instance_types" { +data "tencentcloud_instance_types" "instance_types" { filter { name = "instance-family" values = ["S3"] @@ -50,24 +29,44 @@ data "tencentcloud_instance_types" "my_favorite_instance_types" { memory_size = 1 } -data "tencentcloud_availability_zones" "my_favorite_zones" { +data "tencentcloud_availability_zones" "zones" {} + +resource "tencentcloud_vpc" "vpc" { + name = "ci-test-eni-vpc" + cidr_block = "10.0.0.0/16" +} + +resource "tencentcloud_subnet" "subnet" { + availability_zone = "ap-guangzhou-3" + name = "ci-test-eni-subnet" + vpc_id = tencentcloud_vpc.vpc.id + cidr_block = "10.0.0.0/16" + is_multicast = false +} + +resource "tencentcloud_eni" "eni" { + name = "ci-test-eni" + vpc_id = tencentcloud_vpc.vpc.id + subnet_id = tencentcloud_subnet.subnet.id + description = "eni desc" + ipv4_count = 1 } -resource "tencentcloud_instance" "foo" { +resource "tencentcloud_instance" "example" { instance_name = "ci-test-eni-attach" - availability_zone = data.tencentcloud_availability_zones.my_favorite_zones.zones.0.name - image_id = data.tencentcloud_images.my_favorite_image.images.0.image_id - instance_type = data.tencentcloud_instance_types.my_favorite_instance_types.instance_types.0.instance_type + availability_zone = data.tencentcloud_availability_zones.zones.zones.0.name + image_id = data.tencentcloud_images.images.images.0.image_id + instance_type = data.tencentcloud_instance_types.instance_types.instance_types.0.instance_type system_disk_type = "CLOUD_PREMIUM" disable_security_service = true disable_monitor_service = true - vpc_id = tencentcloud_vpc.foo.id - subnet_id = tencentcloud_subnet.foo.id + vpc_id = tencentcloud_vpc.vpc.id + subnet_id = tencentcloud_subnet.subnet.id } -resource "tencentcloud_eni_attachment" "foo" { - eni_id = tencentcloud_eni.foo.id - instance_id = tencentcloud_instance.foo.id +resource "tencentcloud_eni_attachment" "example" { + eni_id = tencentcloud_eni.eni.id + instance_id = tencentcloud_instance.example.id } ``` @@ -91,6 +90,6 @@ In addition to all arguments above, the following attributes are exported: ENI attachment can be imported using the id, e.g. ``` - $ terraform import tencentcloud_eni_attachment.foo eni-gtlvkjvz+ins-0h3a5new +terraform import tencentcloud_eni_attachment.example eni-gtlvkjvz+ins-0h3a5new ```