From 4aed9359cb159dc804a6b031c0829328d5a561eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?=
 <charled.breteche@gmail.com>
Date: Wed, 12 Oct 2022 08:52:42 +0200
Subject: [PATCH] refactor: manage webhooks with webhook controller (#4846)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: add config support to webhook controller

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* refactor: add client config to webhook controller

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* migrate verify webhook

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* v1

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* refactor: move policy webhooks management in webhook controller

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* policy validating webhook config

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* watch policies

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* refactor: migrate resource webhook management in webhook controller

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* mutating webhook

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* auto update

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* cleanup

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* auto update and wildcard policies

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* policy readiness

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix: can't use v1 admission

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* reduce reconcile

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* watchdog

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* cleanup

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* health check

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* runtime utils

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* runtime utils

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* cleanup

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* watchdog check

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* remove delete from mutating webhook

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* cleanup

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
---
 api/kyverno/v1/clusterpolicy_types.go   |   5 +
 api/kyverno/v1/policy_interface.go      |   1 +
 api/kyverno/v1/policy_types.go          |   5 +
 cmd/kyverno/main.go                     |  96 ++-
 pkg/config/config.go                    |  21 +-
 pkg/controllers/webhook/controller.go   | 875 ++++++++++++++++++++----
 pkg/controllers/webhook/log.go          |   2 +-
 pkg/controllers/webhook/utils.go        |  72 ++
 pkg/controllers/webhook/utils_test.go   |  16 +
 pkg/utils/runtime/utils.go              | 121 ++++
 pkg/webhookconfig/common.go             | 383 -----------
 pkg/webhookconfig/configmanager.go      | 681 ------------------
 pkg/webhookconfig/configmanager_test.go |  16 -
 pkg/webhookconfig/monitor.go            | 235 -------
 pkg/webhookconfig/registration.go       | 653 ------------------
 pkg/webhookconfig/status.go             | 134 ----
 pkg/webhooks/handlers/admission.go      |   4 +-
 pkg/webhooks/handlers/monitor.go        |  15 -
 pkg/webhooks/handlers/probe.go          |   4 +-
 pkg/webhooks/server.go                  |  87 ++-
 20 files changed, 1087 insertions(+), 2339 deletions(-)
 create mode 100644 pkg/controllers/webhook/utils.go
 create mode 100644 pkg/controllers/webhook/utils_test.go
 create mode 100644 pkg/utils/runtime/utils.go
 delete mode 100644 pkg/webhookconfig/common.go
 delete mode 100644 pkg/webhookconfig/configmanager.go
 delete mode 100644 pkg/webhookconfig/configmanager_test.go
 delete mode 100644 pkg/webhookconfig/monitor.go
 delete mode 100644 pkg/webhookconfig/registration.go
 delete mode 100644 pkg/webhookconfig/status.go
 delete mode 100644 pkg/webhooks/handlers/monitor.go

diff --git a/api/kyverno/v1/clusterpolicy_types.go b/api/kyverno/v1/clusterpolicy_types.go
index 78baa2f619..540cfe048f 100644
--- a/api/kyverno/v1/clusterpolicy_types.go
+++ b/api/kyverno/v1/clusterpolicy_types.go
@@ -83,6 +83,11 @@ func (p *ClusterPolicy) GetSpec() *Spec {
 	return &p.Spec
 }
 
+// GetStatus returns the policy status
+func (p *ClusterPolicy) GetStatus() *PolicyStatus {
+	return &p.Status
+}
+
 // IsNamespaced indicates if the policy is namespace scoped
 func (p *ClusterPolicy) IsNamespaced() bool {
 	return p.GetNamespace() != ""
diff --git a/api/kyverno/v1/policy_interface.go b/api/kyverno/v1/policy_interface.go
index 752359ff24..e5c2eb8f85 100644
--- a/api/kyverno/v1/policy_interface.go
+++ b/api/kyverno/v1/policy_interface.go
@@ -14,6 +14,7 @@ type PolicyInterface interface {
 	HasAutoGenAnnotation() bool
 	IsNamespaced() bool
 	GetSpec() *Spec
+	GetStatus() *PolicyStatus
 	Validate(sets.String) field.ErrorList
 	GetKind() string
 	CreateDeepCopy() PolicyInterface
diff --git a/api/kyverno/v1/policy_types.go b/api/kyverno/v1/policy_types.go
index 9aef36fce3..57a55ee868 100755
--- a/api/kyverno/v1/policy_types.go
+++ b/api/kyverno/v1/policy_types.go
@@ -84,6 +84,11 @@ func (p *Policy) GetSpec() *Spec {
 	return &p.Spec
 }
 
+// GetStatus returns the policy status
+func (p *Policy) GetStatus() *PolicyStatus {
+	return &p.Status
+}
+
 // IsNamespaced indicates if the policy is namespace scoped
 func (p *Policy) IsNamespaced() bool {
 	return true
diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index 014a5dac46..4970707a06 100644
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -20,7 +20,6 @@ import (
 	kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
 	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	kyvernoclient "github.com/kyverno/kyverno/pkg/clients/wrappers"
-	"github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/controllers/certmanager"
 	configcontroller "github.com/kyverno/kyverno/pkg/controllers/config"
@@ -44,14 +43,15 @@ import (
 	"github.com/kyverno/kyverno/pkg/toggle"
 	"github.com/kyverno/kyverno/pkg/tracing"
 	"github.com/kyverno/kyverno/pkg/utils"
+	runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
 	"github.com/kyverno/kyverno/pkg/version"
-	"github.com/kyverno/kyverno/pkg/webhookconfig"
 	"github.com/kyverno/kyverno/pkg/webhooks"
 	webhookspolicy "github.com/kyverno/kyverno/pkg/webhooks/policy"
 	webhooksresource "github.com/kyverno/kyverno/pkg/webhooks/resource"
 	webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
 	_ "go.uber.org/automaxprocs" // #nosec
 	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+	coordinationv1 "k8s.io/api/coordination/v1"
 	corev1 "k8s.io/api/core/v1"
 	kubeinformers "k8s.io/client-go/informers"
 	"k8s.io/client-go/kubernetes"
@@ -99,7 +99,7 @@ var (
 func parseFlags() error {
 	logging.Init(nil)
 	flag.StringVar(&logFormat, "loggingFormat", logging.TextFormat, "This determines the output format of the logger.")
-	flag.IntVar(&webhookTimeout, "webhookTimeout", int(webhookconfig.DefaultWebhookTimeout), "Timeout for webhook configurations.")
+	flag.IntVar(&webhookTimeout, "webhookTimeout", webhookcontroller.DefaultWebhookTimeout, "Timeout for webhook configurations.")
 	flag.IntVar(&genWorkers, "genWorkers", 10, "Workers for generate controller.")
 	flag.IntVar(&maxQueuedEvents, "maxQueuedEvents", 1000, "Maximum events to be queued.")
 	flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
@@ -414,6 +414,7 @@ func createrLeaderControllers(
 	metricsConfig *metrics.MetricsConfig,
 	eventGenerator event.Interface,
 	certRenewer *tls.CertRenewer,
+	runtime runtimeutils.Runtime,
 ) ([]controller, error) {
 	policyCtrl, err := policy.NewPolicyController(
 		kyvernoClient,
@@ -436,6 +437,7 @@ func createrLeaderControllers(
 		certRenewer,
 	)
 	webhookController := webhookcontroller.NewController(
+		dynamicClient.Discovery(),
 		metrics.ObjectClient[*corev1.Secret](
 			metrics.NamespacedClientQueryRecorder(metricsConfig, config.KyvernoNamespace(), "Secret", metrics.KubeClient),
 			kubeClient.CoreV1().Secrets(config.KyvernoNamespace()),
@@ -448,10 +450,22 @@ func createrLeaderControllers(
 			metrics.ClusteredClientQueryRecorder(metricsConfig, "ValidatingWebhookConfiguration", metrics.KubeClient),
 			kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
 		),
-		kubeKyvernoInformer.Core().V1().Secrets(),
-		kubeKyvernoInformer.Core().V1().ConfigMaps(),
+		metrics.ObjectClient[*coordinationv1.Lease](
+			metrics.ClusteredClientQueryRecorder(metricsConfig, "Lease", metrics.KubeClient),
+			kubeClient.CoordinationV1().Leases(config.KyvernoNamespace()),
+		),
+		kyvernoClient,
 		kubeInformer.Admissionregistration().V1().MutatingWebhookConfigurations(),
 		kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
+		kyvernoInformer.Kyverno().V1().ClusterPolicies(),
+		kyvernoInformer.Kyverno().V1().Policies(),
+		kubeKyvernoInformer.Core().V1().Secrets(),
+		kubeKyvernoInformer.Core().V1().ConfigMaps(),
+		kubeKyvernoInformer.Coordination().V1().Leases(),
+		serverIP,
+		int32(webhookTimeout),
+		autoUpdateWebhooks,
+		runtime,
 	)
 	return append(
 			[]controller{
@@ -537,26 +551,8 @@ func main() {
 	kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, resyncPeriod)
 	kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace()))
 	kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(kyvernoClient, resyncPeriod)
-	webhookCfg := webhookconfig.NewRegister(
-		signalCtx,
-		clientConfig,
-		dynamicClient,
-		kubeClient,
-		kyvernoClient,
-		kubeInformer.Admissionregistration().V1().MutatingWebhookConfigurations(),
-		kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
-		kubeKyvernoInformer.Apps().V1().Deployments(),
-		kyvernoInformer.Kyverno().V1().ClusterPolicies(),
-		kyvernoInformer.Kyverno().V1().Policies(),
-		metricsConfig,
-		serverIP,
-		int32(webhookTimeout),
-		autoUpdateWebhooks,
-		logging.GlobalLogger(),
-	)
 	configuration, err := config.NewConfiguration(
 		kubeClient,
-		webhookCfg.UpdateWebhookChan,
 	)
 	if err != nil {
 		logger.Error(err, "failed to initialize configuration")
@@ -591,12 +587,18 @@ func main() {
 		maxQueuedEvents,
 		logging.WithName("EventGenerator"),
 	)
-	// This controller only subscribe to events, nothing is returned...
+	// this controller only subscribe to events, nothing is returned...
 	policymetricscontroller.NewController(
 		metricsConfig,
 		kyvernoInformer.Kyverno().V1().ClusterPolicies(),
 		kyvernoInformer.Kyverno().V1().Policies(),
 	)
+	runtime := runtimeutils.NewRuntime(
+		logger.WithName("runtime"),
+		serverIP,
+		kubeKyvernoInformer.Coordination().V1().Leases(),
+		kubeKyvernoInformer.Apps().V1().Deployments(),
+	)
 	// create non leader controllers
 	nonLeaderControllers, nonLeaderBootstrap := createNonLeaderControllers(
 		kubeInformer,
@@ -640,10 +642,10 @@ func main() {
 			// when losing the lead we just terminate the pod
 			defer signalCancel()
 			// validate config
-			if err := webhookCfg.ValidateWebhookConfigurations(config.KyvernoNamespace(), config.KyvernoConfigMapName()); err != nil {
-				logger.Error(err, "invalid format of the Kyverno init ConfigMap, please correct the format of 'data.webhooks'")
-				os.Exit(1)
-			}
+			// if err := webhookCfg.ValidateWebhookConfigurations(config.KyvernoNamespace(), config.KyvernoConfigMapName()); err != nil {
+			// 	logger.Error(err, "invalid format of the Kyverno init ConfigMap, please correct the format of 'data.webhooks'")
+			// 	os.Exit(1)
+			// }
 			// create leader factories
 			kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, resyncPeriod)
 			kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace()))
@@ -662,6 +664,7 @@ func main() {
 				metricsConfig,
 				eventGenerator,
 				certRenewer,
+				runtime,
 			)
 			if err != nil {
 				logger.Error(err, "failed to create leader controllers")
@@ -681,16 +684,6 @@ func main() {
 			for _, controller := range leaderControllers {
 				go controller.run(signalCtx, logger.WithName("controllers"))
 			}
-			// bootstrap
-			if autoUpdateWebhooks {
-				go webhookCfg.UpdateWebhookConfigurations(configuration)
-			}
-			registerWrapperRetry := common.RetryFunc(time.Second, webhookRegistrationTimeout, webhookCfg.Register, "failed to register webhook", logger)
-			if err := registerWrapperRetry(); err != nil {
-				logger.Error(err, "timeout registering admission control webhooks")
-				os.Exit(1)
-			}
-			webhookCfg.UpdateWebhookChan <- true
 			// wait until we loose the lead (or signal context is canceled)
 			<-ctx.Done()
 		},
@@ -702,16 +695,6 @@ func main() {
 	}
 	// start leader election
 	go le.Run(signalCtx)
-	// create monitor
-	webhookMonitor, err := webhookconfig.NewMonitor(kubeClient, logging.GlobalLogger())
-	if err != nil {
-		logger.Error(err, "failed to initialize webhookMonitor")
-		os.Exit(1)
-	}
-	// start monitor (only when running in cluster)
-	if serverIP == "" {
-		go webhookMonitor.Run(signalCtx, webhookCfg, certRenewer, eventGenerator)
-	}
 	// create webhooks server
 	urgen := webhookgenerate.NewGenerator(
 		kyvernoClient,
@@ -740,6 +723,7 @@ func main() {
 	server := webhooks.NewServer(
 		policyHandlers,
 		resourceHandlers,
+		configuration,
 		func() ([]byte, []byte, error) {
 			secret, err := secretLister.Secrets(config.KyvernoNamespace()).Get(tls.GenerateTLSPairSecretName())
 			if err != nil {
@@ -747,9 +731,19 @@ func main() {
 			}
 			return secret.Data[corev1.TLSCertKey], secret.Data[corev1.TLSPrivateKeyKey], nil
 		},
-		configuration,
-		webhookCfg,
-		webhookMonitor,
+		metrics.ObjectClient[*admissionregistrationv1.MutatingWebhookConfiguration](
+			metrics.ClusteredClientQueryRecorder(metricsConfig, "MutatingWebhookConfiguration", metrics.KubeClient),
+			kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations(),
+		),
+		metrics.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration](
+			metrics.ClusteredClientQueryRecorder(metricsConfig, "ValidatingWebhookConfiguration", metrics.KubeClient),
+			kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
+		),
+		metrics.ObjectClient[*coordinationv1.Lease](
+			metrics.ClusteredClientQueryRecorder(metricsConfig, "Lease", metrics.KubeClient),
+			kubeClient.CoordinationV1().Leases(config.KyvernoNamespace()),
+		),
+		runtime,
 	)
 	// start informers and wait for cache sync
 	// we need to call start again because we potentially registered new informers
diff --git a/pkg/config/config.go b/pkg/config/config.go
index e1d28e87f6..56f1481dba 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -18,32 +18,22 @@ import (
 const (
 	// MutatingWebhookConfigurationName default resource mutating webhook configuration name
 	MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
-	// MutatingWebhookConfigurationDebugName default resource mutating webhook configuration name for debug mode
-	MutatingWebhookConfigurationDebugName = "kyverno-resource-mutating-webhook-cfg-debug"
 	// MutatingWebhookName default resource mutating webhook name
 	MutatingWebhookName = "mutate.kyverno.svc"
 	// ValidatingWebhookConfigurationName ...
 	ValidatingWebhookConfigurationName = "kyverno-resource-validating-webhook-cfg"
-	// ValidatingWebhookConfigurationDebugName ...
-	ValidatingWebhookConfigurationDebugName = "kyverno-resource-validating-webhook-cfg-debug"
 	// ValidatingWebhookName ...
 	ValidatingWebhookName = "validate.kyverno.svc"
 	// VerifyMutatingWebhookConfigurationName default verify mutating webhook configuration name
 	VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
-	// VerifyMutatingWebhookConfigurationDebugName default verify mutating webhook configuration name for debug mode
-	VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
 	// VerifyMutatingWebhookName default verify mutating webhook name
 	VerifyMutatingWebhookName = "monitor-webhooks.kyverno.svc"
 	// PolicyValidatingWebhookConfigurationName default policy validating webhook configuration name
 	PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
-	// PolicyValidatingWebhookConfigurationDebugName default policy validating webhook configuration name for debug mode
-	PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
 	// PolicyValidatingWebhookName default policy validating webhook name
 	PolicyValidatingWebhookName = "validate-policy.kyverno.svc"
 	// PolicyMutatingWebhookConfigurationName default policy mutating webhook configuration name
 	PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
-	// PolicyMutatingWebhookConfigurationDebugName default policy mutating webhook configuration name for debug mode
-	PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug"
 	// PolicyMutatingWebhookName default policy mutating webhook name
 	PolicyMutatingWebhookName = "mutate-policy.kyverno.svc"
 	// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
@@ -139,21 +129,19 @@ type configuration struct {
 	restrictDevelopmentUsername []string
 	webhooks                    []WebhookConfig
 	generateSuccessEvents       bool
-	updateWebhookConfigurations chan<- bool
 }
 
 // NewConfiguration ...
-func NewDefaultConfiguration(updateWebhookConfigurations chan<- bool) *configuration {
+func NewDefaultConfiguration() *configuration {
 	return &configuration{
-		updateWebhookConfigurations: updateWebhookConfigurations,
 		restrictDevelopmentUsername: []string{"minikube-user", "kubernetes-admin"},
 		excludeGroupRole:            defaultExcludeGroupRole,
 	}
 }
 
 // NewConfiguration ...
-func NewConfiguration(client kubernetes.Interface, updateWebhookConfigurations chan<- bool) (Configuration, error) {
-	cd := NewDefaultConfiguration(updateWebhookConfigurations)
+func NewConfiguration(client kubernetes.Interface) (Configuration, error) {
+	cd := NewDefaultConfiguration()
 	if cm, err := client.CoreV1().ConfigMaps(kyvernoNamespace).Get(context.TODO(), kyvernoConfigMapName, metav1.GetOptions{}); err != nil {
 		if !errors.IsNotFound(err) {
 			return nil, err
@@ -232,9 +220,6 @@ func (cd *configuration) Load(cm *corev1.ConfigMap) {
 	}
 	if updateWebhook {
 		logger.Info("webhook configurations changed, updating webhook configurations")
-		if cd.updateWebhookConfigurations != nil {
-			cd.updateWebhookConfigurations <- true
-		}
 	}
 }
 
diff --git a/pkg/controllers/webhook/controller.go b/pkg/controllers/webhook/controller.go
index cfd6539a44..841b7745de 100644
--- a/pkg/controllers/webhook/controller.go
+++ b/pkg/controllers/webhook/controller.go
@@ -1,139 +1,283 @@
-package background
+package webhook
 
 import (
 	"context"
+	"fmt"
+	"strings"
+	"sync"
+	"time"
 
 	"github.com/go-logr/logr"
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/autogen"
+	"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
+	kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
+	kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/controllers"
 	"github.com/kyverno/kyverno/pkg/tls"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
+	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
+	runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
 	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+	coordinationv1 "k8s.io/api/coordination/v1"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/selection"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/util/sets"
 	admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
+	coordinationv1informers "k8s.io/client-go/informers/coordination/v1"
 	corev1informers "k8s.io/client-go/informers/core/v1"
 	admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
+	coordinationv1listers "k8s.io/client-go/listers/coordination/v1"
 	corev1listers "k8s.io/client-go/listers/core/v1"
+	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/util/workqueue"
 )
 
 const (
 	// Workers is the number of workers for this controller
-	Workers        = 2
-	ControllerName = "webhook-ca-controller"
-	maxRetries     = 10
+	Workers                   = 2
+	ControllerName            = "webhook-controller"
+	DefaultWebhookTimeout     = 10
+	AnnotationLastRequestTime = "kyverno.io/last-request-time"
+	IdleDeadline              = tickerInterval * 5
+	maxRetries                = 10
+	managedByLabel            = "webhook.kyverno.io/managed-by"
+	tickerInterval            = 30 * time.Second
+)
+
+var (
+	noneOnDryRun = admissionregistrationv1.SideEffectClassNoneOnDryRun
+	ifNeeded     = admissionregistrationv1.IfNeededReinvocationPolicy
+	ignore       = admissionregistrationv1.Ignore
+	fail         = admissionregistrationv1.Fail
+	policyRule   = admissionregistrationv1.Rule{
+		Resources:   []string{"clusterpolicies/*", "policies/*"},
+		APIGroups:   []string{"kyverno.io"},
+		APIVersions: []string{"v1", "v2beta1"},
+	}
+	verifyRule = admissionregistrationv1.Rule{
+		Resources:   []string{"leases"},
+		APIGroups:   []string{"coordination.k8s.io"},
+		APIVersions: []string{"v1"},
+	}
 )
 
 type controller struct {
 	// clients
-	secretClient controllerutils.GetClient[*corev1.Secret]
-	mwcClient    controllerutils.UpdateClient[*admissionregistrationv1.MutatingWebhookConfiguration]
-	vwcClient    controllerutils.UpdateClient[*admissionregistrationv1.ValidatingWebhookConfiguration]
+	discoveryClient dclient.IDiscovery
+	secretClient    controllerutils.GetClient[*corev1.Secret]
+	mwcClient       controllerutils.ObjectClient[*admissionregistrationv1.MutatingWebhookConfiguration]
+	vwcClient       controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration]
+	leaseClient     controllerutils.UpdateClient[*coordinationv1.Lease]
+	kyvernoClient   versioned.Interface
 
 	// listers
-	secretLister    corev1listers.SecretLister
-	configMapLister corev1listers.ConfigMapLister
 	mwcLister       admissionregistrationv1listers.MutatingWebhookConfigurationLister
 	vwcLister       admissionregistrationv1listers.ValidatingWebhookConfigurationLister
+	cpolLister      kyvernov1listers.ClusterPolicyLister
+	polLister       kyvernov1listers.PolicyLister
+	secretLister    corev1listers.SecretLister
+	configMapLister corev1listers.ConfigMapLister
+	leaseLister     coordinationv1listers.LeaseLister
 
 	// queue
-	queue      workqueue.RateLimitingInterface
-	mwcEnqueue controllerutils.EnqueueFunc
-	vwcEnqueue controllerutils.EnqueueFunc
+	queue workqueue.RateLimitingInterface
+
+	// config
+	server             string
+	defaultTimeout     int32
+	autoUpdateWebhooks bool
+	runtime            runtimeutils.Runtime
+
+	// state
+	lock        sync.RWMutex
+	policyState map[string]sets.String
 }
 
 func NewController(
+	discoveryClient dclient.IDiscovery,
 	secretClient controllerutils.GetClient[*corev1.Secret],
-	mwcClient controllerutils.UpdateClient[*admissionregistrationv1.MutatingWebhookConfiguration],
-	vwcClient controllerutils.UpdateClient[*admissionregistrationv1.ValidatingWebhookConfiguration],
-	secretInformer corev1informers.SecretInformer,
-	configMapInformer corev1informers.ConfigMapInformer,
+	mwcClient controllerutils.ObjectClient[*admissionregistrationv1.MutatingWebhookConfiguration],
+	vwcClient controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration],
+	leaseClient controllerutils.UpdateClient[*coordinationv1.Lease],
+	kyvernoClient versioned.Interface,
 	mwcInformer admissionregistrationv1informers.MutatingWebhookConfigurationInformer,
 	vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
+	cpolInformer kyvernov1informers.ClusterPolicyInformer,
+	polInformer kyvernov1informers.PolicyInformer,
+	secretInformer corev1informers.SecretInformer,
+	configMapInformer corev1informers.ConfigMapInformer,
+	leaseInformer coordinationv1informers.LeaseInformer,
+	server string,
+	defaultTimeout int32,
+	autoUpdateWebhooks bool,
+	runtime runtimeutils.Runtime,
 ) controllers.Controller {
 	queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), ControllerName)
 	c := controller{
-		secretClient:    secretClient,
-		mwcClient:       mwcClient,
-		vwcClient:       vwcClient,
-		secretLister:    secretInformer.Lister(),
-		configMapLister: configMapInformer.Lister(),
-		mwcLister:       mwcInformer.Lister(),
-		vwcLister:       vwcInformer.Lister(),
-		queue:           queue,
-		mwcEnqueue:      controllerutils.AddDefaultEventHandlers(logger.V(3), mwcInformer.Informer(), queue),
-		vwcEnqueue:      controllerutils.AddDefaultEventHandlers(logger.V(3), vwcInformer.Informer(), queue),
+		discoveryClient:    discoveryClient,
+		secretClient:       secretClient,
+		mwcClient:          mwcClient,
+		vwcClient:          vwcClient,
+		leaseClient:        leaseClient,
+		kyvernoClient:      kyvernoClient,
+		mwcLister:          mwcInformer.Lister(),
+		vwcLister:          vwcInformer.Lister(),
+		cpolLister:         cpolInformer.Lister(),
+		polLister:          polInformer.Lister(),
+		secretLister:       secretInformer.Lister(),
+		configMapLister:    configMapInformer.Lister(),
+		leaseLister:        leaseInformer.Lister(),
+		queue:              queue,
+		server:             server,
+		defaultTimeout:     defaultTimeout,
+		autoUpdateWebhooks: autoUpdateWebhooks,
+		runtime:            runtime,
+		policyState: map[string]sets.String{
+			config.MutatingWebhookConfigurationName:   sets.NewString(),
+			config.ValidatingWebhookConfigurationName: sets.NewString(),
+		},
 	}
+	controllerutils.AddDefaultEventHandlers(logger, mwcInformer.Informer(), queue)
+	controllerutils.AddDefaultEventHandlers(logger, vwcInformer.Informer(), queue)
 	controllerutils.AddEventHandlersT(
 		secretInformer.Informer(),
-		func(obj *corev1.Secret) { c.secretChanged(obj) },
-		func(_, obj *corev1.Secret) { c.secretChanged(obj) },
-		func(obj *corev1.Secret) { c.secretChanged(obj) },
+		func(obj *corev1.Secret) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
+				c.enqueueAll()
+			}
+		},
+		func(_, obj *corev1.Secret) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
+				c.enqueueAll()
+			}
+		},
+		func(obj *corev1.Secret) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
+				c.enqueueAll()
+			}
+		},
 	)
 	controllerutils.AddEventHandlersT(
 		configMapInformer.Informer(),
-		func(obj *corev1.ConfigMap) { c.configMapChanged(obj) },
-		func(_, obj *corev1.ConfigMap) { c.configMapChanged(obj) },
-		func(obj *corev1.ConfigMap) { c.configMapChanged(obj) },
+		func(obj *corev1.ConfigMap) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoConfigMapName() {
+				c.enqueueAll()
+			}
+		},
+		func(_, obj *corev1.ConfigMap) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoConfigMapName() {
+				c.enqueueAll()
+			}
+		},
+		func(obj *corev1.ConfigMap) {
+			if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoConfigMapName() {
+				c.enqueueAll()
+			}
+		},
+	)
+	controllerutils.AddEventHandlers(
+		cpolInformer.Informer(),
+		func(interface{}) { c.enqueueResourceWebhooks(0) },
+		func(interface{}, interface{}) { c.enqueueResourceWebhooks(0) },
+		func(interface{}) { c.enqueueResourceWebhooks(0) },
+	)
+	controllerutils.AddEventHandlers(
+		polInformer.Informer(),
+		func(interface{}) { c.enqueueResourceWebhooks(0) },
+		func(interface{}, interface{}) { c.enqueueResourceWebhooks(0) },
+		func(interface{}) { c.enqueueResourceWebhooks(0) },
 	)
 	return &c
 }
 
 func (c *controller) Run(ctx context.Context, workers int) {
-	controllerutils.Run(ctx, ControllerName, logger.V(3), c.queue, workers, maxRetries, c.reconcile)
+	// add our known webhooks to the queue
+	c.enqueueAll()
+	go c.watchdog(ctx)
+	controllerutils.Run(ctx, ControllerName, logger, c.queue, workers, maxRetries, c.reconcile)
 }
 
-func (c *controller) secretChanged(secret *corev1.Secret) {
-	if secret.GetName() == tls.GenerateRootCASecretName() && secret.GetNamespace() == config.KyvernoNamespace() {
-		if err := c.enqueueAll(); err != nil {
-			logger.Error(err, "failed to enqueue on secret change")
+func (c *controller) watchdog(ctx context.Context) {
+	ticker := time.NewTicker(tickerInterval)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			lease, err := c.getLease()
+			if err != nil {
+				logger.Error(err, "failed to get lease")
+			} else {
+				if _, err := controllerutils.Update(
+					ctx,
+					lease,
+					c.leaseClient,
+					func(lease *coordinationv1.Lease) error {
+						if lease.Annotations == nil {
+							lease.Annotations = map[string]string{}
+						}
+						lease.Annotations[AnnotationLastRequestTime] = time.Now().Format(time.RFC3339)
+						if lease.Labels == nil {
+							lease.Labels = map[string]string{}
+						}
+						lease.Labels["app.kubernetes.io/name"] = kyvernov1.ValueKyvernoApp
+						return nil
+					},
+				); err != nil {
+					logger.Error(err, "failed to get lease")
+				}
+			}
+			c.enqueueResourceWebhooks(0)
 		}
 	}
 }
 
-func (c *controller) configMapChanged(cm *corev1.ConfigMap) {
-	if cm.GetName() == config.KyvernoConfigMapName() && cm.GetNamespace() == config.KyvernoNamespace() {
-		if err := c.enqueueAll(); err != nil {
-			logger.Error(err, "failed to enqueue on configmap change")
-		}
+func (c *controller) watchdogCheck() bool {
+	lease, err := c.getLease()
+	if err != nil {
+		logger.Error(err, "failed to get lease")
+		return false
 	}
+	annotations := lease.GetAnnotations()
+	if annotations == nil {
+		return false
+	}
+	annTime, err := time.Parse(time.RFC3339, annotations[AnnotationLastRequestTime])
+	if err != nil {
+		return false
+	}
+	return time.Now().Before(annTime.Add(IdleDeadline))
 }
 
-func (c *controller) enqueueAll() error {
-	requirement, err := labels.NewRequirement("webhook.kyverno.io/managed-by", selection.Equals, []string{kyvernov1.ValueKyvernoApp})
-	if err != nil {
-		return err
-	}
-	selector := labels.Everything().Add(*requirement)
-	mwcs, err := c.mwcLister.List(selector)
-	if err != nil {
-		return err
-	}
-	for _, mwc := range mwcs {
-		err = c.mwcEnqueue(mwc)
-		if err != nil {
-			return err
-		}
-	}
-	vwcs, err := c.vwcLister.List(selector)
-	if err != nil {
-		return err
-	}
-	for _, vwc := range vwcs {
-		err = c.vwcEnqueue(vwc)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
+func (c *controller) enqueueAll() {
+	c.enqueuePolicyWebhooks()
+	c.enqueueResourceWebhooks(0)
+	c.enqueueVerifyWebhook()
+}
+
+func (c *controller) enqueuePolicyWebhooks() {
+	c.queue.Add(config.PolicyValidatingWebhookConfigurationName)
+	c.queue.Add(config.PolicyMutatingWebhookConfigurationName)
+}
+
+func (c *controller) enqueueResourceWebhooks(duration time.Duration) {
+	c.queue.AddAfter(config.MutatingWebhookConfigurationName, duration)
+	c.queue.AddAfter(config.ValidatingWebhookConfigurationName, duration)
+}
+
+func (c *controller) enqueueVerifyWebhook() {
+	c.queue.Add(config.VerifyMutatingWebhookConfigurationName)
 }
 
 func (c *controller) loadConfig() config.Configuration {
-	cfg := config.NewDefaultConfiguration(nil)
+	cfg := config.NewDefaultConfiguration()
 	cm, err := c.configMapLister.ConfigMaps(config.KyvernoNamespace()).Get(config.KyvernoConfigMapName())
 	if err == nil {
 		cfg.Load(cm)
@@ -141,78 +285,575 @@ func (c *controller) loadConfig() config.Configuration {
 	return cfg
 }
 
-func (c *controller) reconcileMutatingWebhookConfiguration(ctx context.Context, logger logr.Logger, name string) error {
-	w, err := c.mwcLister.Get(name)
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			return nil
+func (c *controller) recordPolicyState(webhookConfigurationName string, policies ...kyvernov1.PolicyInterface) {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	if _, ok := c.policyState[webhookConfigurationName]; !ok {
+		return
+	}
+	c.policyState[webhookConfigurationName] = sets.NewString()
+	for _, policy := range policies {
+		policyKey, err := cache.MetaNamespaceKeyFunc(policy)
+		if err != nil {
+			logger.Error(err, "failed to compute policy key", "policy", policy)
 		}
-		return err
+		c.policyState[webhookConfigurationName].Insert(policyKey)
 	}
-	labels := w.GetLabels()
-	if labels == nil || labels["webhook.kyverno.io/managed-by"] != kyvernov1.ValueKyvernoApp {
-		return nil
+}
+
+func (c *controller) clientConfig(caBundle []byte, path string) admissionregistrationv1.WebhookClientConfig {
+	clientConfig := admissionregistrationv1.WebhookClientConfig{
+		CABundle: caBundle,
 	}
-	cfg := c.loadConfig()
-	webhookCfg := config.WebhookConfig{}
-	webhookCfgs := cfg.GetWebhooks()
-	if len(webhookCfgs) > 0 {
-		webhookCfg = webhookCfgs[0]
+	if c.server == "" {
+		clientConfig.Service = &admissionregistrationv1.ServiceReference{
+			Namespace: config.KyvernoNamespace(),
+			Name:      config.KyvernoServiceName(),
+			Path:      &path,
+		}
+	} else {
+		url := fmt.Sprintf("https://%s%s", c.server, path)
+		clientConfig.URL = &url
 	}
+	return clientConfig
+}
+
+func (c *controller) reconcileResourceValidatingWebhookConfiguration(ctx context.Context) error {
+	if c.autoUpdateWebhooks {
+		return c.reconcileValidatingWebhookConfiguration(ctx, c.autoUpdateWebhooks, c.buildResourceValidatingWebhookConfiguration)
+	} else {
+		return c.reconcileValidatingWebhookConfiguration(ctx, c.autoUpdateWebhooks, c.buildDefaultResourceValidatingWebhookConfiguration)
+	}
+}
+
+func (c *controller) reconcileResourceMutatingWebhookConfiguration(ctx context.Context) error {
+	if c.autoUpdateWebhooks {
+		return c.reconcileMutatingWebhookConfiguration(ctx, c.autoUpdateWebhooks, c.buildResourceMutatingWebhookConfiguration)
+	} else {
+		return c.reconcileMutatingWebhookConfiguration(ctx, c.autoUpdateWebhooks, c.buildDefaultResourceMutatingWebhookConfiguration)
+	}
+}
+
+func (c *controller) reconcilePolicyValidatingWebhookConfiguration(ctx context.Context) error {
+	return c.reconcileValidatingWebhookConfiguration(ctx, true, c.buildPolicyValidatingWebhookConfiguration)
+}
+
+func (c *controller) reconcilePolicyMutatingWebhookConfiguration(ctx context.Context) error {
+	return c.reconcileMutatingWebhookConfiguration(ctx, true, c.buildPolicyMutatingWebhookConfiguration)
+}
+
+func (c *controller) reconcileVerifyMutatingWebhookConfiguration(ctx context.Context) error {
+	return c.reconcileMutatingWebhookConfiguration(ctx, true, c.buildVerifyMutatingWebhookConfiguration)
+}
+
+func (c *controller) reconcileValidatingWebhookConfiguration(ctx context.Context, autoUpdateWebhooks bool, build func([]byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error)) error {
 	caData, err := tls.ReadRootCASecret(c.secretClient)
 	if err != nil {
 		return err
 	}
-	_, err = controllerutils.Update(ctx, w, c.mwcClient, func(w *admissionregistrationv1.MutatingWebhookConfiguration) error {
-		for i := range w.Webhooks {
-			w.Webhooks[i].ClientConfig.CABundle = caData
-			w.Webhooks[i].ObjectSelector = webhookCfg.ObjectSelector
-			w.Webhooks[i].NamespaceSelector = webhookCfg.NamespaceSelector
+	desired, err := build(caData)
+	if err != nil {
+		return err
+	}
+	observed, err := c.vwcLister.Get(desired.Name)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			_, err := c.vwcClient.Create(ctx, desired, metav1.CreateOptions{})
+			return err
 		}
+		return err
+	}
+	if !autoUpdateWebhooks {
+		return nil
+	}
+	_, err = controllerutils.Update(ctx, observed, c.vwcClient, func(w *admissionregistrationv1.ValidatingWebhookConfiguration) error {
+		w.Labels = desired.Labels
+		w.OwnerReferences = desired.OwnerReferences
+		w.Webhooks = desired.Webhooks
 		return nil
 	})
 	return err
 }
 
-func (c *controller) reconcileValidatingWebhookConfiguration(ctx context.Context, logger logr.Logger, name string) error {
-	w, err := c.vwcLister.Get(name)
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			return nil
-		}
-		return err
-	}
-	labels := w.GetLabels()
-	if labels == nil || labels["webhook.kyverno.io/managed-by"] != kyvernov1.ValueKyvernoApp {
-		return nil
-	}
-	cfg := c.loadConfig()
-	webhookCfg := config.WebhookConfig{}
-	webhookCfgs := cfg.GetWebhooks()
-	if len(webhookCfgs) > 0 {
-		webhookCfg = webhookCfgs[0]
-	}
+func (c *controller) reconcileMutatingWebhookConfiguration(ctx context.Context, autoUpdateWebhooks bool, build func([]byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error)) error {
 	caData, err := tls.ReadRootCASecret(c.secretClient)
 	if err != nil {
 		return err
 	}
-	_, err = controllerutils.Update(ctx, w, c.vwcClient, func(w *admissionregistrationv1.ValidatingWebhookConfiguration) error {
-		for i := range w.Webhooks {
-			w.Webhooks[i].ClientConfig.CABundle = caData
-			w.Webhooks[i].ObjectSelector = webhookCfg.ObjectSelector
-			w.Webhooks[i].NamespaceSelector = webhookCfg.NamespaceSelector
+	desired, err := build(caData)
+	if err != nil {
+		return err
+	}
+	observed, err := c.mwcLister.Get(desired.Name)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			_, err := c.mwcClient.Create(ctx, desired, metav1.CreateOptions{})
+			return err
 		}
+		return err
+	}
+	if !autoUpdateWebhooks {
+		return nil
+	}
+	_, err = controllerutils.Update(ctx, observed, c.mwcClient, func(w *admissionregistrationv1.MutatingWebhookConfiguration) error {
+		w.Labels = desired.Labels
+		w.OwnerReferences = desired.OwnerReferences
+		w.Webhooks = desired.Webhooks
 		return nil
 	})
 	return err
 }
 
-func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error {
-	if err := c.reconcileMutatingWebhookConfiguration(ctx, logger, name); err != nil {
+func (c *controller) updatePolicyStatuses(ctx context.Context) error {
+	c.lock.RLock()
+	defer c.lock.RUnlock()
+	policies, err := c.getAllPolicies()
+	if err != nil {
 		return err
 	}
-	if err := c.reconcileValidatingWebhookConfiguration(ctx, logger, name); err != nil {
-		return err
+	for _, policy := range policies {
+		policyKey, err := cache.MetaNamespaceKeyFunc(policy)
+		if err != nil {
+			return err
+		}
+		ready := true
+		for _, set := range c.policyState {
+			if !set.Has(policyKey) {
+				ready = false
+			}
+		}
+		if policy.IsReady() != ready {
+			policy = policy.CreateDeepCopy()
+			status := policy.GetStatus()
+			status.SetReady(ready)
+			if policy.GetNamespace() == "" {
+				_, err := c.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(ctx, policy.(*kyvernov1.ClusterPolicy), metav1.UpdateOptions{})
+				if err != nil {
+					return err
+				}
+			} else {
+				_, err := c.kyvernoClient.KyvernoV1().Policies(policy.GetNamespace()).UpdateStatus(ctx, policy.(*kyvernov1.Policy), metav1.UpdateOptions{})
+				if err != nil {
+					return err
+				}
+			}
+		}
 	}
 	return nil
 }
+
+func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error {
+	switch name {
+	case config.MutatingWebhookConfigurationName:
+		if c.runtime.IsRollingUpdate() {
+			c.enqueueResourceWebhooks(1 * time.Second)
+		} else {
+			if err := c.reconcileResourceMutatingWebhookConfiguration(ctx); err != nil {
+				return err
+			}
+			if err := c.updatePolicyStatuses(ctx); err != nil {
+				return err
+			}
+		}
+	case config.ValidatingWebhookConfigurationName:
+		if c.runtime.IsRollingUpdate() {
+			c.enqueueResourceWebhooks(1 * time.Second)
+		} else {
+			if err := c.reconcileResourceValidatingWebhookConfiguration(ctx); err != nil {
+				return err
+			}
+			if err := c.updatePolicyStatuses(ctx); err != nil {
+				return err
+			}
+		}
+	case config.PolicyValidatingWebhookConfigurationName:
+		return c.reconcilePolicyValidatingWebhookConfiguration(ctx)
+	case config.PolicyMutatingWebhookConfigurationName:
+		return c.reconcilePolicyMutatingWebhookConfiguration(ctx)
+	case config.VerifyMutatingWebhookConfigurationName:
+		return c.reconcileVerifyMutatingWebhookConfiguration(ctx)
+	}
+	return nil
+}
+
+func (c *controller) buildVerifyMutatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
+	return &admissionregistrationv1.MutatingWebhookConfiguration{
+			ObjectMeta: objectMeta(config.VerifyMutatingWebhookConfigurationName),
+			Webhooks: []admissionregistrationv1.MutatingWebhook{{
+				Name:         config.VerifyMutatingWebhookName,
+				ClientConfig: c.clientConfig(caBundle, config.VerifyMutatingWebhookServicePath),
+				Rules: []admissionregistrationv1.RuleWithOperations{{
+					Rule: verifyRule,
+					Operations: []admissionregistrationv1.OperationType{
+						admissionregistrationv1.Update,
+					},
+				}},
+				FailurePolicy:           &ignore,
+				SideEffects:             &noneOnDryRun,
+				ReinvocationPolicy:      &ifNeeded,
+				AdmissionReviewVersions: []string{"v1beta1"},
+				ObjectSelector: &metav1.LabelSelector{
+					MatchLabels: map[string]string{
+						"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
+					},
+				},
+			}},
+		},
+		nil
+}
+
+func (c *controller) buildPolicyMutatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
+	return &admissionregistrationv1.MutatingWebhookConfiguration{
+			ObjectMeta: objectMeta(config.PolicyMutatingWebhookConfigurationName),
+			Webhooks: []admissionregistrationv1.MutatingWebhook{{
+				Name:         config.PolicyMutatingWebhookName,
+				ClientConfig: c.clientConfig(caBundle, config.PolicyMutatingWebhookServicePath),
+				Rules: []admissionregistrationv1.RuleWithOperations{{
+					Rule: policyRule,
+					Operations: []admissionregistrationv1.OperationType{
+						admissionregistrationv1.Create,
+						admissionregistrationv1.Update,
+					},
+				}},
+				FailurePolicy:           &ignore,
+				SideEffects:             &noneOnDryRun,
+				ReinvocationPolicy:      &ifNeeded,
+				AdmissionReviewVersions: []string{"v1beta1"},
+			}},
+		},
+		nil
+}
+
+func (c *controller) buildPolicyValidatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
+	return &admissionregistrationv1.ValidatingWebhookConfiguration{
+			ObjectMeta: objectMeta(config.PolicyValidatingWebhookConfigurationName),
+			Webhooks: []admissionregistrationv1.ValidatingWebhook{{
+				Name:         config.PolicyValidatingWebhookName,
+				ClientConfig: c.clientConfig(caBundle, config.PolicyValidatingWebhookServicePath),
+				Rules: []admissionregistrationv1.RuleWithOperations{{
+					Rule: policyRule,
+					Operations: []admissionregistrationv1.OperationType{
+						admissionregistrationv1.Create,
+						admissionregistrationv1.Update,
+					},
+				}},
+				FailurePolicy:           &ignore,
+				SideEffects:             &noneOnDryRun,
+				AdmissionReviewVersions: []string{"v1beta1"},
+			}},
+		},
+		nil
+}
+
+func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
+	return &admissionregistrationv1.MutatingWebhookConfiguration{
+			ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName),
+			Webhooks: []admissionregistrationv1.MutatingWebhook{{
+				Name:         config.MutatingWebhookName + "-ignore",
+				ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/ignore"),
+				Rules: []admissionregistrationv1.RuleWithOperations{{
+					Rule: admissionregistrationv1.Rule{
+						APIGroups:   []string{"*"},
+						APIVersions: []string{"*"},
+						Resources:   []string{"*/*"},
+					},
+					Operations: []admissionregistrationv1.OperationType{
+						admissionregistrationv1.Create,
+						admissionregistrationv1.Update,
+					},
+				}},
+				FailurePolicy:           &ignore,
+				SideEffects:             &noneOnDryRun,
+				AdmissionReviewVersions: []string{"v1beta1"},
+				TimeoutSeconds:          &c.defaultTimeout,
+				ReinvocationPolicy:      &ifNeeded,
+			}},
+		},
+		nil
+}
+
+func (c *controller) buildResourceMutatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
+	result := admissionregistrationv1.MutatingWebhookConfiguration{
+		ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName),
+		Webhooks:   []admissionregistrationv1.MutatingWebhook{},
+	}
+	if c.watchdogCheck() {
+		ignore := newWebhook(c.defaultTimeout, ignore)
+		fail := newWebhook(c.defaultTimeout, fail)
+		policies, err := c.getAllPolicies()
+		if err != nil {
+			return nil, err
+		}
+		c.recordPolicyState(config.MutatingWebhookConfigurationName, policies...)
+		// TODO: shouldn't be per failure policy, depending of the policy/rules that apply ?
+		if hasWildcard(policies...) {
+			ignore.setWildcard()
+			fail.setWildcard()
+		} else {
+			for _, p := range policies {
+				spec := p.GetSpec()
+				if spec.HasMutate() || spec.HasVerifyImages() {
+					if spec.GetFailurePolicy() == kyvernov1.Ignore {
+						c.mergeWebhook(ignore, p, false)
+					} else {
+						c.mergeWebhook(fail, p, false)
+					}
+				}
+			}
+		}
+		cfg := c.loadConfig()
+		webhookCfg := config.WebhookConfig{}
+		webhookCfgs := cfg.GetWebhooks()
+		if len(webhookCfgs) > 0 {
+			webhookCfg = webhookCfgs[0]
+		}
+		if !ignore.isEmpty() {
+			result.Webhooks = append(
+				result.Webhooks,
+				admissionregistrationv1.MutatingWebhook{
+					Name:         config.MutatingWebhookName + "-ignore",
+					ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/ignore"),
+					Rules: []admissionregistrationv1.RuleWithOperations{
+						ignore.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
+					},
+					FailurePolicy:           &ignore.failurePolicy,
+					SideEffects:             &noneOnDryRun,
+					AdmissionReviewVersions: []string{"v1beta1"},
+					NamespaceSelector:       webhookCfg.NamespaceSelector,
+					ObjectSelector:          webhookCfg.ObjectSelector,
+					TimeoutSeconds:          &ignore.maxWebhookTimeout,
+					ReinvocationPolicy:      &ifNeeded,
+				},
+			)
+		}
+		if !fail.isEmpty() {
+			result.Webhooks = append(
+				result.Webhooks,
+				admissionregistrationv1.MutatingWebhook{
+					Name:         config.MutatingWebhookName + "-fail",
+					ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/fail"),
+					Rules: []admissionregistrationv1.RuleWithOperations{
+						fail.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
+					},
+					FailurePolicy:           &fail.failurePolicy,
+					SideEffects:             &noneOnDryRun,
+					AdmissionReviewVersions: []string{"v1beta1"},
+					NamespaceSelector:       webhookCfg.NamespaceSelector,
+					ObjectSelector:          webhookCfg.ObjectSelector,
+					TimeoutSeconds:          &fail.maxWebhookTimeout,
+					ReinvocationPolicy:      &ifNeeded,
+				},
+			)
+		}
+	} else {
+		c.recordPolicyState(config.MutatingWebhookConfigurationName)
+	}
+	return &result, nil
+}
+
+func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
+	return &admissionregistrationv1.ValidatingWebhookConfiguration{
+			ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName),
+			Webhooks: []admissionregistrationv1.ValidatingWebhook{{
+				Name:         config.ValidatingWebhookName + "-ignore",
+				ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/ignore"),
+				Rules: []admissionregistrationv1.RuleWithOperations{{
+					Rule: admissionregistrationv1.Rule{
+						APIGroups:   []string{"*"},
+						APIVersions: []string{"*"},
+						Resources:   []string{"*/*"},
+					},
+					Operations: []admissionregistrationv1.OperationType{
+						admissionregistrationv1.Create,
+						admissionregistrationv1.Update,
+						admissionregistrationv1.Delete,
+						admissionregistrationv1.Connect,
+					},
+				}},
+				FailurePolicy:           &ignore,
+				SideEffects:             &noneOnDryRun,
+				AdmissionReviewVersions: []string{"v1beta1"},
+				TimeoutSeconds:          &c.defaultTimeout,
+			}},
+		},
+		nil
+}
+
+func (c *controller) buildResourceValidatingWebhookConfiguration(caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
+	result := admissionregistrationv1.ValidatingWebhookConfiguration{
+		ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName),
+		Webhooks:   []admissionregistrationv1.ValidatingWebhook{},
+	}
+	if c.watchdogCheck() {
+		ignore := newWebhook(c.defaultTimeout, ignore)
+		fail := newWebhook(c.defaultTimeout, fail)
+		policies, err := c.getAllPolicies()
+		if err != nil {
+			return nil, err
+		}
+		c.recordPolicyState(config.ValidatingWebhookConfigurationName, policies...)
+		// TODO: shouldn't be per failure policy, depending of the policy/rules that apply ?
+		if hasWildcard(policies...) {
+			ignore.setWildcard()
+			fail.setWildcard()
+		} else {
+			for _, p := range policies {
+				spec := p.GetSpec()
+				if spec.HasValidate() || spec.HasGenerate() || spec.HasMutate() || spec.HasImagesValidationChecks() || spec.HasYAMLSignatureVerify() {
+					if spec.GetFailurePolicy() == kyvernov1.Ignore {
+						c.mergeWebhook(ignore, p, true)
+					} else {
+						c.mergeWebhook(fail, p, true)
+					}
+				}
+			}
+		}
+		cfg := c.loadConfig()
+		webhookCfg := config.WebhookConfig{}
+		webhookCfgs := cfg.GetWebhooks()
+		if len(webhookCfgs) > 0 {
+			webhookCfg = webhookCfgs[0]
+		}
+		if !ignore.isEmpty() {
+			result.Webhooks = append(
+				result.Webhooks,
+				admissionregistrationv1.ValidatingWebhook{
+					Name:         config.ValidatingWebhookName + "-ignore",
+					ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/ignore"),
+					Rules: []admissionregistrationv1.RuleWithOperations{
+						ignore.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
+					},
+					FailurePolicy:           &ignore.failurePolicy,
+					SideEffects:             &noneOnDryRun,
+					AdmissionReviewVersions: []string{"v1beta1"},
+					NamespaceSelector:       webhookCfg.NamespaceSelector,
+					ObjectSelector:          webhookCfg.ObjectSelector,
+					TimeoutSeconds:          &ignore.maxWebhookTimeout,
+				},
+			)
+		}
+		if !fail.isEmpty() {
+			result.Webhooks = append(
+				result.Webhooks,
+				admissionregistrationv1.ValidatingWebhook{
+					Name:         config.ValidatingWebhookName + "-fail",
+					ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/fail"),
+					Rules: []admissionregistrationv1.RuleWithOperations{
+						fail.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
+					},
+					FailurePolicy:           &fail.failurePolicy,
+					SideEffects:             &noneOnDryRun,
+					AdmissionReviewVersions: []string{"v1beta1"},
+					NamespaceSelector:       webhookCfg.NamespaceSelector,
+					ObjectSelector:          webhookCfg.ObjectSelector,
+					TimeoutSeconds:          &fail.maxWebhookTimeout,
+				},
+			)
+		}
+	} else {
+		c.recordPolicyState(config.MutatingWebhookConfigurationName)
+	}
+	return &result, nil
+}
+
+func (c *controller) getAllPolicies() ([]kyvernov1.PolicyInterface, error) {
+	var policies []kyvernov1.PolicyInterface
+	if cpols, err := c.cpolLister.List(labels.Everything()); err != nil {
+		return nil, err
+	} else {
+		for _, cpol := range cpols {
+			policies = append(policies, cpol)
+		}
+	}
+	if pols, err := c.polLister.List(labels.Everything()); err != nil {
+		return nil, err
+	} else {
+		for _, pol := range pols {
+			policies = append(policies, pol)
+		}
+	}
+	return policies, nil
+}
+
+func (c *controller) getLease() (*coordinationv1.Lease, error) {
+	return c.leaseLister.Leases(config.KyvernoNamespace()).Get("kyverno")
+}
+
+// mergeWebhook merges the matching kinds of the policy to webhook.rule
+func (c *controller) mergeWebhook(dst *webhook, policy kyvernov1.PolicyInterface, updateValidate bool) {
+	matchedGVK := make([]string, 0)
+	for _, rule := range autogen.ComputeRules(policy) {
+		// matching kinds in generate policies need to be added to both webhook
+		if rule.HasGenerate() {
+			matchedGVK = append(matchedGVK, rule.MatchResources.GetKinds()...)
+			matchedGVK = append(matchedGVK, rule.Generation.ResourceSpec.Kind)
+			continue
+		}
+		if (updateValidate && rule.HasValidate() || rule.HasImagesValidationChecks()) ||
+			(updateValidate && rule.HasMutate() && rule.IsMutateExisting()) ||
+			(!updateValidate && rule.HasMutate()) && !rule.IsMutateExisting() ||
+			(!updateValidate && rule.HasVerifyImages()) || (!updateValidate && rule.HasYAMLSignatureVerify()) {
+			matchedGVK = append(matchedGVK, rule.MatchResources.GetKinds()...)
+		}
+	}
+	gvkMap := make(map[string]int)
+	gvrList := make([]schema.GroupVersionResource, 0)
+	for _, gvk := range matchedGVK {
+		if _, ok := gvkMap[gvk]; !ok {
+			gvkMap[gvk] = 1
+			// NOTE: webhook stores GVR in its rules while policy stores GVK in its rules definition
+			gv, k := kubeutils.GetKindFromGVK(gvk)
+			switch k {
+			case "Binding":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/binding"})
+			case "NodeProxyOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes/proxy"})
+			case "PodAttachOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/attach"})
+			case "PodExecOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/exec"})
+			case "PodPortForwardOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/portforward"})
+			case "PodProxyOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/proxy"})
+			case "ServiceProxyOptions":
+				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services/proxy"})
+			default:
+				_, gvr, err := c.discoveryClient.FindResource(gv, k)
+				if err != nil {
+					logger.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
+					continue
+				}
+				if strings.Contains(gvk, "*") {
+					group := kubeutils.GetGroupFromGVK(gvk)
+					gvrList = append(gvrList, schema.GroupVersionResource{Group: group, Version: "*", Resource: gvr.Resource})
+				} else {
+					logger.V(4).Info("configuring webhook", "GVK", gvk, "GVR", gvr)
+					gvrList = append(gvrList, gvr)
+				}
+			}
+		}
+	}
+	for _, gvr := range gvrList {
+		dst.groups.Insert(gvr.Group)
+		if gvr.Version == "*" {
+			dst.versions = sets.NewString()
+			dst.versions.Insert(gvr.Version)
+		} else if !dst.versions.Has("*") {
+			dst.versions.Insert(gvr.Version)
+		}
+		dst.resources.Insert(gvr.Resource)
+	}
+	if dst.resources.Has("pods") {
+		dst.resources.Insert("pods/ephemeralcontainers")
+	}
+	if dst.resources.Has("services") {
+		dst.resources.Insert("services/status")
+	}
+	spec := policy.GetSpec()
+	if spec.WebhookTimeoutSeconds != nil {
+		if dst.maxWebhookTimeout < *spec.WebhookTimeoutSeconds {
+			dst.maxWebhookTimeout = *spec.WebhookTimeoutSeconds
+		}
+	}
+}
diff --git a/pkg/controllers/webhook/log.go b/pkg/controllers/webhook/log.go
index 8122928705..4bcd1d9e65 100644
--- a/pkg/controllers/webhook/log.go
+++ b/pkg/controllers/webhook/log.go
@@ -1,4 +1,4 @@
-package background
+package webhook
 
 import "sigs.k8s.io/controller-runtime/pkg/log"
 
diff --git a/pkg/controllers/webhook/utils.go b/pkg/controllers/webhook/utils.go
new file mode 100644
index 0000000000..dcecff803b
--- /dev/null
+++ b/pkg/controllers/webhook/utils.go
@@ -0,0 +1,72 @@
+package webhook
+
+import (
+	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/utils"
+	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
+)
+
+// webhook is the instance that aggregates the GVK of existing policies
+// based on kind, failurePolicy and webhookTimeout
+type webhook struct {
+	maxWebhookTimeout int32
+	failurePolicy     admissionregistrationv1.FailurePolicyType
+	groups            sets.String
+	versions          sets.String
+	resources         sets.String
+}
+
+func newWebhook(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType) *webhook {
+	return &webhook{
+		maxWebhookTimeout: timeout,
+		failurePolicy:     failurePolicy,
+		groups:            sets.NewString(),
+		versions:          sets.NewString(),
+		resources:         sets.NewString(),
+	}
+}
+
+func (wh *webhook) buildRuleWithOperations(ops ...admissionregistrationv1.OperationType) admissionregistrationv1.RuleWithOperations {
+	return admissionregistrationv1.RuleWithOperations{
+		Rule: admissionregistrationv1.Rule{
+			APIGroups:   wh.groups.List(),
+			APIVersions: wh.versions.List(),
+			Resources:   wh.resources.List(),
+		},
+		Operations: ops,
+	}
+}
+
+func (wh *webhook) isEmpty() bool {
+	return wh.groups.Len() == 0 || wh.versions.Len() == 0 || wh.resources.Len() == 0
+}
+
+func (wh *webhook) setWildcard() {
+	wh.groups = sets.NewString("*")
+	wh.versions = sets.NewString("*")
+	wh.resources = sets.NewString("*/*")
+}
+
+func hasWildcard(policies ...kyvernov1.PolicyInterface) bool {
+	for _, policy := range policies {
+		spec := policy.GetSpec()
+		for _, rule := range spec.Rules {
+			if kinds := rule.MatchResources.GetKinds(); utils.ContainsString(kinds, "*") {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func objectMeta(name string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
+	return metav1.ObjectMeta{
+		Name: name,
+		Labels: map[string]string{
+			managedByLabel: kyvernov1.ValueKyvernoApp,
+		},
+		OwnerReferences: owner,
+	}
+}
diff --git a/pkg/controllers/webhook/utils_test.go b/pkg/controllers/webhook/utils_test.go
new file mode 100644
index 0000000000..97cb03f12b
--- /dev/null
+++ b/pkg/controllers/webhook/utils_test.go
@@ -0,0 +1,16 @@
+package webhook
+
+import (
+	"testing"
+
+	"gotest.tools/assert"
+	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+)
+
+func Test_webhook_isEmpty(t *testing.T) {
+	empty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore)
+	assert.Equal(t, empty.isEmpty(), true)
+	notEmpty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore)
+	notEmpty.setWildcard()
+	assert.Equal(t, notEmpty.isEmpty(), false)
+}
diff --git a/pkg/utils/runtime/utils.go b/pkg/utils/runtime/utils.go
new file mode 100644
index 0000000000..1367011ee7
--- /dev/null
+++ b/pkg/utils/runtime/utils.go
@@ -0,0 +1,121 @@
+package runtime
+
+import (
+	"time"
+
+	"github.com/go-logr/logr"
+	"github.com/kyverno/kyverno/pkg/config"
+	appsv1 "k8s.io/api/apps/v1"
+	coordinationv1 "k8s.io/api/coordination/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	appsv1informers "k8s.io/client-go/informers/apps/v1"
+	coordinationv1informers "k8s.io/client-go/informers/coordination/v1"
+	appsv1listers "k8s.io/client-go/listers/apps/v1"
+	coordinationv1listers "k8s.io/client-go/listers/coordination/v1"
+)
+
+const (
+	AnnotationLastRequestTime = "kyverno.io/last-request-time"
+	IdleDeadline              = tickerInterval * 5
+	tickerInterval            = 30 * time.Second
+)
+
+type Runtime interface {
+	IsDebug() bool
+	IsReady() bool
+	IsLive() bool
+	IsRollingUpdate() bool
+	IsGoingDown() bool
+}
+
+type runtime struct {
+	serverIP         string
+	leaseLister      coordinationv1listers.LeaseLister
+	deploymentLister appsv1listers.DeploymentLister
+	logger           logr.Logger
+}
+
+func NewRuntime(
+	logger logr.Logger,
+	serverIP string,
+	leaseInformer coordinationv1informers.LeaseInformer,
+	deploymentInformer appsv1informers.DeploymentInformer,
+) Runtime {
+	return &runtime{
+		serverIP:         serverIP,
+		leaseLister:      leaseInformer.Lister(),
+		deploymentLister: deploymentInformer.Lister(),
+		logger:           logger,
+	}
+}
+
+func (c *runtime) IsDebug() bool {
+	return c.serverIP != ""
+}
+
+func (c *runtime) IsLive() bool {
+	return c.IsDebug() || c.check()
+}
+
+func (c *runtime) IsReady() bool {
+	return c.IsDebug() || c.check()
+}
+
+func (c *runtime) IsRollingUpdate() bool {
+	if c.IsDebug() {
+		return false
+	}
+	deployment, err := c.getDeployment()
+	if err != nil {
+		return true
+	}
+	var replicas int32 = 1
+	if deployment.Spec.Replicas != nil {
+		replicas = *deployment.Spec.Replicas
+	}
+	nonTerminatedReplicas := deployment.Status.Replicas
+	if nonTerminatedReplicas > replicas {
+		c.logger.Info("detect Kyverno is in rolling update, won't trigger the update again")
+		return true
+	}
+	return false
+}
+
+func (c *runtime) IsGoingDown() bool {
+	if c.IsDebug() {
+		return false
+	}
+	deployment, err := c.getDeployment()
+	if err != nil {
+		return apierrors.IsNotFound(err)
+	}
+	if deployment.Spec.Replicas != nil {
+		return *deployment.Spec.Replicas == 0
+	}
+	return false
+}
+
+func (c *runtime) getLease() (*coordinationv1.Lease, error) {
+	return c.leaseLister.Leases(config.KyvernoNamespace()).Get("kyverno")
+}
+
+func (c *runtime) getDeployment() (*appsv1.Deployment, error) {
+	return c.deploymentLister.Deployments(config.KyvernoNamespace()).Get("kyverno")
+}
+
+func (c *runtime) check() bool {
+	lease, err := c.getLease()
+	if err != nil {
+		c.logger.Error(err, "failed to get lease")
+		return false
+	}
+	annotations := lease.GetAnnotations()
+	if annotations == nil {
+		return false
+	}
+	annTime, err := time.Parse(time.RFC3339, annotations[AnnotationLastRequestTime])
+	if err != nil {
+		return false
+	}
+	return time.Now().Before(annTime.Add(IdleDeadline))
+}
diff --git a/pkg/webhookconfig/common.go b/pkg/webhookconfig/common.go
deleted file mode 100644
index 03bca9f1bb..0000000000
--- a/pkg/webhookconfig/common.go
+++ /dev/null
@@ -1,383 +0,0 @@
-package webhookconfig
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"reflect"
-	"strings"
-
-	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/config"
-	"github.com/kyverno/kyverno/pkg/metrics"
-	"github.com/kyverno/kyverno/pkg/tls"
-	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	rbacv1 "k8s.io/api/rbac/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-const (
-	managedByLabel string = "webhook.kyverno.io/managed-by"
-)
-
-var (
-	noneOnDryRun = admissionregistrationv1.SideEffectClassNoneOnDryRun
-	never        = admissionregistrationv1.NeverReinvocationPolicy
-	ifNeeded     = admissionregistrationv1.IfNeededReinvocationPolicy
-	policyRule   = admissionregistrationv1.Rule{
-		Resources:   []string{"clusterpolicies/*", "policies/*"},
-		APIGroups:   []string{"kyverno.io"},
-		APIVersions: []string{"v1", "v2beta1"},
-	}
-	verifyRule = admissionregistrationv1.Rule{
-		Resources:   []string{"leases"},
-		APIGroups:   []string{"coordination.k8s.io"},
-		APIVersions: []string{"v1"},
-	}
-	vertifyObjectSelector = &metav1.LabelSelector{
-		MatchLabels: map[string]string{
-			"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
-		},
-	}
-	update       = []admissionregistrationv1.OperationType{admissionregistrationv1.Update}
-	createUpdate = []admissionregistrationv1.OperationType{admissionregistrationv1.Create, admissionregistrationv1.Update}
-	all          = []admissionregistrationv1.OperationType{admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect}
-)
-
-func (wrc *Register) readCaData() []byte {
-	logger := wrc.log.WithName("readCaData")
-	var caData []byte
-	var err error
-	recorder := metrics.NamespacedClientQueryRecorder(wrc.metricsConfig, config.KyvernoNamespace(), "Secret", metrics.KubeClient)
-	secretsClient := metrics.ObjectClient[*corev1.Secret](recorder, wrc.kubeClient.CoreV1().Secrets(config.KyvernoNamespace()))
-	// Check if ca is defined in the secret tls-ca
-	// assume the key and signed cert have been defined in secret tls.kyverno
-	if caData, err = tls.ReadRootCASecret(secretsClient); err == nil {
-		logger.V(4).Info("read CA from secret")
-		return caData
-	}
-
-	logger.V(4).Info("failed to read CA from kubeconfig")
-	return nil
-}
-
-func getHealthyPodsIP(pods []corev1.Pod) []string {
-	var ips []string
-	for _, pod := range pods {
-		if pod.Status.Phase == "Running" {
-			ips = append(ips, pod.Status.PodIP)
-		}
-	}
-	return ips
-}
-
-func (wrc *Register) GetKubePolicyClusterRoleName() (*rbacv1.ClusterRole, error) {
-	selector := &metav1.LabelSelector{
-		MatchLabels: map[string]string{
-			"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
-		},
-	}
-	clusterRoles, err := wrc.kubeClient.RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(selector)})
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientList, metrics.KubeClient, "ClusterRole", "")
-	if err != nil {
-		return nil, err
-	}
-	for _, cr := range clusterRoles.Items {
-		if strings.HasSuffix(cr.GetName(), "webhook") {
-			return &cr, nil
-		}
-	}
-	return nil, errors.New("failed to get cluster role with suffix webhook")
-}
-
-// GetKubePolicyDeployment gets Kyverno deployment using the resource cache
-// it does not initialize any client call
-func (wrc *Register) GetKubePolicyDeployment() (*appsv1.Deployment, error) {
-	deploy, err := wrc.kDeplLister.Deployments(config.KyvernoNamespace()).Get(config.KyvernoDeploymentName())
-	if err != nil {
-		return nil, err
-	}
-	return deploy, nil
-}
-
-func (wrc *Register) constructOwner() metav1.OwnerReference {
-	logger := wrc.log
-	kubeClusterRoleName, err := wrc.GetKubePolicyClusterRoleName()
-	if err != nil {
-		logger.Error(err, "failed to get cluster role")
-		return metav1.OwnerReference{}
-	}
-	return metav1.OwnerReference{
-		APIVersion: config.ClusterRoleAPIVersion,
-		Kind:       config.ClusterRoleKind,
-		Name:       kubeClusterRoleName.GetName(),
-		UID:        kubeClusterRoleName.GetUID(),
-	}
-}
-
-// webhook utils
-
-func generateRules(rule admissionregistrationv1.Rule, operationTypes []admissionregistrationv1.OperationType) []admissionregistrationv1.RuleWithOperations {
-	if !reflect.DeepEqual(rule, admissionregistrationv1.Rule{}) {
-		return []admissionregistrationv1.RuleWithOperations{{Operations: operationTypes, Rule: rule}}
-	}
-	return nil
-}
-
-func generateDebugMutatingWebhook(name, url string, caData []byte, timeoutSeconds int32, rule admissionregistrationv1.Rule, operationTypes []admissionregistrationv1.OperationType, failurePolicy admissionregistrationv1.FailurePolicyType) admissionregistrationv1.MutatingWebhook {
-	return admissionregistrationv1.MutatingWebhook{
-		ReinvocationPolicy: &never,
-		Name:               name,
-		ClientConfig: admissionregistrationv1.WebhookClientConfig{
-			URL:      &url,
-			CABundle: caData,
-		},
-		SideEffects:             &noneOnDryRun,
-		AdmissionReviewVersions: []string{"v1beta1"},
-		TimeoutSeconds:          &timeoutSeconds,
-		FailurePolicy:           &failurePolicy,
-		Rules:                   generateRules(rule, operationTypes),
-	}
-}
-
-func generateDebugValidatingWebhook(name, url string, caData []byte, timeoutSeconds int32, rule admissionregistrationv1.Rule, operationTypes []admissionregistrationv1.OperationType, failurePolicy admissionregistrationv1.FailurePolicyType) admissionregistrationv1.ValidatingWebhook {
-	return admissionregistrationv1.ValidatingWebhook{
-		Name: name,
-		ClientConfig: admissionregistrationv1.WebhookClientConfig{
-			URL:      &url,
-			CABundle: caData,
-		},
-		SideEffects:             &noneOnDryRun,
-		AdmissionReviewVersions: []string{"v1beta1"},
-		TimeoutSeconds:          &timeoutSeconds,
-		FailurePolicy:           &failurePolicy,
-		Rules:                   generateRules(rule, operationTypes),
-	}
-}
-
-func generateMutatingWebhook(name, servicePath string, caData []byte, timeoutSeconds int32, rule admissionregistrationv1.Rule, operationTypes []admissionregistrationv1.OperationType, failurePolicy admissionregistrationv1.FailurePolicyType) admissionregistrationv1.MutatingWebhook {
-	return admissionregistrationv1.MutatingWebhook{
-		ReinvocationPolicy: &ifNeeded,
-		Name:               name,
-		ClientConfig: admissionregistrationv1.WebhookClientConfig{
-			Service: &admissionregistrationv1.ServiceReference{
-				Namespace: config.KyvernoNamespace(),
-				Name:      config.KyvernoServiceName(),
-				Path:      &servicePath,
-			},
-			CABundle: caData,
-		},
-		SideEffects:             &noneOnDryRun,
-		AdmissionReviewVersions: []string{"v1beta1"},
-		TimeoutSeconds:          &timeoutSeconds,
-		FailurePolicy:           &failurePolicy,
-		Rules:                   generateRules(rule, operationTypes),
-	}
-}
-
-func generateValidatingWebhook(name, servicePath string, caData []byte, timeoutSeconds int32, rule admissionregistrationv1.Rule, operationTypes []admissionregistrationv1.OperationType, failurePolicy admissionregistrationv1.FailurePolicyType) admissionregistrationv1.ValidatingWebhook {
-	return admissionregistrationv1.ValidatingWebhook{
-		Name: name,
-		ClientConfig: admissionregistrationv1.WebhookClientConfig{
-			Service: &admissionregistrationv1.ServiceReference{
-				Namespace: config.KyvernoNamespace(),
-				Name:      config.KyvernoServiceName(),
-				Path:      &servicePath,
-			},
-			CABundle: caData,
-		},
-		SideEffects:             &noneOnDryRun,
-		AdmissionReviewVersions: []string{"v1beta1"},
-		TimeoutSeconds:          &timeoutSeconds,
-		FailurePolicy:           &failurePolicy,
-		Rules:                   generateRules(rule, operationTypes),
-	}
-}
-
-func generateObjectMeta(name string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
-	return metav1.ObjectMeta{
-		Name: name,
-		Labels: map[string]string{
-			managedByLabel: kyvernov1.ValueKyvernoApp,
-		},
-		OwnerReferences: owner,
-	}
-}
-
-// policy webhook configuration utils
-
-func getPolicyMutatingWebhookConfigName(serverIP string) string {
-	if serverIP != "" {
-		return config.PolicyMutatingWebhookConfigurationDebugName
-	}
-	return config.PolicyMutatingWebhookConfigurationName
-}
-
-func getPolicyValidatingWebhookConfigName(serverIP string) string {
-	if serverIP != "" {
-		return config.PolicyValidatingWebhookConfigurationDebugName
-	}
-	return config.PolicyValidatingWebhookConfigurationName
-}
-
-func constructPolicyValidatingWebhookConfig(caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.ValidatingWebhookConfiguration {
-	name, path := config.PolicyValidatingWebhookName, config.PolicyValidatingWebhookServicePath
-	return &admissionregistrationv1.ValidatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.PolicyValidatingWebhookConfigurationName, owner),
-		Webhooks: []admissionregistrationv1.ValidatingWebhook{
-			generateValidatingWebhook(name, path, caData, timeoutSeconds, policyRule, createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-}
-
-func constructDebugPolicyValidatingWebhookConfig(serverIP string, caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.ValidatingWebhookConfiguration {
-	name, url := config.PolicyValidatingWebhookName, fmt.Sprintf("https://%s%s", serverIP, config.PolicyValidatingWebhookServicePath)
-	return &admissionregistrationv1.ValidatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.PolicyValidatingWebhookConfigurationDebugName, owner),
-		Webhooks: []admissionregistrationv1.ValidatingWebhook{
-			generateDebugValidatingWebhook(name, url, caData, timeoutSeconds, policyRule, createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-}
-
-func constructPolicyMutatingWebhookConfig(caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, path := config.PolicyMutatingWebhookName, config.PolicyMutatingWebhookServicePath
-	return &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.PolicyMutatingWebhookConfigurationName, owner),
-		Webhooks: []admissionregistrationv1.MutatingWebhook{
-			generateMutatingWebhook(name, path, caData, timeoutSeconds, policyRule, createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-}
-
-func constructDebugPolicyMutatingWebhookConfig(serverIP string, caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, url := config.PolicyMutatingWebhookName, fmt.Sprintf("https://%s%s", serverIP, config.PolicyMutatingWebhookServicePath)
-	return &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.PolicyMutatingWebhookConfigurationDebugName, owner),
-		Webhooks: []admissionregistrationv1.MutatingWebhook{
-			generateDebugMutatingWebhook(name, url, caData, timeoutSeconds, policyRule, createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-}
-
-// resource webhook configuration utils
-
-func getResourceMutatingWebhookConfigName(serverIP string) string {
-	if serverIP != "" {
-		return config.MutatingWebhookConfigurationDebugName
-	}
-	return config.MutatingWebhookConfigurationName
-}
-
-func getResourceValidatingWebhookConfigName(serverIP string) string {
-	if serverIP != "" {
-		return config.ValidatingWebhookConfigurationDebugName
-	}
-	return config.ValidatingWebhookConfigurationName
-}
-
-func defaultResourceWebhookRule(autoUpdate bool) admissionregistrationv1.Rule {
-	if autoUpdate {
-		return admissionregistrationv1.Rule{}
-	}
-	return admissionregistrationv1.Rule{
-		APIGroups:   []string{"*"},
-		APIVersions: []string{"*"},
-		Resources:   []string{"*/*"},
-	}
-}
-
-func constructDefaultDebugMutatingWebhookConfig(serverIP string, caData []byte, timeoutSeconds int32, autoUpdate bool, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, baseUrl := config.MutatingWebhookName, fmt.Sprintf("https://%s%s", serverIP, config.MutatingWebhookServicePath)
-	url := fmt.Sprintf("%s/ignore", baseUrl)
-	webhook := &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.MutatingWebhookConfigurationDebugName, owner),
-		Webhooks: []admissionregistrationv1.MutatingWebhook{
-			generateDebugMutatingWebhook(name+"-ignore", url, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-	if autoUpdate {
-		url := fmt.Sprintf("%s/fail", baseUrl)
-		webhook.Webhooks = append(webhook.Webhooks, generateDebugMutatingWebhook(name+"-fail", url, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), createUpdate, admissionregistrationv1.Fail))
-	}
-	return webhook
-}
-
-func constructDefaultMutatingWebhookConfig(caData []byte, timeoutSeconds int32, autoUpdate bool, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, basePath := config.MutatingWebhookName, config.MutatingWebhookServicePath
-	path := fmt.Sprintf("%s/ignore", basePath)
-	webhook := &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.MutatingWebhookConfigurationName, owner),
-		Webhooks: []admissionregistrationv1.MutatingWebhook{
-			generateMutatingWebhook(name+"-ignore", path, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), createUpdate, admissionregistrationv1.Ignore),
-		},
-	}
-	if autoUpdate {
-		path := fmt.Sprintf("%s/fail", basePath)
-		webhook.Webhooks = append(webhook.Webhooks, generateMutatingWebhook(name+"-fail", path, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), createUpdate, admissionregistrationv1.Fail))
-	}
-	return webhook
-}
-
-func constructDefaultDebugValidatingWebhookConfig(serverIP string, caData []byte, timeoutSeconds int32, autoUpdate bool, owner metav1.OwnerReference) *admissionregistrationv1.ValidatingWebhookConfiguration {
-	name, baseUrl := config.ValidatingWebhookName, fmt.Sprintf("https://%s%s", serverIP, config.ValidatingWebhookServicePath)
-	url := fmt.Sprintf("%s/ignore", baseUrl)
-	webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.ValidatingWebhookConfigurationDebugName, owner),
-		Webhooks: []admissionregistrationv1.ValidatingWebhook{
-			generateDebugValidatingWebhook(name+"-ignore", url, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), all, admissionregistrationv1.Ignore),
-		},
-	}
-	if autoUpdate {
-		url := fmt.Sprintf("%s/fail", baseUrl)
-		webhook.Webhooks = append(webhook.Webhooks, generateDebugValidatingWebhook(name+"-fail", url, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), all, admissionregistrationv1.Fail))
-	}
-	return webhook
-}
-
-func constructDefaultValidatingWebhookConfig(caData []byte, timeoutSeconds int32, autoUpdate bool, owner metav1.OwnerReference) *admissionregistrationv1.ValidatingWebhookConfiguration {
-	name, basePath := config.ValidatingWebhookName, config.ValidatingWebhookServicePath
-	path := fmt.Sprintf("%s/ignore", basePath)
-	webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.ValidatingWebhookConfigurationName, owner),
-		Webhooks: []admissionregistrationv1.ValidatingWebhook{
-			generateValidatingWebhook(name+"-ignore", path, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), all, admissionregistrationv1.Ignore),
-		},
-	}
-	if autoUpdate {
-		path := fmt.Sprintf("%s/fail", basePath)
-		webhook.Webhooks = append(webhook.Webhooks, generateValidatingWebhook(name+"-fail", path, caData, timeoutSeconds, defaultResourceWebhookRule(autoUpdate), all, admissionregistrationv1.Fail))
-	}
-	return webhook
-}
-
-// verify webhook configuration utils
-
-func getVerifyMutatingWebhookConfigName(serverIP string) string {
-	if serverIP != "" {
-		return config.VerifyMutatingWebhookConfigurationDebugName
-	}
-	return config.VerifyMutatingWebhookConfigurationName
-}
-
-func constructVerifyMutatingWebhookConfig(caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, path := config.VerifyMutatingWebhookName, config.VerifyMutatingWebhookServicePath
-	webhook := generateMutatingWebhook(name, path, caData, timeoutSeconds, verifyRule, update, admissionregistrationv1.Ignore)
-	webhook.ObjectSelector = vertifyObjectSelector
-	return &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.VerifyMutatingWebhookConfigurationName, owner),
-		Webhooks:   []admissionregistrationv1.MutatingWebhook{webhook},
-	}
-}
-
-func constructDebugVerifyMutatingWebhookConfig(serverIP string, caData []byte, timeoutSeconds int32, owner metav1.OwnerReference) *admissionregistrationv1.MutatingWebhookConfiguration {
-	name, url := config.VerifyMutatingWebhookName, fmt.Sprintf("https://%s%s", serverIP, config.VerifyMutatingWebhookServicePath)
-	webhook := generateDebugMutatingWebhook(name, url, caData, timeoutSeconds, verifyRule, update, admissionregistrationv1.Ignore)
-	webhook.ObjectSelector = vertifyObjectSelector
-	return &admissionregistrationv1.MutatingWebhookConfiguration{
-		ObjectMeta: generateObjectMeta(config.VerifyMutatingWebhookConfigurationDebugName, owner),
-		Webhooks:   []admissionregistrationv1.MutatingWebhook{webhook},
-	}
-}
diff --git a/pkg/webhookconfig/configmanager.go b/pkg/webhookconfig/configmanager.go
deleted file mode 100644
index 470257b88b..0000000000
--- a/pkg/webhookconfig/configmanager.go
+++ /dev/null
@@ -1,681 +0,0 @@
-package webhookconfig
-
-import (
-	"context"
-	"reflect"
-	"strings"
-	"sync/atomic"
-	"time"
-
-	"github.com/go-logr/logr"
-	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/autogen"
-	"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
-	kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
-	kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/clients/dclient"
-	"github.com/kyverno/kyverno/pkg/config"
-	"github.com/kyverno/kyverno/pkg/metrics"
-	"github.com/kyverno/kyverno/pkg/toggle"
-	"github.com/kyverno/kyverno/pkg/utils"
-	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
-	"github.com/pkg/errors"
-	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/apimachinery/pkg/util/sets"
-	admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
-	"k8s.io/client-go/kubernetes"
-	admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
-	"k8s.io/client-go/tools/cache"
-	"k8s.io/client-go/util/workqueue"
-)
-
-var DefaultWebhookTimeout int32 = 10
-
-// webhookConfigManager manges the webhook configuration dynamically
-// it is NOT multi-thread safe
-type webhookConfigManager struct {
-	// clients
-	discoveryClient dclient.IDiscovery
-	kubeClient      kubernetes.Interface
-	kyvernoClient   versioned.Interface
-
-	// informers
-	pInformer        kyvernov1informers.ClusterPolicyInformer
-	npInformer       kyvernov1informers.PolicyInformer
-	mutateInformer   admissionregistrationv1informers.MutatingWebhookConfigurationInformer
-	validateInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer
-
-	// listers
-	pLister        kyvernov1listers.ClusterPolicyLister
-	npLister       kyvernov1listers.PolicyLister
-	mutateLister   admissionregistrationv1listers.MutatingWebhookConfigurationLister
-	validateLister admissionregistrationv1listers.ValidatingWebhookConfigurationLister
-
-	// queue
-	queue workqueue.RateLimitingInterface
-
-	metricsConfig metrics.MetricsConfigManager
-
-	// serverIP used to get the name of debug webhooks
-	serverIP           string
-	autoUpdateWebhooks bool
-
-	// wildcardPolicy indicates the number of policies that matches all kinds (*) defined
-	wildcardPolicy int64
-
-	createDefaultWebhook chan<- string
-
-	stopCh <-chan struct{}
-
-	log logr.Logger
-}
-
-type manage interface {
-	start()
-}
-
-func newWebhookConfigManager(
-	ctx context.Context,
-	discoveryClient dclient.IDiscovery,
-	kubeClient kubernetes.Interface,
-	kyvernoClient versioned.Interface,
-	pInformer kyvernov1informers.ClusterPolicyInformer,
-	npInformer kyvernov1informers.PolicyInformer,
-	mwcInformer admissionregistrationv1informers.MutatingWebhookConfigurationInformer,
-	vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
-	metricsConfig metrics.MetricsConfigManager,
-	serverIP string,
-	autoUpdateWebhooks bool,
-	createDefaultWebhook chan<- string,
-	log logr.Logger,
-) manage {
-	m := &webhookConfigManager{
-		discoveryClient:      discoveryClient,
-		kyvernoClient:        kyvernoClient,
-		kubeClient:           kubeClient,
-		pInformer:            pInformer,
-		npInformer:           npInformer,
-		mutateInformer:       mwcInformer,
-		validateInformer:     vwcInformer,
-		pLister:              pInformer.Lister(),
-		npLister:             npInformer.Lister(),
-		mutateLister:         mwcInformer.Lister(),
-		validateLister:       vwcInformer.Lister(),
-		metricsConfig:        metricsConfig,
-		queue:                workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "configmanager"),
-		wildcardPolicy:       0,
-		serverIP:             serverIP,
-		autoUpdateWebhooks:   autoUpdateWebhooks,
-		createDefaultWebhook: createDefaultWebhook,
-		stopCh:               ctx.Done(),
-		log:                  log,
-	}
-
-	return m
-}
-
-func (m *webhookConfigManager) handleErr(err error, key interface{}) {
-	logger := m.log
-	if err == nil {
-		m.queue.Forget(key)
-		return
-	}
-	if m.queue.NumRequeues(key) < 3 {
-		logger.Error(err, "failed to sync policy", "key", key)
-		m.queue.AddRateLimited(key)
-		return
-	}
-	utilruntime.HandleError(err)
-	logger.V(2).Info("dropping policy out of queue", "key", key)
-	m.queue.Forget(key)
-}
-
-func (m *webhookConfigManager) addClusterPolicy(obj interface{}) {
-	p := obj.(*kyvernov1.ClusterPolicy)
-	if hasWildcard(&p.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, int64(1))
-	}
-	m.enqueue(p)
-}
-
-func (m *webhookConfigManager) updateClusterPolicy(old, cur interface{}) {
-	oldP, curP := old.(*kyvernov1.ClusterPolicy), cur.(*kyvernov1.ClusterPolicy)
-	if reflect.DeepEqual(oldP.Spec, curP.Spec) {
-		return
-	}
-	if hasWildcard(&oldP.Spec) && !hasWildcard(&curP.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
-	} else if !hasWildcard(&oldP.Spec) && hasWildcard(&curP.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, int64(1))
-	}
-	m.enqueue(curP)
-}
-
-func (m *webhookConfigManager) deleteClusterPolicy(obj interface{}) {
-	p, ok := kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.ClusterPolicy)
-	if !ok {
-		// utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
-		m.log.V(2).Info("Failed to get deleted object", "obj", obj)
-		return
-	}
-	if hasWildcard(&p.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
-	}
-	m.enqueue(p)
-}
-
-func (m *webhookConfigManager) addPolicy(obj interface{}) {
-	p := obj.(*kyvernov1.Policy)
-	if hasWildcard(&p.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, int64(1))
-	}
-	m.enqueue(p)
-}
-
-func (m *webhookConfigManager) updatePolicy(old, cur interface{}) {
-	oldP, curP := old.(*kyvernov1.Policy), cur.(*kyvernov1.Policy)
-	if reflect.DeepEqual(oldP.Spec, curP.Spec) {
-		return
-	}
-	if hasWildcard(&oldP.Spec) && !hasWildcard(&curP.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
-	} else if !hasWildcard(&oldP.Spec) && hasWildcard(&curP.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, int64(1))
-	}
-	m.enqueue(curP)
-}
-
-func (m *webhookConfigManager) deletePolicy(obj interface{}) {
-	p, ok := kubeutils.GetObjectWithTombstone(obj).(*kyvernov1.Policy)
-	if !ok {
-		// utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
-		m.log.V(2).Info("Failed to get deleted object", "obj", obj)
-		return
-	}
-	if hasWildcard(&p.Spec) {
-		atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
-	}
-	m.enqueue(p)
-}
-
-func (m *webhookConfigManager) deleteMutatingWebhook(obj interface{}) {
-	m.log.WithName("deleteMutatingWebhook").Info("resource webhook configuration was deleted, recreating...")
-	webhook, ok := kubeutils.GetObjectWithTombstone(obj).(*admissionregistrationv1.MutatingWebhookConfiguration)
-	if !ok {
-		m.log.V(2).Info("Failed to get deleted object", "obj", obj)
-		return
-	}
-	if webhook.GetName() == config.MutatingWebhookConfigurationName {
-		m.enqueueAllPolicies()
-	}
-}
-
-func (m *webhookConfigManager) deleteValidatingWebhook(obj interface{}) {
-	m.log.WithName("deleteMutatingWebhook").Info("resource webhook configuration was deleted, recreating...")
-	webhook, ok := kubeutils.GetObjectWithTombstone(obj).(*admissionregistrationv1.ValidatingWebhookConfiguration)
-	if !ok {
-		m.log.V(2).Info("Failed to get deleted object", "obj", obj)
-		return
-	}
-	if webhook.GetName() == config.ValidatingWebhookConfigurationName {
-		m.enqueueAllPolicies()
-	}
-}
-
-func (m *webhookConfigManager) enqueueAllPolicies() {
-	logger := m.log.WithName("enqueueAllPolicies")
-	policies, err := m.listAllPolicies()
-	if err != nil {
-		logger.Error(err, "unable to list policies")
-	}
-	for _, policy := range policies {
-		m.enqueue(policy)
-		logger.V(4).Info("added policy to the queue", "namespace", policy.GetNamespace(), "name", policy.GetName())
-	}
-}
-
-func (m *webhookConfigManager) enqueue(policy interface{}) {
-	logger := m.log
-	key, err := cache.MetaNamespaceKeyFunc(policy)
-	if err != nil {
-		logger.Error(err, "failed to enqueue policy")
-		return
-	}
-	m.queue.Add(key)
-}
-
-// start is a blocking call to configure webhook
-func (m *webhookConfigManager) start() {
-	defer utilruntime.HandleCrash()
-	defer m.queue.ShutDown()
-
-	m.log.V(2).Info("starting")
-	defer m.log.V(2).Info("shutting down")
-
-	m.pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		AddFunc:    m.addClusterPolicy,
-		UpdateFunc: m.updateClusterPolicy,
-		DeleteFunc: m.deleteClusterPolicy,
-	})
-
-	m.npInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		AddFunc:    m.addPolicy,
-		UpdateFunc: m.updatePolicy,
-		DeleteFunc: m.deletePolicy,
-	})
-
-	m.mutateInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		DeleteFunc: m.deleteMutatingWebhook,
-	})
-
-	m.validateInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		DeleteFunc: m.deleteValidatingWebhook,
-	})
-
-	for m.processNextWorkItem() {
-	}
-}
-
-func (m *webhookConfigManager) processNextWorkItem() bool {
-	key, quit := m.queue.Get()
-	if quit {
-		return false
-	}
-	defer m.queue.Done(key)
-	err := m.sync(key.(string))
-	m.handleErr(err, key)
-	return true
-}
-
-func (m *webhookConfigManager) sync(key string) error {
-	logger := m.log.WithName("sync")
-	startTime := time.Now()
-	logger.V(4).Info("started syncing policy", "key", key, "startTime", startTime)
-	defer func() {
-		logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String())
-	}()
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		logger.Info("invalid resource key", "key", key)
-		return nil
-	}
-	return m.reconcileWebhook(namespace, name)
-}
-
-func (m *webhookConfigManager) reconcileWebhook(namespace, name string) error {
-	logger := m.log.WithName("reconcileWebhook").WithValues("namespace", namespace, "policy", name)
-
-	_, err := m.getPolicy(namespace, name)
-	isDeleted := apierrors.IsNotFound(err)
-	if err != nil && !isDeleted {
-		return errors.Wrapf(err, "unable to get policy object %s/%s", namespace, name)
-	}
-
-	ready := true
-	var updateErr error
-	// build webhook only if auto-update is enabled, otherwise directly update status to ready
-	if m.autoUpdateWebhooks {
-		webhooks, err := m.buildWebhooks(namespace)
-		if err != nil {
-			return err
-		}
-
-		if err := m.updateWebhookConfig(webhooks); err != nil {
-			ready = false
-			updateErr = errors.Wrapf(err, "failed to update webhook configurations for policy")
-		}
-
-		// DELETION of the policy
-		if isDeleted {
-			return nil
-		}
-	}
-
-	if err := m.updateStatus(namespace, name, ready); err != nil {
-		return errors.Wrapf(err, "failed to update policy status %s/%s", namespace, name)
-	}
-
-	if ready {
-		logger.Info("policy is ready to serve admission requests")
-	}
-	return updateErr
-}
-
-func (m *webhookConfigManager) getPolicy(namespace, name string) (kyvernov1.PolicyInterface, error) {
-	if namespace == "" {
-		return m.pLister.Get(name)
-	} else {
-		return m.npLister.Policies(namespace).Get(name)
-	}
-}
-
-func (m *webhookConfigManager) listAllPolicies() ([]kyvernov1.PolicyInterface, error) {
-	policies := []kyvernov1.PolicyInterface{}
-	polList, err := m.npLister.Policies(metav1.NamespaceAll).List(labels.Everything())
-	if err != nil {
-		return nil, errors.Wrapf(err, "failed to list Policy")
-	}
-	for _, p := range polList {
-		policies = append(policies, p)
-	}
-	cpolList, err := m.pLister.List(labels.Everything())
-	if err != nil {
-		return nil, errors.Wrapf(err, "failed to list ClusterPolicy")
-	}
-	for _, p := range cpolList {
-		policies = append(policies, p)
-	}
-	return policies, nil
-}
-
-func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook, err error) {
-	mutateIgnore := newWebhook(kindMutating, DefaultWebhookTimeout, kyvernov1.Ignore)
-	mutateFail := newWebhook(kindMutating, DefaultWebhookTimeout, kyvernov1.Fail)
-	validateIgnore := newWebhook(kindValidating, DefaultWebhookTimeout, kyvernov1.Ignore)
-	validateFail := newWebhook(kindValidating, DefaultWebhookTimeout, kyvernov1.Fail)
-
-	if atomic.LoadInt64(&m.wildcardPolicy) != 0 {
-		for _, w := range []*webhook{mutateIgnore, mutateFail, validateIgnore, validateFail} {
-			setWildcardConfig(w)
-		}
-
-		m.log.V(4).WithName("buildWebhooks").Info("warning: found wildcard policy, setting webhook configurations to accept admission requests of all kinds")
-		return append(res, mutateIgnore, mutateFail, validateIgnore, validateFail), nil
-	}
-
-	policies, err := m.listAllPolicies()
-	if err != nil {
-		return nil, errors.Wrap(err, "unable to list current policies")
-	}
-
-	for _, p := range policies {
-		spec := p.GetSpec()
-		if spec.HasValidate() || spec.HasGenerate() || spec.HasMutate() || spec.HasImagesValidationChecks() || spec.HasYAMLSignatureVerify() {
-			if spec.GetFailurePolicy() == kyvernov1.Ignore {
-				m.mergeWebhook(validateIgnore, p, true)
-			} else {
-				m.mergeWebhook(validateFail, p, true)
-			}
-		}
-
-		if spec.HasMutate() || spec.HasVerifyImages() {
-			if spec.GetFailurePolicy() == kyvernov1.Ignore {
-				m.mergeWebhook(mutateIgnore, p, false)
-			} else {
-				m.mergeWebhook(mutateFail, p, false)
-			}
-		}
-	}
-
-	res = append(res, mutateIgnore, mutateFail, validateIgnore, validateFail)
-	return res, nil
-}
-
-func (m *webhookConfigManager) updateWebhookConfig(webhooks []*webhook) error {
-	logger := m.log.WithName("updateWebhookConfig")
-
-	webhooksMap := map[string]*webhook{}
-	for _, w := range webhooks {
-		webhooksMap[webhookKey(w.kind, string(w.failurePolicy))] = w
-	}
-
-	var errs []string
-	if err := m.updateMutatingWebhookConfiguration(getResourceMutatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
-		logger.V(4).Info("failed to update mutatingwebhookconfigurations", "error", err.Error())
-		errs = append(errs, err.Error())
-	}
-
-	if err := m.updateValidatingWebhookConfiguration(getResourceValidatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
-		logger.V(4).Info("failed to update validatingwebhookconfigurations", "error", err.Error())
-		errs = append(errs, err.Error())
-	}
-
-	if len(errs) != 0 {
-		return errors.New(strings.Join(errs, "\n"))
-	}
-
-	return nil
-}
-
-func (m *webhookConfigManager) updateMutatingWebhookConfiguration(webhookName string, webhooksMap map[string]*webhook) error {
-	logger := m.log.WithName("updateMutatingWebhookConfiduration").WithValues("name", webhookName)
-	resourceWebhook, err := m.mutateLister.Get(webhookName)
-	if err != nil && !apierrors.IsNotFound(err) {
-		return errors.Wrapf(err, "unable to get %s/%s", kindMutating, webhookName)
-	} else if apierrors.IsNotFound(err) {
-		m.createDefaultWebhook <- kindMutating
-		return err
-	}
-	for i := range resourceWebhook.Webhooks {
-		newWebhook := webhooksMap[webhookKey(kindMutating, string(*resourceWebhook.Webhooks[i].FailurePolicy))]
-		if newWebhook == nil || newWebhook.isEmpty() {
-			resourceWebhook.Webhooks[i].Rules = []admissionregistrationv1.RuleWithOperations{}
-		} else {
-			resourceWebhook.Webhooks[i].TimeoutSeconds = &newWebhook.maxWebhookTimeout
-			resourceWebhook.Webhooks[i].Rules = []admissionregistrationv1.RuleWithOperations{
-				newWebhook.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete),
-			}
-		}
-	}
-	if _, err := m.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(context.TODO(), resourceWebhook, metav1.UpdateOptions{}); err != nil {
-		m.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindMutating, "")
-		return errors.Wrapf(err, "unable to update: %s", resourceWebhook.GetName())
-	}
-	logger.V(4).Info("successfully updated the webhook configuration")
-	return nil
-}
-
-func (m *webhookConfigManager) updateValidatingWebhookConfiguration(webhookName string, webhooksMap map[string]*webhook) error {
-	logger := m.log.WithName("updateMutatingWebhookConfiduration").WithValues("name", webhookName)
-	resourceWebhook, err := m.validateLister.Get(webhookName)
-	if err != nil && !apierrors.IsNotFound(err) {
-		return errors.Wrapf(err, "unable to get %s/%s", kindValidating, webhookName)
-	} else if apierrors.IsNotFound(err) {
-		m.createDefaultWebhook <- kindValidating
-		return err
-	}
-	for i := range resourceWebhook.Webhooks {
-		newWebhook := webhooksMap[webhookKey(kindValidating, string(*resourceWebhook.Webhooks[i].FailurePolicy))]
-		if newWebhook == nil || newWebhook.isEmpty() {
-			resourceWebhook.Webhooks[i].Rules = []admissionregistrationv1.RuleWithOperations{}
-		} else {
-			resourceWebhook.Webhooks[i].TimeoutSeconds = &newWebhook.maxWebhookTimeout
-			resourceWebhook.Webhooks[i].Rules = []admissionregistrationv1.RuleWithOperations{
-				newWebhook.buildRuleWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
-			}
-		}
-	}
-	if _, err := m.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Update(context.TODO(), resourceWebhook, metav1.UpdateOptions{}); err != nil {
-		m.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindValidating, "")
-		return errors.Wrapf(err, "unable to update: %s", resourceWebhook.GetName())
-	}
-	logger.V(4).Info("successfully updated the webhook configuration")
-	return nil
-}
-
-func (m *webhookConfigManager) updateStatus(namespace, name string, ready bool) error {
-	update := func(meta *metav1.ObjectMeta, p kyvernov1.PolicyInterface, status *kyvernov1.PolicyStatus) bool {
-		copy := status.DeepCopy()
-		status.SetReady(ready)
-		if toggle.AutogenInternals.Enabled() {
-			var rules []kyvernov1.Rule
-			for _, rule := range autogen.ComputeRules(p) {
-				if strings.HasPrefix(rule.Name, "autogen-") {
-					rules = append(rules, rule)
-				}
-			}
-			status.Autogen.Rules = rules
-		} else {
-			status.Autogen.Rules = nil
-		}
-		return !reflect.DeepEqual(status, copy)
-	}
-	if namespace == "" {
-		p, err := m.pLister.Get(name)
-		if err != nil {
-			return err
-		}
-		if update(&p.ObjectMeta, p, &p.Status) {
-			if _, err := m.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
-				return err
-			}
-		}
-	} else {
-		p, err := m.npLister.Policies(namespace).Get(name)
-		if err != nil {
-			return err
-		}
-		if update(&p.ObjectMeta, p, &p.Status) {
-			if _, err := m.kyvernoClient.KyvernoV1().Policies(namespace).UpdateStatus(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
-				return err
-			}
-		}
-	}
-	return nil
-}
-
-// webhook is the instance that aggregates the GVK of existing policies
-// based on kind, failurePolicy and webhookTimeout
-type webhook struct {
-	kind              string
-	maxWebhookTimeout int32
-	failurePolicy     kyvernov1.FailurePolicyType
-	groups            sets.String
-	versions          sets.String
-	resources         sets.String
-}
-
-func (wh *webhook) buildRuleWithOperations(ops ...admissionregistrationv1.OperationType) admissionregistrationv1.RuleWithOperations {
-	return admissionregistrationv1.RuleWithOperations{
-		Rule: admissionregistrationv1.Rule{
-			APIGroups:   wh.groups.List(),
-			APIVersions: wh.versions.List(),
-			Resources:   wh.resources.List(),
-		},
-		Operations: ops,
-	}
-}
-
-func (wh *webhook) isEmpty() bool {
-	return wh.groups.Len() == 0 || wh.versions.Len() == 0 || wh.resources.Len() == 0
-}
-
-// mergeWebhook merges the matching kinds of the policy to webhook.rule
-func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyvernov1.PolicyInterface, updateValidate bool) {
-	matchedGVK := make([]string, 0)
-	for _, rule := range autogen.ComputeRules(policy) {
-		// matching kinds in generate policies need to be added to both webhook
-		if rule.HasGenerate() {
-			matchedGVK = append(matchedGVK, rule.MatchResources.GetKinds()...)
-			matchedGVK = append(matchedGVK, rule.Generation.ResourceSpec.Kind)
-			continue
-		}
-
-		if (updateValidate && rule.HasValidate() || rule.HasImagesValidationChecks()) ||
-			(updateValidate && rule.HasMutate() && rule.IsMutateExisting()) ||
-			(!updateValidate && rule.HasMutate()) && !rule.IsMutateExisting() ||
-			(!updateValidate && rule.HasVerifyImages()) || (!updateValidate && rule.HasYAMLSignatureVerify()) {
-			matchedGVK = append(matchedGVK, rule.MatchResources.GetKinds()...)
-		}
-	}
-
-	gvkMap := make(map[string]int)
-	gvrList := make([]schema.GroupVersionResource, 0)
-	for _, gvk := range matchedGVK {
-		if _, ok := gvkMap[gvk]; !ok {
-			gvkMap[gvk] = 1
-
-			// note: webhook stores GVR in its rules while policy stores GVK in its rules definition
-			gv, k := kubeutils.GetKindFromGVK(gvk)
-			switch k {
-			case "Binding":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/binding"})
-			case "NodeProxyOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes/proxy"})
-			case "PodAttachOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/attach"})
-			case "PodExecOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/exec"})
-			case "PodPortForwardOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/portforward"})
-			case "PodProxyOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods/proxy"})
-			case "ServiceProxyOptions":
-				gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services/proxy"})
-			default:
-				_, gvr, err := m.discoveryClient.FindResource(gv, k)
-				if err != nil {
-					m.log.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
-					continue
-				}
-				if strings.Contains(gvk, "*") {
-					group := kubeutils.GetGroupFromGVK(gvk)
-					gvrList = append(gvrList, schema.GroupVersionResource{Group: group, Version: "*", Resource: gvr.Resource})
-				} else {
-					m.log.V(4).Info("configuring webhook", "GVK", gvk, "GVR", gvr)
-					gvrList = append(gvrList, gvr)
-				}
-			}
-		}
-	}
-
-	for _, gvr := range gvrList {
-		dst.groups.Insert(gvr.Group)
-		if gvr.Version == "*" {
-			dst.versions = sets.NewString()
-			dst.versions.Insert(gvr.Version)
-		} else if !dst.versions.Has("*") {
-			dst.versions.Insert(gvr.Version)
-		}
-		dst.resources.Insert(gvr.Resource)
-	}
-
-	if dst.resources.Has("pods") {
-		dst.resources.Insert("pods/ephemeralcontainers")
-	}
-	if dst.resources.Has("services") {
-		dst.resources.Insert("services/status")
-	}
-
-	spec := policy.GetSpec()
-	if spec.WebhookTimeoutSeconds != nil {
-		if dst.maxWebhookTimeout < *spec.WebhookTimeoutSeconds {
-			dst.maxWebhookTimeout = *spec.WebhookTimeoutSeconds
-		}
-	}
-}
-
-func newWebhook(kind string, timeout int32, failurePolicy kyvernov1.FailurePolicyType) *webhook {
-	return &webhook{
-		kind:              kind,
-		maxWebhookTimeout: timeout,
-		failurePolicy:     failurePolicy,
-		groups:            sets.NewString(),
-		versions:          sets.NewString(),
-		resources:         sets.NewString(),
-	}
-}
-
-func webhookKey(webhookKind, failurePolicy string) string {
-	return strings.Join([]string{webhookKind, failurePolicy}, "/")
-}
-
-func hasWildcard(spec *kyvernov1.Spec) bool {
-	for _, rule := range spec.Rules {
-		if kinds := rule.MatchResources.GetKinds(); utils.ContainsString(kinds, "*") {
-			return true
-		}
-	}
-	return false
-}
-
-func setWildcardConfig(w *webhook) {
-	w.groups = sets.NewString("*")
-	w.versions = sets.NewString("*")
-	w.resources = sets.NewString("*/*")
-}
diff --git a/pkg/webhookconfig/configmanager_test.go b/pkg/webhookconfig/configmanager_test.go
deleted file mode 100644
index ee73fc1295..0000000000
--- a/pkg/webhookconfig/configmanager_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package webhookconfig
-
-import (
-	"testing"
-
-	kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
-	"gotest.tools/assert"
-)
-
-func Test_webhook_isEmpty(t *testing.T) {
-	empty := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
-	assert.Equal(t, empty.isEmpty(), true)
-	notEmpty := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
-	setWildcardConfig(notEmpty)
-	assert.Equal(t, notEmpty.isEmpty(), false)
-}
diff --git a/pkg/webhookconfig/monitor.go b/pkg/webhookconfig/monitor.go
deleted file mode 100644
index baf0b50f84..0000000000
--- a/pkg/webhookconfig/monitor.go
+++ /dev/null
@@ -1,235 +0,0 @@
-package webhookconfig
-
-import (
-	"context"
-	"fmt"
-	"sync"
-	"time"
-
-	"github.com/go-logr/logr"
-	"github.com/kyverno/kyverno/pkg/config"
-	"github.com/kyverno/kyverno/pkg/event"
-	"github.com/kyverno/kyverno/pkg/tls"
-	"github.com/pkg/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-	coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
-)
-
-// maxRetryCount defines the max deadline count
-const (
-	tickerInterval    time.Duration = 30 * time.Second
-	idleCheckInterval time.Duration = 60 * time.Second
-	idleDeadline      time.Duration = idleCheckInterval * 5
-)
-
-// Monitor stores the last webhook request time and monitors registered webhooks.
-//
-// If a webhook is not received in the idleCheckInterval the monitor triggers a
-// change in the Kyverno deployment to force a webhook request. If no requests
-// are received after idleDeadline the webhooks are deleted and re-registered.
-//
-// Each instance has an in-memory flag lastSeenRequestTime, recording the last
-// received admission timestamp by the current instance. And the latest timestamp
-// (latestTimestamp) is recorded in the annotation of the Kyverno deployment,
-// this annotation could be updated by any instance. If the duration from
-// latestTimestamp is longer than idleCheckInterval, the monitor triggers an
-// annotation update; otherwise lastSeenRequestTime is updated to latestTimestamp.
-//
-// Webhook configurations are checked every tickerInterval across all instances.
-// Currently the check only queries for the expected resource name, and does
-// not compare other details like the webhook settings.
-type Monitor struct {
-	// leaseClient is used to manage Kyverno lease
-	leaseClient coordinationv1.LeaseInterface
-
-	// lastSeenRequestTime records the timestamp
-	// of the latest received admission request
-	lastSeenRequestTime time.Time
-	mu                  sync.RWMutex
-
-	log logr.Logger
-}
-
-// NewMonitor returns a new instance of webhook monitor
-func NewMonitor(kubeClient kubernetes.Interface, log logr.Logger) (*Monitor, error) {
-	monitor := &Monitor{
-		leaseClient:         kubeClient.CoordinationV1().Leases(config.KyvernoNamespace()),
-		lastSeenRequestTime: time.Now(),
-		log:                 log,
-	}
-
-	return monitor, nil
-}
-
-// Time returns the last request time
-func (t *Monitor) Time() time.Time {
-	t.mu.RLock()
-	defer t.mu.RUnlock()
-	return t.lastSeenRequestTime
-}
-
-// SetTime updates the last request time
-func (t *Monitor) SetTime(tm time.Time) {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-
-	t.lastSeenRequestTime = tm
-}
-
-// Run runs the checker and verify the resource update
-func (t *Monitor) Run(ctx context.Context, register *Register, certRenewer *tls.CertRenewer, eventGen event.Interface) {
-	logger := t.log.WithName("webhookMonitor")
-
-	logger.V(3).Info("starting webhook monitor", "interval", idleCheckInterval.String())
-	status := newStatusControl(t.leaseClient, eventGen, logger.WithName("WebhookStatusControl"))
-
-	ticker := time.NewTicker(tickerInterval)
-	defer ticker.Stop()
-
-	createDefaultWebhook := register.createDefaultWebhook
-	for {
-		select {
-		case webhookKind := <-createDefaultWebhook:
-			logger.Info("received recreation request for resource webhook")
-			if webhookKind == kindMutating {
-				err := register.createResourceMutatingWebhookConfiguration(register.readCaData())
-				if err != nil {
-					logger.Error(err, "failed to create default MutatingWebhookConfiguration for resources, the webhook will be reconciled", "interval", tickerInterval)
-				}
-			} else if webhookKind == kindValidating {
-				err := register.createResourceValidatingWebhookConfiguration(register.readCaData())
-				if err != nil {
-					logger.Error(err, "failed to create default ValidatingWebhookConfiguration for resources, the webhook will be reconciled", "interval", tickerInterval)
-				}
-			}
-
-		case <-ticker.C:
-
-			err := registerWebhookIfNotPresent(register, t.log.WithName("registerWebhookIfNotPresent"))
-			if err != nil {
-				t.log.Error(err, "")
-			}
-
-			// update namespaceSelector every 30 seconds
-			go func() {
-				if register.autoUpdateWebhooks {
-					select {
-					case register.UpdateWebhookChan <- true:
-						logger.V(4).Info("updating webhook configurations for namespaceSelector with latest kyverno ConfigMap")
-					default:
-						logger.V(4).Info("skipped sending update webhook signal as the channel was blocking")
-					}
-				}
-			}()
-
-			timeDiff := time.Since(t.Time())
-			lastRequestTimeFromAnn := lastRequestTimeFromAnnotation(t.leaseClient, t.log.WithName("lastRequestTimeFromAnnotation"))
-			if lastRequestTimeFromAnn == nil {
-				if err := status.UpdateLastRequestTimestmap(t.Time()); err != nil {
-					logger.Error(err, "failed to annotate deployment for lastRequestTime")
-				} else {
-					logger.Info("initialized lastRequestTimestamp", "time", t.Time())
-				}
-				continue
-			}
-
-			switch {
-			case timeDiff > idleDeadline:
-				err := fmt.Errorf("webhook hasn't received requests in %v, updating Kyverno to verify webhook status", idleDeadline.String())
-				logger.Error(err, "webhook check failed", "time", t.Time(), "lastRequestTimestamp", lastRequestTimeFromAnn)
-
-				// update deployment to renew lastSeenRequestTime
-				if err := status.failure(); err != nil {
-					logger.Error(err, "failed to annotate deployment webhook status to failure")
-
-					if err := register.Register(); err != nil {
-						logger.Error(err, "Failed to register webhooks")
-					}
-				}
-
-				continue
-
-			case timeDiff > 2*idleCheckInterval:
-				if skipWebhookCheck(register, logger.WithName("skipWebhookCheck")) {
-					logger.Info("skip validating webhook status, Kyverno is in rolling update")
-					continue
-				}
-
-				if t.Time().Before(*lastRequestTimeFromAnn) {
-					t.SetTime(*lastRequestTimeFromAnn)
-					logger.V(3).Info("updated in-memory timestamp", "time", lastRequestTimeFromAnn)
-				}
-			}
-
-			idleT := time.Since(*lastRequestTimeFromAnn)
-			if idleT > idleCheckInterval {
-				if t.Time().After(*lastRequestTimeFromAnn) {
-					logger.V(3).Info("updating annotation lastRequestTimestamp with the latest in-memory timestamp", "time", t.Time(), "lastRequestTimestamp", lastRequestTimeFromAnn)
-					if err := status.UpdateLastRequestTimestmap(t.Time()); err != nil {
-						logger.Error(err, "failed to update lastRequestTimestamp annotation")
-					}
-				}
-			}
-
-			// if the status was false before then we update it to true
-			// send request to update the Kyverno deployment
-			if err := status.success(); err != nil {
-				logger.Error(err, "failed to annotate deployment webhook status to success")
-			}
-
-		case <-ctx.Done():
-			// handler termination signal
-			logger.V(2).Info("stopping webhook monitor")
-			return
-		}
-	}
-}
-
-func registerWebhookIfNotPresent(register *Register, logger logr.Logger) error {
-	if skipWebhookCheck(register, logger.WithName("skipWebhookCheck")) {
-		logger.Info("skip validating webhook status, Kyverno is in rolling update")
-		return nil
-	}
-
-	if err := register.Check(); err != nil {
-		logger.Error(err, "missing webhooks")
-
-		if err := register.Register(); err != nil {
-			return errors.Wrap(err, "failed to register webhooks")
-		}
-	}
-
-	return nil
-}
-
-func lastRequestTimeFromAnnotation(leaseClient coordinationv1.LeaseInterface, logger logr.Logger) *time.Time {
-	lease, err := leaseClient.Get(context.TODO(), "kyverno", metav1.GetOptions{})
-	if err != nil {
-		logger.Info("Lease 'kyverno' not found. Starting clean-up...")
-	}
-
-	timeStamp := lease.GetAnnotations()
-	if timeStamp == nil {
-		logger.Info("timestamp not set in the annotation, setting")
-		return nil
-	}
-
-	annTime, err := time.Parse(time.RFC3339, timeStamp[annLastRequestTime])
-	if err != nil {
-		logger.Error(err, "failed to parse timestamp annotation", "timeStamp", timeStamp[annLastRequestTime])
-		return nil
-	}
-
-	return &annTime
-}
-
-// skipWebhookCheck returns true if Kyverno is in rolling update
-func skipWebhookCheck(register *Register, logger logr.Logger) bool {
-	deploy, err := register.GetKubePolicyDeployment()
-	if err != nil {
-		logger.Info("unable to get Kyverno deployment", "reason", err.Error())
-		return false
-	}
-	return tls.IsKyvernoInRollingUpdate(deploy)
-}
diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go
deleted file mode 100644
index 71564ce6f5..0000000000
--- a/pkg/webhookconfig/registration.go
+++ /dev/null
@@ -1,653 +0,0 @@
-package webhookconfig
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"reflect"
-	"strings"
-	"sync"
-	"time"
-
-	"github.com/go-logr/logr"
-	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
-	kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/clients/dclient"
-	"github.com/kyverno/kyverno/pkg/config"
-	"github.com/kyverno/kyverno/pkg/metrics"
-	tlsutils "github.com/kyverno/kyverno/pkg/tls"
-	"github.com/kyverno/kyverno/pkg/utils"
-	"github.com/pkg/errors"
-	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
-	errorsapi "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
-	appsv1informers "k8s.io/client-go/informers/apps/v1"
-	"k8s.io/client-go/kubernetes"
-	admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
-	appsv1listers "k8s.io/client-go/listers/apps/v1"
-	rest "k8s.io/client-go/rest"
-)
-
-const (
-	kindMutating   string = "MutatingWebhookConfiguration"
-	kindValidating string = "ValidatingWebhookConfiguration"
-)
-
-// Register manages webhook registration. There are five webhooks:
-// 1. Policy Validation
-// 2. Policy Mutation
-// 3. Resource Validation
-// 4. Resource Mutation
-// 5. Webhook Status Mutation
-type Register struct {
-	// clients
-	kubeClient    kubernetes.Interface
-	kyvernoClient versioned.Interface
-	clientConfig  *rest.Config
-
-	// listers
-	mwcLister   admissionregistrationv1listers.MutatingWebhookConfigurationLister
-	vwcLister   admissionregistrationv1listers.ValidatingWebhookConfigurationLister
-	kDeplLister appsv1listers.DeploymentLister
-
-	metricsConfig metrics.MetricsConfigManager
-
-	// channels
-	stopCh               <-chan struct{}
-	UpdateWebhookChan    chan bool
-	createDefaultWebhook chan string
-
-	serverIP           string // when running outside a cluster
-	timeoutSeconds     int32
-	log                logr.Logger
-	autoUpdateWebhooks bool
-
-	// manage implements methods to manage webhook configurations
-	manage
-}
-
-// NewRegister creates new Register instance
-func NewRegister(
-	ctx context.Context,
-	clientConfig *rest.Config,
-	client dclient.Interface,
-	kubeClient kubernetes.Interface,
-	kyvernoClient versioned.Interface,
-	mwcInformer admissionregistrationv1informers.MutatingWebhookConfigurationInformer,
-	vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
-	kDeplInformer appsv1informers.DeploymentInformer,
-	pInformer kyvernov1informers.ClusterPolicyInformer,
-	npInformer kyvernov1informers.PolicyInformer,
-	metricsConfig metrics.MetricsConfigManager,
-	serverIP string,
-	webhookTimeout int32,
-	autoUpdateWebhooks bool,
-	log logr.Logger,
-) *Register {
-	register := &Register{
-		clientConfig:         clientConfig,
-		kubeClient:           kubeClient,
-		kyvernoClient:        kyvernoClient,
-		mwcLister:            mwcInformer.Lister(),
-		vwcLister:            vwcInformer.Lister(),
-		kDeplLister:          kDeplInformer.Lister(),
-		metricsConfig:        metricsConfig,
-		UpdateWebhookChan:    make(chan bool),
-		createDefaultWebhook: make(chan string),
-		stopCh:               ctx.Done(),
-		serverIP:             serverIP,
-		timeoutSeconds:       webhookTimeout,
-		log:                  log.WithName("Register"),
-		autoUpdateWebhooks:   autoUpdateWebhooks,
-	}
-
-	register.manage = newWebhookConfigManager(
-		ctx,
-		client.Discovery(),
-		kubeClient,
-		kyvernoClient,
-		pInformer,
-		npInformer,
-		mwcInformer,
-		vwcInformer,
-		metricsConfig,
-		serverIP,
-		register.autoUpdateWebhooks,
-		register.createDefaultWebhook,
-		log.WithName("WebhookConfigManager"),
-	)
-
-	return register
-}
-
-// Register clean up the old webhooks and re-creates admission webhooks configs on cluster
-func (wrc *Register) Register() error {
-	logger := wrc.log
-	if wrc.serverIP != "" {
-		logger.Info("Registering webhook", "url", fmt.Sprintf("https://%s", wrc.serverIP))
-	} else {
-		if err := wrc.checkEndpoint(); err != nil {
-			return err
-		}
-	}
-	caData := wrc.readCaData()
-	if caData == nil {
-		return errors.New("Unable to extract CA data from configuration")
-	}
-	var errors []string
-	if err := wrc.createVerifyMutatingWebhookConfiguration(caData); err != nil {
-		errors = append(errors, err.Error())
-	}
-	if err := wrc.createPolicyValidatingWebhookConfiguration(caData); err != nil {
-		errors = append(errors, err.Error())
-	}
-	if err := wrc.createPolicyMutatingWebhookConfiguration(caData); err != nil {
-		errors = append(errors, err.Error())
-	}
-	if err := wrc.createResourceValidatingWebhookConfiguration(caData); err != nil {
-		errors = append(errors, err.Error())
-	}
-	if err := wrc.createResourceMutatingWebhookConfiguration(caData); err != nil {
-		errors = append(errors, err.Error())
-	}
-	if len(errors) > 0 {
-		return fmt.Errorf("%s", strings.Join(errors, ","))
-	}
-	go wrc.manage.start()
-	return nil
-}
-
-// Check returns an error if any of the webhooks are not configured
-func (wrc *Register) Check() error {
-	if _, err := wrc.mwcLister.Get(getVerifyMutatingWebhookConfigName(wrc.serverIP)); err != nil {
-		return err
-	}
-	if _, err := wrc.mwcLister.Get(getResourceMutatingWebhookConfigName(wrc.serverIP)); err != nil {
-		return err
-	}
-	if _, err := wrc.vwcLister.Get(getResourceValidatingWebhookConfigName(wrc.serverIP)); err != nil {
-		return err
-	}
-	if _, err := wrc.mwcLister.Get(getPolicyMutatingWebhookConfigName(wrc.serverIP)); err != nil {
-		return err
-	}
-	if _, err := wrc.vwcLister.Get(getPolicyValidatingWebhookConfigName(wrc.serverIP)); err != nil {
-		return err
-	}
-	return nil
-}
-
-// Remove removes all webhook configurations
-func (wrc *Register) Remove(cleanupKyvernoResource bool, wg *sync.WaitGroup) {
-	defer wg.Done()
-	// delete Lease object to let init container do the cleanup
-	if err := wrc.kubeClient.CoordinationV1().Leases(config.KyvernoNamespace()).Delete(context.TODO(), "kyvernopre-lock", metav1.DeleteOptions{}); err != nil && errorsapi.IsNotFound(err) {
-		wrc.metricsConfig.RecordClientQueries(metrics.ClientDelete, metrics.KubeClient, "Lease", config.KyvernoNamespace())
-		wrc.log.WithName("cleanup").Error(err, "failed to clean up Lease lock")
-	}
-	if cleanupKyvernoResource {
-		wrc.removeWebhookConfigurations()
-	}
-}
-
-func (wrc *Register) ResetPolicyStatus(kyvernoInTermination bool, wg *sync.WaitGroup) {
-	defer wg.Done()
-
-	if !kyvernoInTermination {
-		return
-	}
-
-	logger := wrc.log.WithName("ResetPolicyStatus")
-	cpols, err := wrc.kyvernoClient.KyvernoV1().ClusterPolicies().List(context.TODO(), metav1.ListOptions{})
-	if err == nil {
-		for _, item := range cpols.Items {
-			cpol := item
-			cpol.Status.SetReady(false)
-			if _, err := wrc.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), &cpol, metav1.UpdateOptions{}); err != nil {
-				logger.Error(err, "failed to set ClusterPolicy status READY=false", "name", cpol.GetName())
-			}
-		}
-	} else {
-		logger.Error(err, "failed to list clusterpolicies")
-	}
-
-	pols, err := wrc.kyvernoClient.KyvernoV1().Policies(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
-	if err == nil {
-		for _, item := range pols.Items {
-			pol := item
-			pol.Status.SetReady(false)
-			if _, err := wrc.kyvernoClient.KyvernoV1().Policies(pol.GetNamespace()).UpdateStatus(context.TODO(), &pol, metav1.UpdateOptions{}); err != nil {
-				logger.Error(err, "failed to set Policy status READY=false", "namespace", pol.GetNamespace(), "name", pol.GetName())
-			}
-		}
-	} else {
-		logger.Error(err, "failed to list namespaced policies")
-	}
-}
-
-// GetWebhookTimeOut returns the value of webhook timeout
-func (wrc *Register) GetWebhookTimeOut() time.Duration {
-	return time.Duration(wrc.timeoutSeconds)
-}
-
-// UpdateWebhookConfigurations updates resource webhook configurations dynamically
-// based on the UPDATEs of Kyverno ConfigMap defined in INIT_CONFIG env
-//
-// it currently updates namespaceSelector only, can be extend to update other fields
-// +deprecated
-func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Configuration) {
-	logger := wrc.log.WithName("UpdateWebhookConfigurations")
-	for {
-		<-wrc.UpdateWebhookChan
-		logger.V(4).Info("received the signal to update webhook configurations")
-
-		retry := false
-		if wrc.serverIP != "" {
-			deploy, err := wrc.GetKubePolicyDeployment()
-			if err != nil {
-				retry = true
-			} else {
-				if tlsutils.IsKyvernoInRollingUpdate(deploy) {
-					retry = true
-				}
-			}
-		}
-
-		if !retry {
-			webhookCfgs := configHandler.GetWebhooks()
-			webhookCfg := config.WebhookConfig{}
-			if len(webhookCfgs) > 0 {
-				webhookCfg = webhookCfgs[0]
-			}
-
-			if err := wrc.updateResourceMutatingWebhookConfiguration(webhookCfg); err != nil {
-				logger.Error(err, "unable to update mutatingWebhookConfigurations", "name", getResourceMutatingWebhookConfigName(wrc.serverIP))
-				retry = true
-			}
-
-			if err := wrc.updateResourceValidatingWebhookConfiguration(webhookCfg); err != nil {
-				logger.Error(err, "unable to update validatingWebhookConfigurations", "name", getResourceValidatingWebhookConfigName(wrc.serverIP))
-				retry = true
-			}
-		}
-
-		if retry {
-			go func() {
-				time.Sleep(1 * time.Second)
-				select {
-				case wrc.UpdateWebhookChan <- true:
-					return
-				default:
-					return
-				}
-			}()
-		}
-	}
-}
-
-func (wrc *Register) ValidateWebhookConfigurations(namespace, name string) error {
-	logger := wrc.log.WithName("ValidateWebhookConfigurations")
-	cm, err := wrc.kubeClient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metav1.GetOptions{})
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientGet, metrics.KubeClient, "ConfigMap", namespace)
-	if err != nil {
-		logger.Error(err, "unable to fetch ConfigMap", "namespace", namespace, "name", name)
-		return nil
-	}
-	webhooks, ok := cm.Data["webhooks"]
-	if !ok {
-		logger.V(4).Info("webhook configurations not defined")
-		return nil
-	}
-	webhookCfgs := make([]config.WebhookConfig, 0, 10)
-	return json.Unmarshal([]byte(webhooks), &webhookCfgs)
-}
-
-func (wrc *Register) createMutatingWebhookConfiguration(config *admissionregistrationv1.MutatingWebhookConfiguration) error {
-	logger := wrc.log.WithValues("kind", kindMutating, "name", config.Name)
-
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientCreate, metrics.KubeClient, kindMutating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.TODO(), config, metav1.CreateOptions{}); err != nil {
-		if errorsapi.IsAlreadyExists(err) {
-			logger.V(6).Info("resource mutating webhook configuration already exists", "name", config.Name)
-			return wrc.updateMutatingWebhookConfiguration(config)
-		}
-		logger.Error(err, "failed to create resource mutating webhook configuration", "name", config.Name)
-		return err
-	}
-	logger.Info("created webhook")
-	return nil
-}
-
-func (wrc *Register) createValidatingWebhookConfiguration(config *admissionregistrationv1.ValidatingWebhookConfiguration) error {
-	logger := wrc.log.WithValues("kind", kindValidating, "name", config.Name)
-
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientCreate, metrics.KubeClient, kindValidating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), config, metav1.CreateOptions{}); err != nil {
-		if errorsapi.IsAlreadyExists(err) {
-			logger.V(6).Info("resource validating webhook configuration already exists", "name", config.Name)
-			return wrc.updateValidatingWebhookConfiguration(config)
-		}
-		logger.Error(err, "failed to create resource validating webhook configuration", "name", config.Name)
-		return err
-	}
-	logger.Info("created webhook")
-	return nil
-}
-
-func (wrc *Register) createResourceMutatingWebhookConfiguration(caData []byte) error {
-	owner := wrc.constructOwner()
-	var config *admissionregistrationv1.MutatingWebhookConfiguration
-	if wrc.serverIP != "" {
-		config = constructDefaultDebugMutatingWebhookConfig(wrc.serverIP, caData, wrc.timeoutSeconds, wrc.autoUpdateWebhooks, owner)
-	} else {
-		config = constructDefaultMutatingWebhookConfig(caData, wrc.timeoutSeconds, wrc.autoUpdateWebhooks, owner)
-	}
-	return wrc.createMutatingWebhookConfiguration(config)
-}
-
-func (wrc *Register) createResourceValidatingWebhookConfiguration(caData []byte) error {
-	owner := wrc.constructOwner()
-	var config *admissionregistrationv1.ValidatingWebhookConfiguration
-	if wrc.serverIP != "" {
-		config = constructDefaultDebugValidatingWebhookConfig(wrc.serverIP, caData, wrc.timeoutSeconds, wrc.autoUpdateWebhooks, owner)
-	} else {
-		config = constructDefaultValidatingWebhookConfig(caData, wrc.timeoutSeconds, wrc.autoUpdateWebhooks, owner)
-	}
-	return wrc.createValidatingWebhookConfiguration(config)
-}
-
-func (wrc *Register) createPolicyValidatingWebhookConfiguration(caData []byte) error {
-	owner := wrc.constructOwner()
-	var config *admissionregistrationv1.ValidatingWebhookConfiguration
-	if wrc.serverIP != "" {
-		config = constructDebugPolicyValidatingWebhookConfig(wrc.serverIP, caData, wrc.timeoutSeconds, owner)
-	} else {
-		config = constructPolicyValidatingWebhookConfig(caData, wrc.timeoutSeconds, owner)
-	}
-	return wrc.createValidatingWebhookConfiguration(config)
-}
-
-func (wrc *Register) createPolicyMutatingWebhookConfiguration(caData []byte) error {
-	owner := wrc.constructOwner()
-	var config *admissionregistrationv1.MutatingWebhookConfiguration
-	if wrc.serverIP != "" {
-		config = constructDebugPolicyMutatingWebhookConfig(wrc.serverIP, caData, wrc.timeoutSeconds, owner)
-	} else {
-		config = constructPolicyMutatingWebhookConfig(caData, wrc.timeoutSeconds, owner)
-	}
-	return wrc.createMutatingWebhookConfiguration(config)
-}
-
-func (wrc *Register) createVerifyMutatingWebhookConfiguration(caData []byte) error {
-	owner := wrc.constructOwner()
-	var config *admissionregistrationv1.MutatingWebhookConfiguration
-	if wrc.serverIP != "" {
-		config = constructDebugVerifyMutatingWebhookConfig(wrc.serverIP, caData, wrc.timeoutSeconds, owner)
-	} else {
-		config = constructVerifyMutatingWebhookConfig(caData, wrc.timeoutSeconds, owner)
-	}
-	return wrc.createMutatingWebhookConfiguration(config)
-}
-
-func (wrc *Register) checkEndpoint() error {
-	endpoint, err := wrc.kubeClient.CoreV1().Endpoints(config.KyvernoNamespace()).Get(context.TODO(), config.KyvernoServiceName(), metav1.GetOptions{})
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientGet, metrics.KubeClient, "EndPoint", config.KyvernoNamespace())
-	if err != nil {
-		return fmt.Errorf("failed to get endpoint %s/%s: %v", config.KyvernoNamespace(), config.KyvernoServiceName(), err)
-	}
-	deploy, err := wrc.GetKubePolicyDeployment()
-	if err != nil {
-		return err
-	}
-	if tlsutils.IsKyvernoInRollingUpdate(deploy) {
-		return errors.New("kyverno is in rolling update, please update the timeout by setting the webhookRegistrationTimeout flag")
-	}
-	selector := &metav1.LabelSelector{
-		MatchLabels: map[string]string{
-			"app.kubernetes.io/name": kyvernov1.ValueKyvernoApp,
-		},
-	}
-	pods, err := wrc.kubeClient.CoreV1().Pods(config.KyvernoNamespace()).List(context.TODO(), metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(selector)})
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientList, metrics.KubeClient, "Pod", config.KyvernoNamespace())
-	if err != nil {
-		return fmt.Errorf("failed to list Kyverno Pod: %v", err)
-	}
-	ips := getHealthyPodsIP(pods.Items)
-	if len(ips) == 0 {
-		return fmt.Errorf("pod is not assigned to any node yet")
-	}
-	for _, subset := range endpoint.Subsets {
-		if len(subset.Addresses) == 0 {
-			continue
-		}
-		for _, addr := range subset.Addresses {
-			if utils.ContainsString(ips, addr.IP) {
-				wrc.log.V(2).Info("Endpoint ready", "ns", config.KyvernoNamespace(), "name", config.KyvernoServiceName())
-				return nil
-			}
-		}
-	}
-	err = fmt.Errorf("endpoint not ready")
-	wrc.log.V(3).Info(err.Error(), "ns", config.KyvernoNamespace(), "name", config.KyvernoServiceName())
-	return err
-}
-
-func (wrc *Register) updateResourceValidatingWebhookConfiguration(webhookCfg config.WebhookConfig) error {
-	resource, err := wrc.vwcLister.Get(getResourceValidatingWebhookConfigName(wrc.serverIP))
-	if err != nil {
-		return errors.Wrapf(err, "unable to get validatingWebhookConfigurations")
-	}
-	copy := resource.DeepCopy()
-	for i := range copy.Webhooks {
-		copy.Webhooks[i].ObjectSelector = webhookCfg.ObjectSelector
-		copy.Webhooks[i].NamespaceSelector = webhookCfg.NamespaceSelector
-	}
-	if reflect.DeepEqual(resource.Webhooks, copy.Webhooks) {
-		wrc.log.V(4).Info("namespaceSelector unchanged, skip updating validatingWebhookConfigurations")
-		return nil
-	}
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindValidating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Update(context.TODO(), copy, metav1.UpdateOptions{}); err != nil {
-		return err
-	}
-	wrc.log.V(3).Info("successfully updated validatingWebhookConfigurations", "name", getResourceMutatingWebhookConfigName(wrc.serverIP))
-	return nil
-}
-
-func (wrc *Register) updateResourceMutatingWebhookConfiguration(webhookCfg config.WebhookConfig) error {
-	resource, err := wrc.mwcLister.Get(getResourceMutatingWebhookConfigName(wrc.serverIP))
-	if err != nil {
-		return errors.Wrapf(err, "unable to get mutatingWebhookConfigurations")
-	}
-	copy := resource.DeepCopy()
-	for i := range copy.Webhooks {
-		copy.Webhooks[i].ObjectSelector = webhookCfg.ObjectSelector
-		copy.Webhooks[i].NamespaceSelector = webhookCfg.NamespaceSelector
-	}
-	if reflect.DeepEqual(resource.Webhooks, copy.Webhooks) {
-		wrc.log.V(4).Info("namespaceSelector unchanged, skip updating mutatingWebhookConfigurations")
-		return nil
-	}
-
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindMutating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(context.TODO(), copy, metav1.UpdateOptions{}); err != nil {
-		return err
-	}
-	wrc.log.V(3).Info("successfully updated mutatingWebhookConfigurations", "name", getResourceMutatingWebhookConfigName(wrc.serverIP))
-	return nil
-}
-
-// updateMutatingWebhookConfiguration updates an existing MutatingWebhookConfiguration with the rules provided by
-// the targetConfig. If the targetConfig doesn't provide any rules, the existing rules will be preserved.
-func (wrc *Register) updateMutatingWebhookConfiguration(targetConfig *admissionregistrationv1.MutatingWebhookConfiguration) error {
-	// Fetch the existing webhook.
-	currentConfiguration, err := wrc.mwcLister.Get(targetConfig.Name)
-	if err != nil {
-		return fmt.Errorf("failed to get %s %s: %v", kindMutating, targetConfig.Name, err)
-	}
-	// Create a map of the target webhooks.
-	targetWebhooksMap := make(map[string]admissionregistrationv1.MutatingWebhook)
-	for _, w := range targetConfig.Webhooks {
-		targetWebhooksMap[w.Name] = w
-	}
-	// Update the webhooks.
-	newWebhooks := make([]admissionregistrationv1.MutatingWebhook, 0)
-	for _, w := range currentConfiguration.Webhooks {
-		target, exist := targetWebhooksMap[w.Name]
-		if !exist {
-			continue
-		}
-		delete(targetWebhooksMap, w.Name)
-		// Update the webhook configuration
-		w.ClientConfig.URL = target.ClientConfig.URL
-		w.ClientConfig.Service = target.ClientConfig.Service
-		w.ClientConfig.CABundle = target.ClientConfig.CABundle
-		if target.Rules != nil {
-			// If the target webhook has rule definitions override the current.
-			w.Rules = target.Rules
-		}
-		newWebhooks = append(newWebhooks, w)
-	}
-	// Check if there are additional webhooks defined and add them.
-	for _, w := range targetWebhooksMap {
-		newWebhooks = append(newWebhooks, w)
-	}
-	// Update the current configuration.
-	currentConfiguration.Webhooks = newWebhooks
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindMutating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(context.TODO(), currentConfiguration, metav1.UpdateOptions{}); err != nil {
-		return err
-	}
-	wrc.log.V(3).Info("successfully updated mutatingWebhookConfigurations", "name", targetConfig.Name)
-	return nil
-}
-
-// updateValidatingWebhookConfiguration updates an existing ValidatingWebhookConfiguration with the rules provided by
-// the targetConfig. If the targetConfig doesn't provide any rules, the existing rules will be preserved.
-func (wrc *Register) updateValidatingWebhookConfiguration(targetConfig *admissionregistrationv1.ValidatingWebhookConfiguration) error {
-	// Fetch the existing webhook.
-	currentConfiguration, err := wrc.vwcLister.Get(targetConfig.Name)
-	if err != nil {
-		return fmt.Errorf("failed to get %s %s: %v", kindValidating, targetConfig.Name, err)
-	}
-	// Create a map of the target webhooks.
-	targetWebhooksMap := make(map[string]admissionregistrationv1.ValidatingWebhook)
-	for _, w := range targetConfig.Webhooks {
-		targetWebhooksMap[w.Name] = w
-	}
-	// Update the webhooks.
-	newWebhooks := make([]admissionregistrationv1.ValidatingWebhook, 0)
-	for _, w := range currentConfiguration.Webhooks {
-		target, exist := targetWebhooksMap[w.Name]
-		if !exist {
-			continue
-		}
-		delete(targetWebhooksMap, w.Name)
-		// Update the webhook configuration
-		w.ClientConfig.URL = target.ClientConfig.URL
-		w.ClientConfig.Service = target.ClientConfig.Service
-		w.ClientConfig.CABundle = target.ClientConfig.CABundle
-		if target.Rules != nil {
-			// If the target webhook has rule definitions override the current.
-			w.Rules = target.Rules
-		}
-		newWebhooks = append(newWebhooks, w)
-	}
-	// Check if there are additional webhooks defined and add them.
-	for _, w := range targetWebhooksMap {
-		newWebhooks = append(newWebhooks, w)
-	}
-	// Update the current configuration.
-	currentConfiguration.Webhooks = newWebhooks
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientUpdate, metrics.KubeClient, kindValidating, "")
-	if _, err := wrc.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Update(context.TODO(), currentConfiguration, metav1.UpdateOptions{}); err != nil {
-		return err
-	}
-	wrc.log.V(3).Info("successfully updated validatingWebhookConfigurations", "name", targetConfig.Name)
-	return nil
-}
-
-func (wrc *Register) ShouldCleanupKyvernoResource() bool {
-	logger := wrc.log.WithName("cleanupKyvernoResource")
-	deploy, err := wrc.kubeClient.AppsV1().Deployments(config.KyvernoNamespace()).Get(context.TODO(), config.KyvernoDeploymentName(), metav1.GetOptions{})
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientGet, metrics.KubeClient, "Deployment", config.KyvernoNamespace())
-	if err != nil {
-		if errorsapi.IsNotFound(err) {
-			logger.Info("Kyverno deployment not found, cleanup Kyverno resources")
-			return true
-		}
-		logger.Error(err, "failed to get deployment, not cleaning up kyverno resources")
-		return false
-	}
-	if deploy.GetDeletionTimestamp() != nil {
-		logger.Info("Kyverno is terminating, cleanup Kyverno resources")
-		return true
-	}
-	if deploy.Spec.Replicas != nil && *deploy.Spec.Replicas == 0 {
-		logger.Info("Kyverno is scaled to zero, cleanup Kyverno resources")
-		return true
-	}
-	logger.Info("updating Kyverno Pod, won't clean up Kyverno resources")
-	return false
-}
-
-func (wrc *Register) removeWebhookConfigurations() {
-	startTime := time.Now()
-	wrc.log.V(3).Info("deleting all webhook configurations")
-	defer wrc.log.V(4).Info("removed webhook configurations", "processingTime", time.Since(startTime).String())
-	var wg sync.WaitGroup
-	wg.Add(5)
-	go wrc.removeResourceMutatingWebhookConfiguration(&wg)
-	go wrc.removeResourceValidatingWebhookConfiguration(&wg)
-	go wrc.removePolicyMutatingWebhookConfiguration(&wg)
-	go wrc.removePolicyValidatingWebhookConfiguration(&wg)
-	go wrc.removeVerifyWebhookMutatingWebhookConfig(&wg)
-	wg.Wait()
-}
-
-func (wrc *Register) removeResourceMutatingWebhookConfiguration(wg *sync.WaitGroup) {
-	defer wg.Done()
-	wrc.removeMutatingWebhookConfiguration(getResourceMutatingWebhookConfigName(wrc.serverIP))
-}
-
-func (wrc *Register) removeResourceValidatingWebhookConfiguration(wg *sync.WaitGroup) {
-	defer wg.Done()
-	wrc.removeValidatingWebhookConfiguration(getResourceValidatingWebhookConfigName(wrc.serverIP))
-}
-
-func (wrc *Register) removePolicyMutatingWebhookConfiguration(wg *sync.WaitGroup) {
-	defer wg.Done()
-	wrc.removeMutatingWebhookConfiguration(getPolicyMutatingWebhookConfigName(wrc.serverIP))
-}
-
-func (wrc *Register) removePolicyValidatingWebhookConfiguration(wg *sync.WaitGroup) {
-	defer wg.Done()
-	wrc.removeValidatingWebhookConfiguration(getPolicyValidatingWebhookConfigName(wrc.serverIP))
-}
-
-func (wrc *Register) removeVerifyWebhookMutatingWebhookConfig(wg *sync.WaitGroup) {
-	defer wg.Done()
-	wrc.removeMutatingWebhookConfiguration(getVerifyMutatingWebhookConfigName(wrc.serverIP))
-}
-
-func (wrc *Register) removeMutatingWebhookConfiguration(name string) {
-	logger := wrc.log.WithValues("kind", kindMutating, "name", name)
-	if err := wrc.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil && !errorsapi.IsNotFound(err) {
-		logger.Error(err, "failed to delete the mutating webhook configuration")
-	} else {
-		logger.Info("webhook configuration deleted")
-	}
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientDelete, metrics.KubeClient, kindMutating, "")
-}
-
-func (wrc *Register) removeValidatingWebhookConfiguration(name string) {
-	logger := wrc.log.WithValues("kind", kindValidating, "name", name)
-	if err := wrc.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil && !errorsapi.IsNotFound(err) {
-		logger.Error(err, "failed to delete the validating webhook configuration")
-	} else {
-		logger.Info("webhook configuration deleted")
-	}
-	wrc.metricsConfig.RecordClientQueries(metrics.ClientDelete, metrics.KubeClient, kindValidating, "")
-}
diff --git a/pkg/webhookconfig/status.go b/pkg/webhookconfig/status.go
deleted file mode 100644
index 4c4c49da5b..0000000000
--- a/pkg/webhookconfig/status.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package webhookconfig
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"github.com/go-logr/logr"
-	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/config"
-	"github.com/kyverno/kyverno/pkg/event"
-	"github.com/pkg/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
-)
-
-const (
-	leaseName          string = "kyverno"
-	annWebhookStatus   string = "kyverno.io/webhookActive"
-	annLastRequestTime string = "kyverno.io/last-request-time"
-)
-
-// statusControl controls the webhook status
-type statusControl struct {
-	eventGen    event.Interface
-	log         logr.Logger
-	leaseClient coordinationv1.LeaseInterface
-}
-
-// success ...
-func (vc statusControl) success() error {
-	return vc.setStatus("true")
-}
-
-// failure ...
-func (vc statusControl) failure() error {
-	return vc.setStatus("false")
-}
-
-// NewStatusControl creates a new webhook status control
-func newStatusControl(leaseClient coordinationv1.LeaseInterface, eventGen event.Interface, log logr.Logger) *statusControl {
-	return &statusControl{
-		eventGen:    eventGen,
-		log:         log,
-		leaseClient: leaseClient,
-	}
-}
-
-func (vc statusControl) setStatus(status string) error {
-	logger := vc.log.WithValues("name", leaseName, "namespace", config.KyvernoNamespace())
-	var ann map[string]string
-	var err error
-
-	lease, err := vc.leaseClient.Get(context.TODO(), "kyverno", metav1.GetOptions{})
-	if err != nil {
-		vc.log.WithName("UpdateLastRequestTimestmap").Error(err, "Lease 'kyverno' not found. Starting clean-up...")
-		return err
-	}
-
-	ann = lease.GetAnnotations()
-	if ann == nil {
-		ann = map[string]string{}
-		ann[annWebhookStatus] = status
-	}
-
-	leaseStatus, ok := ann[annWebhookStatus]
-	if ok {
-		if leaseStatus == status {
-			logger.V(4).Info(fmt.Sprintf("annotation %s already set to '%s'", annWebhookStatus, status))
-			return nil
-		}
-	}
-
-	ann[annWebhookStatus] = status
-	lease.SetAnnotations(ann)
-
-	_, err = vc.leaseClient.Update(context.TODO(), lease, metav1.UpdateOptions{})
-	if err != nil {
-		return errors.Wrapf(err, "key %s, val %s", annWebhookStatus, status)
-	}
-
-	logger.Info("updated lease annotation", "key", annWebhookStatus, "val", status)
-
-	// create event on kyverno deployment
-	createStatusUpdateEvent(status, vc.eventGen)
-	return nil
-}
-
-func createStatusUpdateEvent(status string, eventGen event.Interface) {
-	e := event.Info{}
-	e.Kind = "Lease"
-	e.Namespace = config.KyvernoNamespace()
-	e.Name = leaseName
-	e.Reason = "Update"
-	e.Message = fmt.Sprintf("admission control webhook active status changed to %s", status)
-	eventGen.Add(e)
-}
-
-func (vc statusControl) UpdateLastRequestTimestmap(new time.Time) error {
-	lease, err := vc.leaseClient.Get(context.TODO(), leaseName, metav1.GetOptions{})
-	if err != nil {
-		vc.log.WithName("UpdateLastRequestTimestmap").Error(err, "Lease 'kyverno' not found. Starting clean-up...")
-		return err
-	}
-
-	// add label to lease
-	label := lease.GetLabels()
-	if len(label) == 0 {
-		label = make(map[string]string)
-		label["app.kubernetes.io/name"] = kyvernov1.ValueKyvernoApp
-	}
-	lease.SetLabels(label)
-
-	annotation := lease.GetAnnotations()
-	if annotation == nil {
-		annotation = make(map[string]string)
-	}
-
-	t, err := new.MarshalText()
-	if err != nil {
-		return errors.Wrap(err, "failed to marshal timestamp")
-	}
-
-	annotation[annLastRequestTime] = string(t)
-	lease.SetAnnotations(annotation)
-
-	// update annotations in lease
-	_, err = vc.leaseClient.Update(context.TODO(), lease, metav1.UpdateOptions{})
-	if err != nil {
-		return errors.Wrapf(err, "failed to update annotation %s for deployment %s in namespace %s", annLastRequestTime, lease.GetName(), lease.GetNamespace())
-	}
-
-	return nil
-}
diff --git a/pkg/webhooks/handlers/admission.go b/pkg/webhooks/handlers/admission.go
index a1fb220cd1..950a7af82c 100644
--- a/pkg/webhooks/handlers/admission.go
+++ b/pkg/webhooks/handlers/admission.go
@@ -11,7 +11,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/tracing"
 	admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
