Repository: kyverno/kyverno
Pull request #1527 (2021-02-02 10:55:03)

Adding HTTP(git raw or any public url ) URL applying functionality to kyverno cli
## Related issue
Closes- https://github.com/kyverno/kyverno/issues/1422

Changes
- pkg/kyverno/common/common.go, pkg/kyverno/common/fetch.go
  Adding additonal logic to read http files and local files
- Any combination of file type can be applied by the kyverno cli
  - `kyverno apply policy.yaml -r http://url/resource.yaml`
  - `kyverno apply http://url.com/policy.yaml -r http://url.com/resource.yaml`
  - `kyverno apply ./deploy/policy.yaml -r http://url.com/resource.yaml`
- Both `apply` and `validate`command support this feature

**Screenshot**
![Selection_135](https://user-images.githubusercontent.com/20414564/106590048-b6a6e480-6572-11eb-874b-19388e70d282.png)

<!--
Please link the GitHub issue this pull request resolves in the format of `#1234`. If you discussed this change
with a maintainer, please mention her/him using the `@` syntax (e.g. `@JimBugwadia`).

If this change neither resolves an existing issue nor has sign-off from one of the maintainers, there is a
chance substantial changes will be requested or that the changes will be rejected.

You can discuss changes with maintainers in the [Kyvrno Slack Channel](https://kubernetes.slack.com/).
-->

**What type of PR is this?**
<!--

> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind api-change
> /kind bug
> /kind cleanup
> /kind design
> /kind documentation
> /kind failing-test
> /kind feature
-->

## Proposed changes

<!--
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
-->

## Checklist

<!--
Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of
them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.
-->

- [X] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).
- [ ] I have added tests that prove my fix is effective or that my feature
      works.
- [ ] I have added or changed [the documentation](documentation/).

## Further comments

<!--
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution
you did and what alternatives you considered, etc...
-->



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..7501bc73e 100644
--- a/pkg/kyverno/common/common.go
+++ b/pkg/kyverno/common/common.go
@@ -7,8 +7,11 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net/http"
 	"os"
+	"strings"
 	"path/filepath"
+	"strings"
 	"sigs.k8s.io/controller-runtime/pkg/log"
 
 	jsonpatch "github.com/evanphx/json-patch"
@@ -20,21 +23,55 @@ 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)
 
-		path = filepath.Clean(path)
-		fileDesc, err := os.Stat(path)
-		if err != nil {
-			errors = append(errors, err)
-			continue
+		var (
+			fileDesc os.FileInfo
+			err      error
+		)
+
+		isHttpPath := strings.Contains(path, "http")
+
+		// path clean and retrieving file info can be possible if it's not an HTTP URL
+		if !isHttpPath {
+			path = filepath.Clean(path)
+			fileDesc, err = os.Stat(path)
+			if err != nil {
+				errors = append(errors, err)
+				continue
+			}
 		}
 
-		if fileDesc.IsDir() {
+		// apply file from a directory is possible only if the path is not HTTP URL
+		if !isHttpPath && fileDesc.IsDir() {
 			files, err := ioutil.ReadDir(path)
 			if err != nil {
 				errors = append(errors, fmt.Errorf("failed to read %v: %v", path, err.Error()))
@@ -54,10 +91,34 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
 			policies = append(policies, policiesFromDir...)
 
 		} else {
-			fileBytes, err := ioutil.ReadFile(path)
-			if err != nil {
-				errors = append(errors, fmt.Errorf("failed to read %v: %v", path, err.Error()))
-				continue
+			var fileBytes []byte
+			if isHttpPath {
+				resp, err := http.Get(path)
+				if err != nil {
+					fmt.Errorf("failed to process %s", err)
+				}
+				defer resp.Body.Close()
+
+				if resp.StatusCode != http.StatusOK {
+					errors = append(errors, fmt.Errorf("failed to process %v: %v", path, err.Error()))
+					continue
+				}
+
+				fileBytes, err = ioutil.ReadAll(resp.Body)
+				if err != nil {
+					fmt.Errorf("failed to process %s", err)
+				}
+
+				if err != nil {
+					errors = append(errors, fmt.Errorf("failed to read %v: %v", path, err.Error()))
+					continue
+				}
+			} else {
+				fileBytes, err = ioutil.ReadFile(path)
+				if err != nil {
+					errors = append(errors, fmt.Errorf("failed to read %v: %v", path, err.Error()))
+					continue
+				}
 			}
 
 			policiesFromFile, errFromFile := utils.GetPolicy(fileBytes)
@@ -68,6 +129,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
 			}
 
 			policies = append(policies, policiesFromFile...)
+
 		}
 	}
 
@@ -222,3 +284,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..a70b514d9 100644
--- a/pkg/kyverno/common/fetch.go
+++ b/pkg/kyverno/common/fetch.go
@@ -5,6 +5,8 @@ import (
 	"errors"
 	"fmt"
 	"io/ioutil"
+	"net/http"
+	"strings"
 
 	v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
 	client "github.com/kyverno/kyverno/pkg/dclient"
@@ -15,6 +17,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 +99,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) {
@@ -149,10 +198,34 @@ func getResourcesOfTypeFromCluster(resourceTypes []string, dClient *client.Clien
 }
 
 func getFileBytes(path string) ([]byte, error) {
-	file, err := ioutil.ReadFile(path)
-	if err != nil {
-		return nil, err
+
+	var (
+		file []byte
+		err  error
+	)
+
+	if strings.Contains(path, "http") {
+		resp, err := http.Get(path)
+		if err != nil {
+			return nil, err
+		}
+		defer resp.Body.Close()
+
+		if resp.StatusCode != http.StatusOK {
+			return nil, err
+		}
+
+		file, err = ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		file, err = ioutil.ReadFile(path)
+		if err != nil {
+			return nil, err
+		}
 	}
+
 	return file, err
 }
 
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
+}