From e022084dd0e57d48095ecd10f546c59b737272bc Mon Sep 17 00:00:00 2001
From: shivkumar dudhani <shivkumar@nirmata.com>
Date: Wed, 30 Oct 2019 13:39:19 -0700
Subject: [PATCH] add checker to verify if mutatingwebhook is enabled or not +
 refactoring

---
 main.go                           |   8 +--
 pkg/checker/checker.go            | 114 ++++++++++++++++++++++++++++++
 pkg/checker/status.go             | 114 ++++++++++++++++++++++++++++++
 pkg/config/config.go              |   5 ++
 pkg/webhookconfig/checker.go      |  78 ++++++++++++++++++++
 pkg/webhookconfig/policy.go       |  41 +++++++++++
 pkg/webhookconfig/registration.go |  80 ++++++++++-----------
 pkg/webhooks/checker.go           |  89 ++---------------------
 pkg/webhooks/mutation.go          |   2 +-
 pkg/webhooks/policyvalidation.go  |   1 -
 pkg/webhooks/server.go            |  39 ++++++----
 pkg/webhooks/validation.go        |   4 +-
 12 files changed, 432 insertions(+), 143 deletions(-)
 create mode 100644 pkg/checker/checker.go
 create mode 100644 pkg/checker/status.go
 create mode 100644 pkg/webhookconfig/checker.go

diff --git a/main.go b/main.go
index 45d60fd584..60ea2d6688 100644
--- a/main.go
+++ b/main.go
@@ -31,9 +31,6 @@ var (
 	filterK8Resources string
 )
 
