Repository: kubernetes-sigs/descheduler
Pull request #1436 (2024-06-09 16:41:46)

return node fit error in advance
There are many filter policies in the current node fit, similar to the filter plugin in the scheduler. The correct logic should be: if any plugin does not meet the conditions, it should be terminated in advance. The current check logic is incomprehensible, and it actually performs all filter check logic, which is not only time-consuming, but also very unnecessary.(https://github.com/kubernetes-sigs/descheduler/blob/master/pkg/descheduler/node/node.go#L107)

Because the default loop time is 5min, this time can be adjusted. The larger the cluster, the longer the nodeFit time, so the full loop time can easily exceed 5min. What if the user sets the loop time to 1 minute? Comparing one by one I think is very, very unnecessary, too time-consuming, we should be the same as the filter policy in the scheduler.


When checking the pod at https://github.com/kubernetes-sigs/descheduler/blob/master/pkg/framework/plugins/defaultevictor/defaultevictor.go#L67, it also returns early

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign [knelasevero](https://github.com/knelasevero) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fdescheduler).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/descheduler/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["knelasevero"]} -->
k8s-ci-robot: Hi @fanhaouu. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fdescheduler).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

a7i: /ok-to-test

/assign @ingvagabund given the recent discussions on future of NodeFit
ingvagabund: Or, we could run the checks in parallel. The question is whether it's useful to notify about all individual checks errors. If the goal here is to reduce the computation time, then parallelization works better.
k8s-ci-robot: @fanhaouu: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-descheduler-verify-master | ec6299062d476de1dc5e117f7c69bc334a79cb5c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_descheduler/1436/pull-descheduler-verify-master/1800921139610914816) | true | `/test pull-descheduler-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=descheduler&pr=1436). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afanhaouu). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/descheduler/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @fanhaouu: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-descheduler-verify-master | a7c4efc2bd3db3f119f30c4406fb05dec072739f | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_descheduler/1436/pull-descheduler-verify-master/1800923715261698048) | true | `/test pull-descheduler-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=descheduler&pr=1436). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afanhaouu). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/descheduler/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
fanhaouu: > Or, we could run the checks in parallel. The question is whether it's useful to notify about all individual checks errors. If the goal here is to reduce the computation time, then parallelization works better.

thank you, master, I just added the parallel logic. I forgot to submit the code for parallel processing earlier.
ingvagabund: >  I just added the parallel logic.

This is another parallelization. Originally, I mean to parallelize internal of `NodeFit` itself. E.g. the individual checks. If we parallelize on the level of PodFitsAnyNode/PodFitsAnyOtherNode we need a different discussion.
ingvagabund: @fanhaouu in my proposal I'd like to allow users to extend available nodeFit checks through custom plugins. For which we need to make sure none of the individual checks modifies pod/node arguments. Though, I can make a note in the proposal to revisit the parallelization later so I don't block this PR.
fanhaouu: > With the new parallelization would you please create new test cases for both `PodFitsAnyOtherNode` and `PodFitsAnyNode` to make sure each node is processed properly? E.g. run `PodFitsAnyOtherNode` with a list of e.g. 4 nodes:
> 
> * 1st and 3rd nodes do not fit a pod
> * 2nd and 4th nodes do not fit a pod
> 
> To make sure the parallelization works as expected.

ok, master
fanhaouu: > Given there's `podFitsNodes` function would you please create a new test specifically for testing the parallelization? Something like this:
> 
> ```go
> nodesTraversed := make(map[string]struct{})
> ...
> podFitsNodes(
> 	nodeIndexer,
> 	pod,
> 	nodes,
> 	func(pod *v1.Pod, node *v1.Node) bool {
> 		nodesTraversed[node.Name] = struct{}{}
> 		return false
> 	},
> )
> ...
> // checking all four nodes have been processed/read.
> for _, nodeName := range nodeNames {
> 	if _, exists := nodesTraversed[nodeName]; !exists {
> 		t.Failf("Node %v was not proccesed", nodeName)
> 	}
> }
> ```
> 
> To make sure every node is checked. With this the new tests are no longer needed.

ok, thank you, master
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from [ingvagabund](https://github.com/ingvagabund). For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fdescheduler).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/descheduler/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ingvagabund"]} -->
k8s-ci-robot: @fanhaouu: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-descheduler-unit-test-master-master | 00e47f64cba7bf25fc56c45aceee090372d270ea | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_descheduler/1436/pull-descheduler-unit-test-master-master/1805884531144658944) | true | `/test pull-descheduler-unit-test-master-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=descheduler&pr=1436). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afanhaouu). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/descheduler/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @fanhaouu: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-descheduler-unit-test-master-master | 00e47f64cba7bf25fc56c45aceee090372d270ea | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_descheduler/1436/pull-descheduler-unit-test-master-master/1805884531144658944) | true | `/test pull-descheduler-unit-test-master-master`
pull-descheduler-verify-master | 00e47f64cba7bf25fc56c45aceee090372d270ea | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_descheduler/1436/pull-descheduler-verify-master/1805884531043995648) | true | `/test pull-descheduler-verify-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=descheduler&pr=1436). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Afanhaouu). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/descheduler/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/pkg/descheduler/descheduler.go b/pkg/descheduler/descheduler.go
index 2062319ae..92ffb56e7 100644
--- a/pkg/descheduler/descheduler.go
+++ b/pkg/descheduler/descheduler.go
@@ -70,17 +70,16 @@ type profileRunner struct {
 }
 
 type descheduler struct {
-	rs                     *options.DeschedulerServer
-	podLister              listersv1.PodLister
-	nodeLister             listersv1.NodeLister
-	namespaceLister        listersv1.NamespaceLister
-	priorityClassLister    schedulingv1.PriorityClassLister
-	getPodsAssignedToNode  podutil.GetPodsAssignedToNodeFunc
-	sharedInformerFactory  informers.SharedInformerFactory
-	deschedulerPolicy      *api.DeschedulerPolicy
-	eventRecorder          events.EventRecorder
-	podEvictor             *evictions.PodEvictor
-	podEvictionReactionFnc func(*fakeclientset.Clientset) func(action core.Action) (bool, runtime.Object, error)
+	rs                         *options.DeschedulerServer
+	podLister                  listersv1.PodLister
+	nodeLister                 listersv1.NodeLister
+	namespaceLister            listersv1.NamespaceLister
+	priorityClassLister        schedulingv1.PriorityClassLister
+	getPodsAssignedToNode      podutil.GetPodsAssignedToNodeFunc
+	sharedInformerFactory      informers.SharedInformerFactory
+	evictionPolicyGroupVersion string
+	deschedulerPolicy          *api.DeschedulerPolicy
+	eventRecorder              events.EventRecorder
 }
 
 func newDescheduler(rs *options.DeschedulerServer, deschedulerPolicy *api.DeschedulerPolicy, evictionPolicyGroupVersion string, eventRecorder events.EventRecorder, sharedInformerFactory informers.SharedInformerFactory) (*descheduler, error) {
@@ -95,28 +94,17 @@ func newDescheduler(rs *options.DeschedulerServer, deschedulerPolicy *api.Desche
 		return nil, fmt.Errorf("build get pods assigned to node function error: %v", err)
 	}
 
-	podEvictor := evictions.NewPodEvictor(
-		nil,
-		evictionPolicyGroupVersion,
-		rs.DryRun,
-		deschedulerPolicy.MaxNoOfPodsToEvictPerNode,
-		deschedulerPolicy.MaxNoOfPodsToEvictPerNamespace,
-		!rs.DisableMetrics,
-		eventRecorder,
-	)
-
 	return &descheduler{
-		rs:                     rs,
-		podLister:              podLister,
-		nodeLister:             nodeLister,
-		namespaceLister:        namespaceLister,
-		priorityClassLister:    priorityClassLister,
-		getPodsAssignedToNode:  getPodsAssignedToNode,
-		sharedInformerFactory:  sharedInformerFactory,
-		deschedulerPolicy:      deschedulerPolicy,
-		eventRecorder:          eventRecorder,
-		podEvictor:             podEvictor,
-		podEvictionReactionFnc: podEvictionReactionFnc,
+		rs:                         rs,
+		podLister:                  podLister,
+		nodeLister:                 nodeLister,
+		namespaceLister:            namespaceLister,
+		priorityClassLister:        priorityClassLister,
+		getPodsAssignedToNode:      getPodsAssignedToNode,
+		sharedInformerFactory:      sharedInformerFactory,
+		evictionPolicyGroupVersion: evictionPolicyGroupVersion,
+		deschedulerPolicy:          deschedulerPolicy,
+		eventRecorder:              eventRecorder,
 	}, nil
 }
 
