Repository: kyverno/kyverno
Pull request #1539 (2021-02-04 11:12:53)

Add AND logical operator support
Related issue: #1538

**What type of PR is this?**
/kind feature

## Proposed changes
I have added operator '&' support for validation scalar values (strings, integers, etc.)
The reason for this changes described in feature request #1538 

## Checklist
- [x] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).
- [x] I have added tests that prove my fix is effective or that my feature
      works.
- [x] I have added or changed [the documentation](documentation/).



Patch:
diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index dd015ae43..581d3f76b 100755
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -138,6 +138,7 @@ func main() {
 	webhookCfg := webhookconfig.NewRegister(
 		clientConfig,
 		client,
+		rCache,
 		serverIP,
 		int32(webhookTimeout),
 		log.Log)
@@ -171,6 +172,7 @@ func main() {
 	eventGenerator := event.NewEventGenerator(
 		client,
 		pInformer.Kyverno().V1().ClusterPolicies(),
+		rCache,
 		log.Log.WithName("EventGenerator"))
 
 	// Policy Status Handler - deals with all logic related to policy status
diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go
index 255b6a53c..757475991 100644
--- a/pkg/dclient/client.go
+++ b/pkg/dclient/client.go
@@ -8,8 +8,6 @@ import (
 
 	"github.com/go-logr/logr"
 	openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
-	"github.com/kyverno/kyverno/pkg/config"
-	apps "k8s.io/api/apps/v1"
 	certificates "k8s.io/api/certificates/v1beta1"
 	v1 "k8s.io/api/core/v1"
 	helperv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -78,19 +76,6 @@ func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dy
 	return dynamicinformer.NewDynamicSharedInformerFactory(c.client, defaultResync)
 }
 
-//GetKubePolicyDeployment returns kube policy depoyment value
-func (c *Client) GetKubePolicyDeployment() (*apps.Deployment, error) {
-	kubePolicyDeployment, err := c.GetResource("", "Deployment", config.KyvernoNamespace, config.KyvernoDeploymentName)
-	if err != nil {
-		return nil, err
-	}
-	deploy := apps.Deployment{}
-	if err = runtime.DefaultUnstructuredConverter.FromUnstructured(kubePolicyDeployment.UnstructuredContent(), &deploy); err != nil {
-		return nil, err
-	}
-	return &deploy, nil
-}
-
 //GetEventsInterface provides typed interface for events
 //TODO: can we use dynamic client to fetch the typed interface
 // or generate a kube client value to access the interface
diff --git a/pkg/engine/validate/pattern.go b/pkg/engine/validate/pattern.go
index a08f7d4e2..46e9c1cf9 100644
--- a/pkg/engine/validate/pattern.go
+++ b/pkg/engine/validate/pattern.go
@@ -154,10 +154,10 @@ func validateValueWithNilPattern(log logr.Logger, value interface{}) bool {
 
 // Handler for pattern values during validation process
 func validateValueWithStringPatterns(log logr.Logger, value interface{}, pattern string) bool {
-	statements := strings.Split(pattern, "|")
-	for _, statement := range statements {
-		statement = strings.Trim(statement, " ")
-		if validateValueWithStringPattern(log, value, statement) {
+	conditions := strings.Split(pattern, "|")
+	for _, condition := range conditions {
+		condition = strings.Trim(condition, " ")
+		if checkForAndConditionsAndValidate(log, value, condition) {
 			return true
 		}
 	}
@@ -165,6 +165,18 @@ func validateValueWithStringPatterns(log logr.Logger, value interface{}, pattern
 	return false
 }
 
+func checkForAndConditionsAndValidate(log logr.Logger, value interface{}, pattern string) bool {
+	conditions := strings.Split(pattern, "&")
+	for _, condition := range conditions {
+		condition = strings.Trim(condition, " ")
+		if !validateValueWithStringPattern(log, value, condition) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Handler for single pattern value during validation process
 // Detects if pattern has a number
 func validateValueWithStringPattern(log logr.Logger, value interface{}, pattern string) bool {
diff --git a/pkg/event/controller.go b/pkg/event/controller.go
index 7925a2845..bf1f5c1cb 100644
--- a/pkg/event/controller.go
+++ b/pkg/event/controller.go
@@ -8,8 +8,10 @@ import (
 	kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
 	kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
 	client "github.com/kyverno/kyverno/pkg/dclient"
+	"github.com/kyverno/kyverno/pkg/resourcecache"
 	v1 "k8s.io/api/core/v1"
 	errors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -35,6 +37,7 @@ type Generator struct {
 	admissionCtrRecorder record.EventRecorder
 	// events generated at namespaced policy controller to process 'generate' rule
 	genPolicyRecorder record.EventRecorder
+	resCache          resourcecache.ResourceCache
 	log               logr.Logger
 }
 
@@ -44,7 +47,7 @@ type Interface interface {
 }
 
 //NewEventGenerator to generate a new event controller
-func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, log logr.Logger) *Generator {
+func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, resCache resourcecache.ResourceCache, log logr.Logger) *Generator {
 
 	gen := Generator{
 		client:               client,
@@ -54,6 +57,7 @@ func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterP
 		policyCtrRecorder:    initRecorder(client, PolicyController, log),
 		admissionCtrRecorder: initRecorder(client, AdmissionController, log),
 		genPolicyRecorder:    initRecorder(client, GeneratePolicyController, log),
+		resCache:             resCache,
 		log:                  log,
 	}
 	return &gen
@@ -185,7 +189,7 @@ func (gen *Generator) syncHandler(key Info) error {
 			return err
 		}
 	default:
-		robj, err = gen.client.GetResource("", key.Kind, key.Namespace, key.Name)
+		robj, err = gen.getResource(key)
 		if err != nil {
 			if !errors.IsNotFound(err) {
 				logger.Error(err, "failed to get resource", "kind", key.Kind, "name", key.Name, "namespace", key.Namespace)
@@ -211,6 +215,21 @@ func (gen *Generator) syncHandler(key Info) error {
 	return nil
 }
 
+func (gen *Generator) getResource(key Info) (obj *unstructured.Unstructured, err error) {
+	lister, ok := gen.resCache.GetGVRCache(key.Kind)
+	if !ok {
+		if lister, err = gen.resCache.CreateResourceInformer(key.Kind); err != nil {
+			return nil, err
+		}
+	}
+
+	if key.Namespace == "" {
+		return lister.Lister().Get(key.Name)
+	}
+
+	return lister.Lister().Namespace(key.Namespace).Get(key.Name)
+}
+
 //NewEvent builds a event creation request
 func NewEvent(
 	log logr.Logger,
diff --git a/pkg/kyverno/apply/command.go b/pkg/kyverno/apply/command.go
index 34efcec10..573c8e75c 100644
--- a/pkg/kyverno/apply/command.go
+++ b/pkg/kyverno/apply/command.go
@@ -1,10 +1,7 @@
 package apply
 
 import (
-	"bufio"
-	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -14,21 +11,17 @@ import (
 	v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
 	pkgCommon "github.com/kyverno/kyverno/pkg/common"
 	client "github.com/kyverno/kyverno/pkg/dclient"
-	"github.com/kyverno/kyverno/pkg/engine"
-	"github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/kyverno/common"
 	sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
 	"github.com/kyverno/kyverno/pkg/openapi"
 	policy2 "github.com/kyverno/kyverno/pkg/policy"
-	"github.com/kyverno/kyverno/pkg/utils"
 	"github.com/spf13/cobra"
-	yamlv2 "gopkg.in/yaml.v2"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/util/yaml"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	log "sigs.k8s.io/controller-runtime/pkg/log"
 	yaml1 "sigs.k8s.io/yaml"
+	"github.com/go-git/go-billy/v5/memfs"
 )
 
 type resultCounts struct {
@@ -148,12 +141,13 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 	variablesString string, valuesFile string, namespace string, policyPaths []string) (validateEngineResponses []*response.EngineResponse, rc *resultCounts, resources []*unstructured.Unstructured, skippedPolicies []SkippedPolicy, err error) {
 
 	kubernetesConfig := genericclioptions.NewConfigFlags(true)
+	fs := memfs.New()
 
 	if valuesFile != "" && variablesString != "" {
 		return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("pass the values either using set flag or values_file flag", err)
 	}
 
-	variables, valuesMap, err := getVariable(variablesString, valuesFile)
+	variables, valuesMap, err :=  common.GetVariable(variablesString, valuesFile)
 	if err != nil {
 		if !sanitizederror.IsErrorSanitized(err) {
 			return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("failed to decode yaml", err)
@@ -186,7 +180,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 		return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("a stdin pipe can be used for either policies or resources, not both", err)
 	}
 
-	policies, err := getPoliciesFromPaths(policyPaths)
+	policies, err := common.GetPoliciesFromPaths(fs,policyPaths, false)
 	if err != nil {
 		fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
 		os.Exit(1)
@@ -204,14 +198,14 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 		return validateEngineResponses, rc, resources, skippedPolicies, err
 	}
 
-	mutatedPolicies, err := mutatePolices(policies)
+	mutatedPolicies, err := common.MutatePolices(policies)
 	if err != nil {
 		if !sanitizederror.IsErrorSanitized(err) {
 			return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("failed to mutate policy", err)
 		}
 	}
 
-	resources, err = getResourceAccordingToResourcePath(resourcePaths, cluster, mutatedPolicies, dClient, namespace, policyReport)
+	resources, err = common.GetResourceAccordingToResourcePath(fs, resourcePaths, cluster, mutatedPolicies, dClient, namespace, policyReport, false)
 	if err != nil {
 		fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
 		os.Exit(1)
@@ -245,7 +239,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 		}
 
 		matches := common.PolicyHasVariables(*policy)
-		variable := removeDuplicatevariables(matches)
+		variable := common.RemoveDuplicateVariables(matches)
 
 		if len(matches) > 0 && variablesString == "" && valuesFile == "" {
 			rc.skip++
@@ -274,11 +268,18 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 				return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
 			}
 
-			ers, validateErs, err := applyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, rc, policyReport)
+			ers, validateErs, responseError, rcErs, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport)
 			if err != nil {
 				return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
 			}
-
+			if responseError == true {
+				rc.fail++
+			} else {
+				rc.pass++
+			}
+			if rcErs == true {
+				rc.error++
+			}
 			engineResponses = append(engineResponses, ers...)
 			validateEngineResponses = append(validateEngineResponses, validateErs)
 		}
@@ -287,44 +288,6 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
 	return validateEngineResponses, rc, resources, skippedPolicies, nil
 }
 
-// getVariable - get the variables from console/file
-func getVariable(variablesString, valuesFile string) (variables map[string]string, valuesMap map[string]map[string]Resource, err error) {
-	if variablesString != "" {
-		kvpairs := strings.Split(strings.Trim(variablesString, " "), ",")
-		for _, kvpair := range kvpairs {
-			kvs := strings.Split(strings.Trim(kvpair, " "), "=")
-			variables[strings.Trim(kvs[0], " ")] = strings.Trim(kvs[1], " ")
-		}
-	}
-
-	if valuesFile != "" {
-		yamlFile, err := ioutil.ReadFile(valuesFile)
-		if err != nil {
-			return variables, valuesMap, sanitizederror.NewWithError("unable to read yaml", err)
-		}
-
-		valuesBytes, err := yaml.ToJSON(yamlFile)
-		if err != nil {
-			return variables, valuesMap, sanitizederror.NewWithError("failed to convert json", err)
-		}
-
-		values := &Values{}
-		if err := json.Unmarshal(valuesBytes, values); err != nil {
-			return variables, valuesMap, sanitizederror.NewWithError("failed to decode yaml", err)
-		}
-
-		for _, p := range values.Policies {
-			pmap := make(map[string]Resource)
-			for _, r := range p.Resources {
-				pmap[r.Name] = r
-			}
-			valuesMap[p.Name] = pmap
-		}
-	}
-
-	return variables, valuesMap, nil
-}
-
 // checkMutateLogPath - checking path for printing mutated resource (-o flag)
 func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err error) {
 	if mutateLogPath != "" {
@@ -347,68 +310,6 @@ func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err erro
 	return mutateLogPathIsDir, err
 }
 
-// getPoliciesFromPaths - get policies according to the resource path
-func getPoliciesFromPaths(policyPaths []string) (policies []*v1.ClusterPolicy, err error) {
-	if len(policyPaths) > 0 && policyPaths[0] == "-" {
-		if common.IsInputFromPipe() {
-			policyStr := ""
-			scanner := bufio.NewScanner(os.Stdin)
-			for scanner.Scan() {
-				policyStr = policyStr + scanner.Text() + "\n"
-			}
-
-			yamlBytes := []byte(policyStr)
-			policies, err = utils.GetPolicy(yamlBytes)
-			if err != nil {
-				return nil, sanitizederror.NewWithError("failed to extract the resources", err)
-			}
-		}
-	} else {
-		var errors []error
-		policies, errors = common.GetPolicies(policyPaths)
-		if len(policies) == 0 {
-			if len(errors) > 0 {
-				return nil, sanitizederror.NewWithErrors("failed to read policies", errors)
-			}
-
-			return nil, sanitizederror.New(fmt.Sprintf("no policies found in paths %v", policyPaths))
-		}
-
-		if len(errors) > 0 && log.Log.V(1).Enabled() {
-			fmt.Printf("ignoring errors: \n")
-			for _, e := range errors {
-				fmt.Printf("    %v \n", e.Error())
-			}
-		}
-	}
-	return
-}
-
-// getResourceAccordingToResourcePath - get resources according to the resource path
-func getResourceAccordingToResourcePath(resourcePaths []string, cluster bool, policies []*v1.ClusterPolicy, dClient *client.Client, namespace string, policyReport bool) (resources []*unstructured.Unstructured, err error) {
-	if len(resourcePaths) > 0 && resourcePaths[0] == "-" {
-		if common.IsInputFromPipe() {
-			resourceStr := ""
-			scanner := bufio.NewScanner(os.Stdin)
-			for scanner.Scan() {
-				resourceStr = resourceStr + scanner.Text() + "\n"
-			}
-
-			yamlBytes := []byte(resourceStr)
-			resources, err = common.GetResource(yamlBytes)
-			if err != nil {
-				return nil, sanitizederror.NewWithError("failed to extract the resources", err)
-			}
-		}
-	} else if (len(resourcePaths) > 0 && resourcePaths[0] != "-") || len(resourcePaths) < 0 || cluster {
-		resources, err = common.GetResources(policies, resourcePaths, dClient, cluster, namespace, policyReport)
-		if err != nil {
-			return resources, err
-		}
-	}
-	return resources, err
-}
-
 // printReportOrViolation - printing policy report/violations
 func printReportOrViolation(policyReport bool, validateEngineResponses []*response.EngineResponse, rc *resultCounts, resourcePaths []string, resourcesLen int, skippedPolicies []SkippedPolicy) {
 	if policyReport {
@@ -437,165 +338,6 @@ func printReportOrViolation(policyReport bool, validateEngineResponses []*respon
 	}
 }
 
-// applyPolicyOnResource - function to apply policy on resource
-func applyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured,
-	mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string,
-	rc *resultCounts, policyReport bool) ([]*response.EngineResponse, *response.EngineResponse, error) {
-
-	responseError := false
-	engineResponses := make([]*response.EngineResponse, 0)
-
-	resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
-	log.Log.V(3).Info("applying policy on resource", "policy", policy.Name, "resource", resPath)
-
-	ctx := context.NewContext()
-	for key, value := range variables {
-		startString := ""
-		endString := ""
-		for _, k := range strings.Split(key, ".") {
-			startString += fmt.Sprintf(`{"%s":`, k)
-			endString += `}`
-		}
-
-		midString := fmt.Sprintf(`"%s"`, value)
-		finalString := startString + midString + endString
-		var jsonData = []byte(finalString)
-		ctx.AddJSON(jsonData)
-	}
-
-	mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx})
-	engineResponses = append(engineResponses, mutateResponse)
-
-	if !mutateResponse.IsSuccessful() {
-		fmt.Printf("Failed to apply mutate policy %s -> resource %s", policy.Name, resPath)
-		for i, r := range mutateResponse.PolicyResponse.Rules {
-			fmt.Printf("\n%d. %s", i+1, r.Message)
-		}
-		responseError = true
-	} else {
-		if len(mutateResponse.PolicyResponse.Rules) > 0 {
-			yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
-			if err != nil {
-				rc.error++
-			}
-
-			if mutateLogPath == "" {
-				mutatedResource := string(yamlEncodedResource)
-				if len(strings.TrimSpace(mutatedResource)) > 0 {
-					fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath)
-					fmt.Printf("\n" + mutatedResource)
-					fmt.Printf("\n")
-				}
-			} else {
-				err := printMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resource.GetName()+"-mutated")
-				if err != nil {
-					return engineResponses, &response.EngineResponse{}, sanitizederror.NewWithError("failed to print mutated result", err)
-				}
-				fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.")
-			}
-
-		}
-	}
-
-	if resource.GetKind() == "Pod" && len(resource.GetOwnerReferences()) > 0 {
-		if policy.HasAutoGenAnnotation() {
-			if _, ok := policy.GetAnnotations()[engine.PodControllersAnnotation]; ok {
-				delete(policy.Annotations, engine.PodControllersAnnotation)
-			}
-		}
-	}
-
-	policyCtx := &engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource, JSONContext: ctx}
-	validateResponse := engine.Validate(policyCtx)
-	if !policyReport {
-		if !validateResponse.IsSuccessful() {
-			fmt.Printf("\npolicy %s -> resource %s failed: \n", policy.Name, resPath)
-			for i, r := range validateResponse.PolicyResponse.Rules {
-				if !r.Success {
-					fmt.Printf("%d. %s: %s \n", i+1, r.Name, r.Message)
-				}
-			}
-
-			responseError = true
-		}
-	}
-
-	var policyHasGenerate bool
-	for _, rule := range policy.Spec.Rules {
-		if rule.HasGenerate() {
-			policyHasGenerate = true
-		}
-	}
-
-	if policyHasGenerate {
-		ctx := &engine.PolicyContext{Policy: *policy, NewResource: *resource}
-		generateResponse := engine.Generate(ctx)
-		engineResponses = append(engineResponses, generateResponse)
-		if len(generateResponse.PolicyResponse.Rules) > 0 {
-			log.Log.V(3).Info("generate resource is valid", "policy", policy.Name, "resource", resPath)
-		} else {
-			fmt.Printf("generate policy %s resource %s is invalid \n", policy.Name, resPath)
-			for i, r := range generateResponse.PolicyResponse.Rules {
-				fmt.Printf("%d. %s \b", i+1, r.Message)
-			}
-
-			responseError = true
-		}
-	}
-
-	if responseError == true {
-		rc.fail++
-	} else {
-		rc.pass++
-	}
-
-	return engineResponses, validateResponse, nil
-}
-
-// mutatePolicies - function to apply mutation on policies
-func mutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
-	newPolicies := make([]*v1.ClusterPolicy, 0)
-	logger := log.Log.WithName("apply")
-
-	for _, policy := range policies {
-		p, err := common.MutatePolicy(policy, logger)
-		if err != nil {
-			if !sanitizederror.IsErrorSanitized(err) {
-				return nil, sanitizederror.NewWithError("failed to mutate policy.", err)
-			}
-			return nil, err
-		}
-		newPolicies = append(newPolicies, p)
-	}
-	return newPolicies, nil
-}
-
-// printMutatedOutput - function to print output in provided file or directory
-func printMutatedOutput(mutateLogPath string, mutateLogPathIsDir bool, yaml string, fileName string) error {
-	var f *os.File
-	var err error
-	yaml = yaml + ("\n---\n\n")
-
-	if !mutateLogPathIsDir {
-		f, err = os.OpenFile(mutateLogPath, os.O_APPEND|os.O_WRONLY, 0644)
-	} else {
-		f, err = os.OpenFile(mutateLogPath+"/"+fileName+".yaml", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
-	}
-
-	if err != nil {
-		return err
-	}
-	if _, err := f.Write([]byte(yaml)); err != nil {
-		f.Close()
-		return err
-	}
-	if err := f.Close(); err != nil {
-		return err
-	}
-
-	return nil
-}
-
 // createFileOrFolder - creating file or folder according to path provided
 func createFileOrFolder(mutateLogPath string, mutateLogPathIsDir bool) error {
 	mutateLogPath = filepath.Clean(mutateLogPath)
@@ -644,16 +386,3 @@ func createFileOrFolder(mutateLogPath string, mutateLogPathIsDir bool) error {
 	return nil
 }
 
-// removeDuplicatevariables - remove duplicate variables
-func removeDuplicatevariables(matches [][]string) string {
-	var variableStr string
-	for _, m := range matches {
-		for _, v := range m {
-			foundVariable := strings.Contains(variableStr, v)
-			if !foundVariable {
-				variableStr = variableStr + " " + v
-			}
-		}
-	}
-	return variableStr
-}
diff --git a/pkg/kyverno/common/common.go b/pkg/kyverno/common/common.go
index 751c722f3..8ab85bf9b 100644
--- a/pkg/kyverno/common/common.go
+++ b/pkg/kyverno/common/common.go
@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"sigs.k8s.io/controller-runtime/pkg/log"
 
 	jsonpatch "github.com/evanphx/json-patch"
@@ -20,9 +21,32 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/util/yaml"
 	yaml_v2 "sigs.k8s.io/yaml"
+	"github.com/kyverno/kyverno/pkg/engine"
+	"github.com/kyverno/kyverno/pkg/engine/context"
+	"github.com/kyverno/kyverno/pkg/engine/response"
+	yamlv2 "gopkg.in/yaml.v2"
+	"github.com/go-git/go-billy/v5"
+	ut "github.com/kyverno/kyverno/pkg/utils"
+	client "github.com/kyverno/kyverno/pkg/dclient"
 )
 
 // GetPolicies - Extracting the policies from multiple YAML
+
+type Resource struct {
+	Name   string            `json:"name"`
+	Values map[string]string `json:"values"`
+}
+
+type Policy struct {
+	Name      string     `json:"name"`
+	Resources []Resource `json:"resources"`
+}
+
+type Values struct {
+	Policies []Policy `json:"policies"`
+}
+
+
 func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error) {
 	for _, path := range paths {
 		log.Log.V(5).Info("reading policies", "path", path)
@@ -222,3 +246,300 @@ func IsInputFromPipe() bool {
 	fileInfo, _ := os.Stdin.Stat()
 	return fileInfo.Mode()&os.ModeCharDevice == 0
 }
+
+
+// RemoveDuplicateVariables - remove duplicate variables
+func RemoveDuplicateVariables(matches [][]string) string {
+	var variableStr string
+	for _, m := range matches {
+		for _, v := range m {
+			foundVariable := strings.Contains(variableStr, v)
+			if !foundVariable {
+				variableStr = variableStr + " " + v
+			}
+		}
+	}
+	return variableStr
+}
+
+// GetVariable - get the variables from console/file
+func GetVariable(variablesString, valuesFile string) (  map[string]string,  map[string]map[string]Resource,  error) {
+	valuesMap := make(map[string]map[string]Resource)
+	variables := make(map[string]string)
+	if variablesString != "" {
+		kvpairs := strings.Split(strings.Trim(variablesString, " "), ",")
+		for _, kvpair := range kvpairs {
+			kvs := strings.Split(strings.Trim(kvpair, " "), "=")
+			variables[strings.Trim(kvs[0], " ")] = strings.Trim(kvs[1], " ")
+		}
+	}
+	if valuesFile != "" {
+		yamlFile, err := ioutil.ReadFile(valuesFile)
+		if err != nil {
+			return variables, valuesMap, sanitizederror.NewWithError("unable to read yaml", err)
+		}
+
+		valuesBytes, err := yaml.ToJSON(yamlFile)
+		if err != nil {
+			return variables, valuesMap, sanitizederror.NewWithError("failed to convert json", err)
+		}
+
+		values := &Values{}
+		if err := json.Unmarshal(valuesBytes, values); err != nil {
+			return variables, valuesMap, sanitizederror.NewWithError("failed to decode yaml", err)
+		}
+
+		for _, p := range values.Policies {
+			pmap := make(map[string]Resource)
+			for _, r := range p.Resources {
+				pmap[r.Name] = r
+			}
+			valuesMap[p.Name] = pmap
+		}
+	}
+
+	return variables, valuesMap, nil
+}
+
+// MutatePolices - function to apply mutation on policies
+func MutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
+	newPolicies := make([]*v1.ClusterPolicy, 0)
+	logger := log.Log.WithName("apply")
+
+	for _, policy := range policies {
+		p, err := MutatePolicy(policy, logger)
+		if err != nil {
+			if !sanitizederror.IsErrorSanitized(err) {
+				return nil, sanitizederror.NewWithError("failed to mutate policy.", err)
+			}
+			return nil, err
+		}
+		newPolicies = append(newPolicies, p)
+	}
+	return newPolicies, nil
+}
+
+// ApplyPolicyOnResource - function to apply policy on resource
+func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured,
+	mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool) ([]*response.EngineResponse, *response.EngineResponse,  bool,  bool, error) {
+
+	responseError := false
+	rcError := false
+	engineResponses := make([]*response.EngineResponse, 0)
+
+	resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
+	log.Log.V(3).Info("applying policy on resource", "policy", policy.Name, "resource", resPath)
+
+	ctx := context.NewContext()
+	for key, value := range variables {
+		startString := ""
+		endString := ""
+		for _, k := range strings.Split(key, ".") {
+			startString += fmt.Sprintf(`{"%s":`, k)
+			endString += `}`
+		}
+
+		midString := fmt.Sprintf(`"%s"`, value)
+		finalString := startString + midString + endString
+		var jsonData = []byte(finalString)
+		ctx.AddJSON(jsonData)
+	}
+
+	mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx})
+	engineResponses = append(engineResponses, mutateResponse)
+
+	if !mutateResponse.IsSuccessful() {
+		fmt.Printf("Failed to apply mutate policy %s -> resource %s", policy.Name, resPath)
+		for i, r := range mutateResponse.PolicyResponse.Rules {
+			fmt.Printf("\n%d. %s", i+1, r.Message)
+		}
+		responseError = true
+	} else {
+		if len(mutateResponse.PolicyResponse.Rules) > 0 {
+			yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
+			if err != nil {
+				rcError = true
+			}
+
+			if mutateLogPath == "" {
+				mutatedResource := string(yamlEncodedResource)
+				if len(strings.TrimSpace(mutatedResource)) > 0 {
+					fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath)
+					fmt.Printf("\n" + mutatedResource)
+					fmt.Printf("\n")
+				}
+			} else {
+				err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resource.GetName()+"-mutated")
+				if err != nil {
+					return engineResponses, &response.EngineResponse{}, responseError, rcError, sanitizederror.NewWithError("failed to print mutated result", err)
+				}
+				fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.")
+			}
+
+		}
+	}
+
+	if resource.GetKind() == "Pod" && len(resource.GetOwnerReferences()) > 0 {
+		if policy.HasAutoGenAnnotation() {
+			if _, ok := policy.GetAnnotations()[engine.PodControllersAnnotation]; ok {
+				delete(policy.Annotations, engine.PodControllersAnnotation)
+			}
+		}
+	}
+
+	policyCtx := &engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource, JSONContext: ctx}
+	validateResponse := engine.Validate(policyCtx)
+	if !policyReport {
+		if !validateResponse.IsSuccessful() {
+			fmt.Printf("\npolicy %s -> resource %s failed: \n", policy.Name, resPath)
+			for i, r := range validateResponse.PolicyResponse.Rules {
+				if !r.Success {
+					fmt.Printf("%d. %s: %s \n", i+1, r.Name, r.Message)
+				}
+			}
+
+			responseError = true
+		}
+	}
+
+	var policyHasGenerate bool
+	for _, rule := range policy.Spec.Rules {
+		if rule.HasGenerate() {
+			policyHasGenerate = true
+		}
+	}
+
+	if policyHasGenerate {
+		generateResponse := engine.Generate(&engine.PolicyContext{Policy: *policy, NewResource: *resource})
+		engineResponses = append(engineResponses, generateResponse)
+		if len(generateResponse.PolicyResponse.Rules) > 0 {
+			log.Log.V(3).Info("generate resource is valid", "policy", policy.Name, "resource", resPath)
+		} else {
+			fmt.Printf("generate policy %s resource %s is invalid \n", policy.Name, resPath)
+			for i, r := range generateResponse.PolicyResponse.Rules {
+				fmt.Printf("%d. %s \b", i+1, r.Message)
+			}
+
+			responseError = true
+		}
+	}
+	
+	return engineResponses, validateResponse, responseError, rcError, nil
+}
+
+// PrintMutatedOutput - function to print output in provided file or directory
+func PrintMutatedOutput(mutateLogPath string, mutateLogPathIsDir bool, yaml string, fileName string) error {
+	var f *os.File
+	var err error
+	yaml = yaml + ("\n---\n\n")
+
+	if !mutateLogPathIsDir {
+		f, err = os.OpenFile(mutateLogPath, os.O_APPEND|os.O_WRONLY, 0644)
+	} else {
+		f, err = os.OpenFile(mutateLogPath+"/"+fileName+".yaml", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+	}
+
+	if err != nil {
+		return err
+	}
+	if _, err := f.Write([]byte(yaml)); err != nil {
+		f.Close()
+		return err
+	}
+	if err := f.Close(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// GetPoliciesFromPaths - get policies according to the resource path
+func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool) (policies []*v1.ClusterPolicy, err error) {
+	var errors []error
+	if isGit {
+		for _, pp := range dirPath {
+			filep, err := fs.Open(pp)
+			bytes, err := ioutil.ReadAll(filep)
+			if err != nil {
+				fmt.Printf("Error: failed to read file %s: %v", filep.Name(), err.Error())
+			}
+			policyBytes, err := yaml.ToJSON(bytes)
+			if err != nil {
+				fmt.Printf("failed to convert to JSON: %v", err)
+				continue
+			}
+			policiesFromFile, errFromFile := ut.GetPolicy(policyBytes)
+			if errFromFile != nil {
+				err := fmt.Errorf("failed to process : %v", errFromFile.Error())
+				errors = append(errors, err)
+				continue
+			}
+			policies = append(policies, policiesFromFile...)
+		}
+	} else {
+		if len(dirPath) > 0 && dirPath[0] == "-" {
+			if IsInputFromPipe() {
+				policyStr := ""
+				scanner := bufio.NewScanner(os.Stdin)
+				for scanner.Scan() {
+					policyStr = policyStr + scanner.Text() + "\n"
+				}
+				yamlBytes := []byte(policyStr)
+				policies, err = ut.GetPolicy(yamlBytes)
+				if err != nil {
+					return nil, sanitizederror.NewWithError("failed to extract the resources", err)
+				}
+			}
+		} else {
+			var errors []error
+			policies, errors = GetPolicies(dirPath)
+			if len(policies) == 0 {
+				if len(errors) > 0 {
+					return nil, sanitizederror.NewWithErrors("failed to read file", errors)
+				}
+				return nil, sanitizederror.New(fmt.Sprintf("no file found in paths %v", dirPath))
+			}
+			if len(errors) > 0 && log.Log.V(1).Enabled() {
+				fmt.Printf("ignoring errors: \n")
+				for _, e := range errors {
+					fmt.Printf("    %v \n", e.Error())
+				}
+			}
+		}
+	}	
+	return
+}
+
+// GetResourceAccordingToResourcePath - get resources according to the resource path
+func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []string, 
+	cluster bool, policies []*v1.ClusterPolicy, dClient *client.Client, namespace string, policyReport bool, isGit bool) (resources []*unstructured.Unstructured, err error) {
+	if isGit {
+		resources, err = GetResourcesWithTest(fs, policies, resourcePaths, isGit)
+		if err != nil {
+			return nil, sanitizederror.NewWithError("failed to extract the resources", err)
+		}
+	} else {
+		if len(resourcePaths) > 0 && resourcePaths[0] == "-" {
+			if IsInputFromPipe() {
+				resourceStr := ""
+				scanner := bufio.NewScanner(os.Stdin)
+				for scanner.Scan() {
+					resourceStr = resourceStr + scanner.Text() + "\n"
+				}
+
+				yamlBytes := []byte(resourceStr)
+				resources, err = GetResource(yamlBytes)
+				if err != nil {
+					return nil, sanitizederror.NewWithError("failed to extract the resources", err)
+				}
+			}
+		} else if (len(resourcePaths) > 0 && resourcePaths[0] != "-") || len(resourcePaths) < 0 || cluster {
+			resources, err = GetResources(policies, resourcePaths, dClient, cluster, namespace, policyReport)
+			if err != nil {
+				return resources, err
+			}
+		}
+
+	}
+	return resources, err
+}
\ No newline at end of file
diff --git a/pkg/kyverno/common/fetch.go b/pkg/kyverno/common/fetch.go
index 3f4d1b0d5..a7dafe303 100644
--- a/pkg/kyverno/common/fetch.go
+++ b/pkg/kyverno/common/fetch.go
@@ -15,6 +15,7 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/kubernetes/scheme"
 	log "sigs.k8s.io/controller-runtime/pkg/log"
+	"github.com/go-git/go-billy/v5"
 )
 
 // GetResources gets matched resources by the given policies
@@ -96,6 +97,52 @@ func GetResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient
 	}
 	return resources, nil
 }
