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 { for j := range priorityConfigs {
result[i].Score += results[j][i].Score * priorityConfigs[j].Weight result[i].Score += results[j][i].Score * priorityConfigs[j].Weight
} }
}
for _, scoreList := range scoresMap { for j := range scoresMap {
for i := range nodes { result[i].Score += scoresMap[j][i].Score
result[i].Score += scoreList[i]
} }
} }

View File

@ -349,25 +349,29 @@ func (f *framework) RunPostFilterPlugins(
return nil 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). // 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 // It also returns *Status, which is set to non-success if any of the plugins returns
// a non-success status. // a non-success status.
func (f *framework) RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScoreMap, *Status) { func (f *framework) RunScorePlugins(pc *PluginContext, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScores, *Status) {
pluginToNodeScoreMap := make(PluginToNodeScoreMap, len(f.scorePlugins)) pluginToNodeScores := make(PluginToNodeScores, len(f.scorePlugins))
for _, pl := range 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()) ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel() errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(nodes), func(index int) { workqueue.ParallelizeUntil(ctx, 16, len(nodes), func(index int) {
for _, pl := range f.scorePlugins { 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() { if !status.IsSuccess() {
errCh.SendErrorWithCancel(fmt.Errorf(status.Message()), cancel) errCh.SendErrorWithCancel(fmt.Errorf(status.Message()), cancel)
return 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 nil, NewStatus(Error, msg)
} }
return pluginToNodeScoreMap, nil return pluginToNodeScores, nil
} }
// RunNormalizeScorePlugins runs the NormalizeScore function of Score plugins. // RunNormalizeScorePlugins runs the NormalizeScore function of Score plugins.
// It should be called after RunScorePlugins with the PluginToNodeScoreMap result. // It should be called after RunScorePlugins with the PluginToNodeScores result.
// It then modifies the map with normalized scores. It returns a non-success Status // 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. // 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()) ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel() errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(f.scoreWithNormalizePlugins), func(index int) { workqueue.ParallelizeUntil(ctx, 16, len(f.scoreWithNormalizePlugins), func(index int) {
pl := f.scoreWithNormalizePlugins[index] pl := f.scoreWithNormalizePlugins[index]
nodeScoreList, ok := scores[pl.Name()] nodeScoreList, ok := scores[pl.Name()]
if !ok { 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) errCh.SendErrorWithCancel(err, cancel)
return 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 // ApplyScoreWeights applies weights to the score results. It should be called after
// RunNormalizeScorePlugins. // 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()) ctx, cancel := context.WithCancel(context.Background())
errCh := schedutil.NewErrorChannel() errCh := schedutil.NewErrorChannel()
workqueue.ParallelizeUntil(ctx, 16, len(f.scorePlugins), func(index int) { 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()] weight := f.pluginNameToWeightMap[pl.Name()]
nodeScoreList, ok := scores[pl.Name()] nodeScoreList, ok := scores[pl.Name()]
if !ok { 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) errCh.SendErrorWithCancel(err, cancel)
return return
} }
for i := range nodeScoreList { for i := range nodeScoreList {
nodeScoreList[i] = nodeScoreList[i] * weight nodeScoreList[i].Score = nodeScoreList[i].Score * weight
} }
}) })

View File

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

View File

@ -31,10 +31,16 @@ import (
type Code int type Code int
// NodeScoreList declares a list of nodes and their scores. // 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. // NodeScore is a struct with node name and score.
type PluginToNodeScoreMap map[string]NodeScoreList 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. // NodeToStatusMap declares map from node name to its status.
type NodeToStatusMap map[string]*Status type NodeToStatusMap map[string]*Status
@ -294,17 +300,17 @@ type Framework interface {
// stores for each scoring plugin name the corresponding NodeScoreList(s). // 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 // It also returns *Status, which is set to non-success if any of the plugins returns
// a non-success status. // 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 // 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 // normalized scores. It returns a non-success Status if any of the normalize score plugins
// returns a non-success status. // 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 // ApplyScoreWeights applies weights to the score results. It should be called after
// RunNormalizeScorePlugins. // 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 // 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 // *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 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 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 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 return nil
} }