-	"github.com/kyverno/kyverno/pkg/webhookconfig"
 	"go.opentelemetry.io/otel/attribute"
 	admissionv1 "k8s.io/api/admission/v1"
 )
@@ -99,9 +98,8 @@ func Filter(c config.Configuration, inner AdmissionHandler) AdmissionHandler {
 	}
 }
 
-func Verify(m *webhookconfig.Monitor) AdmissionHandler {
+func Verify() AdmissionHandler {
 	return func(logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
-		logger.V(6).Info("incoming request", "last admission request timestamp", m.Time())
 		return admissionutils.Response(true)
 	}
 }
diff --git a/pkg/webhooks/handlers/monitor.go b/pkg/webhooks/handlers/monitor.go
deleted file mode 100644
index d055648834..0000000000
--- a/pkg/webhooks/handlers/monitor.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package handlers
-
-import (
-	"net/http"
-	"time"
-
-	"github.com/kyverno/kyverno/pkg/webhookconfig"
-)
-
-func Monitor(m *webhookconfig.Monitor, inner http.HandlerFunc) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		m.SetTime(time.Now())
-		inner(w, r)
-	}
-}
diff --git a/pkg/webhooks/handlers/probe.go b/pkg/webhooks/handlers/probe.go
index c47ffc76b3..70629cd0fd 100644
--- a/pkg/webhooks/handlers/probe.go
+++ b/pkg/webhooks/handlers/probe.go
@@ -2,10 +2,10 @@ package handlers
 
 import "net/http"
 
