func (g *ReportGenerator) syncHandler(key string) (aggregatedRequests interface{}, err error) {
	g.log.V(4).Info("syncing policy report", "key", key)
	if policy, rule, ok := isDeletedPolicyKey(key); ok {
		return g.removePolicyEntryFromReport(policy, rule)
	}
	namespace := key
	new, aggregatedRequests, err := g.aggregateReports(namespace)
	if err != nil {
		return aggregatedRequests, fmt.Errorf("failed to aggregate reportChangeRequest results %v", err)
	}
	var old interface{}
	if old, err = g.createReportIfNotPresent(namespace, new, aggregatedRequests); err != nil {
		return aggregatedRequests, err
	}
	if old == nil {
		g.cleanupReportRequests(aggregatedRequests)
		return nil, nil
	}
	if err := g.updateReport(old, new, aggregatedRequests); err != nil {
		return aggregatedRequests, err
	}
	g.cleanupReportRequests(aggregatedRequests)
	return nil, nil
}
syncHandler reconciles clusterPolicyReport if namespace == ""
otherwise it updates policyReport
package policyreport
import (
	"fmt"
	"reflect"
	"strings"
	"time"
	"github.com/go-logr/logr"
	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	labels "k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	informers "k8s.io/client-go/informers/core/v1"
	"k8s.io/client-go/kubernetes"
	listerv1 "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	changerequest "github.com/kyverno/kyverno/pkg/api/kyverno/v1alpha2"
	report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
	kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
	requestinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha2"
	policyreportinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/policyreport/v1alpha2"
	requestlister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1alpha2"
	policyreport "github.com/kyverno/kyverno/pkg/client/listers/policyreport/v1alpha2"
	"github.com/kyverno/kyverno/pkg/config"
	dclient "github.com/kyverno/kyverno/pkg/dclient"
	"github.com/kyverno/kyverno/pkg/version"
)
const (
	prWorkQueueName     = "policy-report-controller"
	clusterpolicyreport = "clusterpolicyreport"
)
type ReportGenerator struct {
	pclient *kyvernoclient.Clientset
	dclient *dclient.Client
	clusterReportInformer    policyreportinformer.ClusterPolicyReportInformer
	reportInformer           policyreportinformer.PolicyReportInformer
	reportReqInformer        requestinformer.ReportChangeRequestInformer
	clusterReportReqInformer requestinformer.ClusterReportChangeRequestInformer
	reportLister policyreport.PolicyReportLister
	reportSynced cache.InformerSynced
	clusterReportLister policyreport.ClusterPolicyReportLister
	clusterReportSynced cache.InformerSynced
	reportChangeRequestLister requestlister.ReportChangeRequestLister
	reportReqSynced           cache.InformerSynced
	clusterReportChangeRequestLister requestlister.ClusterReportChangeRequestLister
	clusterReportReqSynced           cache.InformerSynced
	nsLister       listerv1.NamespaceLister
	nsListerSynced cache.InformerSynced
	queue workqueue.RateLimitingInterface
	ReconcileCh chan bool
	log logr.Logger
}
func NewReportGenerator(
	kubeClient kubernetes.Interface,
	pclient *kyvernoclient.Clientset,
	dclient *dclient.Client,
	clusterReportInformer policyreportinformer.ClusterPolicyReportInformer,
	reportInformer policyreportinformer.PolicyReportInformer,
	reportReqInformer requestinformer.ReportChangeRequestInformer,
	clusterReportReqInformer requestinformer.ClusterReportChangeRequestInformer,
	namespace informers.NamespaceInformer,
	log logr.Logger) (*ReportGenerator, error) {
	gen := &ReportGenerator{
		pclient:                  pclient,
		dclient:                  dclient,
		clusterReportInformer:    clusterReportInformer,
		reportInformer:           reportInformer,
		reportReqInformer:        reportReqInformer,
		clusterReportReqInformer: clusterReportReqInformer,
		queue:                    workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), prWorkQueueName),
		ReconcileCh:              make(chan bool, 10),
		log:                      log,
	}
	gen.clusterReportLister = clusterReportInformer.Lister()
	gen.clusterReportSynced = clusterReportInformer.Informer().HasSynced
	gen.reportLister = reportInformer.Lister()
	gen.reportSynced = reportInformer.Informer().HasSynced
	gen.clusterReportChangeRequestLister = clusterReportReqInformer.Lister()
	gen.clusterReportReqSynced = clusterReportReqInformer.Informer().HasSynced
	gen.reportChangeRequestLister = reportReqInformer.Lister()
	gen.reportReqSynced = reportReqInformer.Informer().HasSynced
	gen.nsLister = namespace.Lister()
	gen.nsListerSynced = namespace.Informer().HasSynced
	return gen, nil
}
const deletedPolicyKey string = "deletedpolicy"
func generateCacheKey(changeRequest interface{}) string {
	if request, ok := changeRequest.(*changerequest.ReportChangeRequest); ok {
		label := request.GetLabels()
		policy := label[deletedLabelPolicy]
		rule := label[deletedLabelRule]
		if rule != "" || policy != "" {
			return strings.Join([]string{deletedPolicyKey, policy, rule}, "/")
		}
		ns := label[resourceLabelNamespace]
		if ns == "" {
			ns = "default"
		}
		return ns
	} else if request, ok := changeRequest.(*changerequest.ClusterReportChangeRequest); ok {
		label := request.GetLabels()
		policy := label[deletedLabelPolicy]
		rule := label[deletedLabelRule]
		if rule != "" || policy != "" {
			return strings.Join([]string{deletedPolicyKey, policy, rule}, "/")
		}
		return ""
	}
	return ""
}
func managedRequest(changeRequest interface{}) bool {
	labels := make(map[string]string)
	if request, ok := changeRequest.(*changerequest.ReportChangeRequest); ok {
		labels = request.GetLabels()
	} else if request, ok := changeRequest.(*changerequest.ClusterReportChangeRequest); ok {
		labels = request.GetLabels()
	}
	if v, ok := labels[appVersion]; !ok || v != version.BuildVersion {
		return false
	}
	return true
}
func (g *ReportGenerator) addReportChangeRequest(obj interface{}) {
	if !managedRequest(obj) {
		g.cleanupReportRequests([]*changerequest.ReportChangeRequest{obj.(*changerequest.ReportChangeRequest)})
		return
	}
	key := generateCacheKey(obj)
	g.queue.Add(key)
}
func (g *ReportGenerator) updateReportChangeRequest(old interface{}, cur interface{}) {
	oldReq := old.(*changerequest.ReportChangeRequest)
	curReq := cur.(*changerequest.ReportChangeRequest)
	if reflect.DeepEqual(oldReq.Results, curReq.Results) {
		return
	}
	if !managedRequest(curReq) {
		g.cleanupReportRequests([]*changerequest.ReportChangeRequest{curReq})
		return
	}
	key := generateCacheKey(cur)
	g.queue.Add(key)
}
func (g *ReportGenerator) addClusterReportChangeRequest(obj interface{}) {
	if !managedRequest(obj) {
		g.cleanupReportRequests([]*changerequest.ClusterReportChangeRequest{obj.(*changerequest.ClusterReportChangeRequest)})
		return
	}
	key := generateCacheKey(obj)
	g.queue.Add(key)
}
func (g *ReportGenerator) updateClusterReportChangeRequest(old interface{}, cur interface{}) {
	oldReq := old.(*changerequest.ClusterReportChangeRequest)
	curReq := cur.(*changerequest.ClusterReportChangeRequest)
	if reflect.DeepEqual(oldReq.Results, curReq.Results) {
		return
	}
	if !managedRequest(curReq) {
		return
	}
	g.queue.Add("")
}
func (g *ReportGenerator) deletePolicyReport(obj interface{}) {
	report := obj.(*report.PolicyReport)
	g.log.V(2).Info("PolicyReport deleted", "name", report.GetName())
	g.ReconcileCh <- false
}
func (g *ReportGenerator) deleteClusterPolicyReport(obj interface{}) {
	g.log.V(2).Info("ClusterPolicyReport deleted")
	g.ReconcileCh <- false
}
func (g *ReportGenerator) Run(workers int, stopCh <-chan struct{}) {
	logger := g.log
	defer utilruntime.HandleCrash()
	defer g.queue.ShutDown()
	logger.Info("start")
	defer logger.Info("shutting down")
	if !cache.WaitForCacheSync(stopCh, g.reportReqSynced, g.clusterReportReqSynced, g.reportSynced, g.clusterReportSynced, g.nsListerSynced) {
		logger.Info("failed to sync informer cache")
	}
	g.reportReqInformer.Informer().AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc:    g.addReportChangeRequest,
			UpdateFunc: g.updateReportChangeRequest,
		})
	g.clusterReportReqInformer.Informer().AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			AddFunc:    g.addClusterReportChangeRequest,
			UpdateFunc: g.updateClusterReportChangeRequest,
		})
	g.reportInformer.Informer().AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			DeleteFunc: g.deletePolicyReport,
		})
	g.clusterReportInformer.Informer().AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			DeleteFunc: g.deleteClusterPolicyReport,
		})
	for i := 0; i < workers; i++ {
		go wait.Until(g.runWorker, time.Second, stopCh)
	}
	<-stopCh
}
func (g *ReportGenerator) runWorker() {
	for g.processNextWorkItem() {
	}
}
func (g *ReportGenerator) processNextWorkItem() bool {
	key, shutdown := g.queue.Get()
	if shutdown {
		return false
	}
	defer g.queue.Done(key)
	keyStr, ok := key.(string)
	if !ok {
		g.queue.Forget(key)
		g.log.Info("incorrect type; expecting type 'string'", "obj", key)
		return true
	}
	aggregatedRequests, err := g.syncHandler(keyStr)
	g.handleErr(err, key, aggregatedRequests)
	return true
}
func (g *ReportGenerator) handleErr(err error, key interface{}, aggregatedRequests interface{}) {
	logger := g.log
	if err == nil {
		g.queue.Forget(key)
		return
	}
	if g.queue.NumRequeues(key) < workQueueRetryLimit {
		logger.V(3).Info("retrying policy report", "key", key, "error", err.Error())
		g.queue.AddRateLimited(key)
		return
	}
	logger.Error(err, "failed to process policy report", "key", key)
	g.queue.Forget(key)
	if aggregatedRequests != nil {
		g.cleanupReportRequests(aggregatedRequests)
	}
}