Repository: kyverno/kyverno
Pull request #1536 (2021-02-03 21:26:50)

feat: support for numeric operators
Signed-off-by: Yashvardhan Kukreja <yash.kukreja.98@gmail.com>

## Related issue
#1512 
<!--
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?**
> /kind feature
<!--

> 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
The newly added operators which can be applied to `validate.deny.conditions` and `preconditions` over kyverno policy rules are the following:
- `GreaterThanOrEquals` : key >= value?
- `GreaterThan` : key > value?
- `LessThanOrEquals` : key <= value?
- `LessThan` : key < value?
<!--
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).
- [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/).

## Further comments
Here, the key will only be expected to be int, int64, float, float64, string (for the cases like "2", "2.4" only)
In rest of the cases, an error with saying "Unknown key type found" would be generated because clearly, these are numeric operators and these operations can't be applied on types which can't be casted to int or float.
<!--
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/api/kyverno/v1/policy_types.go b/pkg/api/kyverno/v1/policy_types.go
index b57f6fc4d..83c2e68be 100755
--- a/pkg/api/kyverno/v1/policy_types.go
+++ b/pkg/api/kyverno/v1/policy_types.go
@@ -178,6 +178,14 @@ const (
 	In ConditionOperator = "In"
 	// NotIn evaluates if the key is not contained in the set of values.
 	NotIn ConditionOperator = "NotIn"
+	// GreaterThanOrEquals evaluates if the key (numeric) is greater than or equal to the value (numeric).
+	GreaterThanOrEquals ConditionOperator = "GreaterThanOrEquals"
+	// GreaterThan evaluates if the key (numeric) is greater than the value (numeric).
+	GreaterThan ConditionOperator = "GreaterThan"
+	// LessThan evaluates if the key (numeric) is less than or equal to the value (numeric).
+	LessThanOrEquals ConditionOperator = "LessThanOrEquals"
+	// LessThan evaluates if the key (numeric) is less than the value (numeric).
+	LessThan ConditionOperator = "LessThan"
 )
 
 // MatchResources is used to specify resource and admission review request data for
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/engine/variables/operator/numeric.go b/pkg/engine/variables/operator/numeric.go
new file mode 100644
index 000000000..687cb0146
--- /dev/null
+++ b/pkg/engine/variables/operator/numeric.go
@@ -0,0 +1,150 @@
+package operator
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/go-logr/logr"
+	kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/engine/context"
+)
+
+//NewNumericOperatorHandler returns handler to manage the provided numeric operations (>, >=, <=, <)
+func NewNumericOperatorHandler(log logr.Logger, ctx context.EvalInterface, subHandler VariableSubstitutionHandler, op kyverno.ConditionOperator) OperatorHandler {
+	return NumericOperatorHandler{
+		ctx:        ctx,
+		subHandler: subHandler,
+		log:        log,
+		condition:  op,
+	}
+}
+
+//NumericOperatorHandler provides implementation to handle Numeric Operations associated with policies
+type NumericOperatorHandler struct {
+	ctx        context.EvalInterface
+	subHandler VariableSubstitutionHandler
+	log        logr.Logger
+	condition  kyverno.ConditionOperator
+}
+
+// compareByCondition compares a float64 key with a float64 value on the basis of the provided operator
+func compareByCondition(key float64, value float64, op kyverno.ConditionOperator, log *logr.Logger) bool {
+	switch op {
+	case kyverno.GreaterThanOrEquals:
+		return key >= value
+	case kyverno.GreaterThan:
+		return key > value
+	case kyverno.LessThanOrEquals:
+		return key <= value
+	case kyverno.LessThan:
+		return key < value
+	default:
+		(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan], found %s", op))
+		return false
+	}
+}
+
+func (noh NumericOperatorHandler) Evaluate(key, value interface{}) bool {
+	if key, err := noh.subHandler(noh.log, noh.ctx, key); err != nil {
+		// Failed to resolve the variable
+		noh.log.Error(err, "Failed to resolve variable", "variable", key)
+		return false
+	}
+	if value, err := noh.subHandler(noh.log, noh.ctx, value); err != nil {
+		// Failed to resolve the variable
+		noh.log.Error(err, "Failed to resolve variable", "variable", value)
+		return false
+	}
+
+	switch typedKey := key.(type) {
+	case int:
+		return noh.validateValueWithIntPattern(int64(typedKey), value)
+	case int64:
+		return noh.validateValueWithIntPattern(typedKey, value)
+	case float64:
+		return noh.validateValueWithFloatPattern(typedKey, value)
+	case string:
+		return noh.validateValueWithStringPattern(typedKey, value)
+	default:
+		noh.log.Info("Unsupported type", "value", typedKey, "type", fmt.Sprintf("%T", typedKey))
+		return false
+	}
+}
+
+func (noh NumericOperatorHandler) validateValueWithIntPattern(key int64, value interface{}) bool {
+	switch typedValue := value.(type) {
+	case int:
+		return compareByCondition(float64(key), float64(typedValue), noh.condition, &noh.log)
+	case int64:
+		return compareByCondition(float64(key), float64(typedValue), noh.condition, &noh.log)
+	case float64:
+		return compareByCondition(float64(key), typedValue, noh.condition, &noh.log)
+	case string:
+		// extract float64 and (if that fails) then, int64 from the string
+		float64val, err := strconv.ParseFloat(typedValue, 64)
+		if err == nil {
+			return compareByCondition(float64(key), float64val, noh.condition, &noh.log)
+		}
+		int64val, err := strconv.ParseInt(typedValue, 10, 64)
+		if err == nil {
+			return compareByCondition(float64(key), float64(int64val), noh.condition, &noh.log)
+		}
+		noh.log.Error(fmt.Errorf("Parse Error: "), "Failed to parse both float64 and int64 from the string value")
+		return false
+	default:
+		noh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
+		return false
+	}
+}
+
+func (noh NumericOperatorHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
+	switch typedValue := value.(type) {
+	case int:
+		return compareByCondition(key, float64(typedValue), noh.condition, &noh.log)
+	case int64:
+		return compareByCondition(key, float64(typedValue), noh.condition, &noh.log)
+	case float64:
+		return compareByCondition(key, typedValue, noh.condition, &noh.log)
+	case string:
+		float64val, err := strconv.ParseFloat(typedValue, 64)
+		if err == nil {
+			return compareByCondition(key, float64val, noh.condition, &noh.log)
+		}
+		int64val, err := strconv.ParseInt(typedValue, 10, 64)
+		if err == nil {
+			return compareByCondition(key, float64(int64val), noh.condition, &noh.log)
+		}
+		noh.log.Error(fmt.Errorf("Parse Error: "), "Failed to parse both float64 and int64 from the string value")
+		return false
+	default:
+		noh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
+		return false
+	}
+}
+
+func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, value interface{}) bool {
+	// extracting float64 from the string key
+	float64key, err := strconv.ParseFloat(key, 64)
+	if err == nil {
+		return noh.validateValueWithFloatPattern(float64key, value)
+	}
+	// extracting int64 from the string because float64 extraction failed
+	int64key, err := strconv.ParseInt(key, 10, 64)
+	if err == nil {
+		return noh.validateValueWithIntPattern(int64key, value)
+	}
+	noh.log.Error(fmt.Errorf("Parse Error: "), "Failed to parse both float64 and int64 from the string keyt")
+	return false
+}
+
+// the following functions are unreachable because the key is strictly supposed to be numeric
+// still the following functions are just created to make NumericOperatorHandler struct implement OperatorHandler interface
+func (noh NumericOperatorHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
+	return false
+}
+func (noh NumericOperatorHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
+	return false
+}
+func (noh NumericOperatorHandler) validateValueWithSlicePattern(key []interface{}, value interface{}) bool {
+	return false
+}
diff --git a/pkg/engine/variables/operator/operator.go b/pkg/engine/variables/operator/operator.go
index 34fc9dd75..48801105d 100644
--- a/pkg/engine/variables/operator/operator.go
+++ b/pkg/engine/variables/operator/operator.go
@@ -1,10 +1,11 @@
 package operator
 
 import (
+	"strings"
+
 	"github.com/go-logr/logr"
 	kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/engine/context"
-	"strings"
 )
 
 //OperatorHandler provides interface to manage types
