Merge pull request #119769 from Huang-Wei/bug/prefilter-preemption

Fix a bug that PostFilter plugin may don't function if previous PreFilter plugins return Skip
This commit is contained in:
Kubernetes Prow Robot 2023-08-15 23:12:50 -07:00 committed by GitHub
commit 57212647e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 7 deletions

View File

@ -632,12 +632,13 @@ func (f *frameworkImpl) QueueSortFunc() framework.LessFunc {
// If a non-success status is returned, then the scheduling cycle is aborted.
func (f *frameworkImpl) RunPreFilterPlugins(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (_ *framework.PreFilterResult, status *framework.Status) {
startTime := time.Now()
skipPlugins := sets.New[string]()
defer func() {
state.SkipFilterPlugins = skipPlugins
metrics.FrameworkExtensionPointDuration.WithLabelValues(metrics.PreFilter, status.Code().String(), f.profileName).Observe(metrics.SinceInSeconds(startTime))
}()
var result *framework.PreFilterResult
var pluginsWithNodes []string
skipPlugins := sets.New[string]()
logger := klog.FromContext(ctx)
logger = klog.LoggerWithName(logger, "PreFilter")
// TODO(knelasevero): Remove duplicated keys from log entry calls
@ -671,7 +672,6 @@ func (f *frameworkImpl) RunPreFilterPlugins(ctx context.Context, state *framewor
return nil, framework.NewStatus(framework.Unschedulable, msg)
}
}
state.SkipFilterPlugins = skipPlugins
return result, nil
}

View File

@ -1509,7 +1509,7 @@ func TestRunPreFilterPlugins(t *testing.T) {
inj: injectedResult{PreFilterStatus: int(framework.Error)},
},
},
wantPreFilterResult: nil,
wantSkippedPlugins: sets.New("skip"),
wantStatusCode: framework.Error,
},
{

View File

@ -99,9 +99,16 @@ func waitForNominatedNodeName(cs clientset.Interface, pod *v1.Pod) error {
const tokenFilterName = "token-filter"
// tokenFilter is a fake plugin that implements PreFilter and Filter.
// `Token` simulates the allowed pods number a cluster can host.
// If `EnablePreFilter` is set to false or `Token` is positive, PreFilter passes; otherwise returns Unschedulable
// For each Filter() call, `Token` is decreased by one. When `Token` is positive, Filter passes; otherwise return
// Unschedulable or UnschedulableAndUnresolvable (when `Unresolvable` is set to true)
// AddPod()/RemovePod() adds/removes one token to the cluster to simulate the dryrun preemption
type tokenFilter struct {
Tokens int
Unresolvable bool
EnablePreFilter bool
}
// Name returns name of the plugin.
@ -123,8 +130,11 @@ func (fp *tokenFilter) Filter(ctx context.Context, state *framework.CycleState,
}
func (fp *tokenFilter) PreFilter(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
if !fp.EnablePreFilter || fp.Tokens > 0 {
return nil, nil
}
return nil, framework.NewStatus(framework.Unschedulable)
}
func (fp *tokenFilter) AddPod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod,
podInfoToAdd *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
@ -194,6 +204,7 @@ func TestPreemption(t *testing.T) {
existingPods []*v1.Pod
pod *v1.Pod
initTokens int
enablePreFilter bool
unresolvable bool
preemptedPodIndexes map[int]struct{}
enablePodDisruptionConditions bool
@ -274,6 +285,36 @@ func TestPreemption(t *testing.T) {
}),
preemptedPodIndexes: map[int]struct{}{0: {}},
},
// This is identical with previous subtest except for setting enablePreFilter to true.
// With this fake plugin returning Unschedulable in PreFilter, it's able to exercise the path
// that in-tree plugins return Skip in PreFilter and their AddPod/RemovePod functions are also
// skipped properly upon preemption.
{
name: "basic pod preemption with preFilter",
initTokens: 1,
enablePreFilter: true,
existingPods: []*v1.Pod{
initPausePod(&testutils.PausePodConfig{
Name: "victim-pod",
Namespace: testCtx.NS.Name,
Priority: &lowPriority,
Resources: &v1.ResourceRequirements{Requests: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(200, resource.DecimalSI)},
},
}),
},
pod: initPausePod(&testutils.PausePodConfig{
Name: "preemptor-pod",
Namespace: testCtx.NS.Name,
Priority: &highPriority,
Resources: &v1.ResourceRequirements{Requests: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(200, resource.DecimalSI)},
},
}),
preemptedPodIndexes: map[int]struct{}{0: {}},
},
{
// same as the previous test, but the filter is unresolvable.
name: "basic pod preemption with unresolvable filter",
@ -445,6 +486,7 @@ func TestPreemption(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)()
filter.Tokens = test.initTokens
filter.EnablePreFilter = test.enablePreFilter
filter.Unresolvable = test.unresolvable
pods := make([]*v1.Pod, len(test.existingPods))
// Create and run existingPods.