alibabacloudstack/resource_apsarastack_cs_kubernetes_node_pool.go (1,165 lines of code) (raw):

package alibabacloudstack import ( "encoding/base64" "encoding/json" "fmt" "log" "regexp" "time" "github.com/alibabacloud-go/tea/tea" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" roacs "github.com/alibabacloud-go/cs-20151215/v5/client" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/connectivity" "github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/errmsgs" "github.com/denverdino/aliyungo/cs" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ResourceName = "resource_alibabacloudstack_cs_kubernetes_permissions" func resourceAlibabacloudStackCSKubernetesNodePool() *schema.Resource { resource := &schema.Resource{ Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(90 * time.Minute), Update: schema.DefaultTimeout(60 * time.Minute), Delete: schema.DefaultTimeout(60 * time.Minute), }, Schema: map[string]*schema.Schema{ "cluster_id": { Type: schema.TypeString, Required: true, }, "name": { Type: schema.TypeString, Required: true, }, "node_count": { Type: schema.TypeInt, Optional: true, Computed: true, ConflictsWith: []string{"instances"}, }, "vswitch_ids": { Type: schema.TypeList, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, }, MinItems: 1, }, "instance_types": { Type: schema.TypeList, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, }, MinItems: 1, MaxItems: 10, }, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, ConflictsWith: []string{"key_name", "kms_encrypted_password"}, }, "key_name": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"password", "kms_encrypted_password"}, }, "kms_encrypted_password": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"password", "key_name"}, }, "security_group_id": { Type: schema.TypeString, Optional: true, Computed: true, }, "system_disk_category": { Type: schema.TypeString, Optional: true, Default: DiskCloudEfficiency, }, "system_disk_size": { Type: schema.TypeInt, Optional: true, Default: 40, ValidateFunc: validation.IntBetween(20, 32768), }, "system_disk_performance_level": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"PL0", "PL1", "PL2", "PL3"}, false), DiffSuppressFunc: csNodepoolDiskPerformanceLevelDiffSuppressFunc, }, "platform": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{"AliyunLinux", "Windows", "CentOS", "WindowsCore"}, false), }, "image_id": { Type: schema.TypeString, Optional: true, Computed: true, }, "instance_charge_type": { Type: schema.TypeString, Optional: true, ForceNew: true, Default: PostPaid, ValidateFunc: validation.StringInSlice([]string{string(PrePaid), string(PostPaid)}, false), }, "period": { Type: schema.TypeInt, Optional: true, Default: 1, ValidateFunc: validation.IntInSlice([]int{1, 2, 3, 6, 12, 24, 36, 48, 60}), DiffSuppressFunc: csNodepoolInstancePostPaidDiffSuppressFunc, }, "period_unit": { Type: schema.TypeString, Optional: true, Default: Month, ValidateFunc: validation.StringInSlice([]string{"Month"}, false), DiffSuppressFunc: csNodepoolInstancePostPaidDiffSuppressFunc, }, "auto_renew": { Type: schema.TypeBool, Default: false, Optional: true, DiffSuppressFunc: csNodepoolInstancePostPaidDiffSuppressFunc, }, "auto_renew_period": { Type: schema.TypeInt, Optional: true, Default: 1, ValidateFunc: validation.IntInSlice([]int{1, 2, 3, 6, 12}), DiffSuppressFunc: csNodepoolInstancePostPaidDiffSuppressFunc, }, "install_cloud_monitor": { Type: schema.TypeBool, Optional: true, Default: true, }, "unschedulable": { Type: schema.TypeBool, Optional: true, Default: false, }, "data_disks": { Optional: true, Type: schema.TypeList, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "size": { Type: schema.TypeInt, Optional: true, }, "category": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"all", "cloud", "ephemeral_ssd", "cloud_essd", "cloud_efficiency", "cloud_ssd", "local_disk", "cloud_pperf", "cloud_sperf"}, false), }, "encrypted": { Type: schema.TypeString, Optional: true, }, }, }, }, "tags": { Type: schema.TypeMap, Optional: true, }, "labels": { Optional: true, Type: schema.TypeList, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "key": { Type: schema.TypeString, Required: true, }, "value": { Type: schema.TypeString, Optional: true, }, }, }, }, "taints": { Optional: true, Type: schema.TypeList, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "key": { Type: schema.TypeString, Required: true, }, "value": { Type: schema.TypeString, Optional: true, }, "effect": { Type: schema.TypeString, Optional: true, }, }, }, }, "node_name_mode": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^customized,[a-z0-9]([-a-z0-9\.])*,([5-9]|[1][0-2]),([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`), "Each node name consists of a prefix, an IP substring, and a suffix. For example, if the node IP address is 192.168.0.55, the prefix is aliyun.com, IP substring length is 5, and the suffix is test, the node name will be aliyun.com00055test."), }, "user_data": { Type: schema.TypeString, Optional: true, }, "scaling_group_id": { Type: schema.TypeString, Computed: true, }, "scaling_config": { Type: schema.TypeList, Optional: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "min_size": { Type: schema.TypeInt, Required: true, ValidateFunc: validation.IntBetween(0, 1000), }, "max_size": { Type: schema.TypeInt, Required: true, ValidateFunc: validation.IntBetween(0, 1000), }, "type": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"cpu", "gpu", "gpushare", "spot"}, false), }, "is_bond_eip": { Type: schema.TypeBool, Optional: true, ConflictsWith: []string{"internet_charge_type"}, }, "eip_internet_charge_type": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"PayByBandwidth", "PayByTraffic"}, false), ConflictsWith: []string{"internet_charge_type"}, }, "eip_bandwidth": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(1, 500), ConflictsWith: []string{"internet_charge_type"}, }, }, }, ConflictsWith: []string{"instances"}, }, "scaling_policy": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{"release", "recycle"}, false), DiffSuppressFunc: csNodepoolScalingPolicyDiffSuppressFunc, }, "resource_group_id": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "internet_charge_type": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{"PayByTraffic", "PayByBandwidth"}, false), }, "internet_max_bandwidth_out": { Type: schema.TypeInt, Optional: true, Computed: true, }, "spot_strategy": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{"SpotWithPriceLimit"}, false), }, "spot_price_limit": { Type: schema.TypeList, Optional: true, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "instance_type": { Type: schema.TypeString, Optional: true, }, "price_limit": { Type: schema.TypeString, Optional: true, }, }, }, DiffSuppressFunc: csNodepoolSpotInstanceSettingDiffSuppressFunc, }, "instances": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 100, ConflictsWith: []string{"node_count", "scaling_config"}, }, "keep_instance_name": { Type: schema.TypeBool, Optional: true, Computed: true, }, "format_disk": { Type: schema.TypeBool, Optional: true, Computed: true, }, }, } setResourceFunc(resource, resourceAlibabacloudStackCSKubernetesNodePoolCreate, resourceAlibabacloudStackCSNodePoolRead, resourceAlibabacloudStackCSKubernetesNodePoolUpdate, resourceAlibabacloudStackCSNodePoolDelete) return resource } type NodePoolCommonResponse struct { Response NodePoolID string `json:"nodepool_id,omitempty"` TaskId string `json:"task_id,omitempty"` } type nodePoolDataDisk struct { Category string `json:"category"` Encrypted string `json:"encrypted"` // true|false Size int `json:"size"` } type autoScaling struct { Enable bool `json:"enable"` MaxInstances int64 `json:"max_instances"` MinInstances int64 `json:"min_instances"` Type string `json:"type"` } type kubernetesConfig struct { Taints []cs.Taint `json:"taints"` Labels []cs.Label `json:"labels"` UserData string `json:"user_data"` Runtime string `json:"runtime,omitempty"` RuntimeVersion string `json:"runtime_version"` CmsEnabled bool `json:"cms_enabled"` Unschedulable bool `json:"unschedulable"` } type tEEConfig struct { TEEEnable bool `json:"tee_enable"` } type CreateClusterNodePoolRequest struct { Count int64 `json:"count"` NodePoolInfo NodePoolInfo `json:"node_pool_info"` ScalingGroup scalingGroup `json:"scaling_group"` KubernetesConfig kubernetesConfig `json:"kubernetes_config"` AutoScaling autoScaling `json:"auto_scaling"` } type NodePoolInfo struct { NodePoolId string `json:"nodepool_id"` RegionId string `json:"region_id"` Name string `json:"name"` Created time.Time `json:"created"` Updated time.Time `json:"updated"` IsDefault bool `json:"is_default"` NodePoolType string `json:"type"` ResourceGroupId string `json:"resource_group_id"` } type scalingGroup struct { VswitchIds []string `json:"vswitch_ids"` InstanceTypes []string `json:"instance_types"` LoginPassword string `json:"login_password"` SystemDiskCategory string `json:"system_disk_category"` SystemDiskSize int64 `json:"system_disk_size"` DataDisks []nodePoolDataDisk `json:"data_disks"` //支持多个数据盘 Tags []cs.Tag `json:"tags"` ImageId string `json:"image_id"` Platform string `json:"platform"` // 支持包年包月 InstanceChargeType string `json:"instance_charge_type"` ScalingPolicy string `json:"scaling_policy"` // 公网ip InternetChargeType string `json:"internet_charge_type"` InternetMaxBandwidthOut int `json:"internet_max_bandwidth_out"` } type CreateNodePoolRequest struct { ClusterID string `json:"ClusterId"` NodepoolID string `json:"NodepoolId"` UpdateNodes bool `json:"update_nodes"` ScalingGroup scalingGroup `json:"scaling_group"` KubernetesConfig kubernetesConfig `json:"kubernetes_config"` TEEConfig tEEConfig `json:"tee_config"` AutoScaling autoScaling `json:"auto_scaling"` } func resourceAlibabacloudStackCSKubernetesNodePoolCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AlibabacloudStackClient) csService := CsService{client} // prepare args and set default value request, err := buildNodePoolArgs(d, meta) if err != nil { return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, "alibabacloudstack_cs_kubernetes_node_pool", "PrepareKubernetesNodePoolArgs", err) } request.Headers["x-acs-asapi-gateway-version"] = "3.0" response, err := client.ProcessCommonRequest(request) if err != nil { if response == nil { return errmsgs.WrapErrorf(err, "Process Common Request Failed") } errmsg := errmsgs.GetBaseResponseErrorMessage(response.BaseResponse) return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_cs_kubernetes_node_pool", "CreateClusterNodePool", response, errmsg) } nodepoolresponse := NodePoolCommonResponse{} if err := json.Unmarshal(response.GetHttpContentBytes(), &nodepoolresponse); err != nil { return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, "alibabacloudstack_cs_kubernetes_node_pool", "NodePoolCommonResponse", response) } d.SetId(nodepoolresponse.NodePoolID) // reset interval to 10s stateConf := BuildStateConf([]string{"initial", "scaling"}, []string{"active"}, d.Timeout(schema.TimeoutCreate), 30*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), d.Get("cluster_id").(string), []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, "ResourceID:%s , TaskID:%s ", d.Id(), nodepoolresponse.TaskId) } // attach existing node if v, ok := d.GetOk("instances"); ok && v != nil { attachExistingInstance(d, meta) } return nil } func resourceAlibabacloudStackCSKubernetesNodePoolUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AlibabacloudStackClient) csService := CsService{client} clusterId := d.Get("cluster_id").(string) d.Partial(true) update := false args := &CreateNodePoolRequest{ ClusterID: clusterId, NodepoolID: d.Id(), UpdateNodes: true, ScalingGroup: scalingGroup{}, KubernetesConfig: kubernetesConfig{}, TEEConfig: tEEConfig{}, AutoScaling: autoScaling{}, } if d.HasChange("node_count") { oldV, newV := d.GetChange("node_count") oldValue, ok := oldV.(int) if !ok { return errmsgs.WrapErrorf(fmt.Errorf("node_count old value can not be parsed"), "parseError %d", oldValue) } newValue, ok := newV.(int) if !ok { return errmsgs.WrapErrorf(fmt.Errorf("node_count new value can not be parsed"), "parseError %d", newValue) } log.Printf("UUUUUUUUUUUUUUUUUUUUUUUUUUUU %d , %d", newValue, oldValue) if newValue < oldValue { err := RemoveNodePoolNodes(d, meta, clusterId, d.Id(), nil, nil) if err != nil { return err } // The removal of a node is logically independent. // The removal of a node should not involve parameter changes. return nil } //update = true if newValue > oldValue { err := ScaleClusterNodePool(d, meta, clusterId, d.Id(), oldValue, newValue) if err != nil { return err } // The removal of a node is logically independent. // The removal of a node should not involve parameter changes. return nil } } if d.HasChange("vswitch_ids") { update = true args.ScalingGroup.VswitchIds = expandStringList(d.Get("vswitch_ids").([]interface{})) } if d.HasChange("install_cloud_monitor") { update = true args.KubernetesConfig.CmsEnabled = d.Get("install_cloud_monitor").(bool) } if d.HasChange("unschedulable") { update = true args.KubernetesConfig.Unschedulable = d.Get("unschedulable").(bool) } if d.HasChange("instance_types") { update = true args.ScalingGroup.InstanceTypes = expandStringList(d.Get("instance_types").([]interface{})) } // password is required by update method args.ScalingGroup.LoginPassword = d.Get("password").(string) if d.HasChange("password") { update = true args.ScalingGroup.LoginPassword = d.Get("password").(string) } if d.HasChange("system_disk_category") { update = true args.ScalingGroup.SystemDiskCategory = d.Get("system_disk_category").(string) } if d.HasChange("system_disk_size") { update = true args.ScalingGroup.SystemDiskSize = int64(d.Get("system_disk_size").(int)) } if d.HasChange("image_id") { update = true args.ScalingGroup.ImageId = d.Get("image_id").(string) } if d.HasChange("data_disks") { update = true setNodePoolDataDisks(&args.ScalingGroup, d) } if d.HasChange("tags") { update = true setNodePoolTags(&args.ScalingGroup, d) } if d.HasChange("labels") { update = true setNodePoolLabels(&args.KubernetesConfig, d) } if d.HasChange("taints") { update = true setNodePoolTaints(&args.KubernetesConfig, d) } if d.HasChange("user_data") { update = true if v := d.Get("user_data").(string); v != "" { _, base64DecodeError := base64.StdEncoding.DecodeString(v) if base64DecodeError == nil { args.KubernetesConfig.UserData = v } else { args.KubernetesConfig.UserData = base64.StdEncoding.EncodeToString([]byte(v)) } } } if d.HasChange("scaling_config") { update = true if v, ok := d.GetOk("scaling_config"); ok { args.AutoScaling = setAutoScalingConfig(v.([]interface{})) } } if v, ok := d.GetOk("internet_charge_type"); ok { update = true args.ScalingGroup.InternetChargeType = v.(string) } if v, ok := d.GetOk("internet_max_bandwidth_out"); ok { update = true args.ScalingGroup.InternetMaxBandwidthOut = v.(int) } if v, ok := d.GetOk("platform"); ok { update = true args.ScalingGroup.Platform = v.(string) } if d.HasChange("scaling_policy") { update = true args.ScalingGroup.ScalingPolicy = d.Get("scaling_policy").(string) } if update { //begin request := client.NewCommonRequest("POST", "CS", "2015-12-15", "ModifyClusterNodePool", fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, d.Id())) request.QueryParams["ClusterId"] = clusterId request.QueryParams["SignatureVersion"] = "1.0" request.Headers["x-acs-asapi-gateway-version"] = "3.0" jsonData, err := json.Marshal(args) if err != nil { return errmsgs.WrapError(fmt.Errorf("Error marshaling to JSON: %v", err)) } request.SetContentType(requests.Json) request.SetContent(jsonData) response, err := client.ProcessCommonRequest(request) if err != nil { if response == nil { return errmsgs.WrapErrorf(err, "Process Common Request Failed") } errmsg := errmsgs.GetBaseResponseErrorMessage(response.BaseResponse) return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), "UpdateKubernetesNodePool", response, errmsg) } stateConf := BuildStateConf([]string{"scaling", "updating"}, []string{"active"}, d.Timeout(schema.TimeoutUpdate), 30*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), clusterId, []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id()) } } // attach or remove existing node if d.HasChange("instances") { rawOldValue, rawNewValue := d.GetChange("instances") oldValue, ok := rawOldValue.([]interface{}) if !ok { return errmsgs.WrapErrorf(fmt.Errorf("instances old value can not be parsed"), "parseError %d", oldValue) } newValue, ok := rawNewValue.([]interface{}) if !ok { return errmsgs.WrapErrorf(fmt.Errorf("instances new value can not be parsed"), "parseError %d", oldValue) } if len(newValue) > len(oldValue) { attachExistingInstance(d, meta) } else { err := RemoveNodePoolNodes(d, meta, clusterId, d.Id(), oldValue, newValue) if err != nil { return err } } } update = false d.Partial(false) return nil } func resourceAlibabacloudStackCSNodePoolRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AlibabacloudStackClient) clusterId := d.Get("cluster_id").(string) csService := CsService{client} object, err := csService.DescribeCsKubernetesNodePool(d.Id(), clusterId) if err != nil { if errmsgs.NotFoundError(err) { d.SetId("") return nil } return errmsgs.WrapError(err) } d.Set("node_count", object.Status.TotalNodes) d.Set("name", object.NodepoolInfo.Name) //d.Set("vpc_id", object.VpcId) d.Set("vswitch_ids", object.ScalingGroup.VswitchIds) d.Set("instance_types", object.ScalingGroup.InstanceTypes) d.Set("key_name", object.ScalingGroup.KeyPair) d.Set("security_group_id", object.ScalingGroup.SecurityGroupID) d.Set("system_disk_category", object.ScalingGroup.SystemDiskCategory) d.Set("system_disk_size", object.ScalingGroup.SystemDiskSize) d.Set("image_id", object.ScalingGroup.ImageID) d.Set("platform", object.ScalingGroup.Platform) d.Set("scaling_policy", object.ScalingGroup.ScalingPolicy) d.Set("node_name_mode", object.KubernetesConfig.NodeNameMode) d.Set("user_data", object.KubernetesConfig.UserData) d.Set("scaling_group_id", object.ScalingGroup.ScalingGroupID) d.Set("unschedulable", object.KubernetesConfig.Unschedulable) d.Set("instance_charge_type", object.ScalingGroup.InstanceChargeType) d.Set("resource_group_id", object.NodepoolInfo.ResourceGroupID) d.Set("spot_strategy", object.ScalingGroup.SpotStrategy) //d.Set("internet_charge_type", object.ScalingGroup.InternetChargeType) //d.Set("internet_max_bandwidth_out", object.ScalingGroup.InternetMaxBandwidthOut) d.Set("install_cloud_monitor", object.KubernetesConfig.CmsEnabled) if object.ScalingGroup.InstanceChargeType == "PrePaid" { d.Set("period", object.ScalingGroup.Period) d.Set("period_unit", object.ScalingGroup.PeriodUnit) d.Set("auto_renew", object.ScalingGroup.AutoRenew) d.Set("auto_renew_period", object.ScalingGroup.AutoRenewPeriod) } if passwd, ok := d.GetOk("password"); ok && passwd.(string) != "" { d.Set("password", passwd) } // if parts, err := ParseResourceId(d.Id(), 2); err != nil { // return errmsgs.WrapError(err) // } else { // d.Set("cluster_id", string(parts[0])) // } if err := d.Set("data_disks", flattenNodeDataDisksConfig(object.ScalingGroup.DataDisks)); err != nil { return errmsgs.WrapError(err) } if err := d.Set("taints", flattenTaintsConfig(object.KubernetesConfig.Taints)); err != nil { return errmsgs.WrapError(err) } if err := d.Set("labels", flattenLabelsConfig(object.KubernetesConfig.Labels)); err != nil { return errmsgs.WrapError(err) } if err := d.Set("tags", flattenTagsConfig(object.ScalingGroup.Tags)); err != nil { return errmsgs.WrapError(err) } // if object.Management.Enable == true { // if err := d.Set("management", flattenManagementNodepoolConfig(&object.Management)); err != nil { // return errmsgs.WrapError(err) // } // } if object.AutoScaling.Enable == true { if err := d.Set("scaling_config", flattenAutoScalingConfig(&object.AutoScaling)); err != nil { return errmsgs.WrapError(err) } } if err := d.Set("spot_price_limit", flattenSpotPriceLimit(object.ScalingGroup.SpotPriceLimit)); err != nil { return errmsgs.WrapError(err) } return nil } func resourceAlibabacloudStackCSNodePoolDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AlibabacloudStackClient) csService := CsService{client} clusterId := d.Get("cluster_id").(string) var raw interface{} // delete all nodes err := RemoveNodePoolNodes(d, meta, clusterId, d.Id(), nil, nil) if err != nil { return err } req := client.NewCommonRequest("DELETE", "CS", "2015-12-15", "DeleteClusterNodepool", fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, d.Id())) req.QueryParams["ClusterId"] = clusterId req.QueryParams["NodepoolId"] = d.Id() req.Headers["x-acs-asapi-gateway-version"] = "3.0" response, err := client.ProcessCommonRequest(req) if err != nil { if response == nil { return errmsgs.WrapErrorf(err, "Process Common Request Failed") } errmsg := errmsgs.GetBaseResponseErrorMessage(response.BaseResponse) return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), "DeleteClusterNodePool", raw, errmsg) } stateConf := BuildStateConf([]string{"deleting"}, []string{}, d.Timeout(schema.TimeoutUpdate), 30*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), clusterId, []string{"failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id()) } return nil } func buildNodePoolArgs(d *schema.ResourceData, meta interface{}) (*requests.CommonRequest, error) { client := meta.(*connectivity.AlibabacloudStackClient) password := d.Get("password").(string) if password == "" { if v := d.Get("kms_encrypted_password").(string); v != "" { kmsService := KmsService{client} decryptResp, err := kmsService.Decrypt2(v, d.Get("kms_encryption_context").(map[string]interface{})) if err != nil { return nil, errmsgs.WrapError(err) } password = decryptResp } else if v := d.Get("key_name").(string); v == "" { return nil, errmsgs.WrapError(fmt.Errorf("password is require while kms_encrypted_password or key_name not set")) } } request := client.NewCommonRequest("POST", "CS", "2015-12-15", "CreateClusterNodePool", fmt.Sprintf("/clusters/%s/nodepools", d.Get("cluster_id").(string))) NodePoolInfo := NodePoolInfo{ Name: d.Get("name").(string), NodePoolType: "ess", // hard code the type } ScalingGroup := scalingGroup{ VswitchIds: expandStringList(d.Get("vswitch_ids").([]interface{})), InstanceTypes: expandStringList(d.Get("instance_types").([]interface{})), LoginPassword: password, SystemDiskCategory: d.Get("system_disk_category").(string), SystemDiskSize: int64(d.Get("system_disk_size").(int)), ImageId: d.Get("image_id").(string), Platform: d.Get("platform").(string), } KubernetesConfig := kubernetesConfig{} AutoScaling := autoScaling{} setNodePoolDataDisks(&ScalingGroup, d) setNodePoolTags(&ScalingGroup, d) setNodePoolTaints(&KubernetesConfig, d) setNodePoolLabels(&KubernetesConfig, d) if v, ok := d.GetOk("instance_charge_type"); ok { ScalingGroup.InstanceChargeType = v.(string) } if v, ok := d.GetOk("password"); ok { ScalingGroup.LoginPassword = v.(string) } if v, ok := d.GetOk("install_cloud_monitor"); ok { KubernetesConfig.CmsEnabled = v.(bool) } if v, ok := d.GetOk("unschedulable"); ok { KubernetesConfig.Unschedulable = v.(bool) } if v, ok := d.GetOk("user_data"); ok && v != "" { _, base64DecodeError := base64.StdEncoding.DecodeString(v.(string)) if base64DecodeError == nil { KubernetesConfig.UserData = v.(string) } else { KubernetesConfig.UserData = base64.StdEncoding.EncodeToString([]byte(v.(string))) } } // set auto scaling config if v, ok := d.GetOk("scaling_policy"); ok { ScalingGroup.ScalingPolicy = v.(string) } if v, ok := d.GetOk("scaling_config"); ok { if sc, ok := v.([]interface{}); len(sc) > 0 && ok { AutoScaling = setAutoScalingConfig(sc) } } // set manage nodepool params // if v, ok := d.GetOk("resource_group_id"); ok { // ScalingGroup.ResourceGroupId = v.(string) // } // setting spot instance if v, ok := d.GetOk("internet_charge_type"); ok { ScalingGroup.InternetChargeType = v.(string) } if v, ok := d.GetOk("internet_max_bandwidth_out"); ok { ScalingGroup.InternetMaxBandwidthOut = v.(int) } request.QueryParams["ClusterId"] = d.Get("cluster_id").(string) request.QueryParams["Password"] = d.Get("password").(string) request.QueryParams["SignatureVersion"] = "1.0" body := CreateClusterNodePoolRequest{ Count: int64(d.Get("node_count").(int)), NodePoolInfo: NodePoolInfo, ScalingGroup: ScalingGroup, KubernetesConfig: KubernetesConfig, AutoScaling: AutoScaling, } jsonData, err := json.Marshal(body) if err != nil { return nil, errmsgs.WrapError(fmt.Errorf("Error marshaling to JSON: %v", err)) } request.SetContentType(requests.Json) request.SetContent(jsonData) return request, nil } func ConvertCsTags(d *schema.ResourceData) ([]cs.Tag, error) { tags := make([]cs.Tag, 0) tagsMap, ok := d.Get("tags").(map[string]interface{}) if ok { for key, value := range tagsMap { if value != nil { if v, ok := value.(string); ok { tags = append(tags, cs.Tag{ Key: key, Value: v, }) } } } } return tags, nil } func setNodePoolTags(scalingGroup *scalingGroup, d *schema.ResourceData) error { if _, ok := d.GetOk("tags"); ok { if tags, err := ConvertCsTags(d); err == nil { scalingGroup.Tags = tags } } return nil } func setNodePoolLabels(config *kubernetesConfig, d *schema.ResourceData) error { if v, ok := d.GetOk("labels"); ok && len(v.([]interface{})) > 0 { vl := v.([]interface{}) labels := make([]cs.Label, 0) for _, i := range vl { if m, ok := i.(map[string]interface{}); ok { labels = append(labels, cs.Label{ Key: m["key"].(string), Value: m["value"].(string), }) } } config.Labels = labels } return nil } func setNodePoolDataDisks(scalingGroup *scalingGroup, d *schema.ResourceData) error { if dds, ok := d.GetOk("data_disks"); ok { disks := dds.([]interface{}) createDataDisks := make([]nodePoolDataDisk, 0, len(disks)) for _, e := range disks { pack := e.(map[string]interface{}) dataDisk := nodePoolDataDisk{ Size: pack["size"].(int), Category: pack["category"].(string), Encrypted: pack["encrypted"].(string), } createDataDisks = append(createDataDisks, dataDisk) } scalingGroup.DataDisks = createDataDisks } return nil } func setNodePoolTaints(config *kubernetesConfig, d *schema.ResourceData) error { if v, ok := d.GetOk("taints"); ok && len(v.([]interface{})) > 0 { vl := v.([]interface{}) taints := make([]cs.Taint, 0) for _, i := range vl { if m, ok := i.(map[string]interface{}); ok { taints = append(taints, cs.Taint{ Key: m["key"].(string), Value: m["value"].(string), Effect: cs.Effect(m["effect"].(string)), }) } } config.Taints = taints } return nil } func setManagedNodepoolConfig(l []interface{}) (config cs.Management) { if len(l) == 0 || l[0] == nil { return config } m := l[0].(map[string]interface{}) // Once "management" is set, we think of it as creating a managed node pool config.Enable = true if v, ok := m["auto_repair"].(bool); ok { config.AutoRepair = v } if v, ok := m["auto_upgrade"].(bool); ok { config.UpgradeConf.AutoUpgrade = v } if v, ok := m["surge"].(int); ok { config.UpgradeConf.Surge = int64(v) } if v, ok := m["surge_percentage"].(int); ok { config.UpgradeConf.SurgePercentage = int64(v) } if v, ok := m["max_unavailable"].(int); ok { config.UpgradeConf.MaxUnavailable = int64(v) } return config } func setAutoScalingConfig(l []interface{}) (config autoScaling) { if len(l) == 0 || l[0] == nil { return config } m := l[0].(map[string]interface{}) // Once "scaling_config" is set, we think of it as creating a auto scaling node pool config.Enable = true if v, ok := m["min_size"].(int); ok { config.MinInstances = int64(v) } if v, ok := m["max_size"].(int); ok { config.MaxInstances = int64(v) } if v, ok := m["type"].(string); ok { config.Type = v } return config } func setSpotPriceLimit(l []interface{}) (config []cs.SpotPrice) { if len(l) == 0 || l[0] == nil { return config } for _, v := range l { if m, ok := v.(map[string]interface{}); ok { config = append(config, cs.SpotPrice{ InstanceType: m["instance_type"].(string), PriceLimit: m["price_limit"].(string), }) } } return } func flattenSpotPriceLimit(config []cs.SpotPrice) (m []map[string]interface{}) { if config == nil { return []map[string]interface{}{} } for _, spotInfo := range config { m = append(m, map[string]interface{}{ "instance_type": spotInfo.InstanceType, "price_limit": spotInfo.PriceLimit, }) } return m } func flattenAutoScalingConfig(config *cs.AutoScaling) (m []map[string]interface{}) { if config == nil { return } m = append(m, map[string]interface{}{ "min_size": config.MinInstances, "max_size": config.MaxInstances, "type": config.Type, "is_bond_eip": config.IsBindEip, "eip_internet_charge_type": config.EipInternetChargeType, "eip_bandwidth": config.EipBandWidth, }) return } func flattenManagementNodepoolConfig(config *cs.Management) (m []map[string]interface{}) { if config == nil { return } m = append(m, map[string]interface{}{ "auto_repair": config.AutoRepair, "auto_upgrade": config.UpgradeConf.AutoUpgrade, "surge": config.UpgradeConf.Surge, "surge_percentage": config.UpgradeConf.SurgePercentage, "max_unavailable": config.UpgradeConf.MaxUnavailable, }) return } func flattenNodeDataDisksConfig(config []cs.NodePoolDataDisk) (m []map[string]interface{}) { if config == nil { return []map[string]interface{}{} } for _, disks := range config { m = append(m, map[string]interface{}{ "size": disks.Size, "category": disks.Category, "encrypted": disks.Encrypted, "performance_level": disks.PerformanceLevel, }) } return m } func flattenTaintsConfig(config []cs.Taint) (m []map[string]interface{}) { if config == nil { return []map[string]interface{}{} } for _, taint := range config { m = append(m, map[string]interface{}{ "key": taint.Key, "value": taint.Value, "effect": taint.Effect, }) } return m } func flattenLabelsConfig(config []cs.Label) (m []map[string]interface{}) { if config == nil { return []map[string]interface{}{} } for _, label := range config { m = append(m, map[string]interface{}{ "key": label.Key, "value": label.Value, }) } return m } func flattenTagsConfig(config []cs.Tag) map[string]string { m := make(map[string]string, len(config)) if len(config) < 0 { return m } for _, tag := range config { if tag.Key != DefaultClusterTag { m[tag.Key] = tag.Value } } return m } func RemoveNodePoolNodes(d *schema.ResourceData, meta interface{}, clusterid, nodepoolid string, oldNodes []interface{}, newNodes []interface{}) error { client := meta.(*connectivity.AlibabacloudStackClient) csService := CsService{client} // list all nodes of the nodepool object, err := csService.DescribeClusterNodes(clusterid, d.Id()) if err != nil { if errmsgs.NotFoundError(err) { d.SetId("") return nil } return errmsgs.WrapError(err) } // fetch the NodeName of all nodes var allNodeName []string for _, value := range object.Nodes { allNodeName = append(allNodeName, value.NodeName) } removeNodesName := allNodeName // remove automatically created nodes if d.HasChange("node_count") { o, n := d.GetChange("node_count") count := o.(int) - n.(int) removeNodesName = allNodeName[:count] } // remove manually added nodes if d.HasChange("instances") { var removeInstanceList []string var attachNodeList []string if oldNodes != nil && newNodes != nil { attachNodeList = difference(expandStringList(oldNodes), expandStringList(newNodes)) } if len(newNodes) == 0 { attachNodeList = expandStringList(oldNodes) } for _, v := range object.Nodes { for _, name := range attachNodeList { if name == v.InstanceID { removeInstanceList = append(removeInstanceList, v.NodeName) } } } removeNodesName = removeInstanceList } if len(removeNodesName) > 0 { req := csService.client.NewCommonRequest("POST", "CS", "2015-12-15", "RemoveClusterNodes", fmt.Sprintf("/api/v2/clusters/%s/nodes/remove", clusterid)) req.QueryParams["SignatureVersion"] = "1.0" req.Headers["x-acs-asapi-gateway-version"] = "3.0" body := map[string]interface{}{ "release_node": true, "drain_node": true, "nodes": removeNodesName, "ClusterId": clusterid, } jsonData, err := json.Marshal(body) if err != nil { return errmsgs.WrapError(fmt.Errorf("Error marshaling to JSON: %v", err)) } req.SetContentType(requests.Json) req.SetContent(jsonData) resp, err := csService.client.ProcessCommonRequest(req) if err != nil { if resp == nil { return errmsgs.WrapErrorf(err, "Process Common Request Failed") } return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), "DeleteKubernetesClusterNodes", errmsgs.DenverdinoAliyungo) } stateConf := BuildStateConf([]string{"removing"}, []string{"active"}, d.Timeout(schema.TimeoutUpdate), 60*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), clusterid, []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id()) } } return nil } func ScaleClusterNodePool(d *schema.ResourceData, meta interface{}, clusterid, nodepoolid string, oldValue, newValue int) error { var raw interface{} client := meta.(*connectivity.AlibabacloudStackClient) csService := CsService{client} // list all nodes of the nodepool req := csService.client.NewCommonRequest("POST", "CS", "2015-12-15", "ScaleClusterNodePool", fmt.Sprintf("/clusters/%s/nodepools/%s)", clusterid, nodepoolid)) req.QueryParams["SignatureVersion"] = "1.0" body := map[string]interface{}{ "ClusterId": clusterid, "NodepoolId": nodepoolid, "count": int64(newValue) - int64(oldValue), } jsonData, err := json.Marshal(body) if err != nil { return errmsgs.WrapError(fmt.Errorf("Error marshaling to JSON: %v", err)) } req.SetContentType(requests.Json) req.SetContent(jsonData) req.Headers["x-acs-asapi-gateway-version"] = "3.0" response, err := csService.client.ProcessCommonRequest(req) if err != nil { if response == nil { return errmsgs.WrapErrorf(err, "Process Common Request Failed") } return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), "ScaleClusterNodePool", raw) } stateConf := BuildStateConf([]string{"scaling"}, []string{"active"}, d.Timeout(schema.TimeoutUpdate), 30*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), clusterid, []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id()) } return nil } func difference(slice1 []string, slice2 []string) []string { var diff []string for i := 0; i < 2; i++ { for _, s1 := range slice1 { found := false for _, s2 := range slice2 { if s1 == s2 { found = true break } } if !found { diff = append(diff, s1) } } if i == 0 { slice1, slice2 = slice2, slice1 } } return diff } func attachExistingInstance(d *schema.ResourceData, meta interface{}) error { csService := CsService{meta.(*connectivity.AlibabacloudStackClient)} client, err := meta.(*connectivity.AlibabacloudStackClient).NewRoaCsClient() if err != nil { return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, ResourceName, "InitializeClient", err) } clusterId := d.Get("cluster_id").(string) args := &roacs.AttachInstancesRequest{ NodepoolId: tea.String(d.Id()), FormatDisk: tea.Bool(false), KeepInstanceName: tea.Bool(true), } if v, ok := d.GetOk("password"); ok { args.Password = tea.String(v.(string)) } if v, ok := d.GetOk("key_name"); ok { args.KeyPair = tea.String(v.(string)) } if v, ok := d.GetOk("format_disk"); ok { args.FormatDisk = tea.Bool(v.(bool)) } if v, ok := d.GetOk("keep_instance_name"); ok { args.KeepInstanceName = tea.Bool(v.(bool)) } if v, ok := d.GetOk("image_id"); ok { args.ImageId = tea.String(v.(string)) } if v, ok := d.GetOk("instances"); ok { args.Instances = tea.StringSlice(expandStringList(v.([]interface{}))) } _, err = client.AttachInstances(tea.String(clusterId), args) if err != nil { return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, ResourceName, "AttachInstances", errmsgs.AliyunTablestoreGoSdk) } stateConf := BuildStateConf([]string{"scaling"}, []string{"active"}, d.Timeout(schema.TimeoutUpdate), 30*time.Second, csService.CsKubernetesNodePoolStateRefreshFunc(d.Id(), clusterId, []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id()) } return nil }