@@ -44,6 +45,12 @@ func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvern
 	case strings.ToLower(string(kyverno.NotIn)):
 		return NewNotInHandler(log, ctx, subHandler)
 
+	case strings.ToLower(string(kyverno.GreaterThanOrEquals)),
+		strings.ToLower(string(kyverno.GreaterThan)),
+		strings.ToLower(string(kyverno.LessThanOrEquals)),
+		strings.ToLower(string(kyverno.LessThan)):
+		return NewNumericOperatorHandler(log, ctx, subHandler, op)
+
 	default:
 		log.Info("operator not supported", "operator", str)
 	}
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/engine/variables/evaluate_test.go b/pkg/engine/variables/evaluate_test.go
index 48342e5d9..8b1322444 100644
--- a/pkg/engine/variables/evaluate_test.go
+++ b/pkg/engine/variables/evaluate_test.go
@@ -66,6 +66,150 @@ func Test_Eval_NoEqual_Const_String_Fail(t *testing.T) {
 	}
 }
 
+func Test_Eval_GreaterThanOrEquals_Const_string_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_string_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_string_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1.1",
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    "2",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_string_Equal_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.GreaterThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_string_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.GreaterThan,
+		Value:    0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_string_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1.1",
+		Operator: kyverno.GreaterThan,
+		Value:    "2",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_string_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_string_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "0",
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_string_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "2.0",
+		Operator: kyverno.LessThanOrEquals,
+		Value:    "1.1",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_string_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "1",
+		Operator: kyverno.LessThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_string_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "0",
+		Operator: kyverno.LessThan,
+		Value:    1,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThan_Const_string_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      "2.0",
+		Operator: kyverno.LessThan,
+		Value:    "1.1",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
 //Bool
 
 func Test_Eval_Equal_Const_Bool_Pass(t *testing.T) {
@@ -181,6 +325,150 @@ func Test_Eval_NoEqual_Const_int_Fail(t *testing.T) {
 	}
 }
 