@@ -141,10 +129,7 @@ func (d *descheduler) runDeschedulerLoop(ctx context.Context, nodes []*v1.Node)
 	if d.rs.DryRun {
 		klog.V(3).Infof("Building a cached client from the cluster for the dry run")
 		// Create a new cache so we start from scratch without any leftovers
-		fakeClient := fakeclientset.NewSimpleClientset()
-		// simulate a pod eviction by deleting a pod
-		fakeClient.PrependReactor("create", "pods", d.podEvictionReactionFnc(fakeClient))
-		err := cachedClient(d.rs.Client, fakeClient, d.podLister, d.nodeLister, d.namespaceLister, d.priorityClassLister)
+		fakeClient, err := cachedClient(d.rs.Client, d.podLister, d.nodeLister, d.namespaceLister, d.priorityClassLister)
 		if err != nil {
 			return err
 		}
@@ -168,13 +153,21 @@ func (d *descheduler) runDeschedulerLoop(ctx context.Context, nodes []*v1.Node)
 		client = d.rs.Client
 	}
 
-	klog.V(3).Infof("Setting up the pod evictor")
-	d.podEvictor.SetClient(client)
-	d.podEvictor.ResetCounters()
+	klog.V(3).Infof("Building a pod evictor")
+	podEvictor := evictions.NewPodEvictor(
+		client,
+		d.evictionPolicyGroupVersion,
+		d.rs.DryRun,
+		d.deschedulerPolicy.MaxNoOfPodsToEvictPerNode,
+		d.deschedulerPolicy.MaxNoOfPodsToEvictPerNamespace,
+		nodes,
+		!d.rs.DisableMetrics,
+		d.eventRecorder,
+	)
 
-	d.runProfiles(ctx, client, nodes)
+	d.runProfiles(ctx, client, nodes, podEvictor)
 
-	klog.V(1).InfoS("Number of evicted pods", "totalEvicted", d.podEvictor.TotalEvicted())
+	klog.V(1).InfoS("Number of evicted pods", "totalEvicted", podEvictor.TotalEvicted())
 
 	return nil
 }
@@ -182,7 +175,7 @@ func (d *descheduler) runDeschedulerLoop(ctx context.Context, nodes []*v1.Node)
 // runProfiles runs all the deschedule plugins of all profiles and
 // later runs through all balance plugins of all profiles. (All Balance plugins should come after all Deschedule plugins)
 // see https://github.com/kubernetes-sigs/descheduler/issues/979
