Merge pull request #83849 from draveness/feature/node-locality-as-score-plugin
feat: implement imagelocality as a score plugin
This commit is contained in:
		@@ -171,7 +171,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"NodeAffinityPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -185,6 +184,9 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "NodeResources"},
 | 
			
		||||
					{Name: "VolumeRestrictions"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -235,7 +237,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -250,7 +251,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "VolumeRestrictions"},
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -305,7 +309,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -322,7 +325,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "VolumeRestrictions"},
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// Do not change this JSON after the corresponding release has been tagged.
 | 
			
		||||
@@ -386,7 +392,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -403,7 +408,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "VolumeRestrictions"},
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -480,7 +488,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -497,7 +504,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "VolumeRestrictions"},
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -575,7 +585,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -593,7 +602,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -676,7 +688,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -694,7 +705,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -789,7 +803,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -808,7 +821,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -905,7 +921,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -924,7 +939,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -1021,7 +1039,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -1040,7 +1057,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -1141,7 +1161,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
			),
 | 
			
		||||
			wantPrioritizers: sets.NewString(
 | 
			
		||||
				"EqualPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
				"LeastRequestedPriority",
 | 
			
		||||
				"BalancedResourceAllocation",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
@@ -1160,7 +1179,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 2}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 2},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 2},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantExtenders: []schedulerapi.ExtenderConfig{{
 | 
			
		||||
				URLPrefix:        "/prefix",
 | 
			
		||||
@@ -1192,6 +1214,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	scoreToPriorityMap := map[string]string{
 | 
			
		||||
		"TaintToleration": "TaintTolerationPriority",
 | 
			
		||||
		"ImageLocality":   "ImageLocalityPriority",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for v, tc := range schedulerFiles {
 | 
			
		||||
@@ -1245,7 +1268,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
 | 
			
		||||
				seenPredicates.Insert(filterToPredicateMap[p.Name])
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
			for _, p := range gotPlugins["FilterPlugin"] {
 | 
			
		||||
			for _, p := range gotPlugins["ScorePlugin"] {
 | 
			
		||||
				seenPriorities.Insert(scoreToPriorityMap[p.Name])
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ go_library(
 | 
			
		||||
        "//pkg/scheduler/algorithm/predicates:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/algorithm/priorities:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/apis/config:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/imagelocality:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/nodeaffinity:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/nodename:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/noderesources:go_default_library",
 | 
			
		||||
@@ -37,6 +38,7 @@ filegroup(
 | 
			
		||||
    srcs = [
 | 
			
		||||
        ":package-srcs",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/examples:all-srcs",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/imagelocality:all-srcs",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/migration:all-srcs",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/nodeaffinity:all-srcs",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/nodename:all-srcs",
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/apis/config"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/imagelocality"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
 | 
			
		||||
@@ -56,6 +57,7 @@ type RegistryArgs struct {
 | 
			
		||||
// runs custom plugins, can pass a different Registry when initializing the scheduler.
 | 
			
		||||
func NewDefaultRegistry(args *RegistryArgs) framework.Registry {
 | 
			
		||||
	return framework.Registry{
 | 
			
		||||
		imagelocality.Name:   imagelocality.New,
 | 
			
		||||
		tainttoleration.Name: tainttoleration.New,
 | 
			
		||||
		noderesources.Name:   noderesources.New,
 | 
			
		||||
		nodename.Name:        nodename.New,
 | 
			
		||||
@@ -128,6 +130,12 @@ func NewDefaultConfigProducerRegistry() *ConfigProducerRegistry {
 | 
			
		||||
			return
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	registry.RegisterPriority(priorities.ImageLocalityPriority,
 | 
			
		||||
		func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
 | 
			
		||||
			plugins.Score = appendToPluginSet(plugins.Score, imagelocality.Name, &args.Weight)
 | 
			
		||||
			return
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	return registry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								pkg/scheduler/framework/plugins/imagelocality/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/scheduler/framework/plugins/imagelocality/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 | 
			
		||||
 | 
			
		||||
go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = ["image_locality.go"],
 | 
			
		||||
    importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/imagelocality",
 | 
			
		||||
    visibility = ["//visibility:public"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/scheduler/algorithm/priorities:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/migration:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = ["image_locality_test.go"],
 | 
			
		||||
    embed = [":go_default_library"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/scheduler/algorithm/priorities:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/plugins/migration:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/framework/v1alpha1:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/nodeinfo:go_default_library",
 | 
			
		||||
        "//pkg/scheduler/testing:go_default_library",
 | 
			
		||||
        "//pkg/util/parsers:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/apps/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",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
filegroup(
 | 
			
		||||
    name = "package-srcs",
 | 
			
		||||
    srcs = glob(["**"]),
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    visibility = ["//visibility:private"],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
filegroup(
 | 
			
		||||
    name = "all-srcs",
 | 
			
		||||
    srcs = [":package-srcs"],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    visibility = ["//visibility:public"],
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 imagelocality
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
 | 
			
		||||
	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var mb int64 = 1024 * 1024
 | 
			
		||||
 | 
			
		||||
// ImageLocality is a plugin that checks if a pod tolerates a node's taints.
 | 
			
		||||
type ImageLocality struct {
 | 
			
		||||
	handle framework.FrameworkHandle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ = framework.ScorePlugin(&ImageLocality{})
 | 
			
		||||
 | 
			
		||||
// Name is the name of the plugin used in the plugin registry and configurations.
 | 
			
		||||
const Name = "ImageLocality"
 | 
			
		||||
 | 
			
		||||
// Name returns name of the plugin. It is used in logs, etc.
 | 
			
		||||
func (pl *ImageLocality) Name() string {
 | 
			
		||||
	return Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Score invoked at the score extension point.
 | 
			
		||||
func (pl *ImageLocality) Score(state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
 | 
			
		||||
	nodeInfo, exist := pl.handle.NodeInfoSnapshot().NodeInfoMap[nodeName]
 | 
			
		||||
	if !exist {
 | 
			
		||||
		return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	meta := migration.PriorityMetadata(state)
 | 
			
		||||
	s, err := priorities.ImageLocalityPriorityMap(pod, meta, nodeInfo)
 | 
			
		||||
	return s.Score, migration.ErrorToFrameworkStatus(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScoreExtensions of the Score plugin.
 | 
			
		||||
func (pl *ImageLocality) ScoreExtensions() framework.ScoreExtensions {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New initializes a new plugin and returns it.
 | 
			
		||||
func New(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
 | 
			
		||||
	return &ImageLocality{handle: h}, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,229 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 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 imagelocality
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	apps "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration"
 | 
			
		||||
	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
 | 
			
		||||
	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
 | 
			
		||||
	schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/parsers"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestImageLocalityPriority(t *testing.T) {
 | 
			
		||||
	test40250 := v1.PodSpec{
 | 
			
		||||
		Containers: []v1.Container{
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/40",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/250",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	test40300 := v1.PodSpec{
 | 
			
		||||
		Containers: []v1.Container{
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/40",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/300",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testMinMax := v1.PodSpec{
 | 
			
		||||
		Containers: []v1.Container{
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/10",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Image: "gcr.io/2000",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	node403002000 := v1.NodeStatus{
 | 
			
		||||
		Images: []v1.ContainerImage{
 | 
			
		||||
			{
 | 
			
		||||
				Names: []string{
 | 
			
		||||
					"gcr.io/40:" + parsers.DefaultImageTag,
 | 
			
		||||
					"gcr.io/40:v1",
 | 
			
		||||
					"gcr.io/40:v1",
 | 
			
		||||
				},
 | 
			
		||||
				SizeBytes: int64(40 * mb),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Names: []string{
 | 
			
		||||
					"gcr.io/300:" + parsers.DefaultImageTag,
 | 
			
		||||
					"gcr.io/300:v1",
 | 
			
		||||
				},
 | 
			
		||||
				SizeBytes: int64(300 * mb),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Names: []string{
 | 
			
		||||
					"gcr.io/2000:" + parsers.DefaultImageTag,
 | 
			
		||||
				},
 | 
			
		||||
				SizeBytes: int64(2000 * mb),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	node25010 := v1.NodeStatus{
 | 
			
		||||
		Images: []v1.ContainerImage{
 | 
			
		||||
			{
 | 
			
		||||
				Names: []string{
 | 
			
		||||
					"gcr.io/250:" + parsers.DefaultImageTag,
 | 
			
		||||
				},
 | 
			
		||||
				SizeBytes: int64(250 * mb),
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Names: []string{
 | 
			
		||||
					"gcr.io/10:" + parsers.DefaultImageTag,
 | 
			
		||||
					"gcr.io/10:v1",
 | 
			
		||||
				},
 | 
			
		||||
				SizeBytes: int64(10 * mb),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nodeWithNoImages := v1.NodeStatus{}
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		pod          *v1.Pod
 | 
			
		||||
		pods         []*v1.Pod
 | 
			
		||||
		nodes        []*v1.Node
 | 
			
		||||
		expectedList framework.NodeScoreList
 | 
			
		||||
		name         string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			// Pod: gcr.io/40 gcr.io/250
 | 
			
		||||
 | 
			
		||||
			// Node1
 | 
			
		||||
			// Image: gcr.io/40:latest 40MB
 | 
			
		||||
			// Score: 0 (40M/2 < 23M, min-threshold)
 | 
			
		||||
 | 
			
		||||
			// Node2
 | 
			
		||||
			// Image: gcr.io/250:latest 250MB
 | 
			
		||||
			// Score: 100 * (250M/2 - 23M)/(1000M - 23M) = 100
 | 
			
		||||
			pod:          &v1.Pod{Spec: test40250},
 | 
			
		||||
			nodes:        []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
 | 
			
		||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 10}},
 | 
			
		||||
			name:         "two images spread on two nodes, prefer the larger image one",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Pod: gcr.io/40 gcr.io/300
 | 
			
		||||
 | 
			
		||||
			// Node1
 | 
			
		||||
			// Image: gcr.io/40:latest 40MB, gcr.io/300:latest 300MB
 | 
			
		||||
			// Score: 100 * ((40M + 300M)/2 - 23M)/(1000M - 23M) = 15
 | 
			
		||||
 | 
			
		||||
			// Node2
 | 
			
		||||
			// Image: not present
 | 
			
		||||
			// Score: 0
 | 
			
		||||
			pod:          &v1.Pod{Spec: test40300},
 | 
			
		||||
			nodes:        []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
 | 
			
		||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 15}, {Name: "machine2", Score: 0}},
 | 
			
		||||
			name:         "two images on one node, prefer this node",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Pod: gcr.io/2000 gcr.io/10
 | 
			
		||||
 | 
			
		||||
			// Node1
 | 
			
		||||
			// Image: gcr.io/2000:latest 2000MB
 | 
			
		||||
			// Score: 100 (2000M/2 >= 1000M, max-threshold)
 | 
			
		||||
 | 
			
		||||
			// Node2
 | 
			
		||||
			// Image: gcr.io/10:latest 10MB
 | 
			
		||||
			// Score: 0 (10M/2 < 23M, min-threshold)
 | 
			
		||||
			pod:          &v1.Pod{Spec: testMinMax},
 | 
			
		||||
			nodes:        []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
 | 
			
		||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: 0}},
 | 
			
		||||
			name:         "if exceed limit, use limit",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			// Pod: gcr.io/2000 gcr.io/10
 | 
			
		||||
 | 
			
		||||
			// Node1
 | 
			
		||||
			// Image: gcr.io/2000:latest 2000MB
 | 
			
		||||
			// Score: 100 * (2000M/3 - 23M)/(1000M - 23M) = 65
 | 
			
		||||
 | 
			
		||||
			// Node2
 | 
			
		||||
			// Image: gcr.io/10:latest 10MB
 | 
			
		||||
			// Score: 0 (10M/2 < 23M, min-threshold)
 | 
			
		||||
 | 
			
		||||
			// Node3
 | 
			
		||||
			// Image:
 | 
			
		||||
			// Score: 0
 | 
			
		||||
			pod:          &v1.Pod{Spec: testMinMax},
 | 
			
		||||
			nodes:        []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010), makeImageNode("machine3", nodeWithNoImages)},
 | 
			
		||||
			expectedList: []framework.NodeScore{{Name: "machine1", Score: 65}, {Name: "machine2", Score: 0}, {Name: "machine3", Score: 0}},
 | 
			
		||||
			name:         "if exceed limit, use limit (with node which has no images present)",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			metaDataProducer := priorities.NewPriorityMetadataFactory(
 | 
			
		||||
				schedulertesting.FakeServiceLister([]*v1.Service{}),
 | 
			
		||||
				schedulertesting.FakeControllerLister([]*v1.ReplicationController{}),
 | 
			
		||||
				schedulertesting.FakeReplicaSetLister([]*apps.ReplicaSet{}),
 | 
			
		||||
				schedulertesting.FakeStatefulSetLister([]*apps.StatefulSet{}))
 | 
			
		||||
 | 
			
		||||
			nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(nil, test.nodes)
 | 
			
		||||
 | 
			
		||||
			meta := metaDataProducer(test.pod, nodeNameToInfo)
 | 
			
		||||
 | 
			
		||||
			state := framework.NewCycleState()
 | 
			
		||||
			state.Write(migration.PrioritiesStateKey, &migration.PrioritiesStateData{Reference: meta})
 | 
			
		||||
 | 
			
		||||
			fh, _ := framework.NewFramework(nil, nil, nil)
 | 
			
		||||
			snapshot := fh.NodeInfoSnapshot()
 | 
			
		||||
			snapshot.NodeInfoMap = schedulernodeinfo.CreateNodeNameToInfoMap(nil, test.nodes)
 | 
			
		||||
 | 
			
		||||
			p, _ := New(nil, fh)
 | 
			
		||||
			var gotList framework.NodeScoreList
 | 
			
		||||
			for _, n := range test.nodes {
 | 
			
		||||
				nodeName := n.ObjectMeta.Name
 | 
			
		||||
				score, status := p.(framework.ScorePlugin).Score(state, test.pod, nodeName)
 | 
			
		||||
				if !status.IsSuccess() {
 | 
			
		||||
					t.Errorf("unexpected error: %v", status)
 | 
			
		||||
				}
 | 
			
		||||
				gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !reflect.DeepEqual(test.expectedList, gotList) {
 | 
			
		||||
				t.Errorf("expected:\n\t%+v,\ngot:\n\t%+v", test.expectedList, gotList)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeImageNode(node string, status v1.NodeStatus) *v1.Node {
 | 
			
		||||
	return &v1.Node{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{Name: node},
 | 
			
		||||
		Status:     status,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -19,7 +19,7 @@ package tainttoleration
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
 | 
			
		||||
 
 | 
			
		||||
@@ -144,7 +144,6 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
 | 
			
		||||
				"NodeAffinityPriority",
 | 
			
		||||
				"NodePreferAvoidPodsPriority",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
			),
 | 
			
		||||
			expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
 | 
			
		||||
				"FilterPlugin": {
 | 
			
		||||
@@ -152,7 +151,10 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 1}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 1},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 1},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -213,7 +215,6 @@ kind: Policy
 | 
			
		||||
				"NodeAffinityPriority",
 | 
			
		||||
				"NodePreferAvoidPodsPriority",
 | 
			
		||||
				"SelectorSpreadPriority",
 | 
			
		||||
				"ImageLocalityPriority",
 | 
			
		||||
			),
 | 
			
		||||
			expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
 | 
			
		||||
				"FilterPlugin": {
 | 
			
		||||
@@ -221,7 +222,10 @@ kind: Policy
 | 
			
		||||
					{Name: "TaintToleration"},
 | 
			
		||||
					{Name: "VolumeBinding"},
 | 
			
		||||
				},
 | 
			
		||||
				"ScorePlugin": {{Name: "TaintToleration", Weight: 1}},
 | 
			
		||||
				"ScorePlugin": {
 | 
			
		||||
					{Name: "ImageLocality", Weight: 1},
 | 
			
		||||
					{Name: "TaintToleration", Weight: 1},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user