Skip to content

Commit 9f7e7fd

Browse files
authored
Merge pull request #1425 from a7i/automated-cherry-pick-of-#1378-#1390-#1412-#1413-#1416-#1395-upstream-release-1.30
Automated cherry pick of #1378: Fix the replicas type for the helm-chart #1390: allow 'falsey' value in cmdOption #1412: fix helm's default deschedulerPolicy #1413: fix TOC location in Readme #1416: use cmd context instead of using context.Background() #1395: fix the issue that the pod anti-filtering rules are not
2 parents 7999094 + 7e85b79 commit 9f7e7fd

File tree

10 files changed

+140
-77
lines changed

10 files changed

+140
-77
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
![Release Charts](https://github.com/kubernetes-sigs/descheduler/workflows/Release%20Charts/badge.svg)
33

44
<p align="left">
5-
↖️ Click at the [bullet list icon] at the top left corner of the Readme visualization for the github generated table of contents.
5+
↗️️ Click at the [bullet list icon] at the top right corner of the Readme visualization for the github generated table of contents.
66
</p>
77

88
<p align="center">

charts/descheduler/templates/NOTES.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Descheduler installed as a {{ .Values.kind }}.
22

33
{{- if eq .Values.kind "Deployment" }}
4-
{{- if eq .Values.replicas 1.0}}
4+
{{- if eq (.Values.replicas | int) 1 }}
55
WARNING: You set replica count as 1 and workload kind as Deployment however leaderElection is not enabled. Consider enabling Leader Election for HA mode.
66
{{- end}}
77
{{- if .Values.leaderElection }}

charts/descheduler/templates/cronjob.yaml

+5-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ spec:
8181
args:
8282
- --policy-config-file=/policy-dir/policy.yaml
8383
{{- range $key, $value := .Values.cmdOptions }}
84-
- {{ printf "--%s" $key }}{{ if $value }}={{ $value }}{{ end }}
84+
{{- if ne $value nil }}
85+
- {{ printf "--%s=%s" $key (toString $value) }}
86+
{{- else }}
87+
- {{ printf "--%s" $key }}
88+
{{- end }}
8589
{{- end }}
8690
livenessProbe:
8791
{{- toYaml .Values.livenessProbe | nindent 16 }}

charts/descheduler/templates/deployment.yaml

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ metadata:
77
labels:
88
{{- include "descheduler.labels" . | nindent 4 }}
99
spec:
10-
{{- if gt .Values.replicas 1.0}}
10+
{{- if gt (.Values.replicas | int) 1 }}
1111
{{- if not .Values.leaderElection.enabled }}
1212
{{- fail "You must set leaderElection to use more than 1 replica"}}
1313
{{- end}}
@@ -53,7 +53,11 @@ spec:
5353
- --policy-config-file=/policy-dir/policy.yaml
5454
- --descheduling-interval={{ required "deschedulingInterval required for running as Deployment" .Values.deschedulingInterval }}
5555
{{- range $key, $value := .Values.cmdOptions }}
56-
- {{ printf "--%s" $key }}{{ if $value }}={{ $value }}{{ end }}
56+
{{- if ne $value nil }}
57+
- {{ printf "--%s=%s" $key (toString $value) }}
58+
{{- else }}
59+
- {{ printf "--%s" $key }}
60+
{{- end }}
5761
{{- end }}
5862
{{- include "descheduler.leaderElection" . | nindent 12 }}
5963
ports:

charts/descheduler/values.yaml

+3-5
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,13 @@ deschedulerPolicy:
111111
args:
112112
podRestartThreshold: 100
113113
includingInitContainers: true
114-
- name: RemovePodsViolatingNodeTaints
114+
- name: RemovePodsViolatingNodeAffinity
115115
args:
116116
nodeAffinityType:
117-
- requiredDuringSchedulingIgnoredDuringExecution
117+
- requiredDuringSchedulingIgnoredDuringExecution
118+
- name: RemovePodsViolatingNodeTaints
118119
- name: RemovePodsViolatingInterPodAntiAffinity
119120
- name: RemovePodsViolatingTopologySpreadConstraint
120-
args:
121-
includeSoftConstraints: false
122121
- name: LowNodeUtilization
123122
args:
124123
thresholds:
@@ -133,7 +132,6 @@ deschedulerPolicy:
133132
balance:
134133
enabled:
135134
- RemoveDuplicates
136-
- RemovePodsViolatingNodeAffinity
137135
- RemovePodsViolatingTopologySpreadConstraint
138136
- LowNodeUtilization
139137
deschedule:

cmd/descheduler/app/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func NewDeschedulerCommand(out io.Writer) *cobra.Command {
7777

7878
secureServing.DisableHTTP2 = !s.EnableHTTP2
7979

80-
ctx, done := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
80+
ctx, done := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
8181

8282
pathRecorderMux := mux.NewPathRecorderMux("descheduler")
8383
if !s.DisableMetrics {

pkg/descheduler/node/node.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,30 @@ func podMatchesInterPodAntiAffinity(nodeIndexer podutil.GetPodsAssignedToNodeFun
343343
if err != nil {
344344
return false, fmt.Errorf("error listing all pods: %v", err)
345345
}
346+
assignedPodsInNamespace := podutil.GroupByNamespace(podsOnNode)
346347

347-
podsInANamespace := podutil.GroupByNamespace(podsOnNode)
348-
nodeMap := utils.CreateNodeMap([]*v1.Node{node})
348+
for _, term := range utils.GetPodAntiAffinityTerms(pod.Spec.Affinity.PodAntiAffinity) {
349+
namespaces := utils.GetNamespacesFromPodAffinityTerm(pod, &term)
350+
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
351+
if err != nil {
352+
klog.ErrorS(err, "Unable to convert LabelSelector into Selector")
353+
return false, err
354+
}
355+
356+
for namespace := range namespaces {
357+
for _, assignedPod := range assignedPodsInNamespace[namespace] {
358+
if assignedPod.Name == pod.Name || !utils.PodMatchesTermsNamespaceAndSelector(assignedPod, namespaces, selector) {
359+
klog.V(4).InfoS("Pod doesn't match inter-pod anti-affinity rule of assigned pod on node", "candidatePod", klog.KObj(pod), "assignedPod", klog.KObj(assignedPod))
360+
continue
361+
}
362+
363+
if _, ok := node.Labels[term.TopologyKey]; ok {
364+
klog.V(1).InfoS("Pod matches inter-pod anti-affinity rule of assigned pod on node", "candidatePod", klog.KObj(pod), "assignedPod", klog.KObj(assignedPod))
365+
return true, nil
366+
}
367+
}
368+
}
369+
}
349370

350-
return utils.CheckPodsWithAntiAffinityExist(pod, podsInANamespace, nodeMap), nil
371+
return false, nil
351372
}

pkg/descheduler/node/node_test.go

+33-5
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,9 @@ func TestNodeFit(t *testing.T) {
759759
"region": "main-region",
760760
}
761761
})
762+
763+
nodeNolabel := test.BuildTestNode("node", 64000, 128*1000*1000*1000, 2, nil)
764+
762765
tests := []struct {
763766
description string
764767
pod *v1.Pod
@@ -767,7 +770,7 @@ func TestNodeFit(t *testing.T) {
767770
err error
768771
}{
769772
{
770-
description: "insufficient cpu",
773+
description: "Insufficient cpu",
771774
pod: test.BuildTestPod("p1", 10000, 2*1000*1000*1000, "", nil),
772775
node: node,
773776
podsOnNode: []*v1.Pod{
@@ -776,7 +779,7 @@ func TestNodeFit(t *testing.T) {
776779
err: errors.New("insufficient cpu"),
777780
},
778781
{
779-
description: "insufficient pod num",
782+
description: "Insufficient pod num",
780783
pod: test.BuildTestPod("p1", 1000, 2*1000*1000*1000, "", nil),
781784
node: node,
782785
podsOnNode: []*v1.Pod{
@@ -786,7 +789,7 @@ func TestNodeFit(t *testing.T) {
786789
err: errors.New("insufficient pods"),
787790
},
788791
{
789-
description: "matches inter-pod anti-affinity rule of pod on node",
792+
description: "Pod matches inter-pod anti-affinity rule of other pod on node",
790793
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, node.Name, nil), "foo", "bar"),
791794
node: node,
792795
podsOnNode: []*v1.Pod{
@@ -795,11 +798,36 @@ func TestNodeFit(t *testing.T) {
795798
err: errors.New("pod matches inter-pod anti-affinity rule of other pod on node"),
796799
},
797800
{
798-
description: "pod fits on node",
801+
description: "Pod doesn't match inter-pod anti-affinity rule of other pod on node, because pod and other pod is not same namespace",
802+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, node.Name, nil), "foo", "bar"),
803+
node: node,
804+
podsOnNode: []*v1.Pod{
805+
test.PodWithPodAntiAffinity(test.BuildTestPod("p2", 1000, 1000, node.Name, func(pod *v1.Pod) {
806+
pod.Namespace = "test"
807+
}), "foo", "bar"),
808+
},
809+
},
810+
{
811+
description: "Pod doesn't match inter-pod anti-affinity rule of other pod on node, because other pod not match labels of pod",
812+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, node.Name, nil), "foo", "bar"),
813+
node: node,
814+
podsOnNode: []*v1.Pod{
815+
test.PodWithPodAntiAffinity(test.BuildTestPod("p2", 1000, 1000, node.Name, nil), "foo1", "bar1"),
816+
},
817+
},
818+
{
819+
description: "Pod doesn't match inter-pod anti-affinity rule of other pod on node, because node have no topologyKey",
820+
pod: test.PodWithPodAntiAffinity(test.BuildTestPod("p1", 1000, 1000, "node1", nil), "foo", "bar"),
821+
node: nodeNolabel,
822+
podsOnNode: []*v1.Pod{
823+
test.PodWithPodAntiAffinity(test.BuildTestPod("p2", 1000, 1000, node.Name, nil), "foo", "bar"),
824+
},
825+
},
826+
{
827+
description: "Pod fits on node",
799828
pod: test.BuildTestPod("p1", 1000, 1000, "", func(pod *v1.Pod) {}),
800829
node: node,
801830
podsOnNode: []*v1.Pod{},
802-
err: nil,
803831
},
804832
}
805833

pkg/utils/predicates.go

+66-29
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,37 @@ import (
2424
v1 "k8s.io/api/core/v1"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/labels"
27+
"k8s.io/apimachinery/pkg/util/sets"
2728
"k8s.io/component-helpers/scheduling/corev1"
2829
"k8s.io/klog/v2"
2930
)
3031

32+
// GetNamespacesFromPodAffinityTerm returns a set of names
33+
// according to the namespaces indicated in podAffinityTerm.
34+
// If namespaces is empty it considers the given pod's namespace.
35+
func GetNamespacesFromPodAffinityTerm(pod *v1.Pod, podAffinityTerm *v1.PodAffinityTerm) sets.Set[string] {
36+
names := sets.New[string]()
37+
if len(podAffinityTerm.Namespaces) == 0 {
38+
names.Insert(pod.Namespace)
39+
} else {
40+
names.Insert(podAffinityTerm.Namespaces...)
41+
}
42+
return names
43+
}
44+
45+
// PodMatchesTermsNamespaceAndSelector returns true if the given <pod>
46+
// matches the namespace and selector defined by <affinityPod>`s <term>.
47+
func PodMatchesTermsNamespaceAndSelector(pod *v1.Pod, namespaces sets.Set[string], selector labels.Selector) bool {
48+
if !namespaces.Has(pod.Namespace) {
49+
return false
50+
}
51+
52+
if !selector.Matches(labels.Set(pod.Labels)) {
53+
return false
54+
}
55+
return true
56+
}
57+
3158
// The following code has been copied from predicates package to avoid the
3259
// huge vendoring issues, mostly copied from
3360
// k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates/
@@ -309,42 +336,52 @@ func CreateNodeMap(nodes []*v1.Node) map[string]*v1.Node {
309336
return m
310337
}
311338

312-
// CheckPodsWithAntiAffinityExist checks if there are other pods on the node that the current pod cannot tolerate.
313-
func CheckPodsWithAntiAffinityExist(pod *v1.Pod, pods map[string][]*v1.Pod, nodeMap map[string]*v1.Node) bool {
314-
affinity := pod.Spec.Affinity
315-
if affinity != nil && affinity.PodAntiAffinity != nil {
316-
for _, term := range getPodAntiAffinityTerms(affinity.PodAntiAffinity) {
317-
namespaces := getNamespacesFromPodAffinityTerm(pod, &term)
318-
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
319-
if err != nil {
320-
klog.ErrorS(err, "Unable to convert LabelSelector into Selector")
321-
return false
322-
}
323-
for namespace := range namespaces {
324-
for _, existingPod := range pods[namespace] {
325-
if existingPod.Name != pod.Name && podMatchesTermsNamespaceAndSelector(existingPod, namespaces, selector) {
326-
node, ok := nodeMap[pod.Spec.NodeName]
327-
if !ok {
328-
continue
329-
}
330-
nodeHavingExistingPod, ok := nodeMap[existingPod.Spec.NodeName]
331-
if !ok {
332-
continue
333-
}
334-
if hasSameLabelValue(node, nodeHavingExistingPod, term.TopologyKey) {
335-
klog.V(1).InfoS("Found Pods matching PodAntiAffinity", "pod with anti-affinity", klog.KObj(pod))
336-
return true
337-
}
338-
}
339+
// CheckPodsWithAntiAffinityExist checks if there are other pods on the node that the current candidate pod cannot tolerate.
340+
func CheckPodsWithAntiAffinityExist(candidatePod *v1.Pod, assignedPods map[string][]*v1.Pod, nodeMap map[string]*v1.Node) bool {
341+
nodeHavingCandidatePod, ok := nodeMap[candidatePod.Spec.NodeName]
342+
if !ok {
343+
klog.Warningf("CandidatePod %s does not exist in nodeMap", klog.KObj(candidatePod))
344+
return false
345+
}
346+
347+
affinity := candidatePod.Spec.Affinity
348+
if affinity == nil || affinity.PodAntiAffinity == nil {
349+
return false
350+
}
351+
352+
for _, term := range GetPodAntiAffinityTerms(affinity.PodAntiAffinity) {
353+
namespaces := GetNamespacesFromPodAffinityTerm(candidatePod, &term)
354+
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
355+
if err != nil {
356+
klog.ErrorS(err, "Unable to convert LabelSelector into Selector")
357+
return false
358+
}
359+
360+
for namespace := range namespaces {
361+
for _, assignedPod := range assignedPods[namespace] {
362+
if assignedPod.Name == candidatePod.Name || !PodMatchesTermsNamespaceAndSelector(assignedPod, namespaces, selector) {
363+
klog.V(4).InfoS("CandidatePod doesn't matches inter-pod anti-affinity rule of assigned pod on node", "candidatePod", klog.KObj(candidatePod), "assignedPod", klog.KObj(assignedPod))
364+
continue
365+
}
366+
367+
nodeHavingAssignedPod, ok := nodeMap[assignedPod.Spec.NodeName]
368+
if !ok {
369+
continue
370+
}
371+
372+
if hasSameLabelValue(nodeHavingCandidatePod, nodeHavingAssignedPod, term.TopologyKey) {
373+
klog.V(1).InfoS("CandidatePod matches inter-pod anti-affinity rule of assigned pod on node", "candidatePod", klog.KObj(candidatePod), "assignedPod", klog.KObj(assignedPod))
374+
return true
339375
}
340376
}
341377
}
342378
}
379+
343380
return false
344381
}
345382

346-
// getPodAntiAffinityTerms gets the antiaffinity terms for the given pod.
347-
func getPodAntiAffinityTerms(podAntiAffinity *v1.PodAntiAffinity) (terms []v1.PodAffinityTerm) {
383+
// GetPodAntiAffinityTerms gets the antiaffinity terms for the given pod.
384+
func GetPodAntiAffinityTerms(podAntiAffinity *v1.PodAntiAffinity) (terms []v1.PodAffinityTerm) {
348385
if podAntiAffinity != nil {
349386
if len(podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
350387
terms = podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution

pkg/utils/priority.go

-29
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,13 @@ import (
44
"context"
55
"fmt"
66

7-
v1 "k8s.io/api/core/v1"
87
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9-
"k8s.io/apimachinery/pkg/labels"
10-
"k8s.io/apimachinery/pkg/util/sets"
118
clientset "k8s.io/client-go/kubernetes"
129
"sigs.k8s.io/descheduler/pkg/api"
1310
)
1411

1512
const SystemCriticalPriority = 2 * int32(1000000000)
1613

17-
// getNamespacesFromPodAffinityTerm returns a set of names
18-
// according to the namespaces indicated in podAffinityTerm.
19-
// If namespaces is empty it considers the given pod's namespace.
20-
func getNamespacesFromPodAffinityTerm(pod *v1.Pod, podAffinityTerm *v1.PodAffinityTerm) sets.Set[string] {
21-
names := sets.New[string]()
22-
if len(podAffinityTerm.Namespaces) == 0 {
23-
names.Insert(pod.Namespace)
24-
} else {
25-
names.Insert(podAffinityTerm.Namespaces...)
26-
}
27-
return names
28-
}
29-
30-
// podMatchesTermsNamespaceAndSelector returns true if the given <pod>
31-
// matches the namespace and selector defined by <affinityPod>`s <term>.
32-
func podMatchesTermsNamespaceAndSelector(pod *v1.Pod, namespaces sets.Set[string], selector labels.Selector) bool {
33-
if !namespaces.Has(pod.Namespace) {
34-
return false
35-
}
36-
37-
if !selector.Matches(labels.Set(pod.Labels)) {
38-
return false
39-
}
40-
return true
41-
}
42-
4314
// GetPriorityFromPriorityClass gets priority from the given priority class.
4415
// If no priority class is provided, it will return SystemCriticalPriority by default.
4516
func GetPriorityFromPriorityClass(ctx context.Context, client clientset.Interface, name string) (int32, error) {

0 commit comments

Comments
 (0)