mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
Move RequestedToCapacityRatio argument processing to its plugin
This commit is contained in:
parent
405714b262
commit
b9e2389e0d
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user