Move RequestedToCapacityRatio argument processing to its plugin
This commit is contained in:
		| @@ -32,7 +32,6 @@ go_library( | |||||||
|         "//pkg/scheduler/framework/plugins/volumezone:go_default_library", |         "//pkg/scheduler/framework/plugins/volumezone:go_default_library", | ||||||
|         "//pkg/scheduler/framework/v1alpha1:go_default_library", |         "//pkg/scheduler/framework/v1alpha1:go_default_library", | ||||||
|         "//pkg/scheduler/volumebinder:go_default_library", |         "//pkg/scheduler/volumebinder:go_default_library", | ||||||
|         "//staging/src/k8s.io/api/core/v1:go_default_library", |  | ||||||
|         "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", |         "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", | ||||||
|         "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", |         "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", | ||||||
|         "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", |         "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", | ||||||
| @@ -82,9 +81,6 @@ go_test( | |||||||
|     embed = [":go_default_library"], |     embed = [":go_default_library"], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/scheduler/apis/config:go_default_library", |         "//pkg/scheduler/apis/config:go_default_library", | ||||||
|         "//pkg/scheduler/framework/plugins/noderesources:go_default_library", |  | ||||||
|         "//staging/src/k8s.io/api/core/v1:go_default_library", |  | ||||||
|         "//vendor/github.com/google/go-cmp/cmp:go_default_library", |         "//vendor/github.com/google/go-cmp/cmp:go_default_library", | ||||||
|         "//vendor/github.com/stretchr/testify/assert:go_default_library", |  | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -20,7 +20,6 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"k8s.io/api/core/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||||
| @@ -46,7 +45,6 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumebinding" | 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumebinding" | ||||||
| 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumerestrictions" | 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumerestrictions" | ||||||
| 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumezone" | 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumezone" | ||||||
| 	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // LegacyRegistry is used to store current state of registered predicates and priorities. | // LegacyRegistry is used to store current state of registered predicates and priorities. | ||||||
| @@ -504,10 +502,8 @@ func (lr *LegacyRegistry) ProcessPriorityPolicy(policy config.PriorityPolicy, co | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if policy.Argument.RequestedToCapacityRatioArguments != nil { | 	if policy.Argument.RequestedToCapacityRatioArguments != nil { | ||||||
| 		scoringFunctionShape, resources := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(policy.Argument.RequestedToCapacityRatioArguments) |  | ||||||
| 		configProducerArgs.RequestedToCapacityRatioArgs = &noderesources.RequestedToCapacityRatioArgs{ | 		configProducerArgs.RequestedToCapacityRatioArgs = &noderesources.RequestedToCapacityRatioArgs{ | ||||||
| 			FunctionShape:       scoringFunctionShape, | 			RequestedToCapacityRatioArguments: *policy.Argument.RequestedToCapacityRatioArguments, | ||||||
| 			ResourceToWeightMap: resources, |  | ||||||
| 		} | 		} | ||||||
| 		// We do not allow specifying the name for custom plugins, see #83472 | 		// We do not allow specifying the name for custom plugins, see #83472 | ||||||
| 		priorityName = noderesources.RequestedToCapacityRatioName | 		priorityName = noderesources.RequestedToCapacityRatioName | ||||||
| @@ -516,37 +512,6 @@ func (lr *LegacyRegistry) ProcessPriorityPolicy(policy config.PriorityPolicy, co | |||||||
| 	return priorityName | 	return priorityName | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO(ahg-g): move to RequestedToCapacityRatio plugin. |  | ||||||
| func buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(arguments *config.RequestedToCapacityRatioArguments) (noderesources.FunctionShape, noderesources.ResourceToWeightMap) { |  | ||||||
| 	n := len(arguments.Shape) |  | ||||||
| 	points := make([]noderesources.FunctionShapePoint, 0, n) |  | ||||||
| 	for _, point := range arguments.Shape { |  | ||||||
| 		points = append(points, noderesources.FunctionShapePoint{ |  | ||||||
| 			Utilization: int64(point.Utilization), |  | ||||||
| 			// MaxCustomPriorityScore may diverge from the max score used in the scheduler and defined by MaxNodeScore, |  | ||||||
| 			// therefore we need to scale the score returned by requested to capacity ratio to the score range |  | ||||||
| 			// used by the scheduler. |  | ||||||
| 			Score: int64(point.Score) * (framework.MaxNodeScore / config.MaxCustomPriorityScore), |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 	shape, err := noderesources.NewFunctionShape(points) |  | ||||||
| 	if err != nil { |  | ||||||
| 		klog.Fatalf("invalid RequestedToCapacityRatioPriority arguments: %s", err.Error()) |  | ||||||
| 	} |  | ||||||
| 	resourceToWeightMap := make(noderesources.ResourceToWeightMap) |  | ||||||
| 	if len(arguments.Resources) == 0 { |  | ||||||
| 		resourceToWeightMap = noderesources.DefaultRequestedRatioResources |  | ||||||
| 		return shape, resourceToWeightMap |  | ||||||
| 	} |  | ||||||
| 	for _, resource := range arguments.Resources { |  | ||||||
| 		resourceToWeightMap[v1.ResourceName(resource.Name)] = resource.Weight |  | ||||||
| 		if resource.Weight == 0 { |  | ||||||
| 			resourceToWeightMap[v1.ResourceName(resource.Name)] = 1 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return shape, resourceToWeightMap |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func validatePredicateOrDie(predicate config.PredicatePolicy) { | func validatePredicateOrDie(predicate config.PredicatePolicy) { | ||||||
| 	if predicate.Argument != nil { | 	if predicate.Argument != nil { | ||||||
| 		numArgs := 0 | 		numArgs := 0 | ||||||
|   | |||||||
| @@ -22,61 +22,9 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/google/go-cmp/cmp" | 	"github.com/google/go-cmp/cmp" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| 	v1 "k8s.io/api/core/v1" |  | ||||||
| 	"k8s.io/kubernetes/pkg/scheduler/apis/config" | 	"k8s.io/kubernetes/pkg/scheduler/apis/config" | ||||||
| 	schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config" |  | ||||||
| 	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestBuildScoringFunctionShapeFromRequestedToCapacityRatioArguments(t *testing.T) { |  | ||||||
| 	arguments := schedulerapi.RequestedToCapacityRatioArguments{ |  | ||||||
| 		Shape: []schedulerapi.UtilizationShapePoint{ |  | ||||||
| 			{Utilization: 10, Score: 1}, |  | ||||||
| 			{Utilization: 30, Score: 5}, |  | ||||||
| 			{Utilization: 70, Score: 2}, |  | ||||||
| 		}, |  | ||||||
| 		Resources: []schedulerapi.ResourceSpec{ |  | ||||||
| 			{Name: string(v1.ResourceCPU)}, |  | ||||||
| 			{Name: string(v1.ResourceMemory)}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	builtShape, resources := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(&arguments) |  | ||||||
| 	expectedShape, _ := noderesources.NewFunctionShape([]noderesources.FunctionShapePoint{ |  | ||||||
| 		{Utilization: 10, Score: 10}, |  | ||||||
| 		{Utilization: 30, Score: 50}, |  | ||||||
| 		{Utilization: 70, Score: 20}, |  | ||||||
| 	}) |  | ||||||
| 	expectedResources := noderesources.ResourceToWeightMap{ |  | ||||||
| 		v1.ResourceCPU:    1, |  | ||||||
| 		v1.ResourceMemory: 1, |  | ||||||
| 	} |  | ||||||
| 	assert.Equal(t, expectedShape, builtShape) |  | ||||||
| 	assert.Equal(t, expectedResources, resources) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestBuildScoringFunctionShapeFromRequestedToCapacityRatioArgumentsNilResourceToWeightMap(t *testing.T) { |  | ||||||
| 	arguments := schedulerapi.RequestedToCapacityRatioArguments{ |  | ||||||
| 		Shape: []schedulerapi.UtilizationShapePoint{ |  | ||||||
| 			{Utilization: 10, Score: 1}, |  | ||||||
| 			{Utilization: 30, Score: 5}, |  | ||||||
| 			{Utilization: 70, Score: 2}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	builtShape, resources := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(&arguments) |  | ||||||
| 	expectedShape, _ := noderesources.NewFunctionShape([]noderesources.FunctionShapePoint{ |  | ||||||
| 		{Utilization: 10, Score: 10}, |  | ||||||
| 		{Utilization: 30, Score: 50}, |  | ||||||
| 		{Utilization: 70, Score: 20}, |  | ||||||
| 	}) |  | ||||||
| 	expectedResources := noderesources.ResourceToWeightMap{ |  | ||||||
| 		v1.ResourceCPU:    1, |  | ||||||
| 		v1.ResourceMemory: 1, |  | ||||||
| 	} |  | ||||||
| 	assert.Equal(t, expectedShape, builtShape) |  | ||||||
| 	assert.Equal(t, expectedResources, resources) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func produceConfig(keys []string, producersMap map[string]ConfigProducer, args ConfigProducerArgs) (*config.Plugins, []config.PluginConfig, error) { | func produceConfig(keys []string, producersMap map[string]ConfigProducer, args ConfigProducerArgs) (*config.Plugins, []config.PluginConfig, error) { | ||||||
| 	var plugins config.Plugins | 	var plugins config.Plugins | ||||||
| 	var pluginConfig []config.PluginConfig | 	var pluginConfig []config.PluginConfig | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ go_library( | |||||||
|         "//pkg/features:go_default_library", |         "//pkg/features:go_default_library", | ||||||
|         "//pkg/scheduler/algorithm/predicates:go_default_library", |         "//pkg/scheduler/algorithm/predicates:go_default_library", | ||||||
|         "//pkg/scheduler/algorithm/priorities/util:go_default_library", |         "//pkg/scheduler/algorithm/priorities/util:go_default_library", | ||||||
|  |         "//pkg/scheduler/apis/config: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", | ||||||
|   | |||||||
| @@ -74,7 +74,7 @@ func NewBalancedAllocation(_ *runtime.Unknown, h framework.FrameworkHandle) (fra | |||||||
| 		resourceAllocationScorer: resourceAllocationScorer{ | 		resourceAllocationScorer: resourceAllocationScorer{ | ||||||
| 			BalancedAllocationName, | 			BalancedAllocationName, | ||||||
| 			balancedResourceScorer, | 			balancedResourceScorer, | ||||||
| 			DefaultRequestedRatioResources, | 			defaultRequestedRatioResources, | ||||||
| 		}, | 		}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -69,14 +69,14 @@ func NewLeastAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framewo | |||||||
| 		resourceAllocationScorer: resourceAllocationScorer{ | 		resourceAllocationScorer: resourceAllocationScorer{ | ||||||
| 			LeastAllocatedName, | 			LeastAllocatedName, | ||||||
| 			leastResourceScorer, | 			leastResourceScorer, | ||||||
| 			DefaultRequestedRatioResources, | 			defaultRequestedRatioResources, | ||||||
| 		}, | 		}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func leastResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 { | func leastResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 { | ||||||
| 	var nodeScore, weightSum int64 | 	var nodeScore, weightSum int64 | ||||||
| 	for resource, weight := range DefaultRequestedRatioResources { | 	for resource, weight := range defaultRequestedRatioResources { | ||||||
| 		resourceScore := leastRequestedScore(requested[resource], allocable[resource]) | 		resourceScore := leastRequestedScore(requested[resource], allocable[resource]) | ||||||
| 		nodeScore += resourceScore * weight | 		nodeScore += resourceScore * weight | ||||||
| 		weightSum += weight | 		weightSum += weight | ||||||
|   | |||||||
| @@ -67,14 +67,14 @@ func NewMostAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framewor | |||||||
| 		resourceAllocationScorer: resourceAllocationScorer{ | 		resourceAllocationScorer: resourceAllocationScorer{ | ||||||
| 			MostAllocatedName, | 			MostAllocatedName, | ||||||
| 			mostResourceScorer, | 			mostResourceScorer, | ||||||
| 			DefaultRequestedRatioResources, | 			defaultRequestedRatioResources, | ||||||
| 		}, | 		}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func mostResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 { | func mostResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 { | ||||||
| 	var nodeScore, weightSum int64 | 	var nodeScore, weightSum int64 | ||||||
| 	for resource, weight := range DefaultRequestedRatioResources { | 	for resource, weight := range defaultRequestedRatioResources { | ||||||
| 		resourceScore := mostRequestedScore(requested[resource], allocable[resource]) | 		resourceScore := mostRequestedScore(requested[resource], allocable[resource]) | ||||||
| 		nodeScore += resourceScore * weight | 		nodeScore += resourceScore * weight | ||||||
| 		weightSum += weight | 		weightSum += weight | ||||||
|   | |||||||
| @@ -24,6 +24,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/klog" | 	"k8s.io/klog" | ||||||
|  | 	"k8s.io/kubernetes/pkg/scheduler/apis/config" | ||||||
| 	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" | 	framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -36,37 +37,61 @@ const ( | |||||||
| 	maxScore                     = framework.MaxNodeScore | 	maxScore                     = framework.MaxNodeScore | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // FunctionShape represents shape of scoring function. |  | ||||||
| // For safety use NewFunctionShape which performs precondition checks for struct creation. |  | ||||||
| type FunctionShape []FunctionShapePoint |  | ||||||
|  |  | ||||||
| // FunctionShapePoint represents single point in scoring function shape. |  | ||||||
| type FunctionShapePoint struct { |  | ||||||
| 	// Utilization is function argument. |  | ||||||
| 	Utilization int64 |  | ||||||
| 	// Score is function value. |  | ||||||
| 	Score int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RequestedToCapacityRatioArgs holds the args that are used to configure the plugin. | // RequestedToCapacityRatioArgs holds the args that are used to configure the plugin. | ||||||
| type RequestedToCapacityRatioArgs struct { | type RequestedToCapacityRatioArgs struct { | ||||||
| 	FunctionShape       FunctionShape | 	config.RequestedToCapacityRatioArguments | ||||||
| 	ResourceToWeightMap ResourceToWeightMap | } | ||||||
|  |  | ||||||
|  | type functionShape []functionShapePoint | ||||||
|  |  | ||||||
|  | type functionShapePoint struct { | ||||||
|  | 	// Utilization is function argument. | ||||||
|  | 	utilization int64 | ||||||
|  | 	// Score is function value. | ||||||
|  | 	score int64 | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewRequestedToCapacityRatio initializes a new plugin and returns it. | // NewRequestedToCapacityRatio initializes a new plugin and returns it. | ||||||
| func NewRequestedToCapacityRatio(plArgs *runtime.Unknown, handle framework.FrameworkHandle) (framework.Plugin, error) { | func NewRequestedToCapacityRatio(plArgs *runtime.Unknown, handle framework.FrameworkHandle) (framework.Plugin, error) { | ||||||
| 	args := &RequestedToCapacityRatioArgs{} | 	args := &config.RequestedToCapacityRatioArguments{} | ||||||
| 	if err := framework.DecodeInto(plArgs, args); err != nil { | 	if err := framework.DecodeInto(plArgs, args); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	shape := make([]functionShapePoint, 0, len(args.Shape)) | ||||||
|  | 	for _, point := range args.Shape { | ||||||
|  | 		shape = append(shape, functionShapePoint{ | ||||||
|  | 			utilization: int64(point.Utilization), | ||||||
|  | 			// MaxCustomPriorityScore may diverge from the max score used in the scheduler and defined by MaxNodeScore, | ||||||
|  | 			// therefore we need to scale the score returned by requested to capacity ratio to the score range | ||||||
|  | 			// used by the scheduler. | ||||||
|  | 			score: int64(point.Score) * (framework.MaxNodeScore / config.MaxCustomPriorityScore), | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := validateFunctionShape(shape); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resourceToWeightMap := make(resourceToWeightMap) | ||||||
|  | 	for _, resource := range args.Resources { | ||||||
|  | 		resourceToWeightMap[v1.ResourceName(resource.Name)] = resource.Weight | ||||||
|  | 		if resource.Weight == 0 { | ||||||
|  | 			// Apply the default weight. | ||||||
|  | 			resourceToWeightMap[v1.ResourceName(resource.Name)] = 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if len(args.Resources) == 0 { | ||||||
|  | 		// If no resources specified, used the default set. | ||||||
|  | 		resourceToWeightMap = defaultRequestedRatioResources | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return &RequestedToCapacityRatio{ | 	return &RequestedToCapacityRatio{ | ||||||
| 		handle: handle, | 		handle: handle, | ||||||
| 		resourceAllocationScorer: resourceAllocationScorer{ | 		resourceAllocationScorer: resourceAllocationScorer{ | ||||||
| 			RequestedToCapacityRatioName, | 			RequestedToCapacityRatioName, | ||||||
| 			buildRequestedToCapacityRatioScorerFunction(args.FunctionShape, args.ResourceToWeightMap), | 			buildRequestedToCapacityRatioScorerFunction(shape, resourceToWeightMap), | ||||||
| 			args.ResourceToWeightMap, | 			resourceToWeightMap, | ||||||
| 		}, | 		}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
| @@ -99,44 +124,36 @@ func (pl *RequestedToCapacityRatio) ScoreExtensions() framework.ScoreExtensions | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewFunctionShape creates instance of FunctionShape in a safe way performing all | func validateFunctionShape(shape functionShape) error { | ||||||
| // necessary sanity checks. | 	if len(shape) == 0 { | ||||||
| func NewFunctionShape(points []FunctionShapePoint) (FunctionShape, error) { | 		return fmt.Errorf("at least one point must be specified") | ||||||
|  |  | ||||||
| 	n := len(points) |  | ||||||
|  |  | ||||||
| 	if n == 0 { |  | ||||||
| 		return nil, fmt.Errorf("at least one point must be specified") |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i := 1; i < n; i++ { | 	for i := 1; i < len(shape); i++ { | ||||||
| 		if points[i-1].Utilization >= points[i].Utilization { | 		if shape[i-1].utilization >= shape[i].utilization { | ||||||
| 			return nil, fmt.Errorf("utilization values must be sorted. Utilization[%d]==%d >= Utilization[%d]==%d", i-1, points[i-1].Utilization, i, points[i].Utilization) | 			return fmt.Errorf("utilization values must be sorted. Utilization[%d]==%d >= Utilization[%d]==%d", i-1, shape[i-1].utilization, i, shape[i].utilization) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i, point := range points { | 	for i, point := range shape { | ||||||
| 		if point.Utilization < minUtilization { | 		if point.utilization < minUtilization { | ||||||
| 			return nil, fmt.Errorf("utilization values must not be less than %d. Utilization[%d]==%d", minUtilization, i, point.Utilization) | 			return fmt.Errorf("utilization values must not be less than %d. Utilization[%d]==%d", minUtilization, i, point.utilization) | ||||||
| 		} | 		} | ||||||
| 		if point.Utilization > maxUtilization { | 		if point.utilization > maxUtilization { | ||||||
| 			return nil, fmt.Errorf("utilization values must not be greater than %d. Utilization[%d]==%d", maxUtilization, i, point.Utilization) | 			return fmt.Errorf("utilization values must not be greater than %d. Utilization[%d]==%d", maxUtilization, i, point.utilization) | ||||||
| 		} | 		} | ||||||
| 		if point.Score < minScore { | 		if point.score < minScore { | ||||||
| 			return nil, fmt.Errorf("score values must not be less than %d. Score[%d]==%d", minScore, i, point.Score) | 			return fmt.Errorf("score values must not be less than %d. Score[%d]==%d", minScore, i, point.score) | ||||||
| 		} | 		} | ||||||
| 		if point.Score > maxScore { | 		if int64(point.score) > maxScore { | ||||||
| 			return nil, fmt.Errorf("score valuses not be greater than %d. Score[%d]==%d", maxScore, i, point.Score) | 			return fmt.Errorf("score values not be greater than %d. Score[%d]==%d", maxScore, i, point.score) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// We make defensive copy so we make no assumption if array passed as argument is not changed afterwards | 	return nil | ||||||
| 	pointsCopy := make(FunctionShape, n) |  | ||||||
| 	copy(pointsCopy, points) |  | ||||||
| 	return pointsCopy, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func validateResourceWeightMap(resourceToWeightMap ResourceToWeightMap) error { | func validateResourceWeightMap(resourceToWeightMap resourceToWeightMap) error { | ||||||
| 	if len(resourceToWeightMap) == 0 { | 	if len(resourceToWeightMap) == 0 { | ||||||
| 		return fmt.Errorf("resourceToWeightMap cannot be nil") | 		return fmt.Errorf("resourceToWeightMap cannot be nil") | ||||||
| 	} | 	} | ||||||
| @@ -149,7 +166,7 @@ func validateResourceWeightMap(resourceToWeightMap ResourceToWeightMap) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func buildRequestedToCapacityRatioScorerFunction(scoringFunctionShape FunctionShape, resourceToWeightMap ResourceToWeightMap) func(resourceToValueMap, resourceToValueMap, bool, int, int) int64 { | func buildRequestedToCapacityRatioScorerFunction(scoringFunctionShape functionShape, resourceToWeightMap resourceToWeightMap) func(resourceToValueMap, resourceToValueMap, bool, int, int) int64 { | ||||||
| 	rawScoringFunction := buildBrokenLinearFunction(scoringFunctionShape) | 	rawScoringFunction := buildBrokenLinearFunction(scoringFunctionShape) | ||||||
| 	err := validateResourceWeightMap(resourceToWeightMap) | 	err := validateResourceWeightMap(resourceToWeightMap) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -179,25 +196,24 @@ func buildRequestedToCapacityRatioScorerFunction(scoringFunctionShape FunctionSh | |||||||
| } | } | ||||||
|  |  | ||||||
| // Creates a function which is built using linear segments. Segments are defined via shape array. | // Creates a function which is built using linear segments. Segments are defined via shape array. | ||||||
| // Shape[i].Utilization slice represents points on "utilization" axis where different segments meet. | // Shape[i].utilization slice represents points on "utilization" axis where different segments meet. | ||||||
| // Shape[i].Score represents function values at meeting points. | // Shape[i].score represents function values at meeting points. | ||||||
| // | // | ||||||
| // function f(p) is defined as: | // function f(p) is defined as: | ||||||
| //   shape[0].Score for p < f[0].Utilization | //   shape[0].score for p < f[0].utilization | ||||||
| //   shape[i].Score for p == shape[i].Utilization | //   shape[i].score for p == shape[i].utilization | ||||||
| //   shape[n-1].Score for p > shape[n-1].Utilization | //   shape[n-1].score for p > shape[n-1].utilization | ||||||
| // and linear between points (p < shape[i].Utilization) | // and linear between points (p < shape[i].utilization) | ||||||
| func buildBrokenLinearFunction(shape FunctionShape) func(int64) int64 { | func buildBrokenLinearFunction(shape functionShape) func(int64) int64 { | ||||||
| 	n := len(shape) |  | ||||||
| 	return func(p int64) int64 { | 	return func(p int64) int64 { | ||||||
| 		for i := 0; i < n; i++ { | 		for i := 0; i < len(shape); i++ { | ||||||
| 			if p <= shape[i].Utilization { | 			if p <= int64(shape[i].utilization) { | ||||||
| 				if i == 0 { | 				if i == 0 { | ||||||
| 					return shape[0].Score | 					return shape[0].score | ||||||
| 				} | 				} | ||||||
| 				return shape[i-1].Score + (shape[i].Score-shape[i-1].Score)*(p-shape[i-1].Utilization)/(shape[i].Utilization-shape[i-1].Utilization) | 				return shape[i-1].score + (shape[i].score-shape[i-1].score)*(p-shape[i-1].utilization)/(shape[i].utilization-shape[i-1].utilization) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		return shape[n-1].Score | 		return shape[len(shape)-1].score | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -67,8 +67,11 @@ func TestRequestedToCapacityRatio(t *testing.T) { | |||||||
| 			state := framework.NewCycleState() | 			state := framework.NewCycleState() | ||||||
| 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.scheduledPods, test.nodes)) | 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.scheduledPods, test.nodes)) | ||||||
| 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | ||||||
| 			args := &runtime.Unknown{Raw: []byte(`{"FunctionShape" : [{"Utilization" : 0, "Score" : 100}, {"Utilization" : 100, "Score" : 0}], "ResourceToWeightMap" : {"memory" : 1, "cpu" : 1}}`)} | 			args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 10}, {"utilization" : 100, "score" : 0}], "resources" : [{"name" : "memory", "weight" : 1}, {"name" : "cpu", "weight" : 1}]}`)} | ||||||
| 			p, _ := NewRequestedToCapacityRatio(args, fh) | 			p, err := NewRequestedToCapacityRatio(args, fh) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("unexpected error: %v", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			var gotPriorities framework.NodeScoreList | 			var gotPriorities framework.NodeScoreList | ||||||
| 			for _, n := range test.nodes { | 			for _, n := range test.nodes { | ||||||
| @@ -106,43 +109,43 @@ func makePod(node string, milliCPU, memory int64) *v1.Pod { | |||||||
|  |  | ||||||
| func TestCreatingFunctionShapeErrorsIfEmptyPoints(t *testing.T) { | func TestCreatingFunctionShapeErrorsIfEmptyPoints(t *testing.T) { | ||||||
| 	var err error | 	var err error | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{}) | 	err = validateFunctionShape([]functionShapePoint{}) | ||||||
| 	assert.Equal(t, "at least one point must be specified", err.Error()) | 	assert.Equal(t, "at least one point must be specified", err.Error()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestCreatingResourceNegativeWeight(t *testing.T) { | func TestCreatingResourceNegativeWeight(t *testing.T) { | ||||||
| 	err := validateResourceWeightMap(ResourceToWeightMap{v1.ResourceCPU: -1}) | 	err := validateResourceWeightMap(resourceToWeightMap{v1.ResourceCPU: -1}) | ||||||
| 	assert.Equal(t, "resource cpu weight -1 must not be less than 1", err.Error()) | 	assert.Equal(t, "resource cpu weight -1 must not be less than 1", err.Error()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestCreatingResourceDefaultWeight(t *testing.T) { | func TestCreatingResourceDefaultWeight(t *testing.T) { | ||||||
| 	err := validateResourceWeightMap(ResourceToWeightMap{}) | 	err := validateResourceWeightMap(resourceToWeightMap{}) | ||||||
| 	assert.Equal(t, "resourceToWeightMap cannot be nil", err.Error()) | 	assert.Equal(t, "resourceToWeightMap cannot be nil", err.Error()) | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestCreatingFunctionShapeErrorsIfXIsNotSorted(t *testing.T) { | func TestCreatingFunctionShapeErrorsIfXIsNotSorted(t *testing.T) { | ||||||
| 	var err error | 	var err error | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{10, 1}, {15, 2}, {20, 3}, {19, 4}, {25, 5}}) | 	err = validateFunctionShape([]functionShapePoint{{10, 1}, {15, 2}, {20, 3}, {19, 4}, {25, 5}}) | ||||||
| 	assert.Equal(t, "utilization values must be sorted. Utilization[2]==20 >= Utilization[3]==19", err.Error()) | 	assert.Equal(t, "utilization values must be sorted. Utilization[2]==20 >= Utilization[3]==19", err.Error()) | ||||||
|  |  | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{10, 1}, {20, 2}, {20, 3}, {22, 4}, {25, 5}}) | 	err = validateFunctionShape([]functionShapePoint{{10, 1}, {20, 2}, {20, 3}, {22, 4}, {25, 5}}) | ||||||
| 	assert.Equal(t, "utilization values must be sorted. Utilization[1]==20 >= Utilization[2]==20", err.Error()) | 	assert.Equal(t, "utilization values must be sorted. Utilization[1]==20 >= Utilization[2]==20", err.Error()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestCreatingFunctionPointNotInAllowedRange(t *testing.T) { | func TestCreatingFunctionPointNotInAllowedRange(t *testing.T) { | ||||||
| 	var err error | 	var err error | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{-1, 0}, {100, 100}}) | 	err = validateFunctionShape([]functionShapePoint{{-1, 0}, {100, 100}}) | ||||||
| 	assert.Equal(t, "utilization values must not be less than 0. Utilization[0]==-1", err.Error()) | 	assert.Equal(t, "utilization values must not be less than 0. Utilization[0]==-1", err.Error()) | ||||||
|  |  | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{0, 0}, {101, 100}}) | 	err = validateFunctionShape([]functionShapePoint{{0, 0}, {101, 100}}) | ||||||
| 	assert.Equal(t, "utilization values must not be greater than 100. Utilization[1]==101", err.Error()) | 	assert.Equal(t, "utilization values must not be greater than 100. Utilization[1]==101", err.Error()) | ||||||
|  |  | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{0, -1}, {100, 100}}) | 	err = validateFunctionShape([]functionShapePoint{{0, -1}, {100, 100}}) | ||||||
| 	assert.Equal(t, "score values must not be less than 0. Score[0]==-1", err.Error()) | 	assert.Equal(t, "score values must not be less than 0. Score[0]==-1", err.Error()) | ||||||
|  |  | ||||||
| 	_, err = NewFunctionShape([]FunctionShapePoint{{0, 0}, {100, 101}}) | 	err = validateFunctionShape([]functionShapePoint{{0, 0}, {100, 101}}) | ||||||
| 	assert.Equal(t, "score valuses not be greater than 100. Score[1]==101", err.Error()) | 	assert.Equal(t, "score values not be greater than 100. Score[1]==101", err.Error()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBrokenLinearFunction(t *testing.T) { | func TestBrokenLinearFunction(t *testing.T) { | ||||||
| @@ -151,13 +154,13 @@ func TestBrokenLinearFunction(t *testing.T) { | |||||||
| 		expected int64 | 		expected int64 | ||||||
| 	} | 	} | ||||||
| 	type Test struct { | 	type Test struct { | ||||||
| 		points     []FunctionShapePoint | 		points     []functionShapePoint | ||||||
| 		assertions []Assertion | 		assertions []Assertion | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	tests := []Test{ | 	tests := []Test{ | ||||||
| 		{ | 		{ | ||||||
| 			points: []FunctionShapePoint{{10, 1}, {90, 9}}, | 			points: []functionShapePoint{{10, 1}, {90, 9}}, | ||||||
| 			assertions: []Assertion{ | 			assertions: []Assertion{ | ||||||
| 				{p: -10, expected: 1}, | 				{p: -10, expected: 1}, | ||||||
| 				{p: 0, expected: 1}, | 				{p: 0, expected: 1}, | ||||||
| @@ -174,7 +177,7 @@ func TestBrokenLinearFunction(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			points: []FunctionShapePoint{{0, 2}, {40, 10}, {100, 0}}, | 			points: []functionShapePoint{{0, 2}, {40, 10}, {100, 0}}, | ||||||
| 			assertions: []Assertion{ | 			assertions: []Assertion{ | ||||||
| 				{p: -10, expected: 2}, | 				{p: -10, expected: 2}, | ||||||
| 				{p: 0, expected: 2}, | 				{p: 0, expected: 2}, | ||||||
| @@ -187,7 +190,7 @@ func TestBrokenLinearFunction(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			points: []FunctionShapePoint{{0, 2}, {40, 2}, {100, 2}}, | 			points: []functionShapePoint{{0, 2}, {40, 2}, {100, 2}}, | ||||||
| 			assertions: []Assertion{ | 			assertions: []Assertion{ | ||||||
| 				{p: -10, expected: 2}, | 				{p: -10, expected: 2}, | ||||||
| 				{p: 0, expected: 2}, | 				{p: 0, expected: 2}, | ||||||
| @@ -202,9 +205,7 @@ func TestBrokenLinearFunction(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, test := range tests { | 	for _, test := range tests { | ||||||
| 		functionShape, err := NewFunctionShape(test.points) | 		function := buildBrokenLinearFunction(test.points) | ||||||
| 		assert.Nil(t, err) |  | ||||||
| 		function := buildBrokenLinearFunction(functionShape) |  | ||||||
| 		for _, assertion := range test.assertions { | 		for _, assertion := range test.assertions { | ||||||
| 			assert.InDelta(t, assertion.expected, function(assertion.p), 0.1, "points=%v, p=%f", test.points, assertion.p) | 			assert.InDelta(t, assertion.expected, function(assertion.p), 0.1, "points=%v, p=%f", test.points, assertion.p) | ||||||
| 		} | 		} | ||||||
| @@ -350,8 +351,11 @@ func TestResourceBinPackingSingleExtended(t *testing.T) { | |||||||
| 			state := framework.NewCycleState() | 			state := framework.NewCycleState() | ||||||
| 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.pods, test.nodes)) | 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.pods, test.nodes)) | ||||||
| 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | ||||||
| 			args := &runtime.Unknown{Raw: []byte(`{"FunctionShape" : [{"Utilization" : 0, "Score" : 0}, {"Utilization" : 100, "Score" : 10}], "ResourceToWeightMap" : {"intel.com/foo" : 1}}`)} | 			args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 0}, {"utilization" : 100, "score" : 1}], "resources" : [{"name" : "intel.com/foo", "weight" : 1}]}`)} | ||||||
| 			p, _ := NewRequestedToCapacityRatio(args, fh) | 			p, err := NewRequestedToCapacityRatio(args, fh) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("unexpected error: %v", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			var gotList framework.NodeScoreList | 			var gotList framework.NodeScoreList | ||||||
| 			for _, n := range test.nodes { | 			for _, n := range test.nodes { | ||||||
| @@ -582,8 +586,11 @@ func TestResourceBinPackingMultipleExtended(t *testing.T) { | |||||||
| 			state := framework.NewCycleState() | 			state := framework.NewCycleState() | ||||||
| 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.pods, test.nodes)) | 			snapshot := nodeinfosnapshot.NewSnapshot(nodeinfosnapshot.CreateNodeInfoMap(test.pods, test.nodes)) | ||||||
| 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | 			fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot)) | ||||||
| 			args := &runtime.Unknown{Raw: []byte(`{"FunctionShape" : [{"Utilization" : 0, "Score" : 0}, {"Utilization" : 100, "Score" : 10}], "ResourceToWeightMap" : {"intel.com/foo" : 3, "intel.com/bar" : 5}}`)} | 			args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 0}, {"utilization" : 100, "score" : 1}], "resources" : [{"name" : "intel.com/foo", "weight" : 3}, {"name" : "intel.com/bar", "weight": 5}]}`)} | ||||||
| 			p, _ := NewRequestedToCapacityRatio(args, fh) | 			p, err := NewRequestedToCapacityRatio(args, fh) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("unexpected error: %v", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			var gotList framework.NodeScoreList | 			var gotList framework.NodeScoreList | ||||||
| 			for _, n := range test.nodes { | 			for _, n := range test.nodes { | ||||||
|   | |||||||
| @@ -27,17 +27,17 @@ import ( | |||||||
| 	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" | 	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ResourceToWeightMap contains resource name and weight. | // resourceToWeightMap contains resource name and weight. | ||||||
| type ResourceToWeightMap map[v1.ResourceName]int64 | type resourceToWeightMap map[v1.ResourceName]int64 | ||||||
|  |  | ||||||
| // DefaultRequestedRatioResources is used to set default requestToWeight map for CPU and memory | // defaultRequestedRatioResources is used to set default requestToWeight map for CPU and memory | ||||||
| var DefaultRequestedRatioResources = ResourceToWeightMap{v1.ResourceMemory: 1, v1.ResourceCPU: 1} | var defaultRequestedRatioResources = resourceToWeightMap{v1.ResourceMemory: 1, v1.ResourceCPU: 1} | ||||||
|  |  | ||||||
| // resourceAllocationScorer contains information to calculate resource allocation score. | // resourceAllocationScorer contains information to calculate resource allocation score. | ||||||
| type resourceAllocationScorer struct { | type resourceAllocationScorer struct { | ||||||
| 	Name                string | 	Name                string | ||||||
| 	scorer              func(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 | 	scorer              func(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 | ||||||
| 	resourceToWeightMap ResourceToWeightMap | 	resourceToWeightMap resourceToWeightMap | ||||||
| } | } | ||||||
|  |  | ||||||
| // resourceToValueMap contains resource name and score. | // resourceToValueMap contains resource name and score. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Abdullah Gharaibeh
					Abdullah Gharaibeh