@@ -24,6 +24,7 @@ import (
24
24
"errors"
25
25
"fmt"
26
26
"net/http"
27
+ "regexp"
27
28
"sort"
28
29
"strings"
29
30
"sync"
@@ -106,21 +107,23 @@ func (p *DefaultProvider) GetClusterCNI(_ context.Context) (string, error) {
106
107
return p .clusterCNI , nil
107
108
}
108
109
109
- // Get the ID of the target nodepool id when DescribeClusterAttachScriptsRequest.
110
- // If there is no default nodepool, select the nodepool with the most HealthyNodes.
110
+ // We need to manually retrieve the runtime configuration of the nodepool, with the default node pool prioritized.
111
+ // If there is no default node pool, we will choose the runtime configuration of the node pool with the most HealthyNodes.
112
+ // In some cases, the DescribeClusterAttachScripts interface may return the Docker runtime in clusters running version 1.24,
113
+ // even though Docker runtime only supports clusters of version 1.22 and below.
111
114
//
112
115
//nolint:gocyclo
113
- func (p * DefaultProvider ) getTargetNodePoolID (ctx context.Context ) (* string , error ) {
116
+ func (p * DefaultProvider ) getClusterAttachRuntimeConfiguration (ctx context.Context ) (string , string , error ) {
114
117
resp , err := p .ackClient .DescribeClusterNodePools (tea .String (p .clusterID ), & ackclient.DescribeClusterNodePoolsRequest {})
115
118
if err != nil {
116
119
log .FromContext (ctx ).Error (err , "Failed to describe cluster nodepools" )
117
- return nil , err
120
+ return "" , "" , err
118
121
}
119
122
if resp == nil || resp .Body == nil || resp .Body .Nodepools == nil {
120
- return nil , fmt .Errorf ("empty describe cluster nodepools response" )
123
+ return "" , "" , fmt .Errorf ("empty describe cluster nodepools response" )
121
124
}
122
125
if len (resp .Body .Nodepools ) == 0 {
123
- return nil , fmt .Errorf ("no nodepool found" )
126
+ return "" , "" , fmt .Errorf ("no nodepool found" )
124
127
}
125
128
126
129
nodepools := resp .Body .Nodepools
@@ -145,10 +148,12 @@ func (p *DefaultProvider) getTargetNodePoolID(ctx context.Context) (*string, err
145
148
})
146
149
147
150
targetNodepool := nodepools [0 ]
148
- if targetNodepool .NodepoolInfo == nil {
149
- return nil , fmt .Errorf ("target describe cluster nodepool is empty" )
151
+ if targetNodepool .KubernetesConfig == nil || targetNodepool .KubernetesConfig .Runtime == nil ||
152
+ targetNodepool .KubernetesConfig .RuntimeVersion == nil {
153
+ return "" , "" , fmt .Errorf ("target describe cluster nodepool is empty" )
150
154
}
151
- return targetNodepool .NodepoolInfo .NodepoolId , nil
155
+ return tea .StringValue (targetNodepool .KubernetesConfig .Runtime ),
156
+ tea .StringValue (targetNodepool .KubernetesConfig .RuntimeVersion ), nil
152
157
}
153
158
154
159
func (p * DefaultProvider ) GetNodeRegisterScript (ctx context.Context ,
@@ -161,34 +166,34 @@ func (p *DefaultProvider) GetNodeRegisterScript(ctx context.Context,
161
166
return p .resolveUserData (cachedScript .(string ), labels , nodeClaim , kubeletCfg , userData ), nil
162
167
}
163
168
164
- nodepoolID , err := p .getTargetNodePoolID (ctx )
165
- if err != nil {
166
- // Don't return here, we can process when there is no default cluster id.
167
- // We need to try to obtain a usable nodepool ID in order to get the cluster attach scripts.
168
- // One known scenario is on an ACK cluster with version 1.24, where the user deleted the default nodepool and
169
- // created a nodepool with a containerd runtime. The DescribeClusterAttachScriptsRequest api will use the
170
- // CRI configuration of the deleted default nodepool, which might be using the Docker runtime.
171
- // This could result in nodes failing to register to the new cluster.
172
- log .FromContext (ctx ).Error (err , "Failed to get default nodepool id" )
173
- }
174
169
reqPara := & ackclient.DescribeClusterAttachScriptsRequest {
175
170
KeepInstanceName : tea .Bool (true ),
176
- NodepoolId : nodepoolID ,
177
171
}
178
172
resp , err := p .ackClient .DescribeClusterAttachScripts (tea .String (p .clusterID ), reqPara )
179
173
if err != nil {
180
174
log .FromContext (ctx ).Error (err , "Failed to get node registration script" )
181
175
return "" , err
182
176
}
183
- s := tea .StringValue (resp .Body )
184
- if s == "" {
177
+ respStr := tea .StringValue (resp .Body )
178
+ if respStr == "" {
185
179
err := errors .New ("empty node registration script" )
186
180
log .FromContext (ctx ).Error (err , "" )
187
181
return "" , err
188
182
}
183
+ respStr = strings .ReplaceAll (respStr , "\r \n " , "" )
184
+
185
+ runtime , runtimeVersion , err := p .getClusterAttachRuntimeConfiguration (ctx )
186
+ if err != nil {
187
+ // If error happen, we do nothing here
188
+ log .FromContext (ctx ).Error (err , "Failed to get cluster attach runtime configuration" )
189
+ } else {
190
+ // Replace the runtime and runtime-version parameter
191
+ respStr = regexp .MustCompile (`\s+--runtime\s+\S+\s+--runtime-version\s+\S+` ).ReplaceAllString (
192
+ respStr , fmt .Sprintf (" --runtime %s --runtime-version %s" , runtime , runtimeVersion ))
193
+ }
189
194
190
- p .cache .SetDefault (p .clusterID , s )
191
- return p .resolveUserData (s , labels , nodeClaim , kubeletCfg , userData ), nil
195
+ p .cache .SetDefault (p .clusterID , respStr )
196
+ return p .resolveUserData (respStr , labels , nodeClaim , kubeletCfg , userData ), nil
192
197
}
193
198
194
199
func (p * DefaultProvider ) resolveUserData (respStr string , labels map [string ]string , nodeClaim * karpv1.NodeClaim ,
@@ -207,8 +212,7 @@ func (p *DefaultProvider) resolveUserData(respStr string, labels map[string]stri
207
212
}
208
213
209
214
// Clean up the input string
210
- cleanupStr := strings .ReplaceAll (respStr , "\r \n " , "" )
211
- script .WriteString (cleanupStr + " " )
215
+ script .WriteString (respStr + " " )
212
216
// Add labels
213
217
script .WriteString (fmt .Sprintf ("--labels %s " , p .formatLabels (labels )))
214
218
// Add kubelet config
0 commit comments