diff --git a/pkg/scheduler/framework/runtime/framework.go b/pkg/scheduler/framework/runtime/framework.go index 705d2b42472..e126d24c84d 100644 --- a/pkg/scheduler/framework/runtime/framework.go +++ b/pkg/scheduler/framework/runtime/framework.go @@ -670,6 +670,7 @@ func (f *frameworkImpl) RunPreFilterPlugins(ctx context.Context, state *framewor if verboseLogs { logger = klog.LoggerWithName(logger, "PreFilter") } + var returnStatus *framework.Status for _, pl := range f.preFilterPlugins { ctx := ctx if verboseLogs { @@ -684,7 +685,16 @@ func (f *frameworkImpl) RunPreFilterPlugins(ctx context.Context, state *framewor if !s.IsSuccess() { s.SetPlugin(pl.Name()) if s.IsRejected() { - return nil, s + if s.Code() == framework.UnschedulableAndUnresolvable { + // In this case, the preemption shouldn't happen in this scheduling cycle. + // So, no need to execute all PreFilter. + return nil, s + } + // In this case, the preemption should happen later in this scheduling cycle. + // So we need to execute all PreFilter. + // https://github.com/kubernetes/kubernetes/issues/119770 + returnStatus = s + continue } return nil, framework.AsStatus(fmt.Errorf("running PreFilter plugin %q: %w", pl.Name(), s.AsError())).WithPlugin(pl.Name()) } @@ -697,10 +707,14 @@ func (f *frameworkImpl) RunPreFilterPlugins(ctx context.Context, state *framewor if len(pluginsWithNodes) == 1 { msg = fmt.Sprintf("node(s) didn't satisfy plugin %v", pluginsWithNodes[0]) } - return nil, framework.NewStatus(framework.Unschedulable, msg) + // In this case, the preemption should happen later in this scheduling cycle. + // So we need to execute all PreFilter. + // https://github.com/kubernetes/kubernetes/issues/119770 + returnStatus = framework.NewStatus(framework.Unschedulable, msg) + continue } } - return result, nil + return result, returnStatus } func (f *frameworkImpl) runPreFilterPlugin(ctx context.Context, pl framework.PreFilterPlugin, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) { diff --git a/pkg/scheduler/framework/runtime/framework_test.go b/pkg/scheduler/framework/runtime/framework_test.go index 8a16de8b182..840eb633e68 100644 --- a/pkg/scheduler/framework/runtime/framework_test.go +++ b/pkg/scheduler/framework/runtime/framework_test.go @@ -197,7 +197,7 @@ func (pl *TestPlugin) ScoreExtensions() framework.ScoreExtensions { } func (pl *TestPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) { - return nil, framework.NewStatus(framework.Code(pl.inj.PreFilterStatus), injectReason) + return pl.inj.PreFilterResult, framework.NewStatus(framework.Code(pl.inj.PreFilterStatus), injectReason) } func (pl *TestPlugin) PreFilterExtensions() framework.PreFilterExtensions { @@ -1622,6 +1622,56 @@ func TestRunPreFilterPlugins(t *testing.T) { wantSkippedPlugins: sets.New("skip1", "skip2"), wantStatusCode: framework.Success, }, + { + name: "one PreFilter plugin returned Unschedulable, but another PreFilter plugin should be executed", + plugins: []*TestPlugin{ + { + name: "unschedulable", + inj: injectedResult{PreFilterStatus: int(framework.Unschedulable)}, + }, + { + // to make sure this plugin is executed, this plugin return Skip and we confirm it via wantSkippedPlugins. + name: "skip", + inj: injectedResult{PreFilterStatus: int(framework.Skip)}, + }, + }, + wantPreFilterResult: nil, + wantSkippedPlugins: sets.New("skip"), + wantStatusCode: framework.Unschedulable, + }, + { + name: "one PreFilter plugin returned UnschedulableAndUnresolvable, and all other plugins aren't executed", + plugins: []*TestPlugin{ + { + name: "unresolvable", + inj: injectedResult{PreFilterStatus: int(framework.UnschedulableAndUnresolvable)}, + }, + { + // to make sure this plugin is not executed, this plugin return Skip and we confirm it via wantSkippedPlugins. + name: "skip", + inj: injectedResult{PreFilterStatus: int(framework.Skip)}, + }, + }, + wantPreFilterResult: nil, + wantStatusCode: framework.UnschedulableAndUnresolvable, + }, + { + name: "all nodes are filtered out by prefilter result, but all other plugins are executed", + plugins: []*TestPlugin{ + { + name: "reject-all-nodes", + inj: injectedResult{PreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()}}, + }, + { + // to make sure this plugin is not executed, this plugin return Skip and we confirm it via wantSkippedPlugins. + name: "skip", + inj: injectedResult{PreFilterStatus: int(framework.Skip)}, + }, + }, + wantPreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()}, + wantSkippedPlugins: sets.New("skip"), + wantStatusCode: framework.Unschedulable, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -3224,20 +3274,21 @@ func buildScoreConfigWithWeights(weights map[string]int32, ps ...string) *config } type injectedResult struct { - ScoreRes int64 `json:"scoreRes,omitempty"` - NormalizeRes int64 `json:"normalizeRes,omitempty"` - ScoreStatus int `json:"scoreStatus,omitempty"` - NormalizeStatus int `json:"normalizeStatus,omitempty"` - PreFilterStatus int `json:"preFilterStatus,omitempty"` - PreFilterAddPodStatus int `json:"preFilterAddPodStatus,omitempty"` - PreFilterRemovePodStatus int `json:"preFilterRemovePodStatus,omitempty"` - FilterStatus int `json:"filterStatus,omitempty"` - PostFilterStatus int `json:"postFilterStatus,omitempty"` - PreScoreStatus int `json:"preScoreStatus,omitempty"` - ReserveStatus int `json:"reserveStatus,omitempty"` - PreBindStatus int `json:"preBindStatus,omitempty"` - BindStatus int `json:"bindStatus,omitempty"` - PermitStatus int `json:"permitStatus,omitempty"` + ScoreRes int64 `json:"scoreRes,omitempty"` + NormalizeRes int64 `json:"normalizeRes,omitempty"` + ScoreStatus int `json:"scoreStatus,omitempty"` + NormalizeStatus int `json:"normalizeStatus,omitempty"` + PreFilterResult *framework.PreFilterResult `json:"preFilterResult,omitempty"` + PreFilterStatus int `json:"preFilterStatus,omitempty"` + PreFilterAddPodStatus int `json:"preFilterAddPodStatus,omitempty"` + PreFilterRemovePodStatus int `json:"preFilterRemovePodStatus,omitempty"` + FilterStatus int `json:"filterStatus,omitempty"` + PostFilterStatus int `json:"postFilterStatus,omitempty"` + PreScoreStatus int `json:"preScoreStatus,omitempty"` + ReserveStatus int `json:"reserveStatus,omitempty"` + PreBindStatus int `json:"preBindStatus,omitempty"` + BindStatus int `json:"bindStatus,omitempty"` + PermitStatus int `json:"permitStatus,omitempty"` } func setScoreRes(inj injectedResult) (int64, *framework.Status) {