@@ -24,6 +24,7 @@ import (
2424 "fmt"
2525 "net/http"
2626 "regexp"
27+ "sort"
2728 "strings"
2829 "sync"
2930
@@ -103,15 +104,71 @@ func (p *DefaultProvider) GetClusterCNI(_ context.Context) (string, error) {
103104 return p .clusterCNI , nil
104105}
105106
107+ // Get the ID of the target nodepool id when DescribeClusterAttachScriptsRequest.
108+ // If there is no default nodepool, select the nodepool with the most HealthyNodes.
109+ //
110+ //nolint:gocyclo
111+ func (p * DefaultProvider ) getTargetNodePoolID (ctx context.Context ) (* string , error ) {
112+ resp , err := p .ackClient .DescribeClusterNodePools (tea .String (p .clusterID ), & ackclient.DescribeClusterNodePoolsRequest {})
113+ if err != nil {
114+ log .FromContext (ctx ).Error (err , "Failed to describe cluster nodepools" )
115+ return nil , err
116+ }
117+ if resp == nil || resp .Body == nil || resp .Body .Nodepools == nil {
118+ return nil , fmt .Errorf ("empty describe cluster nodepools response" )
119+ }
120+ if len (resp .Body .Nodepools ) == 0 {
121+ return nil , fmt .Errorf ("no nodepool found" )
122+ }
123+
124+ nodepools := resp .Body .Nodepools
125+ sort .Slice (nodepools , func (i , j int ) bool {
126+ if nodepools [i ].NodepoolInfo == nil || nodepools [j ].NodepoolInfo == nil {
127+ return false
128+ }
129+
130+ if nodepools [i ].NodepoolInfo .IsDefault != nil && nodepools [j ].NodepoolInfo .IsDefault != nil {
131+ if * nodepools [i ].NodepoolInfo .IsDefault && ! * nodepools [j ].NodepoolInfo .IsDefault {
132+ return true
133+ }
134+ if ! * nodepools [i ].NodepoolInfo .IsDefault && * nodepools [j ].NodepoolInfo .IsDefault {
135+ return false
136+ }
137+ }
138+
139+ if nodepools [i ].Status == nil || nodepools [j ].Status == nil || nodepools [i ].Status .HealthyNodes == nil || nodepools [j ].Status .HealthyNodes == nil {
140+ return false
141+ }
142+ return * nodepools [i ].Status .HealthyNodes > * nodepools [j ].Status .HealthyNodes
143+ })
144+
145+ targetNodepool := nodepools [0 ]
146+ if targetNodepool .NodepoolInfo == nil {
147+ return nil , fmt .Errorf ("target describe cluster nodepool is empty" )
148+ }
149+ return targetNodepool .NodepoolInfo .NodepoolId , nil
150+ }
151+
106152func (p * DefaultProvider ) GetNodeRegisterScript (ctx context.Context ,
107153 labels map [string ]string ,
108154 kubeletCfg * v1alpha1.KubeletConfiguration ) (string , error ) {
109155 if cachedScript , ok := p .cache .Get (p .clusterID ); ok {
110156 return p .resolveUserData (cachedScript .(string ), labels , kubeletCfg ), nil
111157 }
112158
159+ nodepoolID , err := p .getTargetNodePoolID (ctx )
160+ if err != nil {
161+ // Don't return here, we can process when there is no default cluster id.
162+ // We need to try to obtain a usable nodepool ID in order to get the cluster attach scripts.
163+ // One known scenario is on an ACK cluster with version 1.24, where the user deleted the default nodepool and
164+ // created a nodepool with a containerd runtime. The DescribeClusterAttachScriptsRequest api will use the
165+ // CRI configuration of the deleted default nodepool, which might be using the Docker runtime.
166+ // This could result in nodes failing to register to the new cluster.
167+ log .FromContext (ctx ).Error (err , "Failed to get default nodepool id" )
168+ }
113169 reqPara := & ackclient.DescribeClusterAttachScriptsRequest {
114170 KeepInstanceName : tea .Bool (true ),
171+ NodepoolId : nodepoolID ,
115172 }
116173 resp , err := p .ackClient .DescribeClusterAttachScripts (tea .String (p .clusterID ), reqPara )
117174 if err != nil {
0 commit comments