From e057f1f38f5fa323439a8fbe4d674ef3d00b721b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=9C=E6=9D=BE?= Date: Thu, 16 Jan 2025 10:53:53 +0800 Subject: [PATCH] feat: new resource alicloud_ack_one_membership_attaching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doc: add doc for alicloud_ack_one_membership_attaching test: new resource alicloud_ack_one_membership_attaching Signed-off-by: 宜松 --- alicloud/provider.go | 1 + ..._alicloud_ack_one_membership_attachment.go | 145 ++++++++++++++ ...loud_ack_one_membership_attachment_test.go | 184 ++++++++++++++++++ alicloud/service_alicloud_ack_one_v2.go | 66 ++++++- ...ck_one_membership_attachment.html.markdown | 182 +++++++++++++++++ 5 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 alicloud/resource_alicloud_ack_one_membership_attachment.go create mode 100644 alicloud/resource_alicloud_ack_one_membership_attachment_test.go create mode 100644 website/docs/r/ack_one_membership_attachment.html.markdown diff --git a/alicloud/provider.go b/alicloud/provider.go index 40aa0288b93d..6248d7ad5e54 100644 --- a/alicloud/provider.go +++ b/alicloud/provider.go @@ -1058,6 +1058,7 @@ func Provider() terraform.ResourceProvider { "alicloud_arms_environment": resourceAliCloudArmsEnvironment(), "alicloud_hologram_instance": resourceAliCloudHologramInstance(), "alicloud_ack_one_cluster": resourceAliCloudAckOneCluster(), + "alicloud_ack_one_membership_attachment": resourceAliCloudAckOneMembershipAttachment(), "alicloud_drds_polardbx_instance": resourceAliCloudDrdsPolardbxInstance(), "alicloud_gpdb_backup_policy": resourceAliCloudGpdbBackupPolicy(), "alicloud_threat_detection_file_upload_limit": resourceAliCloudThreatDetectionFileUploadLimit(), diff --git a/alicloud/resource_alicloud_ack_one_membership_attachment.go b/alicloud/resource_alicloud_ack_one_membership_attachment.go new file mode 100644 index 000000000000..ffca2d5f82cb --- /dev/null +++ b/alicloud/resource_alicloud_ack_one_membership_attachment.go @@ -0,0 +1,145 @@ +package alicloud + +import ( + "log" + "time" + + util "github.com/alibabacloud-go/tea-utils/service" + "github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAliCloudAckOneMembershipAttachment() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the ACK One fleet cluster", + }, + "sub_cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the ACK cluster that needs to be managed by ACK One fleet", + }, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(25 * time.Minute), + Delete: schema.DefaultTimeout(25 * time.Minute), + }, + Create: resourceAliCloudAckOneMembershipAttachmentCreate, + Read: resourceAliCloudAckOneMembershipAttachmentRead, + Delete: resourceAliCloudAckOneMembershipAttachmentDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + } +} + +func resourceAliCloudAckOneMembershipAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*connectivity.AliyunClient) + + action := "AttachClusterToHub" + var request map[string]interface{} + var response map[string]interface{} + conn, err := client.NewAckoneClient() + if err != nil { + return WrapError(err) + } + request = make(map[string]interface{}) + request["ClusterId"] = d.Get("cluster_id") + request["ClusterIds"] = "[\"" + d.Get("sub_cluster_id").(string) + "\"]" + + runtime := util.RuntimeOptions{} + runtime.SetAutoretry(true) + wait := incrementalWait(3*time.Second, 5*time.Second) + err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + response, err = conn.DoRequest(StringPointer(action), nil, StringPointer("POST"), StringPointer("2022-01-01"), StringPointer("AK"), nil, request, &runtime) + + if err != nil { + if NeedRetry(err) { + wait() + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + addDebug(action, response, request) + + if err != nil { + return WrapErrorf(err, DefaultErrorMsg, "alicloud_ack_one_membership_attachment", action, AlibabaCloudSdkGoERROR) + } + + managedClusterIds := response["ManagedClusterIds"].([]interface{}) + if len(managedClusterIds) != 1 { + return WrapErrorf(err, DefaultErrorMsg, "alicloud_ack_one_membership_attachment", action, AlibabaCloudSdkGoERROR) + } + + managedClusterId := managedClusterIds[0].(string) + d.SetId(response["ClusterId"].(string) + ":" + managedClusterId) + + return nil +} + +func resourceAliCloudAckOneMembershipAttachmentRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*connectivity.AliyunClient) + ackOneServiceV2 := AckOneServiceV2{client} + + objectRaw, err := ackOneServiceV2.DescribeAckOneMembershipAttachment(d.Id()) + if err != nil { + if !d.IsNewResource() && NotFoundError(err) { + log.Printf("[DEBUG] Resource alicloud_ack_one_membership_attachment DescribeAckOneMembershipAttachment Failed!!! %s", err) + d.SetId("") + return nil + } + return WrapError(err) + } + + d.Set("cluster_id", objectRaw["cluster_id"]) + d.Set("sub_cluster_id", objectRaw["sub_cluster_id"]) + return nil +} + +func resourceAliCloudAckOneMembershipAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*connectivity.AliyunClient) + action := "DetachClusterFromHub" + var request map[string]interface{} + var response map[string]interface{} + conn, err := client.NewAckoneClient() + if err != nil { + return WrapError(err) + } + request = make(map[string]interface{}) + request["ClusterId"] = d.Get("cluster_id") + request["ClusterIds"] = "[\"" + d.Get("sub_cluster_id").(string) + "\"]" + + runtime := util.RuntimeOptions{} + runtime.SetAutoretry(true) + wait := incrementalWait(3*time.Second, 5*time.Second) + err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + response, err = conn.DoRequest(StringPointer(action), nil, StringPointer("POST"), StringPointer("2022-01-01"), StringPointer("AK"), nil, request, &runtime) + + if err != nil { + if NeedRetry(err) { + wait() + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + addDebug(action, response, request) + + if err != nil { + if NotFoundError(err) { + return nil + } + return WrapErrorf(err, DefaultErrorMsg, d.Id(), action, AlibabaCloudSdkGoERROR) + } + + return nil +} diff --git a/alicloud/resource_alicloud_ack_one_membership_attachment_test.go b/alicloud/resource_alicloud_ack_one_membership_attachment_test.go new file mode 100644 index 000000000000..12668d9d09f7 --- /dev/null +++ b/alicloud/resource_alicloud_ack_one_membership_attachment_test.go @@ -0,0 +1,184 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccAliCloudAckOneMembershipAttachment_basic(t *testing.T) { + var v map[string]interface{} + resourceId := "alicloud_ack_one_membership_attachment.default" + ra := resourceAttrInit(resourceId, AliCloudAckOneMembershipAttachmentMap) + rc := resourceCheckInitWithDescribeMethod(resourceId, &v, func() interface{} { + return &AckOneServiceV2{testAccProvider.Meta().(*connectivity.AliyunClient)} + }, "DescribeAckOneMembershipAttachment") + rac := resourceAttrCheckInit(rc, ra) + testAccCheck := rac.resourceAttrMapUpdateSet() + rand := acctest.RandInt() + name := fmt.Sprintf("tf-testAccAckOneMembershipAttachment-%d", rand) + testAccConfig := resourceTestAccConfigFunc(resourceId, name, AliCloudAckOneMembershipAttachmentBasicDependence0) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: rac.checkResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testAccConfig(map[string]interface{}{ + "cluster_id": "${alicloud_ack_one_cluster.default.id}", + "sub_cluster_id": "${alicloud_cs_managed_kubernetes.default.id}", + }), + Check: resource.ComposeTestCheckFunc( + testAccCheck( + map[string]string{ + "cluster_id": CHECKSET, + "sub_cluster_id": CHECKSET, + }, + ), + ), + }, + { + ResourceName: resourceId, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + }, + }, + }) +} + +var AliCloudAckOneMembershipAttachmentMap = map[string]string{ + "cluster_id": CHECKSET, + "sub_cluster_id": CHECKSET, +} + +func AliCloudAckOneMembershipAttachmentBasicDependence0(name string) string { + return fmt.Sprintf(` +variable "name" { + default = "%s" +} + +provider "alicloud" { + region = "cn-hangzhou" +} + +data "alicloud_zones" "default" { + available_resource_creation = "VSwitch" +} + +resource "alicloud_vpc" "defaultVpc" { + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "defaultyVSwitch" { + vpc_id = alicloud_vpc.defaultVpc.id + cidr_block = "172.16.2.0/24" + zone_id = data.alicloud_zones.default.zones.0.id +} + +resource "alicloud_ack_one_cluster" "default" { + network { + vpc_id = alicloud_vpc.defaultVpc.id + vswitches = ["${alicloud_vswitch.defaultyVSwitch.id}"] + } +} + +# leave it to empty would create a new one +variable "vpc_id" { + description = "Existing vpc id used to create several vswitches and other resources." + default = "" +} + +variable "vpc_cidr" { + description = "The cidr block used to launch a new vpc when 'vpc_id' is not specified." + default = "10.0.0.0/8" +} + +# leave it to empty then terraform will create several vswitches +variable "vswitch_ids" { + description = "List of existing vswitch id." + type = list(string) + default = [] +} + +variable "vswitch_cidrs" { + description = "List of cidr blocks used to create several new vswitches when 'vswitch_ids' is not specified." + type = list(string) + default = ["10.1.0.0/16", "10.2.0.0/16"] +} + +# options: between 24-28 +variable "node_cidr_mask" { + description = "The node cidr block to specific how many pods can run on single node." + default = 24 +} + +# options: ipvs|iptables +variable "proxy_mode" { + description = "Proxy mode is option of kube-proxy." + default = "ipvs" +} + +variable "service_cidr" { + description = "The kubernetes service cidr block. It cannot be equals to vpc's or vswitch's or pod's and cannot be in them." + default = "192.168.0.0/16" +} + +variable "terway_vswitch_ids" { + description = "List of existing vswitch ids for terway." + type = list(string) + default = [] +} + +variable "terway_vswitch_cidrs" { + description = "List of cidr blocks used to create several new vswitches when 'terway_vswitch_cidrs' is not specified." + type = list(string) + default = ["10.4.0.0/16", "10.5.0.0/16"] +} + +data "alicloud_enhanced_nat_available_zones" "enhanced" {} + +# If there is not specifying vpc_id, the module will launch a new vpc +resource "alicloud_vpc" "vpc" { + count = var.vpc_id == "" ? 1 : 0 + cidr_block = var.vpc_cidr +} + +# According to the vswitch cidr blocks to launch several vswitches +resource "alicloud_vswitch" "vswitches" { + count = length(var.vswitch_ids) > 0 ? 0 : length(var.vswitch_cidrs) + vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id + cidr_block = element(var.vswitch_cidrs, count.index) + zone_id = data.alicloud_enhanced_nat_available_zones.enhanced.zones[count.index].zone_id +} + +# According to the vswitch cidr blocks to launch several vswitches +resource "alicloud_vswitch" "terway_vswitches" { + count = length(var.terway_vswitch_ids) > 0 ? 0 : length(var.terway_vswitch_cidrs) + vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id + cidr_block = element(var.terway_vswitch_cidrs, count.index) + zone_id = data.alicloud_enhanced_nat_available_zones.enhanced.zones[count.index].zone_id +} + +resource "alicloud_cs_managed_kubernetes" "default" { + cluster_spec = "ack.pro.small" + # version can not be defined in variables.tf. + # version = "1.26.3-aliyun.1" + vswitch_ids = length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids)) : length(var.vswitch_cidrs) < 1 ? [] : split(",", join(",", alicloud_vswitch.vswitches.*.id)) + pod_vswitch_ids = length(var.terway_vswitch_ids) > 0 ? split(",", join(",", var.terway_vswitch_ids)) : length(var.terway_vswitch_cidrs) < 1 ? [] : split(",", join(",", alicloud_vswitch.terway_vswitches.*.id)) + new_nat_gateway = true + node_cidr_mask = var.node_cidr_mask + proxy_mode = var.proxy_mode + service_cidr = var.service_cidr + + is_enterprise_security_group = true + + addons { + name = "terway-eniip" + } +} +`, name) +} diff --git a/alicloud/service_alicloud_ack_one_v2.go b/alicloud/service_alicloud_ack_one_v2.go index d2bb2a73f450..5f219feabdbd 100644 --- a/alicloud/service_alicloud_ack_one_v2.go +++ b/alicloud/service_alicloud_ack_one_v2.go @@ -2,6 +2,7 @@ package alicloud import ( "fmt" + "strings" "time" "github.com/PaesslerAG/jsonpath" @@ -44,9 +45,9 @@ func (s *AckOneServiceV2) DescribeAckOneCluster(id string) (object map[string]in } return resource.NonRetryableError(err) } - addDebug(action, response, request) return nil }) + addDebug(action, response, request) if err != nil { if IsExpectedErrors(err, []string{"Cluster.NotFound"}) { @@ -84,4 +85,67 @@ func (s *AckOneServiceV2) AckOneClusterStateRefreshFunc(id string, field string, } } +func (s *AckOneServiceV2) DescribeAckOneMembershipAttachment(id string) (object map[string]interface{}, err error) { + + client := s.client + var request map[string]interface{} + var response map[string]interface{} + var query map[string]interface{} + action := "DescribeManagedClusters" + conn, err := client.NewAckoneClient() + if err != nil { + return object, WrapError(err) + } + request = make(map[string]interface{}) + query = make(map[string]interface{}) + clusterId := strings.Split(id, ":")[0] + subClusterId := strings.Split(id, ":")[1] + query["ClusterId"] = clusterId + + runtime := util.RuntimeOptions{} + runtime.SetAutoretry(true) + wait := incrementalWait(3*time.Second, 5*time.Second) + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + response, err = conn.DoRequest(StringPointer(action), nil, StringPointer("POST"), StringPointer("2022-01-01"), StringPointer("AK"), query, request, &runtime) + + if err != nil { + if NeedRetry(err) { + wait() + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + addDebug(action, response, request) + + if err != nil { + if IsExpectedErrors(err, []string{"Cluster.NotFound"}) { + return object, WrapErrorf(Error(GetNotFoundMessage("Cluster", id)), NotFoundMsg, response) + } + return object, WrapErrorf(err, DefaultErrorMsg, id, action, AlibabaCloudSdkGoERROR) + } + + clusters := response["Clusters"].([]interface{}) + + found := false + for _, cluster := range clusters { + managedCluster := cluster.(map[string]interface{}) + clusterId := managedCluster["Cluster"].(map[string]interface{})["ClusterID"].(string) + if clusterId == subClusterId { + found = true + break + } + } + if !found { + return object, WrapErrorf(Error(GetNotFoundMessage("Cluster", id)), NotFoundMsg, response) + } + + v := map[string]interface{}{ + "cluster_id": clusterId, + "sub_cluster_id": subClusterId, + } + return v, nil +} + // DescribeAckOneCluster >>> Encapsulated. diff --git a/website/docs/r/ack_one_membership_attachment.html.markdown b/website/docs/r/ack_one_membership_attachment.html.markdown new file mode 100644 index 000000000000..a7d832ca0d10 --- /dev/null +++ b/website/docs/r/ack_one_membership_attachment.html.markdown @@ -0,0 +1,182 @@ +--- +subcategory: "Ack One" +layout: "alicloud" +page_title: "Alicloud: alicloud_ack_one_membership_attachment" +description: |- + Provides a Alicloud Ack One Membership Attachment resource. +--- + +# alicloud_ack_one_membership_attachment + +Provides an Ack One Membership Attachment resource. Fleet Manager Membership Attachment. + +For information about Ack One Membership Attachment and how to use it, see [How to attach cluster tp hub](https://www.alibabacloud.com/help/en/ack/distributed-cloud-container-platform-for-kubernetes/developer-reference/api-adcp-2022-01-01-attachclustertohub). + +-> **NOTE:** Available since v1.242.0. + +## Example Usage + +Basic Usage + +```terraform +provider "alicloud" { + region = "cn-hangzhou" +} + +variable "name" { + default = "example" +} + +provider "alicloud" { + region = "cn-hangzhou" +} + +data "alicloud_zones" "default" { + available_resource_creation = "VSwitch" +} + +resource "alicloud_vpc" "defaultVpc" { + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "defaultyVSwitch" { + vpc_id = alicloud_vpc.defaultVpc.id + cidr_block = "172.16.2.0/24" + zone_id = data.alicloud_zones.default.zones.0.id +} + +resource "alicloud_ack_one_cluster" "default" { + network { + vpc_id = alicloud_vpc.defaultVpc.id + vswitches = ["${alicloud_vswitch.defaultyVSwitch.id}"] + } +} + +# leave it to empty would create a new one +variable "vpc_id" { + description = "Existing vpc id used to create several vswitches and other resources." + default = "" +} + +variable "vpc_cidr" { + description = "The cidr block used to launch a new vpc when 'vpc_id' is not specified." + default = "10.0.0.0/8" +} + +# leave it to empty then terraform will create several vswitches +variable "vswitch_ids" { + description = "List of existing vswitch id." + type = list(string) + default = [] +} + +variable "vswitch_cidrs" { + description = "List of cidr blocks used to create several new vswitches when 'vswitch_ids' is not specified." + type = list(string) + default = ["10.1.0.0/16", "10.2.0.0/16"] +} + +# options: between 24-28 +variable "node_cidr_mask" { + description = "The node cidr block to specific how many pods can run on single node." + default = 24 +} + +# options: ipvs|iptables +variable "proxy_mode" { + description = "Proxy mode is option of kube-proxy." + default = "ipvs" +} + +variable "service_cidr" { + description = "The kubernetes service cidr block. It cannot be equals to vpc's or vswitch's or pod's and cannot be in them." + default = "192.168.0.0/16" +} + +variable "terway_vswitch_ids" { + description = "List of existing vswitch ids for terway." + type = list(string) + default = [] +} + +variable "terway_vswitch_cidrs" { + description = "List of cidr blocks used to create several new vswitches when 'terway_vswitch_cidrs' is not specified." + type = list(string) + default = ["10.4.0.0/16", "10.5.0.0/16"] +} + +data "alicloud_enhanced_nat_available_zones" "enhanced" {} + +# If there is not specifying vpc_id, the module will launch a new vpc +resource "alicloud_vpc" "vpc" { + count = var.vpc_id == "" ? 1 : 0 + cidr_block = var.vpc_cidr +} + +# According to the vswitch cidr blocks to launch several vswitches +resource "alicloud_vswitch" "vswitches" { + count = length(var.vswitch_ids) > 0 ? 0 : length(var.vswitch_cidrs) + vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id + cidr_block = element(var.vswitch_cidrs, count.index) + zone_id = data.alicloud_enhanced_nat_available_zones.enhanced.zones[count.index].zone_id +} + +# According to the vswitch cidr blocks to launch several vswitches +resource "alicloud_vswitch" "terway_vswitches" { + count = length(var.terway_vswitch_ids) > 0 ? 0 : length(var.terway_vswitch_cidrs) + vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id + cidr_block = element(var.terway_vswitch_cidrs, count.index) + zone_id = data.alicloud_enhanced_nat_available_zones.enhanced.zones[count.index].zone_id +} + +resource "alicloud_cs_managed_kubernetes" "default" { + cluster_spec = "ack.pro.small" + # version can not be defined in variables.tf. + # version = "1.26.3-aliyun.1" + vswitch_ids = length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids)) : length(var.vswitch_cidrs) < 1 ? [] : split(",", join(",", alicloud_vswitch.vswitches.*.id)) + pod_vswitch_ids = length(var.terway_vswitch_ids) > 0 ? split(",", join(",", var.terway_vswitch_ids)) : length(var.terway_vswitch_cidrs) < 1 ? [] : split(",", join(",", alicloud_vswitch.terway_vswitches.*.id)) + new_nat_gateway = true + node_cidr_mask = var.node_cidr_mask + proxy_mode = var.proxy_mode + service_cidr = var.service_cidr + + is_enterprise_security_group = true + + addons { + name = "terway-eniip" + } +} + +resource "alicloud_ack_one_membership_attachment" "default" { + cluster_id = alicloud_ack_one_cluster.default.id + sub_cluster_id = alicloud_cs_managed_kubernetes.default.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cluster_id` - (Required, ForceNew) The ID of the cluster to which the membership is being attached. +* `sub_cluster_id` - (Required, ForceNew) The ID of the member being attached to the cluster. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The resource ID in terraform of Membership Attachment. It formats as < cluster_id >:< sub_cluster_id >. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration-0-11/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 25 mins) Used when creating the Membership Attachment. +* `delete` - (Defaults to 25 mins) Used when deleting the Membership Attachment. + +## Import + +Ack One Membership Attachment can be imported using the id, which consists of cluster_id and sub_cluster_id, e.g. + +```shell +terraform import alicloud_ack_one_membership_attachment.example : +```