Repository: kubernetes-sigs/kueue

Issue:
MultiKueue: eliminate the dispatcherName from the workload webhook
<!-- Please only use this template for submitting clean up requests -->

**What would you like to be cleaned**:

Delete this hack `dispatcherName != configapi.MultiKueueDispatcherModeAllAtOnce` from : https://github.com/kubernetes-sigs/kueue/blob/af0d80ee04210a537fc25dda7a8e23445dbb2763/pkg/webhooks/workload_webhook.go#L375C64-L385

It was added in the interim step of the development when the "AllAtOnce" dispatcher was not setting NominatedClusterNames. Later we decided to set NominatedClusterNames also for this dispatcher: https://github.com/kubernetes-sigs/kueue/blob/af0d80ee04210a537fc25dda7a8e23445dbb2763/pkg/controller/admissionchecks/multikueue/workload.go#L484

**Why is this needed**:

To cleanup the code from unnecessary complexity.

Hints from issue comments:
@mimowo: cc @mszadkow @mszadkow: /assign

Solution patch:
diff --git a/cmd/kueue/main.go b/cmd/kueue/main.go
index d6f702cb7..1f8cff3dd 100644
--- a/cmd/kueue/main.go
+++ b/cmd/kueue/main.go
@@ -389,7 +389,7 @@ func setupControllers(ctx context.Context, mgr ctrl.Manager, cCache *schdcache.C
 		}
 	}
 
