Repository: kubernetes-sigs/descheduler (Go)

Issue:
Unable to create a profile" err="unable to build DefaultEvictor plugin
Hi!

I'm facing an error in Descheduler pod logs:

```
"unable to create a profile" err="unable to build DefaultEvictor plugin: unable to initialize \"DefaultEvictor\" plugin: indexer conflict: map[metadata.ownerReferences:{}]" profile="Descheduler"
```

I'm installing Descheduler via Helm Chart v0.30.1 with Deployment mode.

My policy config looks like this:
```
deschedulerPolicy:
  profiles:
    - name: "Descheduler"
      pluginConfig:
        - name: "DefaultEvictor"
          args:
            evictLocalStoragePods: false
            evictSystemCriticalPods: false
            ignorePvcPods: false
            evictFailedBarePods: false
            nodeFit: true
            minReplicas: 2
        - name: "RemoveDuplicates"
        - name: "LowNodeUtilization"
          args:
            thresholds:
              cpu : 30
              memory: 30
              pods: 20
            targetThresholds:
              cpu : 80
              memory: 80
              pods: 50
        - name: "HighNodeUtilization"
          args:
            thresholds:
              cpu : 80
              memory: 80
              pods: 50
        - name: "RemovePodsViolatingInterPodAntiAffinity"
        - name: "RemovePodsViolatingNodeTaints"
        - name: "RemovePodsViolatingNodeAffinity"
          args:
            nodeAffinityType:
              - "requiredDuringSchedulingIgnoredDuringExecution"
        - name: "RemovePodsViolatingTopologySpreadConstraint"
          args:
            topologyBalanceNodeFit: true
            constraints:
              - "DoNotSchedule"
              - "ScheduleAnyway"
      plugins:
        balance:
          enabled:
            - "RemoveDuplicates"
            - "LowNodeUtilization"
            - "HighNodeUtilization"
            - "RemovePodsViolatingTopologySpreadConstraint"
        deschedule:
          enabled:
            - "RemovePodsViolatingInterPodAntiAffinity"
            - "RemovePodsViolatingNodeAffinity"
            - "RemovePodsViolatingNodeTaints"
```

I also see that when pod starts, everything works fine until CronJob finishes... Then I'm starting to get these errors...

Maybe I'm configuring something wrong? 🤔 


Discussion:
It seems related to minReplicas. Removing it fixes the problem for me..
Thank you for reporting this @timetofaceit 
/assign



