Repository: kyverno/kyverno
Pull request #1373 (2020-12-09 03:02:15)

1370 clean up stale RCRs
## Related issue
Fixes https://github.com/kyverno/kyverno/issues/1370


Patch:
diff --git a/cmd/initContainer/main.go b/cmd/initContainer/main.go
index 4b3765c23..8b5cc785a 100644
--- a/cmd/initContainer/main.go
+++ b/cmd/initContainer/main.go
@@ -266,11 +266,7 @@ func removeClusterPolicyReport(client *client.Client, kind string) error {
 	}
 
 	for _, cpolr := range cpolrs.Items {
-		if err := client.DeleteResource(cpolr.GetAPIVersion(), cpolr.GetKind(), "", cpolr.GetName(), false); err != nil {
-			logger.Error(err, "failed to delete clusterPolicyReport", "name", cpolr.GetName())
-		} else {
-			logger.Info("successfully cleaned up ClusterPolicyReport")
-		}
+		deleteResource(client, cpolr.GetAPIVersion(), cpolr.GetKind(), "", cpolr.GetName(), nil)
 	}
 	return nil
 }
@@ -292,14 +288,12 @@ func removePolicyReport(client *client.Client, kind string) error {
 			fmt.Sprintf("pr-ns-%s", ns.GetName()),
 		}
 
+		var wg sync.WaitGroup
+		wg.Add(len(reportNames))
 		for _, reportName := range reportNames {
-			err := client.DeleteResource("", kind, ns.GetName(), reportName, false)
-			if err != nil && !errors.IsNotFound(err) {
-				logger.Error(err, "failed to delete resource", "kind", kind, "name", reportName)
-			} else {
-				logger.Info("successfully cleaned up resource", "kind", kind, "name", reportName)
-			}
+			go deleteResource(client, "", kind, ns.GetName(), reportName, &wg)
 		}
+		wg.Wait()
 	}
 
 	return nil
@@ -315,13 +309,12 @@ func removeReportChangeRequest(client *client.Client, kind string) error {
 		return nil
 	}
 
+	var wg sync.WaitGroup
+	wg.Add(len(rcrList.Items))
 	for _, rcr := range rcrList.Items {
-		if err := client.DeleteResource(rcr.GetAPIVersion(), rcr.GetKind(), rcr.GetNamespace(), rcr.GetName(), false); err != nil {
-			logger.Error(err, "failed to delete reportChangeRequest", "name", rcr.GetName())
-		} else {
-			logger.Info("successfully cleaned up reportChangeRequest", "name", rcr.GetName())
-		}
+		go deleteResource(client, rcr.GetAPIVersion(), rcr.GetKind(), rcr.GetNamespace(), rcr.GetName(), &wg)
 	}
+	wg.Wait()
 	return nil
 }
 
@@ -333,11 +326,7 @@ func removeClusterReportChangeRequest(client *client.Client, kind string) error
 	}
 
 	for _, crcr := range crcrList.Items {
-		if err := client.DeleteResource(crcr.GetAPIVersion(), crcr.GetKind(), "", crcr.GetName(), false); err != nil {
-			logger.Error(err, "failed to delete clusterReportChangeRequest", "name", crcr.GetName())
-		} else {
-			logger.Info("successfully cleaned up clusterReportChangeRequest")
-		}
+		deleteResource(client, crcr.GetAPIVersion(), crcr.GetKind(), "", crcr.GetName(), nil)
 	}
 	return nil
 }
@@ -365,3 +354,17 @@ func getKyvernoNameSpace() string {
 	}
 	return kyvernoNamespace
 }
