Skip to content

Commit 2aa29aa

Browse files
committed
min replicas - use informer indexer for pod owner ref uid
1 parent 6fdb783 commit 2aa29aa

File tree

4 files changed

+86
-16
lines changed

4 files changed

+86
-16
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ The Default Evictor Plugin is used by default for filtering pods before processi
142142
|`labelSelector`|`metav1.LabelSelector`||(see [label filtering](#label-filtering))|
143143
|`priorityThreshold`|`priorityThreshold`||(see [priority filtering](#priority-filtering))|
144144
|`nodeFit`|`bool`|`false`|(see [node fit filtering](#node-fit-filtering))|
145-
|`minReplicas`|`uint`|`0`| ignore eviction of pods where owner (e.g. Deployment) replicas is below this threshold |
145+
|`minReplicas`|`uint`|`0`| ignore eviction of pods where owner (e.g. `ReplicaSet`) replicas is below this threshold |
146146

147147
### Example policy
148148

pkg/descheduler/pod/pods.go

+23-11
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,25 @@ func BuildGetPodsAssignedToNodeFunc(podInformer cache.SharedIndexInformer) (GetP
143143
if err != nil {
144144
return nil, err
145145
}
146-
pods := make([]*v1.Pod, 0, len(objs))
147-
for _, obj := range objs {
148-
pod, ok := obj.(*v1.Pod)
149-
if !ok {
150-
continue
151-
}
152-
if filter(pod) {
153-
pods = append(pods, pod)
154-
}
155-
}
156-
return pods, nil
146+
return ConvertToPods(objs, filter), nil
157147
}
158148
return getPodsAssignedToNode, nil
159149
}
160150

151+
func ConvertToPods(objs []interface{}, filter FilterFunc) []*v1.Pod {
152+
pods := make([]*v1.Pod, 0, len(objs))
153+
for _, obj := range objs {
154+
pod, ok := obj.(*v1.Pod)
155+
if !ok {
156+
continue
157+
}
158+
if filter == nil || filter(pod) {
159+
pods = append(pods, pod)
160+
}
161+
}
162+
return pods
163+
}
164+
161165
// ListPodsOnNodes returns all pods on given nodes.
162166
func ListPodsOnNodes(nodes []*v1.Node, getPodsAssignedToNode GetPodsAssignedToNodeFunc, filter FilterFunc) ([]*v1.Pod, error) {
163167
pods := make([]*v1.Pod, 0)
@@ -216,6 +220,14 @@ func OwnerRef(pod *v1.Pod) []metav1.OwnerReference {
216220
return pod.ObjectMeta.GetOwnerReferences()
217221
}
218222

223+
func OwnerRefUIDs(pod *v1.Pod) []string {
224+
var ownerRefUIDs []string
225+
for _, ownerRef := range OwnerRef(pod) {
226+
ownerRefUIDs = append(ownerRefUIDs, string(ownerRef.UID))
227+
}
228+
return ownerRefUIDs
229+
}
230+
219231
func IsBestEffortPod(pod *v1.Pod) bool {
220232
return utils.GetPodQOS(pod) == v1.PodQOSBestEffort
221233
}

pkg/framework/plugins/defaultevictor/defaultevictor.go

+42-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ package defaultevictor
1616
import (
1717
// "context"
1818
"context"
19+
"errors"
1920
"fmt"
2021

2122
v1 "k8s.io/api/core/v1"
2223
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2324
"k8s.io/apimachinery/pkg/labels"
2425
"k8s.io/apimachinery/pkg/runtime"
25-
"k8s.io/apimachinery/pkg/util/errors"
26+
utilerrors "k8s.io/apimachinery/pkg/util/errors"
27+
"k8s.io/client-go/tools/cache"
2628
"k8s.io/klog/v2"
2729
nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node"
2830
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
@@ -144,16 +146,33 @@ func New(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plug
144146
}
145147

146148
if defaultEvictorArgs.MinReplicas > 1 {
147-
podLister := handle.SharedInformerFactory().Core().V1().Pods().Lister()
149+
indexName := "metadata.ownerReferences"
150+
indexer, err := getPodIndexerByOwnerRefs(indexName, handle)
151+
if err != nil {
152+
return nil, err
153+
}
148154
ev.constraints = append(ev.constraints, func(pod *v1.Pod) error {
149-
pods, err := podLister.Pods(pod.Namespace).List(labels.Everything())
155+
if len(pod.OwnerReferences) == 0 {
156+
return nil
157+
}
158+
159+
if len(pod.OwnerReferences) > 1 {
160+
klog.V(5).InfoS("pod has %d owner references which is not supported for MinReplicas check", len(pod.OwnerReferences))
161+
return nil
162+
}
163+
164+
ownerRef := pod.OwnerReferences[0]
165+
objs, err := indexer.ByIndex(indexName, string(ownerRef.UID))
150166
if err != nil {
151167
return fmt.Errorf("unable to list pods for minReplicas filter in the policy parameter")
152168
}
169+
170+
pods := podutil.ConvertToPods(objs, nil)
153171
ownerReplicas := podutil.GetOwnerReplicasCount(pods, pod)
154172
if ownerReplicas < defaultEvictorArgs.MinReplicas {
155173
return fmt.Errorf("owner has %d replicas which is less than minReplicas of %d", ownerReplicas, defaultEvictorArgs.MinReplicas)
156174
}
175+
157176
return nil
158177
})
159178
}
@@ -214,9 +233,28 @@ func (d *DefaultEvictor) Filter(pod *v1.Pod) bool {
214233
}
215234

216235
if len(checkErrs) > 0 {
217-
klog.V(4).InfoS("Pod fails the following checks", "pod", klog.KObj(pod), "checks", errors.NewAggregate(checkErrs).Error())
236+
klog.V(4).InfoS("Pod fails the following checks", "pod", klog.KObj(pod), "checks", utilerrors.NewAggregate(checkErrs).Error())
218237
return false
219238
}
220239

221240
return true
222241
}
242+
243+
func getPodIndexerByOwnerRefs(indexName string, handle frameworktypes.Handle) (cache.Indexer, error) {
244+
podInformer := handle.SharedInformerFactory().Core().V1().Pods().Informer()
245+
if err := podInformer.AddIndexers(cache.Indexers{
246+
indexName: func(obj interface{}) ([]string, error) {
247+
pod, ok := obj.(*v1.Pod)
248+
if !ok {
249+
return []string{}, errors.New("unexpected object")
250+
}
251+
252+
return podutil.OwnerRefUIDs(pod), nil
253+
},
254+
}); err != nil {
255+
return nil, err
256+
}
257+
258+
indexer := podInformer.GetIndexer()
259+
return indexer, nil
260+
}

pkg/framework/plugins/defaultevictor/defaultevictor_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
v1 "k8s.io/api/core/v1"
2121
"k8s.io/apimachinery/pkg/api/resource"
2222
"k8s.io/apimachinery/pkg/runtime"
23+
"k8s.io/apimachinery/pkg/util/uuid"
2324
"k8s.io/client-go/informers"
2425
"k8s.io/client-go/kubernetes/fake"
2526
"sigs.k8s.io/descheduler/pkg/api"
@@ -363,6 +364,8 @@ func TestDefaultEvictorFilter(t *testing.T) {
363364
nodeTaintKey := "hardware"
364365
nodeTaintValue := "gpu"
365366

367+
ownerRefUUID := uuid.NewUUID()
368+
366369
type testCase struct {
367370
description string
368371
pods []*v1.Pod
@@ -710,9 +713,11 @@ func TestDefaultEvictorFilter(t *testing.T) {
710713
pods: []*v1.Pod{
711714
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
712715
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
716+
pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
713717
}),
714718
test.BuildTestPod("p2", 1, 1, n1.Name, func(pod *v1.Pod) {
715719
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
720+
pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
716721
}),
717722
},
718723
minReplicas: 2,
@@ -722,13 +727,28 @@ func TestDefaultEvictorFilter(t *testing.T) {
722727
pods: []*v1.Pod{
723728
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
724729
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
730+
pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
725731
}),
726732
test.BuildTestPod("p2", 1, 1, n1.Name, func(pod *v1.Pod) {
727733
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
734+
pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
728735
}),
729736
},
730737
minReplicas: 3,
731738
result: false,
739+
}, {
740+
description: "minReplicas of 2, multiple owners, no eviction",
741+
pods: []*v1.Pod{
742+
test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
743+
pod.ObjectMeta.OwnerReferences = append(test.GetNormalPodOwnerRefList(), test.GetNormalPodOwnerRefList()...)
744+
pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
745+
}),
746+
test.BuildTestPod("p2", 1, 1, n1.Name, func(pod *v1.Pod) {
747+
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
748+
}),
749+
},
750+
minReplicas: 2,
751+
result: true,
732752
},
733753
}
734754

0 commit comments

Comments
 (0)