From 1889e0cad652f3cff9d1b645c397633fef10054a Mon Sep 17 00:00:00 2001 From: Natasha Sarkar Date: Mon, 10 Mar 2025 16:34:07 +0000 Subject: [PATCH] scheduler sets observedGeneration in pod conditions --- .../framework/preemption/preemption.go | 9 +++++---- pkg/scheduler/schedule_one.go | 9 +++++---- test/e2e/node/pods.go | 20 +++++++++++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pkg/scheduler/framework/preemption/preemption.go b/pkg/scheduler/framework/preemption/preemption.go index 070b6ef3672..d23c18595cc 100644 --- a/pkg/scheduler/framework/preemption/preemption.go +++ b/pkg/scheduler/framework/preemption/preemption.go @@ -171,10 +171,11 @@ func NewEvaluator(pluginName string, fh framework.Handle, i Interface, enableAsy logger.V(2).Info("Preemptor pod rejected a waiting pod", "preemptor", klog.KObj(preemptor), "waitingPod", klog.KObj(victim), "node", c.Name()) } else { condition := &v1.PodCondition{ - Type: v1.DisruptionTarget, - Status: v1.ConditionTrue, - Reason: v1.PodReasonPreemptionByScheduler, - Message: fmt.Sprintf("%s: preempting to accommodate a higher priority pod", preemptor.Spec.SchedulerName), + Type: v1.DisruptionTarget, + ObservedGeneration: apipod.GetPodObservedGenerationIfEnabledOnCondition(&victim.Status, victim.Generation, v1.DisruptionTarget), + Status: v1.ConditionTrue, + Reason: v1.PodReasonPreemptionByScheduler, + Message: fmt.Sprintf("%s: preempting to accommodate a higher priority pod", preemptor.Spec.SchedulerName), } newStatus := victim.Status.DeepCopy() updated := apipod.UpdatePodCondition(newStatus, condition) diff --git a/pkg/scheduler/schedule_one.go b/pkg/scheduler/schedule_one.go index 12a943e1c35..5078470b974 100644 --- a/pkg/scheduler/schedule_one.go +++ b/pkg/scheduler/schedule_one.go @@ -1097,10 +1097,11 @@ func (sched *Scheduler) handleSchedulingFailure(ctx context.Context, fwk framewo msg := truncateMessage(errMsg) fwk.EventRecorder().Eventf(pod, nil, v1.EventTypeWarning, "FailedScheduling", "Scheduling", msg) if err := updatePod(ctx, sched.client, pod, &v1.PodCondition{ - Type: v1.PodScheduled, - Status: v1.ConditionFalse, - Reason: reason, - Message: errMsg, + Type: v1.PodScheduled, + ObservedGeneration: podutil.GetPodObservedGenerationIfEnabledOnCondition(&pod.Status, pod.Generation, v1.PodScheduled), + Status: v1.ConditionFalse, + Reason: reason, + Message: errMsg, }, nominatingInfo); err != nil { logger.Error(err, "Error updating pod", "pod", klog.KObj(pod)) } diff --git a/test/e2e/node/pods.go b/test/e2e/node/pods.go index 76eb3186a0e..477e401244b 100644 --- a/test/e2e/node/pods.go +++ b/test/e2e/node/pods.go @@ -529,7 +529,7 @@ var _ = SIGDescribe("Pods Extended (pod generation)", feature.PodObservedGenerat } }) - ginkgo.It("custom-set generation on new pods should be overwritten to 1", func(ctx context.Context) { + ginkgo.It("custom-set generation on new pods and graceful delete", func(ctx context.Context) { ginkgo.By("creating the pod") name := "pod-generation-" + string(uuid.NewUUID()) value := strconv.Itoa(time.Now().Nanosecond()) @@ -598,6 +598,10 @@ var _ = SIGDescribe("Pods Extended (pod generation)", feature.PodObservedGenerat gomega.Expect(pod.Generation).To(gomega.BeEquivalentTo(expectedPodGeneration)) }) + // This is the same test as https://github.com/kubernetes/kubernetes/blob/aa08c90fca8d30038d3f05c0e8f127b540b40289/test/e2e/node/pod_admission.go#L35, + // except that this verifies the pod generation and observedGeneration, which is + // currently behind a feature gate. When we GA observedGeneration functionality, + // we can fold these tests together into one. ginkgo.It("pod rejected by kubelet should have updated generation and observedGeneration", func(ctx context.Context) { node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) framework.ExpectNoError(err, "Failed to get a ready schedulable node") @@ -631,6 +635,18 @@ var _ = SIGDescribe("Pods Extended (pod generation)", feature.PodObservedGenerat return podClient.Delete(ctx, pod.Name, metav1.DeleteOptions{}) }) + // Wait for the scheduler to update the pod status + err = e2epod.WaitForPodNameUnschedulableInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace) + framework.ExpectNoError(err) + + // Fetch the pod to verify that the scheduler has set the PodScheduled condition + // with observedGeneration. + pod, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{}) + framework.ExpectNoError(err) + gomega.Expect(len(pod.Status.Conditions)).To(gomega.BeEquivalentTo(1)) + gomega.Expect(pod.Status.Conditions[0].Type).To(gomega.BeEquivalentTo(v1.PodScheduled)) + gomega.Expect(pod.Status.Conditions[0].ObservedGeneration).To(gomega.BeEquivalentTo(1)) + // Force assign the Pod to a node in order to get rejection status. binding := &v1.Binding{ ObjectMeta: metav1.ObjectMeta{ @@ -650,7 +666,7 @@ var _ = SIGDescribe("Pods Extended (pod generation)", feature.PodObservedGenerat framework.ExpectNoError(err) // Fetch the rejected Pod and verify the generation and observedGeneration. - gotPod, err := f.ClientSet.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{}) + gotPod, err := podClient.Get(ctx, pod.Name, metav1.GetOptions{}) framework.ExpectNoError(err) gomega.Expect(gotPod.Generation).To(gomega.BeEquivalentTo(1)) gomega.Expect(gotPod.Status.ObservedGeneration).To(gomega.BeEquivalentTo(1))