-// TODO: tune resync time differently for each informer
-const defaultReSyncTime = 10 * time.Second
-
 func main() {
 	defer glog.Flush()
 	printVersionInfo()
@@ -162,7 +159,10 @@ func main() {
 	go egen.Run(1, stopCh)
 	go nsc.Run(1, stopCh)
 
-	//TODO add WG for the go routines?
+	// verifys if the admission control is enabled and active
+	// resync: 60 seconds
+	// deadline: 60 seconds (send request)
+	// max deadline: deadline*3 (set the deployment annotation as false)
 	server.RunAsync(stopCh)
 
 	<-stopCh
diff --git a/pkg/checker/checker.go b/pkg/checker/checker.go
new file mode 100644
index 0000000000..62fe232abe
--- /dev/null
+++ b/pkg/checker/checker.go
@@ -0,0 +1,114 @@
+package checker
+
+import (
+	"sync"
+	"time"
+
+	"github.com/golang/glog"
+	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
+	dclient "github.com/nirmata/kyverno/pkg/dclient"
+	"k8s.io/apimachinery/pkg/labels"
+)
+
+//MaxRetryCount defines the max deadline count
+const MaxRetryCount int = 3
+
+// LastReqTime
+type LastReqTime struct {
+	t  time.Time
+	mu sync.RWMutex
+}
+
+func (t *LastReqTime) Time() time.Time {
+	t.mu.RLock()
+	defer t.mu.RUnlock()
+	return t.t
+}
+
+func (t *LastReqTime) SetTime(tm time.Time) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	glog.V(4).Info("updating last request time")
+	t.t = tm
+}
+
+func NewLastReqTime() *LastReqTime {
+	return &LastReqTime{
+		t: time.Now(),
+	}
+}
+
+func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolicyLister) bool {
+	policies, err := pLister.ListResources(labels.NewSelector())
+	if err != nil {
+		glog.Error()
+	}
+	for _, policy := range policies {
+		if policy.HasMutateOrValidate() {
+			// as there exists one policy with mutate or validate rule
+			// so there must be a webhook configuration on resource
+			return true
+		}
+	}
+	return false
+}
+
+//Run runs the checker and verify the resource update
+func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
+	glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync)
+	maxDeadline := deadline * time.Duration(MaxRetryCount)
+	ticker := time.NewTicker(defaultResync)
+	var statuscontrol StatusInterface
+	/// interface to update and increment kyverno webhook status via annotations
+	statuscontrol = NewVerifyControl(client)
+	// send the initial update status
+	if checkIfPolicyWithMutateAndGenerateExists(pLister) {
+		if err := statuscontrol.SuccessStatus(); err != nil {
+			glog.Error(err)
+		}
+	}
+
+	defer ticker.Stop()
+	// - has recieved request ->  set webhookstatus as "True"
+	// - no requests recieved
+	// 						  -> if greater than deadline, send update request
+	// 						  -> if greater than maxDeadline, send failed status update
+	for {
+		select {
+		case <-ticker.C:
+			// if there are no policies then we dont have a webhook on resource.
+			// we indirectly check if the resource
+			if !checkIfPolicyWithMutateAndGenerateExists(pLister) {
+				continue
+			}
+			// get current time
+			timeDiff := time.Since(t.Time())
+			if timeDiff > maxDeadline {
+				glog.Infof("failed to recieve any request for more than %v ", maxDeadline)
+				glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
+				// set the status unavailable
+				if err := statuscontrol.FailedStatus(); err != nil {
+					glog.Error(err)
+				}
+				continue
+			}
+			if timeDiff > deadline {
+				glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
+				// send request to update the kyverno deployment
+				if err := statuscontrol.IncrementAnnotation(); err != nil {
+					glog.Error(err)
+				}
+				continue
+			}
+			// if the status was false before then we update it to true
+			// send request to update the kyverno deployment
+			if err := statuscontrol.SuccessStatus(); err != nil {
+				glog.Error(err)
+			}
+		case <-stopCh:
+			// handler termination signal
+			glog.V(2).Infof("stopping default resync for webhook checker")
+			return
+		}
+	}
+}
diff --git a/pkg/checker/status.go b/pkg/checker/status.go
new file mode 100644
index 0000000000..240fd89a1d
--- /dev/null
+++ b/pkg/checker/status.go
@@ -0,0 +1,114 @@
+package checker
+
+import (
+	"strconv"
+
+	"github.com/golang/glog"
+	dclient "github.com/nirmata/kyverno/pkg/dclient"
+)
+
+const deployName string = "kyverno"
+const deployNamespace string = "kyverno"
+
+const annCounter string = "kyverno.io/generationCounter"
+const annWebhookStats string = "kyverno.io/webhookActive"
+
+//StatusInterface provides api to update webhook active annotations on kyverno deployments
+type StatusInterface interface {
+	// Increments generation counter annotation
+	IncrementAnnotation() error
+	// update annotation to inform webhook is active
+	SuccessStatus() error
+	// update annotation to inform webhook is inactive
+	FailedStatus() error
+}
+
+//StatusControl controls the webhook status
+type StatusControl struct {
+	client *dclient.Client
+}
+
+//SuccessStatus ...
+func (vc StatusControl) SuccessStatus() error {
+	return vc.setStatus("true")
+}
+
+//FailedStatus ...
+func (vc StatusControl) FailedStatus() error {
+	return vc.setStatus("false")
+}
+
+// NewVerifyControl ...
+func NewVerifyControl(client *dclient.Client) *StatusControl {
+	return &StatusControl{
+		client: client,
+	}
+}
+
+func (vc StatusControl) setStatus(status string) error {
+	glog.Infof("setting deployment %s in ns %s annotation %s to %s", deployName, deployNamespace, annWebhookStats, status)
+	var ann map[string]string
+	var err error
+	deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
+	if err != nil {
+		glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
+		return err
+	}
+	ann = deploy.GetAnnotations()
+	if ann == nil {
+		ann = map[string]string{}
+		ann[annWebhookStats] = status
+	}
+	webhookAction, ok := ann[annWebhookStats]
+	if ok {
+		// annotatiaion is present
+		if webhookAction == status {
+			glog.V(4).Infof("annotation %s already set to '%s'", annWebhookStats, status)
+			return nil
+		}
+	}
+	// set the status
+	ann[annWebhookStats] = status
+	deploy.SetAnnotations(ann)
+	// update counter
+	_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
+	if err != nil {
+		glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annWebhookStats, deployName, deployNamespace, err)
+		return err
+	}
+	return nil
+}
+
+//IncrementAnnotation ...
+func (vc StatusControl) IncrementAnnotation() error {
+	glog.Infof("setting deployment %s in ns %s annotation %s", deployName, deployNamespace, annCounter)
+	var ann map[string]string
+	var err error
+	deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
+	if err != nil {
+		glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
+		return err
+	}
+	ann = deploy.GetAnnotations()
+	if ann == nil {
+		ann = map[string]string{}
+		ann[annCounter] = "0"
+	}
+	counter, err := strconv.Atoi(ann[annCounter])
+	if err != nil {
+		glog.V(4).Infof("failed to parse string: %v", err)
+		return err
+	}
+	// increment counter
+	counter++
+	ann[annCounter] = strconv.Itoa(counter)
+	glog.Info("incrementing annotation %s counter to %d", annCounter, counter)
+	deploy.SetAnnotations(ann)
+	// update counter
+	_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
+	if err != nil {
+		glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annCounter, deployName, deployNamespace, err)
+		return err
+	}
+	return nil
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 0fc8a9fe48..8cb472a86d 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -15,6 +15,10 @@ const (
 	// ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
 	// ValidatingWebhookName               = "nirmata.kyverno.policy-validating-webhook"
 
+	VerifyMutatingWebhookConfigurationName      = "kyverno-verify-mutating-webhook-cfg"
+	VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
+	VerifyMutatingWebhookName                   = "nirmata.kyverno.verify-mutating-webhook"
+
 	PolicyValidatingWebhookConfigurationName      = "kyverno-policy-validating-webhook-cfg"
 	PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
 	PolicyValidatingWebhookName                   = "nirmata.kyverno.policy-validating-webhook"
@@ -36,6 +40,7 @@ var (
 	ValidatingWebhookServicePath       = "/validate"
 	PolicyValidatingWebhookServicePath = "/policyvalidate"
 	PolicyMutatingWebhookServicePath   = "/policymutate"
+	VerifyMutatingWebhookServicePath   = "/verifymutate"
 
 	SupportedKinds = []string{
 		"ConfigMap",
diff --git a/pkg/webhookconfig/checker.go b/pkg/webhookconfig/checker.go
new file mode 100644
index 0000000000..3494fc159f
--- /dev/null
+++ b/pkg/webhookconfig/checker.go
@@ -0,0 +1,78 @@
+package webhookconfig
+
+import (
+	"fmt"
+
+	"github.com/golang/glog"
+	"github.com/nirmata/kyverno/pkg/config"
+	admregapi "k8s.io/api/admissionregistration/v1beta1"
+	errorsapi "k8s.io/apimachinery/pkg/api/errors"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
+	return &admregapi.MutatingWebhookConfiguration{
+		ObjectMeta: v1.ObjectMeta{
+			Name: config.VerifyMutatingWebhookConfigurationName,
+			OwnerReferences: []v1.OwnerReference{
+				wrc.constructOwner(),
+			},
+		},
+		Webhooks: []admregapi.Webhook{
+			generateWebhook(
+				config.VerifyMutatingWebhookName,
+				config.VerifyMutatingWebhookServicePath,
+				caData,
+				true,
+				wrc.timeoutSeconds,
+				"deployments/*",
+				"apps",
+				"v1",
+				[]admregapi.OperationType{admregapi.Update},
+			),
+		},
+	}
+}
+
+func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
+	url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.VerifyMutatingWebhookServicePath)
+	glog.V(4).Infof("Debug VerifyMutatingWebhookConfig is registered with url %s\n", url)
+	return &admregapi.MutatingWebhookConfiguration{
+		ObjectMeta: v1.ObjectMeta{
+			Name: config.VerifyMutatingWebhookConfigurationDebugName,
+		},
+		Webhooks: []admregapi.Webhook{
+			generateDebugWebhook(
+				config.VerifyMutatingWebhookName,
+				url,
+				caData,
+				true,
+				wrc.timeoutSeconds,
+				"deployments/*",
+				"apps",
+				"v1",
+				[]admregapi.OperationType{admregapi.Update},
+			),
+		},
+	}
+}
+
+func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig() {
+	// Muating webhook configuration
+	var err error
+	var mutatingConfig string
+	if wrc.serverIP != "" {
+		mutatingConfig = config.VerifyMutatingWebhookConfigurationDebugName
+	} else {
+		mutatingConfig = config.VerifyMutatingWebhookConfigurationName
+	}
+	glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
+	err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
+	if errorsapi.IsNotFound(err) {
+		glog.V(4).Infof("verify webhook configuration %s, does not exits. not deleting", mutatingConfig)
+	} else if err != nil {
+		glog.Errorf("failed to delete verify webhook configuration %s: %v", mutatingConfig, err)
+	} else {
+		glog.V(4).Infof("succesfully deleted verify webhook configuration %s", mutatingConfig)
+	}
+}
diff --git a/pkg/webhookconfig/policy.go b/pkg/webhookconfig/policy.go
index 60a7b5bfae..41fc8e81af 100644
--- a/pkg/webhookconfig/policy.go
+++ b/pkg/webhookconfig/policy.go
@@ -6,6 +6,7 @@ import (
 	"github.com/golang/glog"
 	"github.com/nirmata/kyverno/pkg/config"
 	admregapi "k8s.io/api/admissionregistration/v1beta1"
+	errorsapi "k8s.io/apimachinery/pkg/api/errors"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -104,3 +105,43 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c
 		},
 	}
 }
