Skip to content

Commit a5b2f54

Browse files
Add unit tests for inter-pod anti-affinity check
* Test logic in GroupByNodeName * Test NodeFit() case where pods matches inter-pod anti-affinity * Test for inter-pod anti-affinity pods match terms, have label selector
1 parent d8cea2c commit a5b2f54

File tree

6 files changed

+152
-20
lines changed

6 files changed

+152
-20
lines changed

pkg/descheduler/node/node_test.go

+11-17
Original file line numberDiff line numberDiff line change
@@ -754,15 +754,11 @@ func TestPodFitsAnyOtherNode(t *testing.T) {
754754
}
755755

756756
func TestNodeFit(t *testing.T) {
757-
node := test.BuildTestNode("node", 64000, 128*1000*1000*1000, 2, nil)
758-
759-
// pod anti-affinity pods
760-
podAntiAffinityPod1 := test.BuildTestPod("p1", 1000, 1000, "node", nil)
761-
test.SetPodAntiAffinity(podAntiAffinityPod1, "foo", "bar")
762-
763-
podAntiAffinityPod2 := test.BuildTestPod("p2", 1000, 1000, "node2", nil)
764-
test.SetPodAntiAffinity(podAntiAffinityPod2, "foo", "bar")
765-
757+
node := test.BuildTestNode("node", 64000, 128*1000*1000*1000, 2, func(node *v1.Node) {
758+
node.ObjectMeta.Labels = map[string]string{
759+
"region": "main-region",
760+
}
761+
})
766762
tests := []struct {
767763
description string
768764
pod *v1.Pod
@@ -791,14 +787,12 @@ func TestNodeFit(t *testing.T) {
791787
},
792788
{
793789
description: "matches inter-pod anti-affinity rule of pod on node",
794-
pod: podAntiAffinityPod1,
795-
node: test.BuildTestNode("node2", 2000, 3000, 10, func(node *v1.Node) {
796-
node.ObjectMeta.Labels = map[string]string{
797-
"region": "main-region",
798-
}
799-
}),
800-
podsOnNode: []*v1.Pod{podAntiAffinityPod2},
801-
err: errors.New("pod matches inter-pod anti-affinity rule of other pod on node"),
790+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, node.Name, nil), "foo", "bar"),
791+
node: node,
792+
podsOnNode: []*v1.Pod{
793+
test.PodWithPodAntiAffinity(test.BuildTestPod("p2", 1000, 1000, node.Name, nil), "foo", "bar"),
794+
},
795+
err: errors.New("pod matches inter-pod anti-affinity rule of other pod on node"),
802796
},
803797
{
804798
description: "pod fits on node",

pkg/descheduler/pod/pods_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,67 @@ func TestSortPodsBasedOnAge(t *testing.T) {
176176
}
177177
}
178178
}
179+
180+
func TestGroupByNodeName(t *testing.T) {
181+
tests := []struct {
182+
name string
183+
pods []*v1.Pod
184+
expMap map[string][]*v1.Pod
185+
}{
186+
{
187+
name: "list of pods is empty",
188+
pods: []*v1.Pod{},
189+
expMap: map[string][]*v1.Pod{},
190+
},
191+
{
192+
name: "pods are on same node",
193+
pods: []*v1.Pod{
194+
{Spec: v1.PodSpec{
195+
NodeName: "node1",
196+
}},
197+
{Spec: v1.PodSpec{
198+
NodeName: "node1",
199+
}},
200+
},
201+
expMap: map[string][]*v1.Pod{"node1": {
202+
{Spec: v1.PodSpec{
203+
NodeName: "node1",
204+
}},
205+
{Spec: v1.PodSpec{
206+
NodeName: "node1",
207+
}},
208+
}},
209+
},
210+
{
211+
name: "pods are on different nodes",
212+
pods: []*v1.Pod{
213+
{Spec: v1.PodSpec{
214+
NodeName: "node1",
215+
}},
216+
{Spec: v1.PodSpec{
217+
NodeName: "node2",
218+
}},
219+
},
220+
expMap: map[string][]*v1.Pod{
221+
"node1": {
222+
{Spec: v1.PodSpec{
223+
NodeName: "node1",
224+
}},
225+
},
226+
"node2": {
227+
{Spec: v1.PodSpec{
228+
NodeName: "node2",
229+
}},
230+
},
231+
},
232+
},
233+
}
234+
for _, test := range tests {
235+
t.Run(test.name, func(t *testing.T) {
236+
resultMap := GroupByNodeName(test.pods)
237+
if !reflect.DeepEqual(resultMap, test.expMap) {
238+
t.Errorf("Expected %v node map, got %v", test.expMap, resultMap)
239+
}
240+
})
241+
}
242+
}

pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ import (
2424
"k8s.io/apimachinery/pkg/util/sets"
2525
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
2626
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
27-
"sigs.k8s.io/descheduler/pkg/utils"
28-
2927
frameworktypes "sigs.k8s.io/descheduler/pkg/framework/types"
28+
"sigs.k8s.io/descheduler/pkg/utils"
3029

3130
v1 "k8s.io/api/core/v1"
3231
"k8s.io/klog/v2"

pkg/utils/predicates.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ func CheckPodsWithAntiAffinityExist(pod *v1.Pod, pods map[string][]*v1.Pod, node
332332
continue
333333
}
334334
if hasSameLabelValue(node, nodeHavingExistingPod, term.TopologyKey) {
335-
klog.V(1).InfoS("Found Pods violating PodAntiAffinity", "pod to evicted", klog.KObj(pod))
335+
klog.V(1).InfoS("Found Pods matching PodAntiAffinity", "pod with anti-affinity", klog.KObj(pod))
336336
return true
337337
}
338338
}

pkg/utils/predicates_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
v1 "k8s.io/api/core/v1"
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"sigs.k8s.io/descheduler/test"
910
)
1011

1112
func TestUniqueSortTolerations(t *testing.T) {
@@ -1041,3 +1042,71 @@ func TestPodNodeAffinityWeight(t *testing.T) {
10411042
})
10421043
}
10431044
}
1045+
1046+
func TestCheckPodsWithAntiAffinityExist(t *testing.T) {
1047+
tests := []struct {
1048+
name string
1049+
pod *v1.Pod
1050+
podsInNamespace map[string][]*v1.Pod
1051+
nodeMap map[string]*v1.Node
1052+
affinity *v1.PodAntiAffinity
1053+
expMatch bool
1054+
}{
1055+
{
1056+
name: "found pod matching pod anti-affinity",
1057+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, "node", nil), "foo", "bar"),
1058+
podsInNamespace: map[string][]*v1.Pod{
1059+
"default": {
1060+
test.PodWithPodAntiAffinity(test.BuildTestPod("p2", 1000, 1000, "node", nil), "foo", "bar"),
1061+
},
1062+
},
1063+
nodeMap: map[string]*v1.Node{
1064+
"node": test.BuildTestNode("node", 64000, 128*1000*1000*1000, 2, func(node *v1.Node) {
1065+
node.ObjectMeta.Labels = map[string]string{
1066+
"region": "main-region",
1067+
}
1068+
}),
1069+
},
1070+
expMatch: true,
1071+
},
1072+
{
1073+
name: "no match with invalid label selector",
1074+
pod: &v1.Pod{
1075+
Spec: v1.PodSpec{
1076+
Affinity: &v1.Affinity{
1077+
PodAntiAffinity: &v1.PodAntiAffinity{
1078+
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
1079+
{
1080+
LabelSelector: &metav1.LabelSelector{
1081+
MatchLabels: map[string]string{
1082+
"wrong": "selector",
1083+
},
1084+
},
1085+
},
1086+
},
1087+
},
1088+
},
1089+
},
1090+
},
1091+
podsInNamespace: map[string][]*v1.Pod{},
1092+
nodeMap: map[string]*v1.Node{},
1093+
expMatch: false,
1094+
},
1095+
{
1096+
name: "no match if pod does not match terms namespace",
1097+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, "node", func(pod *v1.Pod) {
1098+
pod.Namespace = "other"
1099+
}), "foo", "bar"),
1100+
podsInNamespace: map[string][]*v1.Pod{},
1101+
nodeMap: map[string]*v1.Node{},
1102+
expMatch: false,
1103+
},
1104+
}
1105+
for _, test := range tests {
1106+
t.Run(test.name, func(t *testing.T) {
1107+
if match := CheckPodsWithAntiAffinityExist(test.pod, test.podsInNamespace, test.nodeMap); match != test.expMatch {
1108+
t.Errorf("exp %v got %v", test.expMatch, match)
1109+
}
1110+
})
1111+
}
1112+
}

test/test_utils.go

+6
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,9 @@ func SetPodAntiAffinity(inputPod *v1.Pod, labelKey, labelValue string) {
358358
},
359359
}
360360
}
361+
362+
func PodWithPodAntiAffinity(inputPod *v1.Pod, labelKey, labelValue string) *v1.Pod {
363+
SetPodAntiAffinity(inputPod, labelKey, labelValue)
364+
inputPod.Labels = map[string]string{labelKey: labelValue}
365+
return inputPod
366+
}

0 commit comments

Comments
 (0)