Repository: kedacore/keda

Issue:
Test Function Skipped: Name Does Not Start with 'Test'
### Report

The test function is not being executed because its name does not start with Test. In Go, test functions must follow the naming convention func TestXxx(t *testing.T) in order to be recognized and run by the go test command. Since this function doesn't follow that pattern, it gets skipped during test execution.

### Expected Behavior

While running the below test all test functions should be called.

`go test -v -tags e2e ./utils/setup_test.go`

### Actual Behavior

Since, below test funciton in setup_test.go is miss spelled, so it's not getting called.

```
func TesVerifyPodsIdentity(t *testing.T) {
	if AzureRunWorkloadIdentityTests == StringTrue {
		assert.True(t, WaitForDeploymentReplicaReadyCount(t, KubeClient, "azure-wi-webhook-controller-manager", "azure-workload-identity-system", 2, 30, 6),
			"replica count should be 1 after 3 minutes")
	}

	if AwsIdentityTests == StringTrue {
		assert.True(t, WaitForDeploymentReplicaReadyCount(t, KubeClient, "aws-identity-webhook", "aws-identity-system", 1, 30, 6),
			"replica count should be 1 after 3 minutes")
	}

	if GcpIdentityTests == StringTrue {
		assert.True(t, WaitForDeploymentReplicaReadyCount(t, KubeClient, "gcp-identity-webhook-controller-manager", "gcp-identity-system", 1, 30, 6),
			"replica count should be 1 after 3 minutes")
	}
}
```

### Steps to Reproduce the Problem

Run the command

`go test -v -tags e2e ./utils/setup_test.go`


### Logs from KEDA operator

```
example
```


### KEDA Version

None

### Kubernetes Version

None

### Platform

None

### Scaler Details

_No response_

### Anything else?

_No response_

Discussion:
@jxs1211: I'd like to help

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f55554488..4dcb13683 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -91,7 +91,7 @@ New deprecation(s):
 
 ### Other
 
