|
9 | 9 | "net" |
10 | 10 | "strconv" |
11 | 11 | "strings" |
| 12 | + "time" |
12 | 13 |
|
13 | 14 | "github.com/asaskevich/govalidator" |
14 | 15 | "github.com/vultr/govultr/v3" |
@@ -186,7 +187,7 @@ func (l *loadbalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri |
186 | 187 | // Check if creation is disabled |
187 | 188 | if create, ok := service.Annotations[annoVultrLoadBalancerCreate]; ok { |
188 | 189 | if strings.EqualFold(create, "false") { |
189 | | - return nil, fmt.Errorf("%s set to %s - load balancer will not be created", annoVultrLoadBalancerCreate, create) |
| 190 | + return nil, cloudprovider.ImplementedElsewhere |
190 | 191 | } |
191 | 192 | } |
192 | 193 |
|
@@ -216,12 +217,18 @@ func (l *loadbalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri |
216 | 217 | return nil, fmt.Errorf("load-balancer is not yet active - current status: %s", lb.Status) |
217 | 218 | } |
218 | 219 |
|
219 | | - // Update load balancer configuration (pass the lb to avoid another API call) |
220 | 220 | if updateErr := l.updateLoadBalancerWithLB(ctx, clusterName, service, nodes, lb); updateErr != nil { |
| 221 | + if isLBActivating(updateErr) { |
| 222 | + ingress := l.buildLoadBalancerIngress(service, lb) |
| 223 | + if len(ingress) > 0 { |
| 224 | + klog.V(2).Infof("LB %s update deferred: nodes still activating; returning current ingress and retrying in background", lb.ID) |
| 225 | + l.retryLBUpdateAsync(ctx, lb.ID, clusterName, service, nodes) |
| 226 | + return &v1.LoadBalancerStatus{Ingress: ingress}, nil |
| 227 | + } |
| 228 | + } |
221 | 229 | return nil, updateErr |
222 | 230 | } |
223 | 231 |
|
224 | | - // Build and return status from the lb we already have |
225 | 232 | ingress := l.buildLoadBalancerIngress(service, lb) |
226 | 233 | return &v1.LoadBalancerStatus{ |
227 | 234 | Ingress: ingress, |
@@ -1250,3 +1257,58 @@ func checkEnabledIPv6(service *v1.Service) bool { |
1250 | 1257 |
|
1251 | 1258 | return false |
1252 | 1259 | } |
| 1260 | + |
| 1261 | +func isLBActivating(err error) bool { |
| 1262 | + if err == nil { |
| 1263 | + return false |
| 1264 | + } |
| 1265 | + msg := strings.ToLower(err.Error()) |
| 1266 | + return strings.Contains(msg, "still activating") || |
| 1267 | + strings.Contains(msg, "activation in progress") || |
| 1268 | + strings.Contains(msg, "activating") |
| 1269 | +} |
| 1270 | + |
| 1271 | +func (l *loadbalancers) retryLBUpdateAsync(ctx context.Context, lbID, clusterName string, service *v1.Service, nodes []*v1.Node) { |
| 1272 | + bgCtx, cancel := context.WithTimeout(ctx, 10*time.Minute) |
| 1273 | + |
| 1274 | + go func() { |
| 1275 | + defer cancel() |
| 1276 | + |
| 1277 | + backoffs := []time.Duration{ |
| 1278 | + 2 * time.Second, |
| 1279 | + 3 * time.Second, |
| 1280 | + 5 * time.Second, |
| 1281 | + 8 * time.Second, |
| 1282 | + 13 * time.Second, |
| 1283 | + 21 * time.Second, |
| 1284 | + 34 * time.Second, |
| 1285 | + } |
| 1286 | + |
| 1287 | + for _, d := range backoffs { |
| 1288 | + select { |
| 1289 | + case <-bgCtx.Done(): |
| 1290 | + klog.V(3).Infof("Background LB %s update canceled/expired: %v", lbID, bgCtx.Err()) |
| 1291 | + return |
| 1292 | + case <-time.After(d): |
| 1293 | + } |
| 1294 | + |
| 1295 | + lb, getErr := l.getVultrLB(bgCtx, service) |
| 1296 | + if getErr != nil { |
| 1297 | + klog.V(4).Infof("Background LB %s: getVultrLB failed, will retry: %v", lbID, getErr) |
| 1298 | + continue |
| 1299 | + } |
| 1300 | + |
| 1301 | + if err := l.updateLoadBalancerWithLB(bgCtx, clusterName, service, nodes, lb); err != nil { |
| 1302 | + if isLBActivating(err) { |
| 1303 | + klog.V(4).Infof("Background LB %s update still activating, will retry: %v", lbID, err) |
| 1304 | + continue |
| 1305 | + } |
| 1306 | + klog.V(3).Infof("Background LB %s update stopped (non-activating error): %v", lbID, err) |
| 1307 | + return |
| 1308 | + } |
| 1309 | + |
| 1310 | + klog.V(2).Infof("Background LB %s update finalized after activation", lbID) |
| 1311 | + return |
| 1312 | + } |
| 1313 | + }() |
| 1314 | +} |
0 commit comments