Skip to content

Commit ad3da77

Browse files
Merge pull request #8434 from PoornimaSingour/OCPBUGS-84308
OCPBUGS-84308: fix(cpo) delete terminated MCD pods to retry in-place upgrades
2 parents a101e66 + 9092cca commit ad3da77

2 files changed

Lines changed: 567 additions & 32 deletions

File tree

control-plane-operator/hostedclusterconfigoperator/controllers/inplaceupgrader/inplaceupgrader.go

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
1010
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests"
1111
"github.com/openshift/hypershift/support/globalconfig"
12+
"github.com/openshift/hypershift/support/k8sutil"
1213
"github.com/openshift/hypershift/support/releaseinfo"
1314
"github.com/openshift/hypershift/support/upsert"
1415

@@ -58,6 +59,11 @@ const (
5859
TokenSecretPayloadKey = "payload"
5960
TokenSecretReleaseKey = "release"
6061
TokenSecretReleaseVersionKey = "release-version"
62+
63+
// upgradeRequeueInterval is how often the controller rechecks while an
64+
// upgrade is in progress, closing the gap when a force-deleted pod's
65+
// deletion event is missed.
66+
upgradeRequeueInterval = 30 * time.Second
6167
)
6268

6369
type Reconciler struct {
@@ -146,7 +152,14 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
146152
return ctrl.Result{}, fmt.Errorf("token secret %s/%s is missing %q key", tokenSecret.Namespace, tokenSecret.Name, TokenSecretReleaseVersionKey)
147153
}
148154

149-
return ctrl.Result{}, r.reconcileInPlaceUpgrade(ctx, nodePoolUpgradeAPI, tokenSecret, mcoImage, releaseVersion)
155+
if err := r.reconcileInPlaceUpgrade(ctx, nodePoolUpgradeAPI, tokenSecret, mcoImage, releaseVersion); err != nil {
156+
return ctrl.Result{}, err
157+
}
158+
// Requeue periodically while an upgrade is in progress. The controller only
159+
// watches Nodes and MachineSets, so if an upgrade pod is force-deleted the
160+
// deletion event is missed and the replacement pod is never created. A
161+
// periodic recheck closes that gap.
162+
return ctrl.Result{RequeueAfter: upgradeRequeueInterval}, nil
150163
}
151164

152165
type nodePoolUpgradeAPI struct {
@@ -276,7 +289,7 @@ func (r *Reconciler) reconcileInPlaceUpgrade(ctx context.Context, nodePoolUpgrad
276289

277290
err = r.reconcileUpgradePods(ctx, r.guestClusterClient, nodes, nodePoolUpgradeAPI.spec.poolRef.GetName(), mcoImage, nodePoolUpgradeAPI.proxy)
278291
if err != nil {
279-
return fmt.Errorf("failed to delete idle upgrade pods: %w", err)
292+
return fmt.Errorf("failed to reconcile upgrade pods: %w", err)
280293
}
281294
return nil
282295
}
@@ -311,24 +324,14 @@ func (r *Reconciler) reconcileUpgradePods(ctx context.Context, hostedClusterClie
311324
pod := inPlaceUpgradePod(namespace.Name, node.Name)
312325

313326
if node.Annotations[CurrentMachineConfigAnnotationKey] == node.Annotations[DesiredMachineConfigAnnotationKey] &&
314-
node.Annotations[DesiredDrainerAnnotationKey] == node.Annotations[LastAppliedDrainerAnnotationKey] {
327+
node.Annotations[DesiredDrainerAnnotationKey] == node.Annotations[LastAppliedDrainerAnnotationKey] &&
328+
node.Annotations[MachineConfigDaemonStateAnnotationKey] == MachineConfigDaemonStateDone {
315329
// the node is updated and does not require a MCD running
316-
if err := hostedClusterClient.Get(ctx, client.ObjectKeyFromObject(pod), pod); err != nil {
317-
if apierrors.IsNotFound(err) {
318-
continue
319-
}
320-
return fmt.Errorf("error getting upgrade MCD pod: %w", err)
321-
}
322-
if pod.DeletionTimestamp != nil {
323-
continue
324-
}
325-
if err := hostedClusterClient.Delete(ctx, pod); err != nil {
326-
if apierrors.IsNotFound(err) {
327-
continue
328-
}
329-
return fmt.Errorf("error deleting upgrade MCD pod: %w", err)
330+
if existed, err := k8sutil.DeleteIfNeeded(ctx, hostedClusterClient, pod); err != nil {
331+
return err
332+
} else if existed {
333+
log.Info("Deleted idle upgrade pod")
330334
}
331-
log.Info("Deleted idle upgrade pod")
332335
} else {
333336
if err := hostedClusterClient.Get(ctx, types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}, pod); err != nil {
334337
if !apierrors.IsNotFound(err) {
@@ -348,6 +351,19 @@ func (r *Reconciler) reconcileUpgradePods(ctx context.Context, hostedClusterClie
348351
} else {
349352
log.Info("create upgrade pod", "result", result)
350353
}
354+
// A pod with RestartPolicy=OnFailure only reaches a terminal phase after kubelet has exhausted its restart attempts (e.g. eviction or node loss), so deleting it here does not interrupt an active retry.
355+
} else if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed {
356+
if pod.DeletionTimestamp != nil {
357+
continue
358+
}
359+
log.Info("Detected terminated upgrade pod on node that still needs upgrade, deleting for retry",
360+
"node", node.Name, "podPhase", pod.Status.Phase)
361+
if err := hostedClusterClient.Delete(ctx, pod); err != nil {
362+
if apierrors.IsNotFound(err) {
363+
continue
364+
}
365+
return fmt.Errorf("error deleting terminated upgrade MCD pod for node %s: %w", node.Name, err)
366+
}
351367
}
352368
}
353369
}
@@ -483,20 +499,8 @@ func deleteUpgradeManifests(ctx context.Context, hostedClusterClient client.Clie
483499
namespace := inPlaceUpgradeNamespace(poolName)
484500
for _, node := range nodes {
485501
pod := inPlaceUpgradePod(namespace.Name, node.Name)
486-
if err := hostedClusterClient.Get(ctx, client.ObjectKeyFromObject(pod), pod); err != nil {
487-
if apierrors.IsNotFound(err) {
488-
continue
489-
}
490-
return fmt.Errorf("error getting upgrade MCD pod: %w", err)
491-
}
492-
if pod.DeletionTimestamp != nil {
493-
continue
494-
}
495-
if err := hostedClusterClient.Delete(ctx, pod); err != nil {
496-
if apierrors.IsNotFound(err) {
497-
continue
498-
}
499-
return fmt.Errorf("error deleting upgrade MCD pod: %w", err)
502+
if _, err := k8sutil.DeleteIfNeeded(ctx, hostedClusterClient, pod); err != nil {
503+
return err
500504
}
501505
}
502506
return nil

0 commit comments

Comments
 (0)