Aggregate mulitple NodePreference custom priorities to a single score plugin.
This commit is contained in:
		@@ -64,7 +64,6 @@ go_test(
 | 
				
			|||||||
        "metadata_test.go",
 | 
					        "metadata_test.go",
 | 
				
			||||||
        "most_requested_test.go",
 | 
					        "most_requested_test.go",
 | 
				
			||||||
        "node_affinity_test.go",
 | 
					        "node_affinity_test.go",
 | 
				
			||||||
        "node_label_test.go",
 | 
					 | 
				
			||||||
        "node_prefer_avoid_pods_test.go",
 | 
					        "node_prefer_avoid_pods_test.go",
 | 
				
			||||||
        "requested_to_capacity_ratio_test.go",
 | 
					        "requested_to_capacity_ratio_test.go",
 | 
				
			||||||
        "resource_limits_test.go",
 | 
					        "resource_limits_test.go",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,15 +27,15 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NodeLabelPrioritizer contains information to calculate node label priority.
 | 
					// NodeLabelPrioritizer contains information to calculate node label priority.
 | 
				
			||||||
type NodeLabelPrioritizer struct {
 | 
					type NodeLabelPrioritizer struct {
 | 
				
			||||||
	label    string
 | 
						presentLabelsPreference []string
 | 
				
			||||||
	presence bool
 | 
						absentLabelsPreference  []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewNodeLabelPriority creates a NodeLabelPrioritizer.
 | 
					// NewNodeLabelPriority creates a NodeLabelPrioritizer.
 | 
				
			||||||
func NewNodeLabelPriority(label string, presence bool) (PriorityMapFunction, PriorityReduceFunction) {
 | 
					func NewNodeLabelPriority(presentLabelsPreference []string, absentLabelsPreference []string) (PriorityMapFunction, PriorityReduceFunction) {
 | 
				
			||||||
	labelPrioritizer := &NodeLabelPrioritizer{
 | 
						labelPrioritizer := &NodeLabelPrioritizer{
 | 
				
			||||||
		label:    label,
 | 
							presentLabelsPreference: presentLabelsPreference,
 | 
				
			||||||
		presence: presence,
 | 
							absentLabelsPreference:  absentLabelsPreference,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return labelPrioritizer.CalculateNodeLabelPriorityMap, nil
 | 
						return labelPrioritizer.CalculateNodeLabelPriorityMap, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -49,11 +49,20 @@ func (n *NodeLabelPrioritizer) CalculateNodeLabelPriorityMap(pod *v1.Pod, meta i
 | 
				
			|||||||
		return framework.NodeScore{}, fmt.Errorf("node not found")
 | 
							return framework.NodeScore{}, fmt.Errorf("node not found")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	exists := labels.Set(node.Labels).Has(n.label)
 | 
					 | 
				
			||||||
	score := int64(0)
 | 
						score := int64(0)
 | 
				
			||||||
	if (exists && n.presence) || (!exists && !n.presence) {
 | 
						for _, label := range n.presentLabelsPreference {
 | 
				
			||||||
		score = framework.MaxNodeScore
 | 
							if labels.Set(node.Labels).Has(label) {
 | 
				
			||||||
 | 
								score += framework.MaxNodeScore
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						for _, label := range n.absentLabelsPreference {
 | 
				
			||||||
 | 
							if !labels.Set(node.Labels).Has(label) {
 | 
				
			||||||
 | 
								score += framework.MaxNodeScore
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Take average score for each label to ensure the score doesn't exceed MaxNodeScore.
 | 
				
			||||||
 | 
						score /= int64(len(n.presentLabelsPreference) + len(n.absentLabelsPreference))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return framework.NodeScore{
 | 
						return framework.NodeScore{
 | 
				
			||||||
		Name:  node.Name,
 | 
							Name:  node.Name,
 | 
				
			||||||
		Score: score,
 | 
							Score: score,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,127 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2016 The Kubernetes Authors.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package priorities
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
					 | 
				
			||||||
	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
					 | 
				
			||||||
	nodeinfosnapshot "k8s.io/kubernetes/pkg/scheduler/nodeinfo/snapshot"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestNewNodeLabelPriority(t *testing.T) {
 | 
					 | 
				
			||||||
	label1 := map[string]string{"foo": "bar"}
 | 
					 | 
				
			||||||
	label2 := map[string]string{"bar": "foo"}
 | 
					 | 
				
			||||||
	label3 := map[string]string{"bar": "baz"}
 | 
					 | 
				
			||||||
	tests := []struct {
 | 
					 | 
				
			||||||
		nodes        []*v1.Node
 | 
					 | 
				
			||||||
		label        string
 | 
					 | 
				
			||||||
		presence     bool
 | 
					 | 
				
			||||||
		expectedList framework.NodeScoreList
 | 
					 | 
				
			||||||
		name         string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}, {Name: "machine3", Score: 0}},
 | 
					 | 
				
			||||||
			label:        "baz",
 | 
					 | 
				
			||||||
			presence:     true,
 | 
					 | 
				
			||||||
			name:         "no match found, presence true",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: framework.MaxNodeScore}, {Name: "machine3", Score: framework.MaxNodeScore}},
 | 
					 | 
				
			||||||
			label:        "baz",
 | 
					 | 
				
			||||||
			presence:     false,
 | 
					 | 
				
			||||||
			name:         "no match found, presence false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: 0}, {Name: "machine3", Score: 0}},
 | 
					 | 
				
			||||||
			label:        "foo",
 | 
					 | 
				
			||||||
			presence:     true,
 | 
					 | 
				
			||||||
			name:         "one match found, presence true",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: framework.MaxNodeScore}, {Name: "machine3", Score: framework.MaxNodeScore}},
 | 
					 | 
				
			||||||
			label:        "foo",
 | 
					 | 
				
			||||||
			presence:     false,
 | 
					 | 
				
			||||||
			name:         "one match found, presence false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: framework.MaxNodeScore}, {Name: "machine3", Score: framework.MaxNodeScore}},
 | 
					 | 
				
			||||||
			label:        "bar",
 | 
					 | 
				
			||||||
			presence:     true,
 | 
					 | 
				
			||||||
			name:         "two matches found, presence true",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			nodes: []*v1.Node{
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
 | 
					 | 
				
			||||||
				{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: 0}, {Name: "machine3", Score: 0}},
 | 
					 | 
				
			||||||
			label:        "bar",
 | 
					 | 
				
			||||||
			presence:     false,
 | 
					 | 
				
			||||||
			name:         "two matches found, presence false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, test := range tests {
 | 
					 | 
				
			||||||
		t.Run(test.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			snapshot := nodeinfosnapshot.NewSnapshot(nil, test.nodes)
 | 
					 | 
				
			||||||
			labelPrioritizer := &NodeLabelPrioritizer{
 | 
					 | 
				
			||||||
				label:    test.label,
 | 
					 | 
				
			||||||
				presence: test.presence,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			list, err := priorityFunction(labelPrioritizer.CalculateNodeLabelPriorityMap, nil, nil)(nil, snapshot, test.nodes)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				t.Errorf("unexpected error: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// sort the two lists to avoid failures on account of different ordering
 | 
					 | 
				
			||||||
			sortNodeScoreList(test.expectedList)
 | 
					 | 
				
			||||||
			sortNodeScoreList(list)
 | 
					 | 
				
			||||||
			if !reflect.DeepEqual(test.expectedList, list) {
 | 
					 | 
				
			||||||
				t.Errorf("expected %#v, got %#v", test.expectedList, list)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -400,15 +400,35 @@ func RegisterCustomPriorityFunction(policy schedulerapi.PriorityPolicy, args *pl
 | 
				
			|||||||
				Weight: policy.Weight,
 | 
									Weight: policy.Weight,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if policy.Argument.LabelPreference != nil {
 | 
							} else if policy.Argument.LabelPreference != nil {
 | 
				
			||||||
 | 
								// We use the NodeLabel plugin name for all NodeLabel custom priorities.
 | 
				
			||||||
 | 
								// It may get called multiple times but we essentially only register one instance of NodeLabel priority.
 | 
				
			||||||
 | 
								// This name is then used to find the registered plugin and run the plugin instead of the priority.
 | 
				
			||||||
 | 
								name = nodelabel.Name
 | 
				
			||||||
 | 
								if args.NodeLabelArgs == nil {
 | 
				
			||||||
 | 
									args.NodeLabelArgs = &nodelabel.Args{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if policy.Argument.LabelPreference.Presence {
 | 
				
			||||||
 | 
									args.NodeLabelArgs.PresentLabelsPreference = append(args.NodeLabelArgs.PresentLabelsPreference, policy.Argument.LabelPreference.Label)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									args.NodeLabelArgs.AbsentLabelsPreference = append(args.NodeLabelArgs.AbsentLabelsPreference, policy.Argument.LabelPreference.Label)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								schedulerFactoryMutex.RLock()
 | 
				
			||||||
 | 
								weight := policy.Weight
 | 
				
			||||||
 | 
								if existing, ok := priorityFunctionMap[name]; ok {
 | 
				
			||||||
 | 
									// If there are n NodeLabel priority configured in the policy, the weight for the corresponding
 | 
				
			||||||
 | 
									// priority is n*(weight of each priority in policy).
 | 
				
			||||||
 | 
									weight += existing.Weight
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			pcf = &PriorityConfigFactory{
 | 
								pcf = &PriorityConfigFactory{
 | 
				
			||||||
				MapReduceFunction: func(args PluginFactoryArgs) (priorities.PriorityMapFunction, priorities.PriorityReduceFunction) {
 | 
									MapReduceFunction: func(_ PluginFactoryArgs) (priorities.PriorityMapFunction, priorities.PriorityReduceFunction) {
 | 
				
			||||||
					return priorities.NewNodeLabelPriority(
 | 
										return priorities.NewNodeLabelPriority(
 | 
				
			||||||
						policy.Argument.LabelPreference.Label,
 | 
											args.NodeLabelArgs.PresentLabelsPreference,
 | 
				
			||||||
						policy.Argument.LabelPreference.Presence,
 | 
											args.NodeLabelArgs.AbsentLabelsPreference,
 | 
				
			||||||
					)
 | 
										)
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Weight: policy.Weight,
 | 
									Weight: weight,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								schedulerFactoryMutex.RUnlock()
 | 
				
			||||||
		} else if policy.Argument.RequestedToCapacityRatioArguments != nil {
 | 
							} else if policy.Argument.RequestedToCapacityRatioArguments != nil {
 | 
				
			||||||
			scoringFunctionShape, resources := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(policy.Argument.RequestedToCapacityRatioArguments)
 | 
								scoringFunctionShape, resources := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(policy.Argument.RequestedToCapacityRatioArguments)
 | 
				
			||||||
			args.RequestedToCapacityRatioArgs = &requestedtocapacityratio.Args{
 | 
								args.RequestedToCapacityRatioArgs = &requestedtocapacityratio.Args{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,7 +103,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
			wantPrioritizers: sets.NewString(
 | 
								wantPrioritizers: sets.NewString(
 | 
				
			||||||
				"ServiceSpreadingPriority",
 | 
									"ServiceSpreadingPriority",
 | 
				
			||||||
				"TestServiceAntiAffinity",
 | 
									"TestServiceAntiAffinity",
 | 
				
			||||||
				"TestLabelPreference",
 | 
					 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			wantPlugins: map[string][]config.Plugin{
 | 
								wantPlugins: map[string][]config.Plugin{
 | 
				
			||||||
				"FilterPlugin": {
 | 
									"FilterPlugin": {
 | 
				
			||||||
@@ -116,6 +115,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
				"ScorePlugin": {
 | 
									"ScorePlugin": {
 | 
				
			||||||
					{Name: "NodeResourcesLeastAllocated", Weight: 1},
 | 
										{Name: "NodeResourcesLeastAllocated", Weight: 1},
 | 
				
			||||||
 | 
										{Name: "NodeLabel", Weight: 4},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -151,7 +151,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
				"EqualPriority",
 | 
									"EqualPriority",
 | 
				
			||||||
				"SelectorSpreadPriority",
 | 
									"SelectorSpreadPriority",
 | 
				
			||||||
				"TestServiceAntiAffinity",
 | 
									"TestServiceAntiAffinity",
 | 
				
			||||||
				"TestLabelPreference",
 | 
					 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			wantPlugins: map[string][]config.Plugin{
 | 
								wantPlugins: map[string][]config.Plugin{
 | 
				
			||||||
				"FilterPlugin": {
 | 
									"FilterPlugin": {
 | 
				
			||||||
@@ -167,6 +166,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
				"ScorePlugin": {
 | 
									"ScorePlugin": {
 | 
				
			||||||
					{Name: "NodeResourcesBalancedAllocation", Weight: 2},
 | 
										{Name: "NodeResourcesBalancedAllocation", Weight: 2},
 | 
				
			||||||
					{Name: "NodeResourcesLeastAllocated", Weight: 2},
 | 
										{Name: "NodeResourcesLeastAllocated", Weight: 2},
 | 
				
			||||||
 | 
										{Name: "NodeLabel", Weight: 4},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -207,7 +207,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
				"EqualPriority",
 | 
									"EqualPriority",
 | 
				
			||||||
				"SelectorSpreadPriority",
 | 
									"SelectorSpreadPriority",
 | 
				
			||||||
				"TestServiceAntiAffinity",
 | 
									"TestServiceAntiAffinity",
 | 
				
			||||||
				"TestLabelPreference",
 | 
					 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			wantPlugins: map[string][]config.Plugin{
 | 
								wantPlugins: map[string][]config.Plugin{
 | 
				
			||||||
				"FilterPlugin": {
 | 
									"FilterPlugin": {
 | 
				
			||||||
@@ -229,6 +228,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
					{Name: "ImageLocality", Weight: 2},
 | 
										{Name: "ImageLocality", Weight: 2},
 | 
				
			||||||
					{Name: "NodeResourcesLeastAllocated", Weight: 2},
 | 
										{Name: "NodeResourcesLeastAllocated", Weight: 2},
 | 
				
			||||||
					{Name: "NodeAffinity", Weight: 2},
 | 
										{Name: "NodeAffinity", Weight: 2},
 | 
				
			||||||
 | 
										{Name: "NodeLabel", Weight: 4},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -1263,6 +1263,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
				
			|||||||
		"NodeResourcesBalancedAllocation": "BalancedResourceAllocation",
 | 
							"NodeResourcesBalancedAllocation": "BalancedResourceAllocation",
 | 
				
			||||||
		"NodeResourcesMostAllocated":      "MostRequestedPriority",
 | 
							"NodeResourcesMostAllocated":      "MostRequestedPriority",
 | 
				
			||||||
		"RequestedToCapacityRatio":        "RequestedToCapacityRatioPriority",
 | 
							"RequestedToCapacityRatio":        "RequestedToCapacityRatioPriority",
 | 
				
			||||||
 | 
							"NodeLabel":                       "TestLabelPreference",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, tc := range testcases {
 | 
						for _, tc := range testcases {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,6 +98,8 @@ func TestCreateFromConfig(t *testing.T) {
 | 
				
			|||||||
		],
 | 
							],
 | 
				
			||||||
		"priorities" : [
 | 
							"priorities" : [
 | 
				
			||||||
			{"name" : "RackSpread", "weight" : 3, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}},
 | 
								{"name" : "RackSpread", "weight" : 3, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}},
 | 
				
			||||||
 | 
								{"name" : "LabelPreference1", "weight" : 3, "argument" : {"labelPreference" : {"label" : "l1", "presence": true}}},
 | 
				
			||||||
 | 
								{"name" : "LabelPreference2", "weight" : 3, "argument" : {"labelPreference" : {"label" : "l2", "presence": false}}},
 | 
				
			||||||
			{"name" : "PriorityOne", "weight" : 2},
 | 
								{"name" : "PriorityOne", "weight" : 2},
 | 
				
			||||||
			{"name" : "PriorityTwo", "weight" : 1}		]
 | 
								{"name" : "PriorityTwo", "weight" : 1}		]
 | 
				
			||||||
	}`)
 | 
						}`)
 | 
				
			||||||
@@ -114,29 +116,36 @@ func TestCreateFromConfig(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Wrong hardPodAffinitySymmetricWeight, ecpected: %d, got: %d", v1.DefaultHardPodAffinitySymmetricWeight, hpa)
 | 
							t.Errorf("Wrong hardPodAffinitySymmetricWeight, ecpected: %d, got: %d", v1.DefaultHardPodAffinitySymmetricWeight, hpa)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Verify that custom predicates are converted to framework plugins.
 | 
						// Verify that node label predicate/priority are converted to framework plugins.
 | 
				
			||||||
	if !pluginExists(nodelabel.Name, "FilterPlugin", conf) {
 | 
						if _, ok := findPlugin(nodelabel.Name, "FilterPlugin", conf); !ok {
 | 
				
			||||||
		t.Error("NodeLabel plugin not exist in framework.")
 | 
							t.Fatalf("NodeLabel plugin not exist in framework.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Verify that the policy config is converted to plugin config for custom predicates.
 | 
						nodeLabelScorePlugin, ok := findPlugin(nodelabel.Name, "ScorePlugin", conf)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("NodeLabel plugin not exist in framework.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if nodeLabelScorePlugin.Weight != 6 {
 | 
				
			||||||
 | 
							t.Errorf("Wrong weight. Got: %v, want: 6", nodeLabelScorePlugin.Weight)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Verify that the policy config is converted to plugin config for node label predicate/priority.
 | 
				
			||||||
	nodeLabelConfig := findPluginConfig(nodelabel.Name, conf)
 | 
						nodeLabelConfig := findPluginConfig(nodelabel.Name, conf)
 | 
				
			||||||
	encoding, err := json.Marshal(nodeLabelConfig)
 | 
						encoding, err := json.Marshal(nodeLabelConfig)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("Failed to marshal %+v: %v", nodeLabelConfig, err)
 | 
							t.Errorf("Failed to marshal %+v: %v", nodeLabelConfig, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	want := `{"Name":"NodeLabel","Args":{"presentLabels":["zone"],"absentLabels":["foo"]}}`
 | 
						want := `{"Name":"NodeLabel","Args":{"presentLabels":["zone"],"absentLabels":["foo"],"presentLabelsPreference":["l1"],"absentLabelsPreference":["l2"]}}`
 | 
				
			||||||
	if string(encoding) != want {
 | 
						if string(encoding) != want {
 | 
				
			||||||
		t.Errorf("Config for NodeLabel plugin mismatch. got: %v, want: %v", string(encoding), want)
 | 
							t.Errorf("Config for NodeLabel plugin mismatch. got: %v, want: %v", string(encoding), want)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func pluginExists(name, extensionPoint string, schedConf *Config) bool {
 | 
					func findPlugin(name, extensionPoint string, schedConf *Config) (schedulerapi.Plugin, bool) {
 | 
				
			||||||
	for _, pl := range schedConf.Framework.ListPlugins()[extensionPoint] {
 | 
						for _, pl := range schedConf.Framework.ListPlugins()[extensionPoint] {
 | 
				
			||||||
		if pl.Name == name {
 | 
							if pl.Name == name {
 | 
				
			||||||
			return true
 | 
								return pl, true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
						return schedulerapi.Plugin{}, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func findPluginConfig(name string, schedConf *Config) schedulerapi.PluginConfig {
 | 
					func findPluginConfig(name string, schedConf *Config) schedulerapi.PluginConfig {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,6 +256,12 @@ func NewDefaultConfigProducerRegistry() *ConfigProducerRegistry {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						registry.RegisterPriority(nodelabel.Name,
 | 
				
			||||||
 | 
							func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
 | 
				
			||||||
 | 
								plugins.Score = appendToPluginSet(plugins.Score, nodelabel.Name, &args.Weight)
 | 
				
			||||||
 | 
								pluginConfig = append(pluginConfig, makePluginConfig(nodelabel.Name, args.NodeLabelArgs))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	return registry
 | 
						return registry
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ go_library(
 | 
				
			|||||||
    visibility = ["//visibility:public"],
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//pkg/scheduler/algorithm/predicates:go_default_library",
 | 
					        "//pkg/scheduler/algorithm/predicates:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/scheduler/algorithm/priorities:go_default_library",
 | 
				
			||||||
        "//pkg/scheduler/framework/plugins/migration:go_default_library",
 | 
					        "//pkg/scheduler/framework/plugins/migration:go_default_library",
 | 
				
			||||||
        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
					        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
				
			||||||
        "//pkg/scheduler/nodeinfo:go_default_library",
 | 
					        "//pkg/scheduler/nodeinfo:go_default_library",
 | 
				
			||||||
@@ -22,6 +23,7 @@ go_test(
 | 
				
			|||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
					        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
				
			||||||
        "//pkg/scheduler/nodeinfo:go_default_library",
 | 
					        "//pkg/scheduler/nodeinfo:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/scheduler/nodeinfo/snapshot:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	v1 "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/algorithm/predicates"
 | 
						"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
 | 
						"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
 | 
				
			||||||
	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
						framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
 | 
						"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
 | 
				
			||||||
@@ -37,42 +38,56 @@ type Args struct {
 | 
				
			|||||||
	PresentLabels []string `json:"presentLabels,omitempty"`
 | 
						PresentLabels []string `json:"presentLabels,omitempty"`
 | 
				
			||||||
	// AbsentLabels should be absent for the node to be considered a fit for hosting the pod
 | 
						// AbsentLabels should be absent for the node to be considered a fit for hosting the pod
 | 
				
			||||||
	AbsentLabels []string `json:"absentLabels,omitempty"`
 | 
						AbsentLabels []string `json:"absentLabels,omitempty"`
 | 
				
			||||||
 | 
						// Nodes that have labels in the list will get a higher score.
 | 
				
			||||||
 | 
						PresentLabelsPreference []string `json:"presentLabelsPreference,omitempty"`
 | 
				
			||||||
 | 
						// Nodes that don't have labels in the list will get a higher score.
 | 
				
			||||||
 | 
						AbsentLabelsPreference []string `json:"absentLabelsPreference,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// validateArgs validates that PresentLabels and AbsentLabels do not conflict.
 | 
					// validateArgs validates that presentLabels and absentLabels do not conflict.
 | 
				
			||||||
func validateArgs(args *Args) error {
 | 
					func validateNoConflict(presentLabels []string, absentLabels []string) error {
 | 
				
			||||||
	presentLabels := make(map[string]struct{}, len(args.PresentLabels))
 | 
						m := make(map[string]struct{}, len(presentLabels))
 | 
				
			||||||
	for _, l := range args.PresentLabels {
 | 
						for _, l := range presentLabels {
 | 
				
			||||||
		presentLabels[l] = struct{}{}
 | 
							m[l] = struct{}{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, l := range args.AbsentLabels {
 | 
						for _, l := range absentLabels {
 | 
				
			||||||
		if _, ok := presentLabels[l]; ok {
 | 
							if _, ok := m[l]; ok {
 | 
				
			||||||
			return fmt.Errorf("detecting at least one label (e.g., %q) that exist in both the present and absent label list: %+v", l, args)
 | 
								return fmt.Errorf("detecting at least one label (e.g., %q) that exist in both the present(%+v) and absent(%+v) label list", l, presentLabels, absentLabels)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New initializes a new plugin and returns it.
 | 
					// New initializes a new plugin and returns it.
 | 
				
			||||||
func New(plArgs *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
 | 
					func New(plArgs *runtime.Unknown, handle framework.FrameworkHandle) (framework.Plugin, error) {
 | 
				
			||||||
	args := &Args{}
 | 
						args := &Args{}
 | 
				
			||||||
	if err := framework.DecodeInto(plArgs, args); err != nil {
 | 
						if err := framework.DecodeInto(plArgs, args); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := validateArgs(args); err != nil {
 | 
						if err := validateNoConflict(args.PresentLabels, args.AbsentLabels); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if err := validateNoConflict(args.PresentLabelsPreference, args.AbsentLabelsPreference); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Note that the reduce function is always nil therefore it's ignored.
 | 
				
			||||||
 | 
						prioritize, _ := priorities.NewNodeLabelPriority(args.PresentLabelsPreference, args.AbsentLabelsPreference)
 | 
				
			||||||
	return &NodeLabel{
 | 
						return &NodeLabel{
 | 
				
			||||||
		predicate: predicates.NewNodeLabelPredicate(args.PresentLabels, args.AbsentLabels),
 | 
							handle:     handle,
 | 
				
			||||||
 | 
							predicate:  predicates.NewNodeLabelPredicate(args.PresentLabels, args.AbsentLabels),
 | 
				
			||||||
 | 
							prioritize: prioritize,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NodeLabel checks whether a pod can fit based on the node labels which match a filter that it requests.
 | 
					// NodeLabel checks whether a pod can fit based on the node labels which match a filter that it requests.
 | 
				
			||||||
type NodeLabel struct {
 | 
					type NodeLabel struct {
 | 
				
			||||||
	predicate predicates.FitPredicate
 | 
						handle     framework.FrameworkHandle
 | 
				
			||||||
 | 
						predicate  predicates.FitPredicate
 | 
				
			||||||
 | 
						prioritize priorities.PriorityMapFunction
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ framework.FilterPlugin = &NodeLabel{}
 | 
					var _ framework.FilterPlugin = &NodeLabel{}
 | 
				
			||||||
 | 
					var _ framework.ScorePlugin = &NodeLabel{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Name returns name of the plugin. It is used in logs, etc.
 | 
					// Name returns name of the plugin. It is used in logs, etc.
 | 
				
			||||||
func (pl *NodeLabel) Name() string {
 | 
					func (pl *NodeLabel) Name() string {
 | 
				
			||||||
@@ -85,3 +100,19 @@ func (pl *NodeLabel) Filter(ctx context.Context, _ *framework.CycleState, pod *v
 | 
				
			|||||||
	_, reasons, err := pl.predicate(pod, nil, nodeInfo)
 | 
						_, reasons, err := pl.predicate(pod, nil, nodeInfo)
 | 
				
			||||||
	return migration.PredicateResultToFrameworkStatus(reasons, err)
 | 
						return migration.PredicateResultToFrameworkStatus(reasons, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Score invoked at the score extension point.
 | 
				
			||||||
 | 
					func (pl *NodeLabel) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
 | 
				
			||||||
 | 
						nodeInfo, err := pl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Note that node label priority function doesn't use metadata, hence passing nil here.
 | 
				
			||||||
 | 
						s, err := pl.prioritize(pod, nil, nodeInfo)
 | 
				
			||||||
 | 
						return s.Score, migration.ErrorToFrameworkStatus(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ScoreExtensions of the Score plugin.
 | 
				
			||||||
 | 
					func (pl *NodeLabel) ScoreExtensions() framework.ScoreExtensions {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,14 +25,48 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
						framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
				
			||||||
	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
 | 
						schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
 | 
				
			||||||
 | 
						nodeinfosnapshot "k8s.io/kubernetes/pkg/scheduler/nodeinfo/snapshot"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestValidateNodeLabelArgs(t *testing.T) {
 | 
					func TestValidateNodeLabelArgs(t *testing.T) {
 | 
				
			||||||
	// "bar" exists in both present and absent labels therefore validatio should fail.
 | 
						tests := []struct {
 | 
				
			||||||
	args := &runtime.Unknown{Raw: []byte(`{"presentLabels" : ["foo", "bar"], "absentLabels" : ["bar", "baz"]}`)}
 | 
							name string
 | 
				
			||||||
	_, err := New(args, nil)
 | 
							args string
 | 
				
			||||||
	if err == nil {
 | 
							err  bool
 | 
				
			||||||
		t.Fatal("Plugin initialization should fail.")
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "happy case",
 | 
				
			||||||
 | 
								args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["baz"]}`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "label presence conflict",
 | 
				
			||||||
 | 
								// "bar" exists in both present and absent labels therefore validation should fail.
 | 
				
			||||||
 | 
								args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["bar", "baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["baz"]}`,
 | 
				
			||||||
 | 
								err:  true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "label preference conflict",
 | 
				
			||||||
 | 
								// "bar" exists in both present and absent labels preferences therefore validation should fail.
 | 
				
			||||||
 | 
								args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["bar", "baz"]}`,
 | 
				
			||||||
 | 
								err:  true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "both label presence and preference conflict",
 | 
				
			||||||
 | 
								args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["bar", "baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["bar", "baz"]}`,
 | 
				
			||||||
 | 
								err:  true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								args := &runtime.Unknown{Raw: []byte(test.args)}
 | 
				
			||||||
 | 
								_, err := New(args, nil)
 | 
				
			||||||
 | 
								if test.err && err == nil {
 | 
				
			||||||
 | 
									t.Fatal("Plugin initialization should fail.")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !test.err && err != nil {
 | 
				
			||||||
 | 
									t.Fatalf("Plugin initialization shouldn't fail: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -115,3 +149,98 @@ func TestNodeLabelFilter(t *testing.T) {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNodeLabelScore(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							rawArgs string
 | 
				
			||||||
 | 
							want    int64
 | 
				
			||||||
 | 
							name    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["foo"]}`,
 | 
				
			||||||
 | 
								name:    "one present label match",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    0,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["somelabel"]}`,
 | 
				
			||||||
 | 
								name:    "one present label mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["foo", "bar"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels match",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    0,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore / 2,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["foo", "somelabel"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels only one matches",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    0,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["foo"]}`,
 | 
				
			||||||
 | 
								name:    "one absent label match",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["somelabel"]}`,
 | 
				
			||||||
 | 
								name:    "one absent label mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    0,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["foo", "bar"]}`,
 | 
				
			||||||
 | 
								name:    "two absent labels match",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
 | 
				
			||||||
 | 
								name:    "two absent labels mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore / 2,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["foo", "somelabel"]}`,
 | 
				
			||||||
 | 
								name:    "two absent labels only one matches",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    framework.MaxNodeScore,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels match, two absent labels mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    0,
 | 
				
			||||||
 | 
								rawArgs: `{"absentLabelsPreference" : ["foo", "bar"], "presentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels both mismatch, two absent labels both match",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								want:    3 * framework.MaxNodeScore / 4,
 | 
				
			||||||
 | 
								rawArgs: `{"presentLabelsPreference" : ["foo", "somelabel"], "absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
 | 
				
			||||||
 | 
								name:    "two present labels one matches, two absent labels mismatch",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								state := framework.NewCycleState()
 | 
				
			||||||
 | 
								node := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: map[string]string{"foo": "", "bar": ""}}}
 | 
				
			||||||
 | 
								fh, _ := framework.NewFramework(nil, nil, nil, framework.WithNodeInfoSnapshot(nodeinfosnapshot.NewSnapshot(nil, []*v1.Node{node})))
 | 
				
			||||||
 | 
								args := &runtime.Unknown{Raw: []byte(test.rawArgs)}
 | 
				
			||||||
 | 
								p, err := New(args, fh)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatalf("Failed to create plugin: %+v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								nodeName := node.ObjectMeta.Name
 | 
				
			||||||
 | 
								score, status := p.(framework.ScorePlugin).Score(context.Background(), state, nil, nodeName)
 | 
				
			||||||
 | 
								if !status.IsSuccess() {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error: %v", status)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if test.want != score {
 | 
				
			||||||
 | 
									t.Errorf("Wrong score. got %#v, want %#v", score, test.want)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user