From ccffcd290c7840150501c2e7927b8d06e000cc5b Mon Sep 17 00:00:00 2001 From: Kagashino Date: Mon, 4 Jul 2022 19:21:57 +0800 Subject: [PATCH 1/4] feat: tke - support separate tke cluster endpoint resource switch --- tencentcloud/basic_test.go | 6 +- tencentcloud/provider.go | 2 + .../resource_tc_kubernetes_cluster.go | 23 +- ...resource_tc_kubernetes_cluster_endpoint.go | 408 ++++++++++++++++++ ...rce_tc_kubernetes_cluster_endpoint_test.go | 126 ++++++ .../kubernetes_cluster_endpoint.html.markdown | 67 +++ website/tencentcloud.erb | 3 + 7 files changed, 620 insertions(+), 15 deletions(-) create mode 100644 tencentcloud/resource_tc_kubernetes_cluster_endpoint.go create mode 100644 tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go create mode 100644 website/docs/r/kubernetes_cluster_endpoint.html.markdown diff --git a/tencentcloud/basic_test.go b/tencentcloud/basic_test.go index 4bb9f81e85..3ff1cbf47d 100644 --- a/tencentcloud/basic_test.go +++ b/tencentcloud/basic_test.go @@ -601,9 +601,9 @@ variable "tke_cidr_a" { default = [ "10.31.0.0/23", "10.31.2.0/24", - "10.31.3.0/25", - "10.31.3.128/26", - "10.31.3.192/26" + "10.31.3.0/24", + "10.31.16.0/24", + "10.31.32.0/24" ] } diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index e08d9063ea..0ee24d1d9c 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -370,6 +370,7 @@ Tencent Kubernetes Engine(TKE) tencentcloud_eks_container_instance tencentcloud_kubernetes_auth_attachment tencentcloud_kubernetes_addon_attachment + tencentcloud_kubernetes_cluster_endpoint TDMQ Resource @@ -977,6 +978,7 @@ func Provider() terraform.ResourceProvider { "tencentcloud_container_cluster": resourceTencentCloudContainerCluster(), "tencentcloud_container_cluster_instance": resourceTencentCloudContainerClusterInstance(), "tencentcloud_kubernetes_cluster": resourceTencentCloudTkeCluster(), + "tencentcloud_kubernetes_cluster_endpoint": resourceTencentCloudTkeClusterEndpoint(), "tencentcloud_eks_cluster": resourceTencentCloudEksCluster(), "tencentcloud_eks_container_instance": resourceTencentCloudEksContainerInstance(), "tencentcloud_kubernetes_addon_attachment": resourceTencentCloudTkeAddonAttachment(), diff --git a/tencentcloud/resource_tc_kubernetes_cluster.go b/tencentcloud/resource_tc_kubernetes_cluster.go index 8d460fa68f..02656025ca 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster.go +++ b/tencentcloud/resource_tc_kubernetes_cluster.go @@ -2484,16 +2484,18 @@ func resourceTencentCloudTkeClusterRead(d *schema.ResourceData, meta interface{} _ = d.Set("pgw_endpoint", emptyStrFunc(securityRet.Response.PgwEndpoint)) _ = d.Set("security_policy", policies) - if emptyStrFunc(securityRet.Response.ClusterExternalEndpoint) == "" { - _ = d.Set("cluster_internet", false) - } else { - _ = d.Set("cluster_internet", true) - } + if v, ok := d.GetOk("worker_config"); ok && len(v.([]interface{})) > 0 { + if emptyStrFunc(securityRet.Response.ClusterExternalEndpoint) == "" { + _ = d.Set("cluster_internet", false) + } else { + _ = d.Set("cluster_internet", true) + } - if emptyStrFunc(securityRet.Response.PgwEndpoint) == "" { - _ = d.Set("cluster_intranet", false) - } else { - _ = d.Set("cluster_intranet", true) + if emptyStrFunc(securityRet.Response.PgwEndpoint) == "" { + _ = d.Set("cluster_intranet", false) + } else { + _ = d.Set("cluster_intranet", true) + } } var globalConfig *tke.ClusterAsGroupOption @@ -2777,7 +2779,6 @@ func resourceTencentCloudTkeClusterUpdate(d *schema.ResourceData, meta interface return err } } - d.SetPartial("cluster_internet") } if clusterInternet { @@ -2790,8 +2791,6 @@ func resourceTencentCloudTkeClusterUpdate(d *schema.ResourceData, meta interface } d.SetPartial("managed_cluster_internet_security_policies") } - } else { - d.SetPartial("managed_cluster_internet_security_policies") } if d.HasChange("project_id") || d.HasChange("cluster_name") || d.HasChange("cluster_desc") || d.HasChange("cluster_level") || d.HasChange("auto_upgrade_cluster_level") { diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go new file mode 100644 index 0000000000..cdddac1954 --- /dev/null +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go @@ -0,0 +1,408 @@ +/* +Provide a resource to create a KubernetesClusterEndpoint. +This resource allows you to create an empty cluster first without any workers. Only all attached node depends create complete, cluster endpoint will finally be enabled. + +~> **NOTE:** Recommend using `depends_on` to make sure endpoint create after node pools or workers does. + +Example Usage + +```hcl +resource "tencentcloud_kubernetes_nodepool" "pool1" {} + +resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { + cluster_id = "cls-xxxxxxxx" + cluster_internet = true + cluster_intranet = true + managed_cluster_internet_security_policies = [ + "192.168.0.0/24" + ] + cluster_intranet_subnet_id = "subnet-xxxxxxxx" + depends_on [ + tencentcloud_kubernetes_nodepool.pool1 + ] +} +``` + + +Import + +KubernetesClusterEndpoint instance can be imported by passing cluster id, e.g. +``` +$ terraform import tencentcloud_kubernetes_cluster_endpoint.test cluster-id +``` + +*/ +package tencentcloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-multierror" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudTkeClusterEndpoint() *schema.Resource { + return &schema.Resource{ + Read: resourceTencentCloudTkeClusterEndpointRead, + Create: resourceTencentCloudTkeClusterEndpointCreate, + Update: resourceTencentCloudTkeClusterEndpointUpdate, + Delete: resourceTencentCloudTkeClusterEndpointDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "cluster_id": { + Type: schema.TypeString, + Required: true, + Description: "Specify cluster ID.", + }, + "cluster_internet": { + Type: schema.TypeBool, + Default: false, + Optional: true, + Description: "Open internet access or not.", + }, + "cluster_intranet": { + Type: schema.TypeBool, + Default: false, + Optional: true, + Description: "Open intranet access or not.", + }, + "managed_cluster_internet_security_policies": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Security policies for managed cluster internet, like:'192.168.1.0/24' or '113.116.51.27', '0.0.0.0/0' means all." + + " This field can only set when field `cluster_deploy_type` is 'MANAGED_CLUSTER' and `cluster_internet` is true." + + " `managed_cluster_internet_security_policies` can not delete or empty once be set.", + }, + "cluster_intranet_subnet_id": { + Type: schema.TypeString, + Optional: true, + Description: "Subnet id who can access this independent cluster, this field must and can only set when `cluster_intranet` is true." + + " `cluster_intranet_subnet_id` can not modify once be set.", + }, + // Computed + "cluster_deploy_type": { + Type: schema.TypeString, + Computed: true, + Description: "Cluster deploy type of `MANAGED_CLUSTER` or `INDEPENDENT_CLUSTER`.", + }, + "user_name": { + Type: schema.TypeString, + Computed: true, + Description: "User name of account.", + }, + "password": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "Password of account.", + }, + "certification_authority": { + Type: schema.TypeString, + Computed: true, + Description: "The certificate used for access.", + }, + "cluster_external_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "External network address to access.", + }, + "domain": { + Type: schema.TypeString, + Computed: true, + Description: "Domain name for access.", + }, + "pgw_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "The Intranet address used for access.", + }, + }, + } +} + +func resourceTencentCloudTkeClusterEndpointRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_kubernetes_cluster_endpoint.read")() + defer inconsistentCheck(d, meta)() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := TkeService{client} + + id := d.Id() + info, has, err := service.DescribeCluster(ctx, id) + if err != nil { + d.SetId("") + return err + } + if !has { + d.SetId("") + return fmt.Errorf("cluster %s not found", id) + } + + d.SetId(id) + _ = d.Set("cluster_id", id) + + _ = d.Set("cluster_deploy_type", info.DeployType) + + response, err := service.DescribeClusterSecurity(ctx, id) + + if err != nil { + return err + } + + var ( + security = response.Response + clusterInternet = security.ClusterExternalEndpoint != nil && *security.ClusterExternalEndpoint != "" + clusterIntranet = security.PgwEndpoint != nil && *security.PgwEndpoint != "" + ) + + _ = d.Set("cluster_internet", clusterInternet) + _ = d.Set("cluster_intranet", clusterIntranet) + _ = d.Set("user_name", security.UserName) + _ = d.Set("password", security.Password) + _ = d.Set("certification_authority", security.CertificationAuthority) + _ = d.Set("cluster_external_endpoint", security.ClusterExternalEndpoint) + _ = d.Set("domain", security.Domain) + _ = d.Set("pgw_endpoint", security.PgwEndpoint) + + if len(security.SecurityPolicy) > 0 { + _ = d.Set("managed_cluster_internet_security_policies", security.SecurityPolicy) + } + + return nil +} + +func resourceTencentCloudTkeClusterEndpointCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_kubernetes_cluster_endpoint.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := TkeService{client} + + id := d.Get("cluster_id").(string) + var ( + err error + isManagedCluster bool + securityPolicies []string + clusterInternet = d.Get("cluster_internet").(bool) + clusterIntranet = d.Get("cluster_intranet").(bool) + intranetSubnetId = d.Get("cluster_intranet_subnet_id").(string) + ) + + clusterInfo, has, err := service.DescribeCluster(ctx, id) + if err != nil { + return err + } + if !has { + return fmt.Errorf("cluster %s not found", id) + } + isManagedCluster = clusterInfo.DeployType == TKE_DEPLOY_TYPE_MANAGED + + if v, ok := d.Get("managed_cluster_internet_security_policies").([]interface{}); ok && len(v) > 0 { + securityPolicies = helper.InterfacesStrings(v) + } + + if clusterIntranet && intranetSubnetId == "" { + return fmt.Errorf("`cluster_intranet_subnet_id` must set when `cluster_intranet` is true") + } + if !clusterIntranet && intranetSubnetId != "" { + return fmt.Errorf("`cluster_intranet_subnet_id` can only set when `cluster_intranet` is true") + } + + if !(clusterInternet && isManagedCluster) && len(securityPolicies) > 0 { + return fmt.Errorf("`managed_cluster_internet_security_policies` can only set when field `cluster_deploy_type` is 'MANAGED_CLUSTER' and `cluster_internet` is true.") + } + + // Create Intranet(Private) Network + if clusterIntranet { + err := tencentCloudClusterIntranetSwitch(ctx, &service, id, intranetSubnetId, true) + if err != nil { + return err + } + } + + //TKE_DEPLOY_TYPE_INDEPENDENT Open the internet + if clusterInternet { + err := tencentCloudClusterInternetSwitch(ctx, &service, id, true, isManagedCluster, securityPolicies) + if err != nil { + return err + } + err = waitForClusterEndpointFinish(ctx, &service, id, true, isManagedCluster) + if err != nil { + return err + } + } + + d.SetId(id) + + return resourceTencentCloudTkeClusterEndpointRead(d, meta) +} + +func resourceTencentCloudTkeClusterEndpointUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_kubernetes_cluster_endpoint.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := TkeService{client} + id := d.Id() + + var ( + isManagedCluster bool + err error + ) + if v, ok := d.GetOk("cluster_deploy_type"); ok { + isManagedCluster = v.(string) == TKE_DEPLOY_TYPE_MANAGED + } + + if d.HasChange("cluster_internet") { + clusterInternet := d.Get("cluster_internet").(bool) + policies := helper.InterfacesStrings(d.Get("managed_cluster_internet_security_policies").([]interface{})) + err = tencentCloudClusterInternetSwitch(ctx, &service, id, clusterInternet, isManagedCluster, policies) + if err != nil { + return err + } + err = waitForClusterEndpointFinish(ctx, &service, id, clusterInternet, isManagedCluster) + if err != nil { + return err + } + } + + if d.HasChange("cluster_intranet") { + clusterIntranet := d.Get("cluster_intranet").(bool) + subnetId := d.Get("cluster_intranet_subnet_id").(string) + err = tencentCloudClusterIntranetSwitch(ctx, &service, id, subnetId, clusterIntranet) + if err != nil { + return err + } + } + + return resourceTencentCloudTkeClusterEndpointRead(d, meta) +} + +func resourceTencentCloudTkeClusterEndpointDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_kubernetes_cluster_endpoint.delete")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := TkeService{client} + var ( + id = d.Id() + err error + isManagedCluster bool + ) + if v, ok := d.GetOk("cluster_deploy_type"); ok { + isManagedCluster = v.(string) == TKE_DEPLOY_TYPE_MANAGED + } + + response, err := service.DescribeClusterSecurity(ctx, id) + + if err != nil { + return err + } + + var ( + security = response.Response + clusterInternet = security.ClusterExternalEndpoint != nil && *security.ClusterExternalEndpoint != "" + clusterIntranet = security.PgwEndpoint != nil && *security.PgwEndpoint != "" + errs multierror.Error + ) + + if clusterInternet { + err = tencentCloudClusterInternetSwitch(ctx, &service, id, false, isManagedCluster, nil) + if err != nil { + errs = *multierror.Append(err) + } else { + taskErr := waitForClusterEndpointFinish(ctx, &service, id, false, isManagedCluster) + if taskErr != nil { + errs = *multierror.Append(taskErr) + } + } + } + + if clusterIntranet { + err = tencentCloudClusterIntranetSwitch(ctx, &service, id, "", false) + if err != nil { + errs = *multierror.Append(err) + } + } + + return errs.ErrorOrNil() +} + +func waitForClusterEndpointFinish(ctx context.Context, service *TkeService, id string, enabled bool, isManagedCluster bool) (err error) { + return resource.Retry(2*readRetryTimeout, func() *resource.RetryError { + var ( + status string + message string + inErr error + retryableState = TkeInternetStatusCreating + finishStates = []string{TkeInternetStatusNotfound, TkeInternetStatusCreated} + ) + + if !enabled { + retryableState = TkeInternetStatusDeleting + finishStates = []string{TkeInternetStatusNotfound, TkeInternetStatusDeleted} + } + + if isManagedCluster { + status, message, inErr = service.DescribeClusterEndpointVipStatus(ctx, id) + } else { + status, message, inErr = service.DescribeClusterEndpointStatus(ctx, id) + } + if inErr != nil { + return retryError(inErr) + } + if status == retryableState { + return resource.RetryableError( + fmt.Errorf("%s create cluster internet endpoint status still is %s", id, status)) + } + if IsContains(finishStates, status) { + return nil + } + return resource.NonRetryableError( + fmt.Errorf("%s create cluster internet endpoint error ,status is %s,message is %s", id, status, message)) + }) +} + +func tencentCloudClusterInternetSwitch(ctx context.Context, service *TkeService, id string, enable, isManagedCluster bool, policies []string) (err error) { + if enable { + if isManagedCluster { + err = service.CreateClusterEndpointVip(ctx, id, policies) + } else { + err = service.CreateClusterEndpoint(ctx, id, "", true) + } + } else { + if isManagedCluster { + err = service.DeleteClusterEndpointVip(ctx, id) + } else { + err = service.DeleteClusterEndpoint(ctx, id, true) + } + } + return nil +} + +func tencentCloudClusterIntranetSwitch(ctx context.Context, service *TkeService, id, subnetId string, enable bool) (err error) { + if enable { + err = service.CreateClusterEndpoint(ctx, id, subnetId, false) + if err != nil { + return + } + } else { + err = service.DeleteClusterEndpoint(ctx, id, false) + if err != nil { + return + } + } + return nil +} diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go new file mode 100644 index 0000000000..73e0f86c9e --- /dev/null +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go @@ -0,0 +1,126 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccTencentCloudTkeClusterEndpoint(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTkeClusterEndpointBasic, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckTkeExists("tencentcloud_kubernetes_cluster.managed_cluster"), + resource.TestCheckResourceAttrSet("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_id"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_internet", "true"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_intranet", "true"), + ), + }, + { + ResourceName: "tencentcloud_kubernetes_cluster_endpoint.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_intranet_subnet_id"}, + }, + { + Config: testAccTkeClusterEndpointBasicUpdate, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckTkeExists("tencentcloud_kubernetes_cluster.managed_cluster"), + resource.TestCheckResourceAttrSet("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_id"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_internet", "true"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_intranet", "true"), + ), + }, + }, + }) +} + +const testAccTkeClusterEndpointBasicDeps = TkeCIDRs + TkeDataSource + ClusterAttachmentInstanceType + defaultImages + ` +variable "availability_zone" { + default = "ap-guangzhou-3" +} + +data "tencentcloud_vpc_instances" "vpcs" { + name = "keep_tke_exclusive_vpc" +} + +data "tencentcloud_vpc_subnets" "sub" { + vpc_id = data.tencentcloud_vpc_instances.vpcs.instance_list.0.vpc_id +} + +resource "tencentcloud_instance" "foo" { + instance_name = "tf-auto-test-1-2" + availability_zone = data.tencentcloud_vpc_subnets.sub.instance_list.0.availability_zone + image_id = var.default_img_id + instance_type = local.type1 + system_disk_type = "CLOUD_PREMIUM" + system_disk_size = 50 + vpc_id = data.tencentcloud_vpc_instances.vpcs.instance_list.0.vpc_id + subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id + tags = data.tencentcloud_kubernetes_clusters.tke.list.0.tags # new added node will passive add tag by cluster +} + +resource "tencentcloud_kubernetes_cluster" "managed_cluster" { + vpc_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.vpc_id + cluster_cidr = var.tke_cidr_a.3 + cluster_max_pod_num = 32 + cluster_name = "for-endpoint" + cluster_version = "1.20.6" + cluster_max_service_num = 32 + cluster_os = "tlinux2.2(tkernel3)x86_64" + + cluster_deploy_type = "MANAGED_CLUSTER" +} + +resource "tencentcloud_kubernetes_cluster_attachment" "test_attach" { + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + instance_id = tencentcloud_instance.foo.id + password = "Lo4wbdit" + unschedulable = 0 +} + +resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + cluster_internet = true + cluster_intranet = true + managed_cluster_internet_security_policies = [ + "192.168.0.0/24" + ] + cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id + depends_on = [ + tencentcloud_kubernetes_cluster_attachment.test_attach + ] +} +` + +const testAccTkeClusterEndpointBasic = testAccTkeClusterEndpointBasicDeps + ` +resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + cluster_internet = true + cluster_intranet = true + managed_cluster_internet_security_policies = [ + "192.168.0.0/24" + ] + cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id + depends_on = [ + tencentcloud_kubernetes_cluster_attachment.test_attach + ] +} +` + +const testAccTkeClusterEndpointBasicUpdate = testAccTkeClusterEndpointBasicDeps + ` +resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + cluster_internet = false + cluster_intranet = true + cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id + depends_on = [ + tencentcloud_kubernetes_cluster_attachment.test_attach + ] +} +` diff --git a/website/docs/r/kubernetes_cluster_endpoint.html.markdown b/website/docs/r/kubernetes_cluster_endpoint.html.markdown new file mode 100644 index 0000000000..a0f013b9c4 --- /dev/null +++ b/website/docs/r/kubernetes_cluster_endpoint.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "Tencent Kubernetes Engine(TKE)" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_kubernetes_cluster_endpoint" +sidebar_current: "docs-tencentcloud-resource-kubernetes_cluster_endpoint" +description: |- + Provide a resource to create a KubernetesClusterEndpoint. +This resource allows you to create an empty cluster first without any workers. Only all attached node depends create complete, cluster endpoint will finally be enabled. +--- + +# tencentcloud_kubernetes_cluster_endpoint + +Provide a resource to create a KubernetesClusterEndpoint. +This resource allows you to create an empty cluster first without any workers. Only all attached node depends create complete, cluster endpoint will finally be enabled. + +~> **NOTE:** Recommend using `depends_on` to make sure endpoint create after node pools or workers does. + +## Example Usage + +```hcl +resource "tencentcloud_kubernetes_nodepool" "pool1" {} + +resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { + cluster_id = "cls-xxxxxxxx" + cluster_internet = true + cluster_intranet = true + managed_cluster_internet_security_policies = [ + "192.168.0.0/24" + ] + cluster_intranet_subnet_id = "subnet-xxxxxxxx" + depends_on[ + tencentcloud_kubernetes_nodepool.pool1 + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cluster_id` - (Required) Specify cluster ID. +* `cluster_internet` - (Optional) Open internet access or not. +* `cluster_intranet_subnet_id` - (Optional) Subnet id who can access this independent cluster, this field must and can only set when `cluster_intranet` is true. `cluster_intranet_subnet_id` can not modify once be set. +* `cluster_intranet` - (Optional) Open intranet access or not. +* `managed_cluster_internet_security_policies` - (Optional) Security policies for managed cluster internet, like:'192.168.1.0/24' or '113.116.51.27', '0.0.0.0/0' means all. This field can only set when field `cluster_deploy_type` is 'MANAGED_CLUSTER' and `cluster_internet` is true. `managed_cluster_internet_security_policies` can not delete or empty once be set. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `certification_authority` - The certificate used for access. +* `cluster_deploy_type` - Cluster deploy type of `MANAGED_CLUSTER` or `INDEPENDENT_CLUSTER`. +* `cluster_external_endpoint` - External network address to access. +* `domain` - Domain name for access. +* `password` - Password of account. +* `pgw_endpoint` - The Intranet address used for access. +* `user_name` - User name of account. + + +## Import + +KubernetesClusterEndpoint instance can be imported by passing cluster id, e.g. +``` +$ terraform import tencentcloud_kubernetes_cluster_endpoint.test cluster-id +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 41bb47d9bf..0a7d2e3c6e 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -1557,6 +1557,9 @@
  • tencentcloud_kubernetes_cluster_attachment
  • +
  • + tencentcloud_kubernetes_cluster_endpoint +
  • tencentcloud_kubernetes_node_pool
  • From d9f1e16765f4271d7a7229bff35401928f82cee5 Mon Sep 17 00:00:00 2001 From: Kagashino Date: Tue, 5 Jul 2022 10:07:00 +0800 Subject: [PATCH 2/4] fix: tke endpoint - remove duplicate foo block --- .../resource_tc_kubernetes_cluster_endpoint_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go index 73e0f86c9e..36f86fbb8a 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go @@ -84,18 +84,6 @@ resource "tencentcloud_kubernetes_cluster_attachment" "test_attach" { unschedulable = 0 } -resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { - cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id - cluster_internet = true - cluster_intranet = true - managed_cluster_internet_security_policies = [ - "192.168.0.0/24" - ] - cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id - depends_on = [ - tencentcloud_kubernetes_cluster_attachment.test_attach - ] -} ` const testAccTkeClusterEndpointBasic = testAccTkeClusterEndpointBasicDeps + ` From cd0deff81d68d0804dc0e464f43a85c850f8d785 Mon Sep 17 00:00:00 2001 From: Kagashino Date: Wed, 6 Jul 2022 10:26:00 +0800 Subject: [PATCH 3/4] fix: tke endpoint - fix assert --- .../resource_tc_kubernetes_cluster_endpoint_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go index 36f86fbb8a..5178f35744 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go @@ -19,6 +19,13 @@ func TestAccTencentCloudTkeClusterEndpoint(t *testing.T) { resource.TestCheckResourceAttrSet("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_id"), resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_internet", "true"), resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_intranet", "true"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "managed_cluster_internet_security_policies.#", "1"), + resource.TestCheckResourceAttr( + "tencentcloud_kubernetes_cluster_endpoint.foo", + "managed_cluster_internet_security_policies.0", + "192.168.0.0/24", + ), + resource.TestCheckResourceAttrSet("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_intranet_subnet_id"), ), }, { @@ -32,7 +39,7 @@ func TestAccTencentCloudTkeClusterEndpoint(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckTkeExists("tencentcloud_kubernetes_cluster.managed_cluster"), resource.TestCheckResourceAttrSet("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_id"), - resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_internet", "true"), + resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_internet", "false"), resource.TestCheckResourceAttr("tencentcloud_kubernetes_cluster_endpoint.foo", "cluster_intranet", "true"), ), }, From f0d4d58359c0244f6a3590c9048d6befb6a2ea36 Mon Sep 17 00:00:00 2001 From: Kagashino Date: Fri, 8 Jul 2022 18:49:19 +0800 Subject: [PATCH 4/4] fix: tke endpoint - add retry for nodepool --- ...resource_tc_kubernetes_cluster_endpoint.go | 58 ++++++++++++------ ...rce_tc_kubernetes_cluster_endpoint_test.go | 59 +++++++++++++------ 2 files changed, 80 insertions(+), 37 deletions(-) diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go index cdddac1954..74f510840c 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint.go @@ -38,6 +38,8 @@ import ( "context" "fmt" + tke "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tke/v20180525" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -376,33 +378,51 @@ func waitForClusterEndpointFinish(ctx context.Context, service *TkeService, id s } func tencentCloudClusterInternetSwitch(ctx context.Context, service *TkeService, id string, enable, isManagedCluster bool, policies []string) (err error) { - if enable { - if isManagedCluster { - err = service.CreateClusterEndpointVip(ctx, id, policies) - } else { - err = service.CreateClusterEndpoint(ctx, id, "", true) - } - } else { - if isManagedCluster { - err = service.DeleteClusterEndpointVip(ctx, id) + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + if enable { + if isManagedCluster { + err = service.CreateClusterEndpointVip(ctx, id, policies) + } else { + err = service.CreateClusterEndpoint(ctx, id, "", true) + } + if err != nil { + return retryError(err, tke.RESOURCEUNAVAILABLE_CLUSTERSTATE) + } } else { - err = service.DeleteClusterEndpoint(ctx, id, true) + if isManagedCluster { + err = service.DeleteClusterEndpointVip(ctx, id) + } else { + err = service.DeleteClusterEndpoint(ctx, id, true) + } + if err != nil { + return retryError(err) + } } + return nil + }) + if err != nil { + return err } return nil } func tencentCloudClusterIntranetSwitch(ctx context.Context, service *TkeService, id, subnetId string, enable bool) (err error) { - if enable { - err = service.CreateClusterEndpoint(ctx, id, subnetId, false) - if err != nil { - return - } - } else { - err = service.DeleteClusterEndpoint(ctx, id, false) - if err != nil { - return + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + if enable { + err = service.CreateClusterEndpoint(ctx, id, subnetId, false) + if err != nil { + return retryError(err, tke.RESOURCEUNAVAILABLE_CLUSTERSTATE) + } + } else { + err = service.DeleteClusterEndpoint(ctx, id, false) + if err != nil { + return retryError(err) + } } + return nil + }) + if err != nil { + return err } return nil } diff --git a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go index 5178f35744..c2582892b2 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go +++ b/tencentcloud/resource_tc_kubernetes_cluster_endpoint_test.go @@ -60,18 +60,6 @@ data "tencentcloud_vpc_subnets" "sub" { vpc_id = data.tencentcloud_vpc_instances.vpcs.instance_list.0.vpc_id } -resource "tencentcloud_instance" "foo" { - instance_name = "tf-auto-test-1-2" - availability_zone = data.tencentcloud_vpc_subnets.sub.instance_list.0.availability_zone - image_id = var.default_img_id - instance_type = local.type1 - system_disk_type = "CLOUD_PREMIUM" - system_disk_size = 50 - vpc_id = data.tencentcloud_vpc_instances.vpcs.instance_list.0.vpc_id - subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id - tags = data.tencentcloud_kubernetes_clusters.tke.list.0.tags # new added node will passive add tag by cluster -} - resource "tencentcloud_kubernetes_cluster" "managed_cluster" { vpc_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.vpc_id cluster_cidr = var.tke_cidr_a.3 @@ -84,11 +72,46 @@ resource "tencentcloud_kubernetes_cluster" "managed_cluster" { cluster_deploy_type = "MANAGED_CLUSTER" } -resource "tencentcloud_kubernetes_cluster_attachment" "test_attach" { - cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id - instance_id = tencentcloud_instance.foo.id - password = "Lo4wbdit" +data "tencentcloud_security_groups" "sg" { + name = "default" +} + +resource "tencentcloud_kubernetes_node_pool" "np_test" { + name = "test-endpoint-attachment" + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + max_size = 1 + min_size = 1 + vpc_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.vpc_id + subnet_ids = [data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id] + retry_policy = "INCREMENTAL_INTERVALS" + desired_capacity = 1 + enable_auto_scale = true + scaling_group_name = "basic_group" + default_cooldown = 400 + termination_policies = ["OLDEST_INSTANCE"] + + auto_scaling_config { + instance_type = local.type1 + system_disk_type = "CLOUD_PREMIUM" + system_disk_size = "50" + security_group_ids = [data.tencentcloud_security_groups.sg.security_groups[0].security_group_id] + + cam_role_name = "TCB_QcsRole" + data_disk { + disk_type = "CLOUD_PREMIUM" + disk_size = 50 + } + + internet_charge_type = "TRAFFIC_POSTPAID_BY_HOUR" + internet_max_bandwidth_out = 10 + public_ip_assigned = true + password = "test123#" + enhanced_security_service = false + enhanced_monitor_service = false + + } unschedulable = 0 + node_os="Tencent tlinux release 2.2 (Final)" } ` @@ -103,7 +126,7 @@ resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { ] cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id depends_on = [ - tencentcloud_kubernetes_cluster_attachment.test_attach + tencentcloud_kubernetes_node_pool.np_test ] } ` @@ -115,7 +138,7 @@ resource "tencentcloud_kubernetes_cluster_endpoint" "foo" { cluster_intranet = true cluster_intranet_subnet_id = data.tencentcloud_vpc_subnets.sub.instance_list.0.subnet_id depends_on = [ - tencentcloud_kubernetes_cluster_attachment.test_attach + tencentcloud_kubernetes_node_pool.np_test ] } `