+
+func deleteResource(client *client.Client, apiversion, kind, ns, name string, wg *sync.WaitGroup) {
+	if wg != nil {
+		defer wg.Done()
+	}
+
+	err := client.DeleteResource(apiversion, kind, ns, name, false)
+	if err != nil && !errors.IsNotFound(err) {
+		logger.Error(err, "failed to delete resource", "kind", kind, "name", name)
+		return
+	}
+
+	logger.Info("successfully cleaned up resource", "kind", kind, "name", name)
+}
diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index 85222765a..d9c33a824 100755
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -11,7 +11,6 @@ import (
 
 	kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
 	kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
-	"github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	dclient "github.com/kyverno/kyverno/pkg/dclient"
 	event "github.com/kyverno/kyverno/pkg/event"
@@ -80,9 +79,6 @@ func main() {
 		go http.ListenAndServe("localhost:6060", nil)
 	}
 
-	// Policy report is enabled by default in Kyverno 1.3.0+
-	os.Setenv("POLICY-TYPE", common.PolicyReport)
-
 	version.PrintVersionInfo(log.Log)
 	cleanUp := make(chan struct{})
 	stopCh := signal.SetupSignalHandler()
diff --git a/pkg/engine/common/anchorKey.go b/pkg/engine/common/anchorKey.go
index 6a01e3ca2..52e328a28 100644
--- a/pkg/engine/common/anchorKey.go
+++ b/pkg/engine/common/anchorKey.go
@@ -1,16 +1,68 @@
 package common
 
 import (
+	"errors"
+	"fmt"
+	"strings"
+
 	"github.com/kyverno/kyverno/pkg/engine/anchor/common"
 )
 
+// IsConditionalAnchorError checks if error message has conditional anchor error string
+func IsConditionalAnchorError(msg string) bool {
+	if strings.Contains(msg, ConditionalAnchorErrMsg) {
+		return true
+	}
+	return false
+}
+
+// NewConditionalAnchorError returns a new instance of ConditionalAnchorError
+func NewConditionalAnchorError(msg string) ValidateAnchorError {
+	return ValidateAnchorError{
+		Err:     ConditionalAnchorErr,
+		Message: fmt.Sprintf("%s: %s", ConditionalAnchorErrMsg, msg),
+	}
+}
+
+// IsConditionAnchorError ...
+func (e ValidateAnchorError) IsConditionAnchorError() bool {
+	if e.Err == ConditionalAnchorErr {
+		return true
+	}
+	return false
+}
+
+// IsNil ...
+func (e ValidateAnchorError) IsNil() bool {
+	return e == ValidateAnchorError{}
+}
+
+func (e ValidateAnchorError) Error() error {
+	return errors.New(e.Message)
+}
+
+// AnchorError is the const specification of anchor errors
+type AnchorError int
+
+// ConditionalAnchorErr ...
+const ConditionalAnchorErr AnchorError = iota
+
+// ValidateAnchorError represents the error type of validation anchors
+type ValidateAnchorError struct {
+	Err     AnchorError
+	Message string
+}
+
+// ConditionalAnchorErrMsg - the error message for conditional anchor error
+var ConditionalAnchorErrMsg = "conditionalAnchorError"
+
 // AnchorKey - contains map of anchors
 type AnchorKey struct {
 	// anchorMap - for each anchor key in the patterns it will maintains information if the key exists in the resource
 	// if anchor key of the pattern exists in the resource then (key)=true else (key)=false
 	anchorMap map[string]bool
 	// AnchorError - used in validate to break execution of the recursion when if condition fails
-	AnchorError error
+	AnchorError ValidateAnchorError
 }
 
 // NewAnchorMap -initialize anchorMap
diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go
index 9af9cd708..69f0db9a0 100644
--- a/pkg/engine/generation.go
+++ b/pkg/engine/generation.go
@@ -15,7 +15,7 @@ import (
 )
 
 // Generate checks for validity of generate rule on the resource
-// 1. validate variables to be susbtitute in the general ruleInfo (match,exclude,condition)
+// 1. validate variables to be substitute in the general ruleInfo (match,exclude,condition)
 //    - the caller has to check the ruleResponse to determine whether the path exist
 // 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
 func Generate(policyContext PolicyContext) (resp response.EngineResponse) {
diff --git a/pkg/engine/validate/utils.go b/pkg/engine/validate/utils.go
index f2c3fa77f..6d087acd6 100644
--- a/pkg/engine/validate/utils.go
+++ b/pkg/engine/validate/utils.go
@@ -37,8 +37,9 @@ func getSortedNestedAnchorResource(resources map[string]interface{}) *list.List
 	for k, v := range resources {
 		if hasNestedAnchors(v) {
 			sortedResourceKeys.PushFront(k)
+		} else {
+			sortedResourceKeys.PushBack(k)
 		}
-		sortedResourceKeys.PushBack(k)
 	}
 	return sortedResourceKeys
 }
diff --git a/pkg/engine/validate/validate.go b/pkg/engine/validate/validate.go
index fbde96e8b..58bc3cc33 100644
--- a/pkg/engine/validate/validate.go
+++ b/pkg/engine/validate/validate.go
@@ -3,7 +3,6 @@ package validate
 import (
 	"errors"
 	"fmt"
-	"github.com/kyverno/kyverno/pkg/engine/wildcards"
 	"path"
 	"reflect"
 	"strconv"
@@ -14,6 +13,7 @@ import (
 	commonAnchors "github.com/kyverno/kyverno/pkg/engine/anchor/common"
 	"github.com/kyverno/kyverno/pkg/engine/common"
 	"github.com/kyverno/kyverno/pkg/engine/operator"
+	"github.com/kyverno/kyverno/pkg/engine/wildcards"
 )
 
 // ValidateResourceWithPattern is a start of element-by-element validation process
@@ -23,6 +23,10 @@ func ValidateResourceWithPattern(log logr.Logger, resource, pattern interface{})
 	ac := common.NewAnchorMap()
 	elemPath, err := validateResourceElement(log, resource, pattern, pattern, "/", ac)
 	if err != nil {
+		if common.IsConditionalAnchorError(err.Error()) {
+			return "", nil
+		}
+
 		if !ac.IsAnchorError() {
 			return elemPath, err
 		}
@@ -102,19 +106,14 @@ func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{}
 		if err != nil {
 			// If Conditional anchor fails then we don't process the resources
 			if commonAnchors.IsConditionAnchor(key) {
-				ac.AnchorError = err
-				log.Error(err, "condition anchor did not satisfy, wont process the resource")
-				return "", nil
+				ac.AnchorError = common.NewConditionalAnchorError(fmt.Sprintf("condition anchor did not satisfy: %s", err.Error()))
+				log.V(3).Info(ac.AnchorError.Message)
+				return "", ac.AnchorError.Error()
 			}
 			return handlerPath, err
 		}
 	}
 
-	// If anchor fails then succeed validate and skip further validation of recursion
-	if ac.AnchorError != nil {
-		return "", nil
-	}
-
 	// Evaluate resources
 	// getSortedNestedAnchorResource - keeps the anchor key to start of the list
 	sortedResourceKeys := getSortedNestedAnchorResource(resources)
@@ -150,6 +149,9 @@ func validateArray(log logr.Logger, resourceArray, patternArray []interface{}, o
 				currentPath := path + strconv.Itoa(i) + "/"
 				elemPath, err := validateResourceElement(log, resourceArray[i], patternElement, originPattern, currentPath, ac)
 				if err != nil {
+					if common.IsConditionalAnchorError(err.Error()) {
+						continue
+					}
 					return elemPath, err
 				}
 			}
@@ -283,6 +285,9 @@ func validateArrayOfMaps(log logr.Logger, resourceMapArray []interface{}, patter
 		currentPath := path + strconv.Itoa(i) + "/"
 		returnpath, err := validateResourceElement(log, resourceElement, patternMap, originPattern, currentPath, ac)
 		if err != nil {
+			if common.IsConditionalAnchorError(err.Error()) {
+				continue
+			}
 			return returnpath, err
 		}
 	}
diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go
index bf79244e3..4cfd9e1c7 100644
--- a/pkg/engine/validation.go
+++ b/pkg/engine/validation.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/go-logr/logr"
 	kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
+	"github.com/kyverno/kyverno/pkg/engine/common"
 	"github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/utils"
@@ -197,6 +198,10 @@ func validateResource(log logr.Logger, ctx context.EvalInterface, policy kyverno
 
 		if rule.Validation.Pattern != nil || rule.Validation.AnyPattern != nil {
 			ruleResponse := validatePatterns(log, ctx, resource, rule)
+			if common.IsConditionalAnchorError(ruleResponse.Message) {
+				continue
+			}
+
 			incrementAppliedCount(resp)
 			resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
 		}
diff --git a/pkg/policy/validate_controller.go b/pkg/policy/validate_controller.go
index 60f0d0559..25091b0e6 100644
--- a/pkg/policy/validate_controller.go
+++ b/pkg/policy/validate_controller.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"math/rand"
-	"os"
 	"reflect"
 	"time"
 
@@ -14,7 +13,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/client/clientset/versioned/scheme"
 	kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
 	kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/constant"
 	client "github.com/kyverno/kyverno/pkg/dclient"
@@ -204,9 +202,8 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) {
 	}
 
 	logger.V(4).Info("updating policy", "name", oldP.Name)
-	if os.Getenv("POLICY-TYPE") == common.PolicyReport {
-		pc.enqueueDeletedRule(oldP, curP)
-	}
+
+	pc.enqueueDeletedRule(oldP, curP)
 	pc.enqueuePolicy(curP)
 }
 
@@ -259,9 +256,8 @@ func (pc *PolicyController) updateNsPolicy(old, cur interface{}) {
 	}
 
 	logger.V(4).Info("updating namespace policy", "namespace", oldP.Namespace, "name", oldP.Name)
-	if os.Getenv("POLICY-TYPE") == common.PolicyReport {
-		pc.enqueueDeletedRule(ConvertPolicyToClusterPolicy(oldP), ncurP)
-	}
+
+	pc.enqueueDeletedRule(ConvertPolicyToClusterPolicy(oldP), ncurP)
 	pc.enqueuePolicy(ncurP)
 }
 
