Merge pull request #135325 from brejman/issue-134393

Fix queue hint for inter-pod anti-affinity
This commit is contained in:
Kubernetes Prow Robot 2025-12-17 20:01:02 -08:00 committed by GitHub
commit 285eb9fdba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 86 additions and 8 deletions

View file

@ -220,15 +220,27 @@ func (pl *InterPodAffinity) isSchedulableAfterPodChange(logger klog.Logger, pod
return fwk.QueueSkip, nil
}
// Pod is deleted. Return Queue when the deleted pod matching the target pod's anti-affinity.
if !podMatchesAllAffinityTerms(antiTerms, originalPod) {
logger.V(5).Info("a scheduled pod was deleted but it doesn't match the target pod's anti-affinity",
"pod", klog.KObj(pod), "modifiedPod", klog.KObj(modifiedPod))
return fwk.QueueSkip, nil
// Pod is deleted. Return Queue when the deleted pod matches the target pod's anti-affinity or vice versa.
if podMatchesAllAffinityTerms(antiTerms, originalPod) {
logger.V(5).Info("a scheduled pod was deleted and it matches the target pod's anti-affinity. The pod may be schedulable now",
"pod", klog.KObj(pod), "originalPod", klog.KObj(originalPod))
return fwk.Queue, nil
}
logger.V(5).Info("a scheduled pod was deleted and it matches the target pod's anti-affinity. The pod may be schedulable now",
"pod", klog.KObj(pod), "modifiedPod", klog.KObj(modifiedPod))
return fwk.Queue, nil
originalPodAntiTerms, err := fwk.GetAffinityTerms(originalPod, fwk.GetPodAntiAffinityTerms(originalPod.Spec.Affinity))
if err != nil {
return fwk.Queue, err
}
if podMatchesAllAffinityTerms(originalPodAntiTerms, pod) {
logger.V(5).Info("a scheduled pod was deleted and the target pod matches the deleted pod's anti-affinity. The pod may be schedulable now",
"pod", klog.KObj(pod), "originalPod", klog.KObj(originalPod))
return fwk.Queue, nil
}
logger.V(5).Info("a scheduled pod was deleted but it doesn't match the target pod's anti-affinity, nor vice versa",
"pod", klog.KObj(pod), "originalPod", klog.KObj(originalPod))
return fwk.QueueSkip, nil
}
func (pl *InterPodAffinity) isSchedulableAfterNodeChange(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (fwk.QueueingHint, error) {

View file

@ -129,6 +129,24 @@ func Test_isSchedulableAfterPodChange(t *testing.T) {
oldPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(),
expectedHint: fwk.Queue,
},
{
name: "delete a pod with anti-affinity that matches pending pod",
pod: st.MakePod().Name("p").Label("service", "securityscan").Obj(),
oldPod: st.MakePod().Node("fake-node").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
expectedHint: fwk.Queue,
},
{
name: "delete a pod with anti-affinity that doesn't match pending pod",
pod: st.MakePod().Name("p").Label("service", "foo").Obj(),
oldPod: st.MakePod().Node("fake-node").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
expectedHint: fwk.QueueSkip,
},
{
name: "delete a pod which doesn't match pending pod's anti-affinity and has anti-affinity that doesn't match pending pod",
pod: st.MakePod().Name("p").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Label("service", "foo").Obj(),
oldPod: st.MakePod().Node("fake-node").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Label("service", "foo").Obj(),
expectedHint: fwk.QueueSkip,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {

View file

@ -682,6 +682,54 @@ var CoreResourceEnqueueTestCases = []*CoreResourceEnqueueTestCase{
WantRequeuedPods: sets.New("pod2"),
EnableSchedulingQueueHint: sets.New(true),
},
{
Name: "Pod rejected by the InterPodAffinity plugin is requeued when deleting the existing pod that matches the target podAntiAffinity",
EnablePlugins: []string{names.InterPodAffinity},
InitialNodes: []*v1.Node{st.MakeNode().Name("fake-node").Label("node", "fake-node").Obj()},
InitialPods: []*v1.Pod{
st.MakePod().Name("pod1").Label("anti1", "anti1").Container("image").Node("fake-node").Obj(),
st.MakePod().Name("pod2").Label("anti2", "anti2").Container("image").Node("fake-node").Obj(),
},
Pods: []*v1.Pod{
// - Pod3 and pod4 will be rejected by the PodAffinity plugin.
st.MakePod().Name("pod3").Label("anti1", "anti1").PodAntiAffinityExists("anti1", "node", st.PodAntiAffinityWithRequiredReq).Container("image").Obj(),
st.MakePod().Name("pod4").Label("anti2", "anti2").PodAntiAffinityExists("anti2", "node", st.PodAntiAffinityWithRequiredReq).Container("image").Obj(),
},
TriggerFn: func(testCtx *testutils.TestContext) (map[fwk.ClusterEvent]uint64, error) {
// Delete pod1 which will make pod3 schedulable because pod3's antiAffinity won't match pod1 anymore.
if err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Delete(testCtx.Ctx, "pod1", metav1.DeleteOptions{GracePeriodSeconds: new(int64)}); err != nil {
return nil, fmt.Errorf("failed to delete pod1: %w", err)
}
return map[fwk.ClusterEvent]uint64{{Resource: assignedPod, ActionType: fwk.Delete}: 1}, nil
},
WantRequeuedPods: sets.New("pod3"),
EnableSchedulingQueueHint: sets.New(true),
},
{
Name: "Pod rejected by the InterPodAffinity plugin is requeued when deleting the existing pod with podAntiAffinity that matches the target pod",
EnablePlugins: []string{names.InterPodAffinity},
InitialNodes: []*v1.Node{st.MakeNode().Name("fake-node").Label("node", "fake-node").Obj()},
InitialPods: []*v1.Pod{
st.MakePod().Name("pod1").PodAntiAffinityExists("anti1", "node", st.PodAntiAffinityWithRequiredReq).Container("image").Node("fake-node").Obj(),
st.MakePod().Name("pod2").PodAntiAffinityExists("anti2", "node", st.PodAntiAffinityWithRequiredReq).Container("image").Node("fake-node").Obj(),
},
Pods: []*v1.Pod{
// - Pod3 and pod4 will be rejected by the PodAffinity plugin.
st.MakePod().Name("pod3").Label("anti1", "anti1").Container("image").Obj(),
st.MakePod().Name("pod4").Label("anti2", "anti2").Container("image").Obj(),
},
TriggerFn: func(testCtx *testutils.TestContext) (map[fwk.ClusterEvent]uint64, error) {
// Delete pod1 which will make pod3 schedulable because pod1's antiAffinity won't match pod3 anymore.
if err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Delete(testCtx.Ctx, "pod1", metav1.DeleteOptions{GracePeriodSeconds: new(int64)}); err != nil {
return nil, fmt.Errorf("failed to delete pod1: %w", err)
}
return map[fwk.ClusterEvent]uint64{{Resource: assignedPod, ActionType: fwk.Delete}: 1}, nil
},
WantRequeuedPods: sets.New("pod3"),
EnableSchedulingQueueHint: sets.New(true),
},
{
Name: "Pod rejected by the PodAffinity plugin is requeued when updating the existed pod's label to make it match the pod's podAffinity",
EnablePlugins: []string{names.InterPodAffinity},