diff --git a/pkg/api/v1/pod/util.go b/pkg/api/v1/pod/util.go index b3490add6b8..2db2b195c17 100644 --- a/pkg/api/v1/pod/util.go +++ b/pkg/api/v1/pod/util.go @@ -428,3 +428,21 @@ func GetPodObservedGenerationIfEnabled(pod *v1.Pod) int64 { } return 0 } + +// We will emit condition.observedGeneration if the feature is enabled OR if condition.observedGeneration is already set. +// This protects against an infinite loop of kubelet trying to clear the value after the FG is turned off, and +// the API server preserving existing values when an incoming update tries to clear it. +func GetPodObservedGenerationIfEnabledOnCondition(pod *v1.Pod, conditionType v1.PodConditionType) int64 { + if pod == nil { + return 0 + } + if utilfeature.DefaultFeatureGate.Enabled(features.PodObservedGenerationTracking) { + return pod.Generation + } + for _, condition := range pod.Status.Conditions { + if condition.Type == conditionType && condition.ObservedGeneration != 0 { + return pod.Generation + } + } + return 0 +} diff --git a/pkg/controller/disruption/disruption.go b/pkg/controller/disruption/disruption.go index edaff5af2bd..7fb6a54085f 100644 --- a/pkg/controller/disruption/disruption.go +++ b/pkg/controller/disruption/disruption.go @@ -787,8 +787,9 @@ func (dc *DisruptionController) syncStalePodDisruption(ctx context.Context, key newPod := pod.DeepCopy() updated := apipod.UpdatePodCondition(&newPod.Status, &v1.PodCondition{ - Type: v1.DisruptionTarget, - Status: v1.ConditionFalse, + Type: v1.DisruptionTarget, + ObservedGeneration: apipod.GetPodObservedGenerationIfEnabledOnCondition(newPod, v1.DisruptionTarget), + Status: v1.ConditionFalse, }) if !updated { return nil diff --git a/pkg/controller/podgc/gc_controller.go b/pkg/controller/podgc/gc_controller.go index 7502675c7a0..6a9d8020c12 100644 --- a/pkg/controller/podgc/gc_controller.go +++ b/pkg/controller/podgc/gc_controller.go @@ -246,10 +246,11 @@ func (gcc *PodGCController) gcOrphaned(ctx context.Context, pods []*v1.Pod, node } logger.V(2).Info("Found orphaned Pod assigned to the Node, deleting", "pod", klog.KObj(pod), "node", klog.KRef("", pod.Spec.NodeName)) condition := &v1.PodCondition{ - Type: v1.DisruptionTarget, - Status: v1.ConditionTrue, - Reason: "DeletionByPodGC", - Message: "PodGC: node no longer exists", + Type: v1.DisruptionTarget, + ObservedGeneration: apipod.GetPodObservedGenerationIfEnabledOnCondition(pod, v1.DisruptionTarget), + Status: v1.ConditionTrue, + Reason: "DeletionByPodGC", + Message: "PodGC: node no longer exists", } if err := gcc.markFailedAndDeletePodWithCondition(ctx, pod, condition); err != nil { utilruntime.HandleError(err) diff --git a/pkg/controller/tainteviction/taint_eviction.go b/pkg/controller/tainteviction/taint_eviction.go index 48ab6f0ec51..aeae369534a 100644 --- a/pkg/controller/tainteviction/taint_eviction.go +++ b/pkg/controller/tainteviction/taint_eviction.go @@ -133,10 +133,11 @@ func addConditionAndDeletePod(ctx context.Context, c clientset.Interface, name, } newStatus := pod.Status.DeepCopy() updated := apipod.UpdatePodCondition(newStatus, &v1.PodCondition{ - Type: v1.DisruptionTarget, - Status: v1.ConditionTrue, - Reason: "DeletionByTaintManager", - Message: "Taint manager: deleting due to NoExecute taint", + Type: v1.DisruptionTarget, + ObservedGeneration: apipod.GetPodObservedGenerationIfEnabledOnCondition(pod, v1.DisruptionTarget), + Status: v1.ConditionTrue, + Reason: "DeletionByTaintManager", + Message: "Taint manager: deleting due to NoExecute taint", }) if updated { if _, _, _, err := utilpod.PatchPodStatus(ctx, c, pod.Namespace, pod.Name, pod.UID, pod.Status, *newStatus); err != nil {