-func Probe(check func() error) http.HandlerFunc {
+func Probe(check func() bool) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		if check != nil {
-			if err := check(); err != nil {
+			if !check() {
 				w.WriteHeader(http.StatusInternalServerError)
 			}
 		}
diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go
index 09caff1e63..71ed206eef 100644
--- a/pkg/webhooks/server.go
+++ b/pkg/webhooks/server.go
@@ -5,7 +5,6 @@ import (
 	"crypto/tls"
 	"fmt"
 	"net/http"
-	"sync"
 	"time"
 
 	"github.com/go-logr/logr"
@@ -15,9 +14,14 @@ import (
 	"github.com/kyverno/kyverno/pkg/toggle"
 	"github.com/kyverno/kyverno/pkg/utils"
 	admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
-	"github.com/kyverno/kyverno/pkg/webhookconfig"
+	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
+	runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
 	"github.com/kyverno/kyverno/pkg/webhooks/handlers"
 	admissionv1 "k8s.io/api/admission/v1"
+	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+	coordinationv1 "k8s.io/api/coordination/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
@@ -45,9 +49,12 @@ type ResourceHandlers interface {
 }
 
 type server struct {
-	server          *http.Server
-	webhookRegister *webhookconfig.Register
-	cleanUp         chan struct{}
+	server      *http.Server
+	runtime     runtimeutils.Runtime
+	mwcClient   controllerutils.DeleteClient[*admissionregistrationv1.MutatingWebhookConfiguration]
+	vwcClient   controllerutils.DeleteClient[*admissionregistrationv1.ValidatingWebhookConfiguration]
+	leaseClient controllerutils.DeleteClient[*coordinationv1.Lease]
+	cleanUp     chan struct{}
 }
 
 type TlsProvider func() ([]byte, []byte, error)
@@ -56,22 +63,24 @@ type TlsProvider func() ([]byte, []byte, error)
 func NewServer(
 	policyHandlers PolicyHandlers,
 	resourceHandlers ResourceHandlers,
-	tlsProvider TlsProvider,
 	configuration config.Configuration,
-	register *webhookconfig.Register,
-	monitor *webhookconfig.Monitor,
+	tlsProvider TlsProvider,
+	mwcClient controllerutils.DeleteClient[*admissionregistrationv1.MutatingWebhookConfiguration],
+	vwcClient controllerutils.DeleteClient[*admissionregistrationv1.ValidatingWebhookConfiguration],
+	leaseClient controllerutils.DeleteClient[*coordinationv1.Lease],
+	runtime runtimeutils.Runtime,
 ) Server {
 	mux := httprouter.New()
 	resourceLogger := logger.WithName("resource")
 	policyLogger := logger.WithName("policy")
 	verifyLogger := logger.WithName("verify")
-	registerWebhookHandlers(resourceLogger.WithName("mutate"), mux, config.MutatingWebhookServicePath, monitor, configuration, resourceHandlers.Mutate)
-	registerWebhookHandlers(resourceLogger.WithName("validate"), mux, config.ValidatingWebhookServicePath, monitor, configuration, resourceHandlers.Validate)
-	mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, admission(policyLogger.WithName("mutate"), monitor, filter(configuration, policyHandlers.Mutate)))
-	mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, admission(policyLogger.WithName("validate"), monitor, filter(configuration, policyHandlers.Validate)))
-	mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, admission(verifyLogger.WithName("mutate"), monitor, handlers.Verify(monitor)))
-	mux.HandlerFunc("GET", config.LivenessServicePath, handlers.Probe(register.Check))
-	mux.HandlerFunc("GET", config.ReadinessServicePath, handlers.Probe(nil))
+	registerWebhookHandlers(resourceLogger.WithName("mutate"), mux, config.MutatingWebhookServicePath, configuration, resourceHandlers.Mutate)
+	registerWebhookHandlers(resourceLogger.WithName("validate"), mux, config.ValidatingWebhookServicePath, configuration, resourceHandlers.Validate)
+	mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, admission(policyLogger.WithName("mutate"), filter(configuration, policyHandlers.Mutate)))
+	mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, admission(policyLogger.WithName("validate"), filter(configuration, policyHandlers.Validate)))
+	mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, admission(verifyLogger.WithName("mutate"), handlers.Verify()))
+	mux.HandlerFunc("GET", config.LivenessServicePath, handlers.Probe(runtime.IsLive))
+	mux.HandlerFunc("GET", config.ReadinessServicePath, handlers.Probe(runtime.IsReady))
 	return &server{
 		server: &http.Server{
 			Addr: ":9443",
@@ -94,8 +103,11 @@ func NewServer(
 			WriteTimeout:      30 * time.Second,
 			ReadHeaderTimeout: 30 * time.Second,
 		},
-		webhookRegister: register,
-		cleanUp:         make(chan struct{}),
+		mwcClient:   mwcClient,
+		vwcClient:   vwcClient,
+		leaseClient: leaseClient,
+		runtime:     runtime,
+		cleanUp:     make(chan struct{}),
 	}
 }
 