-- TODO ([#XXX](https://github.com/kedacore/keda/issues/XXX))
+- **General**: Replace deprecated webhook.Validator with webhook.CustomValidator ([#6660](https://github.com/kedacore/keda/issues/6660))
 
 ## v2.17.1
 
diff --git a/apis/eventing/v1alpha1/cloudeventsource_webhook.go b/apis/eventing/v1alpha1/cloudeventsource_webhook.go
index 2dc577665..57a0506a7 100644
--- a/apis/eventing/v1alpha1/cloudeventsource_webhook.go
+++ b/apis/eventing/v1alpha1/cloudeventsource_webhook.go
@@ -17,6 +17,7 @@ limitations under the License.
 package v1alpha1
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"slices"
@@ -33,28 +34,61 @@ var cloudeventsourcelog = logf.Log.WithName("cloudeventsource-validation-webhook
 
 func (ces *CloudEventSource) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&CloudEventSourceCustomValidator{}).
 		For(ces).
 		Complete()
 }
 
 func (cces *ClusterCloudEventSource) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&ClusterCloudEventSourceCustomValidator{}).
 		For(cces).
 		Complete()
 }
 
 // +kubebuilder:webhook:path=/validate-eventing-keda-sh-v1alpha1-cloudeventsource,mutating=false,failurePolicy=ignore,sideEffects=None,groups=eventing.keda.sh,resources=cloudeventsources,verbs=create;update,versions=v1alpha1,name=vcloudeventsource.kb.io,admissionReviewVersions=v1
 
-var _ webhook.Validator = &CloudEventSource{}
+// CloudEventSourceCustomValidator is a custom validator for CloudEventSource objects
+type CloudEventSourceCustomValidator struct{}
+
+func (cescv CloudEventSourceCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ces := obj.(*CloudEventSource)
+	return ces.ValidateCreate(request.DryRun)
+}
+
+func (cescv CloudEventSourceCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ces := newObj.(*CloudEventSource)
+	old := oldObj.(*CloudEventSource)
+	return ces.ValidateUpdate(old, request.DryRun)
+}
+
+func (cescv CloudEventSourceCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ces := obj.(*CloudEventSource)
+	return ces.ValidateDelete(request.DryRun)
+}
+
+var _ webhook.CustomValidator = &CloudEventSourceCustomValidator{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (ces *CloudEventSource) ValidateCreate() (admission.Warnings, error) {
+func (ces *CloudEventSource) ValidateCreate(_ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(ces, "", "  ")
 	cloudeventsourcelog.Info(fmt.Sprintf("validating cloudeventsource creation for %s", string(val)))
 	return validateSpec(&ces.Spec)
 }
 
-func (ces *CloudEventSource) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
+func (ces *CloudEventSource) ValidateUpdate(old runtime.Object, _ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(ces, "", "  ")
 	cloudeventsourcelog.V(1).Info(fmt.Sprintf("validating cloudeventsource update for %s", string(val)))
 
@@ -66,22 +100,53 @@ func (ces *CloudEventSource) ValidateUpdate(old runtime.Object) (admission.Warni
 	return validateSpec(&ces.Spec)
 }
 
-func (ces *CloudEventSource) ValidateDelete() (admission.Warnings, error) {
+func (ces *CloudEventSource) ValidateDelete(_ *bool) (admission.Warnings, error) {
 	return nil, nil
 }
 
 // +kubebuilder:webhook:path=/validate-eventing-keda-sh-v1alpha1-clustercloudeventsource,mutating=false,failurePolicy=ignore,sideEffects=None,groups=eventing.keda.sh,resources=clustercloudeventsources,verbs=create;update,versions=v1alpha1,name=vclustercloudeventsource.kb.io,admissionReviewVersions=v1
 
-var _ webhook.Validator = &ClusterCloudEventSource{}
+// ClusterCloudEventSourceCustomValidator is a custom validator for ClusterCloudEventSource objects
+type ClusterCloudEventSourceCustomValidator struct{}
+
+func (ccescv ClusterCloudEventSourceCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cces := obj.(*ClusterCloudEventSource)
+	return cces.ValidateCreate(request.DryRun)
+}
+
+func (ccescv ClusterCloudEventSourceCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cces := newObj.(*ClusterCloudEventSource)
+	old := oldObj.(*ClusterCloudEventSource)
+	return cces.ValidateUpdate(old, request.DryRun)
+}
+
+func (ccescv ClusterCloudEventSourceCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cces := obj.(*ClusterCloudEventSource)
+	return cces.ValidateDelete(request.DryRun)
+}
+
+var _ webhook.CustomValidator = &ClusterCloudEventSourceCustomValidator{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (cces *ClusterCloudEventSource) ValidateCreate() (admission.Warnings, error) {
+func (cces *ClusterCloudEventSource) ValidateCreate(_ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(cces, "", "  ")
 	cloudeventsourcelog.Info(fmt.Sprintf("validating clustercloudeventsource creation for %s", string(val)))
 	return validateSpec(&cces.Spec)
 }
 
-func (cces *ClusterCloudEventSource) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
+func (cces *ClusterCloudEventSource) ValidateUpdate(old runtime.Object, _ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(cces, "", "  ")
 	cloudeventsourcelog.V(1).Info(fmt.Sprintf("validating clustercloudeventsource update for %s", string(val)))
 
@@ -93,7 +158,7 @@ func (cces *ClusterCloudEventSource) ValidateUpdate(old runtime.Object) (admissi
 	return validateSpec(&cces.Spec)
 }
 
-func (cces *ClusterCloudEventSource) ValidateDelete() (admission.Warnings, error) {
+func (cces *ClusterCloudEventSource) ValidateDelete(_ *bool) (admission.Warnings, error) {
 	return nil, nil
 }
 
diff --git a/apis/keda/v1alpha1/scaledjob_webhook.go b/apis/keda/v1alpha1/scaledjob_webhook.go
index 24eb03d3f..0aec11644 100644
--- a/apis/keda/v1alpha1/scaledjob_webhook.go
+++ b/apis/keda/v1alpha1/scaledjob_webhook.go
@@ -17,6 +17,7 @@ limitations under the License.
 package v1alpha1
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 
@@ -32,22 +33,54 @@ var scaledjoblog = logf.Log.WithName("scaledjob-validation-webhook")
 
 func (s *ScaledJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&ScaledJobCustomValidator{}).
 		For(s).
 		Complete()
 }
 
 // +kubebuilder:webhook:path=/validate-keda-sh-v1alpha1-scaledjob,mutating=false,failurePolicy=ignore,sideEffects=None,groups=keda.sh,resources=scaledjobs,verbs=create;update,versions=v1alpha1,name=vscaledjob.kb.io,admissionReviewVersions=v1
 
-var _ webhook.Validator = &ScaledJob{}
+// ScaledJobCustomValidator is a custom validator for ScaledJob objects
+type ScaledJobCustomValidator struct{}
+
+func (sjcv ScaledJobCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	sj := obj.(*ScaledJob)
+	return sj.ValidateCreate(request.DryRun)
+}
+
+func (sjcv ScaledJobCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	sj := newObj.(*ScaledJob)
+	old := oldObj.(*ScaledJob)
+	return sj.ValidateUpdate(old, request.DryRun)
+}
+
+func (sjcv ScaledJobCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	sj := obj.(*ScaledJob)
+	return sj.ValidateDelete(request.DryRun)
+}
+
+var _ webhook.CustomValidator = &ScaledJobCustomValidator{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (s *ScaledJob) ValidateCreate() (admission.Warnings, error) {
+func (s *ScaledJob) ValidateCreate(dryRun *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(s, "", "  ")
 	scaledjoblog.Info(fmt.Sprintf("validating scaledjob creation for %s", string(val)))
-	return nil, verifyTriggers(s, "create", false)
+	return nil, verifyTriggers(s, "create", *dryRun)
 }
 
-func (s *ScaledJob) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
+func (s *ScaledJob) ValidateUpdate(old runtime.Object, dryRun *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(s, "", "  ")
 	scaledobjectlog.V(1).Info(fmt.Sprintf("validating scaledjob update for %s", string(val)))
 
@@ -56,10 +89,10 @@ func (s *ScaledJob) ValidateUpdate(old runtime.Object) (admission.Warnings, erro
 		scaledjoblog.V(1).Info("finalizer removal, skipping validation")
 		return nil, nil
 	}
-	return nil, verifyTriggers(s, "update", false)
+	return nil, verifyTriggers(s, "update", *dryRun)
 }
 
-func (s *ScaledJob) ValidateDelete() (admission.Warnings, error) {
+func (s *ScaledJob) ValidateDelete(_ *bool) (admission.Warnings, error) {
 	return nil, nil
 }
 
diff --git a/apis/keda/v1alpha1/scaledobject_webhook.go b/apis/keda/v1alpha1/scaledobject_webhook.go
index 5c981bf37..c6dca1eee 100644
--- a/apis/keda/v1alpha1/scaledobject_webhook.go
+++ b/apis/keda/v1alpha1/scaledobject_webhook.go
@@ -53,6 +53,18 @@ var memoryString = "memory"
 var cpuString = "cpu"
 
 func (so *ScaledObject) SetupWebhookWithManager(mgr ctrl.Manager, cacheMissFallback bool) error {
+	err := setupKubernetesClients(mgr, cacheMissFallback)
+	if err != nil {
+		return fmt.Errorf("failed to setup kubernetes clients: %w", err)
+	}
+
+	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&ScaledObjectCustomValidator{}).
+		For(so).
+		Complete()
+}
+
+func setupKubernetesClients(mgr ctrl.Manager, cacheMissFallback bool) error {
 	kc = mgr.GetClient()
 	restMapper = mgr.GetRESTMapper()
 	cacheMissToDirectClient = cacheMissFallback
@@ -70,10 +82,8 @@ func (so *ScaledObject) SetupWebhookWithManager(mgr ctrl.Manager, cacheMissFallb
 			return fmt.Errorf("failed to initialize direct client: %w", err)
 		}
 	}
-	return ctrl.NewWebhookManagedBy(mgr).
-		WithValidator(&ScaledObjectCustomValidator{}).
-		For(so).
-		Complete()
+
+	return nil
 }
 
 // +kubebuilder:webhook:path=/validate-keda-sh-v1alpha1-scaledobject,mutating=false,failurePolicy=ignore,sideEffects=None,groups=keda.sh,resources=scaledobjects,verbs=create;update,versions=v1alpha1,name=vscaledobject.kb.io,admissionReviewVersions=v1
diff --git a/apis/keda/v1alpha1/triggerauthentication_webhook.go b/apis/keda/v1alpha1/triggerauthentication_webhook.go
index 83fb6348e..2e425ed78 100644
--- a/apis/keda/v1alpha1/triggerauthentication_webhook.go
+++ b/apis/keda/v1alpha1/triggerauthentication_webhook.go
@@ -17,6 +17,7 @@ limitations under the License.
 package v1alpha1
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 
@@ -37,28 +38,61 @@ var triggerauthenticationlog = logf.Log.WithName("triggerauthentication-validati
 
 func (ta *TriggerAuthentication) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&TriggerAuthenticationCustomValidator{}).
 		For(ta).
 		Complete()
 }
 
 func (cta *ClusterTriggerAuthentication) SetupWebhookWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewWebhookManagedBy(mgr).
+		WithValidator(&ClusterTriggerAuthenticationCustomValidator{}).
 		For(cta).
 		Complete()
 }
 
 // +kubebuilder:webhook:path=/validate-keda-sh-v1alpha1-triggerauthentication,mutating=false,failurePolicy=ignore,sideEffects=None,groups=keda.sh,resources=triggerauthentications,verbs=create;update,versions=v1alpha1,name=vstriggerauthentication.kb.io,admissionReviewVersions=v1
 
-var _ webhook.Validator = &TriggerAuthentication{}
+// TriggerAuthenticationCustomValidator is a custom validator for TriggerAuthentication objects
+type TriggerAuthenticationCustomValidator struct{}
+
+func (tacv TriggerAuthenticationCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ta := obj.(*TriggerAuthentication)
+	return ta.ValidateCreate(request.DryRun)
+}
+
+func (tacv TriggerAuthenticationCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ta := newObj.(*TriggerAuthentication)
+	old := oldObj.(*TriggerAuthentication)
+	return ta.ValidateUpdate(old, request.DryRun)
+}
+
+func (tacv TriggerAuthenticationCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ta := obj.(*TriggerAuthentication)
+	return ta.ValidateDelete(request.DryRun)
+}
+
+var _ webhook.CustomValidator = &TriggerAuthenticationCustomValidator{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (ta *TriggerAuthentication) ValidateCreate() (admission.Warnings, error) {
+func (ta *TriggerAuthentication) ValidateCreate(_ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(ta, "", "  ")
 	triggerauthenticationlog.Info(fmt.Sprintf("validating triggerauthentication creation for %s", string(val)))
 	return validateSpec(&ta.Spec)
 }
 
-func (ta *TriggerAuthentication) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
+func (ta *TriggerAuthentication) ValidateUpdate(old runtime.Object, _ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(ta, "", "  ")
 	scaledobjectlog.V(1).Info(fmt.Sprintf("validating triggerauthentication update for %s", string(val)))
 
@@ -70,22 +104,53 @@ func (ta *TriggerAuthentication) ValidateUpdate(old runtime.Object) (admission.W
 	return validateSpec(&ta.Spec)
 }
 
-func (ta *TriggerAuthentication) ValidateDelete() (admission.Warnings, error) {
+func (ta *TriggerAuthentication) ValidateDelete(_ *bool) (admission.Warnings, error) {
 	return nil, nil
 }
 
 // +kubebuilder:webhook:path=/validate-keda-sh-v1alpha1-clustertriggerauthentication,mutating=false,failurePolicy=ignore,sideEffects=None,groups=keda.sh,resources=clustertriggerauthentications,verbs=create;update,versions=v1alpha1,name=vsclustertriggerauthentication.kb.io,admissionReviewVersions=v1
 
-var _ webhook.Validator = &ClusterTriggerAuthentication{}
+// ClusterTriggerAuthenticationCustomValidator is a custom validator for ClusterTriggerAuthentication objects
+type ClusterTriggerAuthenticationCustomValidator struct{}
+
+func (ctacv ClusterTriggerAuthenticationCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cta := obj.(*ClusterTriggerAuthentication)
+	return cta.ValidateCreate(request.DryRun)
+}
+
+func (ctacv ClusterTriggerAuthenticationCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cta := newObj.(*ClusterTriggerAuthentication)
+	old := oldObj.(*ClusterTriggerAuthentication)
+	return cta.ValidateUpdate(old, request.DryRun)
+}
+
+func (ctacv ClusterTriggerAuthenticationCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
+	request, err := admission.RequestFromContext(ctx)
+	if err != nil {
+		return nil, err
+	}
+	cta := obj.(*ClusterTriggerAuthentication)
+	return cta.ValidateDelete(request.DryRun)
+}
+
+var _ webhook.CustomValidator = &ClusterTriggerAuthenticationCustomValidator{}
 
 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (cta *ClusterTriggerAuthentication) ValidateCreate() (admission.Warnings, error) {
+func (cta *ClusterTriggerAuthentication) ValidateCreate(_ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(cta, "", "  ")
 	triggerauthenticationlog.Info(fmt.Sprintf("validating clustertriggerauthentication creation for %s", string(val)))
 	return validateSpec(&cta.Spec)
 }
 
-func (cta *ClusterTriggerAuthentication) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
+func (cta *ClusterTriggerAuthentication) ValidateUpdate(old runtime.Object, _ *bool) (admission.Warnings, error) {
 	val, _ := json.MarshalIndent(cta, "", "  ")
 	scaledobjectlog.V(1).Info(fmt.Sprintf("validating clustertriggerauthentication update for %s", string(val)))
 
@@ -98,7 +163,7 @@ func (cta *ClusterTriggerAuthentication) ValidateUpdate(old runtime.Object) (adm
 	return validateSpec(&cta.Spec)
 }
 
-func (cta *ClusterTriggerAuthentication) ValidateDelete() (admission.Warnings, error) {
+func (cta *ClusterTriggerAuthentication) ValidateDelete(_ *bool) (admission.Warnings, error) {
 	return nil, nil
 }
 



Test patch:
diff --git a/tests/utils/setup_test.go b/tests/utils/setup_test.go
index 4ff97c9a4..7d6c0d5f6 100644
--- a/tests/utils/setup_test.go
+++ b/tests/utils/setup_test.go
@@ -151,7 +151,7 @@ func TestSetupGcpIdentityComponents(t *testing.T) {
 	require.NoErrorf(t, err, "cannot install workload identity webhook - %s", err)
 }
 
-func TesVerifyPodsIdentity(t *testing.T) {
+func TestVerifyPodsIdentity(t *testing.T) {
 	if AzureRunWorkloadIdentityTests == StringTrue {
 		assert.True(t, WaitForDeploymentReplicaReadyCount(t, KubeClient, "azure-wi-webhook-controller-manager", "azure-workload-identity-system", 2, 30, 6),
 			"replica count should be 1 after 3 minutes")