feature(scheduler): won't run Score if PreScore returned a Skip status (#115652)

* allow preScore to return skip status to skip running the corresponding score extension

* add test case for all skipped

* add test case for select host

* update plugin status

* skip score when all plugins are skipped

* update
This commit is contained in:
kidddddddddddddddddddddd
2023-02-14 06:53:29 +08:00
committed by GitHub
parent 436ca94642
commit f5a69ffda9
6 changed files with 355 additions and 29 deletions

View File

@@ -1061,13 +1061,139 @@ func TestPreEnqueuePlugins(t *testing.T) {
}
}
func TestRunPreScorePlugins(t *testing.T) {
tests := []struct {
name string
plugins []*TestPlugin
wantSkippedPlugins sets.Set[string]
wantStatusCode framework.Code
}{
{
name: "all PreScorePlugins returned success",
plugins: []*TestPlugin{
{
name: "success1",
},
{
name: "success2",
},
},
wantStatusCode: framework.Success,
},
{
name: "one PreScore plugin returned success, but another PreScore plugin returned non-success",
plugins: []*TestPlugin{
{
name: "success",
},
{
name: "error",
inj: injectedResult{PreScoreStatus: int(framework.Error)},
},
},
wantStatusCode: framework.Error,
},
{
name: "one PreScore plugin returned skip, but another PreScore plugin returned non-success",
plugins: []*TestPlugin{
{
name: "skip",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
{
name: "error",
inj: injectedResult{PreScoreStatus: int(framework.Error)},
},
},
wantStatusCode: framework.Error,
},
{
name: "all PreScore plugins returned skip",
plugins: []*TestPlugin{
{
name: "skip1",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
{
name: "skip2",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
{
name: "skip3",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
},
wantSkippedPlugins: sets.New("skip1", "skip2", "skip3"),
wantStatusCode: framework.Success,
},
{
name: "some PreScore plugins returned skip",
plugins: []*TestPlugin{
{
name: "skip1",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
{
name: "success1",
},
{
name: "skip2",
inj: injectedResult{PreScoreStatus: int(framework.Skip)},
},
{
name: "success2",
},
},
wantSkippedPlugins: sets.New("skip1", "skip2"),
wantStatusCode: framework.Success,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := make(Registry)
enabled := make([]config.Plugin, len(tt.plugins))
for i, p := range tt.plugins {
p := p
enabled[i].Name = p.name
r.Register(p.name, func(_ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
return p, nil
})
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
f, err := newFrameworkWithQueueSortAndBind(
r,
config.KubeSchedulerProfile{Plugins: &config.Plugins{PreScore: config.PluginSet{Enabled: enabled}}},
ctx.Done(),
)
if err != nil {
t.Fatalf("Failed to create framework for testing: %v", err)
}
state := framework.NewCycleState()
status := f.RunPreScorePlugins(ctx, state, nil, nil)
if status.Code() != tt.wantStatusCode {
t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
}
skipped := state.SkipScorePlugins
if d := cmp.Diff(skipped, tt.wantSkippedPlugins); d != "" {
t.Errorf("wrong skip score plugins. got: %v, want: %v, diff: %s", skipped, tt.wantSkippedPlugins, d)
}
})
}
}
func TestRunScorePlugins(t *testing.T) {
tests := []struct {
name string
registry Registry
plugins *config.Plugins
pluginConfigs []config.PluginConfig
want []framework.NodePluginScores
name string
registry Registry
plugins *config.Plugins
pluginConfigs []config.PluginConfig
want []framework.NodePluginScores
skippedPlugins sets.Set[string]
// If err is true, we expect RunScorePlugin to fail.
err bool
}{
@@ -1345,6 +1471,70 @@ func TestRunScorePlugins(t *testing.T) {
},
},
},
{
name: "one success plugin, one skip plugin",
plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
pluginConfigs: []config.PluginConfig{
{
Name: scorePlugin1,
Args: &runtime.Unknown{
Raw: []byte(`{ "scoreRes": 1 }`),
},
},
{
Name: scoreWithNormalizePlugin1,
Args: &runtime.Unknown{
Raw: []byte(`{ "scoreStatus": 1 }`), // To make sure this plugin isn't called, set error as an injected result.
},
},
},
skippedPlugins: sets.New(scoreWithNormalizePlugin1),
want: []framework.NodePluginScores{
{
Name: "node1",
Scores: []framework.PluginScore{
{
Name: scorePlugin1,
Score: 1,
},
},
TotalScore: 1,
},
{
Name: "node2",
Scores: []framework.PluginScore{
{
Name: scorePlugin1,
Score: 1,
},
},
TotalScore: 1,
},
},
},
{
name: "all plugins are skipped in prescore",
plugins: buildScoreConfigDefaultWeights(scorePlugin1),
pluginConfigs: []config.PluginConfig{
{
Name: scorePlugin1,
Args: &runtime.Unknown{
Raw: []byte(`{ "scoreStatus": 1 }`), // To make sure this plugin isn't called, set error as an injected result.
},
},
},
skippedPlugins: sets.New(scorePlugin1),
want: []framework.NodePluginScores{
{
Name: "node1",
Scores: []framework.PluginScore{},
},
{
Name: "node2",
Scores: []framework.PluginScore{},
},
},
},
}
for _, tt := range tests {
@@ -1361,6 +1551,8 @@ func TestRunScorePlugins(t *testing.T) {
t.Fatalf("Failed to create framework for testing: %v", err)
}
state := framework.NewCycleState()
state.SkipScorePlugins = tt.skippedPlugins
res, status := f.RunScorePlugins(ctx, state, pod, nodes)
if tt.err {