Patch:
diff --git a/pkg/framework/plugins/defaultevictor/defaultevictor.go b/pkg/framework/plugins/defaultevictor/defaultevictor.go
index f7e635562d..ff25ff5c0f 100644
--- a/pkg/framework/plugins/defaultevictor/defaultevictor.go
+++ b/pkg/framework/plugins/defaultevictor/defaultevictor.go
@@ -244,6 +244,15 @@ func (d *DefaultEvictor) Filter(pod *v1.Pod) bool {
 
 func getPodIndexerByOwnerRefs(indexName string, handle frameworktypes.Handle) (cache.Indexer, error) {
 	podInformer := handle.SharedInformerFactory().Core().V1().Pods().Informer()
+	indexer := podInformer.GetIndexer()
+
+	// do not reinitialize the indexer, if it's been defined already
+	for name := range indexer.GetIndexers() {
+		if name == indexName {
+			return indexer, nil
+		}
+	}
+
 	if err := podInformer.AddIndexers(cache.Indexers{
 		indexName: func(obj interface{}) ([]string, error) {
 			pod, ok := obj.(*v1.Pod)
@@ -257,6 +266,5 @@ func getPodIndexerByOwnerRefs(indexName string, handle frameworktypes.Handle) (c
 		return nil, err
 	}
 
-	indexer := podInformer.GetIndexer()
 	return indexer, nil
 }


Test patch:
diff --git a/pkg/framework/plugins/defaultevictor/defaultevictor_test.go b/pkg/framework/plugins/defaultevictor/defaultevictor_test.go
index 0cd3d804d2..16dcad49db 100644
--- a/pkg/framework/plugins/defaultevictor/defaultevictor_test.go
+++ b/pkg/framework/plugins/defaultevictor/defaultevictor_test.go
@@ -15,6 +15,7 @@ package defaultevictor
 
 import (
 	"context"
+	"fmt"
 	"testing"
 
 	v1 "k8s.io/api/core/v1"
@@ -31,6 +32,19 @@ import (
 	"sigs.k8s.io/descheduler/test"
 )
 
+type testCase struct {
+	description             string
+	pods                    []*v1.Pod
+	nodes                   []*v1.Node
+	evictFailedBarePods     bool
+	evictLocalStoragePods   bool
+	evictSystemCriticalPods bool
+	priorityThreshold       *int32
+	nodeFit                 bool
+	minReplicas             uint
+	result                  bool
+}
+
 func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
 	n1 := test.BuildTestNode("node1", 1000, 2000, 13, nil)
 
@@ -39,17 +53,6 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
 
 	nodeLabelKey := "datacenter"
 	nodeLabelValue := "east"
-	type testCase struct {
-		description             string
-		pods                    []*v1.Pod
-		nodes                   []*v1.Node
-		evictFailedBarePods     bool
-		evictLocalStoragePods   bool
-		evictSystemCriticalPods bool
-		priorityThreshold       *int32
-		nodeFit                 bool
-		result                  bool
-	}
 
 	testCases := []testCase{
 		{
@@ -305,45 +308,7 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.Background())
 			defer cancel()
 
-			var objs []runtime.Object
-			for _, node := range test.nodes {
-				objs = append(objs, node)
-			}
-			for _, pod := range test.pods {
-				objs = append(objs, pod)
-			}
-
-			fakeClient := fake.NewSimpleClientset(objs...)
-
-			sharedInformerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
-			podInformer := sharedInformerFactory.Core().V1().Pods().Informer()
-
-			getPodsAssignedToNode, err := podutil.BuildGetPodsAssignedToNodeFunc(podInformer)
-			if err != nil {
-				t.Errorf("Build get pods assigned to node function error: %v", err)
-			}
-
-			sharedInformerFactory.Start(ctx.Done())
-			sharedInformerFactory.WaitForCacheSync(ctx.Done())
-
-			defaultEvictorArgs := &DefaultEvictorArgs{
-				EvictLocalStoragePods:   test.evictLocalStoragePods,
-				EvictSystemCriticalPods: test.evictSystemCriticalPods,
-				IgnorePvcPods:           false,
-				EvictFailedBarePods:     test.evictFailedBarePods,
-				PriorityThreshold: &api.PriorityThreshold{
-					Value: test.priorityThreshold,
-				},
-				NodeFit: test.nodeFit,
-			}
-
-			evictorPlugin, err := New(
-				defaultEvictorArgs,
-				&frameworkfake.HandleImpl{
-					ClientsetImpl:                 fakeClient,
-					GetPodsAssignedToNodeFuncImpl: getPodsAssignedToNode,
-					SharedInformerFactoryImpl:     sharedInformerFactory,
-				})
+			evictorPlugin, err := initializePlugin(ctx, test)
 			if err != nil {
 				t.Fatalf("Unable to initialize the plugin: %v", err)
 			}
@@ -366,19 +331,6 @@ func TestDefaultEvictorFilter(t *testing.T) {
 
 	ownerRefUUID := uuid.NewUUID()
 
-	type testCase struct {
-		description             string
-		pods                    []*v1.Pod
-		nodes                   []*v1.Node
-		evictFailedBarePods     bool
-		evictLocalStoragePods   bool
-		evictSystemCriticalPods bool
-		priorityThreshold       *int32
-		nodeFit                 bool
-		minReplicas             uint
-		result                  bool
-	}
-
 	testCases := []testCase{
 		{
 			description: "Failed pod eviction with no ownerRefs",
@@ -757,54 +709,106 @@ func TestDefaultEvictorFilter(t *testing.T) {
 			ctx, cancel := context.WithCancel(context.Background())
 			defer cancel()
 
-			var objs []runtime.Object
-			for _, node := range test.nodes {
-				objs = append(objs, node)
+			evictorPlugin, err := initializePlugin(ctx, test)
+			if err != nil {
+				t.Fatalf("Unable to initialize the plugin: %v", err)
 			}
-			for _, pod := range test.pods {
-				objs = append(objs, pod)
+
+			result := evictorPlugin.(frameworktypes.EvictorPlugin).Filter(test.pods[0])
+			if (result) != test.result {
+				t.Errorf("Filter should return for pod %s %t, but it returns %t", test.pods[0].Name, test.result, result)
 			}
+		})
+	}
+}
 
-			fakeClient := fake.NewSimpleClientset(objs...)
+func TestReinitialization(t *testing.T) {
+	n1 := test.BuildTestNode("node1", 1000, 2000, 13, nil)
+	ownerRefUUID := uuid.NewUUID()
+
+	testCases := []testCase{
+		{
+			description: "minReplicas of 2, multiple owners, eviction",
+			pods: []*v1.Pod{
+				test.BuildTestPod("p1", 1, 1, n1.Name, func(pod *v1.Pod) {
+					pod.ObjectMeta.OwnerReferences = append(test.GetNormalPodOwnerRefList(), test.GetNormalPodOwnerRefList()...)
+					pod.ObjectMeta.OwnerReferences[0].UID = ownerRefUUID
+				}),
+				test.BuildTestPod("p2", 1, 1, n1.Name, func(pod *v1.Pod) {
+					pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
+				}),
+			},
+			minReplicas: 2,
+			result:      true,
+		},
+	}
 
-			sharedInformerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
-			podInformer := sharedInformerFactory.Core().V1().Pods().Informer()
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			ctx, cancel := context.WithCancel(context.Background())
+			defer cancel()
 
-			getPodsAssignedToNode, err := podutil.BuildGetPodsAssignedToNodeFunc(podInformer)
+			evictorPlugin, err := initializePlugin(ctx, test)
 			if err != nil {
-				t.Errorf("Build get pods assigned to node function error: %v", err)
+				t.Fatalf("Unable to initialize the plugin: %v", err)
 			}
 
-			sharedInformerFactory.Start(ctx.Done())
-			sharedInformerFactory.WaitForCacheSync(ctx.Done())
-
-			defaultEvictorArgs := &DefaultEvictorArgs{
-				EvictLocalStoragePods:   test.evictLocalStoragePods,
-				EvictSystemCriticalPods: test.evictSystemCriticalPods,
-				IgnorePvcPods:           false,
-				EvictFailedBarePods:     test.evictFailedBarePods,
-				PriorityThreshold: &api.PriorityThreshold{
-					Value: test.priorityThreshold,
-				},
-				NodeFit:     test.nodeFit,
-				MinReplicas: test.minReplicas,
+			defaultEvictor, ok := evictorPlugin.(*DefaultEvictor)
+			if !ok {
+				t.Fatalf("Unable to initialize as a DefaultEvictor plugin")
 			}
-
-			evictorPlugin, err := New(
-				defaultEvictorArgs,
-				&frameworkfake.HandleImpl{
-					ClientsetImpl:                 fakeClient,
-					GetPodsAssignedToNodeFuncImpl: getPodsAssignedToNode,
-					SharedInformerFactoryImpl:     sharedInformerFactory,
-				})
+			_, err = New(defaultEvictor.args, defaultEvictor.handle)
 			if err != nil {
-				t.Fatalf("Unable to initialize the plugin: %v", err)
+				t.Fatalf("Unable to reinitialize the plugin: %v", err)
 			}
+		})
+	}
+}
 
-			result := evictorPlugin.(frameworktypes.EvictorPlugin).Filter(test.pods[0])
-			if (result) != test.result {
-				t.Errorf("Filter should return for pod %s %t, but it returns %t", test.pods[0].Name, test.result, result)
-			}
+func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin, error) {
+	var objs []runtime.Object
+	for _, node := range test.nodes {
+		objs = append(objs, node)
+	}
+	for _, pod := range test.pods {
+		objs = append(objs, pod)
+	}
+
+	fakeClient := fake.NewSimpleClientset(objs...)
+
+	sharedInformerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
+	podInformer := sharedInformerFactory.Core().V1().Pods().Informer()
+
+	getPodsAssignedToNode, err := podutil.BuildGetPodsAssignedToNodeFunc(podInformer)
+	if err != nil {
+		return nil, fmt.Errorf("build get pods assigned to node function error: %v", err)
+	}
+
+	sharedInformerFactory.Start(ctx.Done())
+	sharedInformerFactory.WaitForCacheSync(ctx.Done())
+
+	defaultEvictorArgs := &DefaultEvictorArgs{
+		EvictLocalStoragePods:   test.evictLocalStoragePods,
+		EvictSystemCriticalPods: test.evictSystemCriticalPods,
+		IgnorePvcPods:           false,
+		EvictFailedBarePods:     test.evictFailedBarePods,
+		PriorityThreshold: &api.PriorityThreshold{
+			Value: test.priorityThreshold,
+		},
+		NodeFit:     test.nodeFit,
+		MinReplicas: test.minReplicas,
+	}
+
+	evictorPlugin, err := New(
+		defaultEvictorArgs,
+		&frameworkfake.HandleImpl{
+			ClientsetImpl:                 fakeClient,
+			GetPodsAssignedToNodeFuncImpl: getPodsAssignedToNode,
+			SharedInformerFactoryImpl:     sharedInformerFactory,
 		})
+	if err != nil {
+		return nil, fmt.Errorf("unable to initialize the plugin: %v", err)
 	}
+
+	return evictorPlugin, nil
 }