@@ -395,7 +391,9 @@ func (pc *PolicyController) syncPolicy(key string) error {
 	} else {
 		var nspolicy *kyverno.Policy
 		nspolicy, err = pc.npLister.Policies(namespace).Get(key)
-		policy = ConvertPolicyToClusterPolicy(nspolicy)
+		if err == nil && nspolicy != nil {
+			policy = ConvertPolicyToClusterPolicy(nspolicy)
+		}
 	}
 
 	if err != nil {
diff --git a/pkg/policyreport/builder.go b/pkg/policyreport/builder.go
index 464e1573a..82fc74700 100755
--- a/pkg/policyreport/builder.go
+++ b/pkg/policyreport/builder.go
@@ -2,7 +2,6 @@ package policyreport
 
 import (
 	"fmt"
-	"os"
 	"reflect"
 
 	"github.com/go-logr/logr"
@@ -10,7 +9,6 @@ import (
 	request "github.com/kyverno/kyverno/pkg/api/kyverno/v1alpha1"
 	report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha1"
 	kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/utils"
@@ -52,12 +50,7 @@ func GeneratePRsFromEngineResponse(ers []response.EngineResponse, log logr.Logge
 			log.V(4).Info("resource does no have a name assigned yet, not creating a policy violation", "resource", er.PolicyResponse.Resource)
 			continue
 		}
-		// skip when response succeed
-		if os.Getenv("POLICY-TYPE") != common.PolicyReport {
-			if er.IsSuccessful() {
-				continue
-			}
-		}
+
 		// build policy violation info
 		pvInfos = append(pvInfos, buildPVInfo(er))
 	}
@@ -220,11 +213,6 @@ func buildPVInfo(er response.EngineResponse) Info {
 func buildViolatedRules(er response.EngineResponse) []kyverno.ViolatedRule {
 	var violatedRules []kyverno.ViolatedRule
 	for _, rule := range er.PolicyResponse.Rules {
-		if os.Getenv("POLICY-TYPE") != common.PolicyReport {
-			if rule.Success {
-				continue
-			}
-		}
 		vrule := kyverno.ViolatedRule{
 			Name:    rule.Name,
 			Type:    rule.Type,
diff --git a/pkg/policyreport/reportcontroller.go b/pkg/policyreport/reportcontroller.go
index 0e107db06..eec00c48d 100644
--- a/pkg/policyreport/reportcontroller.go
+++ b/pkg/policyreport/reportcontroller.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"reflect"
 	"strings"
+	"sync"
 
 	"github.com/go-logr/logr"
 	changerequest "github.com/kyverno/kyverno/pkg/api/kyverno/v1alpha1"
@@ -243,9 +244,13 @@ func (g *ReportGenerator) syncHandler(key string) error {
 	}
 
 	var old interface{}
-	if old, err = g.createReportIfNotPresent(namespace, new, aggregatedRequests); err != nil || old == nil {
+	if old, err = g.createReportIfNotPresent(namespace, new, aggregatedRequests); err != nil {
 		return err
 	}
+	if old == nil {
+		g.cleanupReportRequests(aggregatedRequests)
+		return nil
+	}
 
 	if err := g.updateReport(old, new, aggregatedRequests); err != nil {
 		return err
@@ -260,10 +265,16 @@ func (g *ReportGenerator) syncHandler(key string) error {
 func (g *ReportGenerator) createReportIfNotPresent(namespace string, new *unstructured.Unstructured, aggregatedRequests interface{}) (report interface{}, err error) {
 	log := g.log.WithName("createReportIfNotPresent")
 	if namespace != "" {
-		if ns, err := g.nsLister.Get(namespace); err == nil {
-			if ns.GetDeletionTimestamp() != nil {
+		ns, err := g.nsLister.Get(namespace)
+		if err != nil {
+			if apierrors.IsNotFound(err) {
 				return nil, nil
 			}
+			return nil, fmt.Errorf("failed to fetch namespace: %v", err)
+		}
+
+		if ns.GetDeletionTimestamp() != nil {
+			return nil, nil
 		}
 
 		report, err = g.reportLister.PolicyReports(namespace).Get(generatePolicyReportName((namespace)))
@@ -395,6 +406,11 @@ func (g *ReportGenerator) aggregateReports(namespace string) (
 			if !apierrors.IsNotFound(err) {
 				return nil, nil, fmt.Errorf("unable to get namespace %s: %v", namespace, err)
 			}
+			// Namespace is deleted, create a fake ns to clean up RCRs
+			ns = new(v1.Namespace)
+			ns.SetName(namespace)
+			now := metav1.Now()
+			ns.SetDeletionTimestamp(&now)
 		}
 
 		selector := labels.SelectorFromSet(labels.Set(map[string]string{resourceLabelNamespace: namespace}))
@@ -465,6 +481,7 @@ func mergeRequests(ns *v1.Namespace, requestsGeneral interface{}) (*unstructured
 
 		req := &unstructured.Unstructured{Object: obj}
 		setReport(req, ns)
+
 		return req, aggregatedRequests, nil
 	}
 
@@ -552,13 +569,19 @@ func (g *ReportGenerator) updateReport(old interface{}, new *unstructured.Unstru
 func (g *ReportGenerator) cleanupReportRequests(requestsGeneral interface{}) {
 	defer g.log.V(5).Info("successfully cleaned up report requests")
 	if requests, ok := requestsGeneral.([]*changerequest.ReportChangeRequest); ok {
+		var wg sync.WaitGroup
+		wg.Add(len(requests))
 		for _, request := range requests {
-			if err := g.dclient.DeleteResource(request.APIVersion, "ReportChangeRequest", config.KyvernoNamespace, request.Name, false); err != nil {
-				if !apierrors.IsNotFound(err) {
-					g.log.Error(err, "failed to delete report request")
+			go func(request *changerequest.ReportChangeRequest) {
+				if err := g.dclient.DeleteResource(request.APIVersion, "ReportChangeRequest", config.KyvernoNamespace, request.Name, false); err != nil {
+					if !apierrors.IsNotFound(err) {
+						g.log.Error(err, "failed to delete report request")
+					}
 				}
-			}
+				wg.Done()
+			}(request)
 		}
+		wg.Wait()
 	}
 
 	if requests, ok := requestsGeneral.([]*changerequest.ClusterReportChangeRequest); ok {
diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go
index 5d89aebbb..0bd31a177 100644
--- a/pkg/webhooks/server.go
+++ b/pkg/webhooks/server.go
@@ -19,7 +19,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/config"
 	client "github.com/kyverno/kyverno/pkg/dclient"
 	context2 "github.com/kyverno/kyverno/pkg/engine/context"
-	enginutils "github.com/kyverno/kyverno/pkg/engine/utils"
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/openapi"
 	"github.com/kyverno/kyverno/pkg/policycache"
@@ -33,7 +32,6 @@ import (
 	"github.com/kyverno/kyverno/pkg/webhooks/generate"
 	v1beta1 "k8s.io/api/admission/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	rbacinformer "k8s.io/client-go/informers/rbac/v1"
 	rbaclister "k8s.io/client-go/listers/rbac/v1"
 	"k8s.io/client-go/tools/cache"
@@ -364,19 +362,7 @@ func (ws *WebhookServer) ResourceMutation(request *v1beta1.AdmissionRequest) *v1
 }
 
 func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
-	logger := ws.log.WithName("resourceValidation").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
-
-	if request.Operation == v1beta1.Delete || request.Operation == v1beta1.Update {
-		if err := ws.excludeKyvernoResources(request); err != nil {
-			return &v1beta1.AdmissionResponse{
-				Allowed: false,
-				Result: &metav1.Status{
-					Status:  "Failure",
-					Message: err.Error(),
-				},
-			}
-		}
-	}
+	logger := ws.log.WithName("Validate").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
 
 	if !ws.supportMutateValidate {
 		logger.Info("mutate and validate rules are not supported prior to Kubernetes 1.14.0")
@@ -539,34 +525,3 @@ func (ws *WebhookServer) bodyToAdmissionReview(request *http.Request, writer htt
 
 	return admissionReview
 }
-
-// excludeKyvernoResources will check resource can have access or not
-func (ws *WebhookServer) excludeKyvernoResources(request *v1beta1.AdmissionRequest) error {
-	logger := ws.log.WithName("resourceValidation").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
-
-	var resource *unstructured.Unstructured
-	var err error
-	if request.Operation == v1beta1.Delete {
-		resource, err = enginutils.ConvertToUnstructured(request.OldObject.Raw)
-	} else if request.Operation == v1beta1.Update {
-		resource, err = enginutils.ConvertToUnstructured(request.Object.Raw)
-	}
-	if err != nil {
-		logger.Error(err, "failed to convert object resource to unstructured format")
-		return err
-	}
-
-	labels := resource.GetLabels()
-	if labels["app.kubernetes.io/managed-by"] == "kyverno" && labels["policy.kyverno.io/synchronize"] == "enable" {
-		isAuthorized, err := userinfo.IsRoleAuthorize(ws.rbLister, ws.crbLister, ws.rLister, ws.crLister, request, ws.configHandler)
-		if err != nil {
-			return fmt.Errorf("failed to get RBAC information for request %v", err)
-		}
-
-		if !isAuthorized {
-			return fmt.Errorf("resource is managed by a Kyverno policy and cannot be update manually. You can edit the policy %s to update this resource", labels["policy.kyverno.io/policy-name"])
-		}
-	}
-
-	return nil
-}
diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go
index 69890e4ca..30518fd3c 100644
--- a/pkg/webhooks/validation.go
+++ b/pkg/webhooks/validation.go
@@ -1,7 +1,6 @@
 package webhooks
 
 import (
-	"os"
 	"reflect"
 	"sort"
 	"time"
@@ -9,7 +8,6 @@ import (
 	"github.com/go-logr/logr"
 	kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
 	v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
-	"github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/engine"
 	"github.com/kyverno/kyverno/pkg/engine/context"
@@ -130,21 +128,20 @@ func HandleValidation(
 	prInfos := policyreport.GeneratePRsFromEngineResponse(engineResponses, logger)
 	prGenerator.Add(prInfos...)
 
-	if os.Getenv("POLICY-TYPE") == common.PolicyReport {
-		if request.Operation == v1beta1.Delete {
-			prGenerator.Add(policyreport.Info{
-				Resource: unstructured.Unstructured{
-					Object: map[string]interface{}{
-						"kind": oldR.GetKind(),
-						"metadata": map[string]interface{}{
-							"name":      oldR.GetName(),
-							"namespace": oldR.GetNamespace(),
-						},
+	if request.Operation == v1beta1.Delete {
+		prGenerator.Add(policyreport.Info{
+			Resource: unstructured.Unstructured{
+				Object: map[string]interface{}{
+					"kind": oldR.GetKind(),
+					"metadata": map[string]interface{}{
+						"name":      oldR.GetName(),
+						"namespace": oldR.GetNamespace(),
 					},
 				},
-			})
-		}
+			},
+		})
 	}
+
 	return true, ""
 }
 


Test patch:
diff --git a/pkg/engine/validate/validate_test.go b/pkg/engine/validate/validate_test.go
index 7e73c0a43..e037d7bd2 100644
--- a/pkg/engine/validate/validate_test.go
+++ b/pkg/engine/validate/validate_test.go
@@ -2,6 +2,7 @@ package validate
 
 import (
 	"encoding/json"
+	"fmt"
 	"testing"
 
 	"github.com/kyverno/kyverno/pkg/engine/common"
@@ -1418,3 +1419,162 @@ func testValidationPattern(t *testing.T, num string, patternBytes []byte, resour
 		assert.Assert(t, err != nil, num)
 	}
 }
+
+func TestConditionalAnchorWithMultiplePatterns(t *testing.T) {
+	testCases := []struct {
+		name     string
+		pattern  []byte
+		resource []byte
+		nilErr   bool
+	}{
+		{
+			name:     "test-1",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-2",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-x",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "!*:* | *:latest","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-3",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-4",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-5",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-6",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-7",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx", "imagePullPolicy": "Always"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-8",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Always"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-9",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Always"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-10",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx", "imagePullPolicy": "Never"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-11",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Never"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-12",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Never"},{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-13",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-14",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-15",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-16",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-17",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-18",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.28", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Never"}]}}`),
+			nilErr:   true,
+		},
+		{
+			name:     "test-19",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-20",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:latest", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:latest", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   false,
+		},
+		{
+			name:     "test-21",
+			pattern:  []byte(`{"spec": {"containers": [{"name": "*","(image)": "*:latest | !*:*","imagePullPolicy": "!Always"}]}}`),
+			resource: []byte(`{"spec": {"containers": [{"name": "busybox","image": "busybox:1.2.3", "imagePullPolicy": "Always"},{"name": "nginx","image": "nginx:1.2.3", "imagePullPolicy": "Always"}]}}`),
+			nilErr:   true,
+		},
+	}
+
+	for _, testCase := range testCases {
+		var pattern, resource interface{}
+		err := json.Unmarshal(testCase.pattern, &pattern)
+		assert.NilError(t, err)
+		err = json.Unmarshal(testCase.resource, &resource)
+		assert.NilError(t, err)
+
+		_, err = ValidateResourceWithPattern(log.Log, resource, pattern)
+		if testCase.nilErr {
+			assert.NilError(t, err, fmt.Sprintf("\ntest: %s\npattern: %s\nresource: %s\n", testCase.name, pattern, resource))
+		} else {
+			assert.Assert(t,
+				err != nil,
+				fmt.Sprintf("\ntest: %s\npattern: %s\nresource: %s\nmsg: %v", testCase.name, pattern, resource, err))
+		}
+	}
+}
diff --git a/test/e2e/generate/generate_test.go b/test/e2e/generate/generate_test.go
index 465491a52..0eb55d235 100644
--- a/test/e2e/generate/generate_test.go
+++ b/test/e2e/generate/generate_test.go
@@ -131,16 +131,6 @@ func Test_ClusterRole_ClusterRoleBinding_Sets(t *testing.T) {
 		Expect(rbRes.GetName()).To(Equal(tests.ClusterRoleBindingName))
 		// ============================================
 
-		// If Sync=true, Verify that an Error will occur on deletion of created resources
-		if tests.Sync {
-			// Delete generated ClusterRoleBinding and It'll Fail
-			err = e2eClient.DeleteClusteredResource(crbGVR, tests.ClusterRoleBindingName)
-			Expect(err).To(HaveOccurred())
-			// Delete generated ClusterRole and It'll Fail
-			err = e2eClient.DeleteClusteredResource(crGVR, tests.ClusterRoleName)
-			Expect(err).To(HaveOccurred())
-		}
-
 		// ======= CleanUp Resources =====
 		e2eClient.CleanClusterPolicies(clPolGVR)
 		// Clear Namespace
@@ -251,20 +241,6 @@ func Test_Role_RoleBinding_Sets(t *testing.T) {
 		Expect(rbRes.GetName()).To(Equal(tests.RoleBindingName))
 		// ============================================
 
-		// If Sync=true, Verify that an Error will occur on deletion of created resources
-		if tests.Sync {
-
-			// Delete generated RoleBinding and It'll Fail
-			By(fmt.Sprintf("Sync : Delete RoleBinding as Namespace %s", tests.ResourceNamespace))
-			err = e2eClient.DeleteNamespacedResource(rbGVR, tests.ResourceNamespace, tests.RoleBindingName)
-			Expect(err).To(HaveOccurred())
-
-			// Delete generated Role and It'll Fail
-			By(fmt.Sprintf("Sync : Delete Role as Namespace %s", tests.ResourceNamespace))
-			err = e2eClient.DeleteNamespacedResource(rGVR, tests.ResourceNamespace, tests.RoleName)
-			Expect(err).To(HaveOccurred())
-		}
-
 		// ======= CleanUp Resources =====
 		e2eClient.CleanClusterPolicies(clPolGVR)