From 9d48a44601a861defff3b8787e028e4439e64bdc Mon Sep 17 00:00:00 2001 From: Dave Chen Date: Fri, 15 Jan 2021 13:58:12 +0800 Subject: [PATCH] Expose node status so that external preemption plugins can use it Signed-off-by: Dave Chen --- .../defaultpreemption/default_preemption.go | 40 +++++++++---------- .../default_preemption_test.go | 4 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go index aefc0ffd2ed..4561b2fdfc1 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go @@ -138,11 +138,23 @@ func (pl *DefaultPreemption) preempt(ctx context.Context, state *framework.Cycle } // 2) Find all preemption candidates. - candidates, err := pl.FindCandidates(ctx, state, pod, m) - if err != nil || len(candidates) == 0 { + candidates, evaluatedNodeNum, status, err := pl.FindCandidates(ctx, state, pod, m) + if err != nil { return "", err } + // Return a FitError only when there are no candidates that fit the pod. + if len(candidates) == 0 { + return "", &framework.FitError{ + Pod: pod, + NumAllNodes: len(evaluatedNodeNum), + Diagnosis: framework.Diagnosis{ + NodeToStatusMap: status, + // Leave FailedPlugins as nil as it won't be used on moving Pods. + }, + } + } + // 3) Interact with registered Extenders to filter out some candidates if needed. candidates, err = CallExtenders(ph.Extenders(), pod, nodeLister, candidates) if err != nil { @@ -186,15 +198,14 @@ func (pl *DefaultPreemption) getOffsetAndNumCandidates(numNodes int32) (int32, i // FindCandidates calculates a slice of preemption candidates. // Each candidate is executable to make the given schedulable. -func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framework.CycleState, pod *v1.Pod, m framework.NodeToStatusMap) ([]Candidate, error) { +func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framework.CycleState, pod *v1.Pod, m framework.NodeToStatusMap) ([]Candidate, []*framework.NodeInfo, framework.NodeToStatusMap, error) { allNodes, err := pl.fh.SnapshotSharedLister().NodeInfos().List() if err != nil { - return nil, err + return nil, nil, nil, err } if len(allNodes) == 0 { - return nil, fmt.Errorf("no nodes available") + return nil, nil, nil, fmt.Errorf("no nodes available") } - potentialNodes := nodesWherePreemptionMightHelp(allNodes, m) if len(potentialNodes) == 0 { klog.V(3).Infof("Preemption will not help schedule pod %v/%v on any node.", pod.Namespace, pod.Name) @@ -203,12 +214,12 @@ func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framewor klog.Errorf("Cannot clear 'NominatedNodeName' field of pod %v/%v: %v", pod.Namespace, pod.Name, err) // We do not return as this error is not critical. } - return nil, nil + return nil, nil, nil, nil } pdbs, err := getPodDisruptionBudgets(pl.pdbLister) if err != nil { - return nil, err + return nil, nil, nil, err } offset, numCandidates := pl.getOffsetAndNumCandidates(int32(len(potentialNodes))) @@ -220,18 +231,7 @@ func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framewor klog.Infof("from a pool of %d nodes (offset: %d, sample %d nodes: %v), ~%d candidates will be chosen", len(potentialNodes), offset, len(sample), sample, numCandidates) } candidates, nodeStatuses := dryRunPreemption(ctx, pl.fh, state, pod, potentialNodes, pdbs, offset, numCandidates) - // Return a FitError only when there are no candidates that fit the pod. - if len(candidates) == 0 { - return candidates, &framework.FitError{ - Pod: pod, - NumAllNodes: len(potentialNodes), - Diagnosis: framework.Diagnosis{ - NodeToStatusMap: nodeStatuses, - // Leave FailedPlugins as nil as it won't be used on moving Pods. - }, - } - } - return candidates, nil + return candidates, potentialNodes, nodeStatuses, nil } // PodEligibleToPreemptOthers determines whether this pod should be considered diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go index af21b358be8..62810ff908f 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go @@ -154,7 +154,9 @@ func TestPostFilter(t *testing.T) { "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable), }, wantResult: nil, - wantStatus: framework.NewStatus(framework.Unschedulable), + // node1 is not actually been evaluated, it will not be processed by preemption, the status here is an internal status, the final message that is going to be written in + // the log will be looks like "Status after running PostFilter plugins for pod" pod="default/p" status=&{code:2 reasons:[0/0 nodes are available: .] err:} + wantStatus: framework.NewStatus(framework.Unschedulable, "0/0 nodes are available: ."), }, { name: "pod can be made schedulable on one node",