mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 05:14:13 +01:00
Check existing constraints before assigning
This commit is contained in:
@@ -19,6 +19,7 @@ package strategies
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math"
|
"math"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@@ -104,14 +105,20 @@ func RemovePodsViolatingTopologySpreadConstraint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ...where there is a topology constraint
|
// ...where there is a topology constraint
|
||||||
namespaceTopologySpreadConstraints := make(map[v1.TopologySpreadConstraint]struct{})
|
namespaceTopologySpreadConstraints := []v1.TopologySpreadConstraint{}
|
||||||
for _, pod := range namespacePods.Items {
|
for _, pod := range namespacePods.Items {
|
||||||
for _, constraint := range pod.Spec.TopologySpreadConstraints {
|
for _, constraint := range pod.Spec.TopologySpreadConstraints {
|
||||||
// Ignore soft topology constraints if they are not included
|
// Ignore soft topology constraints if they are not included
|
||||||
if constraint.WhenUnsatisfiable == v1.ScheduleAnyway && (strategy.Params == nil || !strategy.Params.IncludeSoftConstraints) {
|
if constraint.WhenUnsatisfiable == v1.ScheduleAnyway && (strategy.Params == nil || !strategy.Params.IncludeSoftConstraints) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
namespaceTopologySpreadConstraints[constraint] = struct{}{}
|
// Need to check v1.TopologySpreadConstraint deepEquality because
|
||||||
|
// v1.TopologySpreadConstraint has pointer fields
|
||||||
|
// and we don't need to go over duplicated constraints later on
|
||||||
|
if hasIdenticalConstraints(constraint, namespaceTopologySpreadConstraints) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
namespaceTopologySpreadConstraints = append(namespaceTopologySpreadConstraints, constraint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(namespaceTopologySpreadConstraints) == 0 {
|
if len(namespaceTopologySpreadConstraints) == 0 {
|
||||||
@@ -119,7 +126,7 @@ func RemovePodsViolatingTopologySpreadConstraint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. for each topologySpreadConstraint in that namespace
|
// 2. for each topologySpreadConstraint in that namespace
|
||||||
for constraint := range namespaceTopologySpreadConstraints {
|
for _, constraint := range namespaceTopologySpreadConstraints {
|
||||||
constraintTopologies := make(map[topologyPair][]*v1.Pod)
|
constraintTopologies := make(map[topologyPair][]*v1.Pod)
|
||||||
// pre-populate the topologyPair map with all the topologies available from the nodeMap
|
// pre-populate the topologyPair map with all the topologies available from the nodeMap
|
||||||
// (we can't just build it from existing pods' nodes because a topology may have 0 pods)
|
// (we can't just build it from existing pods' nodes because a topology may have 0 pods)
|
||||||
@@ -183,6 +190,16 @@ func RemovePodsViolatingTopologySpreadConstraint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasIdenticalConstraints checks if we already had an identical TopologySpreadConstraint in namespaceTopologySpreadConstraints slice
|
||||||
|
func hasIdenticalConstraints(newConstraint v1.TopologySpreadConstraint, namespaceTopologySpreadConstraints []v1.TopologySpreadConstraint) bool {
|
||||||
|
for _, constraint := range namespaceTopologySpreadConstraints {
|
||||||
|
if reflect.DeepEqual(newConstraint, constraint) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// topologyIsBalanced checks if any domains in the topology differ by more than the MaxSkew
|
// topologyIsBalanced checks if any domains in the topology differ by more than the MaxSkew
|
||||||
// this is called before any sorting or other calculations and is used to skip topologies that don't need to be balanced
|
// this is called before any sorting or other calculations and is used to skip topologies that don't need to be balanced
|
||||||
func topologyIsBalanced(topology map[topologyPair][]*v1.Pod, constraint v1.TopologySpreadConstraint) bool {
|
func topologyIsBalanced(topology map[topologyPair][]*v1.Pod, constraint v1.TopologySpreadConstraint) bool {
|
||||||
|
|||||||
@@ -1041,3 +1041,54 @@ func getDefaultTopologyConstraints(maxSkew int32) []v1.TopologySpreadConstraint
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckIdenticalConstraints(t *testing.T) {
|
||||||
|
newConstraintSame := v1.TopologySpreadConstraint{
|
||||||
|
MaxSkew: 2,
|
||||||
|
TopologyKey: "zone",
|
||||||
|
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||||
|
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||||
|
}
|
||||||
|
newConstraintDifferent := v1.TopologySpreadConstraint{
|
||||||
|
MaxSkew: 3,
|
||||||
|
TopologyKey: "node",
|
||||||
|
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||||
|
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||||
|
}
|
||||||
|
namespaceTopologySpreadConstraint := []v1.TopologySpreadConstraint{}
|
||||||
|
namespaceTopologySpreadConstraint = []v1.TopologySpreadConstraint{
|
||||||
|
{
|
||||||
|
MaxSkew: 2,
|
||||||
|
TopologyKey: "zone",
|
||||||
|
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||||
|
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
namespaceTopologySpreadConstraints []v1.TopologySpreadConstraint
|
||||||
|
newConstraint v1.TopologySpreadConstraint
|
||||||
|
expectedResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "new constraint is identical",
|
||||||
|
namespaceTopologySpreadConstraints: namespaceTopologySpreadConstraint,
|
||||||
|
newConstraint: newConstraintSame,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new constraint is different",
|
||||||
|
namespaceTopologySpreadConstraints: namespaceTopologySpreadConstraint,
|
||||||
|
newConstraint: newConstraintDifferent,
|
||||||
|
expectedResult: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
isIdentical := hasIdenticalConstraints(tc.newConstraint, tc.namespaceTopologySpreadConstraints)
|
||||||
|
if isIdentical != tc.expectedResult {
|
||||||
|
t.Errorf("Test error for description: %s. Expected result %v, got %v", tc.name, tc.expectedResult, isIdentical)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user