+func Test_Eval_GreaterThanOrEquals_Const_int_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_int_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_int_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    "2",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_int_Equal_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_int_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThan,
+		Value:    0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_int_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.GreaterThan,
+		Value:    "2",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_int_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_int_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      0,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_int_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      2,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    "1",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_int_Equal_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1,
+		Operator: kyverno.LessThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_int_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      0,
+		Operator: kyverno.LessThan,
+		Value:    1,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThan_Const_int_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      2,
+		Operator: kyverno.LessThan,
+		Value:    "1",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
 // int64
 func Test_Eval_Equal_Const_int64_Pass(t *testing.T) {
 	ctx := context.NewContext()
@@ -296,6 +584,150 @@ func Test_Eval_NoEqual_Const_float64_Fail(t *testing.T) {
 	}
 }
 
+func Test_Eval_GreaterThanOrEquals_Const_float64_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.0,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_float64_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.5,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThanOrEquals_Const_float64_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.95,
+		Operator: kyverno.GreaterThanOrEquals,
+		Value:    "2",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_float64_Equal_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.0,
+		Operator: kyverno.GreaterThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_float64_Greater_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.5,
+		Operator: kyverno.GreaterThan,
+		Value:    "0",
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_GreaterThan_Const_float64_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.95,
+		Operator: kyverno.GreaterThan,
+		Value:    "2.5",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_float64_Equal_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.0,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1.0,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_float64_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      0.5,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    1,
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThanOrEquals_Const_float64_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      2.0,
+		Operator: kyverno.LessThanOrEquals,
+		Value:    "1.95",
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_float64_Equal_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      1.0,
+		Operator: kyverno.LessThan,
+		Value:    1.0,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
+func Test_Eval_LessThan_Const_float64_Less_Pass(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      0.5,
+		Operator: kyverno.LessThan,
+		Value:    "1.5",
+	}
+	if !Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to pass")
+	}
+}
+
+func Test_Eval_LessThan_Const_float64_Fail(t *testing.T) {
+	ctx := context.NewContext()
+	condition := kyverno.Condition{
+		Key:      2.5,
+		Operator: kyverno.LessThan,
+		Value:    1.95,
+	}
+	if Evaluate(log.Log, ctx, condition) {
+		t.Error("expected to fail")
+	}
+}
+
 //object/map[string]interface
 
 func Test_Eval_Equal_Const_object_Pass(t *testing.T) {
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
+}