+
+// removePolicyWebhookConfigurations removes mutating and validating webhook configurations, if already presnt
+// webhookConfigurations are re-created later
+func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
+	// Validating webhook configuration
+	var err error
+	var validatingConfig string
+	if wrc.serverIP != "" {
+		validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
+	} else {
+		validatingConfig = config.PolicyValidatingWebhookConfigurationName
+	}
+	glog.V(4).Infof("removing webhook configuration %s", validatingConfig)
+	err = wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
+	if errorsapi.IsNotFound(err) {
+		glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig)
+	} else if err != nil {
+		glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
+	} else {
+		glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
+	}
+
+	// Mutating webhook configuration
+	var mutatingConfig string
+	if wrc.serverIP != "" {
+		mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
+	} else {
+		mutatingConfig = config.PolicyMutatingWebhookConfigurationName
+	}
+
+	glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
+	err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
+	if errorsapi.IsNotFound(err) {
+		glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig)
+	} else if err != nil {
+		glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
+	} else {
+		glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
+	}
+}
diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go
index 2511a13167..409f9f66a0 100644
--- a/pkg/webhookconfig/registration.go
+++ b/pkg/webhookconfig/registration.go
@@ -64,6 +64,13 @@ func (wrc *WebhookRegistrationClient) Register() error {
 	if err := wrc.createPolicyMutatingWebhookConfiguration(); err != nil {
 		return err
 	}
+
+	// create Verify mutating webhook configuration resource
+	// that is used to check if admission control is enabled or not
+	if err := wrc.createVerifyMutatingWebhookConfiguration(); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -184,6 +191,36 @@ func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration()
 	return nil
 }
 
