From 35bbf4f1036a7e2116797ae2aac810c7b5cdf749 Mon Sep 17 00:00:00 2001 From: zhangpeihua Date: Tue, 11 Apr 2023 10:51:29 +0800 Subject: [PATCH 01/11] feat: add private link services --- .../main.tf | 3 + .../main.tf | 3 + example/privatelinkVpcEndpointService/main.tf | 8 + .../main.tf | 9 + .../main.tf | 14 + ...ngine_privatelink_vpc_endpoint_services.go | 151 ++++++++ ...engine_privatelink_vpc_endpoint_service.go | 169 +++++++++ ...engine_privatelink_vpc_endpoint_service.go | 326 ++++++++++++++++++ ...telink_vpc_endpoint_service_permissions.go | 53 +++ ...atelink_vpc_endpoint_service_permission.go | 89 +++++ ...atelink_vpc_endpoint_service_permission.go | 188 ++++++++++ ...ivatelink_vpc_endpoint_service_resource.go | 89 +++++ ...ivatelink_vpc_endpoint_service_resource.go | 192 +++++++++++ volcengine/provider.go | 12 + 14 files changed, 1306 insertions(+) create mode 100644 example/dataPrivatelinkVpcEndpointServicePermissions/main.tf create mode 100644 example/dataPrivatelinkVpcEndpointServices/main.tf create mode 100644 example/privatelinkVpcEndpointService/main.tf create mode 100644 example/privatelinkVpcEndpointServicePermission/main.tf create mode 100644 example/privatelinkVpcEndpointServiceResource/main.tf create mode 100644 volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go create mode 100644 volcengine/privatelink/vpc_endpoint_service/resource_volcengine_privatelink_vpc_endpoint_service.go create mode 100644 volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go create mode 100644 volcengine/privatelink/vpc_endpoint_service_permission/data_source_volcengine_privatelink_vpc_endpoint_service_permissions.go create mode 100644 volcengine/privatelink/vpc_endpoint_service_permission/resource_volcengine_privatelink_vpc_endpoint_service_permission.go create mode 100644 volcengine/privatelink/vpc_endpoint_service_permission/service_volcengine_privatelink_vpc_endpoint_service_permission.go create mode 100644 volcengine/privatelink/vpc_endpoint_service_resource/resource_volcengine_privatelink_vpc_endpoint_service_resource.go create mode 100644 volcengine/privatelink/vpc_endpoint_service_resource/service_volcengine_privatelink_vpc_endpoint_service_resource.go diff --git a/example/dataPrivatelinkVpcEndpointServicePermissions/main.tf b/example/dataPrivatelinkVpcEndpointServicePermissions/main.tf new file mode 100644 index 00000000..1961a8ae --- /dev/null +++ b/example/dataPrivatelinkVpcEndpointServicePermissions/main.tf @@ -0,0 +1,3 @@ +data "volcengine_privatelink_vpc_endpoint_service_permissions" "default" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" +} \ No newline at end of file diff --git a/example/dataPrivatelinkVpcEndpointServices/main.tf b/example/dataPrivatelinkVpcEndpointServices/main.tf new file mode 100644 index 00000000..16f8417f --- /dev/null +++ b/example/dataPrivatelinkVpcEndpointServices/main.tf @@ -0,0 +1,3 @@ +data "volcengine_privatelink_vpc_endpoint_services" "default" { + ids = ["epsvc-3rel73uf2ewao5zsk2j2l58ro", "epsvc-2d72mxjgq02yo58ozfe5tndeh"] +} \ No newline at end of file diff --git a/example/privatelinkVpcEndpointService/main.tf b/example/privatelinkVpcEndpointService/main.tf new file mode 100644 index 00000000..b61b630b --- /dev/null +++ b/example/privatelinkVpcEndpointService/main.tf @@ -0,0 +1,8 @@ +resource "volcengine_privatelink_vpc_endpoint_service" "foo" { + resources { + resource_id = "clb-2bzxccdjo9uyo2dx0eg0orzla" + resource_type = "CLB" + } + description = "tftest" + auto_accept_enabled = true +} \ No newline at end of file diff --git a/example/privatelinkVpcEndpointServicePermission/main.tf b/example/privatelinkVpcEndpointServicePermission/main.tf new file mode 100644 index 00000000..05950359 --- /dev/null +++ b/example/privatelinkVpcEndpointServicePermission/main.tf @@ -0,0 +1,9 @@ +resource "volcengine_privatelink_vpc_endpoint_service_permission" "foo" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" + permit_account_id = "210000000" +} + +resource "volcengine_privatelink_vpc_endpoint_service_permission" "foo1" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" + permit_account_id = "210000001" +} \ No newline at end of file diff --git a/example/privatelinkVpcEndpointServiceResource/main.tf b/example/privatelinkVpcEndpointServiceResource/main.tf new file mode 100644 index 00000000..7b444ff1 --- /dev/null +++ b/example/privatelinkVpcEndpointServiceResource/main.tf @@ -0,0 +1,14 @@ +resource "volcengine_privatelink_vpc_endpoint_service_resource" "foo" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" + resource_id = "clb-3reii8qfbp7gg5zsk2hsrbe3c" +} + +resource "volcengine_privatelink_vpc_endpoint_service_resource" "foo1" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" + resource_id = "clb-2d6sfye98rzls58ozfducee1o" +} + +resource "volcengine_privatelink_vpc_endpoint_service_resource" "foo2" { + service_id = "epsvc-3rel73uf2ewao5zsk2j2l58ro" + resource_id = "clb-3refkvae02gow5zsk2ilaev5y" +} \ No newline at end of file diff --git a/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go b/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go new file mode 100644 index 00000000..ff145b19 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go @@ -0,0 +1,151 @@ +package vpc_endpoint_service + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcenginePrivatelinkVpcEndpointServices() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcenginePrivatelinkVpcEndpointServiceRead, + Schema: map[string]*schema.Schema{ + "ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "The IDs of vpc endpoint service.", + }, + "service_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of vpc endpoint service.", + }, + "project_name": { + Type: schema.TypeString, + Optional: true, + Description: "The project name of vpc endpoint service.", + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsValidRegExp, + Description: "A Name Regex of vpc endpoint service.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Returns the total amount of the data list.", + }, + "services": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of service.", + }, + "service_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of service.", + }, + "service_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of service.", + }, + "service_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of service.", + }, + "service_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of service.", + }, + "service_resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type of service.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of service.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of service.", + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of service.", + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "The update time of service.", + }, + "auto_accept_enabled": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether auto accept node connect.", + }, + "zone_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IDs of zones.", + }, + "resources": { + Type: schema.TypeList, + Computed: true, + Description: "The resources info.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of resource.", + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of resource.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The zone id of resource.", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcenginePrivatelinkVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) error { + service := NewService(meta.(*ve.SdkClient)) + return ve.DefaultDispatcher().Data(service, d, DataSourceVolcenginePrivatelinkVpcEndpointServices()) +} diff --git a/volcengine/privatelink/vpc_endpoint_service/resource_volcengine_privatelink_vpc_endpoint_service.go b/volcengine/privatelink/vpc_endpoint_service/resource_volcengine_privatelink_vpc_endpoint_service.go new file mode 100644 index 00000000..a403e000 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service/resource_volcengine_privatelink_vpc_endpoint_service.go @@ -0,0 +1,169 @@ +package vpc_endpoint_service + +import ( + "bytes" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VpcEndpointService can be imported using the id, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint_service.default epsvc-2fe630gurkl37k5gfuy33**** +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpointService() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivateLinkVpcEndpointServiceCreate, + Read: resourceVolcenginePrivateLinkVpcEndpointServiceRead, + Update: resourceVolcenginePrivateLinkVpcEndpointServiceUpdate, + Delete: resourceVolcenginePrivateLinkVpcEndpointServiceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "auto_accept_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether auto accept node connect.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of service.", + }, + // 创建 service 时候,必须传入一个 resource;在修改 resource 的时候,必须保留一个,不能全部删除 + "resources": { + Type: schema.TypeSet, + Required: true, + Description: "The resources info. When create vpc endpoint service, the resource must exist.", + Set: resourceHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource_type": { + Type: schema.TypeString, + Required: true, + Description: "The type of resource.", + }, + "resource_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of resource.", + }, + }, + }, + }, + "service_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of service.", + }, + "service_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of service.", + }, + "service_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of service.", + }, + "service_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of service.", + }, + "service_resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type of service.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of service.", + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of service.", + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "The update time of service.", + }, + "zone_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IDs of zones.", + }, + }, + } + return resource +} + +var resourceHash = func(v interface{}) int { + if v == nil { + return hashcode.String("") + } + m := v.(map[string]interface{}) + var ( + buf bytes.Buffer + ) + buf.WriteString(fmt.Sprintf("%v#%v", m["resource_type"], m["resource_id"])) + return hashcode.String(buf.String()) +} + +func resourceVolcenginePrivateLinkVpcEndpointServiceCreate(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointService()) + if err != nil { + return fmt.Errorf("error on creating vpc endpoint service %q, %w", d.Id(), err) + } + return resourceVolcenginePrivateLinkVpcEndpointServiceRead(d, meta) +} + +func resourceVolcenginePrivateLinkVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointService()) + if err != nil { + return fmt.Errorf("error on reading vpc endpoint service %q, %w", d.Id(), err) + } + return err +} + +func resourceVolcenginePrivateLinkVpcEndpointServiceUpdate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Update(service, d, ResourceVolcenginePrivatelinkVpcEndpointService()) + if err != nil { + return fmt.Errorf("error on updating vpc endoint service %q, %w", d.Id(), err) + } + return resourceVolcenginePrivateLinkVpcEndpointServiceRead(d, meta) +} + +func resourceVolcenginePrivateLinkVpcEndpointServiceDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(service, d, ResourceVolcenginePrivatelinkVpcEndpointService()) + if err != nil { + return fmt.Errorf("error on deleting vpc endpoint service%q, %w", d.Id(), err) + } + return err +} diff --git a/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go b/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go new file mode 100644 index 00000000..eb079885 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go @@ -0,0 +1,326 @@ +package vpc_endpoint_service + +import ( + "errors" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineService struct { + Client *ve.SdkClient +} + +func NewService(c *ve.SdkClient) *VolcengineService { + return &VolcengineService{ + Client: c, + } +} + +func (s *VolcengineService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointServices" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, condition, *resp) + results, err = ve.ObtainSdkValue("Result.VpcEndpointServices", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.VpcEndpointServices is not Slice") + } + + for index, element := range data { + resources, err := s.describeResources(map[string]interface{}{ + "ServiceId": element.(map[string]interface{})["ServiceId"], + }) + if err != nil { + return nil, err + } + data[index].(map[string]interface{})["Resources"] = resources + } + return data, err + }) +} + +func (s *VolcengineService) describeResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointServiceResources" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, resp) + results, err = ve.ObtainSdkValue("Result.Resources", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.Resources is not Slice") + } + return data, err + }) +} + +func (s *VolcengineService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + req := map[string]interface{}{ + "ServiceIds.1": id, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("Vpc endpoint service %s not exist ", id) + } + return data, err +} + +func (s *VolcengineService) WithResourceResponseHandlers(nodePool map[string]interface{}) []ve.ResourceResponseHandler { + return nil +} + +func (s *VolcengineService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + data map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "Error") + data, err = s.ReadResource(resourceData, id) + if err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("Status", data) + if err != nil { + return nil, "", err + } + for _, v := range failStates { + if v == status.(string) { + return nil, "", fmt.Errorf("Vpc endpoint service status error, status: %s", status.(string)) + } + } + //注意 返回的第一个参数不能为空 否则会一直等下去 + return data, status.(string), err + }, + } +} + +func (s *VolcengineService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "CreateVpcEndpointService", + ConvertMode: ve.RequestConvertAll, + Convert: map[string]ve.RequestConvert{ + "resources": { + TargetField: "Resources", + ConvertType: ve.ConvertListN, + }, + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id, _ := ve.ObtainSdkValue("Result.ServiceId", *resp) + d.SetId(id.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + return []ve.Callback{callback} + +} + +func (s *VolcengineService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + + if resourceData.HasChange("description") || resourceData.HasChange("auto_accept_enabled") { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "ModifyVpcEndpointServiceAttributes", + ConvertMode: ve.RequestConvertInConvert, + Convert: map[string]ve.RequestConvert{ + "auto_accept_enabled": { + TargetField: "AutoAcceptEnabled", + }, + "description": { + TargetField: "Description", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + (*call.SdkParam)["ServiceId"] = d.Id() + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, callback) + } + + if resourceData.HasChange("resources") { + add, remove, _, _ := ve.GetSetDifference("resources", resourceData, resourceHash, false) + for _, element := range remove.List() { + callbacks = append(callbacks, s.resourceActionCallback(resourceData, "DetachResourceFromVpcEndpointService", element)) + } + for _, element := range add.List() { + callbacks = append(callbacks, s.resourceActionCallback(resourceData, "AttachResourceToVpcEndpointService", element)) + } + } + return callbacks +} + +func (s *VolcengineService) resourceActionCallback(resourceData *schema.ResourceData, action string, element interface{}) ve.Callback { + return ve.Callback{ + Call: ve.SdkCall{ + Action: action, + ConvertMode: ve.RequestConvertIgnore, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + (*call.SdkParam)["ServiceId"] = d.Id() + (*call.SdkParam)["ResourceId"] = element.(map[string]interface{})["resource_id"] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } +} + +func (s *VolcengineService) RemoveResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DeleteVpcEndpointService", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "ServiceId": resourceData.Id(), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + }, + } + return []ve.Callback{callback} +} + +func (VolcengineService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + RequestConverts: map[string]ve.RequestConvert{ + "ids": { + TargetField: "ServiceIds", + ConvertType: ve.ConvertWithN, + }, + }, + NameField: "ServiceName", + IdField: "ServiceId", + CollectField: "services", + ResponseConverts: map[string]ve.ResponseConvert{ + "ServiceId": { + TargetField: "id", + KeepDefault: true, + }, + }, + } +} + +func (s *VolcengineService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/privatelink/vpc_endpoint_service_permission/data_source_volcengine_privatelink_vpc_endpoint_service_permissions.go b/volcengine/privatelink/vpc_endpoint_service_permission/data_source_volcengine_privatelink_vpc_endpoint_service_permissions.go new file mode 100644 index 00000000..d7d09a91 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service_permission/data_source_volcengine_privatelink_vpc_endpoint_service_permissions.go @@ -0,0 +1,53 @@ +package vpc_endpoint_service_permission + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcenginePrivatelinkVpcEndpointServicePermissionRead, + Schema: map[string]*schema.Schema{ + "service_id": { + Type: schema.TypeString, + Required: true, + Description: "The Id of service.", + }, + "permit_account_id": { + Type: schema.TypeString, + Optional: true, + Description: "The Id of permit account.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Returns the total amount of the data list.", + }, + "permissions": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "permit_account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The permit account id.", + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcenginePrivatelinkVpcEndpointServicePermissionRead(d *schema.ResourceData, meta interface{}) error { + service := NewService(meta.(*ve.SdkClient)) + return ve.DefaultDispatcher().Data(service, d, DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions()) +} diff --git a/volcengine/privatelink/vpc_endpoint_service_permission/resource_volcengine_privatelink_vpc_endpoint_service_permission.go b/volcengine/privatelink/vpc_endpoint_service_permission/resource_volcengine_privatelink_vpc_endpoint_service_permission.go new file mode 100644 index 00000000..e6be355a --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service_permission/resource_volcengine_privatelink_vpc_endpoint_service_permission.go @@ -0,0 +1,89 @@ +package vpc_endpoint_service_permission + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VpcEndpointServicePermission can be imported using the serviceId:permitAccountId, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint_service_permission.default epsvc-2fe630gurkl37k5gfuy33****:2100000000 +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpointServicePermission() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivatelinkVpcEndpointServicePermissionCreate, + Read: resourceVolcenginePrivatelinkVpcEndpointServicePermissionRead, + Delete: resourceVolcenginePrivatelinkVpcEndpointServicePermissionDelete, + Importer: &schema.ResourceImporter{ + State: func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("service_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("permit_account_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil + }, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "permit_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of account.", + }, + "service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of service.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivatelinkVpcEndpointServicePermissionCreate(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointServicePermission()) + if err != nil { + return fmt.Errorf("error on creating vpc endpoint service permission %q, %w", d.Id(), err) + } + return resourceVolcenginePrivatelinkVpcEndpointServicePermissionRead(d, meta) +} + +func resourceVolcenginePrivatelinkVpcEndpointServicePermissionRead(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointServicePermission()) + if err != nil { + return fmt.Errorf("error on reading vpc endpoint service permission %q, %w", d.Id(), err) + } + return err +} + +func resourceVolcenginePrivatelinkVpcEndpointServicePermissionDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(service, d, ResourceVolcenginePrivatelinkVpcEndpointServicePermission()) + if err != nil { + return fmt.Errorf("error on deleting vpc endpoint service permission %q, %w", d.Id(), err) + } + return err +} diff --git a/volcengine/privatelink/vpc_endpoint_service_permission/service_volcengine_privatelink_vpc_endpoint_service_permission.go b/volcengine/privatelink/vpc_endpoint_service_permission/service_volcengine_privatelink_vpc_endpoint_service_permission.go new file mode 100644 index 00000000..39d2f772 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service_permission/service_volcengine_privatelink_vpc_endpoint_service_permission.go @@ -0,0 +1,188 @@ +package vpc_endpoint_service_permission + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" +) + +type VolcengineService struct { + Client *ve.SdkClient +} + +func NewService(c *ve.SdkClient) *VolcengineService { + return &VolcengineService{ + Client: c, + } +} + +func (s *VolcengineService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointServicePermissions" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, *resp) + results, err = ve.ObtainSdkValue("Result.Permissions", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.Permissions is not Slice") + } + + return data, err + }) + +} + +func (s *VolcengineService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + + ids := strings.Split(id, ":") + serviceId := ids[0] + accountId := ids[1] + + results, err = s.ReadResources(map[string]interface{}{ + "ServiceId": serviceId, + "PermitAccountId": accountId, + }) + if err != nil { + return data, err + } + if len(results) == 0 { + return data, fmt.Errorf("Vpc endpoint service permission %s not exist ", id) + } + + return map[string]interface{}{ + "ServiceId": serviceId, + "PermitAccountId": accountId, + }, nil +} + +func (s *VolcengineService) WithResourceResponseHandlers(nodePool map[string]interface{}) []ve.ResourceResponseHandler { + return nil +} + +func (s *VolcengineService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return nil +} + +func (s *VolcengineService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "AddPermissionToVpcEndpointService", + ConvertMode: ve.RequestConvertAll, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + d.SetId(fmt.Sprint((*call.SdkParam)["ServiceId"], ":", (*call.SdkParam)["PermitAccountId"])) + return nil + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint_service.NewService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: resourceData.Get("service_id").(string), + }, + }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + return callbacks +} + +func (s *VolcengineService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + ids := strings.Split(s.ReadResourceId(resourceData.Id()), ":") + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "RemovePermissionFromVpcEndpointService", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "ServiceId": ids[0], + "PermitAccountId": ids[1], + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint_service.NewService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: resourceData.Get("service_id").(string), + }, + }, + }, + } + return []ve.Callback{callback} +} + +func (VolcengineService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + CollectField: "permissions", + } +} + +func (s *VolcengineService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/privatelink/vpc_endpoint_service_resource/resource_volcengine_privatelink_vpc_endpoint_service_resource.go b/volcengine/privatelink/vpc_endpoint_service_resource/resource_volcengine_privatelink_vpc_endpoint_service_resource.go new file mode 100644 index 00000000..a6c66203 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service_resource/resource_volcengine_privatelink_vpc_endpoint_service_resource.go @@ -0,0 +1,89 @@ +package vpc_endpoint_service_resource + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VpcEndpointServiceResource can be imported using the serviceId:resourceId, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint_service_resource.default epsvc-2fe630gurkl37k5gfuy33****:clb-bp1o94dp5i6ea**** +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpointServiceResource() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivatelinkVpcEndpointServiceResourceCreate, + Read: resourceVolcenginePrivatelinkVpcEndpointServiceResourceRead, + Delete: resourceVolcenginePrivatelinkVpcEndpointServiceResourceDelete, + Importer: &schema.ResourceImporter{ + State: func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("service_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("resource_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil + }, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of resource.", + }, + "service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of service.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivatelinkVpcEndpointServiceResourceCreate(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointServiceResource()) + if err != nil { + return fmt.Errorf("error on creating vpc endpoint service resource %q, %w", d.Id(), err) + } + return resourceVolcenginePrivatelinkVpcEndpointServiceResourceRead(d, meta) +} + +func resourceVolcenginePrivatelinkVpcEndpointServiceResourceRead(d *schema.ResourceData, meta interface{}) (err error) { + aclService := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(aclService, d, ResourceVolcenginePrivatelinkVpcEndpointServiceResource()) + if err != nil { + return fmt.Errorf("error on reading vpc endpoint service resource %q, %w", d.Id(), err) + } + return err +} + +func resourceVolcenginePrivatelinkVpcEndpointServiceResourceDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(service, d, ResourceVolcenginePrivatelinkVpcEndpointServiceResource()) + if err != nil { + return fmt.Errorf("error on deleting vpc endpoint service resource %q, %w", d.Id(), err) + } + return err +} diff --git a/volcengine/privatelink/vpc_endpoint_service_resource/service_volcengine_privatelink_vpc_endpoint_service_resource.go b/volcengine/privatelink/vpc_endpoint_service_resource/service_volcengine_privatelink_vpc_endpoint_service_resource.go new file mode 100644 index 00000000..c70ba8e3 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_service_resource/service_volcengine_privatelink_vpc_endpoint_service_resource.go @@ -0,0 +1,192 @@ +package vpc_endpoint_service_resource + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" +) + +type VolcengineService struct { + Client *ve.SdkClient +} + +func NewService(c *ve.SdkClient) *VolcengineService { + return &VolcengineService{ + Client: c, + } +} + +func (s *VolcengineService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + return nil, err +} + +func (s *VolcengineService) describeResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointServiceResources" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, *resp) + results, err = ve.ObtainSdkValue("Result.Resources", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.Resources is not Slice") + } + return data, err + }) +} + +func (s *VolcengineService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + + ids := strings.Split(id, ":") + serviceId := ids[0] + resourceId := ids[1] + + results, err = s.describeResources(map[string]interface{}{ + "ServiceId": serviceId, + }) + if err != nil { + return data, err + } + if len(results) == 0 { + return data, fmt.Errorf("Vpc endpoint service resource %s not exist ", id) + } + + for _, ele := range results { + if ele.(map[string]interface{})["ResourceId"] == resourceId { + return map[string]interface{}{ + "ServiceId": serviceId, + "ResourceId": resourceId, + }, nil + } + } + return data, fmt.Errorf("resource does not associate target service. service_id: %s, resource_id: %s", serviceId, resourceId) +} + +func (s *VolcengineService) WithResourceResponseHandlers(nodePool map[string]interface{}) []ve.ResourceResponseHandler { + return nil +} + +func (s *VolcengineService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return nil +} + +func (s *VolcengineService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "AttachResourceToVpcEndpointService", + ConvertMode: ve.RequestConvertAll, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + d.SetId(fmt.Sprint((*call.SdkParam)["ServiceId"], ":", (*call.SdkParam)["ResourceId"])) + return nil + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint_service.NewService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: resourceData.Get("service_id").(string), + }, + }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + return callbacks +} + +func (s *VolcengineService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + ids := strings.Split(s.ReadResourceId(resourceData.Id()), ":") + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DetachResourceFromVpcEndpointService", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "ServiceId": ids[0], + "ResourceId": ids[1], + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint_service.NewService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: resourceData.Get("service_id").(string), + }, + }, + }, + } + return []ve.Callback{callback} +} + +func (VolcengineService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{} +} + +func (s *VolcengineService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/provider.go b/volcengine/provider.go index ccc6b88c..f679e966 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -4,6 +4,9 @@ import ( "strings" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/ssl_state" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_permission" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_resource" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/spec" @@ -301,6 +304,10 @@ func Provider() terraform.ResourceProvider { // ================ Bioos ================== "volcengine_bioos_clusters": bioosCluster.DataSourceVolcengineBioosClusters(), "volcengine_bioos_workspaces": workspace.DataSourceVolcengineBioosWorkspaces(), + + // ================ PrivateLink ================== + "volcengine_privatelink_vpc_endpoint_services": vpc_endpoint_service.DataSourceVolcenginePrivatelinkVpcEndpointServices(), + "volcengine_privatelink_vpc_endpoint_service_permissions": vpc_endpoint_service_permission.DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions(), }, ResourcesMap: map[string]*schema.Resource{ "volcengine_vpc": vpc.ResourceVolcengineVpc(), @@ -433,6 +440,11 @@ func Provider() terraform.ResourceProvider { "volcengine_bioos_cluster": bioosCluster.ResourceVolcengineBioosCluster(), "volcengine_bioos_workspace": workspace.ResourceVolcengineBioosWorkspace(), "volcengine_bioos_cluster_bind": cluster_bind.ResourceVolcengineBioosClusterBind(), + + // ================ PrivateLink ================== + "volcengine_privatelink_vpc_endpoint_service": vpc_endpoint_service.ResourceVolcenginePrivatelinkVpcEndpointService(), + "volcengine_privatelink_vpc_endpoint_service_resource": vpc_endpoint_service_resource.ResourceVolcenginePrivatelinkVpcEndpointServiceResource(), + "volcengine_privatelink_vpc_endpoint_service_permission": vpc_endpoint_service_permission.ResourceVolcenginePrivatelinkVpcEndpointServicePermission(), }, ConfigureFunc: ProviderConfigure, } From 1774924287ca1821a3215775733871ad7cbe2a85 Mon Sep 17 00:00:00 2001 From: zhangpeihua Date: Tue, 11 Apr 2023 16:00:39 +0800 Subject: [PATCH 02/11] feat: support service check for privatelink --- .../service_volcengine_privatelink_vpc_endpoint_service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go b/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go index eb079885..00469dee 100644 --- a/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go +++ b/volcengine/privatelink/vpc_endpoint_service/service_volcengine_privatelink_vpc_endpoint_service.go @@ -237,12 +237,12 @@ func (s *VolcengineService) ModifyResource(resourceData *schema.ResourceData, re if resourceData.HasChange("resources") { add, remove, _, _ := ve.GetSetDifference("resources", resourceData, resourceHash, false) - for _, element := range remove.List() { - callbacks = append(callbacks, s.resourceActionCallback(resourceData, "DetachResourceFromVpcEndpointService", element)) - } for _, element := range add.List() { callbacks = append(callbacks, s.resourceActionCallback(resourceData, "AttachResourceToVpcEndpointService", element)) } + for _, element := range remove.List() { + callbacks = append(callbacks, s.resourceActionCallback(resourceData, "DetachResourceFromVpcEndpointService", element)) + } } return callbacks } From e1fa51cf57e203ea45955e55697ce851b43af38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Thu, 13 Apr 2023 09:51:40 +0800 Subject: [PATCH 03/11] feat: add pl security group --- example/privatelinkSecurityGroup/main.tf | 4 + ...e_volcengine_privatelink_security_group.go | 91 ++++++++++ ...e_volcengine_privatelink_security_group.go | 160 ++++++++++++++++++ volcengine/provider.go | 3 + 4 files changed, 258 insertions(+) create mode 100644 example/privatelinkSecurityGroup/main.tf create mode 100644 volcengine/privatelink/security_group/resource_volcengine_privatelink_security_group.go create mode 100644 volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go diff --git a/example/privatelinkSecurityGroup/main.tf b/example/privatelinkSecurityGroup/main.tf new file mode 100644 index 00000000..3c49c4a0 --- /dev/null +++ b/example/privatelinkSecurityGroup/main.tf @@ -0,0 +1,4 @@ +resource "volcengine_privatelink_security_group" "foo" { + endpoint_id = "ep-3rel74u229dz45zsk2i6l69qa" + security_group_id = "sg-2d6722jpp55og58ozfd1sqtdb" +} \ No newline at end of file diff --git a/volcengine/privatelink/security_group/resource_volcengine_privatelink_security_group.go b/volcengine/privatelink/security_group/resource_volcengine_privatelink_security_group.go new file mode 100644 index 00000000..a35c7663 --- /dev/null +++ b/volcengine/privatelink/security_group/resource_volcengine_privatelink_security_group.go @@ -0,0 +1,91 @@ +package security_group + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +PrivateLink Security Group Service can be imported using the endpoint id and security group id, e.g. +``` +$ terraform import volcengine_privatelink_security_group.default ep-2fe630gurkl37k5gfuy33****:sg-xxxxx +``` + +*/ + +func ResourceVolcenginePrivatelinkSecurityGroupService() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivatelinkSecurityGroupCreate, + Read: resourceVolcenginePrivatelinkSecurityGroupRead, + Delete: resourceVolcenginePrivatelinkSecurityGroupDelete, + Importer: &schema.ResourceImporter{ + State: sgImporter, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "endpoint_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the endpoint.", + }, + "security_group_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the security group.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivatelinkSecurityGroupCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewPrivateLinkSecurityGroupService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(service, d, ResourceVolcenginePrivatelinkSecurityGroupService()) + if err != nil { + return fmt.Errorf("error on creating private link security group service %q, %w", d.Id(), err) + } + return resourceVolcenginePrivatelinkSecurityGroupRead(d, meta) +} + +func resourceVolcenginePrivatelinkSecurityGroupRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewPrivateLinkSecurityGroupService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(service, d, ResourceVolcenginePrivatelinkSecurityGroupService()) + if err != nil { + return fmt.Errorf("error on reading private link security group service %q, %w", d.Id(), err) + } + return nil +} + +func resourceVolcenginePrivatelinkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewPrivateLinkSecurityGroupService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(service, d, ResourceVolcenginePrivatelinkSecurityGroupService()) + if err != nil { + return fmt.Errorf("error on deleting private link security group service %q, %w", d.Id(), err) + } + return nil +} + +var sgImporter = func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("endpoint_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("security_group_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil +} diff --git a/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go new file mode 100644 index 00000000..68119810 --- /dev/null +++ b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go @@ -0,0 +1,160 @@ +package security_group + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" +) + +type VolcenginePrivateLinkSecurityGroupService struct { + Client *ve.SdkClient +} + +func (v *VolcenginePrivateLinkSecurityGroupService) GetClient() *ve.SdkClient { + return v.Client +} + +func (v *VolcenginePrivateLinkSecurityGroupService) ReadResources(condition map[string]interface{}) (data []interface{}, err error) { + return data, err +} + +func (v *VolcenginePrivateLinkSecurityGroupService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + if id == "" { + id = resourceData.Id() + } + ids := strings.Split(id, ":") + if len(ids) != 2 { + return data, errors.New("Invalid security group id ") + } + endpointId := ids[0] + securityGroupId := ids[1] + action := "DescribeVpcEndpointSecurityGroups" + req := map[string]interface{}{ + "EndpointId": endpointId, + } + resp, err := v.Client.UniversalClient.DoCall(getUniversalInfo(action), &req) + if err != nil { + return data, err + } + if resp == nil { + return data, fmt.Errorf("Security group %s not exists ", id) + } + securityGroupIds, err := ve.ObtainSdkValue("Result.SecurityGroupIds", *resp) + if err != nil { + return data, err + } + for _, s := range securityGroupIds.([]interface{}) { + if _, ok := s.(string); !ok { + return data, errors.New("security group id is not string") + } else { + if securityGroupId == s.(string) { + data = map[string]interface{}{ + "SecurityGroupId": securityGroupId, + "EndpointId": endpointId, + } + break + } + } + } + if len(data) == 0 { + return data, fmt.Errorf("Security group %s not exists ", id) + } + return data, nil +} + +func (v *VolcenginePrivateLinkSecurityGroupService) RefreshResourceState(data *schema.ResourceData, strings []string, duration time.Duration, s string) *resource.StateChangeConf { + return nil +} + +func (v *VolcenginePrivateLinkSecurityGroupService) WithResourceResponseHandlers(m map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return m, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (v *VolcenginePrivateLinkSecurityGroupService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + endpointId := resourceData.Get("endpoint_id").(string) + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "AttachSecurityGroupToVpcEndpoint", + ConvertMode: ve.RequestConvertAll, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + return v.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id := fmt.Sprintf("%s:%s", endpointId, d.Get("security_group_id")) + d.SetId(id) + return nil + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint_service.NewService(v.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: endpointId, + }, + }, + LockId: func(d *schema.ResourceData) string { + return endpointId + }, + }, + } + return []ve.Callback{callback} +} + +func (v *VolcenginePrivateLinkSecurityGroupService) ModifyResource(data *schema.ResourceData, resource *schema.Resource) []ve.Callback { + return nil +} + +func (v *VolcenginePrivateLinkSecurityGroupService) RemoveResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DetachSecurityGroupFromVpcEndpoint", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "EndpointId": resourceData.Get("endpoint_id"), + "SecurityGroupId": resourceData.Get("security_group_id"), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + return v.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, v.ReadResource, 5*time.Minute) + }, + }, + } + return []ve.Callback{callback} +} + +func (v *VolcenginePrivateLinkSecurityGroupService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{} +} + +func (v *VolcenginePrivateLinkSecurityGroupService) ReadResourceId(id string) string { + return id +} + +func NewPrivateLinkSecurityGroupService(c *ve.SdkClient) *VolcenginePrivateLinkSecurityGroupService { + return &VolcenginePrivateLinkSecurityGroupService{ + Client: c, + } +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/provider.go b/volcengine/provider.go index f679e966..04591c64 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -4,6 +4,8 @@ import ( "strings" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/ssl_state" + + plSecurityGroup "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/security_group" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_permission" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_resource" @@ -445,6 +447,7 @@ func Provider() terraform.ResourceProvider { "volcengine_privatelink_vpc_endpoint_service": vpc_endpoint_service.ResourceVolcenginePrivatelinkVpcEndpointService(), "volcengine_privatelink_vpc_endpoint_service_resource": vpc_endpoint_service_resource.ResourceVolcenginePrivatelinkVpcEndpointServiceResource(), "volcengine_privatelink_vpc_endpoint_service_permission": vpc_endpoint_service_permission.ResourceVolcenginePrivatelinkVpcEndpointServicePermission(), + "volcengine_privatelink_security_group": plSecurityGroup.ResourceVolcenginePrivatelinkSecurityGroupService(), }, ConfigureFunc: ProviderConfigure, } From 27edc65ba27271ffbe54cab935a6267ce2a3b29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Thu, 13 Apr 2023 11:40:15 +0800 Subject: [PATCH 04/11] feat: add pl vpc endpoint connection --- .../main.tf | 4 + .../privatelinkVpcEndpointConnection/main.tf | 5 + ...ne_privatelink_vpc_endpoint_connections.go | 132 ++++++++++++ ...ine_privatelink_vpc_endpoint_connection.go | 91 ++++++++ ...ine_privatelink_vpc_endpoint_connection.go | 204 ++++++++++++++++++ volcengine/provider.go | 3 + 6 files changed, 439 insertions(+) create mode 100644 example/dataPrivatelinkVpcEndpointConnections/main.tf create mode 100644 example/privatelinkVpcEndpointConnection/main.tf create mode 100644 volcengine/privatelink/vpc_endpoint_connection/data_source_volcengine_privatelink_vpc_endpoint_connections.go create mode 100644 volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go create mode 100644 volcengine/privatelink/vpc_endpoint_connection/service_volcengine_privatelink_vpc_endpoint_connection.go diff --git a/example/dataPrivatelinkVpcEndpointConnections/main.tf b/example/dataPrivatelinkVpcEndpointConnections/main.tf new file mode 100644 index 00000000..f14d7b2d --- /dev/null +++ b/example/dataPrivatelinkVpcEndpointConnections/main.tf @@ -0,0 +1,4 @@ +data "volcengine_privatelink_vpc_endpoint_connections" "default" { + endpoint_id = "ep-3rel74u229dz45zsk2i6l69qa" + service_id = "epsvc-2byz5mykk9y4g2dx0efs4aqz3" +} \ No newline at end of file diff --git a/example/privatelinkVpcEndpointConnection/main.tf b/example/privatelinkVpcEndpointConnection/main.tf new file mode 100644 index 00000000..d7eea57a --- /dev/null +++ b/example/privatelinkVpcEndpointConnection/main.tf @@ -0,0 +1,5 @@ +resource "volcengine_privatelink_vpc_endpoint_connection" "foo" { + endpoint_id = "ep-3rel74u229dz45zsk2i6l69qa" + service_id = "epsvc-2byz5mykk9y4g2dx0efs4aqz3" +} + diff --git a/volcengine/privatelink/vpc_endpoint_connection/data_source_volcengine_privatelink_vpc_endpoint_connections.go b/volcengine/privatelink/vpc_endpoint_connection/data_source_volcengine_privatelink_vpc_endpoint_connections.go new file mode 100644 index 00000000..cee11b83 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_connection/data_source_volcengine_privatelink_vpc_endpoint_connections.go @@ -0,0 +1,132 @@ +package vpc_endpoint_connection + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcenginePrivatelinkVpcEndpointConnections() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcenginePrivatelinkVpcEndpointConnectionsRead, + Schema: map[string]*schema.Schema{ + "service_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of the vpc endpoint service.", + }, + "endpoint_id": { + Type: schema.TypeString, + Optional: true, + Description: "The id of the vpc endpoint.", + }, + "endpoint_owner_account_id": { + Type: schema.TypeString, + Optional: true, + Description: "The account id of the vpc endpoint.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Returns the total amount of the data list.", + }, + "connections": { + Description: "The list of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "service_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vpc endpoint service.", + }, + "endpoint_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vpc endpoint.", + }, + "endpoint_owner_account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The account id of the vpc endpoint.", + }, + "endpoint_vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "The vpc id of the vpc endpoint.", + }, + "connection_status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the connection.", + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of the connection.", + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "The update time of the connection.", + }, + "zones": { + Type: schema.TypeList, + Computed: true, + Description: "The available zones.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the zone.", + }, + "zone_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of the zone.", + }, + "zone_status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the zone.", + }, + "subnet_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the subnet.", + }, + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the network interface.", + }, + "network_interface_ip": { + Type: schema.TypeString, + Computed: true, + Description: "The ip address of the network interface.", + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the resource.", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcenginePrivatelinkVpcEndpointConnectionsRead(d *schema.ResourceData, meta interface{}) error { + service := NewVpcEndpointConnectionService(meta.(*ve.SdkClient)) + return ve.DefaultDispatcher().Data(service, d, DataSourceVolcenginePrivatelinkVpcEndpointConnections()) +} diff --git a/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go b/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go new file mode 100644 index 00000000..e7594caf --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go @@ -0,0 +1,91 @@ +package vpc_endpoint_connection + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +PrivateLink Vpc Endpoint Connection Service can be imported using the endpoint id and service id, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint_connection.default ep-3rel74u229dz45zsk2i6l69qa:epsvc-2byz5mykk9y4g2dx0efs4aqz3 +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpointConnectionService() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivatelinkVpcEndpointConnectionCreate, + Read: resourceVolcenginePrivatelinkVpcEndpointConnectionRead, + Delete: resourceVolcenginePrivatelinkVpcEndpointConnectionDelete, + Importer: &schema.ResourceImporter{ + State: vpcConnectionImporter, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "endpoint_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the endpoint.", + }, + "service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the security group.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivatelinkVpcEndpointConnectionCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVpcEndpointConnectionService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(service, d, ResourceVolcenginePrivatelinkVpcEndpointConnectionService()) + if err != nil { + return fmt.Errorf("error on creating private link VpcEndpointConnection service %q, %w", d.Id(), err) + } + return resourceVolcenginePrivatelinkVpcEndpointConnectionRead(d, meta) +} + +func resourceVolcenginePrivatelinkVpcEndpointConnectionRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVpcEndpointConnectionService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(service, d, ResourceVolcenginePrivatelinkVpcEndpointConnectionService()) + if err != nil { + return fmt.Errorf("error on reading private link VpcEndpointConnection service %q, %w", d.Id(), err) + } + return nil +} + +func resourceVolcenginePrivatelinkVpcEndpointConnectionDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVpcEndpointConnectionService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(service, d, ResourceVolcenginePrivatelinkVpcEndpointConnectionService()) + if err != nil { + return fmt.Errorf("error on deleting private link VpcEndpointConnection service %q, %w", d.Id(), err) + } + return nil +} + +var vpcConnectionImporter = func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("endpoint_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("service_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil +} diff --git a/volcengine/privatelink/vpc_endpoint_connection/service_volcengine_privatelink_vpc_endpoint_connection.go b/volcengine/privatelink/vpc_endpoint_connection/service_volcengine_privatelink_vpc_endpoint_connection.go new file mode 100644 index 00000000..57a551c8 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_connection/service_volcengine_privatelink_vpc_endpoint_connection.go @@ -0,0 +1,204 @@ +package vpc_endpoint_connection + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcenginePrivateLinkVpcEndpointConnection struct { + Client *ve.SdkClient +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) GetClient() *ve.SdkClient { + return v.Client +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointConnections" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = v.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = v.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, condition, *resp) + results, err = ve.ObtainSdkValue("Result.EndpointConnections", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.EndpointConnections is not Slice") + } + return data, err + }) +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = v.ReadResourceId(resourceData.Id()) + } + ids := strings.Split(id, ":") + if len(ids) != 2 { + return nil, errors.New("vpc endpoint connection id err") + } + req := map[string]interface{}{ + "EndpointId": ids[0], + "ServiceId": ids[1], + } + results, err = v.ReadResources(req) + if err != nil { + return data, err + } + for _, r := range results { + if data, ok = r.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("vpc endpoint connection %s not exist", id) + } + return data, nil +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + data map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "Error") + data, err = v.ReadResource(resourceData, id) + if err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("ConnectionStatus", data) + if err != nil { + return nil, "", err + } + for _, f := range failStates { + if f == status.(string) { + return nil, "", fmt.Errorf("Vpc endpoint connection status error, status: %s ", status.(string)) + } + } + //注意 返回的第一个参数不能为空 否则会一直等下去 + return data, status.(string), err + }, + } +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) WithResourceResponseHandlers(m map[string]interface{}) []ve.ResourceResponseHandler { + return nil +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) CreateResource(data *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "EnableVpcEndpointConnection", + ConvertMode: ve.RequestConvertAll, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + return v.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id := fmt.Sprintf("%s:%s", d.Get("endpoint_id"), d.Get("service_id")) + d.SetId(id) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Connected"}, + Timeout: data.Timeout(schema.TimeoutCreate), + }, + }, + } + return []ve.Callback{callback} +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) ModifyResource(data *schema.ResourceData, resource *schema.Resource) []ve.Callback { + return nil +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) RemoveResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DisableVpcEndpointConnection", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "EndpointId": resourceData.Get("endpoint_id"), + "ServiceId": resourceData.Get("service_id"), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + return v.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Rejected"}, + Timeout: resourceData.Timeout(schema.TimeoutDelete), + }, + }, + } + return []ve.Callback{callback} +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + CollectField: "connections", + ResponseConverts: map[string]ve.ResponseConvert{ + "NetworkInterfaceIP": { + TargetField: "network_interface_ip", + }, + }, + } +} + +func (v *VolcenginePrivateLinkVpcEndpointConnection) ReadResourceId(s string) string { + return s +} + +func NewVpcEndpointConnectionService(c *ve.SdkClient) *VolcenginePrivateLinkVpcEndpointConnection { + return &VolcenginePrivateLinkVpcEndpointConnection{ + Client: c, + } +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/provider.go b/volcengine/provider.go index 04591c64..87887e2f 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -6,6 +6,7 @@ import ( "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/ssl_state" plSecurityGroup "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/security_group" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_connection" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_permission" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_resource" @@ -310,6 +311,7 @@ func Provider() terraform.ResourceProvider { // ================ PrivateLink ================== "volcengine_privatelink_vpc_endpoint_services": vpc_endpoint_service.DataSourceVolcenginePrivatelinkVpcEndpointServices(), "volcengine_privatelink_vpc_endpoint_service_permissions": vpc_endpoint_service_permission.DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions(), + "volcengine_privatelink_vpc_endpoint_connections": vpc_endpoint_connection.DataSourceVolcenginePrivatelinkVpcEndpointConnections(), }, ResourcesMap: map[string]*schema.Resource{ "volcengine_vpc": vpc.ResourceVolcengineVpc(), @@ -448,6 +450,7 @@ func Provider() terraform.ResourceProvider { "volcengine_privatelink_vpc_endpoint_service_resource": vpc_endpoint_service_resource.ResourceVolcenginePrivatelinkVpcEndpointServiceResource(), "volcengine_privatelink_vpc_endpoint_service_permission": vpc_endpoint_service_permission.ResourceVolcenginePrivatelinkVpcEndpointServicePermission(), "volcengine_privatelink_security_group": plSecurityGroup.ResourceVolcenginePrivatelinkSecurityGroupService(), + "volcengine_privatelink_vpc_endpoint_connection": vpc_endpoint_connection.ResourceVolcenginePrivatelinkVpcEndpointConnectionService(), }, ConfigureFunc: ProviderConfigure, } From a862410f848633c11de4ad39a0c881dd28666ae7 Mon Sep 17 00:00:00 2001 From: "maoshuai.17" Date: Thu, 13 Apr 2023 15:28:18 +0800 Subject: [PATCH 05/11] feat: support vpc endpoint --- ...ce_volcengine_privatelink_vpc_endpoints.go | 150 +++++++++ ...rce_volcengine_privatelink_vpc_endpoint.go | 173 +++++++++++ ...ice_volcengine_privatelink_vpc_endpoint.go | 289 ++++++++++++++++++ ...ngine_privatelink_vpc_endpoint_services.go | 5 - volcengine/provider.go | 3 + 5 files changed, 615 insertions(+), 5 deletions(-) create mode 100644 volcengine/privatelink/vpc_endpoint/data_source_volcengine_privatelink_vpc_endpoints.go create mode 100644 volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go create mode 100644 volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go diff --git a/volcengine/privatelink/vpc_endpoint/data_source_volcengine_privatelink_vpc_endpoints.go b/volcengine/privatelink/vpc_endpoint/data_source_volcengine_privatelink_vpc_endpoints.go new file mode 100644 index 00000000..fc02150f --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint/data_source_volcengine_privatelink_vpc_endpoints.go @@ -0,0 +1,150 @@ +package vpc_endpoint + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcenginePrivatelinkVpcEndpoints() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcenginePrivatelinkVpcEndpointsRead, + Schema: map[string]*schema.Schema{ + "ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "The IDs of vpc endpoint.", + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Description: "The vpc id of vpc endpoint.", + }, + "endpoint_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of vpc endpoint.", + }, + "service_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of vpc endpoint service.", + }, + "status": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"Creating", "Pending", "Available", "Deleting", "Inactive"}, false), + Description: "The status of vpc endpoint. Valid values: `Creating`, `Pending`, `Available`, `Deleting`, `Inactive`.", + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsValidRegExp, + Description: "A Name Regex of vpc endpoint.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Returns the total amount of the data list.", + }, + "vpc_endpoints": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint.", + }, + "endpoint_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint.", + }, + "endpoint_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of vpc endpoint.", + }, + "endpoint_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of vpc endpoint.", + }, + "endpoint_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of vpc endpoint.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of vpc endpoint.", + }, + "business_status": { + Type: schema.TypeString, + Computed: true, + Description: "Whether the vpc endpoint is locked.", + }, + "connection_status": { + Type: schema.TypeString, + Computed: true, + Description: "The connection status of vpc endpoint.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of vpc endpoint.", + }, + "service_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint service.", + }, + "service_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of vpc endpoint service.", + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "The vpc id of vpc endpoint.", + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of vpc endpoint.", + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "The update time of vpc endpoint.", + }, + "deleted_time": { + Type: schema.TypeString, + Computed: true, + Description: "The delete time of vpc endpoint.", + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcenginePrivatelinkVpcEndpointsRead(d *schema.ResourceData, meta interface{}) error { + service := NewVpcEndpointService(meta.(*ve.SdkClient)) + return ve.DefaultDispatcher().Data(service, d, DataSourceVolcenginePrivatelinkVpcEndpoints()) +} diff --git a/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go b/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go new file mode 100644 index 00000000..baff5734 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go @@ -0,0 +1,173 @@ +package vpc_endpoint + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VpcEndpoint can be imported using the id, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint.default ep-3rel74u229dz45zsk2i6l**** +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpoint() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivateLinkVpcEndpointCreate, + Read: resourceVolcenginePrivateLinkVpcEndpointRead, + Update: resourceVolcenginePrivateLinkVpcEndpointUpdate, + Delete: resourceVolcenginePrivateLinkVpcEndpointDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The vpc id of vpc endpoint.", + }, + "security_group_ids": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + Set: schema.HashString, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "the security group ids of vpc endpoint", + }, + //"zone_info": { + // Type: schema.TypeSet, + // Optional: true, + // ForceNew: true, + // Set: schema.HashString, + // Description: "the security group ids of vpc endpoint", + // Elem: schema.Resource{ + // Schema: map[string]*schema.Schema{ + // "vpc_id": { + // Type: schema.TypeString, + // Required: true, + // Description: "The vpc id of vpc endpoint.", + // }, + // }, + // }, + //}, + "service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of vpc endpoint service.", + }, + "service_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The name of vpc endpoint service.", + }, + "endpoint_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The name of vpc endpoint.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The description of vpc endpoint.", + }, + + "endpoint_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of vpc endpoint.", + }, + "endpoint_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of vpc endpoint.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of vpc endpoint.", + }, + "business_status": { + Type: schema.TypeString, + Computed: true, + Description: "Whether the vpc endpoint is locked.", + }, + "connection_status": { + Type: schema.TypeString, + Computed: true, + Description: "The connection status of vpc endpoint.", + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of vpc endpoint.", + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "The update time of vpc endpoint.", + }, + "deleted_time": { + Type: schema.TypeString, + Computed: true, + Description: "The delete time of vpc endpoint.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivateLinkVpcEndpointCreate(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointService := NewVpcEndpointService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(vpcEndpointService, d, ResourceVolcenginePrivatelinkVpcEndpoint()) + if err != nil { + return fmt.Errorf("error on creating vpc endpoint %q, %w", d.Id(), err) + } + return resourceVolcenginePrivateLinkVpcEndpointRead(d, meta) +} + +func resourceVolcenginePrivateLinkVpcEndpointRead(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointService := NewVpcEndpointService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(vpcEndpointService, d, ResourceVolcenginePrivatelinkVpcEndpoint()) + if err != nil { + return fmt.Errorf("error on reading vpc endpoint %q, %w", d.Id(), err) + } + return err +} + +func resourceVolcenginePrivateLinkVpcEndpointUpdate(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointService := NewVpcEndpointService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Update(vpcEndpointService, d, ResourceVolcenginePrivatelinkVpcEndpoint()) + if err != nil { + return fmt.Errorf("error on updating vpc endoint %q, %w", d.Id(), err) + } + return resourceVolcenginePrivateLinkVpcEndpointRead(d, meta) +} + +func resourceVolcenginePrivateLinkVpcEndpointDelete(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointService := NewVpcEndpointService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(vpcEndpointService, d, ResourceVolcenginePrivatelinkVpcEndpoint()) + if err != nil { + return fmt.Errorf("error on deleting vpc endpoint %q, %w", d.Id(), err) + } + return err +} diff --git a/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go new file mode 100644 index 00000000..f2066d1e --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go @@ -0,0 +1,289 @@ +package vpc_endpoint + +import ( + "errors" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineVpcEndpointService struct { + Client *ve.SdkClient +} + +func NewVpcEndpointService(c *ve.SdkClient) *VolcengineVpcEndpointService { + return &VolcengineVpcEndpointService{ + Client: c, + } +} + +func (s *VolcengineVpcEndpointService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVpcEndpointService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 20, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpoints" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, condition, *resp) + results, err = ve.ObtainSdkValue("Result.Endpoints", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.VpcEndpointServices is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVpcEndpointService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + req := map[string]interface{}{ + "EndpointIds.1": id, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("Vpc endpoint service %s not exist ", id) + } + + // 查询 security_group + action := "DescribeVpcEndpointSecurityGroups" + condition := &map[string]interface{}{ + "EndpointId": id, + } + logger.Debug(logger.ReqFormat, action, condition) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(action), condition) + if err != nil { + return data, err + } + logger.Debug(logger.RespFormat, action, resp) + securityGroupIds, err := ve.ObtainSdkValue("Result.SecurityGroupIds", *resp) + if err != nil { + return data, err + } + if securityGroupIds == nil { + securityGroupIds = []interface{}{} + } + data["SecurityGroupIds"] = securityGroupIds + + return data, err +} + +func (s *VolcengineVpcEndpointService) WithResourceResponseHandlers(nodePool map[string]interface{}) []ve.ResourceResponseHandler { + return nil +} + +func (s *VolcengineVpcEndpointService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + data map[string]interface{} + status interface{} + ) + if err = resource.Retry(20*time.Minute, func() *resource.RetryError { + data, err = s.ReadResource(resourceData, id) + if err != nil { + if ve.ResourceNotFoundError(err) { + return resource.RetryableError(err) + } else { + return resource.NonRetryableError(err) + } + } + return nil + }); err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("Status", data) + if err != nil { + return nil, "", err + } + //注意 返回的第一个参数不能为空 否则会一直等下去 + return data, status.(string), err + }, + } +} + +func (s *VolcengineVpcEndpointService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "CreateVpcEndpoint", + ConvertMode: ve.RequestConvertAll, + Convert: map[string]ve.RequestConvert{ + "security_group_ids": { + TargetField: "SecurityGroupIds", + ConvertType: ve.ConvertWithN, + }, + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id, _ := ve.ObtainSdkValue("Result.EndpointId", *resp) + d.SetId(id.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + return []ve.Callback{callback} + +} + +func (s *VolcengineVpcEndpointService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + + if resourceData.HasChange("endpoint_name") || resourceData.HasChange("description") { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "ModifyVpcEndpointAttributes", + ConvertMode: ve.RequestConvertInConvert, + Convert: map[string]ve.RequestConvert{ + "endpoint_name": { + TargetField: "EndpointName", + }, + "description": { + TargetField: "Description", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + (*call.SdkParam)["EndpointId"] = d.Id() + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, call.SdkParam, resp) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, callback) + } + + return callbacks +} + +func (s *VolcengineVpcEndpointService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DeleteVpcEndpoint", + ConvertMode: ve.RequestConvertIgnore, + SdkParam: &map[string]interface{}{ + "EndpointId": resourceData.Id(), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + CallError: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall, baseErr error) error { + //出现错误后重试 + return resource.Retry(15*time.Minute, func() *resource.RetryError { + _, callErr := s.ReadResource(d, "") + if callErr != nil { + if ve.ResourceNotFoundError(callErr) { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("error on reading vpc endpoint on delete %q, %w", d.Id(), callErr)) + } + } + _, callErr = call.ExecuteCall(d, client, call) + if callErr == nil { + return nil + } + return resource.RetryableError(callErr) + }) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + }, + } + return []ve.Callback{callback} +} + +func (VolcengineVpcEndpointService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + RequestConverts: map[string]ve.RequestConvert{ + "ids": { + TargetField: "EndpointIds", + ConvertType: ve.ConvertWithN, + }, + }, + NameField: "EndpointName", + IdField: "EndpointId", + CollectField: "vpc_endpoints", + ResponseConverts: map[string]ve.ResponseConvert{ + "EndpointId": { + TargetField: "id", + KeepDefault: true, + }, + }, + } +} + +func (s *VolcengineVpcEndpointService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go b/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go index ff145b19..3147c42e 100644 --- a/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go +++ b/volcengine/privatelink/vpc_endpoint_service/data_source_volcengine_privatelink_vpc_endpoint_services.go @@ -24,11 +24,6 @@ func DataSourceVolcenginePrivatelinkVpcEndpointServices() *schema.Resource { Optional: true, Description: "The name of vpc endpoint service.", }, - "project_name": { - Type: schema.TypeString, - Optional: true, - Description: "The project name of vpc endpoint service.", - }, "name_regex": { Type: schema.TypeString, Optional: true, diff --git a/volcengine/provider.go b/volcengine/provider.go index 87887e2f..6064ba1a 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -1,6 +1,7 @@ package volcengine import ( + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint" "strings" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/ssl_state" @@ -309,6 +310,7 @@ func Provider() terraform.ResourceProvider { "volcengine_bioos_workspaces": workspace.DataSourceVolcengineBioosWorkspaces(), // ================ PrivateLink ================== + "volcengine_privatelink_vpc_endpoints": vpc_endpoint.DataSourceVolcenginePrivatelinkVpcEndpoints(), "volcengine_privatelink_vpc_endpoint_services": vpc_endpoint_service.DataSourceVolcenginePrivatelinkVpcEndpointServices(), "volcengine_privatelink_vpc_endpoint_service_permissions": vpc_endpoint_service_permission.DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions(), "volcengine_privatelink_vpc_endpoint_connections": vpc_endpoint_connection.DataSourceVolcenginePrivatelinkVpcEndpointConnections(), @@ -446,6 +448,7 @@ func Provider() terraform.ResourceProvider { "volcengine_bioos_cluster_bind": cluster_bind.ResourceVolcengineBioosClusterBind(), // ================ PrivateLink ================== + "volcengine_privatelink_vpc_endpoint": vpc_endpoint.ResourceVolcenginePrivatelinkVpcEndpoint(), "volcengine_privatelink_vpc_endpoint_service": vpc_endpoint_service.ResourceVolcenginePrivatelinkVpcEndpointService(), "volcengine_privatelink_vpc_endpoint_service_resource": vpc_endpoint_service_resource.ResourceVolcenginePrivatelinkVpcEndpointServiceResource(), "volcengine_privatelink_vpc_endpoint_service_permission": vpc_endpoint_service_permission.ResourceVolcenginePrivatelinkVpcEndpointServicePermission(), From cedd5331d9d39eb81af020ab59146ad4fbdbdd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Thu, 13 Apr 2023 15:34:04 +0800 Subject: [PATCH 06/11] feat: add pl security group refresh and connection merge --- example/privatelinkSecurityGroup/main.tf | 2 +- .../service_volcengine_privatelink_security_group.go | 4 ++-- ...resource_volcengine_privatelink_vpc_endpoint_connection.go | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/example/privatelinkSecurityGroup/main.tf b/example/privatelinkSecurityGroup/main.tf index 3c49c4a0..048ac613 100644 --- a/example/privatelinkSecurityGroup/main.tf +++ b/example/privatelinkSecurityGroup/main.tf @@ -1,4 +1,4 @@ resource "volcengine_privatelink_security_group" "foo" { - endpoint_id = "ep-3rel74u229dz45zsk2i6l69qa" + endpoint_id = "ep-2byz5npiuu1hc2dx0efkv7ehc" security_group_id = "sg-2d6722jpp55og58ozfd1sqtdb" } \ No newline at end of file diff --git a/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go index 68119810..0fe06355 100644 --- a/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go +++ b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ve "github.com/volcengine/terraform-provider-volcengine/common" "github.com/volcengine/terraform-provider-volcengine/logger" - "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint" ) type VolcenginePrivateLinkSecurityGroupService struct { @@ -96,7 +96,7 @@ func (v *VolcenginePrivateLinkSecurityGroupService) CreateResource(resourceData return nil }, ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ - vpc_endpoint_service.NewService(v.Client): { + vpc_endpoint.NewVpcEndpointService(v.Client): { Target: []string{"Available"}, Timeout: resourceData.Timeout(schema.TimeoutCreate), ResourceId: endpointId, diff --git a/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go b/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go index e7594caf..9490d8df 100644 --- a/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go +++ b/volcengine/privatelink/vpc_endpoint_connection/resource_volcengine_privatelink_vpc_endpoint_connection.go @@ -46,6 +46,8 @@ func ResourceVolcenginePrivatelinkVpcEndpointConnectionService() *schema.Resourc }, }, } + dataSource := DataSourceVolcenginePrivatelinkVpcEndpointConnections().Schema["connections"].Elem.(*schema.Resource).Schema + ve.MergeDateSourceToResource(dataSource, &resource.Schema) return resource } From 14d0cf9500da8476be64d2d3636f3da38ebb0004 Mon Sep 17 00:00:00 2001 From: "maoshuai.17" Date: Thu, 13 Apr 2023 19:14:35 +0800 Subject: [PATCH 07/11] feat: support vpc endpoint zone --- .../dataPrivatelinkVpcEndpointZones/main.tf | 3 + example/dataPrivatelinkVpcEndpoints/main.tf | 3 + example/privateLinkVpcEndpointZone/main.tf | 5 + example/privatelinkVpcEndpoint/main.tf | 13 + ...ice_volcengine_privatelink_vpc_endpoint.go | 18 +- ...lcengine_privatelink_vpc_endpoint_zones.go | 83 ++++++ ...olcengine_privatelink_vpc_endpoint_zone.go | 118 ++++++++ ...olcengine_privatelink_vpc_endpoint_zone.go | 266 ++++++++++++++++++ volcengine/provider.go | 5 +- 9 files changed, 508 insertions(+), 6 deletions(-) create mode 100644 example/dataPrivatelinkVpcEndpointZones/main.tf create mode 100644 example/dataPrivatelinkVpcEndpoints/main.tf create mode 100644 example/privateLinkVpcEndpointZone/main.tf create mode 100644 example/privatelinkVpcEndpoint/main.tf create mode 100644 volcengine/privatelink/vpc_endpoint_zone/data_source_volcengine_privatelink_vpc_endpoint_zones.go create mode 100644 volcengine/privatelink/vpc_endpoint_zone/resource_volcengine_privatelink_vpc_endpoint_zone.go create mode 100644 volcengine/privatelink/vpc_endpoint_zone/service_volcengine_privatelink_vpc_endpoint_zone.go diff --git a/example/dataPrivatelinkVpcEndpointZones/main.tf b/example/dataPrivatelinkVpcEndpointZones/main.tf new file mode 100644 index 00000000..4f5137ac --- /dev/null +++ b/example/dataPrivatelinkVpcEndpointZones/main.tf @@ -0,0 +1,3 @@ +data "volcengine_privatelink_vpc_endpoint_zones" "default" { + endpoint_id = "ep-2byz5npiuu1hc2dx0efkv****" +} \ No newline at end of file diff --git a/example/dataPrivatelinkVpcEndpoints/main.tf b/example/dataPrivatelinkVpcEndpoints/main.tf new file mode 100644 index 00000000..0c38c5d4 --- /dev/null +++ b/example/dataPrivatelinkVpcEndpoints/main.tf @@ -0,0 +1,3 @@ +data "volcengine_privatelink_vpc_endpoints" "default" { + ids = ["ep-3rel74u229dz45zsk2i6l****"] +} \ No newline at end of file diff --git a/example/privateLinkVpcEndpointZone/main.tf b/example/privateLinkVpcEndpointZone/main.tf new file mode 100644 index 00000000..91f60842 --- /dev/null +++ b/example/privateLinkVpcEndpointZone/main.tf @@ -0,0 +1,5 @@ +resource "volcengine_privatelink_vpc_endpoint_zone" "foo" { + endpoint_id = "ep-2byz5nlkimc5c2dx0ef9g****" + subnet_id = "subnet-2bz47q19zhx4w2dx0eevn****" + private_ip_address = "172.16.0.251" +} \ No newline at end of file diff --git a/example/privatelinkVpcEndpoint/main.tf b/example/privatelinkVpcEndpoint/main.tf new file mode 100644 index 00000000..5ccf321b --- /dev/null +++ b/example/privatelinkVpcEndpoint/main.tf @@ -0,0 +1,13 @@ +resource "volcengine_privatelink_vpc_endpoint" "endpoint" { + vpc_id = "vpc-2d5z8cl807y8058ozfds8****" + security_group_ids = ["sg-2d5z8cr53k45c58ozfdum****"] + service_id = "epsvc-2byz5nzgiansw2dx0eehh****" + endpoint_name = "tf-test-ep" + description = "tf-test" +} + +resource "volcengine_privatelink_vpc_endpoint_zone" "zone" { + endpoint_id = volcengine_privatelink_vpc_endpoint.endpoint.id + subnet_id = "subnet-2bz47q19zhx4w2dx0eevn****" + private_ip_address = "172.16.0.252" +} \ No newline at end of file diff --git a/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go index f2066d1e..4a2636c3 100644 --- a/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go +++ b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ve "github.com/volcengine/terraform-provider-volcengine/common" @@ -54,7 +55,7 @@ func (s *VolcengineVpcEndpointService) ReadResources(m map[string]interface{}) ( results = []interface{}{} } if data, ok = results.([]interface{}); !ok { - return data, errors.New("Result.VpcEndpointServices is not Slice") + return data, errors.New("Result.Endpoints is not Slice") } return data, err }) @@ -107,10 +108,6 @@ func (s *VolcengineVpcEndpointService) ReadResource(resourceData *schema.Resourc return data, err } -func (s *VolcengineVpcEndpointService) WithResourceResponseHandlers(nodePool map[string]interface{}) []ve.ResourceResponseHandler { - return nil -} - func (s *VolcengineVpcEndpointService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { return &resource.StateChangeConf{ Pending: []string{}, @@ -146,6 +143,13 @@ func (s *VolcengineVpcEndpointService) RefreshResourceState(resourceData *schema } } +func (s *VolcengineVpcEndpointService) WithResourceResponseHandlers(data map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return data, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + func (s *VolcengineVpcEndpointService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { callback := ve.Callback{ Call: ve.SdkCall{ @@ -157,6 +161,10 @@ func (s *VolcengineVpcEndpointService) CreateResource(resourceData *schema.Resou ConvertType: ve.ConvertWithN, }, }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + (*call.SdkParam)["ClientToken"] = uuid.New().String() + return true, nil + }, ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { logger.Debug(logger.RespFormat, call.Action, call.SdkParam) resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) diff --git a/volcengine/privatelink/vpc_endpoint_zone/data_source_volcengine_privatelink_vpc_endpoint_zones.go b/volcengine/privatelink/vpc_endpoint_zone/data_source_volcengine_privatelink_vpc_endpoint_zones.go new file mode 100644 index 00000000..9343f876 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_zone/data_source_volcengine_privatelink_vpc_endpoint_zones.go @@ -0,0 +1,83 @@ +package vpc_endpoint_zone + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcenginePrivatelinkVpcEndpointZones() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcenginePrivatelinkVpcEndpointZonesRead, + Schema: map[string]*schema.Schema{ + "endpoint_id": { + Type: schema.TypeString, + Optional: true, + Description: "The endpoint id of query.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Returns the total amount of the data list.", + }, + "vpc_endpoint_zones": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint zone.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint zone.", + }, + "zone_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of vpc endpoint zone.", + }, + "zone_status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of vpc endpoint zone.", + }, + "service_status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of vpc endpoint service.", + }, + "subnet_id": { + Type: schema.TypeString, + Computed: true, + Description: "The subnet id of vpc endpoint.", + }, + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + Description: "The network interface id of vpc endpoint.", + }, + "network_interface_ip": { + Type: schema.TypeString, + Computed: true, + Description: "The network interface ip of vpc endpoint.", + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcenginePrivatelinkVpcEndpointZonesRead(d *schema.ResourceData, meta interface{}) error { + service := NewVpcEndpointZoneService(meta.(*ve.SdkClient)) + return ve.DefaultDispatcher().Data(service, d, DataSourceVolcenginePrivatelinkVpcEndpointZones()) +} diff --git a/volcengine/privatelink/vpc_endpoint_zone/resource_volcengine_privatelink_vpc_endpoint_zone.go b/volcengine/privatelink/vpc_endpoint_zone/resource_volcengine_privatelink_vpc_endpoint_zone.go new file mode 100644 index 00000000..4de75715 --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_zone/resource_volcengine_privatelink_vpc_endpoint_zone.go @@ -0,0 +1,118 @@ +package vpc_endpoint_zone + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VpcEndpointZone can be imported using the endpointId:subnetId, e.g. +``` +$ terraform import volcengine_privatelink_vpc_endpoint_zone.default ep-3rel75r081l345zsk2i59****:subnet-2bz47q19zhx4w2dx0eevn**** +``` + +*/ + +func ResourceVolcenginePrivatelinkVpcEndpointZone() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcenginePrivateLinkVpcEndpointZoneCreate, + Read: resourceVolcenginePrivateLinkVpcEndpointZoneRead, + Delete: resourceVolcenginePrivateLinkVpcEndpointZoneDelete, + Importer: &schema.ResourceImporter{ + State: func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("endpoint_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("subnet_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil + }, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "endpoint_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The endpoint id of vpc endpoint zone.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The subnet id of vpc endpoint zone.", + }, + "private_ip_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The private ip address of vpc endpoint zone.", + }, + + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Id of vpc endpoint zone.", + }, + "zone_domain": { + Type: schema.TypeString, + Computed: true, + Description: "The domain of vpc endpoint zone.", + }, + "zone_status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of vpc endpoint zone.", + }, + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + Description: "The network interface id of vpc endpoint.", + }, + }, + } + return resource +} + +func resourceVolcenginePrivateLinkVpcEndpointZoneCreate(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointZoneService := NewVpcEndpointZoneService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Create(vpcEndpointZoneService, d, ResourceVolcenginePrivatelinkVpcEndpointZone()) + if err != nil { + return fmt.Errorf("error on creating vpc endpoint zone %q, %w", d.Id(), err) + } + return resourceVolcenginePrivateLinkVpcEndpointZoneRead(d, meta) +} + +func resourceVolcenginePrivateLinkVpcEndpointZoneRead(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointZoneService := NewVpcEndpointZoneService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Read(vpcEndpointZoneService, d, ResourceVolcenginePrivatelinkVpcEndpointZone()) + if err != nil { + return fmt.Errorf("error on reading vpc endpoint zone %q, %w", d.Id(), err) + } + return err +} + +func resourceVolcenginePrivateLinkVpcEndpointZoneDelete(d *schema.ResourceData, meta interface{}) (err error) { + vpcEndpointZoneService := NewVpcEndpointZoneService(meta.(*ve.SdkClient)) + err = ve.DefaultDispatcher().Delete(vpcEndpointZoneService, d, ResourceVolcenginePrivatelinkVpcEndpointZone()) + if err != nil { + return fmt.Errorf("error on deleting vpc endpoint zone %q, %w", d.Id(), err) + } + return err +} diff --git a/volcengine/privatelink/vpc_endpoint_zone/service_volcengine_privatelink_vpc_endpoint_zone.go b/volcengine/privatelink/vpc_endpoint_zone/service_volcengine_privatelink_vpc_endpoint_zone.go new file mode 100644 index 00000000..52b014ce --- /dev/null +++ b/volcengine/privatelink/vpc_endpoint_zone/service_volcengine_privatelink_vpc_endpoint_zone.go @@ -0,0 +1,266 @@ +package vpc_endpoint_zone + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint" +) + +type VolcengineVpcEndpointZoneService struct { + Client *ve.SdkClient +} + +func NewVpcEndpointZoneService(c *ve.SdkClient) *VolcengineVpcEndpointZoneService { + return &VolcengineVpcEndpointZoneService{ + Client: c, + } +} + +func (s *VolcengineVpcEndpointZoneService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVpcEndpointZoneService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 10, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeVpcEndpointZones" + logger.Debug(logger.ReqFormat, action, condition) + if condition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &condition) + if err != nil { + return data, err + } + } + logger.Debug(logger.RespFormat, action, condition, *resp) + results, err = ve.ObtainSdkValue("Result.EndpointZones", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.EndpointZones is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVpcEndpointZoneService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + ids := strings.Split(id, ":") + if len(ids) != 2 { + return data, errors.New("Invalid vpc endpoint zone id ") + } + endpointId := ids[0] + subnetId := ids[1] + req := map[string]interface{}{ + "EndpointId": endpointId, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + zoneMap := make(map[string]interface{}) + if zoneMap, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + if subnetId == zoneMap["SubnetId"].(string) { + data = zoneMap + data["PrivateIpAddress"] = data["NetworkInterfaceIP"] + data["EndpointId"] = endpointId + break + } + } + if len(data) == 0 { + return data, fmt.Errorf("Vpc endpoint zone %s not exist ", id) + } + + return data, err +} + +func (s *VolcengineVpcEndpointZoneService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return nil +} + +func (s *VolcengineVpcEndpointZoneService) WithResourceResponseHandlers(data map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return data, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (s *VolcengineVpcEndpointZoneService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + endpointId := resourceData.Get("endpoint_id").(string) + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "AddZoneToVpcEndpoint", + ConvertMode: ve.RequestConvertAll, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + // 查询子网所属的 zone_id + zoneId, err := s.getZoneIdBySubnet(d.Get("subnet_id").(string)) + if err != nil { + return false, err + } + if zoneId == "" { + return false, fmt.Errorf(" Failed to obtain zone from subnet id: %v", d.Get("subnet_id")) + } + (*call.SdkParam)["ZoneId"] = zoneId + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id := fmt.Sprintf("%s:%s", endpointId, d.Get("subnet_id")) + d.SetId(id) + return nil + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint.NewVpcEndpointService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: endpointId, + }, + }, + LockId: func(d *schema.ResourceData) string { + return endpointId + }, + }, + } + return []ve.Callback{callback} + +} + +func (s *VolcengineVpcEndpointZoneService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + return []ve.Callback{} +} + +func (s *VolcengineVpcEndpointZoneService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "RemoveZoneFromVpcEndpoint", + ConvertMode: ve.RequestConvertIgnore, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, errors.New("Invalid vpc endpoint zone id ") + } + endpointId := ids[0] + subnetId := ids[1] + + // 查询子网所属的 zone_id + zoneId, err := s.getZoneIdBySubnet(subnetId) + if err != nil { + return false, err + } + if zoneId == "" { + return false, fmt.Errorf(" Failed to obtain zone from subnet id: %v", d.Get("subnet_id")) + } + + (*call.SdkParam)["EndpointId"] = endpointId + (*call.SdkParam)["ZoneId"] = zoneId + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint.NewVpcEndpointService(s.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutDelete), + ResourceId: resourceData.Get("endpoint_id").(string), + }, + }, + LockId: func(d *schema.ResourceData) string { + return d.Get("endpoint_id").(string) + }, + }, + } + return []ve.Callback{callback} +} + +func (VolcengineVpcEndpointZoneService) DatasourceResources(data *schema.ResourceData, resource *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + IdField: "ZoneId", + CollectField: "vpc_endpoint_zones", + ResponseConverts: map[string]ve.ResponseConvert{ + "ZoneId": { + TargetField: "id", + KeepDefault: true, + }, + "NetworkInterfaceIP": { + TargetField: "network_interface_ip", + }, + }, + } +} + +func (s *VolcengineVpcEndpointZoneService) ReadResourceId(id string) string { + return id +} + +func (s *VolcengineVpcEndpointZoneService) getZoneIdBySubnet(subnetId string) (zoneId string, err error) { + action := "DescribeSubnets" + req := map[string]interface{}{ + "SubnetIds.1": subnetId, + } + resp, err := s.Client.VpcClient.DescribeSubnetsCommon(&req) + if err != nil { + return "", err + } + logger.Debug(logger.RespFormat, action, req, *resp) + results, err := ve.ObtainSdkValue("Result.Subnets", *resp) + if err != nil { + return "", err + } + if results == nil { + results = []interface{}{} + } + subnets, ok := results.([]interface{}) + if !ok { + return "", errors.New("Result.Subnets is not Slice") + } + if len(subnets) == 0 { + return "", fmt.Errorf("subnet %s not exist", subnetId) + } + zoneId = subnets[0].(map[string]interface{})["ZoneId"].(string) + return zoneId, nil +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "privatelink", + Version: "2020-04-01", + HttpMethod: ve.GET, + Action: actionName, + ContentType: ve.Default, + } +} diff --git a/volcengine/provider.go b/volcengine/provider.go index 6064ba1a..c06e7dcf 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -1,16 +1,17 @@ package volcengine import ( - "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint" "strings" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/ssl_state" plSecurityGroup "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/security_group" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_connection" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_permission" "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_service_resource" + "github.com/volcengine/terraform-provider-volcengine/volcengine/privatelink/vpc_endpoint_zone" "github.com/volcengine/terraform-provider-volcengine/volcengine/mongodb/spec" @@ -314,6 +315,7 @@ func Provider() terraform.ResourceProvider { "volcengine_privatelink_vpc_endpoint_services": vpc_endpoint_service.DataSourceVolcenginePrivatelinkVpcEndpointServices(), "volcengine_privatelink_vpc_endpoint_service_permissions": vpc_endpoint_service_permission.DataSourceVolcenginePrivatelinkVpcEndpointServicePermissions(), "volcengine_privatelink_vpc_endpoint_connections": vpc_endpoint_connection.DataSourceVolcenginePrivatelinkVpcEndpointConnections(), + "volcengine_privatelink_vpc_endpoint_zones": vpc_endpoint_zone.DataSourceVolcenginePrivatelinkVpcEndpointZones(), }, ResourcesMap: map[string]*schema.Resource{ "volcengine_vpc": vpc.ResourceVolcengineVpc(), @@ -454,6 +456,7 @@ func Provider() terraform.ResourceProvider { "volcengine_privatelink_vpc_endpoint_service_permission": vpc_endpoint_service_permission.ResourceVolcenginePrivatelinkVpcEndpointServicePermission(), "volcengine_privatelink_security_group": plSecurityGroup.ResourceVolcenginePrivatelinkSecurityGroupService(), "volcengine_privatelink_vpc_endpoint_connection": vpc_endpoint_connection.ResourceVolcenginePrivatelinkVpcEndpointConnectionService(), + "volcengine_privatelink_vpc_endpoint_zone": vpc_endpoint_zone.ResourceVolcenginePrivatelinkVpcEndpointZone(), }, ConfigureFunc: ProviderConfigure, } From 4ed6fd51018a80fe907b576177a68c9ecf16157c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Thu, 13 Apr 2023 20:25:32 +0800 Subject: [PATCH 08/11] feat: add pl security group remove extra refresh --- .../service_volcengine_privatelink_security_group.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go index 0fe06355..67a497e8 100644 --- a/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go +++ b/volcengine/privatelink/security_group/service_volcengine_privatelink_security_group.go @@ -130,6 +130,16 @@ func (v *VolcenginePrivateLinkSecurityGroupService) RemoveResource(resourceData AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { return ve.CheckResourceUtilRemoved(d, v.ReadResource, 5*time.Minute) }, + ExtraRefresh: map[ve.ResourceService]*ve.StateRefresh{ + vpc_endpoint.NewVpcEndpointService(v.Client): { + Target: []string{"Available"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + ResourceId: resourceData.Get("endpoint_id").(string), + }, + }, + LockId: func(d *schema.ResourceData) string { + return resourceData.Get("endpoint_id").(string) + }, }, } return []ve.Callback{callback} From e20d028bfe0da5a7e6a8f0690adc20a533624600 Mon Sep 17 00:00:00 2001 From: "maoshuai.17" Date: Thu, 13 Apr 2023 21:24:55 +0800 Subject: [PATCH 09/11] feat: opt pl vpc endpoint --- example/privatelinkVpcEndpoint/main.tf | 1 - ...rce_volcengine_privatelink_vpc_endpoint.go | 27 +++------------ ...ice_volcengine_privatelink_vpc_endpoint.go | 34 +++++++++++++++++++ 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/example/privatelinkVpcEndpoint/main.tf b/example/privatelinkVpcEndpoint/main.tf index 5ccf321b..a1cca26d 100644 --- a/example/privatelinkVpcEndpoint/main.tf +++ b/example/privatelinkVpcEndpoint/main.tf @@ -1,5 +1,4 @@ resource "volcengine_privatelink_vpc_endpoint" "endpoint" { - vpc_id = "vpc-2d5z8cl807y8058ozfds8****" security_group_ids = ["sg-2d5z8cr53k45c58ozfdum****"] service_id = "epsvc-2byz5nzgiansw2dx0eehh****" endpoint_name = "tf-test-ep" diff --git a/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go b/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go index baff5734..d38bc7e9 100644 --- a/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go +++ b/volcengine/privatelink/vpc_endpoint/resource_volcengine_privatelink_vpc_endpoint.go @@ -33,12 +33,6 @@ func ResourceVolcenginePrivatelinkVpcEndpoint() *schema.Resource { Delete: schema.DefaultTimeout(30 * time.Minute), }, Schema: map[string]*schema.Schema{ - "vpc_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The vpc id of vpc endpoint.", - }, "security_group_ids": { Type: schema.TypeSet, Required: true, @@ -49,22 +43,6 @@ func ResourceVolcenginePrivatelinkVpcEndpoint() *schema.Resource { }, Description: "the security group ids of vpc endpoint", }, - //"zone_info": { - // Type: schema.TypeSet, - // Optional: true, - // ForceNew: true, - // Set: schema.HashString, - // Description: "the security group ids of vpc endpoint", - // Elem: schema.Resource{ - // Schema: map[string]*schema.Schema{ - // "vpc_id": { - // Type: schema.TypeString, - // Required: true, - // Description: "The vpc id of vpc endpoint.", - // }, - // }, - // }, - //}, "service_id": { Type: schema.TypeString, Required: true, @@ -91,6 +69,11 @@ func ResourceVolcenginePrivatelinkVpcEndpoint() *schema.Resource { Description: "The description of vpc endpoint.", }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "The vpc id of vpc endpoint.", + }, "endpoint_type": { Type: schema.TypeString, Computed: true, diff --git a/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go index 4a2636c3..274824f5 100644 --- a/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go +++ b/volcengine/privatelink/vpc_endpoint/service_volcengine_privatelink_vpc_endpoint.go @@ -162,6 +162,34 @@ func (s *VolcengineVpcEndpointService) CreateResource(resourceData *schema.Resou }, }, BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + // 通过安全组查询 vpc + securityGroupId := d.Get("security_group_ids").(*schema.Set).List()[0].(string) + action := "DescribeSecurityGroups" + req := map[string]interface{}{ + "SecurityGroupIds.1": securityGroupId, + } + resp, err := s.Client.VpcClient.DescribeSecurityGroupsCommon(&req) + if err != nil { + return false, err + } + logger.Debug(logger.RespFormat, action, req, *resp) + results, err := ve.ObtainSdkValue("Result.SecurityGroups", *resp) + if err != nil { + return false, err + } + if results == nil { + results = []interface{}{} + } + securityGroups, ok := results.([]interface{}) + if !ok { + return false, errors.New("Result.SecurityGroups is not Slice") + } + if len(securityGroups) == 0 { + return false, fmt.Errorf("securityGroup %s not exist", securityGroupId) + } + vpcId := securityGroups[0].(map[string]interface{})["VpcId"].(string) + + (*call.SdkParam)["VpcId"] = vpcId (*call.SdkParam)["ClientToken"] = uuid.New().String() return true, nil }, @@ -180,6 +208,9 @@ func (s *VolcengineVpcEndpointService) CreateResource(resourceData *schema.Resou Target: []string{"Available"}, Timeout: resourceData.Timeout(schema.TimeoutCreate), }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, }, } return []ve.Callback{callback} @@ -257,6 +288,9 @@ func (s *VolcengineVpcEndpointService) RemoveResource(resourceData *schema.Resou AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) }, + LockId: func(d *schema.ResourceData) string { + return d.Get("service_id").(string) + }, }, } return []ve.Callback{callback} From c55442d077ded3b276e5fa1aed95faf47db37cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Fri, 12 May 2023 04:25:24 +0000 Subject: [PATCH 10/11] feat: vke 2.12 & 2.12.1 update * feat: vke 2.12 & 2.12.1 update * feat: add vke 2.12 & 2.12.1 update doc * feat: update doc * Merge branch 'feat/tos_bucket_endpoint' into 'feat/vkeUpdate' * feat: update pkg doc * Merge branch 'feat/tos1' into 'feat/vkeUpdate' https://code.byted.org/iaasng/terraform-provider-volcengine/merge_requests/244 --- common/common_volcengine_version.go | 2 +- example/vkeCluster/main.tf | 24 +++-- example/vkeNodePool/main.tf | 14 ++- .../bucket/resource_volcengine_tos_bucket.go | 20 ++++ .../bucket/service_volcengine_tos_bucket.go | 27 +++++- .../data_source_volcengine_vke_clusters.go | 38 ++++++++ .../resource_volcengine_vke_cluster.go | 63 ++++++++++++- .../cluster/service_volcengine_vke_cluster.go | 91 +++++++++++++++++++ .../data_source_volcengine_vke_node_pools.go | 5 + .../resource_volcengine_vke_node_pool.go | 12 ++- .../service_volcengine_vke_node_pool.go | 21 +++++ website/docs/d/vke_clusters.html.markdown | 6 ++ website/docs/d/vke_node_pools.html.markdown | 1 + website/docs/r/tos_bucket.html.markdown | 5 +- website/docs/r/vke_cluster.html.markdown | 42 ++++++--- website/docs/r/vke_node_pool.html.markdown | 15 ++- 16 files changed, 349 insertions(+), 37 deletions(-) diff --git a/common/common_volcengine_version.go b/common/common_volcengine_version.go index 3f23fdf4..008d7dea 100644 --- a/common/common_volcengine_version.go +++ b/common/common_volcengine_version.go @@ -2,5 +2,5 @@ package common const ( TerraformProviderName = "terraform-provider-volcengine" - TerraformProviderVersion = "0.0.72" + TerraformProviderVersion = "0.0.73" ) diff --git a/example/vkeCluster/main.tf b/example/vkeCluster/main.tf index d6e76f66..4c312480 100644 --- a/example/vkeCluster/main.tf +++ b/example/vkeCluster/main.tf @@ -3,7 +3,7 @@ resource "volcengine_vke_cluster" "foo" { description = "created by terraform" delete_protection_enabled = false cluster_config { - subnet_ids = ["subnet-2bzud0pbor8qo2dx0ee884y6h"] + subnet_ids = ["subnet-rrqvkt2nq1hcv0x57ccqf3x"] api_server_public_access_enabled = true api_server_public_access_config { public_access_network_config { @@ -14,20 +14,28 @@ resource "volcengine_vke_cluster" "foo" { resource_public_access_default_enabled = true } pods_config { - pod_network_mode = "Flannel" - flannel_config { - pod_cidrs = ["172.27.224.0/19"] - max_pods_per_node = 64 - } + pod_network_mode = "VpcCniShared" +# flannel_config { +# pod_cidrs = ["172.27.224.0/19"] +# max_pods_per_node = 64 +# } vpc_cni_config { - subnet_ids = ["subnet-2bzud0pbor8qo2dx0ee884y6h"] + subnet_ids = ["subnet-rrqvkt2nq1hcv0x57ccqf3x", "subnet-miklcqh75vcw5smt1amo4ik5", "subnet-13g0x0ytpm0hs3n6nu5j591lv"] } } services_config { - service_cidrsv4 = ["192.168.0.0/16"] + service_cidrsv4 = ["172.30.0.0/18"] } tags { key = "k1" value = "v1" } + logging_config { + //log_project_id = "3189316d-a1ee-4892-a8fc-9a566489d590" + log_setups { + enabled = false + log_ttl = 30 + log_type = "Audit" + } + } } \ No newline at end of file diff --git a/example/vkeNodePool/main.tf b/example/vkeNodePool/main.tf index a6657085..dae203c5 100644 --- a/example/vkeNodePool/main.tf +++ b/example/vkeNodePool/main.tf @@ -1,21 +1,21 @@ resource "volcengine_vke_node_pool" "vke_test" { - cluster_id = "ccc2umdnqtoflv91lqtq0" + cluster_id = "ccgd6066rsfegs2dkhlog" name = "tf-test" node_config { - instance_type_ids = ["ecs.r1.large"] - subnet_ids = ["subnet-3reyr9ld3obnk5zsk2iqb1kk3"] + instance_type_ids = ["ecs.g1ie.xlarge"] + subnet_ids = ["subnet-mj1e9jgu96v45smt1a674x3h"] security { login { # ssh_key_pair_name = "ssh-6fbl66fxqm" password = "UHdkMTIzNDU2" } - security_group_ids = ["sg-2bz8cga08u48w2dx0eeym1fzy", "sg-2d6t6djr2wge858ozfczv41xq"] + security_group_ids = ["sg-13fbyz0sok3y83n6nu4hv1q10", "sg-mj1e9tbztgqo5smt1ah8l4bh"] } data_volumes { type = "ESSD_PL0" size = "60" } - instance_charge_type = "PrePaid" + instance_charge_type = "PostPaid" period = 1 ecs_tags { key = "ecs_k1" @@ -37,4 +37,8 @@ resource "volcengine_vke_node_pool" "vke_test" { key = "k1" value = "v1" } + auto_scaling { + enabled = true + subnet_policy = "ZoneBalance" + } } \ No newline at end of file diff --git a/volcengine/tos/bucket/resource_volcengine_tos_bucket.go b/volcengine/tos/bucket/resource_volcengine_tos_bucket.go index 905e9ea3..ff5f9493 100644 --- a/volcengine/tos/bucket/resource_volcengine_tos_bucket.go +++ b/volcengine/tos/bucket/resource_volcengine_tos_bucket.go @@ -115,6 +115,26 @@ func ResourceVolcengineTosBucket() *schema.Resource { }, Set: ve.TosAccountAclHash, }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + Description: "The create date of the TOS bucket.", + }, + "location": { + Type: schema.TypeString, + Computed: true, + Description: "The location of the TOS bucket.", + }, + "extranet_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "The extranet endpoint of the TOS bucket.", + }, + "intranet_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "The intranet endpoint the TOS bucket.", + }, }, } return resource diff --git a/volcengine/tos/bucket/service_volcengine_tos_bucket.go b/volcengine/tos/bucket/service_volcengine_tos_bucket.go index 3ecb9105..21653b01 100644 --- a/volcengine/tos/bucket/service_volcengine_tos_bucket.go +++ b/volcengine/tos/bucket/service_volcengine_tos_bucket.go @@ -57,6 +57,7 @@ func (s *VolcengineTosBucketService) ReadResource(resourceData *schema.ResourceD header http.Header acl map[string]interface{} version map[string]interface{} + buckets []interface{} ) if instanceId == "" { @@ -76,7 +77,31 @@ func (s *VolcengineTosBucketService) ReadResource(resourceData *schema.ResourceD if err != nil { return data, err } - data = make(map[string]interface{}) + + buckets, err = s.ReadResources(nil) + if err != nil { + return data, err + } + var ( + local interface{} + name interface{} + ) + for _, bucket := range buckets { + local, err = ve.ObtainSdkValue("Location", bucket) + if err != nil { + return data, err + } + name, err = ve.ObtainSdkValue("Name", bucket) + if err != nil { + return data, err + } + if local.(string) == s.Client.Region && name.(string) == instanceId { + data = bucket.(map[string]interface{}) + } + } + if data == nil { + data = make(map[string]interface{}) + } if header, ok = (*resp)[ve.TosHeader].(http.Header); ok { if header.Get("X-Tos-Storage-Class") != "" { diff --git a/volcengine/vke/cluster/data_source_volcengine_vke_clusters.go b/volcengine/vke/cluster/data_source_volcengine_vke_clusters.go index 19133c23..eab462ec 100644 --- a/volcengine/vke/cluster/data_source_volcengine_vke_clusters.go +++ b/volcengine/vke/cluster/data_source_volcengine_vke_clusters.go @@ -468,6 +468,44 @@ func DataSourceVolcengineVkeVkeClusters() *schema.Resource { }, }, }, + "logging_config": { + Type: schema.TypeList, + Computed: true, + Description: "Cluster log configuration information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_project_id": { + Type: schema.TypeString, + Computed: true, + Description: "The TLS log item ID of the collection target.", + }, + "log_setups": { + Type: schema.TypeList, + Computed: true, + Description: "Cluster logging options.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_type": { + Type: schema.TypeString, + Computed: true, + Description: "The currently enabled log type, optional values: `Audit`.", + }, + "log_ttl": { + Type: schema.TypeInt, + Computed: true, + Description: "The storage time of logs in Log Service. After the specified log storage time is exceeded, the expired logs in this log topic will be automatically cleared. The unit is days, and the default is 30 days. The value range is 1 to 3650, specifying 3650 days means permanent storage.", + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether to enable the log option, true means enable, false means not enable, the default is false. When Enabled is changed from false to true, a new Topic will be created.", + }, + }, + }, + }, + }, + }, + }, }, }, }, diff --git a/volcengine/vke/cluster/resource_volcengine_vke_cluster.go b/volcengine/vke/cluster/resource_volcengine_vke_cluster.go index 06fc1210..47f5aa7f 100644 --- a/volcengine/vke/cluster/resource_volcengine_vke_cluster.go +++ b/volcengine/vke/cluster/resource_volcengine_vke_cluster.go @@ -1,10 +1,12 @@ package cluster import ( + "bytes" "fmt" "strings" "time" + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ve "github.com/volcengine/terraform-provider-volcengine/common" @@ -137,7 +139,6 @@ func ResourceVolcengineVkeCluster() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Required: true, - ForceNew: true, Description: "The config of the pods.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -151,8 +152,8 @@ func ResourceVolcengineVkeCluster() *schema.Resource { "flannel_config": { Type: schema.TypeList, MaxItems: 1, - Optional: true, ForceNew: true, + Optional: true, Description: "Flannel network configuration.", DiffSuppressFunc: FlannelFieldDiffSuppress, Elem: &schema.Resource{ @@ -180,7 +181,6 @@ func ResourceVolcengineVkeCluster() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, - ForceNew: true, Description: "VPC-CNI network configuration.", DiffSuppressFunc: VpcCniConfigFieldDiffSuppress, Elem: &schema.Resource{ @@ -197,7 +197,6 @@ func ResourceVolcengineVkeCluster() *schema.Resource { "subnet_ids": { Type: schema.TypeSet, Optional: true, - ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, }, @@ -246,6 +245,50 @@ func ResourceVolcengineVkeCluster() *schema.Resource { Computed: true, Description: "Eip allocation Id.", }, + "logging_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Cluster log configuration information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_project_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The TLS log item ID of the collection target.", + }, + "log_setups": { + Type: schema.TypeSet, + Optional: true, + Set: logSetupsHash, + Description: "Cluster logging options. This structure can only be modified and added, and cannot be deleted.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "log_type": { + Type: schema.TypeString, + Required: true, + Description: "The currently enabled log type.", + }, + "log_ttl": { + Type: schema.TypeInt, + Optional: true, + Default: 30, + ValidateFunc: validation.IntBetween(1, 3650), + Description: "The storage time of logs in Log Service. After the specified log storage time is exceeded, the expired logs in this log topic will be automatically cleared. The unit is days, and the default is 30 days. The value range is 1 to 3650, specifying 3650 days means permanent storage.", + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to enable the log option, true means enable, false means not enable, the default is false. When Enabled is changed from false to true, a new Topic will be created.", + }, + }, + }, + }, + }, + }, + }, }, } } @@ -285,3 +328,15 @@ func resourceVolcengineVkeClusterDelete(d *schema.ResourceData, meta interface{} } return err } + +func logSetupsHash(i interface{}) int { + if i == nil { + return hashcode.String("") + } + m := i.(map[string]interface{}) + var ( + buf bytes.Buffer + ) + buf.WriteString(fmt.Sprintf("%v#%v#%v", m["log_type"], m["log_ttl"], m["enabled"])) + return hashcode.String(buf.String()) +} diff --git a/volcengine/vke/cluster/service_volcengine_vke_cluster.go b/volcengine/vke/cluster/service_volcengine_vke_cluster.go index 20c3d0dd..058a6ecb 100644 --- a/volcengine/vke/cluster/service_volcengine_vke_cluster.go +++ b/volcengine/vke/cluster/service_volcengine_vke_cluster.go @@ -3,6 +3,8 @@ package cluster import ( "errors" "fmt" + "reflect" + "sort" "time" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -359,6 +361,14 @@ func (s *VolcengineVkeClusterService) CreateResource(resourceData *schema.Resour TargetField: "Tags", ConvertType: ve.ConvertJsonObjectArray, }, + "logging_config": { + ConvertType: ve.ConvertJsonObject, + NextLevelConvert: map[string]ve.RequestConvert{ + "log_setups": { + ConvertType: ve.ConvertJsonObjectArray, + }, + }, + }, }, BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { if billingType, ok := (*call.SdkParam)["ClusterConfig.ApiServerPublicAccessConfig.PublicAccessNetworkConfig.BillingType"]; ok { @@ -444,6 +454,28 @@ func (s *VolcengineVkeClusterService) ModifyResource(resourceData *schema.Resour }, }, }, + "logging_config": { + ConvertType: ve.ConvertJsonObject, + NextLevelConvert: map[string]ve.RequestConvert{ + "log_setups": { + ConvertType: ve.ConvertJsonObjectArray, + NextLevelConvert: map[string]ve.RequestConvert{ + "log_type": { + ConvertType: ve.ConvertDefault, + ForceGet: true, + }, + "log_ttl": { + ConvertType: ve.ConvertDefault, + ForceGet: true, + }, + "enabled": { + ConvertType: ve.ConvertDefault, + ForceGet: true, + }, + }, + }, + }, + }, }, BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { if billingType, ok := (*call.SdkParam)["ClusterConfig.ApiServerPublicAccessConfig.PublicAccessNetworkConfig.BillingType"]; ok { @@ -456,6 +488,10 @@ func (s *VolcengineVkeClusterService) ModifyResource(resourceData *schema.Resour return true, nil }, ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + err := validateLogSetups(d) + if err != nil { + return nil, err + } logger.Debug(logger.RespFormat, call.Action, call.SdkParam) //修改cluster属性 return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) @@ -688,3 +724,58 @@ func getUniversalInfo(actionName string) ve.UniversalInfo { Action: actionName, } } + +func validateLogSetups(d *schema.ResourceData) error { + if d.HasChange("logging_config.0.log_setups") { + oldSet, newSet := d.GetChange("logging_config.0.log_setups") + logger.DebugInfo("set get change", oldSet, newSet) + oldTypeArr := make([]string, 0) + newTypeArr := make([]string, 0) + // 取到old和new的去重log type数组 + for _, o := range oldSet.(*schema.Set).List() { + if oMap, ok := o.(map[string]interface{}); ok { + if !ContainsInSlice(oldTypeArr, oMap["log_type"].(string)) { + oldTypeArr = append(oldTypeArr, oMap["log_type"].(string)) + } + } + } + for _, n := range newSet.(*schema.Set).List() { + if nMap, ok := n.(map[string]interface{}); ok { + if !ContainsInSlice(newTypeArr, nMap["log_type"].(string)) { + newTypeArr = append(newTypeArr, nMap["log_type"].(string)) + } + } + } + /* + 1. old数组长度大,必出现了减少 报错 + 2. old数组长度小,需判断old所有type是否都在new中,如有缺失,报错 + 3. old和new长度相等,需判断old和new完全相等 + */ + if len(oldTypeArr) > len(newTypeArr) { + return fmt.Errorf("logging setups can only be modified and added, and cannot be deleted") + } + if len(oldTypeArr) < len(newTypeArr) { + for _, o := range oldTypeArr { + if !ContainsInSlice(newTypeArr, o) { + return fmt.Errorf("logging setups can only be modified and added, and cannot be deleted") + } + } + } else { + sort.Strings(newTypeArr) + sort.Strings(oldTypeArr) + if !reflect.DeepEqual(oldTypeArr, newTypeArr) { + return fmt.Errorf("logging setups can only be modified and added, and cannot be deleted") + } + } + } + return nil +} + +func ContainsInSlice(items []string, item string) bool { + for _, eachItem := range items { + if eachItem == item { + return true + } + } + return false +} diff --git a/volcengine/vke/node_pool/data_source_volcengine_vke_node_pools.go b/volcengine/vke/node_pool/data_source_volcengine_vke_node_pools.go index ba4ac24b..242e5a59 100644 --- a/volcengine/vke/node_pool/data_source_volcengine_vke_node_pools.go +++ b/volcengine/vke/node_pool/data_source_volcengine_vke_node_pools.go @@ -168,6 +168,11 @@ func DataSourceVolcengineNodePools() *schema.Resource { Computed: true, Description: "The Priority of AutoScaling.", }, + "subnet_policy": { + Type: schema.TypeString, + Computed: true, + Description: "Multi-subnet scheduling strategy for nodes. The value can be `ZoneBalance` or `Priority`.", + }, "subnet_ids": { Type: schema.TypeList, Computed: true, diff --git a/volcengine/vke/node_pool/resource_volcengine_vke_node_pool.go b/volcengine/vke/node_pool/resource_volcengine_vke_node_pool.go index d3127091..485516dc 100644 --- a/volcengine/vke/node_pool/resource_volcengine_vke_node_pool.go +++ b/volcengine/vke/node_pool/resource_volcengine_vke_node_pool.go @@ -68,7 +68,7 @@ func ResourceVolcengineNodePool() *schema.Resource { Type: schema.TypeInt, Optional: true, Default: 10, - ValidateFunc: validation.IntBetween(0, 2000), + ValidateFunc: validation.IntBetween(1, 2000), Description: "The MaxReplicas of AutoScaling, default 10, range in 1~2000.", }, "min_replicas": { @@ -90,6 +90,16 @@ func ResourceVolcengineNodePool() *schema.Resource { ValidateFunc: validation.IntBetween(0, 100), Description: "The Priority of AutoScaling, default 10, rang in 0~100.", }, + "subnet_policy": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "ZoneBalance", + "Priority", + }, false), + Description: "Multi-subnet scheduling strategy for nodes. The value can be `ZoneBalance` or `Priority`.", + }, }, }, Description: "The node pool elastic scaling configuration information.", diff --git a/volcengine/vke/node_pool/service_volcengine_vke_node_pool.go b/volcengine/vke/node_pool/service_volcengine_vke_node_pool.go index fee2bb28..b60261b6 100644 --- a/volcengine/vke/node_pool/service_volcengine_vke_node_pool.go +++ b/volcengine/vke/node_pool/service_volcengine_vke_node_pool.go @@ -402,6 +402,10 @@ func (s *VolcengineNodePoolService) CreateResource(resourceData *schema.Resource ForceGet: true, TargetField: "Priority", }, + "subnet_policy": { + ForceGet: true, + TargetField: "SubnetPolicy", + }, }, }, "tags": { @@ -409,6 +413,16 @@ func (s *VolcengineNodePoolService) CreateResource(resourceData *schema.Resource ConvertType: ve.ConvertJsonObjectArray, }, }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + if chargeType, ok := (*call.SdkParam)["NodeConfig.InstanceChargeType"]; ok { + if autoScalingEnabled, ok := (*call.SdkParam)["AutoScaling.Enabled"]; ok { + if chargeType.(string) == "PrePaid" && autoScalingEnabled.(bool) { + return false, fmt.Errorf("PrePaid charge type cannot support auto scaling") + } + } + } + return true, nil + }, ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { logger.Debug(logger.RespFormat, call.Action, call.SdkParam) resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) @@ -542,6 +556,10 @@ func (s *VolcengineNodePoolService) ModifyResource(resourceData *schema.Resource ForceGet: true, TargetField: "Priority", }, + "subnet_policy": { + ForceGet: true, + TargetField: "SubnetPolicy", + }, }, }, }, @@ -718,6 +736,9 @@ func (s *VolcengineNodePoolService) DatasourceResources(*schema.ResourceData, *s "AutoScaling.Priority": { TargetField: "priority", }, + "AutoScaling.SubnetPolicy": { + TargetField: "subnet_policy", + }, "KubernetesConfig.Cordon": { TargetField: "cordon", }, diff --git a/website/docs/d/vke_clusters.html.markdown b/website/docs/d/vke_clusters.html.markdown index b5ad8ae8..6e3a38ef 100644 --- a/website/docs/d/vke_clusters.html.markdown +++ b/website/docs/d/vke_clusters.html.markdown @@ -71,6 +71,12 @@ In addition to all arguments above, the following attributes are exported: * `kubeconfig_private` - Kubeconfig data with private network access, returned in BASE64 encoding, it is suggested to use vke_kubeconfig instead. * `kubeconfig_public` - Kubeconfig data with public network access, returned in BASE64 encoding, it is suggested to use vke_kubeconfig instead. * `kubernetes_version` - The Kubernetes version information corresponding to the cluster, specific to the patch version. + * `logging_config` - Cluster log configuration information. + * `log_project_id` - The TLS log item ID of the collection target. + * `log_setups` - Cluster logging options. + * `enabled` - Whether to enable the log option, true means enable, false means not enable, the default is false. When Enabled is changed from false to true, a new Topic will be created. + * `log_ttl` - The storage time of logs in Log Service. After the specified log storage time is exceeded, the expired logs in this log topic will be automatically cleared. The unit is days, and the default is 30 days. The value range is 1 to 3650, specifying 3650 days means permanent storage. + * `log_type` - The currently enabled log type, optional values: `Audit`. * `name` - The name of the cluster. * `node_statistics` - Statistics on the number of nodes corresponding to each master state in the cluster. * `creating_count` - Phase=Creating total number of nodes. diff --git a/website/docs/d/vke_node_pools.html.markdown b/website/docs/d/vke_node_pools.html.markdown index ba374f6e..1c2d8ddb 100644 --- a/website/docs/d/vke_node_pools.html.markdown +++ b/website/docs/d/vke_node_pools.html.markdown @@ -91,6 +91,7 @@ In addition to all arguments above, the following attributes are exported: * `security_strategies` - The SecurityStrategies of NodeConfig. * `security_strategy_enabled` - The SecurityStrategyEnabled of NodeConfig. * `subnet_ids` - The SubnetId of NodeConfig. + * `subnet_policy` - Multi-subnet scheduling strategy for nodes. The value can be `ZoneBalance` or `Priority`. * `system_volume` - The SystemVolume of NodeConfig. * `size` - The Size of SystemVolume. * `type` - The Type of SystemVolume. diff --git a/website/docs/r/tos_bucket.html.markdown b/website/docs/r/tos_bucket.html.markdown index 66e9bee6..2cb9a808 100644 --- a/website/docs/r/tos_bucket.html.markdown +++ b/website/docs/r/tos_bucket.html.markdown @@ -42,7 +42,10 @@ The `account_acl` object supports the following: ## Attributes Reference In addition to all arguments above, the following attributes are exported: * `id` - ID of the resource. - +* `creation_date` - The create date of the TOS bucket. +* `extranet_endpoint` - The extranet endpoint of the TOS bucket. +* `intranet_endpoint` - The intranet endpoint the TOS bucket. +* `location` - The location of the TOS bucket. ## Import diff --git a/website/docs/r/vke_cluster.html.markdown b/website/docs/r/vke_cluster.html.markdown index 7d6316e1..f9db8b12 100644 --- a/website/docs/r/vke_cluster.html.markdown +++ b/website/docs/r/vke_cluster.html.markdown @@ -15,7 +15,7 @@ resource "volcengine_vke_cluster" "foo" { description = "created by terraform" delete_protection_enabled = false cluster_config { - subnet_ids = ["subnet-2bzud0pbor8qo2dx0ee884y6h"] + subnet_ids = ["subnet-rrqvkt2nq1hcv0x57ccqf3x"] api_server_public_access_enabled = true api_server_public_access_config { public_access_network_config { @@ -26,34 +26,43 @@ resource "volcengine_vke_cluster" "foo" { resource_public_access_default_enabled = true } pods_config { - pod_network_mode = "Flannel" - flannel_config { - pod_cidrs = ["172.27.224.0/19"] - max_pods_per_node = 64 - } + pod_network_mode = "VpcCniShared" + # flannel_config { + # pod_cidrs = ["172.27.224.0/19"] + # max_pods_per_node = 64 + # } vpc_cni_config { - subnet_ids = ["subnet-2bzud0pbor8qo2dx0ee884y6h"] + subnet_ids = ["subnet-rrqvkt2nq1hcv0x57ccqf3x", "subnet-miklcqh75vcw5smt1amo4ik5", "subnet-13g0x0ytpm0hs3n6nu5j591lv"] } } services_config { - service_cidrsv4 = ["192.168.0.0/16"] + service_cidrsv4 = ["172.30.0.0/18"] } tags { key = "k1" value = "v1" } + logging_config { + //log_project_id = "3189316d-a1ee-4892-a8fc-9a566489d590" + log_setups { + enabled = false + log_ttl = 30 + log_type = "Audit" + } + } } ``` ## Argument Reference The following arguments are supported: * `cluster_config` - (Required) The config of the cluster. * `name` - (Required) The name of the cluster. -* `pods_config` - (Required, ForceNew) The config of the pods. +* `pods_config` - (Required) The config of the pods. * `services_config` - (Required, ForceNew) The config of the services. * `client_token` - (Optional) ClientToken is a case-sensitive string of no more than 64 ASCII characters passed in by the caller. * `delete_protection_enabled` - (Optional) The delete protection of the cluster, the value is `true` or `false`. * `description` - (Optional) The description of the cluster. * `kubernetes_version` - (Optional, ForceNew) The version of Kubernetes specified when creating a VKE cluster (specified to patch version), if not specified, the latest Kubernetes version supported by VKE is used by default, which is a 3-segment version format starting with a lowercase v, that is, KubernetesVersion with IsLatestVersion=True in the return value of ListSupportedVersions. +* `logging_config` - (Optional) Cluster log configuration information. * `tags` - (Optional) Tags. The `api_server_public_access_config` object supports the following: @@ -72,11 +81,22 @@ The `flannel_config` object supports the following: * `max_pods_per_node` - (Optional, ForceNew) The maximum number of single-node Pod instances for a Flannel container network, the value can be `16` or `32` or `64` or `128` or `256`. * `pod_cidrs` - (Optional, ForceNew) Pod CIDR for the Flannel container network. +The `log_setups` object supports the following: + +* `log_type` - (Required) The currently enabled log type. +* `enabled` - (Optional) Whether to enable the log option, true means enable, false means not enable, the default is false. When Enabled is changed from false to true, a new Topic will be created. +* `log_ttl` - (Optional) The storage time of logs in Log Service. After the specified log storage time is exceeded, the expired logs in this log topic will be automatically cleared. The unit is days, and the default is 30 days. The value range is 1 to 3650, specifying 3650 days means permanent storage. + +The `logging_config` object supports the following: + +* `log_project_id` - (Optional) The TLS log item ID of the collection target. +* `log_setups` - (Optional) Cluster logging options. This structure can only be modified and added, and cannot be deleted. + The `pods_config` object supports the following: * `pod_network_mode` - (Required, ForceNew) The container network model of the cluster, the value is `Flannel` or `VpcCniShared`. Flannel: Flannel network model, an independent Underlay container network solution, combined with the global routing capability of VPC, to achieve a high-performance network experience for the cluster. VpcCniShared: VPC-CNI network model, an Underlay container network solution based on the ENI of the private network elastic network card, with high network communication performance. * `flannel_config` - (Optional, ForceNew) Flannel network configuration. -* `vpc_cni_config` - (Optional, ForceNew) VPC-CNI network configuration. +* `vpc_cni_config` - (Optional) VPC-CNI network configuration. The `public_access_network_config` object supports the following: @@ -94,7 +114,7 @@ The `tags` object supports the following: The `vpc_cni_config` object supports the following: -* `subnet_ids` - (Optional, ForceNew) A list of Pod subnet IDs for the VPC-CNI container network. +* `subnet_ids` - (Optional) A list of Pod subnet IDs for the VPC-CNI container network. * `vpc_id` - (Optional, ForceNew) The private network where the cluster control plane network resides. ## Attributes Reference diff --git a/website/docs/r/vke_node_pool.html.markdown b/website/docs/r/vke_node_pool.html.markdown index 8976c385..6f3b725e 100644 --- a/website/docs/r/vke_node_pool.html.markdown +++ b/website/docs/r/vke_node_pool.html.markdown @@ -11,23 +11,23 @@ Provides a resource to manage vke node pool ## Example Usage ```hcl resource "volcengine_vke_node_pool" "vke_test" { - cluster_id = "ccc2umdnqtoflv91lqtq0" + cluster_id = "ccgd6066rsfegs2dkhlog" name = "tf-test" node_config { - instance_type_ids = ["ecs.r1.large"] - subnet_ids = ["subnet-3reyr9ld3obnk5zsk2iqb1kk3"] + instance_type_ids = ["ecs.g1ie.xlarge"] + subnet_ids = ["subnet-mj1e9jgu96v45smt1a674x3h"] security { login { # ssh_key_pair_name = "ssh-6fbl66fxqm" password = "UHdkMTIzNDU2" } - security_group_ids = ["sg-2bz8cga08u48w2dx0eeym1fzy", "sg-2d6t6djr2wge858ozfczv41xq"] + security_group_ids = ["sg-13fbyz0sok3y83n6nu4hv1q10", "sg-mj1e9tbztgqo5smt1ah8l4bh"] } data_volumes { type = "ESSD_PL0" size = "60" } - instance_charge_type = "PrePaid" + instance_charge_type = "PostPaid" period = 1 ecs_tags { key = "ecs_k1" @@ -49,6 +49,10 @@ resource "volcengine_vke_node_pool" "vke_test" { key = "k1" value = "v1" } + auto_scaling { + enabled = true + subnet_policy = "ZoneBalance" + } } ``` ## Argument Reference @@ -68,6 +72,7 @@ The `auto_scaling` object supports the following: * `max_replicas` - (Optional) The MaxReplicas of AutoScaling, default 10, range in 1~2000. * `min_replicas` - (Optional) The MinReplicas of AutoScaling, default 0. * `priority` - (Optional) The Priority of AutoScaling, default 10, rang in 0~100. +* `subnet_policy` - (Optional) Multi-subnet scheduling strategy for nodes. The value can be `ZoneBalance` or `Priority`. The `data_volumes` object supports the following: From 75e8c453e981e45b700dc3b1dc2b2f009e59225c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=93=B2=E9=93=AD?= Date: Mon, 22 May 2023 17:11:41 +0800 Subject: [PATCH 11/11] feat: opt vke cluster doc --- volcengine/vke/cluster/resource_volcengine_vke_cluster.go | 2 +- website/docs/r/vke_cluster.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volcengine/vke/cluster/resource_volcengine_vke_cluster.go b/volcengine/vke/cluster/resource_volcengine_vke_cluster.go index 47f5aa7f..1fdbd3d9 100644 --- a/volcengine/vke/cluster/resource_volcengine_vke_cluster.go +++ b/volcengine/vke/cluster/resource_volcengine_vke_cluster.go @@ -262,7 +262,7 @@ func ResourceVolcengineVkeCluster() *schema.Resource { Type: schema.TypeSet, Optional: true, Set: logSetupsHash, - Description: "Cluster logging options. This structure can only be modified and added, and cannot be deleted.", + Description: "Cluster logging options. This structure can only be modified and added, and cannot be deleted. When encountering a `cannot be deleted` error, please query the log setups of the current cluster and fill in the current `tf` file.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "log_type": { diff --git a/website/docs/r/vke_cluster.html.markdown b/website/docs/r/vke_cluster.html.markdown index f9db8b12..a8f39d63 100644 --- a/website/docs/r/vke_cluster.html.markdown +++ b/website/docs/r/vke_cluster.html.markdown @@ -90,7 +90,7 @@ The `log_setups` object supports the following: The `logging_config` object supports the following: * `log_project_id` - (Optional) The TLS log item ID of the collection target. -* `log_setups` - (Optional) Cluster logging options. This structure can only be modified and added, and cannot be deleted. +* `log_setups` - (Optional) Cluster logging options. This structure can only be modified and added, and cannot be deleted. When encountering a `cannot be deleted` error, please query the log setups of the current cluster and fill in the current `tf` file. The `pods_config` object supports the following: