alicloud/service_alicloud_cs.go (1,100 lines of code) (raw):
package alicloud
import (
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/alibabacloud-go/tea/tea"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"encoding/base64"
"encoding/json"
"github.com/alibabacloud-go/cs-20151215/v5/client"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
"github.com/denverdino/aliyungo/cs"
)
type CsService struct {
client *connectivity.AliyunClient
}
type CsClient struct {
client *client.Client
}
type Component struct {
ComponentName string `json:"component_name"`
Version string `json:"version"`
NextVersion string `json:"next_version"`
CanUpgrade bool `json:"can_upgrade"`
Required bool `json:"required"`
Status string `json:"status"`
ErrMessage string `json:"err_message"`
Config string `json:"config"`
ConfigSchema string `json:"config_schema"`
Error interface{} `json:"error"`
SupportedActions []string `json:"supported_actions"`
}
const (
COMPONENT_AUTO_SCALER = "cluster-autoscaler"
COMPONENT_DEFAULT_VRESION = "v1.0.0"
SCALING_CONFIGURATION_NAME = "kubernetes_autoscaler_autogen"
DefaultECSTag = "k8s.aliyun.com"
DefaultClusterTag = "ack.aliyun.com"
CsPlayerAccountIdTag = "ack.playeraccount"
RECYCLE_MODE_LABEL = "k8s.io/cluster-autoscaler/node-template/label/policy"
DefaultAutoscalerTag = "k8s.io/cluster-autoscaler"
SCALING_GROUP_NAME = "sg-%s-%s"
DEFAULT_COOL_DOWN_TIME = 300
RELEASE_MODE = "release"
RECYCLE_MODE = "recycle"
PRIORITY_POLICY = "PRIORITY"
COST_OPTIMIZED_POLICY = "COST_OPTIMIZED"
BALANCE_POLICY = "BALANCE"
UpgradeClusterTimeout = 30 * time.Minute
IdMsgWithTask = IdMsg + "TaskInfo: %s" // wait for async task info
)
var (
ATTACH_SCRIPT_WITH_VERSION = `#!/bin/sh
curl http://aliacs-k8s-%s.oss-%s.aliyuncs.com/public/pkg/run/attach/%s/attach_node.sh | bash -s -- --openapi-token %s --ess true `
NETWORK_ADDON_NAMES = []string{"terway", "kube-flannel-ds", "terway-eni", "terway-eniip"}
)
func (s *CsService) GetContainerClusterByName(name string) (cluster cs.ClusterType, err error) {
name = Trim(name)
invoker := NewInvoker()
var clusters []cs.ClusterType
err = invoker.Run(func() error {
raw, e := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
return csClient.DescribeClusters(name)
})
if e != nil {
return e
}
clusters, _ = raw.([]cs.ClusterType)
return nil
})
if err != nil {
return cluster, fmt.Errorf("Describe cluster failed by name %s: %#v.", name, err)
}
if len(clusters) < 1 {
return cluster, GetNotFoundErrorFromString(GetNotFoundMessage("Container Cluster", name))
}
for _, c := range clusters {
if c.Name == name {
return c, nil
}
}
return cluster, GetNotFoundErrorFromString(GetNotFoundMessage("Container Cluster", name))
}
func (s *CsService) GetContainerClusterAndCertsByName(name string) (*cs.ClusterType, *cs.ClusterCerts, error) {
cluster, err := s.GetContainerClusterByName(name)
if err != nil {
return nil, nil, err
}
var certs cs.ClusterCerts
invoker := NewInvoker()
err = invoker.Run(func() error {
raw, e := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
return csClient.GetClusterCerts(cluster.ClusterID)
})
if e != nil {
return e
}
certs, _ = raw.(cs.ClusterCerts)
return nil
})
if err != nil {
return nil, nil, err
}
return &cluster, &certs, nil
}
func (s *CsService) DescribeContainerApplication(clusterName, appName string) (app cs.GetProjectResponse, err error) {
appName = Trim(appName)
cluster, certs, err := s.GetContainerClusterAndCertsByName(clusterName)
if err != nil {
return app, err
}
raw, err := s.client.WithCsProjectClient(cluster.ClusterID, cluster.MasterURL, *certs, func(csProjectClient *cs.ProjectClient) (interface{}, error) {
return csProjectClient.GetProject(appName)
})
app, _ = raw.(cs.GetProjectResponse)
if err != nil {
if IsExpectedErrors(err, []string{"Not Found"}) {
return app, GetNotFoundErrorFromString(GetNotFoundMessage("Container Application", appName))
}
return app, fmt.Errorf("Getting Application failed by name %s: %#v.", appName, err)
}
if app.Name != appName {
return app, GetNotFoundErrorFromString(GetNotFoundMessage("Container Application", appName))
}
return
}
func (s *CsService) WaitForContainerApplication(clusterName, appName string, status Status, timeout int) error {
if timeout <= 0 {
timeout = DefaultTimeout
}
for {
app, err := s.DescribeContainerApplication(clusterName, appName)
if err != nil {
return err
}
if strings.ToLower(app.CurrentState) == strings.ToLower(string(status)) {
break
}
timeout = timeout - DefaultIntervalShort
if timeout <= 0 {
return GetTimeErrorFromString(fmt.Sprintf("Waitting for container application %s is timeout and current status is %s.", string(status), app.CurrentState))
}
time.Sleep(DefaultIntervalShort * time.Second)
}
return nil
}
func (s *CsService) DescribeCsKubernetes(id string) (cluster *cs.KubernetesClusterDetail, err error) {
invoker := NewInvoker()
var requestInfo *cs.Client
var response interface{}
if err := invoker.Run(func() error {
raw, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
requestInfo = csClient
return csClient.DescribeKubernetesClusterDetail(id)
})
response = raw
return err
}); err != nil {
if IsExpectedErrors(err, []string{"ErrorClusterNotFound"}) {
return cluster, WrapErrorf(err, NotFoundMsg, DenverdinoAliyungo)
}
return cluster, WrapErrorf(err, DefaultErrorMsg, id, "DescribeKubernetesCluster", DenverdinoAliyungo)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["ClusterId"] = id
addDebug("DescribeKubernetesCluster", response, requestInfo, requestMap)
}
cluster, _ = response.(*cs.KubernetesClusterDetail)
if cluster.ClusterId != id {
return cluster, WrapErrorf(NotFoundErr("CsKubernetes", id), NotFoundMsg, ProviderERROR)
}
return
}
func (s *CsClient) DescribeClusterDetail(id string) (*client.DescribeClusterDetailResponseBody, error) {
if id == "" {
return nil, WrapError(fmt.Errorf("cluster id is empty"))
}
var err error
var response *client.DescribeClusterDetailResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
response, err = s.client.DescribeClusterDetail(tea.String(id))
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapError(err)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["ClusterId"] = id
addDebug("DescribeClusterDetail", response, id, requestMap)
}
return response.Body, nil
}
// DescribeClusterKubeConfig return cluster kube_config credential.
// It's used for kubernetes/managed_kubernetes/serverless_kubernetes.
// Deprecated, use CsClient.DescribeClusterKubeConfigWithExpiration
func (s *CsService) DescribeClusterKubeConfig(clusterId string, isResource bool) (*cs.ClusterConfig, error) {
invoker := NewInvoker()
var response interface{}
var requestInfo *cs.Client
var config *cs.ClusterConfig
if err := invoker.Run(func() error {
raw, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
requestInfo = csClient
return csClient.DescribeClusterUserConfig(clusterId, false)
})
response = raw
return err
}); err != nil {
if isResource {
return nil, WrapErrorf(err, DefaultErrorMsg, clusterId, "DescribeClusterUserConfig", DenverdinoAliyungo)
}
return nil, WrapErrorf(err, DataDefaultErrorMsg, clusterId, "DescribeClusterUserConfig", DenverdinoAliyungo)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["Id"] = clusterId
addDebug("DescribeClusterUserConfig", response, requestInfo, requestMap)
}
config, _ = response.(*cs.ClusterConfig)
return config, nil
}
// DescribeClusterKubeConfigWithExpiration return cluster kube_config credential with expiration time.
// It's used for kubernetes/managed_kubernetes/serverless_kubernetes.
func (s *CsClient) DescribeClusterKubeConfigWithExpiration(clusterId string, temporaryDurationMinutes int64) (*client.DescribeClusterUserKubeconfigResponseBody, error) {
if clusterId == "" {
return nil, WrapError(fmt.Errorf("clusterid is empty"))
}
request := &client.DescribeClusterUserKubeconfigRequest{
PrivateIpAddress: tea.Bool(false),
}
if temporaryDurationMinutes > 0 {
request.TemporaryDurationMinutes = tea.Int64(temporaryDurationMinutes)
}
var err error
var kubeConfig *client.DescribeClusterUserKubeconfigResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
kubeConfig, err = s.client.DescribeClusterUserKubeconfig(tea.String(clusterId), request)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapError(err)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["ClusterId"] = clusterId
addDebug("DescribeClusterUserConfig", kubeConfig, request, requestMap)
}
return kubeConfig.Body, nil
}
// This function returns all available addons metadata of the cluster
func (s *CsClient) DescribeClusterAddonsMetadata(clusterId string) (map[string]*Component, error) {
result := make(map[string]*Component)
var err error
var resp *client.DescribeClusterAddonsVersionResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
resp, err = s.client.DescribeClusterAddonsVersion(&clusterId)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeClusterAddonsVersion", err)
}
for name, addon := range resp.Body {
c := &Component{}
bytes, err := json.Marshal(addon)
if err != nil {
continue
}
err = json.Unmarshal(bytes, c)
if err != nil {
continue
}
result[name] = c
}
return result, nil
}
// This function returns the latest addon status information
func (s *CsClient) DescribeCsKubernetesAddonStatus(clusterId string, addonName string) (*Component, error) {
result := &Component{}
var err error
var resp *client.DescribeClusterAddonsUpgradeStatusResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
resp, err = s.client.DescribeClusterAddonsUpgradeStatus(&clusterId, &client.DescribeClusterAddonsUpgradeStatusRequest{
ComponentIds: []*string{tea.String(addonName)},
})
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeClusterAddonsUpgradeStatus", err)
}
addon, ok := resp.Body[addonName]
if !ok {
return nil, WrapErrorf(NotFoundErr("alicloud_cs_kubernetes_addon", addonName), ResourceNotfound)
}
addonInfo := addon.(map[string]interface{})["addon_info"]
tasks := addon.(map[string]interface{})["tasks"]
result.Version = addonInfo.(map[string]interface{})["version"].(string)
result.CanUpgrade = addon.(map[string]interface{})["can_upgrade"].(bool)
result.Status = tasks.(map[string]interface{})["status"].(string)
if tErr, ok := tasks.(map[string]interface{})["error"]; ok {
result.Error = tErr
}
if message, ok := tasks.(map[string]interface{})["message"]; ok {
result.ErrMessage = message.(string)
}
return result, nil
}
// This function returns the latest addon instance
func (s *CsClient) DescribeCsKubernetesAddonInstance(clusterId string, addonName string) (*Component, error) {
component := &Component{}
var err error
var resp *client.DescribeClusterAddonInstanceResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
resp, err = s.client.DescribeClusterAddonInstance(&clusterId, tea.String(addonName))
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
if IsExpectedErrors(err, []string{"AddonNotFound"}) {
err = WrapErrorf(NotFoundErr("alicloud_cs_kubernetes_addon", addonName), ResourceNotfound)
return component, err
}
return nil, err
}
// FixMe: Currently, the addon does not support the initial state and needs to be returned in a task state.
if resp.Body.State == nil || *resp.Body.State == "" {
result, err := s.DescribeCsKubernetesAddonStatus(clusterId, addonName)
return result, err
}
if resp.Body.Name != nil {
component.ComponentName = *resp.Body.Name
}
if resp.Body.Version != nil {
component.Version = *resp.Body.Version
}
if resp.Body.State != nil {
component.Status = *resp.Body.State
}
if resp.Body.Config != nil {
component.Config = *resp.Body.Config
}
return component, nil
}
// This function returns the status of all available addons of the cluster
func (s *CsClient) DescribeCsKubernetesAllAvailableAddons(clusterId string) (map[string]*Component, error) {
availableAddons, err := s.DescribeClusterAddonsMetadata(clusterId)
if err != nil {
return nil, err
}
queryList := make([]*string, 0)
for name := range availableAddons {
queryList = append(queryList, tea.String(name))
}
status, err := s.DescribeCsKubernetesAllAddonsStatus(clusterId, queryList)
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeCsKubernetesExistedAddons", err)
}
for name, addon := range availableAddons {
if _, ok := status[name]; !ok {
continue
}
addon.Version = status[name].Version
addon.CanUpgrade = status[name].CanUpgrade
addon.ErrMessage = status[name].ErrMessage
addon.Config = status[name].Config
if addon.Version == "" {
continue
}
addonInstance, err := s.DescribeCsKubernetesAddonInstance(clusterId, name)
if err != nil {
if NotFoundError(err) {
continue
}
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeCsKubernetesExistedAddons", err)
}
addon.Config = addonInstance.Config
addon.Status = addonInstance.Status
}
return availableAddons, nil
}
// This function returns the latest multiple addons status information
func (s *CsClient) DescribeCsKubernetesAllAddonsStatus(clusterId string, addons []*string) (map[string]*Component, error) {
addonsStatus := make(map[string]*Component)
var err error
var resp *client.DescribeClusterAddonsUpgradeStatusResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
resp, err = s.client.DescribeClusterAddonsUpgradeStatus(&clusterId, &client.DescribeClusterAddonsUpgradeStatusRequest{
ComponentIds: addons,
})
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeClusterAddonsUpgradeStatus", err)
}
for name, status := range resp.Body {
c := &Component{}
addonInfo := status.(map[string]interface{})["addon_info"]
tasks := status.(map[string]interface{})["tasks"]
c.Version = addonInfo.(map[string]interface{})["version"].(string)
c.CanUpgrade = status.(map[string]interface{})["can_upgrade"].(bool)
c.Status = tasks.(map[string]interface{})["status"].(string)
if message, ok := tasks.(map[string]interface{})["message"]; ok {
c.ErrMessage = message.(string)
}
addonsStatus[name] = c
}
return addonsStatus, nil
}
func (s *CsClient) DescribeCsKubernetesAddon(id string) (*Component, error) {
parts, err := ParseResourceId(id, 2)
if err != nil {
return nil, WrapError(err)
}
clusterId := parts[0]
addonName := parts[1]
addonInstance, err := s.DescribeCsKubernetesAddonInstance(clusterId, addonName)
if err != nil {
if IsExpectedErrors(err, []string{"AddonNotFound", "ErrorClusterNotFound"}) || NotFoundError(err) {
return nil, WrapErrorf(NotFoundErr("alicloud_cs_kubernetes_addon", id), ResourceNotfound)
}
return nil, err
}
addonsMetadata, err := s.DescribeClusterAddonsMetadata(clusterId)
if err != nil {
return nil, err
}
addonStatus, err := s.DescribeCsKubernetesAddonStatus(clusterId, addonName)
if err != nil {
return nil, err
}
// Update some fields
if addon, existed := addonsMetadata[addonName]; existed {
addon.Version = addonStatus.Version
addon.Status = addonStatus.Status
addon.CanUpgrade = addonStatus.CanUpgrade
addon.ErrMessage = addonStatus.ErrMessage
addon.Config = addonInstance.Config
return addon, nil
}
return nil, WrapErrorf(NotFoundErr("alicloud_cs_kubernetes_addon", id), ResourceNotfound)
}
func (s *CsClient) CsKubernetesAddonTaskRefreshFunc(clusterId string, addonName string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetesAddonStatus(clusterId, addonName)
if err != nil {
if NotFoundError(err) {
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if object.Status == failState {
return object, object.Status, WrapError(Error(FailedToReachTargetStatusWithResponse, clusterId, object.ErrMessage))
}
}
return object, object.Status, nil
}
}
func (s *CsClient) CsKubernetesAddonStateRefreshFunc(clusterId string, addonName string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetesAddonInstance(clusterId, addonName)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if object.Status == failState {
return object, object.Status, WrapError(Error(FailedToReachTargetStatusWithResponse, clusterId, object.ErrMessage))
}
}
return object, object.Status, nil
}
}
func (s *CsClient) CsKubernetesAddonExistRefreshFunc(clusterId string, addonName string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetesAddonInstance(clusterId, addonName)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return object, "deleted", nil
}
return nil, "", WrapError(err)
}
return object, object.Status, nil
}
}
func (s *CsClient) DescribeCsAutoscalingConfig(id string) (*client.CreateAutoscalingConfigRequest, error) {
request := &client.CreateAutoscalingConfigRequest{
CoolDownDuration: tea.String("10m"),
UnneededDuration: tea.String("10m"),
UtilizationThreshold: tea.String("0.5"),
GpuUtilizationThreshold: tea.String("0.5"),
ScanInterval: tea.String("30s"),
}
return request, nil
}
func (s *CsClient) installAddon(d *schema.ResourceData) error {
clusterId := d.Get("cluster_id").(string)
body := make([]*client.InstallClusterAddonsRequestBody, 0)
b := &client.InstallClusterAddonsRequestBody{
Name: tea.String(d.Get("name").(string)),
Version: tea.String(d.Get("version").(string)),
}
if config, exist := d.GetOk("config"); exist {
b.Config = tea.String(config.(string))
}
body = append(body, b)
creationArgs := &client.InstallClusterAddonsRequest{
Body: body,
}
var err error
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err = s.client.InstallClusterAddons(&clusterId, creationArgs)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "installAddon", err)
}
return nil
}
func (s *CsClient) upgradeAddon(d *schema.ResourceData, updateVersion, updateConfig bool) error {
clusterId := d.Get("cluster_id").(string)
body := make([]*client.UpgradeClusterAddonsRequestBody, 0)
b := &client.UpgradeClusterAddonsRequestBody{
ComponentName: tea.String(d.Get("name").(string)),
}
if updateVersion {
b.NextVersion = tea.String(d.Get("version").(string))
}
if updateConfig {
if config, exist := d.GetOk("config"); exist {
b.Config = tea.String(config.(string))
}
}
body = append(body, b)
upgradeArgs := &client.UpgradeClusterAddonsRequest{
Body: body,
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := s.client.UpgradeClusterAddons(&clusterId, upgradeArgs)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "upgradeAddon", err)
}
return nil
}
func (s *CsClient) uninstallAddon(d *schema.ResourceData) error {
parts, err := ParseResourceId(d.Id(), 2)
if err != nil {
return WrapError(err)
}
clusterId := parts[0]
body := make([]*client.UnInstallClusterAddonsRequestAddons, 0)
b := &client.UnInstallClusterAddonsRequestAddons{
Name: tea.String(parts[1]),
}
if v, ok := d.GetOk("cleanup_cloud_resources"); ok {
b.CleanupCloudResources = tea.Bool(v.(bool))
}
body = append(body, b)
uninstallArgs := &client.UnInstallClusterAddonsRequest{
Addons: body,
}
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err = s.client.UnInstallClusterAddons(&clusterId, uninstallArgs)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "uninstallAddon", err)
}
return nil
}
func (s *CsClient) updateAddonConfig(d *schema.ResourceData) error {
clusterId := d.Get("cluster_id").(string)
ComponentName := d.Get("name").(string)
upgradeArgs := &client.ModifyClusterAddonRequest{
Config: tea.String(d.Get("config").(string)),
}
var err error
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err = s.client.ModifyClusterAddon(&clusterId, &ComponentName, upgradeArgs)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "upgradeAddonConfig", err)
}
return nil
}
// This function returns the status of all available addons of the cluster
func (s *CsClient) DescribeCsKubernetesAddonMetadata(clusterId string, name string, version string) (*Component, error) {
var err error
req := &client.DescribeClusterAddonMetadataRequest{
Version: tea.String(version),
}
var resp *client.DescribeClusterAddonMetadataResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
resp, err = s.client.DescribeClusterAddonMetadata(&clusterId, &name, req)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return nil, WrapErrorf(err, DefaultErrorMsg, ResourceAlicloudCSKubernetesAddon, "DescribeCsKubernetesExistedAddons", err)
}
result := &Component{
ComponentName: *resp.Body.Name,
Version: *resp.Body.Version,
ConfigSchema: *resp.Body.ConfigSchema,
}
return result, nil
}
func (s *CsService) DescribeCsKubernetesNodePool(id string) (nodePool *cs.NodePoolDetail, err error) {
invoker := NewInvoker()
var requestInfo *cs.Client
var response interface{}
parts, err := ParseResourceId(id, 2)
if err != nil {
return nil, WrapError(err)
}
clusterId := parts[0]
nodePoolId := parts[1]
if err := invoker.Run(func() error {
raw, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
requestInfo = csClient
return csClient.DescribeNodePoolDetail(clusterId, nodePoolId)
})
response = raw
return err
}); err != nil {
if IsExpectedErrors(err, []string{"ErrorClusterNotFound", "400"}) {
return nil, WrapErrorf(err, NotFoundMsg, DenverdinoAliyungo)
}
return nil, WrapErrorf(err, DefaultErrorMsg, nodePoolId, "DescribeNodePool", DenverdinoAliyungo)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["ClusterId"] = clusterId
requestMap["NodePoolId"] = nodePoolId
addDebug("DescribeNodepool", response, requestInfo, requestMap)
}
nodePool, _ = response.(*cs.NodePoolDetail)
if nodePool.NodePoolId != nodePoolId {
return nil, WrapErrorf(NotFoundErr("CsNodePool", nodePoolId), NotFoundMsg, ProviderERROR)
}
return
}
func (s *CsService) WaitForCsKubernetes(id string, status Status, timeout int) error {
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
for {
object, err := s.DescribeCsKubernetes(id)
if err != nil {
if NotFoundError(err) {
if status == Deleted {
return nil
}
} else {
return WrapError(err)
}
}
if object.ClusterId == id && status != Deleted {
return nil
}
if time.Now().After(deadline) {
return WrapErrorf(err, WaitTimeoutMsg, id, GetFunc(1), timeout, object.ClusterId, id, ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
}
func (s *CsService) DescribeCsManagedKubernetes(id string) (cluster *cs.KubernetesClusterDetail, err error) {
var requestInfo *cs.Client
invoker := NewInvoker()
var response interface{}
if err := invoker.Run(func() error {
raw, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
requestInfo = csClient
return csClient.DescribeKubernetesClusterDetail(id)
})
response = raw
return err
}); err != nil {
if IsExpectedErrors(err, []string{"ErrorClusterNotFound"}) {
return cluster, WrapErrorf(err, NotFoundMsg, AlibabaCloudSdkGoERROR)
}
return cluster, WrapErrorf(err, DefaultErrorMsg, id, "DescribeKubernetesCluster", DenverdinoAliyungo)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["Id"] = id
addDebug("DescribeKubernetesCluster", response, requestInfo, requestMap, map[string]interface{}{"Id": id})
}
cluster, _ = response.(*cs.KubernetesClusterDetail)
if cluster.ClusterId != id {
return cluster, WrapErrorf(NotFoundErr("CSManagedKubernetes", id), NotFoundMsg, ProviderERROR)
}
return
}
func (s *CsService) WaitForCSManagedKubernetes(id string, status Status, timeout int) error {
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
for {
object, err := s.DescribeCsManagedKubernetes(id)
if err != nil {
if NotFoundError(err) {
if status == Deleted {
return nil
}
} else {
return WrapError(err)
}
}
if object.ClusterId == id && status != Deleted {
return nil
}
if time.Now().After(deadline) {
return WrapErrorf(err, WaitTimeoutMsg, id, GetFunc(1), timeout, object.ClusterId, id, ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
}
func (s *CsService) CsKubernetesInstanceStateRefreshFunc(id string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetes(id)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if string(object.State) == failState {
return object, string(object.State), WrapError(Error(FailedToReachTargetStatus, string(object.State)))
}
}
return object, string(object.State), nil
}
}
func (s *CsService) CsKubernetesNodePoolStateRefreshFunc(id string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsKubernetesNodePool(id)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if string(object.State) == failState {
return object, string(object.State), WrapError(Error(FailedToReachTargetStatus, string(object.State)))
}
}
return object, string(object.State), nil
}
}
func (s *CsService) CsManagedKubernetesInstanceStateRefreshFunc(id string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsManagedKubernetes(id)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if string(object.State) == failState {
return object, string(object.State), WrapError(Error(FailedToReachTargetStatus, string(object.State)))
}
}
return object, string(object.State), nil
}
}
func (s *CsService) CsServerlessKubernetesInstanceStateRefreshFunc(id string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeCsServerlessKubernetes(id)
if err != nil {
if NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", WrapError(err)
}
for _, failState := range failStates {
if string(object.State) == failState {
return object, string(object.State), WrapError(Error(FailedToReachTargetStatus, string(object.State)))
}
}
return object, string(object.State), nil
}
}
func (s *CsService) DescribeCsServerlessKubernetes(id string) (*cs.ServerlessClusterResponse, error) {
cluster := &cs.ServerlessClusterResponse{}
var requestInfo *cs.Client
invoker := NewInvoker()
var response interface{}
if err := invoker.Run(func() error {
raw, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
requestInfo = csClient
return csClient.DescribeServerlessKubernetesCluster(id)
})
response = raw
return err
}); err != nil {
if IsExpectedErrors(err, []string{"ErrorClusterNotFound"}) {
return cluster, WrapErrorf(err, NotFoundMsg, DenverdinoAliyungo)
}
return cluster, WrapErrorf(err, DefaultErrorMsg, id, "DescribeServerlessKubernetesCluster", DenverdinoAliyungo)
}
if debugOn() {
requestMap := make(map[string]interface{})
requestMap["Id"] = id
addDebug("DescribeServerlessKubernetesCluster", response, requestInfo, requestMap, map[string]interface{}{"Id": id})
}
cluster, _ = response.(*cs.ServerlessClusterResponse)
if cluster != nil && cluster.ClusterId != id {
return cluster, WrapErrorf(NotFoundErr("CSServerlessKubernetes", id), NotFoundMsg, ProviderERROR)
}
return cluster, nil
}
func (s *CsService) WaitForCSServerlessKubernetes(id string, status Status, timeout int) error {
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
for {
object, err := s.DescribeCsServerlessKubernetes(id)
if err != nil {
if NotFoundError(err) {
if status == Deleted {
return nil
}
} else {
return WrapError(err)
}
}
if object.ClusterId == id && status != Deleted {
return nil
}
if time.Now().After(deadline) {
return WrapErrorf(err, WaitTimeoutMsg, id, GetFunc(1), timeout, object.ClusterId, id, ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
}
func (s *CsService) tagsToMap(tags []cs.Tag) map[string]string {
result := make(map[string]string)
for _, t := range tags {
if !s.ignoreTag(t) {
result[t.Key] = t.Value
}
}
return result
}
func (s *CsService) ignoreTag(t cs.Tag) bool {
filter := []string{"^http://", "^https://"}
for _, v := range filter {
log.Printf("[DEBUG] Matching prefix %v with %v\n", v, t.Key)
ok, _ := regexp.MatchString(v, t.Key)
if ok {
log.Printf("[DEBUG] Found Alibaba Cloud specific t %s (val: %s), ignoring.\n", t.Key, t.Value)
return true
}
}
return false
}
func (s *CsService) GetPermanentToken(clusterId string) (string, error) {
describeClusterTokensResponse, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
return csClient.DescribeClusterTokens(clusterId)
})
if err != nil {
return "", WrapError(fmt.Errorf("failed to get permanent token,because of %v", err))
}
tokens, ok := describeClusterTokensResponse.([]*cs.ClusterTokenResponse)
if ok != true {
return "", WrapError(fmt.Errorf("failed to parse ClusterTokenResponse of cluster %s", clusterId))
}
permanentTokens := make([]string, 0)
for _, token := range tokens {
if token.Expired == 0 && token.IsActive == 1 {
permanentTokens = append(permanentTokens, token.Token)
break
}
}
// create a new token
if len(permanentTokens) == 0 {
createClusterTokenResponse, err := s.client.WithCsClient(func(csClient *cs.Client) (interface{}, error) {
clusterTokenReqeust := &cs.ClusterTokenReqeust{}
clusterTokenReqeust.IsPermanently = true
return csClient.CreateClusterToken(clusterId, clusterTokenReqeust)
})
if err != nil {
return "", WrapError(fmt.Errorf("failed to create permanent token,because of %v", err))
}
token, ok := createClusterTokenResponse.(*cs.ClusterTokenResponse)
if ok != true {
return "", WrapError(fmt.Errorf("failed to parse token of %s", clusterId))
}
return token.Token, nil
}
return permanentTokens[0], nil
}
// GetUserData of cluster
func (s *CsService) GetUserData(clusterId string, labels string, taints string) (string, error) {
token, err := s.GetPermanentToken(clusterId)
if err != nil {
return "", err
}
if labels == "" {
labels = fmt.Sprintf("%s=true", DefaultECSTag)
} else {
labels = fmt.Sprintf("%s,%s=true", labels, DefaultECSTag)
}
cluster, err := s.DescribeCsKubernetes(clusterId)
if err != nil {
return "", WrapError(fmt.Errorf("failed to describe cs kuberentes cluster,because of %v", err))
}
extra_options := make([]string, 0)
if len(labels) > 0 || len(taints) > 0 {
if len(labels) != 0 {
extra_options = append(extra_options, fmt.Sprintf("--labels %s", labels))
}
if len(taints) != 0 {
extra_options = append(extra_options, fmt.Sprintf("--taints %s", taints))
}
}
if network, err := GetKubernetesNetworkName(cluster); err == nil && network != "" {
extra_options = append(extra_options, fmt.Sprintf("--network %s", network))
}
extra_options_in_line := strings.Join(extra_options, " ")
version := cluster.CurrentVersion
region := cluster.RegionId
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(ATTACH_SCRIPT_WITH_VERSION+extra_options_in_line, region, region, version, token))), nil
}
func (s *CsClient) DescribeTaskRefreshFunc(d *schema.ResourceData, taskId string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var err error
var taskInfo *client.DescribeTaskInfoResponse
wait := incrementalWait(3*time.Second, 3*time.Second)
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
taskInfo, err = s.client.DescribeTaskInfo(tea.String(taskId))
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
if NotFoundError(err) {
return taskInfo, "", nil
}
return nil, "", WrapError(err)
}
currentState := tea.StringValue(taskInfo.Body.State)
for _, failState := range failStates {
if currentState == failState {
if taskInfo.Body.Error != nil {
return taskInfo.Body.Error, currentState, WrapError(Error(FailedToReachTargetStatus, currentState))
}
return taskInfo, currentState, WrapError(Error(FailedToReachTargetStatus, currentState))
}
}
return taskInfo, currentState, nil
}
}
func GetKubernetesNetworkName(cluster *cs.KubernetesClusterDetail) (network string, err error) {
metadata := make(map[string]interface{})
if err := json.Unmarshal([]byte(cluster.MetaData), &metadata); err != nil {
return "", fmt.Errorf("unmarshal metaData failed. error: %s", err)
}
for _, name := range NETWORK_ADDON_NAMES {
if _, ok := metadata[fmt.Sprintf("%s%s", name, "Version")]; ok {
return name, nil
}
}
return "", fmt.Errorf("no network addon found")
}
func (s *CsClient) DescribeUserPermission(uid string) ([]*client.DescribeUserPermissionResponseBody, error) {
body, err := s.client.DescribeUserPermission(tea.String(uid))
if err != nil {
return nil, err
}
return body.Body, err
}
func setCerts(d *schema.ResourceData, meta interface{}, skipSetCertificateAuthority bool) error {
client := meta.(*connectivity.AliyunClient)
roaClient, err := client.NewRoaCsClient()
if err != nil {
return WrapError(err)
}
csClient := CsClient{roaClient}
kubeConfig, err := csClient.DescribeClusterKubeConfigWithExpiration(d.Id(), 0)
if err != nil {
log.Printf("[ERROR] Failed to get kubeconfig due to %++v", err)
}
m := flattenAlicloudCSCertificate(kubeConfig)
if len(m) >= 3 {
if ce, ok := d.GetOk("client_cert"); ok && ce.(string) != "" {
if err := writeToFile(ce.(string), m["client_cert"]); err != nil {
return WrapError(err)
}
}
if key, ok := d.GetOk("client_key"); ok && key.(string) != "" {
if err := writeToFile(key.(string), m["client_key"]); err != nil {
return WrapError(err)
}
}
if ca, ok := d.GetOk("cluster_ca_cert"); ok && ca.(string) != "" {
if err := writeToFile(ca.(string), m["cluster_cert"]); err != nil {
return WrapError(err)
}
}
}
// kube_config
if file, ok := d.GetOk("kube_config"); ok && file.(string) != "" {
writeToFile(file.(string), tea.StringValue(kubeConfig.Config))
}
if skipSetCertificateAuthority {
if _, ok := d.GetOk("certificate_authority"); ok {
if err := d.Set("certificate_authority", map[string]string{}); err != nil {
return WrapError(fmt.Errorf("error setting certificate_authority: %s", err))
}
}
} else {
if err := d.Set("certificate_authority", flattenAlicloudCSCertificate(kubeConfig)); err != nil {
return WrapError(fmt.Errorf("error setting certificate_authority: %s", err))
}
}
return nil
}