+func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration() error {
+	var caData []byte
+	var config *admregapi.MutatingWebhookConfiguration
+
+	// read CA data from
+	// 1) secret(config)
+	// 2) kubeconfig
+	if caData = wrc.readCaData(); caData == nil {
+		return errors.New("Unable to extract CA data from configuration")
+	}
+
+	// if serverIP is specified we assume its debug mode
+	if wrc.serverIP != "" {
+		// debug mode
+		// clientConfig - URL
+		config = wrc.constructDebugVerifyMutatingWebhookConfig(caData)
+	} else {
+		// clientConfig - service
+		config = wrc.constructVerifyMutatingWebhookConfig(caData)
+	}
+
+	// create mutating webhook configuration resource
+	if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil {
+		return err
+	}
+
+	glog.V(4).Infof("created Mutating Webhook Configuration %s ", config.Name)
+	return nil
+}
+
 // DeregisterAll deletes webhook configs from cluster
 // This function does not fail on error:
 // Register will fail if the config exists, so there is no need to fail on error
@@ -198,44 +235,7 @@ func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() {
 
 	// mutating and validating webhook configurtion for Policy CRD resource
 	wrc.removePolicyWebhookConfigurations()
-}
-
-// removePolicyWebhookConfigurations removes mutating and validating webhook configurations, if already presnt
-// webhookConfigurations are re-created later
-func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
-	// Validating webhook configuration
-	var err error
-	var validatingConfig string
-	if wrc.serverIP != "" {
-		validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
-	} else {
-		validatingConfig = config.PolicyValidatingWebhookConfigurationName
-	}
-	glog.V(4).Infof("removing webhook configuration %s", validatingConfig)
-	err = wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
-	if errorsapi.IsNotFound(err) {
-		glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig)
-	} else if err != nil {
-		glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
-	} else {
-		glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
-	}
-
-	// Mutating webhook configuration
-	var mutatingConfig string
-	if wrc.serverIP != "" {
-		mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
-	} else {
-		mutatingConfig = config.PolicyMutatingWebhookConfigurationName
-	}
-
-	glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
-	err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
-	if errorsapi.IsNotFound(err) {
-		glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig)
-	} else if err != nil {
-		glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
-	} else {
-		glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
-	}
+
+	// muating webhook configuration use to verify if admission control flow is working or not
+	wrc.removeVerifyWebhookMutatingWebhookConfig()
 }
