Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,8 @@ func (r *KarpenterIgnitionReconciler) Reconcile(ctx context.Context, req ctrl.Re
return ctrl.Result{}, fmt.Errorf("failed to get HostedCluster: %w", err)
}

releaseImage, version, requeue := currentClusterRelease(hostedCluster)
if requeue && openshiftEC2NodeClass.Spec.Version == "" {
log.Info("No version history available for unpinned NodeClass, requeueing")
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
}
releaseImage := hcp.Spec.ReleaseImage
version := currentClusterVersion(hostedCluster)

// When the user requests a version for the OpenshiftEC2NodeClass we perform further validation and lookup of the release image.
// We only detect skew and if the version is valid in this case, as under normal circumstances we can assume the release image
Expand Down Expand Up @@ -345,18 +342,14 @@ func (r *KarpenterIgnitionReconciler) createInMemoryNodePool(
}
}

// currentClusterRelease returns the release image and version of the most recently
// completed update from the HostedCluster's version history, plus a requeue flag.
// It searches history entries for the one with state=Completed and the most recent
// CompletionTime. If no completed entries exist, it falls back to the first Partial
// entry (the version currently being rolled out). If no history entries exist at all,
// it signals the caller to requeue.
// This ensures that during a control plane upgrade, unpinned Karpenter NodeClaims use the
// completed version's release image rather than the desired version, preventing premature
// drift detection and worker node replacement before the control plane is ready.
func currentClusterRelease(hostedCluster *hyperv1.HostedCluster) (string, string, bool) {
// currentClusterVersion returns the version of the most recently completed update from the
// HostedCluster's version history. It searches history entries for the one with state=Completed
// and the most recent CompletionTime. If no completed entries exist and there is exactly one
// history entry, it falls back to the desired version. This handles the case where a cluster
// is still rolling out its initial version.
func currentClusterVersion(hostedCluster *hyperv1.HostedCluster) string {
if hostedCluster.Status.Version == nil {
return hostedCluster.Spec.Release.Image, "", false
return ""
}

var latest *configv1.UpdateHistory
Expand All @@ -369,33 +362,22 @@ func currentClusterRelease(hostedCluster *hyperv1.HostedCluster) (string, string
latest = entry
continue
}
switch {
case entry.CompletionTime == nil:
// keep latest; nothing to compare against
case latest.CompletionTime == nil:
latest = entry
case entry.CompletionTime.After(latest.CompletionTime.Time):
if entry.CompletionTime != nil && latest.CompletionTime != nil && entry.CompletionTime.After(latest.CompletionTime.Time) {
latest = entry
}
}

if latest != nil {
return latest.Image, latest.Version, false
return latest.Version
}

// No completed entries: fall back to the first Partial entry if available.
// CVO prepends newest entries, so the first Partial is the currently rolling-out version.
// This is safer than Spec.Release.Image which flips to the new (desired) version
// immediately on upgrade request.
for i := range hostedCluster.Status.Version.History {
entry := &hostedCluster.Status.Version.History[i]
if entry.State == configv1.PartialUpdate {
return entry.Image, entry.Version, false
}
// If there are no completed entries but exactly one history entry exists, the cluster
// is likely still rolling out its first version. Fall back to the desired version.
if len(hostedCluster.Status.Version.History) == 1 {
return hostedCluster.Status.Version.Desired.Version
}

// Empty history: signal caller to requeue — CVO hasn't reported any version yet.
return "", "", true
return hostedCluster.Status.Version.Desired.Version
}

// validateVersion checks whether the requested version is valid for the given HostedCluster.
Expand Down
Loading