Skip to content

Commit

Permalink
Merge pull request #101 from volcengine/feat/ecs
Browse files Browse the repository at this point in the history
Feat/ecs
  • Loading branch information
zpp12354321 authored Jun 27, 2023
2 parents 057645a + 9802cbd commit 30a7532
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 24 deletions.
10 changes: 5 additions & 5 deletions common/common_volcengine_callback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ func TestSortAndStartTransJson1(t *testing.T) {
"ClusterId": "12345",
},
}
resp := SortAndStartTransJson(req)
resp, _ := SortAndStartTransJson(req)
assert.Equal(t, resp, target)
}

func TestSortAndStartTransJson2(t *testing.T) {
req := SortAndStartTransJson(map[string]interface{}{
req, _ := SortAndStartTransJson(map[string]interface{}{
"Filter.Ids.1": "id123",
"Filter.Ids.2": "id456",
})
Expand All @@ -30,12 +30,12 @@ func TestSortAndStartTransJson2(t *testing.T) {
"Ids": []interface{}{"id123", "id456"},
},
}
resp := SortAndStartTransJson(req)
resp, _ := SortAndStartTransJson(req)
assert.Equal(t, resp, target)
}

func TestSortAndStartTransJson3(t *testing.T) {
req := SortAndStartTransJson(map[string]interface{}{
req, _ := SortAndStartTransJson(map[string]interface{}{
"Filter.ClusterId": "12345",
"Filter.Ids.1": "id123",
"Filter.Ids.2": "id456",
Expand All @@ -61,7 +61,7 @@ func TestSortAndStartTransJson3(t *testing.T) {
},
},
}
resp := SortAndStartTransJson(req)
resp, _ := SortAndStartTransJson(req)
assert.Equal(t, resp, target)

str := `{"Filter":{"ClusterId":"12345","Ids":["id123","id456"],"Nets":[{"Subnet":"subnet1"},{"Subnet":"subnet2"},{"Subnet":"subnet3"}]}}`
Expand Down
21 changes: 20 additions & 1 deletion common/common_volcengine_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,26 @@ func (d *Dispatcher) Delete(resourceService ResourceService, resourceDate *schem
}
}
}
callbacks := resourceService.RemoveResource(resourceDate, resource)
var (
callbacks []Callback
unsubscribeInfo *UnsubscribeInfo
)

// 自动退订逻辑
if unsubscribeEnabled, ok := resourceService.(UnsubscribeEnabled); ok {
unsubscribeInfo, err = unsubscribeEnabled.UnsubscribeInfo(resourceDate, resource)
if err != nil {
return err
}
}

if unsubscribeInfo != nil && unsubscribeInfo.NeedUnsubscribe {
unsubscribeCallback := NewUnsubscribeService(resourceService.GetClient()).UnsubscribeInstance(unsubscribeInfo)
callbacks = append(callbacks, unsubscribeCallback...)
} else {
callbacks = resourceService.RemoveResource(resourceDate, resource)
}

var calls []SdkCall
for _, callback := range callbacks {
if callback.Err != nil {
Expand Down
10 changes: 8 additions & 2 deletions common/common_volcengine_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ func ResourceNotFoundError(err error) bool {
}

func ResourceFlowLimitExceededError(err error) bool {
errMessage := strings.ToLower(err.Error())
if strings.Contains(errMessage, "FlowLimitExceeded") {
if strings.Contains(err.Error(), "FlowLimitExceeded") {
return true
}
return false
}

func UnsubscribeProductError(err error) bool {
if strings.Contains(err.Error(), "The product code is inconsistent with the instance product") {
return true
}
return false
Expand Down
116 changes: 116 additions & 0 deletions common/common_volcengine_unsubscribe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package common

import (
"context"
"fmt"
"time"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/volcengine/terraform-provider-volcengine/logger"
"golang.org/x/sync/semaphore"
"golang.org/x/time/rate"
)

type UnsubscribeEnabled interface {
// UnsubscribeInfo 判断是否需要退订
UnsubscribeInfo(*schema.ResourceData, *schema.Resource) (*UnsubscribeInfo, error)
}

type UnsubscribeInfo struct {
Products []string
InstanceId string
NeedUnsubscribe bool
productIndex int
}

var unsubscribeRate *Rate

func init() {
unsubscribeRate = &Rate{
Limiter: rate.NewLimiter(10, 10),
Semaphore: semaphore.NewWeighted(20),
}
}

type UnsubscribeService struct {
Client *SdkClient
}

func NewUnsubscribeService(c *SdkClient) *UnsubscribeService {
return &UnsubscribeService{
Client: c,
}
}

func (u *UnsubscribeService) UnsubscribeInstance(info *UnsubscribeInfo) []Callback {
var call []Callback
if len(info.Products) == 0 || info.InstanceId == "" {
return call
}
unsubscribe := Callback{
Call: SdkCall{
Action: "UnsubscribeInstance",
ConvertMode: RequestConvertIgnore,
ContentType: ContentTypeJson,
BeforeCall: func(d *schema.ResourceData, client *SdkClient, call SdkCall) (bool, error) {
(*call.SdkParam)["InstanceID"] = info.InstanceId
(*call.SdkParam)["UnsubscribeRelatedInstance"] = true
(*call.SdkParam)["ClientToken"] = uuid.New().String()
return true, nil
},
ExecuteCall: func(d *schema.ResourceData, client *SdkClient, call SdkCall) (*map[string]interface{}, error) {
defer func() {
unsubscribeRate.Semaphore.Release(1)
}()
var err error
ctx := context.Background()
err = unsubscribeRate.Limiter.Wait(ctx)
if err != nil {
return nil, err
}
err = unsubscribeRate.Semaphore.Acquire(ctx, 1)
if err != nil {
return nil, err
}

(*call.SdkParam)["Product"] = info.Products[info.productIndex]
logger.Debug(logger.ReqFormat, call.Action, call.SdkParam)
return u.Client.UniversalClient.DoCall(u.getUniversalInfo(call.Action), call.SdkParam)
},
AfterCall: func(d *schema.ResourceData, client *SdkClient, resp *map[string]interface{}, call SdkCall) error {
return nil
},
CallError: func(d *schema.ResourceData, client *SdkClient, call SdkCall, baseErr error) error {
return resource.Retry(15*time.Minute, func() *resource.RetryError {
if UnsubscribeProductError(baseErr) {
info.productIndex = info.productIndex + 1
if info.productIndex >= len(info.Products) {
return resource.NonRetryableError(fmt.Errorf(" Can not support this instance %s Unsubscribe", info.InstanceId))
}
_, callErr := call.ExecuteCall(d, client, call)
if callErr == nil {
return nil
}
return resource.RetryableError(callErr)
} else {
return resource.NonRetryableError(baseErr)
}
})
},
},
}
call = append(call, unsubscribe)
return call
}

func (u *UnsubscribeService) getUniversalInfo(actionName string) UniversalInfo {
return UniversalInfo{
ServiceName: "billing",
Version: "2022-01-01",
HttpMethod: POST,
Action: actionName,
ContentType: ApplicationJSON,
}
}
2 changes: 1 addition & 1 deletion common/common_volcengine_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package common

const (
TerraformProviderName = "terraform-provider-volcengine"
TerraformProviderVersion = "0.0.84"
TerraformProviderVersion = "0.0.85"
)
8 changes: 5 additions & 3 deletions example/networkInterface/main.tf
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
resource "volcengine_network_interface" "foo" {
subnet_id = "subnet-im67x70vxla88gbssz1hy1z2"
security_group_ids = ["sg-im67wp9lx3i88gbssz3d22b2"]
primary_ip_address = "192.168.0.253"
subnet_id = "subnet-2fe79j7c8o5c059gp68ksxr93"
security_group_ids = ["sg-2fepz3c793g1s59gp67y21r34"]
primary_ip_address = "192.168.5.253"
network_interface_name = "tf-test-up"
description = "tf-test-up"
port_security_enabled = false
project_name = "default"
private_ip_address = ["192.168.5.2"]
//secondary_private_ip_address_count = 0
}
38 changes: 38 additions & 0 deletions volcengine/ecs/ecs_instance/service_volcengine_ecs_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -1426,3 +1426,41 @@ func (s *VolcengineEcsService) ProjectTrn() *ve.ProjectTrn {
ProjectSchemaField: "project_name",
}
}

func (s *VolcengineEcsService) UnsubscribeInfo(resourceData *schema.ResourceData, resource *schema.Resource) (*ve.UnsubscribeInfo, error) {
info := ve.UnsubscribeInfo{
InstanceId: s.ReadResourceId(resourceData.Id()),
}
if resourceData.Get("instance_charge_type") == "PrePaid" {
//查询实例类型的配置
action := "DescribeInstanceTypes"
input := map[string]interface{}{
"InstanceTypeIds.1": resourceData.Get("instance_type"),
}
var (
output *map[string]interface{}
err error
t interface{}
)
output, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &input)
if err != nil {
return &info, err
}
t, err = ve.ObtainSdkValue("Result.InstanceTypes.0", *output)
if err != nil {
return &info, err
}
if tt, ok := t.(map[string]interface{}); ok {
if tt["Gpu"] != nil && tt["Rdma"] != nil {
info.Products = []string{"HPC_GPU", "ECS", "ECS_BareMetal", "GPU_Server"}
} else if tt["Gpu"] != nil && tt["Rdma"] == nil {
info.Products = []string{"GPU_Server", "ECS", "ECS_BareMetal", "HPC_GPU"}
} else {
info.Products = []string{"ECS", "ECS_BareMetal", "GPU_Server", "HPC_GPU"}
}
info.NeedUnsubscribe = true
}

}
return &info, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,22 @@ func ResourceVolcengineNetworkInterface() *schema.Resource {
Description: "Set port security enable or disable.",
},
"secondary_private_ip_address_count": {
Type: schema.TypeInt,
Optional: true,
Description: "The count of secondary private ip address.",
Type: schema.TypeInt,
Optional: true,
Computed: true,
ConflictsWith: []string{"private_ip_address"},
Description: "The count of secondary private ip address. This field conflicts with `private_ip_address`.",
},
"private_ip_address": {
Type: schema.TypeSet,
Optional: true,
Description: "The list of private ip address.",
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Computed: true,
Set: schema.HashString,
ConflictsWith: []string{"secondary_private_ip_address_count"},
Description: "The list of private ip address. This field conflicts with `secondary_private_ip_address_count`.",
},
"project_name": {
Type: schema.TypeString,
Expand Down
Loading

0 comments on commit 30a7532

Please sign in to comment.