diff --git a/pkg/webhooks/checker.go b/pkg/webhooks/checker.go
index 828b6dc0ca..e6ccfc41c9 100644
--- a/pkg/webhooks/checker.go
+++ b/pkg/webhooks/checker.go
@@ -1,91 +1,14 @@
 package webhooks
 
 import (
-	"sync"
-	"time"
-
 	"github.com/golang/glog"
-	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
-	kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
+	"k8s.io/api/admission/v1beta1"
 )
 
-const MaxRetryCount int = 3
-
-// Last Request Time
-type LastReqTime struct {
-	t          time.Time
-	mu         sync.RWMutex
-	RetryCount int
-}
-
-func (t *LastReqTime) Time() time.Time {
-	t.mu.RLock()
-	defer t.mu.RUnlock()
-	return t.t
-}
-
-func (t *LastReqTime) SetTime(tm time.Time) {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	t.t = tm
-	t.RetryCount = MaxRetryCount
-}
-
-func (t *LastReqTime) DecrementRetryCounter() {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	t.RetryCount--
-}
-
-func NewLastReqTime() *LastReqTime {
-	return &LastReqTime{
-		t: time.Now(),
+func (ws *WebhookServer) handleVerifyRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
+	glog.V(4).Infof("Receive request in mutating webhook '/verify': Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
+		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
+	return &v1beta1.AdmissionResponse{
+		Allowed: true,
 	}
 }
-
-func (t *LastReqTime) checker(kyvernoClient *kyvernoclient.Clientset, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
-	sendDummyRequest := func(kyvernoClient *kyvernoclient.Clientset) {
-		dummyPolicy := kyverno.ClusterPolicy{
-			Spec: kyverno.Spec{
-				Rules: []kyverno.Rule{
-					kyverno.Rule{
-						Name: "dummyPolicy",
-						MatchResources: kyverno.MatchResources{
-							ResourceDescription: kyverno.ResourceDescription{
-								Kinds: []string{"Deployment"},
-							},
-						},
-						Validation: kyverno.Validation{
-							Message: "dummy validation policy rule",
-							Pattern: "dummypattern",
-						},
-					},
-				},
-			},
-		}
-		// this
-		kyvernoClient.KyvernoV1alpha1().ClusterPolicies().Create(&dummyPolicy)
-	}
-	glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync)
-	ticker := time.NewTicker(defaultResync)
-	defer ticker.Stop()
-	for {
-		select {
-		case <-ticker.C:
-			// get current time
-			timeDiff := time.Since(t.Time())
-			if timeDiff > deadline {
-				if t.RetryCount == 0 {
-					// set the status unavailable
-				}
-				t.DecrementRetryCounter()
-				// send request again
-			}
-
-		case <-stopCh:
-			// handler termination signal
-			break
-		}
-	}
-	glog.V(2).Info("stopping default resync for webhook checker")
-}
diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go
index a6871631a9..f27100adc5 100644
--- a/pkg/webhooks/mutation.go
+++ b/pkg/webhooks/mutation.go
@@ -11,7 +11,7 @@ import (
 )
 
 // HandleMutation handles mutating webhook admission request
-func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
+func (ws *WebhookServer) handleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
 	glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
 		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
 
diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go
index 7f7d95dd56..b42c19368a 100644
--- a/pkg/webhooks/policyvalidation.go
+++ b/pkg/webhooks/policyvalidation.go
@@ -27,7 +27,6 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
 				Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
 			}}
 	}