-	if failedWebhook, err := webhooks.Setup(mgr, ptr.Deref(cfg.MultiKueue.DispatcherName, configapi.MultiKueueDispatcherModeAllAtOnce)); err != nil {
+	if failedWebhook, err := webhooks.Setup(mgr); err != nil {
 		return fmt.Errorf("unable to create webhook %s: %w", failedWebhook, err)
 	}
 
diff --git a/go.mod b/go.mod
index 41a31750d..9b13cd842 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module sigs.k8s.io/kueue
 go 1.25.0
 
 require (
-	github.com/cert-manager/cert-manager v1.19.0
+	github.com/cert-manager/cert-manager v1.19.1
 	github.com/fsnotify/fsnotify v1.9.0
 	github.com/go-logr/logr v1.4.3
 	github.com/google/go-cmp v0.7.0
diff --git a/go.sum b/go.sum
index a75b9686a..a3bd6af76 100644
--- a/go.sum
+++ b/go.sum
@@ -18,8 +18,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
 github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
 github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
 github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
-github.com/cert-manager/cert-manager v1.19.0 h1:pPKtK06zPriliwq7B12xa7DGKbc4SvnfOjE0BXaAspk=
-github.com/cert-manager/cert-manager v1.19.0/go.mod h1:7VBp/ihp5Xgz/ZJuGy1fNIWbDouQKBonvEyWoJLuhmA=
+github.com/cert-manager/cert-manager v1.19.1 h1:Txh8L/nLWTDcb7ZnXuXbTe15BxQnLbLirXmbNk0fGgY=
+github.com/cert-manager/cert-manager v1.19.1/go.mod h1:8Ps1VXCQRGKT8zNvLQlhDK1gFKWmYKdIPQFmvTS2JeA=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
diff --git a/pkg/webhooks/webhooks.go b/pkg/webhooks/webhooks.go
index c367d94d0..cb7c832bb 100644
--- a/pkg/webhooks/webhooks.go
+++ b/pkg/webhooks/webhooks.go
@@ -22,8 +22,8 @@ import (
 
 // Setup sets up the webhooks for core controllers. It returns the name of the
 // webhook that failed to create and an error, if any.
-func Setup(mgr ctrl.Manager, dispatcherName string) (string, error) {
-	if err := setupWebhookForWorkload(mgr, dispatcherName); err != nil {
+func Setup(mgr ctrl.Manager) (string, error) {
+	if err := setupWebhookForWorkload(mgr); err != nil {
 		return "Workload", err
 	}
 
diff --git a/pkg/webhooks/workload_webhook.go b/pkg/webhooks/workload_webhook.go
index af6184ca1..7bacc236d 100644
--- a/pkg/webhooks/workload_webhook.go
+++ b/pkg/webhooks/workload_webhook.go
@@ -33,22 +33,18 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
-	configapi "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/resources"
 	utilslices "sigs.k8s.io/kueue/pkg/util/slices"
 	"sigs.k8s.io/kueue/pkg/workload"
+	"sigs.k8s.io/kueue/pkg/workloadslicing"
 )
 
-type WorkloadWebhook struct {
-	dispatcherName string
-}
+type WorkloadWebhook struct{}
 
-func setupWebhookForWorkload(mgr ctrl.Manager, dispatcherName string) error {
-	wh := &WorkloadWebhook{
-		dispatcherName: dispatcherName,
-	}
+func setupWebhookForWorkload(mgr ctrl.Manager) error {
+	wh := &WorkloadWebhook{}
 	return ctrl.NewWebhookManagedBy(mgr).
 		For(&kueue.Workload{}).
 		WithDefaulter(wh).
@@ -94,7 +90,7 @@ func (w *WorkloadWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj run
 	oldWL := oldObj.(*kueue.Workload)
 	log := ctrl.LoggerFrom(ctx).WithName("workload-webhook")
 	log.V(5).Info("Validating update")
-	return nil, ValidateWorkloadUpdate(newWL, oldWL, w.dispatcherName).ToAggregate()
+	return nil, ValidateWorkloadUpdate(newWL, oldWL).ToAggregate()
 }
 
 // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type
@@ -273,7 +269,7 @@ func validateReclaimablePods(obj *kueue.Workload, basePath *field.Path) field.Er
 	return ret
 }
 
-func ValidateWorkloadUpdate(newObj, oldObj *kueue.Workload, dispatcherName string) field.ErrorList {
+func ValidateWorkloadUpdate(newObj, oldObj *kueue.Workload) field.ErrorList {
 	var allErrs field.ErrorList
 	specPath := field.NewPath("spec")
 	statusPath := field.NewPath("status")
@@ -287,7 +283,7 @@ func ValidateWorkloadUpdate(newObj, oldObj *kueue.Workload, dispatcherName strin
 	}
 	allErrs = append(allErrs, validateAdmissionUpdate(newObj.Status.Admission, oldObj.Status.Admission, field.NewPath("status", "admission"))...)
 	allErrs = append(allErrs, validateImmutablePodSetUpdates(newObj, oldObj, statusPath.Child("admissionChecks"))...)
-	allErrs = append(allErrs, validateClusterNameUpdate(newObj, oldObj, dispatcherName, statusPath)...)
+	allErrs = append(allErrs, validateClusterNameUpdate(newObj, oldObj, statusPath)...)
 	return allErrs
 }
 
@@ -372,9 +368,13 @@ func validateImmutablePodSets(new, old []kueue.PodSet, path *field.Path) field.E
 	return allErrs
 }
 
-func validateClusterNameUpdate(newObj, oldObj *kueue.Workload, dispatcherName string, statusPath *field.Path) field.ErrorList {
+func validateClusterNameUpdate(newObj, oldObj *kueue.Workload, statusPath *field.Path) field.ErrorList {
 	var allErrs field.ErrorList
-	if oldObj.Status.ClusterName == nil && newObj.Status.ClusterName != nil && dispatcherName != configapi.MultiKueueDispatcherModeAllAtOnce {
+	if features.Enabled(features.ElasticJobsViaWorkloadSlices) && workloadslicing.ReplacementForKey(newObj) != nil {
+		// Allow setting clusterName when the workload is a valid elastic job replacement.
+		return allErrs
+	}
+	if oldObj.Status.ClusterName == nil && newObj.Status.ClusterName != nil {
 		found := slices.Contains(oldObj.Status.NominatedClusterNames, *newObj.Status.ClusterName)
 		if !found {
 			allErrs = append(allErrs, field.Invalid(statusPath.Child("clusterName"), newObj.Status.ClusterName, "when setting clusterName it must be one of the nominatedClusterNames"))
diff --git a/vendor/github.com/cert-manager/cert-manager/pkg/apis/meta/v1/types.go b/vendor/github.com/cert-manager/cert-manager/pkg/apis/meta/v1/types.go
index 6c36d40ec..2b294e1a9 100644
--- a/vendor/github.com/cert-manager/cert-manager/pkg/apis/meta/v1/types.go
+++ b/vendor/github.com/cert-manager/cert-manager/pkg/apis/meta/v1/types.go
@@ -53,12 +53,12 @@ type IssuerReference struct {
 	// Name of the issuer being referred to.
 	Name string `json:"name"`
 	// Kind of the issuer being referred to.
+	// Defaults to 'Issuer'.
 	// +optional
-	// +default="Issuer"
 	Kind string `json:"kind,omitempty"`
 	// Group of the issuer being referred to.
+	// Defaults to 'cert-manager.io'.
 	// +optional
-	// +default="cert-manager.io"
 	Group string `json:"group,omitempty"`
 }
 
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6b58d8331..3b555377a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -26,7 +26,7 @@ github.com/blang/semver/v4
 # github.com/cenkalti/backoff/v5 v5.0.3
 ## explicit; go 1.23
 github.com/cenkalti/backoff/v5
-# github.com/cert-manager/cert-manager v1.19.0
+# github.com/cert-manager/cert-manager v1.19.1
 ## explicit; go 1.25.0
 github.com/cert-manager/cert-manager/pkg/apis/acme
 github.com/cert-manager/cert-manager/pkg/apis/acme/v1



Test patch:
diff --git a/pkg/webhooks/workload_webhook_test.go b/pkg/webhooks/workload_webhook_test.go
index 5dbc0d6be..3db54b92d 100644
--- a/pkg/webhooks/workload_webhook_test.go
+++ b/pkg/webhooks/workload_webhook_test.go
@@ -27,11 +27,12 @@ import (
 	"k8s.io/apimachinery/pkg/util/validation/field"
 	"k8s.io/utils/ptr"
 
-	configapi "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/features"
 	utiltesting "sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
+	"sigs.k8s.io/kueue/pkg/workload"
+	"sigs.k8s.io/kueue/pkg/workloadslicing"
 )
 
 const (
@@ -532,12 +533,30 @@ func TestValidateWorkloadUpdate(t *testing.T) {
 					kueue.PodSetAssignment{Name: "ps1"},
 					kueue.PodSetAssignment{Name: "ps2"}).Obj()).Obj(),
 		},
+		"ClusterName doesn't have to be one of the nominatedClusterNames with ElasticJobs feature gate": {
+			enableElasticJobsFeature: true,
+			before: utiltestingapi.MakeWorkload(testWorkloadName, testWorkloadNamespace).
+				PodSets(
+					*utiltestingapi.MakePodSet("ps1", 3).Obj(),
+					*utiltestingapi.MakePodSet("ps2", 4).Obj()).
+				Obj(),
+			after: utiltestingapi.MakeWorkload(testWorkloadName, testWorkloadNamespace).
+				PodSets(
+					*utiltestingapi.MakePodSet("ps1", 3).Obj(),
+					*utiltestingapi.MakePodSet("ps2", 4).Obj()).
+				ReserveQuota(utiltestingapi.MakeAdmission("cluster-queue").PodSets(
+					kueue.PodSetAssignment{Name: "ps1"},
+					kueue.PodSetAssignment{Name: "ps2"}).Obj()).
+				ClusterName("worker1").
+				Annotation(workloadslicing.WorkloadSliceReplacementFor, string(workload.NewReference(testWorkloadNamespace, testWorkloadName))).
+				Obj(),
+		},
 	}
 	for name, tc := range testCases {
 		t.Run(name, func(t *testing.T) {
 			features.SetFeatureGateDuringTest(t, features.ElasticJobsViaWorkloadSlices, tc.enableElasticJobsFeature)
 			features.SetFeatureGateDuringTest(t, features.TopologyAwareScheduling, tc.enableTopologyAwareScheduling)
-			errList := ValidateWorkloadUpdate(tc.after, tc.before, configapi.MultiKueueDispatcherModeAllAtOnce)
+			errList := ValidateWorkloadUpdate(tc.after, tc.before)
 			if diff := cmp.Diff(tc.wantErr, errList, cmpopts.IgnoreFields(field.Error{}, "Detail", "BadValue")); diff != "" {
 				t.Errorf("ValidateWorkloadUpdate() mismatch (-want +got):\n%s", diff)
 			}
diff --git a/test/integration/multikueue/external_job_test.go b/test/integration/multikueue/external_job_test.go
index cbf44e97a..ac2cd90d7 100644
--- a/test/integration/multikueue/external_job_test.go
+++ b/test/integration/multikueue/external_job_test.go
@@ -27,7 +27,6 @@ import (
 	apimeta "k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
@@ -89,7 +88,7 @@ var _ = ginkgo.Describe("MultiKueue", ginkgo.Ordered, ginkgo.ContinueOnFailure,
 				failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 				gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-				failedWebhook, err := webhooks.Setup(mgr, ptr.Deref(configuration.MultiKueue.DispatcherName, config.MultiKueueDispatcherModeAllAtOnce))
+				failedWebhook, err := webhooks.Setup(mgr)
 				gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 				// Set up RayJob webhook (but not MultiKueue integration)
diff --git a/test/integration/multikueue/suite_test.go b/test/integration/multikueue/suite_test.go
index 4ba15ee54..9a8428d0b 100644
--- a/test/integration/multikueue/suite_test.go
+++ b/test/integration/multikueue/suite_test.go
@@ -30,7 +30,6 @@ import (
 	versionutil "k8s.io/apimachinery/pkg/util/version"
 	"k8s.io/client-go/discovery"
 	"k8s.io/client-go/rest"
-	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
@@ -131,7 +130,7 @@ func managerSetup(ctx context.Context, mgr manager.Manager) {
 	failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-	failedWebhook, err := webhooks.Setup(mgr, ptr.Deref(configuration.MultiKueue.DispatcherName, config.MultiKueueDispatcherModeAllAtOnce))
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	err = workloadjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
diff --git a/test/integration/singlecluster/controller/admissionchecks/provisioning/suite_test.go b/test/integration/singlecluster/controller/admissionchecks/provisioning/suite_test.go
index 73d7a31fd..ed341020b 100644
--- a/test/integration/singlecluster/controller/admissionchecks/provisioning/suite_test.go
+++ b/test/integration/singlecluster/controller/admissionchecks/provisioning/suite_test.go
@@ -98,7 +98,7 @@ func managerSetup(options ...managerSetupOption) framework.ManagerSetup {
 		err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		controllersCfg := &config.Configuration{}
diff --git a/test/integration/singlecluster/controller/core/suite_test.go b/test/integration/singlecluster/controller/core/suite_test.go
index aac947156..3c5cb9f8c 100644
--- a/test/integration/singlecluster/controller/core/suite_test.go
+++ b/test/integration/singlecluster/controller/core/suite_test.go
@@ -72,7 +72,7 @@ func managerAndControllerSetup(controllersCfg *config.Configuration) framework.M
 		err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		if controllersCfg == nil {
diff --git a/test/integration/singlecluster/controller/dra/suite_test.go b/test/integration/singlecluster/controller/dra/suite_test.go
index bc4c5595f..d337546dc 100644
--- a/test/integration/singlecluster/controller/dra/suite_test.go
+++ b/test/integration/singlecluster/controller/dra/suite_test.go
@@ -87,7 +87,7 @@ func managerSetup(modifyConfig func(*config.Configuration)) framework.ManagerSet
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
 		// Webhooks
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		mappings := []config.DeviceClassMapping{
diff --git a/test/integration/singlecluster/controller/jobs/pod/suite_test.go b/test/integration/singlecluster/controller/jobs/pod/suite_test.go
index ace45eb72..fff6ceba3 100644
--- a/test/integration/singlecluster/controller/jobs/pod/suite_test.go
+++ b/test/integration/singlecluster/controller/jobs/pod/suite_test.go
@@ -132,7 +132,7 @@ func managerSetup(
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 		err = pod.SetupWebhook(mgr, opts...)
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		if setupTASControllers {
diff --git a/test/integration/singlecluster/importer/suite_test.go b/test/integration/singlecluster/importer/suite_test.go
index aca05ef4e..547c58592 100644
--- a/test/integration/singlecluster/importer/suite_test.go
+++ b/test/integration/singlecluster/importer/suite_test.go
@@ -77,7 +77,7 @@ func managerAndSchedulerSetup(ctx context.Context, mgr manager.Manager) {
 	failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-	failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	err = workloadjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
diff --git a/test/integration/singlecluster/kueuectl/suite_test.go b/test/integration/singlecluster/kueuectl/suite_test.go
index 2f3186042..c2c13274d 100644
--- a/test/integration/singlecluster/kueuectl/suite_test.go
+++ b/test/integration/singlecluster/kueuectl/suite_test.go
@@ -79,7 +79,7 @@ func managerSetup(ctx context.Context, mgr manager.Manager) {
 	err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-	failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	controllersCfg := &config.Configuration{}
diff --git a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
index 2ccb030ba..4cef95808 100644
--- a/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
+++ b/test/integration/singlecluster/scheduler/fairsharing/fair_sharing_test.go
@@ -918,12 +918,13 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			ginkgo.By("Validate pending workloads")
 			util.ExpectPendingWorkloadsMetric(cq1, 1, 1)
 
-			gomega.Eventually(func(g gomega.Gomega) {
-				util.FinishEvictionsOfAnyWorkloadsInCq(ctx, k8sClient, cq2)
-				util.ExpectAdmittedWorkloadsTotalMetric(cq1, "", 1)
-				util.ExpectClusterQueueWeightedShareMetric(cq1, 0)
-				util.ExpectClusterQueueWeightedShareMetric(cq2, 0)
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			ginkgo.By("Complete preemption")
+			util.FinishEvictionOfWorkloadsInCQ(ctx, k8sClient, cq2, 2)
+
+			ginkgo.By("Expected Total Admitted Workloads and Weighted Share")
+			util.ExpectAdmittedWorkloadsTotalMetric(cq1, "", 1)
+			util.ExpectClusterQueueWeightedShareMetric(cq1, 0)
+			util.ExpectClusterQueueWeightedShareMetric(cq2, 0)
 		})
 
 		ginkgo.It("sticky workload deleted, next workload can admit", func() {
@@ -950,13 +951,13 @@ var _ = ginkgo.Describe("Scheduler", ginkgo.Ordered, ginkgo.ContinueOnFailure, f
 			ginkgo.By("Validate pending workloads")
 			util.ExpectPendingWorkloadsMetric(cq1, 1, 1)
 
+			ginkgo.By("Complete preemption")
+			util.FinishEvictionOfWorkloadsInCQ(ctx, k8sClient, cq2, 2)
+
 			ginkgo.By("Expected Total Admitted Workloads and Weighted Share")
-			gomega.Eventually(func(g gomega.Gomega) {
-				util.FinishEvictionsOfAnyWorkloadsInCq(ctx, k8sClient, cq2)
-				util.ExpectAdmittedWorkloadsTotalMetric(cq1, "", 1)
-				util.ExpectClusterQueueWeightedShareMetric(cq1, 0)
-				util.ExpectClusterQueueWeightedShareMetric(cq2, 0)
-			}, util.Timeout, util.Interval).Should(gomega.Succeed())
+			util.ExpectAdmittedWorkloadsTotalMetric(cq1, "", 1)
+			util.ExpectClusterQueueWeightedShareMetric(cq1, 0)
+			util.ExpectClusterQueueWeightedShareMetric(cq2, 0)
 		})
 	})
 
diff --git a/test/integration/singlecluster/scheduler/fairsharing/suite_test.go b/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
index d4375951b..e1730bc1d 100644
--- a/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
+++ b/test/integration/singlecluster/scheduler/fairsharing/suite_test.go
@@ -87,7 +87,7 @@ func managerAndSchedulerSetup(admissionFairSharing *config.AdmissionFairSharing)
 		failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		err = workloadjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
diff --git a/test/integration/singlecluster/scheduler/podsready/suite_test.go b/test/integration/singlecluster/scheduler/podsready/suite_test.go
index 7abfe8705..c0a868e82 100644
--- a/test/integration/singlecluster/scheduler/podsready/suite_test.go
+++ b/test/integration/singlecluster/scheduler/podsready/suite_test.go
@@ -101,7 +101,7 @@ func managerAndSchedulerSetup(configuration *config.Configuration) framework.Man
 		failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-		failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+		failedWebhook, err := webhooks.Setup(mgr)
 		gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 		err = workloadjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
diff --git a/test/integration/singlecluster/scheduler/suite_test.go b/test/integration/singlecluster/scheduler/suite_test.go
index 04857d527..3ad1ea3d3 100644
--- a/test/integration/singlecluster/scheduler/suite_test.go
+++ b/test/integration/singlecluster/scheduler/suite_test.go
@@ -90,7 +90,7 @@ func managerAndSchedulerSetup(ctx context.Context, mgr manager.Manager) {
 	failedCtrl, err := core.SetupControllers(mgr, queues, cCache, configuration)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "controller", failedCtrl)
 
-	failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	err = workloadjob.SetupIndexes(ctx, mgr.GetFieldIndexer())
diff --git a/test/integration/singlecluster/tas/suite_test.go b/test/integration/singlecluster/tas/suite_test.go
index 1f4357749..be1d3c55e 100644
--- a/test/integration/singlecluster/tas/suite_test.go
+++ b/test/integration/singlecluster/tas/suite_test.go
@@ -75,7 +75,7 @@ func managerSetup(ctx context.Context, mgr manager.Manager) {
 	err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-	failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	controllersCfg := &config.Configuration{}
diff --git a/test/integration/singlecluster/webhook/core/admissioncheck_test.go b/test/integration/singlecluster/webhook/core/admissioncheck_test.go
index 6b3158249..239ebb08e 100644
--- a/test/integration/singlecluster/webhook/core/admissioncheck_test.go
+++ b/test/integration/singlecluster/webhook/core/admissioncheck_test.go
@@ -28,7 +28,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
@@ -38,7 +37,7 @@ import (
 var _ = ginkgo.Describe("AdmissionCheck Webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
diff --git a/test/integration/singlecluster/webhook/core/clusterqueue_test.go b/test/integration/singlecluster/webhook/core/clusterqueue_test.go
index 08e3740c6..92102439d 100644
--- a/test/integration/singlecluster/webhook/core/clusterqueue_test.go
+++ b/test/integration/singlecluster/webhook/core/clusterqueue_test.go
@@ -31,7 +31,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
@@ -46,7 +45,7 @@ const (
 var _ = ginkgo.Describe("ClusterQueue Webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
diff --git a/test/integration/singlecluster/webhook/core/cohort_test.go b/test/integration/singlecluster/webhook/core/cohort_test.go
index 114eb8141..fe6c7d0fb 100644
--- a/test/integration/singlecluster/webhook/core/cohort_test.go
+++ b/test/integration/singlecluster/webhook/core/cohort_test.go
@@ -29,7 +29,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
@@ -39,7 +38,7 @@ import (
 var _ = ginkgo.Describe("Cohort Webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
diff --git a/test/integration/singlecluster/webhook/core/localqueue_test.go b/test/integration/singlecluster/webhook/core/localqueue_test.go
index 01da0f5ff..d9718e5e2 100644
--- a/test/integration/singlecluster/webhook/core/localqueue_test.go
+++ b/test/integration/singlecluster/webhook/core/localqueue_test.go
@@ -24,7 +24,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
@@ -36,7 +35,7 @@ const queueName = "queue-test"
 var _ = ginkgo.Describe("Queue validating webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
diff --git a/test/integration/singlecluster/webhook/core/resourceflavor_test.go b/test/integration/singlecluster/webhook/core/resourceflavor_test.go
index ac9d45d8d..a0423cd45 100644
--- a/test/integration/singlecluster/webhook/core/resourceflavor_test.go
+++ b/test/integration/singlecluster/webhook/core/resourceflavor_test.go
@@ -28,7 +28,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
@@ -43,7 +42,7 @@ const (
 var _ = ginkgo.Describe("ResourceFlavor Webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
diff --git a/test/integration/singlecluster/webhook/core/suite_test.go b/test/integration/singlecluster/webhook/core/suite_test.go
index 4952223c7..66b66d61b 100644
--- a/test/integration/singlecluster/webhook/core/suite_test.go
+++ b/test/integration/singlecluster/webhook/core/suite_test.go
@@ -62,11 +62,11 @@ var _ = ginkgo.AfterSuite(func() {
 	fwk.Teardown()
 })
 
-func managerSetup(ctx context.Context, mgr manager.Manager, dispatcherName string) {
+func managerSetup(ctx context.Context, mgr manager.Manager) {
 	err := indexer.Setup(ctx, mgr.GetFieldIndexer())
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-	failedWebhook, err := webhooks.Setup(mgr, dispatcherName)
+	failedWebhook, err := webhooks.Setup(mgr)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 	cCache := schdcache.New(mgr.GetClient())
diff --git a/test/integration/singlecluster/webhook/core/workload_test.go b/test/integration/singlecluster/webhook/core/workload_test.go
index 9befc27ab..dabd56fa0 100644
--- a/test/integration/singlecluster/webhook/core/workload_test.go
+++ b/test/integration/singlecluster/webhook/core/workload_test.go
@@ -32,13 +32,13 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/constants"
 	"sigs.k8s.io/kueue/pkg/features"
 	"sigs.k8s.io/kueue/pkg/util/testing"
 	utiltestingapi "sigs.k8s.io/kueue/pkg/util/testing/v1beta1"
 	"sigs.k8s.io/kueue/pkg/workload"
+	"sigs.k8s.io/kueue/pkg/workloadslicing"
 	"sigs.k8s.io/kueue/test/util"
 )
 
@@ -60,7 +60,7 @@ var _ = ginkgo.AfterEach(func() {
 var _ = ginkgo.Describe("Workload defaulting webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
@@ -116,7 +116,7 @@ var _ = ginkgo.Describe("Workload defaulting webhook", ginkgo.Ordered, func() {
 var _ = ginkgo.Describe("Workload validating webhook", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
@@ -1062,7 +1062,7 @@ var _ = ginkgo.Describe("Workload validating webhook", ginkgo.Ordered, func() {
 var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher AllAtOnce", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeAllAtOnce)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
@@ -1117,7 +1117,7 @@ var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher Al
 				gomega.Succeed(),
 				gomega.Succeed(),
 			),
-			ginkgo.Entry("Valid: ClusterName is not in NominatedClusters",
+			ginkgo.Entry("Invalid: ClusterName is not in NominatedClusters",
 				func(wl *kueue.Workload) {
 					wl.Status.NominatedClusterNames = []string{"worker1", "worker2"}
 					wl.Status.ClusterName = nil
@@ -1127,21 +1127,9 @@ var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher Al
 					wl.Status.ClusterName = ptr.To("worker3")
 				},
 				gomega.Succeed(),
-				gomega.Succeed(),
-			),
-			ginkgo.Entry("Invalid: ClusterName is changed",
-				func(wl *kueue.Workload) {
-					wl.Status.NominatedClusterNames = nil
-					wl.Status.ClusterName = ptr.To("worker1")
-				},
-				func(wl *kueue.Workload) {
-					wl.Status.NominatedClusterNames = nil
-					wl.Status.ClusterName = ptr.To("worker2")
-				},
-				gomega.Succeed(),
-				testing.BeInvalidError(),
+				testing.BeForbiddenError(),
 			),
-			ginkgo.Entry("Valid: ClusterName is set when NominatedClusters is empty",
+			ginkgo.Entry("Invalid: ClusterName is set when NominatedClusters is empty",
 				func(wl *kueue.Workload) {
 					wl.Status.NominatedClusterNames = nil
 					wl.Status.ClusterName = nil
@@ -1151,7 +1139,7 @@ var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher Al
 					wl.Status.NominatedClusterNames = nil
 				},
 				gomega.Succeed(),
-				gomega.Succeed(),
+				testing.BeForbiddenError(),
 			),
 			ginkgo.Entry("Invalid: ClusterName and NominatedClusters are mutually exclusive",
 				func(wl *kueue.Workload) {},
@@ -1178,7 +1166,7 @@ var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher Al
 var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher Incremental", ginkgo.Ordered, func() {
 	ginkgo.BeforeAll(func() {
 		fwk.StartManager(ctx, cfg, func(ctx context.Context, mgr manager.Manager) {
-			managerSetup(ctx, mgr, config.MultiKueueDispatcherModeIncremental)
+			managerSetup(ctx, mgr)
 		})
 	})
 	ginkgo.AfterAll(func() {
@@ -1288,5 +1276,30 @@ var _ = ginkgo.Describe("Workload validating webhook ClusterName - Dispatcher In
 				gomega.Succeed(),
 			),
 		)
+
+		ginkgo.It("Should allow to set ClusterName without previous nominatedClusterNames when ElasticJobsViaWorkloadSlices feature gate is enabled", func() {
+			features.SetFeatureGateDuringTest(ginkgo.GinkgoTB(), features.ElasticJobsViaWorkloadSlices, true)
+
+			ginkgo.By("Creating a new Workload")
+			wl := utiltestingapi.MakeWorkload(workloadName, ns.Name).
+				PodSets(*utiltestingapi.MakePodSet(kueue.DefaultPodSetName, 1).
+					Request(corev1.ResourceCPU, "1").
+					Obj()).
+				Annotation(workloadslicing.WorkloadSliceReplacementFor, string(workload.NewReference(ns.Name, workloadName))).
+				Obj()
+			util.MustCreate(ctx, k8sClient, wl)
+
+			ginkgo.By("mimic scheduler by setting the status.clusterName during quota reservation")
+			gomega.Eventually(func(g gomega.Gomega) {
+				g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(wl), wl)).To(gomega.Succeed())
+				wl.Status.Admission = utiltestingapi.MakeAdmission("default").
+					PodSets(utiltestingapi.MakePodSetAssignment(kueue.DefaultPodSetName).
+						Assignment(corev1.ResourceCPU, "default", "1").
+						Obj()).
+					Obj()
+				wl.Status.ClusterName = ptr.To("worker1")
+				g.Expect(k8sClient.Status().Update(ctx, wl)).Should(gomega.Succeed())
+			}, util.Timeout, util.Interval).Should(gomega.Succeed())
+		})
 	})
 })
diff --git a/test/integration/singlecluster/webhook/jobs/raycluster_webhook_test.go b/test/integration/singlecluster/webhook/jobs/raycluster_webhook_test.go
index 05d54b7c9..d6f1471da 100644
--- a/test/integration/singlecluster/webhook/jobs/raycluster_webhook_test.go
+++ b/test/integration/singlecluster/webhook/jobs/raycluster_webhook_test.go
@@ -26,7 +26,6 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	config "sigs.k8s.io/kueue/apis/config/v1beta1"
 	kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1"
 	"sigs.k8s.io/kueue/pkg/constants"
 	"sigs.k8s.io/kueue/pkg/controller/core/indexer"
@@ -101,7 +100,7 @@ var _ = ginkgo.Describe("RayCluster Webhook", func() {
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
 				jobframework.EnableIntegration(workloadrayjob.FrameworkName)
 
-				failedWebhook, err := webhooks.Setup(mgr, config.MultiKueueDispatcherModeAllAtOnce)
+				failedWebhook, err := webhooks.Setup(mgr)
 				gomega.Expect(err).ToNot(gomega.HaveOccurred(), "webhook", failedWebhook)
 
 				return nil