+// GetResourcesWithTest with gets matched resources by the given policies
+func GetResourcesWithTest(fs billy.Filesystem,policies []*v1.ClusterPolicy, resourcePaths []string, isGit bool) ([]*unstructured.Unstructured,  error) {
+	resources := make([]*unstructured.Unstructured, 0)
+	var resourceTypesMap = make(map[string]bool)
+	var resourceTypes []string
+	for _, policy := range policies {
+		for _, rule := range policy.Spec.Rules {
+			for _, kind := range rule.MatchResources.Kinds {
+				resourceTypesMap[kind] = true
+			}
+		}
+	}
+	for kind := range resourceTypesMap {
+		resourceTypes = append(resourceTypes, kind)
+	}
+	if len(resourcePaths) > 0 {
+		for _, resourcePath := range resourcePaths {
+			var resourceBytes []byte
+			var err error
+			if isGit {
+				filep, err := fs.Open(resourcePath) 
+				if err != nil {
+					fmt.Printf("Unable to open resource file: %s. error: %s", resourcePath, err)
+					continue
+				}
+				resourceBytes, err = ioutil.ReadAll(filep)
+			} else {
+				resourceBytes, err = getFileBytes(resourcePath)
+			}
+			if err != nil {
+				fmt.Printf("\n----------------------------------------------------------------------\nfailed to load resources: %s. \nerror: %s\n----------------------------------------------------------------------\n", resourcePath, err)
+				continue
+			}
+
+			getResources, err := GetResource(resourceBytes)
+			if err != nil {
+				return nil, err
+			}
+
+			for _, resource := range getResources {
+				resources = append(resources, resource)
+			}
+		}
+	}
+	return resources, nil
+}
 
 // GetResource converts raw bytes to unstructured object
 func GetResource(resourceBytes []byte) ([]*unstructured.Unstructured, error) {
diff --git a/pkg/kyverno/main.go b/pkg/kyverno/main.go
index 73f9ed71f..ea3bdc489 100644
--- a/pkg/kyverno/main.go
+++ b/pkg/kyverno/main.go
@@ -7,6 +7,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/kyverno/apply"
 	"github.com/kyverno/kyverno/pkg/kyverno/validate"
 	"github.com/kyverno/kyverno/pkg/kyverno/version"
+	"github.com/kyverno/kyverno/pkg/kyverno/test"
 	"github.com/spf13/cobra"
 	"k8s.io/klog"
 	"k8s.io/klog/klogr"
@@ -26,6 +27,7 @@ func CLI() {
 		version.Command(),
 		apply.Command(),
 		validate.Command(),
+		test.Command(), 
 	}
 
 	cli.AddCommand(commands...)
diff --git a/pkg/policystatus/policy_status.go b/pkg/policystatus/policy_status.go
index 0074f8f28..cbdd2e0af 100644
--- a/pkg/policystatus/policy_status.go
+++ b/pkg/policystatus/policy_status.go
@@ -3,6 +3,7 @@ package policystatus
 import (
 	"context"
 	"encoding/json"
+	"reflect"
 	"strings"
 	"sync"
 	"time"
@@ -167,6 +168,10 @@ func (s *Sync) updateClusterPolicy(policyName, key string, status v1.PolicyStatu
 		return
 	}
 
+	if reflect.DeepEqual(status, policy.Status) {
+		return
+	}
+
 	policy.Status = status
 	_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), policy, metav1.UpdateOptions{})
 	if err != nil {
@@ -183,6 +188,10 @@ func (s *Sync) updateNamespacedPolicyStatus(policyName, namespace, key string, s
 		return
 	}
 
+	if reflect.DeepEqual(status, policy.Status) {
+		return
+	}
+
 	policy.Status = status
 	_, err = s.client.KyvernoV1().Policies(namespace).UpdateStatus(context.TODO(), policy, metav1.UpdateOptions{})
 	if err != nil {
diff --git a/pkg/resourcecache/main.go b/pkg/resourcecache/main.go
index dfc15a352..096549ae2 100644
--- a/pkg/resourcecache/main.go
+++ b/pkg/resourcecache/main.go
@@ -1,6 +1,8 @@
 package resourcecache
 
 import (
+	"fmt"
+
 	"github.com/go-logr/logr"
 	dclient "github.com/kyverno/kyverno/pkg/dclient"
 	cmap "github.com/orcaman/concurrent-map"
@@ -27,6 +29,8 @@ type resourceCache struct {
 	log logr.Logger
 }
 
+var KyvernoDefaultInformer = []string{"ConfigMap", "Secret", "Deployment", "MutatingWebhookConfiguration", "ValidatingWebhookConfiguration"}
+
 // NewResourceCache - initializes the ResourceCache
 func NewResourceCache(dclient *dclient.Client, dInformer dynamicinformer.DynamicSharedInformerFactory, logger logr.Logger) (ResourceCache, error) {
 	rCache := &resourceCache{
@@ -36,8 +40,9 @@ func NewResourceCache(dclient *dclient.Client, dInformer dynamicinformer.Dynamic
 		log:       logger,
 	}
 
-	if _, err := rCache.CreateResourceInformer("ConfigMap"); err != nil {
-		return nil, err
+	errs := rCache.CreateInformers(KyvernoDefaultInformer...)
+	if len(errs) != 0 {
+		return rCache, fmt.Errorf("failed to register default informers %v", errs)
 	}
 
 	return rCache, nil
diff --git a/pkg/webhookconfig/common.go b/pkg/webhookconfig/common.go
index bebc8c4be..059c0db52 100644
--- a/pkg/webhookconfig/common.go
+++ b/pkg/webhookconfig/common.go
@@ -5,7 +5,9 @@ import (
 
 	"github.com/kyverno/kyverno/pkg/config"
 	admregapi "k8s.io/api/admissionregistration/v1beta1"
+	apps "k8s.io/api/apps/v1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	rest "k8s.io/client-go/rest"
 )
 
@@ -47,7 +49,7 @@ func extractCA(config *rest.Config) (result []byte) {
 
 func (wrc *Register) constructOwner() v1.OwnerReference {
 	logger := wrc.log
-	kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment()
+	kubePolicyDeployment, err := wrc.getKubePolicyDeployment()
 
 	if err != nil {
 		logger.Error(err, "failed to construct OwnerReference")
@@ -62,6 +64,19 @@ func (wrc *Register) constructOwner() v1.OwnerReference {
 	}
 }
 
+func (wrc *Register) getKubePolicyDeployment() (*apps.Deployment, error) {
+	lister, _ := wrc.resCache.GetGVRCache("Deployment")
+	kubePolicyDeployment, err := lister.NamespacedLister(config.KyvernoNamespace).Get(config.KyvernoDeploymentName)
+	if err != nil {
+		return nil, err
+	}
+	deploy := apps.Deployment{}
+	if err = runtime.DefaultUnstructuredConverter.FromUnstructured(kubePolicyDeployment.UnstructuredContent(), &deploy); err != nil {
+		return nil, err
+	}
+	return &deploy, nil
+}
+
 // debug mutating webhook
 func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
 	sideEffect := admregapi.SideEffectClassNoneOnDryRun
diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go
index cc3b3b5e6..64ace56bf 100644
--- a/pkg/webhookconfig/registration.go
+++ b/pkg/webhookconfig/registration.go
@@ -7,13 +7,13 @@ import (
 	"sync"
 	"time"
 
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
 	"github.com/go-logr/logr"
 	"github.com/kyverno/kyverno/pkg/config"
 	client "github.com/kyverno/kyverno/pkg/dclient"
+	"github.com/kyverno/kyverno/pkg/resourcecache"
 	admregapi "k8s.io/api/admissionregistration/v1beta1"
 	errorsapi "k8s.io/apimachinery/pkg/api/errors"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	rest "k8s.io/client-go/rest"
 )
 
@@ -31,6 +31,7 @@ const (
 type Register struct {
 	client         *client.Client
 	clientConfig   *rest.Config
+	resCache       resourcecache.ResourceCache
 	serverIP       string // when running outside a cluster
 	timeoutSeconds int32
 	log            logr.Logger
@@ -40,12 +41,14 @@ type Register struct {
 func NewRegister(
 	clientConfig *rest.Config,
 	client *client.Client,
+	resCache resourcecache.ResourceCache,
 	serverIP string,
 	webhookTimeout int32,
 	log logr.Logger) *Register {
 	return &Register{
 		clientConfig:   clientConfig,
 		client:         client,
+		resCache:       resCache,
 		serverIP:       serverIP,
 		timeoutSeconds: webhookTimeout,
 		log:            log.WithName("Register"),
@@ -89,26 +92,28 @@ func (wrc *Register) Register() error {
 	return nil
 }
 
-// CheckWebhooks returns an error if any of the webhooks are not configured
+// Check returns an error if any of the webhooks are not configured
 func (wrc *Register) Check() error {
+	mutatingCache, _ := wrc.resCache.GetGVRCache(kindMutating)
+	validatingCache, _ := wrc.resCache.GetGVRCache(kindValidating)
 
-	if _, err := wrc.client.GetResource("", kindMutating, "", wrc.getVerifyWebhookMutatingWebhookName()); err != nil {
+	if _, err := mutatingCache.Lister().Get(wrc.getVerifyWebhookMutatingWebhookName()); err != nil {
 		return err
 	}
 
-	if _, err := wrc.client.GetResource("", kindMutating, "", wrc.getResourceMutatingWebhookConfigName()); err != nil {
+	if _, err := mutatingCache.Lister().Get(wrc.getResourceMutatingWebhookConfigName()); err != nil {
 		return err
 	}
 
-	if _, err := wrc.client.GetResource("", kindValidating, "", wrc.getResourceValidatingWebhookConfigName()); err != nil {
+	if _, err := validatingCache.Lister().Get(wrc.getResourceValidatingWebhookConfigName()); err != nil {
 		return err
 	}
 
-	if _, err := wrc.client.GetResource("", kindMutating, "", wrc.getPolicyMutatingWebhookConfigurationName()); err != nil {
+	if _, err := mutatingCache.Lister().Get(wrc.getPolicyMutatingWebhookConfigurationName()); err != nil {
 		return err
 	}
 
-	if _, err := wrc.client.GetResource("", kindValidating, "", wrc.getPolicyValidatingWebhookConfigurationName()); err != nil {
+	if _, err := validatingCache.Lister().Get(wrc.getPolicyValidatingWebhookConfigurationName()); err != nil {
 		return err
 	}
 


Test patch:
diff --git a/pkg/dclient/client_test.go b/pkg/dclient/client_test.go
index 9689d1d8a..2ab750f29 100644
--- a/pkg/dclient/client_test.go
+++ b/pkg/dclient/client_test.go
@@ -117,11 +117,3 @@ func TestCSRInterface(t *testing.T) {
 		t.Errorf("Testing CSR interface not working: %s", err)
 	}
 }
-
-func TestKubePolicyDeployment(t *testing.T) {
-	f := newFixture(t)
-	_, err := f.client.GetKubePolicyDeployment()
-	if err != nil {
-		t.Fatal(err)
-	}
-}
diff --git a/pkg/engine/validate/pattern_test.go b/pkg/engine/validate/pattern_test.go
index 390c74d50..7b4214dd2 100644
--- a/pkg/engine/validate/pattern_test.go
+++ b/pkg/engine/validate/pattern_test.go
@@ -196,6 +196,22 @@ func TestValidateValueWithPattern_StringsLogicalOr(t *testing.T) {
 	assert.Assert(t, ValidateValueWithPattern(log.Log, value, pattern))
 }
 
+func TestValidateValueWithPattern_StringsLogicalAnd(t *testing.T) {
+	pattern := ">1 & <20"
+	value := "10"
+	assert.Assert(t, ValidateValueWithPattern(log.Log, value, pattern))
+}
+
+func TestValidateValueWithPattern_StringsAllLogicalOperators(t *testing.T) {
+	pattern := ">1 & <20 | >31 & <33"
+	value := "10"
+	assert.Assert(t, ValidateValueWithPattern(log.Log, value, pattern))
+	value = "32"
+	assert.Assert(t, ValidateValueWithPattern(log.Log, value, pattern))
+	value = "21"
+	assert.Assert(t, !ValidateValueWithPattern(log.Log, value, pattern))
+}
+
 func TestValidateValueWithPattern_EqualTwoFloats(t *testing.T) {
 	assert.Assert(t, ValidateValueWithPattern(log.Log, 7.0, 7.000))
 }
diff --git a/pkg/kyverno/test/command.go b/pkg/kyverno/test/command.go
new file mode 100644
index 000000000..ad468d188
--- /dev/null
+++ b/pkg/kyverno/test/command.go
@@ -0,0 +1,355 @@
+package test 
+import (
+	"fmt"
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"net/url"
+	"sort"
+	"reflect"
+	"strings"
+	"github.com/spf13/cobra"
+	log "sigs.k8s.io/controller-runtime/pkg/log"
+	sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
+	v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/kyverno/common"
+	"k8s.io/apimachinery/pkg/util/yaml"
+	policy2 "github.com/kyverno/kyverno/pkg/policy"
+	"github.com/kyverno/kyverno/pkg/openapi"
+	"github.com/kyverno/kyverno/pkg/engine/response"
+	corev1 "k8s.io/api/core/v1"
+	"github.com/kyverno/kyverno/pkg/policyreport"
+	report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha1"
+	"github.com/kyverno/kyverno/pkg/engine/utils"
+	"github.com/kataras/tablewriter"
+	"github.com/lensesio/tableprinter"
+	"github.com/go-git/go-billy/v5/memfs"
+	"github.com/go-git/go-billy/v5"
+	"github.com/fatih/color"
+	client "github.com/kyverno/kyverno/pkg/dclient"
+)
+
+// Command returns version command
+func Command() *cobra.Command {
+
+	var  valuesFile string
+	return &cobra.Command{
+		Use:   "test",
+		Short: "Shows current test of kyverno",
+		RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
+			defer func() {
+				if err != nil {
+					if !sanitizederror.IsErrorSanitized(err) {
+						log.Log.Error(err, "failed to sanitize")
+						err = fmt.Errorf("internal error")
+					}
+				}
+			}()
+			err = testCommandExecute(dirPath, valuesFile)
+			if err != nil {
+				log.Log.V(3).Info("a directory is required")
+				return err
+			}
+			return nil
+		},
+	}
+}
+
+type Test struct {
+	Name          string     `json:"name"`
+	Policies      []string     `json:"policies"`
+	Resources     []string     `json:"resources"`
+	Variables     string     `json:"variables"`
+	Results      []TestResults     `json:"results"`
+}
+
+type SkippedPolicy struct {
+	Name     string    `json:"name"`
+	Rules    []v1.Rule `json:"rules"`
+	Variable string    `json:"variable"`
+}
+
+type TestResults struct {
+	Policy    string    `json:"policy"`
+	Rule  string `json:"rule"`
+	Status string    `json:"status"`
+	Resource string    `json:"resource"`	
+}
+
+type ReportResult struct {
+	TestResults 
+	Resources []*corev1.ObjectReference `json:"resources"`
+}
+
+type Resource struct {
+	Name          string     `json:"name"`
+	Values map[string]string `json:"values"`
+}
+
+type Table struct {
+	ID    int    `header:"#"`
+	Resource string    `header:"test"`
+	Result string    `header:"result"`
+	
+}
+type Policy struct {
+	Name      string     `json:"name"`
+	Resources []Resource `json:"resources"`
+}
+
+type Values struct {
+	Policies []Policy `json:"policies"`
+}
+
+func testCommandExecute(dirPath []string, valuesFile string) (err error) {
+	var errors []error
+	fs := memfs.New()
+	
+	if len(dirPath) == 0 {
+			return  sanitizederror.NewWithError(fmt.Sprintf("a directory is required"), err)
+		}
+	if strings.Contains(string(dirPath[0]), "https://") {
+		gitUrl, err := url.Parse(dirPath[0])
+		if err != nil {
+			return  sanitizederror.NewWithError("failed to parse URL", err)
+		}
+		pathElems := strings.Split(gitUrl.Path[1:], "/")
+		if len(pathElems) != 3 {
+			err := fmt.Errorf("invalid URL path %s - expected https://github.com/:owner/:repository/:branch", gitUrl.Path)
+			return  sanitizederror.NewWithError("failed to parse URL", err)
+		}
+		gitUrl.Path = strings.Join([]string{"/", pathElems[0], pathElems[1]}, "/")
+		repoURL := gitUrl.String()
+		cloneRepo, err := clone(repoURL, fs)
+		if err != nil {
+			return  sanitizederror.NewWithError("failed to clone repository ", err)
+		}
+		log.Log.V(3).Info(" clone repository", cloneRepo )
+		policyYamls, err := listYAMLs(fs, "/")
+		if err != nil {
+			return  sanitizederror.NewWithError("failed to list YAMLs in repository", err)
+		}
+		sort.Strings(policyYamls)
+		for _, yamlFilePath := range policyYamls {
+			file, err := fs.Open(yamlFilePath)	
+			bytes, err := ioutil.ReadAll(file)
+			if err != nil {
+				sanitizederror.NewWithError("Error: failed to read file", err)
+			}
+			policyBytes, err := yaml.ToJSON(bytes)
+			if err != nil {
+				sanitizederror.NewWithError("failed to convert to JSON", err)
+				continue
+			}
+			if err := applyPoliciesFromPath(fs, policyBytes, valuesFile, true); err != nil {
+				return sanitizederror.NewWithError("failed to apply test command", err)
+			}	
+		}
+	} else {
+		path := filepath.Clean(dirPath[0])
+		fileDesc, err := os.Stat(path)
+		if err != nil {
+			errors = append(errors, err)
+		}
+		if fileDesc.IsDir() {
+			files, err := ioutil.ReadDir(path)
+			if err != nil {
+				errors = append(errors, fmt.Errorf("failed to read %v: %v", path, err.Error()))
+			}
+			for _, file := range files {
+				fmt.Printf("\napplying  test on file  %s...", file.Name())
+
+				yamlFile, err := ioutil.ReadFile(filepath.Join(path, file.Name()))
+				if err != nil {
+					return  sanitizederror.NewWithError("unable to read yaml", err)
+				}
+				valuesBytes, err := yaml.ToJSON(yamlFile)
+				if err != nil {
+					return  sanitizederror.NewWithError("failed to convert json", err)
+				}
+				if err := applyPoliciesFromPath(fs, valuesBytes, valuesFile, false); err != nil {
+					return sanitizederror.NewWithError("failed to apply test command", err)
+				}	
+			}
+		}
+		if len(errors) > 0 && log.Log.V(1).Enabled() {
+			fmt.Printf("ignoring errors: \n")
+			for _, e := range errors {
+				fmt.Printf("    %v \n", e.Error())
+			}
+		}
+	}
+	return nil
+}
+
+func buildPolicyResults(resps []*response.EngineResponse) map[string][]interface{} {
+	results := make(map[string][]interface{})
+	infos := policyreport.GeneratePRsFromEngineResponse(resps, log.Log)
+	for _, info := range infos {
+		for _, infoResult := range info.Results {
+			for _, rule := range infoResult.Rules {
+				if rule.Type != utils.Validation.String() {
+					continue
+				}
+				result := report.PolicyReportResult{
+					Policy: info.PolicyName,
+					Resources: []*corev1.ObjectReference{
+						{
+							Name:       infoResult.Resource.Name,
+						},
+					},	
+				}
+				result.Rule = rule.Name
+				result.Status = report.PolicyStatus(rule.Check)
+				results[rule.Name] = append(results[rule.Name], result)
+			}
+		}
+	}
+	return results
+}
+
+
+func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile string, isGit bool) (err error) {
+	openAPIController, err := openapi.NewOpenAPIController()
+	engineResponses := make([]*response.EngineResponse, 0)
+	validateEngineResponses := make([]*response.EngineResponse, 0)
+	skippedPolicies := make([]SkippedPolicy, 0)
+	var dClient *client.Client
+	values := &Test{}
+	var variablesString string
+
+	if err := json.Unmarshal(policyBytes, values); err != nil {
+		return sanitizederror.NewWithError("failed to decode yaml", err)
+	}	
+	_, valuesMap, err := common.GetVariable(variablesString, values.Variables)
+	if err != nil {
+		if !sanitizederror.IsErrorSanitized(err) {
+			return  sanitizederror.NewWithError("failed to decode yaml", err)
+		}
+		return  err
+	}
+	policies, err := common.GetPoliciesFromPaths(fs, values.Policies, isGit)
+	if err != nil {
+		fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
+		os.Exit(1)
+	}
+	mutatedPolicies, err := common.MutatePolices(policies)
+		if err != nil {
+		if !sanitizederror.IsErrorSanitized(err) {
+			return  sanitizederror.NewWithError("failed to mutate policy", err)
+			}
+		}
+		resources, err := common.GetResourceAccordingToResourcePath(fs, values.Resources, false,  mutatedPolicies, dClient, "", false, isGit)
+		if err != nil {
+			fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
+			os.Exit(1)
+		}
+		if err != nil {
+			fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
+			os.Exit(1)
+		}
+		msgPolicies := "1 policy"
+		if len(mutatedPolicies) > 1 {
+			msgPolicies = fmt.Sprintf("%d policies", len(policies))
+		}
+		msgResources := "1 resource"
+		if len(resources) > 1 {
+			msgResources = fmt.Sprintf("%d resources", len(resources))
+		}
+		if len(mutatedPolicies) > 0 && len(resources) > 0 {
+			fmt.Printf("\napplying %s to %s... \n", msgPolicies, msgResources)
+		}
+		for _, policy := range mutatedPolicies {
+			err := policy2.Validate(policy, nil, true, openAPIController)
+			if err != nil {
+				fmt.Println("valuesMap1")
+				log.Log.V(3).Info(fmt.Sprintf("skipping policy %v as it is not valid", policy.Name), "error", err)
+				continue
+			}
+			matches := common.PolicyHasVariables(*policy)
+			variable := common.RemoveDuplicateVariables(matches)
+			if len(matches) > 0 && variablesString == "" && values.Variables == "" {
+				skipPolicy := SkippedPolicy{
+					Name:     policy.GetName(),
+					Rules:    policy.Spec.Rules,
+					Variable: variable,
+				}
+				skippedPolicies = append(skippedPolicies, skipPolicy)
+				log.Log.V(3).Info(fmt.Sprintf("skipping policy %s", policy.Name), "error", fmt.Sprintf("policy have variable - %s", variable))
+				continue
+			}
+			for _, resource := range resources {
+				thisPolicyResourceValues := make(map[string]string)
+				if len(valuesMap[policy.GetName()]) != 0 && !reflect.DeepEqual(valuesMap[policy.GetName()][resource.GetName()], Resource{}) {
+					thisPolicyResourceValues = valuesMap[policy.GetName()][resource.GetName()].Values
+				}
+				if len(common.PolicyHasVariables(*policy)) > 0 && len(thisPolicyResourceValues) == 0 {
+					return  sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
+				}
+				
+				ers, validateErs, _, _, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true)
+				if err != nil {
+					return  sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
+				}
+				engineResponses = append(engineResponses, ers...)
+				validateEngineResponses = append(validateEngineResponses, validateErs)
+			}
+		}
+		resultsMap := buildPolicyResults(validateEngineResponses)
+		resultErr := printTestResult(resultsMap, values.Results)
+		if resultErr != nil {
+			return  sanitizederror.NewWithError("Unable to genrate result. Error:", resultErr)
+			os.Exit(1)
+		}
+	return
+}
+
+
+func printTestResult(resps map[string][]interface {}, testResults []TestResults) (error){
+	printer := tableprinter.New(os.Stdout)
+	table := []*Table{}
+	boldRed := color.New(color.FgRed).Add(color.Bold)
+	boldFgCyan := color.New(color.FgCyan).Add(color.Bold)
+	for i, v := range testResults {
+		res := new(Table)
+		res.ID = i+1
+		res.Resource =  boldFgCyan.Sprintf(v.Resource) +  " with " + boldFgCyan.Sprintf(v.Policy)  + "/" +  boldFgCyan.Sprintf(v.Rule) 
+		n := resps[v.Rule]
+		data, _ := json.Marshal(n)
+		valuesBytes, err := yaml.ToJSON(data)
+		if err != nil {
+			return  sanitizederror.NewWithError("failed to convert json", err)
+		}
+		var r []ReportResult
+		json.Unmarshal(valuesBytes, &r)
+		res.Result = boldRed.Sprintf("Fail")
+		if len(r) != 0 {
+			var resource TestResults
+			for _, testRes := range r {
+				if testRes.Resources[0].Name == v.Resource {
+				resource.Policy = testRes.Policy 
+				resource.Rule = testRes.Rule
+				resource.Status = testRes.Status
+				resource.Resource = testRes.Resources[0].Name
+					if v == resource {
+						res.Result =  "Pass"
+					}
+				}
+			}
+		}
+		table = append(table, res)
+	}	
+	printer.BorderTop, printer.BorderBottom, printer.BorderLeft, printer.BorderRight = true, true, true, true
+	printer.CenterSeparator = "│"
+	printer.ColumnSeparator = "│"
+	printer.RowSeparator = "─"
+	printer.RowCharLimit = 300
+	printer.RowLengthTitle  = func(rowsLength int) bool {
+		return rowsLength > 10
+	}
+	printer.HeaderBgColor = tablewriter.BgBlackColor
+	printer.HeaderFgColor = tablewriter.FgGreenColor
+	printer.Print(table)
+	return nil
+}
\ No newline at end of file
diff --git a/pkg/kyverno/test/git.go b/pkg/kyverno/test/git.go
new file mode 100644
index 000000000..8dd1d857c
--- /dev/null
+++ b/pkg/kyverno/test/git.go
@@ -0,0 +1,38 @@
+package test
+import (
+	"os"
+	"path/filepath"
+	"github.com/go-git/go-billy/v5"
+	"github.com/go-git/go-git/v5"
+	"github.com/go-git/go-git/v5/storage/memory"
+)
+
+func clone(path string, fs billy.Filesystem) (*git.Repository, error) {
+	return git.Clone(memory.NewStorage(), fs, &git.CloneOptions{
+		URL:      path,
+		Progress: os.Stdout,
+	})
+}
+
+func listYAMLs(fs billy.Filesystem, path string) ([]string, error) {
+	path = filepath.Clean(path)
+	fis, err := fs.ReadDir(path)
+	if err != nil {
+		return nil, err
+	}
+	yamls := make([]string, 0)
+	for _, fi := range fis {
+		name := filepath.Join(path, fi.Name())
+		if fi.IsDir() {
+			continue
+		}
+
+		ext := filepath.Ext(name)
+		if ext != ".yml" && ext != ".yaml" {
+			continue
+		}
+
+		yamls = append(yamls, name)
+	}
+	return yamls, nil
+}