Merge pull request #137662 from tosi3k/revert-136254-extend-postfilter

Revert "Extend PostFilterResult with a list of victim Pods"
This commit is contained in:
Kubernetes Prow Robot
2026-03-12 17:33:42 +05:30
committed by GitHub
4 changed files with 93 additions and 133 deletions

View File

@@ -289,20 +289,11 @@ type Framework interface {
Close() error
}
// NewPostFilterResult creates PostFilterResult with a provided nominated node
// and a list of victims (if any) that need to be preempted to make the pod schedulable.
func NewPostFilterResult(nodeName string, victims []*v1.Pod) *fwk.PostFilterResult {
func NewPostFilterResultWithNominatedNode(name string) *fwk.PostFilterResult {
return &fwk.PostFilterResult{
NominatingInfo: &fwk.NominatingInfo{
NominatedNodeName: nodeName,
NominatedNodeName: name,
NominatingMode: fwk.ModeOverride,
},
Victims: victims,
}
}
// Deprecated: use NewPostFilterResult instead, even if no victims were identified.
// Deprecated in Kubernetes 1.36 and will be removed in 1.38.
func NewPostFilterResultWithNominatedNode(nodeName string) *fwk.PostFilterResult {
return NewPostFilterResult(nodeName, nil)
}

View File

@@ -52,12 +52,12 @@ import (
apipod "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/scheduler/apis/config"
configv1 "k8s.io/kubernetes/pkg/scheduler/apis/config/v1"
apicache "k8s.io/kubernetes/pkg/scheduler/backend/api_cache"
apidispatcher "k8s.io/kubernetes/pkg/scheduler/backend/api_dispatcher"
"k8s.io/kubernetes/pkg/scheduler/backend/api_cache"
"k8s.io/kubernetes/pkg/scheduler/backend/api_dispatcher"
internalcache "k8s.io/kubernetes/pkg/scheduler/backend/cache"
internalqueue "k8s.io/kubernetes/pkg/scheduler/backend/queue"
"k8s.io/kubernetes/pkg/scheduler/framework"
apicalls "k8s.io/kubernetes/pkg/scheduler/framework/api_calls"
"k8s.io/kubernetes/pkg/scheduler/framework/api_calls"
"k8s.io/kubernetes/pkg/scheduler/framework/parallelize"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
@@ -163,35 +163,19 @@ const (
LabelValueNonViolatingPDB = "non-violating"
)
func checkPostFilterResult(t *testing.T, result *fwk.PostFilterResult, expectedNode string, expectedVictims sets.Set[string]) {
if expectedNode != result.NominatedNodeName {
t.Errorf("expected NodeName %q, got %q", expectedNode, result.NominatedNodeName)
}
if len(result.Victims) != len(expectedVictims) {
t.Errorf("expected %d victims, got %d", len(expectedVictims), len(result.Victims))
}
for _, victim := range result.Victims {
if !expectedVictims.Has(victim.Name) {
t.Errorf("pod %v is not expected to be a victim.", victim.Name)
}
}
}
func TestPostFilter(t *testing.T) {
onePodRes := map[v1.ResourceName]string{v1.ResourcePods: "1"}
nodeRes := map[v1.ResourceName]string{v1.ResourceCPU: "200m", v1.ResourceMemory: "400"}
tests := []struct {
name string
pod *v1.Pod
pods []*v1.Pod
pdbs []*policy.PodDisruptionBudget
nodes []*v1.Node
filteredNodesStatuses *framework.NodeToStatus
extender fwk.Extender
wantNilPostFilterResult bool
expectedVictims sets.Set[string]
expectedNominatedNode string
wantStatus *fwk.Status
name string
pod *v1.Pod
pods []*v1.Pod
pdbs []*policy.PodDisruptionBudget
nodes []*v1.Node
filteredNodesStatuses *framework.NodeToStatus
extender fwk.Extender
wantResult *fwk.PostFilterResult
wantStatus *fwk.Status
}{
{
name: "pod with higher priority can be made schedulable",
@@ -205,9 +189,8 @@ func TestPostFilter(t *testing.T) {
filteredNodesStatuses: framework.NewNodeToStatus(map[string]*fwk.Status{
"node1": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "node1",
expectedVictims: sets.New("p1"),
wantStatus: fwk.NewStatus(fwk.Success),
wantResult: framework.NewPostFilterResultWithNominatedNode("node1"),
wantStatus: fwk.NewStatus(fwk.Success),
},
{
name: "pod with tied priority is still unschedulable",
@@ -221,9 +204,8 @@ func TestPostFilter(t *testing.T) {
filteredNodesStatuses: framework.NewNodeToStatus(map[string]*fwk.Status{
"node1": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "",
expectedVictims: nil,
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod."),
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod."),
},
{
name: "preemption should respect filteredNodesStatuses",
@@ -237,9 +219,8 @@ func TestPostFilter(t *testing.T) {
filteredNodesStatuses: framework.NewNodeToStatus(map[string]*fwk.Status{
"node1": fwk.NewStatus(fwk.UnschedulableAndUnresolvable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "",
expectedVictims: nil,
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling."),
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling."),
},
{
name: "preemption should respect absent NodeToStatusReader entry meaning UnschedulableAndUnresolvable",
@@ -251,8 +232,7 @@ func TestPostFilter(t *testing.T) {
st.MakeNode().Name("node1").Capacity(onePodRes).Obj(),
},
filteredNodesStatuses: framework.NewDefaultNodeToStatus(),
expectedNominatedNode: "",
expectedVictims: nil,
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling."),
},
{
@@ -270,9 +250,8 @@ func TestPostFilter(t *testing.T) {
"node1": fwk.NewStatus(fwk.Unschedulable),
"node2": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "node2",
expectedVictims: sets.New("p2"),
wantStatus: fwk.NewStatus(fwk.Success),
wantResult: framework.NewPostFilterResultWithNominatedNode("node2"),
wantStatus: fwk.NewStatus(fwk.Success),
},
{
name: "pod can be made schedulable on minHighestPriority node",
@@ -294,9 +273,8 @@ func TestPostFilter(t *testing.T) {
"node1": fwk.NewStatus(fwk.Unschedulable),
"node2": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "node2",
expectedVictims: sets.New("p3"),
wantStatus: fwk.NewStatus(fwk.Success),
wantResult: framework.NewPostFilterResultWithNominatedNode("node2"),
wantStatus: fwk.NewStatus(fwk.Success),
},
{
name: "preemption result filtered out by extenders",
@@ -317,9 +295,8 @@ func TestPostFilter(t *testing.T) {
ExtenderName: "FakeExtender1",
Predicates: []tf.FitPredicate{tf.Node1PredicateExtender},
},
expectedNominatedNode: "node1",
expectedVictims: sets.New("p1"),
wantStatus: fwk.NewStatus(fwk.Success),
wantResult: framework.NewPostFilterResultWithNominatedNode("node1"),
wantStatus: fwk.NewStatus(fwk.Success),
},
{
name: "no candidate nodes found, no enough resource after removing low priority pods",
@@ -336,9 +313,8 @@ func TestPostFilter(t *testing.T) {
"node1": fwk.NewStatus(fwk.Unschedulable),
"node2": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "",
expectedVictims: nil,
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/2 nodes are available: 2 Insufficient cpu."),
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/2 nodes are available: 2 Insufficient cpu."),
},
{
name: "no candidate nodes found with mixed reasons, no lower priority pod and no enough CPU resource",
@@ -358,9 +334,8 @@ func TestPostFilter(t *testing.T) {
"node2": fwk.NewStatus(fwk.Unschedulable),
"node3": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "",
expectedVictims: nil,
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/3 nodes are available: 1 Insufficient cpu, 2 No preemption victims found for incoming pod."),
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/3 nodes are available: 1 Insufficient cpu, 2 No preemption victims found for incoming pod."),
},
{
name: "no candidate nodes found with mixed reason, 2 UnschedulableAndUnresolvable nodes and 2 nodes don't have enough CPU resource",
@@ -380,9 +355,8 @@ func TestPostFilter(t *testing.T) {
"node2": fwk.NewStatus(fwk.Unschedulable),
"node4": fwk.NewStatus(fwk.UnschedulableAndUnresolvable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "",
expectedVictims: nil,
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/4 nodes are available: 2 Insufficient cpu, 2 Preemption is not helpful for scheduling."),
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
wantStatus: fwk.NewStatus(fwk.Unschedulable, "preemption: 0/4 nodes are available: 2 Insufficient cpu, 2 Preemption is not helpful for scheduling."),
},
{
name: "only one node but failed with TestPlugin",
@@ -395,8 +369,8 @@ func TestPostFilter(t *testing.T) {
filteredNodesStatuses: framework.NewNodeToStatus(map[string]*fwk.Status{
"node1": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
wantNilPostFilterResult: true,
wantStatus: fwk.AsStatus(errors.New("preemption: running RemovePod on PreFilter plugin \"test-plugin\": failed to remove pod: p")),
wantResult: nil,
wantStatus: fwk.AsStatus(errors.New("preemption: running RemovePod on PreFilter plugin \"test-plugin\": failed to remove pod: p")),
},
{
name: "one failed with TestPlugin and the other pass",
@@ -414,9 +388,8 @@ func TestPostFilter(t *testing.T) {
"node1": fwk.NewStatus(fwk.Unschedulable),
"node2": fwk.NewStatus(fwk.Unschedulable),
}, fwk.NewStatus(fwk.UnschedulableAndUnresolvable)),
expectedNominatedNode: "node2",
expectedVictims: sets.New("p2"),
wantStatus: fwk.NewStatus(fwk.Success),
wantResult: framework.NewPostFilterResultWithNominatedNode("node2"),
wantStatus: fwk.NewStatus(fwk.Success),
},
}
@@ -509,12 +482,8 @@ func TestPostFilter(t *testing.T) {
t.Errorf("Unexpected status (-want, +got):\n%s", diff)
}
}
if tt.wantNilPostFilterResult {
if gotResult != nil {
t.Errorf("expected nil PostFilterResult, got %v", gotResult)
}
} else {
checkPostFilterResult(t, gotResult, tt.expectedNominatedNode, tt.expectedVictims)
if diff := cmp.Diff(tt.wantResult, gotResult); diff != "" {
t.Errorf("Unexpected postFilterResult (-want, +got):\n%s", diff)
}
})
}
@@ -1977,15 +1946,14 @@ func TestPodEligibleToPreemptOthers(t *testing.T) {
func TestPreempt(t *testing.T) {
metrics.Register()
tests := []struct {
name string
pod *v1.Pod
pods []*v1.Pod
extenders []*tf.FakeExtender
nodeNames []string
registerPlugin tf.RegisterPluginFunc
wantNilPostFilterResult bool
expectedVictims sets.Set[string]
expectedNominatedNode string
name string
pod *v1.Pod
pods []*v1.Pod
extenders []*tf.FakeExtender
nodeNames []string
registerPlugin tf.RegisterPluginFunc
want *fwk.PostFilterResult
expectedPods []string // list of preempted pods
}{
{
name: "basic preemption logic",
@@ -1996,10 +1964,10 @@ func TestPreempt(t *testing.T) {
st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).Obj(),
st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).Obj(),
},
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
expectedVictims: sets.New("p1.1", "p1.2"),
expectedNominatedNode: "node1",
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: framework.NewPostFilterResultWithNominatedNode("node1"),
expectedPods: []string{"p1.1", "p1.2"},
},
{
name: "preemption for topology spread constraints",
@@ -2014,10 +1982,10 @@ func TestPreempt(t *testing.T) {
st.MakePod().Name("p-x1").UID("p-x1").Namespace(v1.NamespaceDefault).Node("node-x").Label("foo", "").Priority(highPriority).Obj(),
st.MakePod().Name("p-x2").UID("p-x2").Namespace(v1.NamespaceDefault).Node("node-x").Label("foo", "").Priority(highPriority).Obj(),
},
nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"},
registerPlugin: tf.RegisterPluginAsExtensions(podtopologyspread.Name, podTopologySpreadFunc, "PreFilter", "Filter"),
expectedVictims: sets.New("p-b1"),
expectedNominatedNode: "node-b",
nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"},
registerPlugin: tf.RegisterPluginAsExtensions(podtopologyspread.Name, podTopologySpreadFunc, "PreFilter", "Filter"),
want: framework.NewPostFilterResultWithNominatedNode("node-b"),
expectedPods: []string{"p-b1"},
},
{
name: "Scheduler extenders allow only node1, otherwise node3 would have been chosen",
@@ -2038,9 +2006,9 @@ func TestPreempt(t *testing.T) {
Predicates: []tf.FitPredicate{tf.Node1PredicateExtender},
},
},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
expectedVictims: sets.New("p1.1", "p1.2"),
expectedNominatedNode: "node1",
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: framework.NewPostFilterResultWithNominatedNode("node1"),
expectedPods: []string{"p1.1", "p1.2"},
},
{
name: "Scheduler extenders do not allow any preemption",
@@ -2057,8 +2025,9 @@ func TestPreempt(t *testing.T) {
Predicates: []tf.FitPredicate{tf.FalsePredicateExtender},
},
},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
wantNilPostFilterResult: true,
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: nil,
expectedPods: []string{},
},
{
name: "One scheduler extender allows only node1, the other returns error but ignorable. Only node1 would be chosen",
@@ -2080,9 +2049,9 @@ func TestPreempt(t *testing.T) {
ExtenderName: "FakeExtender2",
},
},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
expectedVictims: sets.New("p1.1", "p1.2"),
expectedNominatedNode: "node1",
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: framework.NewPostFilterResultWithNominatedNode("node1"),
expectedPods: []string{"p1.1", "p1.2"},
},
{
name: "One scheduler extender allows only node1, but it is not interested in given pod, otherwise node1 would have been chosen",
@@ -2104,10 +2073,10 @@ func TestPreempt(t *testing.T) {
Predicates: []tf.FitPredicate{tf.TruePredicateExtender},
},
},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
expectedVictims: sets.New("p2.1"),
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
// sum of priorities of all victims on node1 is larger than node2, node2 is chosen.
expectedNominatedNode: "node2",
want: framework.NewPostFilterResultWithNominatedNode("node2"),
expectedPods: []string{"p2.1"},
},
{
name: "no preempting in pod",
@@ -2118,9 +2087,10 @@ func TestPreempt(t *testing.T) {
st.MakePod().Name("p2.1").UID("p2.1").Namespace(v1.NamespaceDefault).Node("node2").Priority(highPriority).Req(largeRes).Obj(),
st.MakePod().Name("p3.1").UID("p3.1").Namespace(v1.NamespaceDefault).Node("node3").Priority(midPriority).Req(mediumRes).Obj(),
},
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
wantNilPostFilterResult: true,
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: nil,
expectedPods: nil,
},
{
name: "PreemptionPolicy is nil",
@@ -2131,10 +2101,10 @@ func TestPreempt(t *testing.T) {
st.MakePod().Name("p2.1").UID("p2.1").Namespace(v1.NamespaceDefault).Node("node2").Priority(highPriority).Req(largeRes).Obj(),
st.MakePod().Name("p3.1").UID("p3.1").Namespace(v1.NamespaceDefault).Node("node3").Priority(midPriority).Req(mediumRes).Obj(),
},
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
expectedVictims: sets.New("p1.1", "p1.2"),
expectedNominatedNode: "node1",
nodeNames: []string{"node1", "node2", "node3"},
registerPlugin: tf.RegisterPluginAsExtensions(noderesources.Name, nodeResourcesFitFunc, "Filter", "PreFilter"),
want: framework.NewPostFilterResultWithNominatedNode("node1"),
expectedPods: []string{"p1.1", "p1.2"},
},
}
@@ -2298,12 +2268,8 @@ func TestPreempt(t *testing.T) {
if !status.IsSuccess() && !status.IsRejected() {
t.Errorf("unexpected error in preemption: %v", status.AsError())
}
if test.wantNilPostFilterResult {
if res != nil {
t.Errorf("expected nil postFilterResult, got %v", res)
}
} else {
checkPostFilterResult(t, res, test.expectedNominatedNode, test.expectedVictims)
if diff := cmp.Diff(test.want, res); diff != "" {
t.Errorf("Unexpected status (-want, +got):\n%s", diff)
}
if asyncPreemptionEnabled {
@@ -2311,15 +2277,15 @@ func TestPreempt(t *testing.T) {
if err := wait.PollUntilContextTimeout(ctx, time.Millisecond*200, wait.ForeverTestTimeout, false, func(ctx context.Context) (bool, error) {
mu.RLock()
defer mu.RUnlock()
return len(deletedPodNames) == len(test.expectedVictims), nil
return len(deletedPodNames) == len(test.expectedPods), nil
}); err != nil {
t.Errorf("expected %v pods to be deleted, got %v.", len(test.expectedVictims), len(deletedPodNames))
t.Errorf("expected %v pods to be deleted, got %v.", len(test.expectedPods), len(deletedPodNames))
}
} else {
mu.RLock()
// If async preemption is disabled, the pod should be deleted immediately.
if len(deletedPodNames) != len(test.expectedVictims) {
t.Errorf("expected %v pods to be deleted, got %v.", len(test.expectedVictims), len(deletedPodNames))
if len(deletedPodNames) != len(test.expectedPods) {
t.Errorf("expected %v pods to be deleted, got %v.", len(test.expectedPods), len(deletedPodNames))
}
mu.RUnlock()
}
@@ -2344,9 +2310,16 @@ func TestPreempt(t *testing.T) {
}
}
for deletedPod := range deletedPodNames {
if !test.expectedVictims.Has(deletedPod) {
t.Errorf("pod %v is not expected to be deleted.", deletedPod)
for victimName := range deletedPodNames {
found := false
for _, expPod := range test.expectedPods {
if expPod == victimName {
found = true
break
}
}
if !found {
t.Errorf("pod %v is not expected to be a victim.", victimName)
}
}
if res != nil && res.NominatingInfo != nil {

View File

@@ -144,7 +144,7 @@ func (ev *Evaluator) Preempt(ctx context.Context, state fwk.CycleState, pod *v1.
}
fitError.Diagnosis.NodeToStatus.SetAbsentNodesStatus(fwk.NewStatus(fwk.UnschedulableAndUnresolvable, "Preemption is not helpful for scheduling"))
// Specify nominatedNodeName to clear the pod's nominatedNodeName status, if applicable.
return framework.NewPostFilterResult("", nil), fwk.NewStatus(fwk.Unschedulable, fitError.Error())
return framework.NewPostFilterResultWithNominatedNode(""), fwk.NewStatus(fwk.Unschedulable, fitError.Error())
}
// 3) Interact with registered Extenders to filter out some candidates if needed.
@@ -170,7 +170,7 @@ func (ev *Evaluator) Preempt(ctx context.Context, state fwk.CycleState, pod *v1.
}
}
return framework.NewPostFilterResult(bestCandidate.Name(), bestCandidate.Victims().Pods), fwk.NewStatus(fwk.Success)
return framework.NewPostFilterResultWithNominatedNode(bestCandidate.Name()), fwk.NewStatus(fwk.Success)
}
// FindCandidates calculates a slice of preemption candidates.

View File

@@ -414,10 +414,6 @@ func (p *PreFilterResult) Merge(in *PreFilterResult) *PreFilterResult {
// PostFilterResult wraps needed info for scheduler framework to act upon PostFilter phase.
type PostFilterResult struct {
*NominatingInfo
// Victims are the pods that need to be preempted to make room for the preemptor pod.
// For a preemptor that is a member of a pod group, this field skips the pods that were
// identified as victims for other members of the pod group.
Victims []*v1.Pod
}
// PreBindPreFlightResult wraps needed info for scheduler framework to act upon PreBindPreFlight phase.