From 5b26cfaa1e22e3321f58a7f2060096ae05c76407 Mon Sep 17 00:00:00 2001 From: rostachen Date: Sun, 25 Apr 2021 17:25:02 +0800 Subject: [PATCH] `tencentcloud_kubernetes_cluster` add `upgrade_instances_follow_cluster` for upgrade all instances of cluster. --- CHANGELOG.md | 8 +- .../resource_tc_kubernetes_cluster.go | 81 ++++++++++++- tencentcloud/service_tencentcloud_tke.go | 107 ++++++++++++++++++ 3 files changed, 189 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f54d5dd5d5..5ac0122470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## 1.56.3 (Unreleased) +## 1.56.4 (Unreleased) +## 1.56.3 (April 25, 2021) + +ENHANCEMENTS: + +* Resource: `tencentcloud_kubernetes_cluster` add `upgrade_instances_follow_cluster` for upgrade all instances of cluster. + ## 1.56.2 (April 19, 2021) BUG FIXES: diff --git a/tencentcloud/resource_tc_kubernetes_cluster.go b/tencentcloud/resource_tc_kubernetes_cluster.go index adaaee3f9e..1bd072d8ba 100644 --- a/tencentcloud/resource_tc_kubernetes_cluster.go +++ b/tencentcloud/resource_tc_kubernetes_cluster.go @@ -661,6 +661,12 @@ func resourceTencentCloudTkeCluster() *schema.Resource { Default: "1.10.5", Description: "Version of the cluster, Default is '1.10.5'.", }, + "upgrade_instances_follow_cluster": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Indicates whether upgrade all instances when cluster_version change. Default is false.", + }, "cluster_ipvs": { Type: schema.TypeBool, ForceNew: true, @@ -673,7 +679,7 @@ func resourceTencentCloudTkeCluster() *schema.Resource { ForceNew: true, Optional: true, Default: false, - Description: "Indicates whether to enable cluster node auto scaler.", + Description: "Indicates whether to enable cluster node auto scaler. Default is false.", }, "node_pool_global_config": { Type: schema.TypeList, @@ -979,11 +985,11 @@ func resourceTencentCloudTkeCluster() *schema.Resource { Description: "Labels of tke cluster nodes.", }, "unschedulable": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, + Type: schema.TypeInt, + Optional: true, + ForceNew: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if new == "0" && old == ""{ + if new == "0" && old == "" { return true } else { return old == new @@ -1299,6 +1305,57 @@ func tkeGetNodePoolGlobalConfig(d *schema.ResourceData) *tke.ModifyClusterAsGrou return request } +// upgradeClusterInstances upgrade instances, upgrade type try seq:major, hot. +func upgradeClusterInstances(tkeService TkeService, ctx context.Context, id string) error { + // get all available instances for upgrade + upgradeType := "major" + instanceIds, err := tkeService.CheckInstancesUpgradeAble(ctx, id, upgradeType) + if err != nil { + return err + } + if len(instanceIds) == 0 { + upgradeType = "hot" + instanceIds, err = tkeService.CheckInstancesUpgradeAble(ctx, id, upgradeType) + if err != nil { + return err + } + } + log.Println("instancesIds for upgrade:", instanceIds) + if len(instanceIds) == 0 { + return nil + } + + // upgrade instances + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + inErr := tkeService.UpgradeClusterInstances(ctx, id, upgradeType, instanceIds) + if inErr != nil { + return retryError(inErr) + } + return nil + }) + if err != nil { + return err + } + + // check update status + err = resource.Retry(readRetryTimeout, func() *resource.RetryError { + done, inErr := tkeService.GetUpgradeInstanceResult(ctx, id) + if inErr != nil { + return retryError(inErr) + } + if done { + return nil + } else { + return resource.RetryableError(fmt.Errorf("cluster %s, retry...", id)) + } + }) + if err != nil { + return err + } + + return nil +} + func resourceTencentCloudTkeClusterCreate(d *schema.ResourceData, meta interface{}) error { defer logElapsed("resource.tencentcloud_kubernetes_cluster.create")() @@ -2114,7 +2171,7 @@ func resourceTencentCloudTkeClusterUpdate(d *schema.ResourceData, meta interface d.SetPartial("cluster_desc") } - //upgrate k8s version + //upgrade k8s cluster version if d.HasChange("cluster_version") { newVersion := d.Get("cluster_version").(string) isOk, err := tkeService.CheckClusterVersion(ctx, id, newVersion) @@ -2156,6 +2213,18 @@ func resourceTencentCloudTkeClusterUpdate(d *schema.ResourceData, meta interface if err != nil { return err } + + // upgrade instances version + upgrade := false + if v, ok := d.GetOk("upgrade_instances_follow_cluster"); ok { + upgrade = v.(bool) + } + if upgrade { + err := upgradeClusterInstances(tkeService, ctx, id) + if err != nil { + return err + } + } } // update node pool global config diff --git a/tencentcloud/service_tencentcloud_tke.go b/tencentcloud/service_tencentcloud_tke.go index 4a031dbc59..9ff4c9d612 100644 --- a/tencentcloud/service_tencentcloud_tke.go +++ b/tencentcloud/service_tencentcloud_tke.go @@ -328,6 +328,61 @@ func (me *TkeService) DescribeClusterConfig(ctx context.Context, id string) (con return } +func (me *TkeService) GetUpgradeInstanceResult(ctx context.Context, id string) ( + done bool, + errRet error, +) { + + logId := getLogId(ctx) + request := tke.NewGetUpgradeInstanceProgressRequest() + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + request.ClusterId = &id + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseTkeClient().GetUpgradeInstanceProgress(request) + + if err != nil { + errRet = err + return + } + + lifeState := *response.Response.LifeState + + // all instances success, lifeState=done + if lifeState == "done" { + return true, nil + } else if lifeState != "process" { + return false, fmt.Errorf("upgrade instances failed, tke response lifeState is:%s", lifeState) + } + + // parent lifeState=process, check whether all instances in processing. + for _, inst := range response.Response.Instances { + if *inst.LifeState == "done" || *inst.LifeState == "pending" { + continue + } + if *inst.LifeState != "process" { + return false, fmt.Errorf("upgrade instances failed, " + + "instanceId:%s, lifeState is:%s", *inst.InstanceID, *inst.LifeState) + } + // instance lifeState=process, check whether failed or not. + for _, detail := range inst.Detail { + if *detail.LifeState == "failed" { + return false, fmt.Errorf("upgrade instances failed, " + + "instanceId:%s, detail.lifeState is:%s", *inst.InstanceID, *detail.LifeState) + } + } + } + + return +} + func (me *TkeService) CreateCluster(ctx context.Context, basic ClusterBasicSetting, advanced ClusterAdvancedSettings, @@ -915,6 +970,58 @@ func (me *TkeService) CheckClusterVersion(ctx context.Context, id string, cluste return } +func (me *TkeService) CheckInstancesUpgradeAble(ctx context.Context, id string, upgradeType string) (instanceIds []string, errRet error) { + logId := getLogId(ctx) + request := tke.NewCheckInstancesUpgradeAbleRequest() + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, reason[%s]\n", logId, request.GetAction(), errRet.Error()) + } + }() + request.ClusterId = &id + request.UpgradeType = &upgradeType + ratelimit.Check(request.GetAction()) + + resp, err := me.client.UseTkeClient().CheckInstancesUpgradeAble(request) + if err != nil { + errRet = err + return + } + + if resp == nil || resp.Response == nil || resp.Response.UpgradeAbleInstances == nil { + return + } + for _, inst := range resp.Response.UpgradeAbleInstances { + instanceIds = append(instanceIds, *inst.InstanceId) + } + + return +} + +func (me *TkeService) UpgradeClusterInstances(ctx context.Context, id string, upgradeType string, instanceIds []string) (errRet error) { + logId := getLogId(ctx) + request := tke.NewUpgradeClusterInstancesRequest() + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, reason[%s]\n", logId, request.GetAction(), errRet.Error()) + } + }() + op := "create" + request.Operation = &op + request.ClusterId = &id + request.UpgradeType = &upgradeType + request.InstanceIds = helper.Strings(instanceIds) + ratelimit.Check(request.GetAction()) + + _, err := me.client.UseTkeClient().UpgradeClusterInstances(request) + if err != nil { + errRet = err + return + } + + return +} + func (me *TkeService) DescribeImages(ctx context.Context) (imageIds []string, errRet error) { logId := getLogId(ctx) request := tke.NewDescribeImagesRequest()