@@ -126,13 +138,29 @@ func (s *server) Cleanup() <-chan struct{} {
 }
 
 func (s *server) cleanup(ctx context.Context) {
-	cleanupKyvernoResource := s.webhookRegister.ShouldCleanupKyvernoResource()
-
-	var wg sync.WaitGroup
-	wg.Add(2)
-	go s.webhookRegister.Remove(cleanupKyvernoResource, &wg)
-	go s.webhookRegister.ResetPolicyStatus(cleanupKyvernoResource, &wg)
-	wg.Wait()
+	if s.runtime.IsGoingDown() {
+		deleteLease := func(name string) {
+			if err := s.leaseClient.Delete(ctx, name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
+				logger.Error(err, "failed to clean up lease", "name", name)
+			}
+		}
+		deleteVwc := func(name string) {
+			if err := s.vwcClient.Delete(ctx, name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
+				logger.Error(err, "failed to clean up validating webhook configuration", "name", name)
+			}
+		}
+		deleteMwc := func(name string) {
+			if err := s.mwcClient.Delete(ctx, name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
+				logger.Error(err, "failed to clean up mutating webhook configuration", "name", name)
+			}
+		}
+		deleteLease("kyvernopre-lock")
+		deleteVwc(config.ValidatingWebhookConfigurationName)
+		deleteVwc(config.PolicyValidatingWebhookConfigurationName)
+		deleteMwc(config.MutatingWebhookConfigurationName)
+		deleteMwc(config.PolicyMutatingWebhookConfigurationName)
+		deleteMwc(config.VerifyMutatingWebhookConfigurationName)
+	}
 	close(s.cleanUp)
 }
 
