From 348cdbb05f2cb6481a95a119531fd848d803c2b4 Mon Sep 17 00:00:00 2001 From: KGSN Date: Wed, 9 Feb 2022 16:22:55 +0800 Subject: [PATCH 1/3] fix: node_pool testcase - delete also remove instance --- tencentcloud/resource_tc_kubernetes_node_pool_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tencentcloud/resource_tc_kubernetes_node_pool_test.go b/tencentcloud/resource_tc_kubernetes_node_pool_test.go index 2f05f6d715..b9f624cc45 100644 --- a/tencentcloud/resource_tc_kubernetes_node_pool_test.go +++ b/tencentcloud/resource_tc_kubernetes_node_pool_test.go @@ -261,7 +261,7 @@ resource "tencentcloud_kubernetes_node_pool" "np_test" { desired_capacity = 2 enable_auto_scale = false node_os = "ubuntu18.04.1x86_64" - delete_keep_instance = true + delete_keep_instance = false scaling_group_name = "basic_group_test" default_cooldown = 350 termination_policies = ["NEWEST_INSTANCE"] From e5a315c10f2016e965039db15aaf1e8d062d6960 Mon Sep 17 00:00:00 2001 From: KGSN Date: Wed, 9 Feb 2022 16:49:17 +0800 Subject: [PATCH 2/3] fix: data.sec_group name --- tencentcloud/resource_tc_kubernetes_node_pool_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tencentcloud/resource_tc_kubernetes_node_pool_test.go b/tencentcloud/resource_tc_kubernetes_node_pool_test.go index b9f624cc45..a3fdee8ca4 100644 --- a/tencentcloud/resource_tc_kubernetes_node_pool_test.go +++ b/tencentcloud/resource_tc_kubernetes_node_pool_test.go @@ -214,7 +214,7 @@ resource "tencentcloud_kubernetes_node_pool" "np_test" { instance_type = var.default_instance_type system_disk_type = "CLOUD_PREMIUM" system_disk_size = "50" - security_group_ids = [data.tencentcloud_security_groups.group.security_groups[0].security_group_id] + security_group_ids = [data.tencentcloud_security_groups.sg.security_groups[0].security_group_id] data_disk { disk_type = "CLOUD_PREMIUM" From d82798df5132eda36547a9349dfa61302859a3a9 Mon Sep 17 00:00:00 2001 From: KGSN Date: Mon, 14 Feb 2022 21:31:26 +0800 Subject: [PATCH 3/3] feat: node pool charge type --- tencentcloud/extension_as.go | 1 + .../resource_tc_kubernetes_node_pool.go | 189 ++++++++++++++++-- .../resource_tc_kubernetes_node_pool_test.go | 17 +- .../docs/r/kubernetes_node_pool.html.markdown | 50 +++++ 4 files changed, 237 insertions(+), 20 deletions(-) diff --git a/tencentcloud/extension_as.go b/tencentcloud/extension_as.go index d97d3506cf..2e3e45d4db 100644 --- a/tencentcloud/extension_as.go +++ b/tencentcloud/extension_as.go @@ -37,6 +37,7 @@ var INTERNET_CHARGE_ALLOW_TYPE = []string{ const ( INSTANCE_CHARGE_TYPE_POSTPAID = "POSTPAID_BY_HOUR" INSTANCE_CHARGE_TYPE_SPOTPAID = "SPOTPAID" + INSTANCE_CHARGE_TYPE_PREPAID = "PREPAID" ) const ( diff --git a/tencentcloud/resource_tc_kubernetes_node_pool.go b/tencentcloud/resource_tc_kubernetes_node_pool.go index e22c704383..fc7cbf7058 100644 --- a/tencentcloud/resource_tc_kubernetes_node_pool.go +++ b/tencentcloud/resource_tc_kubernetes_node_pool.go @@ -92,6 +92,51 @@ resource "tencentcloud_kubernetes_node_pool" "mynodepool" { ] } } +``` + +Using Spot CVM Instance +```hcl +resource "tencentcloud_kubernetes_node_pool" "mynodepool" { + name = "mynodepool" + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + max_size = 6 + min_size = 1 + vpc_id = data.tencentcloud_vpc_subnets.vpc.instance_list.0.vpc_id + subnet_ids = [data.tencentcloud_vpc_subnets.vpc.instance_list.0.subnet_id] + retry_policy = "INCREMENTAL_INTERVALS" + desired_capacity = 4 + enable_auto_scale = true + multi_zone_subnet_policy = "EQUALITY" + + auto_scaling_config { + instance_type = var.default_instance_type + system_disk_type = "CLOUD_PREMIUM" + system_disk_size = "50" + security_group_ids = ["sg-24vswocp"] + instance_charge_type = "SPOTPAID" + spot_instance_type = "one-time" + spot_max_price = "1000" + + 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 + } + + labels = { + "test1" = "test1", + "test2" = "test2", + } + +} + ``` */ package tencentcloud @@ -193,6 +238,38 @@ func composedKubernetesAsScalingConfigPara() map[string]*schema.Schema { }, }, }, + // payment + "instance_charge_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Charge type of instance. Valid values are `PREPAID`, `POSTPAID_BY_HOUR`, `SPOTPAID`. The default is `POSTPAID_BY_HOUR`. NOTE: `SPOTPAID` instance must set `spot_instance_type` and `spot_max_price` at the same time.", + }, + "instance_charge_type_prepaid_period": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateAllowedIntValue(CVM_PREPAID_PERIOD), + Description: "The tenancy (in month) of the prepaid instance, NOTE: it only works when instance_charge_type is set to `PREPAID`. Valid values are `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `24`, `36`.", + }, + "instance_charge_type_prepaid_renew_flag": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAllowedStringValue(CVM_PREPAID_RENEW_FLAG), + Description: "Auto renewal flag. Valid values: `NOTIFY_AND_AUTO_RENEW`: notify upon expiration and renew automatically, `NOTIFY_AND_MANUAL_RENEW`: notify upon expiration but do not renew automatically, `DISABLE_NOTIFY_AND_MANUAL_RENEW`: neither notify upon expiration nor renew automatically. Default value: `NOTIFY_AND_MANUAL_RENEW`. If this parameter is specified as `NOTIFY_AND_AUTO_RENEW`, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to `PREPAID`.", + }, + "spot_instance_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateAllowedStringValue([]string{"one-time"}), + Description: "Type of spot instance, only support `one-time` now. Note: it only works when instance_charge_type is set to `SPOTPAID`.", + }, + "spot_max_price": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringNumber, + Description: "Max price of a spot instance, is the format of decimal string, for example \"0.50\". Note: it only works when instance_charge_type is set to `SPOTPAID`.", + }, "internet_charge_type": { Type: schema.TypeString, Optional: true, @@ -634,7 +711,32 @@ func composedKubernetesAsScalingConfigParaSerial(dMap map[string]interface{}, me } } - chargeType := INSTANCE_CHARGE_TYPE_POSTPAID + chargeType, ok := dMap["instance_charge_type"].(string) + if !ok || chargeType == "" { + chargeType = INSTANCE_CHARGE_TYPE_POSTPAID + } + + if chargeType == INSTANCE_CHARGE_TYPE_SPOTPAID { + spotMaxPrice := dMap["spot_max_price"].(string) + spotInstanceType := dMap["spot_instance_type"].(string) + request.InstanceMarketOptions = &as.InstanceMarketOptionsRequest{ + MarketType: helper.String("spot"), + SpotOptions: &as.SpotMarketOptions{ + MaxPrice: &spotMaxPrice, + SpotInstanceType: &spotInstanceType, + }, + } + } + + if chargeType == INSTANCE_CHARGE_TYPE_PREPAID { + period := dMap["instance_charge_type_prepaid_period"].(int) + renewFlag := dMap["instance_charge_type_prepaid_renew_flag"].(string) + request.InstanceChargePrepaid = &as.InstanceChargePrepaid{ + Period: helper.IntInt64(period), + RenewFlag: &renewFlag, + } + } + request.InstanceChargeType = &chargeType result = request.ToJsonString() @@ -703,6 +805,35 @@ func composeAsLaunchConfigModifyRequest(d *schema.ResourceData, launchConfigId s } } + chargeType, ok := dMap["instance_charge_type"].(string) + + if !ok || chargeType == "" { + chargeType = INSTANCE_CHARGE_TYPE_POSTPAID + } + + if chargeType == INSTANCE_CHARGE_TYPE_SPOTPAID { + spotMaxPrice := dMap["spot_max_price"].(string) + spotInstanceType := dMap["spot_instance_type"].(string) + request.InstanceMarketOptions = &as.InstanceMarketOptionsRequest{ + MarketType: helper.String("spot"), + SpotOptions: &as.SpotMarketOptions{ + MaxPrice: &spotMaxPrice, + SpotInstanceType: &spotInstanceType, + }, + } + } + + if chargeType == INSTANCE_CHARGE_TYPE_PREPAID { + period := dMap["instance_charge_type_prepaid_period"].(int) + renewFlag := dMap["instance_charge_type_prepaid_renew_flag"].(string) + request.InstanceChargePrepaid = &as.InstanceChargePrepaid{ + Period: helper.IntInt64(period), + RenewFlag: &renewFlag, + } + } + + request.InstanceChargeType = &chargeType + return request } @@ -745,19 +876,27 @@ func resourceKubernetesNodePoolRead(d *schema.ResourceData, meta interface{}) er _ = d.Set("cluster_id", clusterId) //Describe Node Pool - nodePool, has, err := service.DescribeNodePool(ctx, clusterId, nodePoolId) - if err != nil { - err = resource.Retry(readRetryTimeout, func() *resource.RetryError { - _, has, err = service.DescribeNodePool(ctx, clusterId, nodePoolId) - if err != nil { - return retryError(err) - } - return nil - }) - } + var ( + nodePool *tke.NodePool + ) + + err = resource.Retry(readRetryTimeout, func() *resource.RetryError { + nodePool, has, err = service.DescribeNodePool(ctx, clusterId, nodePoolId) + if err != nil { + return resource.NonRetryableError(err) + } + + status := *nodePool.AutoscalingGroupStatus + + if status == "enabling" || status == "disabling" { + return resource.RetryableError(fmt.Errorf("node pool status is %s, retrying", status)) + } - if err != nil { return nil + }) + + if err != nil { + return err } if !has { @@ -803,9 +942,12 @@ func resourceKubernetesNodePoolRead(d *schema.ResourceData, meta interface{}) er if hasLC > 0 { launchConfig := make(map[string]interface{}) if launchCfg.InstanceTypes != nil { - backupInsTypes := launchCfg.InstanceTypes - launchConfig["instance_type"] = backupInsTypes[0] - launchConfig["backup_instance_types"] = helper.StringsInterfaces(backupInsTypes[1:]) + insTypes := launchCfg.InstanceTypes + launchConfig["instance_type"] = insTypes[0] + backupInsTypes := insTypes[1:] + if len(backupInsTypes) > 0 { + launchConfig["backup_instance_types"] = helper.StringsInterfaces(backupInsTypes) + } } else { launchConfig["instance_type"] = launchCfg.InstanceType } @@ -827,6 +969,17 @@ func resourceKubernetesNodePoolRead(d *schema.ResourceData, meta interface{}) er if launchCfg.InternetAccessible.PublicIpAssigned != nil { launchConfig["public_ip_assigned"] = launchCfg.InternetAccessible.PublicIpAssigned } + if launchCfg.InstanceChargeType != nil { + launchConfig["instance_charge_type"] = launchCfg.InstanceChargeType + if *launchCfg.InstanceChargeType == INSTANCE_CHARGE_TYPE_SPOTPAID && launchCfg.InstanceMarketOptions != nil { + launchConfig["spot_instance_type"] = launchCfg.InstanceMarketOptions.SpotOptions.SpotInstanceType + launchConfig["spot_max_price"] = launchCfg.InstanceMarketOptions.SpotOptions.MaxPrice + } + if *launchCfg.InstanceChargeType == INSTANCE_CHARGE_TYPE_PREPAID && launchCfg.InstanceChargePrepaid != nil { + launchConfig["instance_charge_type_prepaid_period"] = launchCfg.InstanceChargePrepaid.Period + launchConfig["instance_charge_type_prepaid_renew_flag"] = launchCfg.InstanceChargePrepaid.RenewFlag + } + } if len(launchCfg.DataDisks) > 0 { dataDisks := make([]map[string]interface{}, 0, len(launchCfg.DataDisks)) for i := range launchCfg.DataDisks { @@ -906,7 +1059,11 @@ func resourceKubernetesNodePoolRead(d *schema.ResourceData, meta interface{}) er _ = d.Set("vpc_id", asg.VpcId) _ = d.Set("retry_policy", asg.RetryPolicy) _ = d.Set("subnet_ids", helper.StringsInterfaces(asg.SubnetIdSet)) - _ = d.Set("multi_zone_subnet_policy", asg.MultiZoneSubnetPolicy) + + // If not check, the diff between computed and default empty value leads to force replacement + if _, ok := d.GetOk("multi_zone_subnet_policy"); ok { + _ = d.Set("multi_zone_subnet_policy", asg.MultiZoneSubnetPolicy) + } } taints := make([]map[string]interface{}, len(nodePool.Taints)) diff --git a/tencentcloud/resource_tc_kubernetes_node_pool_test.go b/tencentcloud/resource_tc_kubernetes_node_pool_test.go index a3fdee8ca4..b70b6ea5bd 100644 --- a/tencentcloud/resource_tc_kubernetes_node_pool_test.go +++ b/tencentcloud/resource_tc_kubernetes_node_pool_test.go @@ -54,13 +54,16 @@ func TestAccTencentCloudTkeNodePoolResource(t *testing.T) { resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.system_disk_size", "100"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.data_disk.#", "2"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.internet_max_bandwidth_out", "20"), + resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.instance_charge_type", "SPOTPAID"), + resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.spot_instance_type", "one-time"), + resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "auto_scaling_config.0.spot_max_price", "1000"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "max_size", "5"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "min_size", "2"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "labels.test3", "test3"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "desired_capacity", "2"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "name", "mynodepoolupdate"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "node_os", "ubuntu18.04.1x86_64"), - resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "unschedulable", "1"), + resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "unschedulable", "0"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "scaling_group_name", "basic_group_test"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "default_cooldown", "350"), resource.TestCheckResourceAttr(testTkeClusterNodePoolResourceKey, "termination_policies.#", "1"), @@ -155,7 +158,9 @@ data "tencentcloud_vpc_subnets" "vpc" { availability_zone = var.availability_zone } -data "tencentcloud_security_groups" "sg" {} +data "tencentcloud_security_groups" "sg" { + name = "test_preset_sg" +} variable "default_instance_type" { default = "S1.SMALL1" @@ -169,6 +174,7 @@ resource "tencentcloud_kubernetes_cluster" "managed_cluster" { cluster_desc = "test cluster desc" cluster_max_service_num = 32 cluster_version = "1.18.4" + cluster_os = "tlinux2.2(tkernel3)x86_64" worker_config { count = 1 @@ -270,7 +276,10 @@ resource "tencentcloud_kubernetes_node_pool" "np_test" { instance_type = var.default_instance_type system_disk_type = "CLOUD_PREMIUM" system_disk_size = "100" - security_group_ids = [data.tencentcloud_security_groups.group.security_groups[0].security_group_id] + security_group_ids = [data.tencentcloud_security_groups.sg.security_groups[0].security_group_id] + instance_charge_type = "SPOTPAID" + spot_instance_type = "one-time" + spot_max_price = "1000" data_disk { disk_type = "CLOUD_PREMIUM" @@ -289,7 +298,7 @@ resource "tencentcloud_kubernetes_node_pool" "np_test" { enhanced_monitor_service = false } - unschedulable = 1 + unschedulable = 0 labels = { "test3" = "test3", "test2" = "test2", diff --git a/website/docs/r/kubernetes_node_pool.html.markdown b/website/docs/r/kubernetes_node_pool.html.markdown index 88eec300ac..053c13c35b 100644 --- a/website/docs/r/kubernetes_node_pool.html.markdown +++ b/website/docs/r/kubernetes_node_pool.html.markdown @@ -103,6 +103,51 @@ resource "tencentcloud_kubernetes_node_pool" "mynodepool" { } ``` +Using Spot CVM Instance + +```hcl +resource "tencentcloud_kubernetes_node_pool" "mynodepool" { + name = "mynodepool" + cluster_id = tencentcloud_kubernetes_cluster.managed_cluster.id + max_size = 6 + min_size = 1 + vpc_id = data.tencentcloud_vpc_subnets.vpc.instance_list.0.vpc_id + subnet_ids = [data.tencentcloud_vpc_subnets.vpc.instance_list.0.subnet_id] + retry_policy = "INCREMENTAL_INTERVALS" + desired_capacity = 4 + enable_auto_scale = true + multi_zone_subnet_policy = "EQUALITY" + + auto_scaling_config { + instance_type = var.default_instance_type + system_disk_type = "CLOUD_PREMIUM" + system_disk_size = "50" + security_group_ids = ["sg-24vswocp"] + instance_charge_type = "SPOTPAID" + spot_instance_type = "one-time" + spot_max_price = "1000" + + 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 + } + + labels = { + "test1" = "test1", + "test2" = "test2", + } + +} +``` + ## Argument Reference The following arguments are supported: @@ -140,12 +185,17 @@ The `auto_scaling_config` object supports the following: * `data_disk` - (Optional) Configurations of data disk. * `enhanced_monitor_service` - (Optional, ForceNew) To specify whether to enable cloud monitor service. Default is TRUE. * `enhanced_security_service` - (Optional, ForceNew) To specify whether to enable cloud security service. Default is TRUE. +* `instance_charge_type_prepaid_period` - (Optional) The tenancy (in month) of the prepaid instance, NOTE: it only works when instance_charge_type is set to `PREPAID`. Valid values are `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `24`, `36`. +* `instance_charge_type_prepaid_renew_flag` - (Optional) Auto renewal flag. Valid values: `NOTIFY_AND_AUTO_RENEW`: notify upon expiration and renew automatically, `NOTIFY_AND_MANUAL_RENEW`: notify upon expiration but do not renew automatically, `DISABLE_NOTIFY_AND_MANUAL_RENEW`: neither notify upon expiration nor renew automatically. Default value: `NOTIFY_AND_MANUAL_RENEW`. If this parameter is specified as `NOTIFY_AND_AUTO_RENEW`, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to `PREPAID`. +* `instance_charge_type` - (Optional) Charge type of instance. Valid values are `PREPAID`, `POSTPAID_BY_HOUR`, `SPOTPAID`. The default is `POSTPAID_BY_HOUR`. NOTE: `SPOTPAID` instance must set `spot_instance_type` and `spot_max_price` at the same time. * `internet_charge_type` - (Optional) Charge types for network traffic. Valid value: `BANDWIDTH_PREPAID`, `TRAFFIC_POSTPAID_BY_HOUR`, `TRAFFIC_POSTPAID_BY_HOUR` and `BANDWIDTH_PACKAGE`. * `internet_max_bandwidth_out` - (Optional) Max bandwidth of Internet access in Mbps. Default is `0`. * `key_ids` - (Optional, ForceNew) ID list of keys. * `password` - (Optional, ForceNew) Password to access. * `public_ip_assigned` - (Optional) Specify whether to assign an Internet IP address. * `security_group_ids` - (Optional) Security groups to which a CVM instance belongs. +* `spot_instance_type` - (Optional) Type of spot instance, only support `one-time` now. Note: it only works when instance_charge_type is set to `SPOTPAID`. +* `spot_max_price` - (Optional) Max price of a spot instance, is the format of decimal string, for example "0.50". Note: it only works when instance_charge_type is set to `SPOTPAID`. * `system_disk_size` - (Optional) Volume of system disk in GB. Default is `50`. * `system_disk_type` - (Optional) Type of a CVM disk. Valid value: `CLOUD_PREMIUM` and `CLOUD_SSD`. Default is `CLOUD_PREMIUM`.