Merge pull request #80901 from draveness/feature/use-map-instead-of-array

feat: use named array instead of array in normalizing score
This commit is contained in:
Kubernetes Prow Robot 2019-08-14 03:28:25 -07:00 committed by GitHub
commit 1906650d92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 278 additions and 72 deletions

View File

@ -804,11 +804,9 @@ func PrioritizeNodes(
for j := range priorityConfigs {
result[i].Score += results[j][i].Score * priorityConfigs[j].Weight
}
}
for _, scoreList := range scoresMap {
for i := range nodes {
result[i].Score += scoreList[i]
for j := range scoresMap {
result[i].Score += scoresMap[j][i].Score
}
}

View File

@ -349,25 +349,29 @@ func (f *framework) RunPostFilterPlugins(
return nil
}
// RunScorePlugins runs the set of configured scoring plugins. It returns a map that
// RunScorePlugins runs the set of configured scoring plugins. It returns a list that
// stores for each scoring plugin name the corresponding NodeScoreList(s).
// It also returns *Status, which is set to non-success if any of the plugins returns
// a non-success status.
func (f *framework) RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScoreMap, *Status) {
pluginToNodeScoreMap := make(PluginToNodeScoreMap, len(f.scorePlugins))
func (f *framework) RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScores, *Status) {
pluginToNodeScores := make(PluginToNodeScores, len(f.scorePlugins))
for _, pl := range f.scorePlugins {
pluginToNodeScoreMap[pl.Name()] = make(NodeScoreList, len(nodes))
pluginToNodeScores[pl.Name()] = make(NodeScoreList, len(nodes))
}
ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(nodes), func(index int) {
for _, pl := range f.scorePlugins {
score, status := pl.Score(pc, pod, nodes[index].Name)
nodeName := nodes[index].Name
score, status := pl.Score(pc, pod, nodeName)
if !status.IsSuccess() {
errCh.SendErrorWithCancel(fmt.Errorf(status.Message()), cancel)
return
}
pluginToNodeScoreMap[pl.Name()][index] = score
pluginToNodeScores[pl.Name()][index] = NodeScore{
Name: nodeName,
Score: score,
}
}
})
@ -377,21 +381,21 @@ func (f *framework) RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.
return nil, NewStatus(Error, msg)
}
return pluginToNodeScoreMap, nil
return pluginToNodeScores, nil
}
// RunNormalizeScorePlugins runs the NormalizeScore function of Score plugins.
// It should be called after RunScorePlugins with the PluginToNodeScoreMap result.
// It then modifies the map with normalized scores. It returns a non-success Status
// It should be called after RunScorePlugins with the PluginToNodeScores result.
// It then modifies the list with normalized scores. It returns a non-success Status
// if any of the NormalizeScore functions returns a non-success status.
func (f *framework) RunNormalizeScorePlugins(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScoreMap) *Status {
func (f *framework) RunNormalizeScorePlugins(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScores) *Status {
ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(f.scoreWithNormalizePlugins), func(index int) {
pl := f.scoreWithNormalizePlugins[index]
nodeScoreList, ok := scores[pl.Name()]
if !ok {
err := fmt.Errorf("normalize score plugin %q has no corresponding scores in the PluginToNodeScoreMap", pl.Name())
err := fmt.Errorf("normalize score plugin %q has no corresponding scores in the PluginToNodeScores", pl.Name())
errCh.SendErrorWithCancel(err, cancel)
return
}
@ -414,7 +418,7 @@ func (f *framework) RunNormalizeScorePlugins(pc *PluginContext, pod *v1.Pod, sco
// ApplyScoreWeights applies weights to the score results. It should be called after
// RunNormalizeScorePlugins.
func (f *framework) ApplyScoreWeights(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScoreMap) *Status {
func (f *framework) ApplyScoreWeights(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScores) *Status {
ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(f.scorePlugins), func(index int) {
@ -423,12 +427,12 @@ func (f *framework) ApplyScoreWeights(pc *PluginContext, pod *v1.Pod, scores Plu
weight := f.pluginNameToWeightMap[pl.Name()]
nodeScoreList, ok := scores[pl.Name()]
if !ok {
err := fmt.Errorf("score plugin %q has no corresponding scores in the PluginToNodeScoreMap", pl.Name())
err := fmt.Errorf("score plugin %q has no corresponding scores in the PluginToNodeScores", pl.Name())
errCh.SendErrorWithCancel(err, cancel)
return
}
for i := range nodeScoreList {
nodeScoreList[i] = nodeScoreList[i] * weight
nodeScoreList[i].Score = nodeScoreList[i].Score * weight
}
})

View File

@ -20,7 +20,7 @@ import (
"reflect"
"testing"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/scheduler/apis/config"
)
@ -67,7 +67,7 @@ func (pl *TestScorePlugin1) NormalizeScore(pc *PluginContext, pod *v1.Pod, score
}
// Simply decrease each node score by 1.
for i := range scores {
scores[i] = scores[i] - 1
scores[i].Score = scores[i].Score - 1
}
return nil
}
@ -91,7 +91,7 @@ func (pl *TestScorePlugin2) Name() string {
func (pl *TestScorePlugin2) NormalizeScore(pc *PluginContext, pod *v1.Pod, scores NodeScoreList) *Status {
// Simply force each node score to 5.
for i := range scores {
scores[i] = 5
scores[i].Score = 5
}
return nil
}
@ -256,8 +256,8 @@ func TestRunNormalizeScorePlugins(t *testing.T) {
name string
registry Registry
plugins *config.Plugins
input PluginToNodeScoreMap
want PluginToNodeScoreMap
input PluginToNodeScores
want PluginToNodeScores
// If err is true, we expect RunNormalizeScorePlugin to fail.
err bool
}{
@ -265,54 +265,162 @@ func TestRunNormalizeScorePlugins(t *testing.T) {
name: "no NormalizeScore plugins",
plugins: plugin3,
registry: registry,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
// No NormalizeScore plugin, map should be untouched.
want: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
want: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
},
{
name: "single Score plugin, single NormalizeScore plugin",
registry: registry,
plugins: plugin1,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
want: PluginToNodeScoreMap{
want: PluginToNodeScores{
// For plugin1, want=input-1.
scorePlugin1: {1, 2},
scorePlugin1: {
{
Name: "node1",
Score: 1,
},
{
Name: "node2",
Score: 2,
},
},
},
},
{
name: "2 Score plugins, 2 NormalizeScore plugins",
registry: registry,
plugins: plugin1And2,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
scorePlugin2: {2, 4},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
scorePlugin2: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 4,
},
},
},
want: PluginToNodeScoreMap{
want: PluginToNodeScores{
// For plugin1, want=input-1.
scorePlugin1: {1, 2},
scorePlugin1: {
{
Name: "node1",
Score: 1,
},
{
Name: "node2",
Score: 2,
},
},
// For plugin2, want=5.
scorePlugin2: {5, 5},
scorePlugin2: {
{
Name: "node1",
Score: 5,
},
{
Name: "node2",
Score: 5,
},
},
},
},
{
name: "2 Score plugins, 1 NormalizeScore plugin",
registry: registry,
plugins: plugin1And3,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
scorePlugin3: {2, 4},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
scorePlugin2: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 4,
},
},
},
want: PluginToNodeScoreMap{
want: PluginToNodeScores{
// For plugin1, want=input-1.
scorePlugin1: {1, 2},
scorePlugin1: {
{
Name: "node1",
Score: 1,
},
{
Name: "node2",
Score: 2,
},
},
// No NormalizeScore for plugin 3. The node scores are untouched.
scorePlugin3: {2, 4},
scorePlugin2: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 4,
},
},
},
},
{
@ -322,9 +430,27 @@ func TestRunNormalizeScorePlugins(t *testing.T) {
scorePlugin2: NewScorePlugin2,
},
plugins: plugin1And2,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
scorePlugin2: {2, 4},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
scorePlugin2: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 4,
},
},
},
err: true,
},
@ -332,8 +458,17 @@ func TestRunNormalizeScorePlugins(t *testing.T) {
name: "2 plugins but score map only contains plugin1",
registry: registry,
plugins: plugin1And2,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
err: true,
},
@ -368,41 +503,104 @@ func TestApplyScoreWeights(t *testing.T) {
tests := []struct {
name string
plugins *config.Plugins
input PluginToNodeScoreMap
want PluginToNodeScoreMap
input PluginToNodeScores
want PluginToNodeScores
// If err is true, we expect ApplyScoreWeights to fail.
err bool
}{
{
name: "single Score plugin, single nodeScoreList",
plugins: plugin1,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
want: PluginToNodeScoreMap{
want: PluginToNodeScores{
// For plugin1, want=input*weight1.
scorePlugin1: {4, 6},
scorePlugin1: {
{
Name: "node1",
Score: 4,
},
{
Name: "node2",
Score: 6,
},
},
},
},
{
name: "2 Score plugins, 2 nodeScoreLists in scoreMap",
plugins: plugin1And2,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
scorePlugin2: {2, 4},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
scorePlugin2: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 4,
},
},
},
want: PluginToNodeScoreMap{
want: PluginToNodeScores{
// For plugin1, want=input*weight1.
scorePlugin1: {4, 6},
scorePlugin1: {
{
Name: "node1",
Score: 4,
},
{
Name: "node2",
Score: 6,
},
},
// For plugin2, want=input*weight2.
scorePlugin2: {6, 12},
scorePlugin2: {
{
Name: "node1",
Score: 6,
},
{
Name: "node2",
Score: 12,
},
},
},
},
{
name: "2 Score plugins, 1 without corresponding nodeScoreList in the score map",
plugins: plugin1And2,
input: PluginToNodeScoreMap{
scorePlugin1: {2, 3},
input: PluginToNodeScores{
scorePlugin1: {
{
Name: "node1",
Score: 2,
},
{
Name: "node2",
Score: 3,
},
},
},
err: true,
},

View File

@ -31,10 +31,16 @@ import (
type Code int
// NodeScoreList declares a list of nodes and their scores.
type NodeScoreList []int
type NodeScoreList []NodeScore
// PluginToNodeScoreMap declares a map from plugin name to its NodeScoreList.
type PluginToNodeScoreMap map[string]NodeScoreList
// NodeScore is a struct with node name and score.
type NodeScore struct {
Name string
Score int
}
// PluginToNodeScores declares a map from plugin name to its NodeScoreList.
type PluginToNodeScores map[string]NodeScoreList
// NodeToStatusMap declares map from node name to its status.
type NodeToStatusMap map[string]*Status
@ -294,17 +300,17 @@ type Framework interface {
// stores for each scoring plugin name the corresponding NodeScoreList(s).
// It also returns *Status, which is set to non-success if any of the plugins returns
// a non-success status.
RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScoreMap, *Status)
RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScores, *Status)
// RunNormalizeScorePlugins runs the normalize score plugins. It should be called after
// RunScorePlugins with the PluginToNodeScoreMap result. It then modifies the map with
// RunScorePlugins with the PluginToNodeScores result. It then modifies the map with
// normalized scores. It returns a non-success Status if any of the normalize score plugins
// returns a non-success status.
RunNormalizeScorePlugins(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScoreMap) *Status
RunNormalizeScorePlugins(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScores) *Status
// ApplyScoreWeights applies weights to the score results. It should be called after
// RunNormalizeScorePlugins.
ApplyScoreWeights(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScoreMap) *Status
ApplyScoreWeights(pc *PluginContext, pod *v1.Pod, scores PluginToNodeScores) *Status
// RunPrebindPlugins runs the set of configured prebind plugins. It returns
// *Status and its code is set to non-success if any of the plugins returns

View File

@ -175,15 +175,15 @@ func (*fakeFramework) RunFilterPlugins(pc *framework.PluginContext, pod *v1.Pod,
return nil
}
func (*fakeFramework) RunScorePlugins(pc *framework.PluginContext, pod *v1.Pod, nodes []*v1.Node) (framework.PluginToNodeScoreMap, *framework.Status) {
func (*fakeFramework) RunScorePlugins(pc *framework.PluginContext, pod *v1.Pod, nodes []*v1.Node) (framework.PluginToNodeScores, *framework.Status) {
return nil, nil
}
func (*fakeFramework) RunNormalizeScorePlugins(pc *framework.PluginContext, pod *v1.Pod, scores framework.PluginToNodeScoreMap) *framework.Status {
func (*fakeFramework) RunNormalizeScorePlugins(pc *framework.PluginContext, pod *v1.Pod, scores framework.PluginToNodeScores) *framework.Status {
return nil
}
func (*fakeFramework) ApplyScoreWeights(pc *framework.PluginContext, pod *v1.Pod, scores framework.PluginToNodeScoreMap) *framework.Status {
func (*fakeFramework) ApplyScoreWeights(pc *framework.PluginContext, pod *v1.Pod, scores framework.PluginToNodeScores) *framework.Status {
return nil
}