@@ -162,31 +190,30 @@ func filter(configuration config.Configuration, inner handlers.AdmissionHandler)
 	return handlers.Filter(configuration, inner)
 }
 
-func admission(logger logr.Logger, monitor *webhookconfig.Monitor, inner handlers.AdmissionHandler) http.HandlerFunc {
-	return handlers.Monitor(monitor, handlers.Admission(logger, protect(inner)))
+func admission(logger logr.Logger, inner handlers.AdmissionHandler) http.HandlerFunc {
+	return handlers.Admission(logger, protect(inner))
 }
 
 func registerWebhookHandlers(
 	logger logr.Logger,
 	mux *httprouter.Router,
 	basePath string,
-	monitor *webhookconfig.Monitor,
 	configuration config.Configuration,
 	handlerFunc func(logr.Logger, *admissionv1.AdmissionRequest, string, time.Time) *admissionv1.AdmissionResponse,
 ) {
-	mux.HandlerFunc("POST", basePath, admission(logger, monitor, filter(
+	mux.HandlerFunc("POST", basePath, admission(logger, filter(
 		configuration,
 		func(logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
 			return handlerFunc(logger, request, "all", startTime)
 		})),
 	)
-	mux.HandlerFunc("POST", basePath+"/fail", admission(logger, monitor, filter(
+	mux.HandlerFunc("POST", basePath+"/fail", admission(logger, filter(
 		configuration,
 		func(logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
 			return handlerFunc(logger, request, "fail", startTime)
 		})),
 	)
-	mux.HandlerFunc("POST", basePath+"/ignore", admission(logger, monitor, filter(
+	mux.HandlerFunc("POST", basePath+"/ignore", admission(logger, filter(
 		configuration,
 		func(logger logr.Logger, request *admissionv1.AdmissionRequest, startTime time.Time) *admissionv1.AdmissionResponse {
 			return handlerFunc(logger, request, "ignore", startTime)