-func (d *descheduler) runProfiles(ctx context.Context, client clientset.Interface, nodes []*v1.Node) {
+func (d *descheduler) runProfiles(ctx context.Context, client clientset.Interface, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
 	var span trace.Span
 	ctx, span = tracing.Tracer().Start(ctx, "runProfiles")
 	defer span.End()
@@ -193,7 +186,7 @@ func (d *descheduler) runProfiles(ctx context.Context, client clientset.Interfac
 			pluginregistry.PluginRegistry,
 			frameworkprofile.WithClientSet(client),
 			frameworkprofile.WithSharedInformerFactory(d.sharedInformerFactory),
-			frameworkprofile.WithPodEvictor(d.podEvictor),
+			frameworkprofile.WithPodEvictor(podEvictor),
 			frameworkprofile.WithGetPodsAssignedToNodeFnc(d.getPodsAssignedToNode),
 		)
 		if err != nil {
@@ -313,8 +306,16 @@ func validateVersionCompatibility(discovery discovery.DiscoveryInterface, versio
 	return nil
 }
 
-func podEvictionReactionFnc(fakeClient *fakeclientset.Clientset) func(action core.Action) (bool, runtime.Object, error) {
-	return func(action core.Action) (bool, runtime.Object, error) {
+func cachedClient(
+	realClient clientset.Interface,
+	podLister listersv1.PodLister,
+	nodeLister listersv1.NodeLister,
+	namespaceLister listersv1.NamespaceLister,
+	priorityClassLister schedulingv1.PriorityClassLister,
+) (clientset.Interface, error) {
+	fakeClient := fakeclientset.NewSimpleClientset()
+	// simulate a pod eviction by deleting a pod
+	fakeClient.PrependReactor("create", "pods", func(action core.Action) (bool, runtime.Object, error) {
 		if action.GetSubresource() == "eviction" {
 			createAct, matched := action.(core.CreateActionImpl)
 			if !matched {
@@ -331,63 +332,54 @@ func podEvictionReactionFnc(fakeClient *fakeclientset.Clientset) func(action cor
 		}
 		// fallback to the default reactor
 		return false, nil, nil
-	}
-}
+	})
 
-func cachedClient(
-	realClient clientset.Interface,
-	fakeClient *fakeclientset.Clientset,
-	podLister listersv1.PodLister,
-	nodeLister listersv1.NodeLister,
-	namespaceLister listersv1.NamespaceLister,
-	priorityClassLister schedulingv1.PriorityClassLister,
-) error {
 	klog.V(3).Infof("Pulling resources for the cached client from the cluster")
 	pods, err := podLister.List(labels.Everything())
 	if err != nil {
-		return fmt.Errorf("unable to list pods: %v", err)
+		return nil, fmt.Errorf("unable to list pods: %v", err)
 	}
 
 	for _, item := range pods {
 		if _, err := fakeClient.CoreV1().Pods(item.Namespace).Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
-			return fmt.Errorf("unable to copy pod: %v", err)
+			return nil, fmt.Errorf("unable to copy pod: %v", err)
 		}
 	}
 
 	nodes, err := nodeLister.List(labels.Everything())
 	if err != nil {
-		return fmt.Errorf("unable to list nodes: %v", err)
+		return nil, fmt.Errorf("unable to list nodes: %v", err)
 	}
 
 	for _, item := range nodes {
 		if _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
-			return fmt.Errorf("unable to copy node: %v", err)
+			return nil, fmt.Errorf("unable to copy node: %v", err)
 		}
 	}
 
 	namespaces, err := namespaceLister.List(labels.Everything())
 	if err != nil {
-		return fmt.Errorf("unable to list namespaces: %v", err)
+		return nil, fmt.Errorf("unable to list namespaces: %v", err)
 	}
 
 	for _, item := range namespaces {
 		if _, err := fakeClient.CoreV1().Namespaces().Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
-			return fmt.Errorf("unable to copy namespace: %v", err)
+			return nil, fmt.Errorf("unable to copy namespace: %v", err)
 		}
 	}
 
 	priorityClasses, err := priorityClassLister.List(labels.Everything())
 	if err != nil {
-		return fmt.Errorf("unable to list priorityclasses: %v", err)
+		return nil, fmt.Errorf("unable to list priorityclasses: %v", err)
 	}
 
 	for _, item := range priorityClasses {
 		if _, err := fakeClient.SchedulingV1().PriorityClasses().Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
-			return fmt.Errorf("unable to copy priorityclass: %v", err)
+			return nil, fmt.Errorf("unable to copy priorityclass: %v", err)
 		}
 	}
 
-	return nil
+	return fakeClient, nil
 }
 
 func RunDeschedulerStrategies(ctx context.Context, rs *options.DeschedulerServer, deschedulerPolicy *api.DeschedulerPolicy, evictionPolicyGroupVersion string) error {
diff --git a/pkg/descheduler/evictions/evictions.go b/pkg/descheduler/evictions/evictions.go
index 87379e04f..ffbf287df 100644
--- a/pkg/descheduler/evictions/evictions.go
+++ b/pkg/descheduler/evictions/evictions.go
@@ -43,6 +43,7 @@ type (
 
 type PodEvictor struct {
 	client                     clientset.Interface
+	nodes                      []*v1.Node
 	policyGroupVersion         string
 	dryRun                     bool
 	maxPodsToEvictPerNode      *uint
@@ -59,17 +60,26 @@ func NewPodEvictor(
 	dryRun bool,
 	maxPodsToEvictPerNode *uint,
 	maxPodsToEvictPerNamespace *uint,
+	nodes []*v1.Node,
 	metricsEnabled bool,
 	eventRecorder events.EventRecorder,
 ) *PodEvictor {
+	nodePodCount := make(nodePodEvictedCount)
+	namespacePodCount := make(namespacePodEvictCount)
+	for _, node := range nodes {
+		// Initialize podsEvicted till now with 0.
+		nodePodCount[node.Name] = 0
+	}
+
 	return &PodEvictor{
 		client:                     client,
+		nodes:                      nodes,
 		policyGroupVersion:         policyGroupVersion,
 		dryRun:                     dryRun,
 		maxPodsToEvictPerNode:      maxPodsToEvictPerNode,
 		maxPodsToEvictPerNamespace: maxPodsToEvictPerNamespace,
-		nodepodCount:               make(nodePodEvictedCount),
-		namespacePodCount:          make(namespacePodEvictCount),
+		nodepodCount:               nodePodCount,
+		namespacePodCount:          namespacePodCount,
 		metricsEnabled:             metricsEnabled,
 		eventRecorder:              eventRecorder,
 	}
@@ -97,15 +107,6 @@ func (pe *PodEvictor) NodeLimitExceeded(node *v1.Node) bool {
 	return false
 }
 
-func (pe *PodEvictor) ResetCounters() {
-	pe.nodepodCount = make(nodePodEvictedCount)
-	pe.namespacePodCount = make(namespacePodEvictCount)
-}
-
-func (pe *PodEvictor) SetClient(client clientset.Interface) {
-	pe.client = client
-}
-
 // EvictOptions provides a handle for passing additional info to EvictPod
 type EvictOptions struct {
 	// Reason allows for passing details about the specific eviction for logging.
diff --git a/pkg/descheduler/node/node.go b/pkg/descheduler/node/node.go
index d28328065..2f0e58e89 100644
--- a/pkg/descheduler/node/node.go
+++ b/pkg/descheduler/node/node.go
@@ -18,20 +18,24 @@ package node
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"sync/atomic"
 
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
-	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	clientset "k8s.io/client-go/kubernetes"
 	listersv1 "k8s.io/client-go/listers/core/v1"
+	"k8s.io/client-go/util/workqueue"
 	"k8s.io/klog/v2"
 	podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
 	"sigs.k8s.io/descheduler/pkg/utils"
 )
 
+const workersCount = 100
+
 // ReadyNodes returns ready nodes irrespective of whether they are
 // schedulable or not.
 func ReadyNodes(ctx context.Context, client clientset.Interface, nodeLister listersv1.NodeLister, nodeSelector string) ([]*v1.Node, error) {
@@ -104,90 +108,96 @@ func IsReady(node *v1.Node) bool {
 // This function is used when the NodeFit pod filtering feature of the Descheduler is enabled.
 // This function currently considers a subset of the Kubernetes Scheduler's predicates when
 // deciding if a pod would fit on a node, but more predicates may be added in the future.
-func NodeFit(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, node *v1.Node) []error {
+// There should be no methods to modify nodes or pods in this method.
+func NodeFit(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, node *v1.Node) error {
 	// Check node selector and required affinity
-	var errors []error
 	if ok, err := utils.PodMatchNodeSelector(pod, node); err != nil {
-		errors = append(errors, err)
+		return err
 	} else if !ok {
-		errors = append(errors, fmt.Errorf("pod node selector does not match the node label"))
+		return errors.New("pod node selector does not match the node label")
 	}
+
 	// Check taints (we only care about NoSchedule and NoExecute taints)
 	ok := utils.TolerationsTolerateTaintsWithFilter(pod.Spec.Tolerations, node.Spec.Taints, func(taint *v1.Taint) bool {
 		return taint.Effect == v1.TaintEffectNoSchedule || taint.Effect == v1.TaintEffectNoExecute
 	})
 	if !ok {
-		errors = append(errors, fmt.Errorf("pod does not tolerate taints on the node"))
+		return errors.New("pod does not tolerate taints on the node")
 	}
+
 	// Check if the pod can fit on a node based off it's requests
 	if pod.Spec.NodeName == "" || pod.Spec.NodeName != node.Name {
-		if ok, reqErrors := fitsRequest(nodeIndexer, pod, node); !ok {
-			errors = append(errors, reqErrors...)
+		if ok, reqError := fitsRequest(nodeIndexer, pod, node); !ok {
+			return reqError
 		}
 	}
+
 	// Check if node is schedulable
 	if IsNodeUnschedulable(node) {
-		errors = append(errors, fmt.Errorf("node is not schedulable"))
+		return errors.New("node is not schedulable")
 	}
 
 	// Check if pod matches inter-pod anti-affinity rule of pod on node
 	if match, err := podMatchesInterPodAntiAffinity(nodeIndexer, pod, node); err != nil {
-		errors = append(errors, err)
+		return err
 	} else if match {
-		errors = append(errors, fmt.Errorf("pod matches inter-pod anti-affinity rule of other pod on node"))
+		return errors.New("pod matches inter-pod anti-affinity rule of other pod on node")
 	}
 
-	return errors
+	return nil
 }
 
-// PodFitsAnyOtherNode checks if the given pod will fit any of the given nodes, besides the node
-// the pod is already running on. The predicates used to determine if the pod will fit can be found in the NodeFit function.
-func PodFitsAnyOtherNode(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, nodes []*v1.Node) bool {
-	for _, node := range nodes {
-		// Skip node pod is already on
-		if node.Name == pod.Spec.NodeName {
-			continue
-		}
+func podFitsNodes(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, nodes []*v1.Node, excludeFilter func(pod *v1.Pod, node *v1.Node) bool) bool {
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
 
-		errors := NodeFit(nodeIndexer, pod, node)
-		if len(errors) == 0 {
+	var filteredLen int32
+	checkNode := func(i int) {
+		node := nodes[i]
+		if excludeFilter != nil && excludeFilter(pod, node) {
+			return
+		}
+		err := NodeFit(nodeIndexer, pod, node)
+		if err == nil {
 			klog.V(4).InfoS("Pod fits on node", "pod", klog.KObj(pod), "node", klog.KObj(node))
-			return true
+			atomic.AddInt32(&filteredLen, 1)
+			cancel()
+		} else {
+			klog.V(4).InfoS("Pod does not fit on node", "pod", klog.KObj(pod), "node", klog.KObj(node), "err", err.Error())
 		}
-		klog.V(4).InfoS("Pod does not fit on any other node",
-			"pod:", klog.KObj(pod), "node:", klog.KObj(node), "error:", utilerrors.NewAggregate(errors).Error())
 	}
 
-	return false
+	// Stops searching for more nodes once a node are found.
+	workqueue.ParallelizeUntil(ctx, workersCount, len(nodes), checkNode)
+
+	return filteredLen > 0
+}
+
+// PodFitsAnyOtherNode checks if the given pod will fit any of the given nodes, besides the node
+// the pod is already running on. The predicates used to determine if the pod will fit can be found in the NodeFit function.
+func PodFitsAnyOtherNode(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, nodes []*v1.Node) bool {
+	return podFitsNodes(nodeIndexer, pod, nodes, func(pod *v1.Pod, node *v1.Node) bool {
+		return pod.Spec.NodeName == node.Name
+	})
 }
 
 // PodFitsAnyNode checks if the given pod will fit any of the given nodes. The predicates used
 // to determine if the pod will fit can be found in the NodeFit function.
 func PodFitsAnyNode(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, nodes []*v1.Node) bool {
-	for _, node := range nodes {
-		errors := NodeFit(nodeIndexer, pod, node)
-		if len(errors) == 0 {
-			klog.V(4).InfoS("Pod fits on node", "pod", klog.KObj(pod), "node", klog.KObj(node))
-			return true
-		}
-		klog.V(4).InfoS("Pod does not fit on any node",
-			"pod:", klog.KObj(pod), "node:", klog.KObj(node), "error:", utilerrors.NewAggregate(errors).Error())
-	}
-
-	return false
+	return podFitsNodes(nodeIndexer, pod, nodes, nil)
 }
 
 // PodFitsCurrentNode checks if the given pod will fit onto the given node. The predicates used
 // to determine if the pod will fit can be found in the NodeFit function.
 func PodFitsCurrentNode(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, node *v1.Node) bool {
-	errors := NodeFit(nodeIndexer, pod, node)
-	if len(errors) == 0 {
+	err := NodeFit(nodeIndexer, pod, node)
+	if err == nil {
 		klog.V(4).InfoS("Pod fits on node", "pod", klog.KObj(pod), "node", klog.KObj(node))
 		return true
 	}
 
 	klog.V(4).InfoS("Pod does not fit on current node",
-		"pod:", klog.KObj(pod), "node:", klog.KObj(node), "error:", utilerrors.NewAggregate(errors).Error())
+		"pod", klog.KObj(pod), "node", klog.KObj(node), "error", err)
 
 	return false
 }
@@ -200,9 +210,7 @@ func IsNodeUnschedulable(node *v1.Node) bool {
 
 // fitsRequest determines if a pod can fit on a node based on its resource requests. It returns true if
 // the pod will fit.
-func fitsRequest(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, node *v1.Node) (bool, []error) {
-	var insufficientResources []error
-
+func fitsRequest(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, node *v1.Node) (bool, error) {
 	// Get pod requests
 	podRequests, _ := utils.PodRequestsAndLimits(pod)
 	resourceNames := make([]v1.ResourceName, 0, len(podRequests))
@@ -212,25 +220,22 @@ func fitsRequest(nodeIndexer podutil.GetPodsAssignedToNodeFunc, pod *v1.Pod, nod
 
 	availableResources, err := nodeAvailableResources(nodeIndexer, node, resourceNames)
 	if err != nil {
-		return false, []error{err}
+		return false, err
 	}
 
-	podFitsOnNode := true
 	for _, resource := range resourceNames {
 		podResourceRequest := podRequests[resource]
 		availableResource, ok := availableResources[resource]
 		if !ok || podResourceRequest.MilliValue() > availableResource.MilliValue() {
-			insufficientResources = append(insufficientResources, fmt.Errorf("insufficient %v", resource))
-			podFitsOnNode = false
+			return false, fmt.Errorf("insufficient %v", resource)
 		}
 	}
 	// check pod num, at least one pod number is avaibalbe
 	if availableResources[v1.ResourcePods].MilliValue() <= 0 {
-		insufficientResources = append(insufficientResources, fmt.Errorf("insufficient %v", v1.ResourcePods))
-		podFitsOnNode = false
+		return false, fmt.Errorf("insufficient %v", v1.ResourcePods)
 	}
 
-	return podFitsOnNode, insufficientResources
+	return true, nil
 }
 
 // nodeAvailableResources returns resources mapped to the quanitity available on the node.
diff --git a/pkg/descheduler/pod/pods.go b/pkg/descheduler/pod/pods.go
index 1e7ef289b..e1aa8656b 100644
--- a/pkg/descheduler/pod/pods.go
+++ b/pkg/descheduler/pod/pods.go
@@ -99,6 +99,9 @@ func (o *Options) BuildFilterFunc() (FilterFunc, error) {
 		}
 	}
 	return func(pod *v1.Pod) bool {
+		if o.filter != nil && !o.filter(pod) {
+			return false
+		}
 		if len(o.includedNamespaces) > 0 && !o.includedNamespaces.Has(pod.Namespace) {
 			return false
 		}
@@ -108,9 +111,6 @@ func (o *Options) BuildFilterFunc() (FilterFunc, error) {
 		if s != nil && !s.Matches(labels.Set(pod.GetLabels())) {
 			return false
 		}
-		if o.filter != nil && !o.filter(pod) {
-			return false
-		}
 		return true
 	}, nil
 }


Test patch:
diff --git a/pkg/descheduler/descheduler_test.go b/pkg/descheduler/descheduler_test.go
index a72621be9..596def186 100644
--- a/pkg/descheduler/descheduler_test.go
+++ b/pkg/descheduler/descheduler_test.go
@@ -13,18 +13,15 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 	apiversion "k8s.io/apimachinery/pkg/version"
 	fakediscovery "k8s.io/client-go/discovery/fake"
-	"k8s.io/client-go/informers"
 	fakeclientset "k8s.io/client-go/kubernetes/fake"
 	core "k8s.io/client-go/testing"
 	"sigs.k8s.io/descheduler/cmd/descheduler/app/options"
 	"sigs.k8s.io/descheduler/pkg/api"
 	"sigs.k8s.io/descheduler/pkg/api/v1alpha1"
-	nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node"
 	"sigs.k8s.io/descheduler/pkg/framework/pluginregistry"
 	"sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor"
 	"sigs.k8s.io/descheduler/pkg/framework/plugins/removeduplicates"
 	"sigs.k8s.io/descheduler/pkg/framework/plugins/removepodsviolatingnodetaints"
-	"sigs.k8s.io/descheduler/pkg/utils"
 	deschedulerversion "sigs.k8s.io/descheduler/pkg/version"
 	"sigs.k8s.io/descheduler/test"
 )
@@ -98,7 +95,7 @@ func TestTaintsUpdated(t *testing.T) {
 	}
 
 	var evictedPods []string
-	client.PrependReactor("create", "pods", podEvictionReactionTestingFnc(&evictedPods))
+	client.PrependReactor("create", "pods", podEvictionReactionFuc(&evictedPods))
 
 	internalDeschedulerPolicy := &api.DeschedulerPolicy{}
 	scope := scope{}
@@ -164,7 +161,7 @@ func TestDuplicate(t *testing.T) {
 	}
 
 	var evictedPods []string
-	client.PrependReactor("create", "pods", podEvictionReactionTestingFnc(&evictedPods))
+	client.PrependReactor("create", "pods", podEvictionReactionFuc(&evictedPods))
 
 	internalDeschedulerPolicy := &api.DeschedulerPolicy{}
 	scope := scope{}
@@ -177,7 +174,7 @@ func TestDuplicate(t *testing.T) {
 	}
 
 	if len(evictedPods) == 0 {
-		t.Fatalf("Unable to evict pods\n")
+		t.Fatalf("Unable to evict pod, node taint did not get propagated to descheduler strategies %v\n", err)
 	}
 }
 
@@ -312,7 +309,7 @@ func TestValidateVersionCompatibility(t *testing.T) {
 	}
 }
 
-func podEvictionReactionTestingFnc(evictedPods *[]string) func(action core.Action) (bool, runtime.Object, error) {
+func podEvictionReactionFuc(evictedPods *[]string) func(action core.Action) (bool, runtime.Object, error) {
 	return func(action core.Action) (bool, runtime.Object, error) {
 		if action.GetSubresource() == "eviction" {
 			createAct, matched := action.(core.CreateActionImpl)
@@ -326,121 +323,3 @@ func podEvictionReactionTestingFnc(evictedPods *[]string) func(action core.Actio
 		return false, nil, nil // fallback to the default reactor
 	}
 }
-
-func initPluginRegistry() {
-	pluginregistry.PluginRegistry = pluginregistry.NewRegistry()
-	pluginregistry.Register(removeduplicates.PluginName, removeduplicates.New, &removeduplicates.RemoveDuplicates{}, &removeduplicates.RemoveDuplicatesArgs{}, removeduplicates.ValidateRemoveDuplicatesArgs, removeduplicates.SetDefaults_RemoveDuplicatesArgs, pluginregistry.PluginRegistry)
-	pluginregistry.Register(defaultevictor.PluginName, defaultevictor.New, &defaultevictor.DefaultEvictor{}, &defaultevictor.DefaultEvictorArgs{}, defaultevictor.ValidateDefaultEvictorArgs, defaultevictor.SetDefaults_DefaultEvictorArgs, pluginregistry.PluginRegistry)
-}
-
-func TestPodEvictorReset(t *testing.T) {
-	initPluginRegistry()
-
-	ctx := context.Background()
-	node1 := test.BuildTestNode("n1", 2000, 3000, 10, nil)
-	node2 := test.BuildTestNode("n2", 2000, 3000, 10, nil)
-
-	p1 := test.BuildTestPod("p1", 100, 0, node1.Name, nil)
-	p1.Namespace = "dev"
-	p2 := test.BuildTestPod("p2", 100, 0, node1.Name, nil)
-	p2.Namespace = "dev"
-	p3 := test.BuildTestPod("p3", 100, 0, node1.Name, nil)
-	p3.Namespace = "dev"
-	p4 := test.BuildTestPod("p4", 100, 0, node1.Name, nil)
-	p4.Namespace = "dev"
-
-	ownerRef1 := test.GetReplicaSetOwnerRefList()
-	p1.ObjectMeta.OwnerReferences = ownerRef1
-	p2.ObjectMeta.OwnerReferences = ownerRef1
-	p3.ObjectMeta.OwnerReferences = ownerRef1
-	p4.ObjectMeta.OwnerReferences = ownerRef1
-
-	client := fakeclientset.NewSimpleClientset(node1, node2, p1, p2, p3, p4)
-	eventClient := fakeclientset.NewSimpleClientset(node1, node2, p1, p2, p3, p4)
-	dp := &v1alpha1.DeschedulerPolicy{
-		Strategies: v1alpha1.StrategyList{
-			"RemoveDuplicates": v1alpha1.DeschedulerStrategy{
-				Enabled: true,
-			},
-		},
-	}
-
-	internalDeschedulerPolicy := &api.DeschedulerPolicy{}
-	scope := scope{}
-	err := v1alpha1.V1alpha1ToInternal(dp, pluginregistry.PluginRegistry, internalDeschedulerPolicy, scope)
-	if err != nil {
-		t.Fatalf("Unable to convert v1alpha1 to v1alpha2: %v", err)
-	}
-
-	rs, err := options.NewDeschedulerServer()
-	if err != nil {
-		t.Fatalf("Unable to initialize server: %v", err)
-	}
-	rs.Client = client
-	rs.EventClient = eventClient
-
-	var evictedPods []string
-	client.PrependReactor("create", "pods", podEvictionReactionTestingFnc(&evictedPods))
-
-	sharedInformerFactory := informers.NewSharedInformerFactoryWithOptions(rs.Client, 0, informers.WithTransform(trimManagedFields))
-	eventBroadcaster, eventRecorder := utils.GetRecorderAndBroadcaster(ctx, client)
-	defer eventBroadcaster.Shutdown()
-
-	descheduler, err := newDescheduler(rs, internalDeschedulerPolicy, "v1", eventRecorder, sharedInformerFactory)
-	if err != nil {
-		t.Fatalf("Unable to create a descheduler instance: %v", err)
-	}
-	var fakeEvictedPods []string
-	descheduler.podEvictionReactionFnc = func(*fakeclientset.Clientset) func(action core.Action) (bool, runtime.Object, error) {
-		return podEvictionReactionTestingFnc(&fakeEvictedPods)
-	}
-
-	ctx, cancel := context.WithCancel(ctx)
-	defer cancel()
-
-	sharedInformerFactory.Start(ctx.Done())
-	sharedInformerFactory.WaitForCacheSync(ctx.Done())
-
-	nodes, err := nodeutil.ReadyNodes(ctx, rs.Client, descheduler.nodeLister, "")
-	if err != nil {
-		t.Fatalf("Unable to get ready nodes: %v", err)
-	}
-
-	// a single pod eviction expected
-	err = descheduler.runDeschedulerLoop(ctx, nodes)
-	if err != nil {
-		t.Fatalf("Unable to run a descheduling loop: %v", err)
-	}
-	if descheduler.podEvictor.TotalEvicted() != 2 || len(evictedPods) != 2 || len(fakeEvictedPods) != 0 {
-		t.Fatalf("Expected (2,2,0) pods evicted, got (%v, %v, %v) instead", descheduler.podEvictor.TotalEvicted(), len(evictedPods), len(fakeEvictedPods))
-	}
-
-	// a single pod eviction expected
-	err = descheduler.runDeschedulerLoop(ctx, nodes)
-	if err != nil {
-		t.Fatalf("Unable to run a descheduling loop: %v", err)
-	}
-	if descheduler.podEvictor.TotalEvicted() != 2 || len(evictedPods) != 4 || len(fakeEvictedPods) != 0 {
-		t.Fatalf("Expected (2,4,0) pods evicted, got (%v, %v, %v) instead", descheduler.podEvictor.TotalEvicted(), len(evictedPods), len(fakeEvictedPods))
-	}
-
-	// check the fake client syncing and the right pods evicted
-	rs.DryRun = true
-	evictedPods = []string{}
-	// a single pod eviction expected
-	err = descheduler.runDeschedulerLoop(ctx, nodes)
-	if err != nil {
-		t.Fatalf("Unable to run a descheduling loop: %v", err)
-	}
-	if descheduler.podEvictor.TotalEvicted() != 2 || len(evictedPods) != 0 || len(fakeEvictedPods) != 2 {
-		t.Fatalf("Expected (2,0,2) pods evicted, got (%v, %v, %v) instead", descheduler.podEvictor.TotalEvicted(), len(evictedPods), len(fakeEvictedPods))
-	}
-	// a single pod eviction expected
-	err = descheduler.runDeschedulerLoop(ctx, nodes)
-	if err != nil {
-		t.Fatalf("Unable to run a descheduling loop: %v", err)
-	}
-	if descheduler.podEvictor.TotalEvicted() != 2 || len(evictedPods) != 0 || len(fakeEvictedPods) != 4 {
-		t.Fatalf("Expected (2,0,4) pods evicted, got (%v, %v, %v) instead", descheduler.podEvictor.TotalEvicted(), len(evictedPods), len(fakeEvictedPods))
-	}
-}
diff --git a/pkg/descheduler/evictions/evictions_test.go b/pkg/descheduler/evictions/evictions_test.go
index 3f2dbe9ed..9a931c781 100644
--- a/pkg/descheduler/evictions/evictions_test.go
+++ b/pkg/descheduler/evictions/evictions_test.go
@@ -22,12 +22,9 @@ import (
 
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes/fake"
 	core "k8s.io/client-go/testing"
-	"k8s.io/client-go/tools/events"
-	utilpointer "k8s.io/utils/pointer"
 	podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
 	"sigs.k8s.io/descheduler/pkg/utils"
 	"sigs.k8s.io/descheduler/test"
@@ -116,52 +113,3 @@ func TestPodTypes(t *testing.T) {
 		t.Errorf("Expected p1 to be a normal pod.")
 	}
 }
-
-func TestNewPodEvictor(t *testing.T) {
-	pod1 := test.BuildTestPod("pod", 400, 0, "node", nil)
-
-	fakeClient := fake.NewSimpleClientset(pod1)
-
-	eventRecorder := &events.FakeRecorder{}
-
-	podEvictor := NewPodEvictor(
-		fakeClient,
-		"policy/v1",
-		false,
-		utilpointer.Uint(1),
-		nil,
-		false,
-		eventRecorder,
-	)
-
-	stubNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node"}}
-
-	// 0 evictions expected
-	if evictions := podEvictor.NodeEvicted(stubNode); evictions != 0 {
-		t.Errorf("Expected 0 node evictions, got %q instead", evictions)
-	}
-	// 0 evictions expected
-	if evictions := podEvictor.TotalEvicted(); evictions != 0 {
-		t.Errorf("Expected 0 total evictions, got %q instead", evictions)
-	}
-
-	if podEvictor.NodeLimitExceeded(stubNode) {
-		t.Errorf("Expected NodeLimitExceeded to return false, got true instead")
-	}
-
-	if !podEvictor.EvictPod(context.TODO(), pod1, EvictOptions{}) {
-		t.Errorf("Expected a pod eviction, got no eviction instead")
-	}
-
-	// 1 node eviction expected
-	if evictions := podEvictor.NodeEvicted(stubNode); evictions != 1 {
-		t.Errorf("Expected 1 node eviction, got %q instead", evictions)
-	}
-	// 1 total eviction expected
-	if evictions := podEvictor.TotalEvicted(); evictions != 1 {
-		t.Errorf("Expected 1 total evictions, got %q instead", evictions)
-	}
-	if !podEvictor.NodeLimitExceeded(stubNode) {
-		t.Errorf("Expected NodeLimitExceeded to return true, got false instead")
-	}
-}
diff --git a/pkg/descheduler/node/node_test.go b/pkg/descheduler/node/node_test.go
index 383e18bc5..006156383 100644
--- a/pkg/descheduler/node/node_test.go
+++ b/pkg/descheduler/node/node_test.go
@@ -19,6 +19,7 @@ package node
 import (
 	"context"
 	"errors"
+	"sync"
 	"testing"
 
 	v1 "k8s.io/api/core/v1"
@@ -230,7 +231,7 @@ func TestPodFitsAnyOtherNode(t *testing.T) {
 	nodeTaintValue := "gpu"
 
 	// Staging node has no scheduling restrictions, but the pod always starts here and PodFitsAnyOtherNode() doesn't take into account the node the pod is running on.
-	nodeNames := []string{"node1", "node2", "stagingNode"}
+	nodeNames := []string{"node1", "node2", "stagingNode", "node4"}
 
 	tests := []struct {
 		description string
@@ -716,6 +717,151 @@ func TestPodFitsAnyOtherNode(t *testing.T) {
 			},
 			success: false,
 		},
+		{
+			description: "There are four nodes. One node has a taint, and the other three nodes do not meet the resource requirements, should fail",
+			pod: test.BuildTestPod("p1", 1000, 2*1000*1000*1000, nodeNames[2], func(pod *v1.Pod) {
+				pod.Spec.NodeSelector = map[string]string{
+					nodeLabelKey: nodeLabelValue,
+				}
+				pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+			}),
+			nodes: []*v1.Node{
+				test.BuildTestNode(nodeNames[0], 64000, 128*1000*1000*1000, 200, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(1000*1000*1000*1000, resource.DecimalSI)
+					node.Spec.Taints = []v1.Taint{
+						{
+							Key:    nodeTaintKey,
+							Value:  nodeTaintValue,
+							Effect: v1.TaintEffectNoSchedule,
+						},
+					}
+				}),
+				test.BuildTestNode(nodeNames[1], 3000, 8*1000*1000*1000, 12, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(200*1000*1000*1000, resource.DecimalSI)
+				}),
+				test.BuildTestNode(nodeNames[2], 3000, 8*1000*1000*1000, 12, nil),
+				test.BuildTestNode(nodeNames[3], 0, 0, 0, nil),
+			},
+			podsOnNodes: []*v1.Pod{
+				test.BuildTestPod("3-core-pod", 2000, 4*1000*1000*1000, nodeNames[1], func(pod *v1.Pod) {
+					pod.ObjectMeta = metav1.ObjectMeta{
+						Namespace: "test",
+						Labels: map[string]string{
+							"test": "true",
+						},
+					}
+					pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+					pod.Spec.Overhead = createResourceList(1000, 1000*1000*1000, 1000*1000*1000)
+				}),
+			},
+			success: false,
+		},
+		{
+			description: "There are four nodes. First node has a taint, second node has no label, third node do not meet the resource requirements, just fourth node meets the requirements, should success",
+			pod: test.BuildTestPod("p1", 1000, 2*1000*1000*1000, nodeNames[2], func(pod *v1.Pod) {
+				pod.Spec.NodeSelector = map[string]string{
+					nodeLabelKey: nodeLabelValue,
+				}
+				pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+			}),
+			nodes: []*v1.Node{
+				test.BuildTestNode(nodeNames[0], 64000, 128*1000*1000*1000, 200, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(1000*1000*1000*1000, resource.DecimalSI)
+					node.Spec.Taints = []v1.Taint{
+						{
+							Key:    nodeTaintKey,
+							Value:  nodeTaintValue,
+							Effect: v1.TaintEffectNoSchedule,
+						},
+					}
+				}),
+				test.BuildTestNode(nodeNames[1], 8000, 8*1000*1000*1000, 12, func(node *v1.Node) {
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(200*1000*1000*1000, resource.DecimalSI)
+				}),
+				test.BuildTestNode(nodeNames[2], 1000, 8*1000*1000*1000, 12, nil),
+				test.BuildTestNode(nodeNames[3], 8000, 8*1000*1000*1000, 12, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(1000*1000*1000*1000, resource.DecimalSI)
+				}),
+			},
+			podsOnNodes: []*v1.Pod{
+				test.BuildTestPod("3-core-pod", 2000, 4*1000*1000*1000, nodeNames[1], func(pod *v1.Pod) {
+					pod.ObjectMeta = metav1.ObjectMeta{
+						Namespace: "test",
+						Labels: map[string]string{
+							"test": "true",
+						},
+					}
+					pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+					pod.Spec.Overhead = createResourceList(1000, 1000*1000*1000, 1000*1000*1000)
+				}),
+			},
+			success: true,
+		},
+		{
+			description: "There are four nodes. First node has a taint, second node has no label, third node do not meet the resource requirements, fourth node is the one where the pod is located, should fail",
+			pod: test.BuildTestPod("p1", 1000, 2*1000*1000*1000, nodeNames[3], func(pod *v1.Pod) {
+				pod.Spec.NodeSelector = map[string]string{
+					nodeLabelKey: nodeLabelValue,
+				}
+				pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+			}),
+			nodes: []*v1.Node{
+				test.BuildTestNode(nodeNames[0], 64000, 128*1000*1000*1000, 200, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(1000*1000*1000*1000, resource.DecimalSI)
+					node.Spec.Taints = []v1.Taint{
+						{
+							Key:    nodeTaintKey,
+							Value:  nodeTaintValue,
+							Effect: v1.TaintEffectNoSchedule,
+						},
+					}
+				}),
+				test.BuildTestNode(nodeNames[1], 8000, 8*1000*1000*1000, 12, func(node *v1.Node) {
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(200*1000*1000*1000, resource.DecimalSI)
+				}),
+				test.BuildTestNode(nodeNames[2], 1000, 8*1000*1000*1000, 12, nil),
+				test.BuildTestNode(nodeNames[3], 8000, 8*1000*1000*1000, 12, func(node *v1.Node) {
+					node.ObjectMeta.Labels = map[string]string{
+						nodeLabelKey: nodeLabelValue,
+					}
+
+					node.Status.Allocatable[v1.ResourceEphemeralStorage] = *resource.NewQuantity(1000*1000*1000*1000, resource.DecimalSI)
+				}),
+			},
+			podsOnNodes: []*v1.Pod{
+				test.BuildTestPod("3-core-pod", 2000, 4*1000*1000*1000, nodeNames[1], func(pod *v1.Pod) {
+					pod.ObjectMeta = metav1.ObjectMeta{
+						Namespace: "test",
+						Labels: map[string]string{
+							"test": "true",
+						},
+					}
+					pod.Spec.Containers[0].Resources.Requests[v1.ResourceEphemeralStorage] = *resource.NewQuantity(10*1000*1000*1000, resource.DecimalSI)
+					pod.Spec.Overhead = createResourceList(1000, 1000*1000*1000, 1000*1000*1000)
+				}),
+			},
+			success: false,
+		},
 	}
 
 	for _, tc := range tests {
@@ -753,6 +899,51 @@ func TestPodFitsAnyOtherNode(t *testing.T) {
 	}
 }
 
+func TestPodFitsNodes(t *testing.T) {
+	nodeNames := []string{"node1", "node2", "node3", "node4"}
+	pod := test.BuildTestPod("p1", 950, 2*1000*1000*1000, nodeNames[0], nil)
+	nodes := []*v1.Node{
+		test.BuildTestNode(nodeNames[0], 1000, 8*1000*1000*1000, 12, nil),
+		test.BuildTestNode(nodeNames[1], 200, 8*1000*1000*1000, 12, nil),
+		test.BuildTestNode(nodeNames[2], 300, 8*1000*1000*1000, 12, nil),
+		test.BuildTestNode(nodeNames[3], 400, 8*1000*1000*1000, 12, nil),
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	var objs []runtime.Object
+	for _, node := range nodes {
+		objs = append(objs, node)
+	}
+	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())
+
+	var nodesTraversed sync.Map
+	podFitsNodes(getPodsAssignedToNode, pod, nodes, func(pod *v1.Pod, node *v1.Node) bool {
+		nodesTraversed.Store(node.Name, node)
+		return true
+	})
+
+	for _, node := range nodes {
+		if _, exists := nodesTraversed.Load(node.Name); !exists {
+			t.Errorf("Node %v was not proccesed", node.Name)
+		}
+	}
+}
+
 func TestNodeFit(t *testing.T) {
 	node := test.BuildTestNode("node", 64000, 128*1000*1000*1000, 2, func(node *v1.Node) {
 		node.ObjectMeta.Labels = map[string]string{
@@ -852,9 +1043,9 @@ func TestNodeFit(t *testing.T) {
 
 			sharedInformerFactory.Start(ctx.Done())
 			sharedInformerFactory.WaitForCacheSync(ctx.Done())
-			errs := NodeFit(getPodsAssignedToNode, tc.pod, tc.node)
-			if (len(errs) == 0 && tc.err != nil) || (len(errs) > 0 && errs[0].Error() != tc.err.Error()) {
-				t.Errorf("Test %#v failed, got %v, expect %v", tc.description, errs, tc.err)
+			err = NodeFit(getPodsAssignedToNode, tc.pod, tc.node)
+			if (err == nil && tc.err != nil) || (err != nil && err.Error() != tc.err.Error()) {
+				t.Errorf("Test %#v failed, got %v, expect %v", tc.description, err, tc.err)
 			}
 		})
 	}
diff --git a/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go b/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go
index d5fcdd651..bfe55594e 100644
--- a/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go
+++ b/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go
@@ -115,7 +115,6 @@ func TestHighNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p2", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -168,7 +167,6 @@ func TestHighNodeUtilization(t *testing.T) {
 				// These won't be evicted.
 				test.BuildTestPod("p2", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -492,6 +490,7 @@ func TestHighNodeUtilization(t *testing.T) {
 				false,
 				nil,
 				nil,
+				testCase.nodes,
 				false,
 				eventRecorder,
 			)
@@ -643,6 +642,7 @@ func TestHighNodeUtilizationWithTaints(t *testing.T) {
 				false,
 				&item.evictionsExpected,
 				nil,
+				item.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go b/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go
index a05bd7259..63ade79f0 100644
--- a/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go
+++ b/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go
@@ -175,23 +175,18 @@ func TestLowNodeUtilization(t *testing.T) {
 			},
 			pods: []*v1.Pod{
 				test.BuildTestPod("p1", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "namespace1"
 				}),
 				test.BuildTestPod("p2", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "namespace1"
 				}),
 				test.BuildTestPod("p3", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "namespace1"
 				}),
 				test.BuildTestPod("p4", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "namespace1"
 				}),
 				test.BuildTestPod("p5", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "namespace1"
 				}),
 				// These won't be evicted.
@@ -247,15 +242,12 @@ func TestLowNodeUtilization(t *testing.T) {
 				test.BuildTestPod("p1", 400, 0, n1NodeName, test.SetRSOwnerRef),
 				test.BuildTestPod("p2", 400, 0, n1NodeName, test.SetRSOwnerRef),
 				test.BuildTestPod("p3", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					// TODO(zhifei92): add ownerRef for pod
 					pod.Namespace = "namespace3"
 				}),
 				test.BuildTestPod("p4", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					// TODO(zhifei92): add ownerRef for pod
 					pod.Namespace = "namespace4"
 				}),
 				test.BuildTestPod("p5", 400, 0, n1NodeName, func(pod *v1.Pod) {
-					// TODO(zhifei92): add ownerRef for pod
 					pod.Namespace = "namespace5"
 				}),
 				// These won't be evicted.
@@ -279,7 +271,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -335,7 +326,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 300, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -406,7 +396,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -474,7 +463,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -550,7 +538,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 0, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					test.SetPodExtendedResourceRequest(pod, extendedResource, 1)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
@@ -633,7 +620,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -698,7 +684,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -791,7 +776,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -843,7 +827,6 @@ func TestLowNodeUtilization(t *testing.T) {
 				}),
 				test.BuildTestPod("p8", 400, 0, n1NodeName, func(pod *v1.Pod) {
 					// A Critical Pod.
-					test.SetNormalOwnerRef(pod)
 					pod.Namespace = "kube-system"
 					priority := utils.SystemCriticalPriority
 					pod.Spec.Priority = &priority
@@ -909,6 +892,7 @@ func TestLowNodeUtilization(t *testing.T) {
 				false,
 				nil,
 				nil,
+				test.nodes,
 				false,
 				eventRecorder,
 			)
@@ -1080,6 +1064,7 @@ func TestLowNodeUtilizationWithTaints(t *testing.T) {
 				false,
 				&item.evictionsExpected,
 				nil,
+				item.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/podlifetime/pod_lifetime_test.go b/pkg/framework/plugins/podlifetime/pod_lifetime_test.go
index 195e94fa1..16536cbc3 100644
--- a/pkg/framework/plugins/podlifetime/pod_lifetime_test.go
+++ b/pkg/framework/plugins/podlifetime/pod_lifetime_test.go
@@ -562,6 +562,7 @@ func TestPodLifeTime(t *testing.T) {
 				false,
 				tc.maxPodsToEvictPerNode,
 				tc.maxPodsToEvictPerNamespace,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removeduplicates/removeduplicates_test.go b/pkg/framework/plugins/removeduplicates/removeduplicates_test.go
index f0f3ecc79..6a14ba878 100644
--- a/pkg/framework/plugins/removeduplicates/removeduplicates_test.go
+++ b/pkg/framework/plugins/removeduplicates/removeduplicates_test.go
@@ -319,6 +319,7 @@ func TestFindDuplicatePods(t *testing.T) {
 				false,
 				nil,
 				nil,
+				testCase.nodes,
 				false,
 				eventRecorder,
 			)
@@ -767,6 +768,7 @@ func TestRemoveDuplicatesUniformly(t *testing.T) {
 				false,
 				nil,
 				nil,
+				testCase.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removefailedpods/failedpods_test.go b/pkg/framework/plugins/removefailedpods/failedpods_test.go
index 8acaf77af..d052fc670 100644
--- a/pkg/framework/plugins/removefailedpods/failedpods_test.go
+++ b/pkg/framework/plugins/removefailedpods/failedpods_test.go
@@ -381,6 +381,7 @@ func TestRemoveFailedPods(t *testing.T) {
 				false,
 				nil,
 				nil,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go
index c4aa6c5dd..bf50e89ae 100644
--- a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go
+++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go
@@ -312,8 +312,8 @@ func TestRemovePodsHavingTooManyRestarts(t *testing.T) {
 		t.Run(tc.description, func(t *testing.T) {
 			pods := append(
 				initPods(node1),
-				test.BuildTestPod("CPU-consumer-1", 150, 100, node4.Name, test.SetNormalOwnerRef),
-				test.BuildTestPod("CPU-consumer-2", 150, 100, node5.Name, test.SetNormalOwnerRef),
+				test.BuildTestPod("CPU-consumer-1", 150, 100, node4.Name, nil),
+				test.BuildTestPod("CPU-consumer-2", 150, 100, node5.Name, nil),
 			)
 			if tc.applyFunc != nil {
 				tc.applyFunc(pods)
@@ -350,6 +350,7 @@ func TestRemovePodsHavingTooManyRestarts(t *testing.T) {
 				false,
 				tc.maxPodsToEvictPerNode,
 				tc.maxNoOfPodsToEvictPerNamespace,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go
index 23990ab4c..648298844 100644
--- a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go
+++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go
@@ -239,6 +239,7 @@ func TestPodAntiAffinity(t *testing.T) {
 				false,
 				test.maxPodsToEvictPerNode,
 				test.maxNoOfPodsToEvictPerNamespace,
+				test.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go
index 32f287888..9cf0cdd2a 100644
--- a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go
+++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go
@@ -365,6 +365,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) {
 				false,
 				tc.maxPodsToEvictPerNode,
 				tc.maxNoOfPodsToEvictPerNamespace,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go
index 4a2c25526..1cbfc3e54 100644
--- a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go
+++ b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go
@@ -110,12 +110,6 @@ func TestDeletePodsViolatingNodeTaints(t *testing.T) {
 	p4 := test.BuildTestPod("p4", 100, 0, node1.Name, nil)
 	p5 := test.BuildTestPod("p5", 100, 0, node1.Name, nil)
 	p6 := test.BuildTestPod("p6", 100, 0, node1.Name, nil)
-	p7 := test.BuildTestPod("p7", 100, 0, node2.Name, nil)
-	p8 := test.BuildTestPod("p8", 100, 0, node2.Name, nil)
-	p9 := test.BuildTestPod("p9", 100, 0, node2.Name, nil)
-	p10 := test.BuildTestPod("p10", 100, 0, node2.Name, nil)
-	p11 := test.BuildTestPod("p11", 100, 0, node2.Name, nil)
-	p12 := test.BuildTestPod("p11", 100, 0, node2.Name, nil)
 
 	p1.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
 	p2.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
@@ -123,11 +117,13 @@ func TestDeletePodsViolatingNodeTaints(t *testing.T) {
 	p4.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
 	p5.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
 	p6.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
-	p7.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
-	p8.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
-	p9.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
-	p10.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
+	p7 := test.BuildTestPod("p7", 100, 0, node2.Name, nil)
+	p8 := test.BuildTestPod("p8", 100, 0, node2.Name, nil)
+	p9 := test.BuildTestPod("p9", 100, 0, node2.Name, nil)
+	p10 := test.BuildTestPod("p10", 100, 0, node2.Name, nil)
+	p11 := test.BuildTestPod("p11", 100, 0, node2.Name, nil)
 	p11.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
+	p12 := test.BuildTestPod("p11", 100, 0, node2.Name, nil)
 	p12.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
 
 	// The following 4 pods won't get evicted.
@@ -410,6 +406,7 @@ func TestDeletePodsViolatingNodeTaints(t *testing.T) {
 				false,
 				tc.maxPodsToEvictPerNode,
 				tc.maxNoOfPodsToEvictPerNamespace,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go
index 7a75ef91c..d7ab72682 100644
--- a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go
+++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go
@@ -1462,6 +1462,7 @@ func TestTopologySpreadConstraint(t *testing.T) {
 				false,
 				nil,
 				nil,
+				tc.nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/pkg/framework/profile/profile_test.go b/pkg/framework/profile/profile_test.go
index 8309739ab..8823c57a6 100644
--- a/pkg/framework/profile/profile_test.go
+++ b/pkg/framework/profile/profile_test.go
@@ -244,7 +244,7 @@ func TestProfileDescheduleBalanceExtensionPointsEviction(t *testing.T) {
 			eventBroadcaster, eventRecorder := utils.GetRecorderAndBroadcaster(ctx, eventClient)
 			defer eventBroadcaster.Shutdown()
 
-			podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, true, eventRecorder)
+			podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, nodes, true, eventRecorder)
 
 			prfl, err := NewProfile(
 				test.config,
@@ -306,6 +306,7 @@ func TestProfileExtensionPoints(t *testing.T) {
 
 	n1 := testutils.BuildTestNode("n1", 2000, 3000, 10, nil)
 	n2 := testutils.BuildTestNode("n2", 2000, 3000, 10, nil)
+	nodes := []*v1.Node{n1, n2}
 
 	p1 := testutils.BuildTestPod(fmt.Sprintf("pod_1_%s", n1.Name), 200, 0, n1.Name, nil)
 	p1.ObjectMeta.OwnerReferences = []metav1.OwnerReference{{}}
@@ -392,7 +393,7 @@ func TestProfileExtensionPoints(t *testing.T) {
 	eventBroadcaster, eventRecorder := utils.GetRecorderAndBroadcaster(ctx, eventClient)
 	defer eventBroadcaster.Shutdown()
 
-	podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, true, eventRecorder)
+	podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, nodes, true, eventRecorder)
 
 	prfl, err := NewProfile(
 		api.DeschedulerProfile{
@@ -604,7 +605,7 @@ func TestProfileExtensionPointOrdering(t *testing.T) {
 	eventBroadcaster, eventRecorder := utils.GetRecorderAndBroadcaster(ctx, eventClient)
 	defer eventBroadcaster.Shutdown()
 
-	podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, true, eventRecorder)
+	podEvictor := evictions.NewPodEvictor(client, "policy/v1", false, nil, nil, nodes, true, eventRecorder)
 
 	prfl, err := NewProfile(
 		api.DeschedulerProfile{
diff --git a/test/e2e/e2e_duplicatepods_test.go b/test/e2e/e2e_duplicatepods_test.go
index 01b4b5778..86a79d18a 100644
--- a/test/e2e/e2e_duplicatepods_test.go
+++ b/test/e2e/e2e_duplicatepods_test.go
@@ -48,7 +48,7 @@ func TestRemoveDuplicates(t *testing.T) {
 		t.Errorf("Error listing node with %v", err)
 	}
 
-	_, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
+	nodes, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
 
 	t.Log("Creating testing namespace")
 	testNamespace := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "e2e-" + strings.ToLower(t.Name())}}
@@ -177,6 +177,7 @@ func TestRemoveDuplicates(t *testing.T) {
 				false,
 				nil,
 				nil,
+				nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/test/e2e/e2e_failedpods_test.go b/test/e2e/e2e_failedpods_test.go
index ca5ad5dd1..b94c1f373 100644
--- a/test/e2e/e2e_failedpods_test.go
+++ b/test/e2e/e2e_failedpods_test.go
@@ -75,7 +75,7 @@ func TestFailedPods(t *testing.T) {
 			defer jobClient.Delete(ctx, job.Name, metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy})
 			waitForJobPodPhase(ctx, t, clientSet, job, v1.PodFailed)
 
-			podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode)
+			podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode, nodes)
 
 			defaultevictorArgs := &defaultevictor.DefaultEvictorArgs{
 				EvictLocalStoragePods:   true,
diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go
index 2527d4e15..8b92212a1 100644
--- a/test/e2e/e2e_test.go
+++ b/test/e2e/e2e_test.go
@@ -196,6 +196,7 @@ func runPodLifetimePlugin(
 		false,
 		nil,
 		maxPodsToEvictPerNamespace,
+		nodes,
 		false,
 		&events.FakeRecorder{},
 	)
@@ -284,7 +285,7 @@ func TestLowNodeUtilization(t *testing.T) {
 		t.Errorf("Error listing node with %v", err)
 	}
 
-	_, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
+	nodes, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
 
 	t.Log("Creating testing namespace")
 	testNamespace := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "e2e-" + strings.ToLower(t.Name())}}
@@ -373,7 +374,7 @@ func TestLowNodeUtilization(t *testing.T) {
 	waitForRCPodsRunning(ctx, t, clientSet, rc)
 
 	// Run LowNodeUtilization plugin
-	podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode)
+	podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode, nodes)
 
 	defaultevictorArgs := &defaultevictor.DefaultEvictorArgs{
 		EvictLocalStoragePods:   true,
@@ -1569,7 +1570,7 @@ func splitNodesAndWorkerNodes(nodes []v1.Node) ([]*v1.Node, []*v1.Node) {
 	return allNodes, workerNodes
 }
 
-func initPodEvictorOrFail(t *testing.T, clientSet clientset.Interface, getPodsAssignedToNode podutil.GetPodsAssignedToNodeFunc) *evictions.PodEvictor {
+func initPodEvictorOrFail(t *testing.T, clientSet clientset.Interface, getPodsAssignedToNode podutil.GetPodsAssignedToNodeFunc, nodes []*v1.Node) *evictions.PodEvictor {
 	evictionPolicyGroupVersion, err := eutils.SupportEviction(clientSet)
 	if err != nil || len(evictionPolicyGroupVersion) == 0 {
 		t.Fatalf("Error creating eviction policy group: %v", err)
@@ -1583,6 +1584,7 @@ func initPodEvictorOrFail(t *testing.T, clientSet clientset.Interface, getPodsAs
 		false,
 		nil,
 		nil,
+		nodes,
 		false,
 		eventRecorder,
 	)
diff --git a/test/e2e/e2e_toomanyrestarts_test.go b/test/e2e/e2e_toomanyrestarts_test.go
index 63c986ee9..c74e7a8d5 100644
--- a/test/e2e/e2e_toomanyrestarts_test.go
+++ b/test/e2e/e2e_toomanyrestarts_test.go
@@ -50,7 +50,7 @@ func TestTooManyRestarts(t *testing.T) {
 		t.Errorf("Error listing node with %v", err)
 	}
 
-	_, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
+	nodes, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
 
 	t.Logf("Creating testing namespace %v", t.Name())
 	testNamespace := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "e2e-" + strings.ToLower(t.Name())}}
@@ -167,6 +167,7 @@ func TestTooManyRestarts(t *testing.T) {
 				false,
 				nil,
 				nil,
+				nodes,
 				false,
 				eventRecorder,
 			)
diff --git a/test/e2e/e2e_topologyspreadconstraint_test.go b/test/e2e/e2e_topologyspreadconstraint_test.go
index 6ecc347ae..7c0fbb366 100644
--- a/test/e2e/e2e_topologyspreadconstraint_test.go
+++ b/test/e2e/e2e_topologyspreadconstraint_test.go
@@ -27,7 +27,7 @@ func TestTopologySpreadConstraint(t *testing.T) {
 	if err != nil {
 		t.Errorf("Error listing node with %v", err)
 	}
-	_, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
+	nodes, workerNodes := splitNodesAndWorkerNodes(nodeList.Items)
 	t.Log("Creating testing namespace")
 	testNamespace := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "e2e-" + strings.ToLower(t.Name())}}
 	if _, err := clientSet.CoreV1().Namespaces().Create(ctx, testNamespace, metav1.CreateOptions{}); err != nil {
@@ -138,7 +138,7 @@ func TestTopologySpreadConstraint(t *testing.T) {
 			defer test.DeleteDeployment(ctx, t, clientSet, violatorDeployment)
 			test.WaitForDeploymentPodsRunning(ctx, t, clientSet, violatorDeployment)
 
-			podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode)
+			podEvictor := initPodEvictorOrFail(t, clientSet, getPodsAssignedToNode, nodes)
 
 			// Run TopologySpreadConstraint strategy
 			t.Logf("Running RemovePodsViolatingTopologySpreadConstraint strategy for %s", name)