Add NormalizeScore extension point for scheduler framework.

This commit is contained in:
Cong Liu
2019-07-29 11:45:01 -04:00
parent 75d5189612
commit 34373662d3
7 changed files with 685 additions and 57 deletions

View File

@@ -38,9 +38,14 @@ type PrefilterPlugin struct {
}
type ScorePlugin struct {
failScore bool
numCalled int
highScoreNode string
failScore bool
numScoreCalled int
highScoreNode string
}
type ScoreWithNormalizePlugin struct {
numScoreCalled int
numNormalizeScoreCalled int
}
type FilterPlugin struct {
@@ -91,19 +96,22 @@ type PermitPlugin struct {
}
const (
prefilterPluginName = "prefilter-plugin"
scorePluginName = "score-plugin"
filterPluginName = "filter-plugin"
reservePluginName = "reserve-plugin"
prebindPluginName = "prebind-plugin"
unreservePluginName = "unreserve-plugin"
postbindPluginName = "postbind-plugin"
permitPluginName = "permit-plugin"
prefilterPluginName = "prefilter-plugin"
scorePluginName = "score-plugin"
scoreWithNormalizePluginName = "score-with-normalize-plugin"
filterPluginName = "filter-plugin"
reservePluginName = "reserve-plugin"
prebindPluginName = "prebind-plugin"
unreservePluginName = "unreserve-plugin"
postbindPluginName = "postbind-plugin"
permitPluginName = "permit-plugin"
)
var _ = framework.PrefilterPlugin(&PrefilterPlugin{})
var _ = framework.ScorePlugin(&ScorePlugin{})
var _ = framework.FilterPlugin(&FilterPlugin{})
var _ = framework.ScorePlugin(&ScorePlugin{})
var _ = framework.ScoreWithNormalizePlugin(&ScoreWithNormalizePlugin{})
var _ = framework.ReservePlugin(&ReservePlugin{})
var _ = framework.PrebindPlugin(&PrebindPlugin{})
var _ = framework.BindPlugin(&BindPlugin{})
@@ -111,6 +119,13 @@ var _ = framework.PostbindPlugin(&PostbindPlugin{})
var _ = framework.UnreservePlugin(&UnreservePlugin{})
var _ = framework.PermitPlugin(&PermitPlugin{})
var scPlugin = &ScorePlugin{}
// NewScorePlugin is the factory for score plugin.
func NewScorePlugin(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
return scPlugin, nil
}
// Name returns name of the score plugin.
func (sp *ScorePlugin) Name() string {
return scorePluginName
@@ -119,21 +134,19 @@ func (sp *ScorePlugin) Name() string {
// reset returns name of the score plugin.
func (sp *ScorePlugin) reset() {
sp.failScore = false
sp.numCalled = 0
sp.numScoreCalled = 0
sp.highScoreNode = ""
}
var scPlugin = &ScorePlugin{}
// Score returns the score of scheduling a pod on a specific node.
func (sp *ScorePlugin) Score(pc *framework.PluginContext, p *v1.Pod, nodeName string) (int, *framework.Status) {
sp.numCalled++
sp.numScoreCalled++
if sp.failScore {
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("injecting failure for pod %v", p.Name))
}
score := 10
if sp.numCalled == 1 {
if sp.numScoreCalled == 1 {
// The first node is scored the highest, the rest is scored lower.
sp.highScoreNode = nodeName
score = 100
@@ -141,9 +154,34 @@ func (sp *ScorePlugin) Score(pc *framework.PluginContext, p *v1.Pod, nodeName st
return score, nil
}
// NewScorePlugin is the factory for score plugin.
func NewScorePlugin(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
return scPlugin, nil
var scoreWithNormalizePlguin = &ScoreWithNormalizePlugin{}
// Name returns name of the score plugin.
func (sp *ScoreWithNormalizePlugin) Name() string {
return scoreWithNormalizePluginName
}
// reset returns name of the score plugin.
func (sp *ScoreWithNormalizePlugin) reset() {
sp.numScoreCalled = 0
sp.numNormalizeScoreCalled = 0
}
// Score returns the score of scheduling a pod on a specific node.
func (sp *ScoreWithNormalizePlugin) Score(pc *framework.PluginContext, p *v1.Pod, nodeName string) (int, *framework.Status) {
sp.numScoreCalled++
score := 10
return score, nil
}
func (sp *ScoreWithNormalizePlugin) NormalizeScore(pc *framework.PluginContext, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
sp.numNormalizeScoreCalled++
return nil
}
// NewScoreWithNormalizePlugin is the factory for score with normalize plugin.
func NewScoreWithNormalizePlugin(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
return scoreWithNormalizePlguin, nil
}
var filterPlugin = &FilterPlugin{}
@@ -490,9 +528,6 @@ func TestPrefilterPlugin(t *testing.T) {
// TestScorePlugin tests invocation of score plugins.
func TestScorePlugin(t *testing.T) {
// Create a plugin registry for testing. Register only a score plugin.
registry := framework.Registry{scorePluginName: NewScorePlugin}
// Setup initial score plugin for testing.
plugins := &schedulerconfig.Plugins{
Score: &schedulerconfig.PluginSet{
@@ -503,22 +538,9 @@ func TestScorePlugin(t *testing.T) {
},
},
}
// Set empty plugin config for testing
emptyPluginConfig := []schedulerconfig.PluginConfig{}
// Create the master and the scheduler with the test plugin set.
context := initTestSchedulerWithOptions(t,
initTestMaster(t, "score-plugin", nil),
false, nil, registry, plugins, emptyPluginConfig, false, time.Second)
context, cs := initTestContextForScorePlugin(t, plugins)
defer cleanupTest(t, context)
cs := context.clientSet
// Add multiple nodes, one of them will be scored much higher than the others.
_, err := createNodes(cs, "test-node", nil, 10)
if err != nil {
t.Fatalf("Cannot create nodes: %v", err)
}
for i, fail := range []bool{false, true} {
scPlugin.failScore = fail
// Create a best effort pod.
@@ -545,8 +567,8 @@ func TestScorePlugin(t *testing.T) {
}
}
if scPlugin.numCalled == 0 {
t.Errorf("Expected the reserve plugin to be called.")
if scPlugin.numScoreCalled == 0 {
t.Errorf("Expected the score plugin to be called.")
}
scPlugin.reset()
@@ -554,6 +576,42 @@ func TestScorePlugin(t *testing.T) {
}
}
// TestNormalizeScorePlugin tests invocation of normalize score plugins.
func TestNormalizeScorePlugin(t *testing.T) {
// Setup initial score plugin for testing.
plugins := &schedulerconfig.Plugins{
Score: &schedulerconfig.PluginSet{
Enabled: []schedulerconfig.Plugin{
{
Name: scoreWithNormalizePluginName,
},
},
},
}
context, cs := initTestContextForScorePlugin(t, plugins)
defer cleanupTest(t, context)
// Create a best effort pod.
pod, err := createPausePod(cs,
initPausePod(cs, &pausePodConfig{Name: "test-pod", Namespace: context.ns.Name}))
if err != nil {
t.Fatalf("Error while creating a test pod: %v", err)
}
if err = waitForPodToSchedule(cs, pod); err != nil {
t.Errorf("Expected the pod to be scheduled. error: %v", err)
}
if scoreWithNormalizePlguin.numScoreCalled == 0 {
t.Errorf("Expected the score plugin to be called.")
}
if scoreWithNormalizePlguin.numNormalizeScoreCalled == 0 {
t.Error("Expected the normalize score plugin to be called")
}
scoreWithNormalizePlguin.reset()
}
// TestReservePlugin tests invocation of reserve plugins.
func TestReservePlugin(t *testing.T) {
// Create a plugin registry for testing. Register only a reserve plugin.
@@ -1477,3 +1535,26 @@ func TestPreemptWithPermitPlugin(t *testing.T) {
perPlugin.reset()
cleanupPods(cs, t, []*v1.Pod{waitingPod, preemptorPod})
}
func initTestContextForScorePlugin(t *testing.T, plugins *schedulerconfig.Plugins) (*testContext, *clientset.Clientset) {
// Create a plugin registry for testing. Register only a score plugin.
registry := framework.Registry{
scorePluginName: NewScorePlugin,
scoreWithNormalizePluginName: NewScoreWithNormalizePlugin,
}
// Set empty plugin config for testing
emptyPluginConfig := []schedulerconfig.PluginConfig{}
// Create the master and the scheduler with the test plugin set.
context := initTestSchedulerWithOptions(t,
initTestMaster(t, "score-plugin", nil),
false, nil, registry, plugins, emptyPluginConfig, false, time.Second)
cs := context.clientSet
_, err := createNodes(cs, "test-node", nil, 10)
if err != nil {
t.Fatalf("Cannot create nodes: %v", err)
}
return context, cs
}