-
 	if err := policyvalidate.Validate(*policy); err != nil {
 		admissionResp = &v1beta1.AdmissionResponse{
 			Allowed: false,
diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go
index f70dfbeab8..204d7a4f81 100644
--- a/pkg/webhooks/server.go
+++ b/pkg/webhooks/server.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/golang/glog"
+	"github.com/nirmata/kyverno/pkg/checker"
 	kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
 	kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
 	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
@@ -43,6 +44,8 @@ type WebhookServer struct {
 	configHandler config.Interface
 	// channel for cleanup notification
 	cleanUp chan<- struct{}
+	// last request time
+	lastReqTime *checker.LastReqTime
 }
 
 // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
@@ -83,6 +86,7 @@ func NewWebhookServer(
 		policyStatus:              policyStatus,
 		configHandler:             configHandler,
 		cleanUp:                   cleanUp,
+		lastReqTime:               checker.NewLastReqTime(),
 	}
 	mux := http.NewServeMux()
 	mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
@@ -103,6 +107,9 @@ func NewWebhookServer(
 
 // Main server endpoint for all requests
 func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
+	// for every request recieved on the ep update last request time,
+	// this is used to verify admission control
+	ws.lastReqTime.SetTime(time.Now())
 	admissionReview := ws.bodyToAdmissionReview(r, w)
 	if admissionReview == nil {
 		return
@@ -114,19 +121,24 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
 
 	// Do not process the admission requests for kinds that are in filterKinds for filtering
 	request := admissionReview.Request
-	if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
-		// Resource CREATE
-		// Resource UPDATE
-		switch r.URL.Path {
-		case config.MutatingWebhookServicePath:
+	switch r.URL.Path {
+	case config.VerifyMutatingWebhookServicePath:
+		// we do not apply filters as this endpoint is used explicity
+		// to watch kyveno deployment and verify if admission control is enabled
+		admissionReview.Response = ws.handleVerifyRequest(request)
+	case config.MutatingWebhookServicePath:
+		if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
 			admissionReview.Response = ws.handleAdmissionRequest(request)
-		case config.PolicyValidatingWebhookServicePath:
+		}
+	case config.PolicyValidatingWebhookServicePath:
+		if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
 			admissionReview.Response = ws.handlePolicyValidation(request)
-		case config.PolicyMutatingWebhookServicePath:
+		}
+	case config.PolicyMutatingWebhookServicePath:
+		if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
 			admissionReview.Response = ws.handlePolicyMutation(request)
 		}
 	}
-
 	admissionReview.Response.UID = request.UID
 
 	responseJSON, err := json.Marshal(admissionReview)
@@ -143,7 +155,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
 
 func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
 	// MUTATION
-	ok, patches, msg := ws.HandleMutation(request)
+	ok, patches, msg := ws.handleMutation(request)
 	if !ok {
 		glog.V(4).Infof("Deny admission request:  %v/%s/%s", request.Kind, request.Namespace, request.Name)
 		return &v1beta1.AdmissionResponse{
@@ -159,7 +171,7 @@ func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionReques
 	patchedResource := processResourceWithPatches(patches, request.Object.Raw)
 
 	// VALIDATION
-	ok, msg = ws.HandleValidation(request, patchedResource)
+	ok, msg = ws.handleValidation(request, patchedResource)
 	if !ok {
 		glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
 		return &v1beta1.AdmissionResponse{
@@ -192,8 +204,11 @@ func (ws *WebhookServer) RunAsync(stopCh <-chan struct{}) {
 		}
 	}(ws)
 	glog.Info("Started Webhook Server")
-
-	go checker(10*time.Second, stopCh)
+	// verifys if the admission control is enabled and active
+	// resync: 60 seconds
+	// deadline: 60 seconds (send request)
+	// max deadline: deadline*3 (set the deployment annotation as false)
+	go ws.lastReqTime.Run(ws.pLister, ws.client, 60*time.Second, 60*time.Second, stopCh)
 }
 
 // Stop TLS server and returns control after the server is shut down
diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go
index 7623f647ec..8fb269e24d 100644
--- a/pkg/webhooks/validation.go
+++ b/pkg/webhooks/validation.go
@@ -11,10 +11,10 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 )
 
-// HandleValidation handles validating webhook admission request
+// handleValidation handles validating webhook admission request
 // If there are no errors in validating rule we apply generation rules
 // patchedResource is the (resource + patches) after applying mutation rules
-func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, patchedResource []byte) (bool, string) {
+func (ws *WebhookServer) handleValidation(request *v1beta1.AdmissionRequest, patchedResource []byte) (bool, string) {
 	glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
 		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)