mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
feat: split policy report per policy bases (#4147)
* feat: split policy report per policy bases Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * add policy name as a handler key Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * update merge change request logic Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * handle the delete resource update on policy report Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * add splitPolicyReport feature gate Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * delete old reports if splitPolicyReport feature enable Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * use trim policyname as label and create name Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com> * fix change request result Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com>
This commit is contained in:
parent
77fb10a430
commit
9226873e68
6 changed files with 322 additions and 39 deletions
|
@ -70,6 +70,7 @@ var (
|
|||
clientRateLimitQPS float64
|
||||
clientRateLimitBurst int
|
||||
changeRequestLimit int
|
||||
splitPolicyReport bool
|
||||
webhookRegistrationTimeout time.Duration
|
||||
setupLog = log.Log.WithName("setup")
|
||||
)
|
||||
|
@ -94,7 +95,7 @@ func main() {
|
|||
flag.Func(toggle.AutogenInternalsFlagName, toggle.AutogenInternalsDescription, toggle.AutogenInternalsFlag)
|
||||
flag.DurationVar(&webhookRegistrationTimeout, "webhookRegistrationTimeout", 120*time.Second, "Timeout for webhook registration, e.g., 30s, 1m, 5m.")
|
||||
flag.IntVar(&changeRequestLimit, "maxReportChangeRequests", 1000, "maximum pending report change requests per namespace or for the cluster-wide policy report")
|
||||
|
||||
flag.BoolVar(&splitPolicyReport, "splitPolicyReport", false, "Set the flag to 'true', to enable the split-up PolicyReports per policy.")
|
||||
if err := flag.Set("v", "2"); err != nil {
|
||||
setupLog.Error(err, "failed to set log level")
|
||||
os.Exit(1)
|
||||
|
@ -206,6 +207,7 @@ func main() {
|
|||
kyvernoV1.ClusterPolicies(),
|
||||
kyvernoV1.Policies(),
|
||||
changeRequestLimit,
|
||||
splitPolicyReport,
|
||||
log.Log.WithName("ReportChangeRequestGenerator"),
|
||||
)
|
||||
|
||||
|
@ -218,6 +220,7 @@ func main() {
|
|||
kyvernoV1alpha2.ClusterReportChangeRequests(),
|
||||
kubeInformer.Core().V1().Namespaces(),
|
||||
reportReqGen.CleanupChangeRequest,
|
||||
splitPolicyReport,
|
||||
log.Log.WithName("PolicyReportGenerator"),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -28,6 +28,7 @@ const (
|
|||
|
||||
// the following labels are used to list rcr / crcr
|
||||
ResourceLabelNamespace string = "kyverno.io/resource.namespace"
|
||||
policyLabel string = "kyverno.io/policy-name"
|
||||
deletedLabelPolicy string = "kyverno.io/delete.policy"
|
||||
deletedLabelRule string = "kyverno.io/delete.rule"
|
||||
|
||||
|
@ -56,6 +57,13 @@ func GeneratePolicyReportName(ns string) string {
|
|||
return name
|
||||
}
|
||||
|
||||
func TrimmedName(s string) string {
|
||||
if len(s) > 63 {
|
||||
return s[:63]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GeneratePRsFromEngineResponse generate Violations from engine responses
|
||||
func GeneratePRsFromEngineResponse(ers []*response.EngineResponse, log logr.Logger) (pvInfos []Info) {
|
||||
for _, er := range ers {
|
||||
|
@ -209,6 +217,7 @@ func set(obj *unstructured.Unstructured, info Info) {
|
|||
|
||||
obj.SetLabels(map[string]string{
|
||||
ResourceLabelNamespace: info.Namespace,
|
||||
policyLabel: TrimmedName(info.PolicyName),
|
||||
appVersion: version.BuildVersion,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -36,19 +36,23 @@ type changeRequestCreator struct {
|
|||
mutex sync.RWMutex
|
||||
queue []string
|
||||
|
||||
// splitPolicyReport enable/disable the PolicyReport split-up per policy feature
|
||||
splitPolicyReport bool
|
||||
|
||||
tickerInterval time.Duration
|
||||
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
func newChangeRequestCreator(client kyvernoclient.Interface, tickerInterval time.Duration, log logr.Logger) creator {
|
||||
func newChangeRequestCreator(client kyvernoclient.Interface, tickerInterval time.Duration, splitPolicyReport bool, log logr.Logger) creator {
|
||||
return &changeRequestCreator{
|
||||
client: client,
|
||||
RCRCache: cache.New(0, 24*time.Hour),
|
||||
CRCRCache: cache.New(0, 24*time.Hour),
|
||||
queue: []string{},
|
||||
tickerInterval: tickerInterval,
|
||||
log: log,
|
||||
client: client,
|
||||
RCRCache: cache.New(0, 24*time.Hour),
|
||||
CRCRCache: cache.New(0, 24*time.Hour),
|
||||
queue: []string{},
|
||||
tickerInterval: tickerInterval,
|
||||
splitPolicyReport: splitPolicyReport,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,10 +114,23 @@ func (c *changeRequestCreator) run(stopChan <-chan struct{}) {
|
|||
ticker := time.NewTicker(c.tickerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
if c.splitPolicyReport {
|
||||
err := CleanupPolicyReport(c.client)
|
||||
if err != nil {
|
||||
c.log.Error(err, "failed to delete old reports")
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
requests, size := c.mergeRequests()
|
||||
requests := []*unstructured.Unstructured{}
|
||||
var size int
|
||||
if c.splitPolicyReport {
|
||||
requests, size = c.mergeRequestsPerPolicy()
|
||||
} else {
|
||||
requests, size = c.mergeRequests()
|
||||
}
|
||||
for _, request := range requests {
|
||||
if err := c.create(request); err != nil {
|
||||
c.log.Error(err, "failed to create report change request", "req", request.Object)
|
||||
|
@ -216,7 +233,96 @@ func (c *changeRequestCreator) mergeRequests() (results []*unstructured.Unstruct
|
|||
results = append(results, mergedNamespacedRCR)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// mergeRequests merges all current cached requests per policy
|
||||
// it blocks writing to the cache
|
||||
func (c *changeRequestCreator) mergeRequestsPerPolicy() (results []*unstructured.Unstructured, size int) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
mergedCRCR := make(map[string]*unstructured.Unstructured)
|
||||
mergedRCR := make(map[string]*unstructured.Unstructured)
|
||||
size = len(c.queue)
|
||||
|
||||
for _, uid := range c.queue {
|
||||
if unstr, ok := c.CRCRCache.Get(uid); ok {
|
||||
if crcr, ok := unstr.(*unstructured.Unstructured); ok {
|
||||
policyName := crcr.GetLabels()[policyLabel]
|
||||
mergedPolicyCRCR, ok := mergedCRCR[policyName]
|
||||
if !ok {
|
||||
mergedPolicyCRCR = &unstructured.Unstructured{}
|
||||
}
|
||||
|
||||
if isDeleteRequest(crcr) {
|
||||
if !reflect.DeepEqual(mergedPolicyCRCR, &unstructured.Unstructured{}) {
|
||||
results = append(results, mergedPolicyCRCR)
|
||||
mergedCRCR[policyName] = &unstructured.Unstructured{}
|
||||
}
|
||||
|
||||
results = append(results, crcr)
|
||||
} else {
|
||||
if reflect.DeepEqual(mergedPolicyCRCR, &unstructured.Unstructured{}) {
|
||||
mergedCRCR[policyName] = crcr
|
||||
continue
|
||||
}
|
||||
|
||||
if ok := merge(mergedPolicyCRCR, crcr); !ok {
|
||||
results = append(results, mergedPolicyCRCR)
|
||||
mergedCRCR[policyName] = crcr
|
||||
} else {
|
||||
mergedCRCR[policyName] = mergedPolicyCRCR
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if unstr, ok := c.RCRCache.Get(uid); ok {
|
||||
if rcr, ok := unstr.(*unstructured.Unstructured); ok {
|
||||
policyName := rcr.GetLabels()[policyLabel]
|
||||
resourceNS := rcr.GetLabels()[ResourceLabelNamespace]
|
||||
mergedNamespacedRCR, ok := mergedRCR[policyName+resourceNS]
|
||||
if !ok {
|
||||
mergedNamespacedRCR = &unstructured.Unstructured{}
|
||||
}
|
||||
|
||||
if isDeleteRequest(rcr) {
|
||||
if !reflect.DeepEqual(mergedNamespacedRCR, &unstructured.Unstructured{}) {
|
||||
results = append(results, mergedNamespacedRCR)
|
||||
mergedRCR[policyName+resourceNS] = &unstructured.Unstructured{}
|
||||
}
|
||||
|
||||
results = append(results, rcr)
|
||||
} else {
|
||||
if reflect.DeepEqual(mergedNamespacedRCR, &unstructured.Unstructured{}) {
|
||||
mergedRCR[policyName+resourceNS] = rcr
|
||||
continue
|
||||
}
|
||||
|
||||
if ok := merge(mergedNamespacedRCR, rcr); !ok {
|
||||
results = append(results, mergedNamespacedRCR)
|
||||
mergedRCR[policyName+resourceNS] = rcr
|
||||
} else {
|
||||
mergedRCR[policyName+resourceNS] = mergedNamespacedRCR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, mergedPolicyCRCR := range mergedCRCR {
|
||||
if !reflect.DeepEqual(mergedPolicyCRCR, &unstructured.Unstructured{}) {
|
||||
results = append(results, mergedPolicyCRCR)
|
||||
}
|
||||
}
|
||||
|
||||
for _, mergedNamespacedRCR := range mergedRCR {
|
||||
if !reflect.DeepEqual(mergedNamespacedRCR, &unstructured.Unstructured{}) {
|
||||
results = append(results, mergedNamespacedRCR)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package policyreport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
@ -12,7 +13,10 @@ import (
|
|||
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernov1alpha2listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1alpha2"
|
||||
policyreportv1alpha2listers "github.com/kyverno/kyverno/pkg/client/listers/policyreport/v1alpha2"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type PolicyReportEraser interface {
|
||||
|
@ -220,3 +224,41 @@ func mapToStruct(in, out interface{}) error {
|
|||
jsonBytes, _ := json.Marshal(in)
|
||||
return json.Unmarshal(jsonBytes, out)
|
||||
}
|
||||
|
||||
func CleanupPolicyReport(client kyvernoclient.Interface) error {
|
||||
var errors []string
|
||||
var gracePeriod int64 = 0
|
||||
|
||||
deleteOptions := metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}
|
||||
selector := labels.SelectorFromSet(labels.Set(map[string]string{LabelSelectorKey: LabelSelectorValue}))
|
||||
|
||||
err := client.KyvernoV1alpha2().ClusterReportChangeRequests().DeleteCollection(context.TODO(), deleteOptions, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
|
||||
err = client.KyvernoV1alpha2().ReportChangeRequests(config.KyvernoNamespace()).DeleteCollection(context.TODO(), deleteOptions, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
|
||||
err = client.Wgpolicyk8sV1alpha2().ClusterPolicyReports().DeleteCollection(context.TODO(), deleteOptions, metav1.ListOptions{LabelSelector: selector.String()})
|
||||
if err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
|
||||
reports, err := client.Wgpolicyk8sV1alpha2().PolicyReports(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
|
||||
if err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
for _, report := range reports.Items {
|
||||
err = client.Wgpolicyk8sV1alpha2().PolicyReports(report.Namespace).Delete(context.TODO(), report.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
errors = append(errors, err.Error())
|
||||
}
|
||||
}
|
||||
if len(errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%v", strings.Join(errors, ";"))
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ type ReportGenerator struct {
|
|||
reportChangeRequestLister kyvernov1alpha2listers.ReportChangeRequestLister
|
||||
clusterReportChangeRequestLister kyvernov1alpha2listers.ClusterReportChangeRequestLister
|
||||
nsLister corev1listers.NamespaceLister
|
||||
// splitPolicyReport enable/disable the PolicyReport split-up per policy feature
|
||||
splitPolicyReport bool
|
||||
|
||||
informersSynced []cache.InformerSynced
|
||||
|
||||
|
@ -91,6 +93,7 @@ func NewReportGenerator(
|
|||
clusterReportReqInformer kyvernov1alpha2informers.ClusterReportChangeRequestInformer,
|
||||
namespace corev1informers.NamespaceInformer,
|
||||
cleanupChangeRequest chan<- ReconcileInfo,
|
||||
splitPolicyReport bool,
|
||||
log logr.Logger,
|
||||
) (*ReportGenerator, error) {
|
||||
gen := &ReportGenerator{
|
||||
|
@ -101,6 +104,7 @@ func NewReportGenerator(
|
|||
reportReqInformer: reportReqInformer,
|
||||
clusterReportReqInformer: clusterReportReqInformer,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), prWorkQueueName),
|
||||
splitPolicyReport: splitPolicyReport,
|
||||
ReconcileCh: make(chan bool, 10),
|
||||
cleanupChangeRequest: cleanupChangeRequest,
|
||||
log: log,
|
||||
|
@ -120,7 +124,7 @@ func NewReportGenerator(
|
|||
// - <namespace name> for the resource
|
||||
// - "" for cluster wide resource
|
||||
// - "deletedpolicy/policyName/ruleName(optional)" for a deleted policy or rule
|
||||
func generateCacheKey(changeRequest interface{}) string {
|
||||
func (g *ReportGenerator) generateCacheKey(changeRequest interface{}) string {
|
||||
if request, ok := changeRequest.(*kyvernov1alpha2.ReportChangeRequest); ok {
|
||||
label := request.GetLabels()
|
||||
policy := label[deletedLabelPolicy]
|
||||
|
@ -133,7 +137,12 @@ func generateCacheKey(changeRequest interface{}) string {
|
|||
if ns == "" {
|
||||
ns = "default"
|
||||
}
|
||||
return ns
|
||||
if g.splitPolicyReport {
|
||||
policy = label[policyLabel]
|
||||
return strings.Join([]string{ns, policy}, "/")
|
||||
} else {
|
||||
return ns
|
||||
}
|
||||
} else if request, ok := changeRequest.(*kyvernov1alpha2.ClusterReportChangeRequest); ok {
|
||||
label := request.GetLabels()
|
||||
policy := label[deletedLabelPolicy]
|
||||
|
@ -141,9 +150,13 @@ func generateCacheKey(changeRequest interface{}) string {
|
|||
if rule != "" || policy != "" {
|
||||
return strings.Join([]string{deletedPolicyKey, policy, rule}, "/")
|
||||
}
|
||||
return ""
|
||||
if g.splitPolicyReport {
|
||||
policy = label[policyLabel]
|
||||
return strings.Join([]string{"", policy}, "/")
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -171,7 +184,7 @@ func (g *ReportGenerator) addReportChangeRequest(obj interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
key := generateCacheKey(obj)
|
||||
key := g.generateCacheKey(obj)
|
||||
g.queue.Add(key)
|
||||
}
|
||||
|
||||
|
@ -187,7 +200,7 @@ func (g *ReportGenerator) updateReportChangeRequest(old interface{}, cur interfa
|
|||
return
|
||||
}
|
||||
|
||||
key := generateCacheKey(cur)
|
||||
key := g.generateCacheKey(cur)
|
||||
g.queue.Add(key)
|
||||
}
|
||||
|
||||
|
@ -197,7 +210,7 @@ func (g *ReportGenerator) addClusterReportChangeRequest(obj interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
key := generateCacheKey(obj)
|
||||
key := g.generateCacheKey(obj)
|
||||
g.queue.Add(key)
|
||||
}
|
||||
|
||||
|
@ -213,7 +226,8 @@ func (g *ReportGenerator) updateClusterReportChangeRequest(old interface{}, cur
|
|||
return
|
||||
}
|
||||
|
||||
g.queue.Add("")
|
||||
key := g.generateCacheKey(cur)
|
||||
g.queue.Add(key)
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) deletePolicyReport(obj interface{}) {
|
||||
|
@ -321,22 +335,43 @@ func (g *ReportGenerator) handleErr(err error, key interface{}, aggregatedReques
|
|||
}
|
||||
|
||||
// syncHandler reconciles clusterPolicyReport if namespace == ""
|
||||
// otherwise it updates policyReport
|
||||
// otherwise it updates policyReport. the key is of type "namespace/policyname"
|
||||
func (g *ReportGenerator) syncHandler(key string) (aggregatedRequests interface{}, err error) {
|
||||
g.log.V(4).Info("syncing policy report", "key", key)
|
||||
namespace := key
|
||||
|
||||
if policy, rule, ok := isDeletedPolicyKey(key); ok {
|
||||
g.log.V(4).Info("sync policy report on policy deletion")
|
||||
return g.removePolicyEntryFromReport(policy, rule)
|
||||
}
|
||||
|
||||
new, aggregatedRequests, err := g.aggregateReports(namespace)
|
||||
var namespace, policyName string
|
||||
if g.splitPolicyReport {
|
||||
namespace = strings.Split(key, "/")[0]
|
||||
policyName = strings.Split(key, "/")[1]
|
||||
} else {
|
||||
namespace = key
|
||||
}
|
||||
new, aggregatedRequests, err := g.aggregateReports(namespace, policyName)
|
||||
if err != nil {
|
||||
return aggregatedRequests, fmt.Errorf("failed to aggregate reportChangeRequest results %v", err)
|
||||
}
|
||||
|
||||
report, err := g.reportLister.PolicyReports(namespace).Get(GeneratePolicyReportName(namespace))
|
||||
if g.splitPolicyReport {
|
||||
deleteResources := getDeletedResources(aggregatedRequests)
|
||||
if len(deleteResources) != 0 {
|
||||
for _, dr := range deleteResources {
|
||||
if err := g.updateReportsForDeletedResource(dr.name, new, aggregatedRequests); err != nil {
|
||||
return aggregatedRequests, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var report *policyreportv1alpha2.PolicyReport
|
||||
if g.splitPolicyReport {
|
||||
report, err = g.reportLister.PolicyReports(namespace).Get(TrimmedName(GeneratePolicyReportName(namespace) + "-" + policyName))
|
||||
} else {
|
||||
report, err = g.reportLister.PolicyReports(namespace).Get(GeneratePolicyReportName(namespace))
|
||||
}
|
||||
if err == nil {
|
||||
if val, ok := report.GetLabels()[inactiveLabelKey]; ok && val == inactiveLabelVal {
|
||||
g.log.Info("got resourceExhausted error, please opt-in via \"splitPolicyReport\" to generate report per policy")
|
||||
|
@ -344,8 +379,9 @@ func (g *ReportGenerator) syncHandler(key string) (aggregatedRequests interface{
|
|||
}
|
||||
}
|
||||
|
||||
// Delete changes request does not have the policyName label set
|
||||
var old interface{}
|
||||
if old, err = g.createReportIfNotPresent(namespace, new, aggregatedRequests); err != nil {
|
||||
if old, err = g.createReportIfNotPresent(namespace, policyName, new, aggregatedRequests); err != nil {
|
||||
return aggregatedRequests, err
|
||||
}
|
||||
|
||||
|
@ -365,7 +401,7 @@ func (g *ReportGenerator) syncHandler(key string) (aggregatedRequests interface{
|
|||
|
||||
// createReportIfNotPresent creates cluster / policyReport if not present
|
||||
// return the existing report if exist
|
||||
func (g *ReportGenerator) createReportIfNotPresent(namespace string, new *unstructured.Unstructured, aggregatedRequests interface{}) (report interface{}, err error) {
|
||||
func (g *ReportGenerator) createReportIfNotPresent(namespace, policyName string, new *unstructured.Unstructured, aggregatedRequests interface{}) (report interface{}, err error) {
|
||||
log := g.log.WithName("createReportIfNotPresent")
|
||||
obj, hasDuplicate, err := updateResults(new.UnstructuredContent(), new.UnstructuredContent(), nil)
|
||||
if hasDuplicate && err != nil {
|
||||
|
@ -387,7 +423,11 @@ func (g *ReportGenerator) createReportIfNotPresent(namespace string, new *unstru
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
report, err = g.reportLister.PolicyReports(namespace).Get(GeneratePolicyReportName(namespace))
|
||||
if g.splitPolicyReport {
|
||||
report, err = g.reportLister.PolicyReports(namespace).Get(TrimmedName(GeneratePolicyReportName(namespace) + "-" + policyName))
|
||||
} else {
|
||||
report, err = g.reportLister.PolicyReports(namespace).Get(GeneratePolicyReportName(namespace))
|
||||
}
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) && new != nil {
|
||||
polr, err := convertToPolr(new)
|
||||
|
@ -407,7 +447,12 @@ func (g *ReportGenerator) createReportIfNotPresent(namespace string, new *unstru
|
|||
return nil, fmt.Errorf("unable to get policyReport: %v", err)
|
||||
}
|
||||
} else {
|
||||
report, err = g.clusterReportLister.Get(GeneratePolicyReportName(namespace))
|
||||
|
||||
if g.splitPolicyReport {
|
||||
report, err = g.clusterReportLister.Get(TrimmedName(GeneratePolicyReportName(namespace) + "-" + policyName))
|
||||
} else {
|
||||
report, err = g.clusterReportLister.Get(GeneratePolicyReportName(namespace))
|
||||
}
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if new != nil {
|
||||
|
@ -485,6 +530,7 @@ func (g *ReportGenerator) removeFromClusterPolicyReport(policyName, ruleName str
|
|||
}
|
||||
|
||||
func (g *ReportGenerator) removeFromPolicyReport(policyName, ruleName string) error {
|
||||
|
||||
namespaces, err := g.client.ListResource("", "Namespace", "", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list namespace %v", err)
|
||||
|
@ -520,6 +566,7 @@ func (g *ReportGenerator) removeFromPolicyReport(policyName, ruleName string) er
|
|||
gv := policyreportv1alpha2.SchemeGroupVersion
|
||||
gvk := schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: "PolicyReport"}
|
||||
r.SetGroupVersionKind(gvk)
|
||||
|
||||
if _, err := g.pclient.Wgpolicyk8sV1alpha2().PolicyReports(r.GetNamespace()).Update(context.TODO(), r, metav1.UpdateOptions{}); err != nil {
|
||||
return fmt.Errorf("failed to update PolicyReport %s %v", r.GetName(), err)
|
||||
}
|
||||
|
@ -528,7 +575,7 @@ func (g *ReportGenerator) removeFromPolicyReport(policyName, ruleName string) er
|
|||
}
|
||||
|
||||
// aggregateReports aggregates cluster / report change requests to a policy report
|
||||
func (g *ReportGenerator) aggregateReports(namespace string) (
|
||||
func (g *ReportGenerator) aggregateReports(namespace, policyName string) (
|
||||
report *unstructured.Unstructured,
|
||||
aggregatedRequests interface{},
|
||||
err error,
|
||||
|
@ -538,14 +585,19 @@ func (g *ReportGenerator) aggregateReports(namespace string) (
|
|||
g.log.Error(err, "failed to get Kyverno namespace, policy reports will not be garbage collected upon termination")
|
||||
}
|
||||
|
||||
selector := labels.NewSelector()
|
||||
if namespace == "" {
|
||||
selector := labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion}))
|
||||
if g.splitPolicyReport {
|
||||
selector = labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion, policyLabel: TrimmedName(policyName)}))
|
||||
} else {
|
||||
selector = labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion}))
|
||||
}
|
||||
requests, err := g.clusterReportChangeRequestLister.List(selector)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to list ClusterReportChangeRequests within: %v", err)
|
||||
}
|
||||
|
||||
if report, aggregatedRequests, err = mergeRequests(nil, kyvernoNamespace, requests); err != nil {
|
||||
if report, aggregatedRequests, err = g.mergeRequests(nil, kyvernoNamespace, policyName, requests); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to merge ClusterReportChangeRequests results: %v", err)
|
||||
}
|
||||
} else {
|
||||
|
@ -561,13 +613,17 @@ func (g *ReportGenerator) aggregateReports(namespace string) (
|
|||
ns.SetDeletionTimestamp(&now)
|
||||
}
|
||||
|
||||
selector := labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion, ResourceLabelNamespace: namespace}))
|
||||
if g.splitPolicyReport {
|
||||
selector = labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion, ResourceLabelNamespace: namespace, policyLabel: TrimmedName(policyName)}))
|
||||
} else {
|
||||
selector = labels.SelectorFromSet(labels.Set(map[string]string{appVersion: version.BuildVersion, ResourceLabelNamespace: namespace}))
|
||||
}
|
||||
requests, err := g.reportChangeRequestLister.ReportChangeRequests(config.KyvernoNamespace()).List(selector)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to list reportChangeRequests within namespace %s: %v", ns, err)
|
||||
}
|
||||
|
||||
if report, aggregatedRequests, err = mergeRequests(ns, kyvernoNamespace, requests); err != nil {
|
||||
if report, aggregatedRequests, err = g.mergeRequests(ns, kyvernoNamespace, policyName, requests); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to merge results: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -575,9 +631,8 @@ func (g *ReportGenerator) aggregateReports(namespace string) (
|
|||
return report, aggregatedRequests, nil
|
||||
}
|
||||
|
||||
func mergeRequests(ns, kyvernoNs *corev1.Namespace, requestsGeneral interface{}) (*unstructured.Unstructured, interface{}, error) {
|
||||
func (g *ReportGenerator) mergeRequests(ns, kyvernoNs *corev1.Namespace, policyName string, requestsGeneral interface{}) (*unstructured.Unstructured, interface{}, error) {
|
||||
results := []policyreportv1alpha2.PolicyReportResult{}
|
||||
|
||||
if requests, ok := requestsGeneral.([]*kyvernov1alpha2.ClusterReportChangeRequest); ok {
|
||||
aggregatedRequests := []*kyvernov1alpha2.ClusterReportChangeRequest{}
|
||||
for _, request := range requests {
|
||||
|
@ -601,7 +656,9 @@ func mergeRequests(ns, kyvernoNs *corev1.Namespace, requestsGeneral interface{})
|
|||
}
|
||||
|
||||
req := &unstructured.Unstructured{Object: obj}
|
||||
setReport(req, nil, kyvernoNs)
|
||||
|
||||
g.setReport(req, ns, kyvernoNs, policyName)
|
||||
|
||||
return req, aggregatedRequests, nil
|
||||
}
|
||||
|
||||
|
@ -628,7 +685,8 @@ func mergeRequests(ns, kyvernoNs *corev1.Namespace, requestsGeneral interface{})
|
|||
}
|
||||
|
||||
req := &unstructured.Unstructured{Object: obj}
|
||||
setReport(req, ns, kyvernoNs)
|
||||
|
||||
g.setReport(req, ns, kyvernoNs, policyName)
|
||||
|
||||
return req, aggregatedRequests, nil
|
||||
}
|
||||
|
@ -636,7 +694,7 @@ func mergeRequests(ns, kyvernoNs *corev1.Namespace, requestsGeneral interface{})
|
|||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func setReport(reportUnstructured *unstructured.Unstructured, ns, kyvernoNs *corev1.Namespace) {
|
||||
func (g *ReportGenerator) setReport(reportUnstructured *unstructured.Unstructured, ns, kyvernoNs *corev1.Namespace, policyname string) {
|
||||
reportUnstructured.SetAPIVersion(policyreportv1alpha2.SchemeGroupVersion.String())
|
||||
reportUnstructured.SetLabels(LabelSelector.MatchLabels)
|
||||
|
||||
|
@ -655,12 +713,20 @@ func setReport(reportUnstructured *unstructured.Unstructured, ns, kyvernoNs *cor
|
|||
}
|
||||
|
||||
if ns == nil {
|
||||
reportUnstructured.SetName(GeneratePolicyReportName(""))
|
||||
if g.splitPolicyReport {
|
||||
reportUnstructured.SetName(TrimmedName(GeneratePolicyReportName("") + "-" + policyname))
|
||||
} else {
|
||||
reportUnstructured.SetName(GeneratePolicyReportName(""))
|
||||
}
|
||||
reportUnstructured.SetKind("ClusterPolicyReport")
|
||||
return
|
||||
}
|
||||
|
||||
reportUnstructured.SetName(GeneratePolicyReportName(ns.GetName()))
|
||||
if g.splitPolicyReport {
|
||||
reportUnstructured.SetName(TrimmedName(GeneratePolicyReportName(ns.GetName()) + "-" + policyname))
|
||||
} else {
|
||||
reportUnstructured.SetName(GeneratePolicyReportName(ns.GetName()))
|
||||
}
|
||||
reportUnstructured.SetNamespace(ns.GetName())
|
||||
reportUnstructured.SetKind("PolicyReport")
|
||||
}
|
||||
|
@ -779,6 +845,62 @@ func (g *ReportGenerator) updateReport(old interface{}, new *unstructured.Unstru
|
|||
return
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) updateReportsForDeletedResource(resName string, new *unstructured.Unstructured, aggregatedRequests interface{}) (err error) {
|
||||
if _, ok := aggregatedRequests.([]*kyvernov1alpha2.ClusterReportChangeRequest); ok {
|
||||
cpolrs, err := g.clusterReportLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list clusterPolicyReport %v", err)
|
||||
}
|
||||
for _, cpolr := range cpolrs {
|
||||
newRes := []policyreportv1alpha2.PolicyReportResult{}
|
||||
for _, result := range cpolr.Results {
|
||||
if len(result.Resources) != 0 {
|
||||
for _, res := range result.Resources {
|
||||
if res.Name != resName {
|
||||
newRes = append(newRes, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cpolr.Results = newRes
|
||||
cpolr.Summary = calculateSummary(newRes)
|
||||
gv := policyreportv1alpha2.SchemeGroupVersion
|
||||
cpolr.SetGroupVersionKind(schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: "ClusterPolicyReport"})
|
||||
if _, err := g.pclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Update(context.TODO(), cpolr, metav1.UpdateOptions{}); err != nil {
|
||||
return fmt.Errorf("failed to update clusterPolicyReport %s %v", cpolr.Name, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
polrs, err := g.reportLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list clusterPolicyReport %v", err)
|
||||
}
|
||||
for _, polr := range polrs {
|
||||
newRes1 := []policyreportv1alpha2.PolicyReportResult{}
|
||||
for _, result := range polr.Results {
|
||||
if len(result.Resources) != 0 {
|
||||
for _, res := range result.Resources {
|
||||
if res.Name != resName {
|
||||
newRes1 = append(newRes1, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
polr.Results = newRes1
|
||||
polr.Summary = calculateSummary(newRes1)
|
||||
gv := policyreportv1alpha2.SchemeGroupVersion
|
||||
|
||||
polr.SetGroupVersionKind(schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: "PolicyReport"})
|
||||
|
||||
if _, err := g.pclient.Wgpolicyk8sV1alpha2().PolicyReports(new.GetNamespace()).Update(context.TODO(), polr, metav1.UpdateOptions{}); err != nil {
|
||||
return fmt.Errorf("failed to update clusterPolicyReport %s %v", polr.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) cleanupReportRequests(requestsGeneral interface{}) {
|
||||
defer g.log.V(5).Info("successfully cleaned up report requests")
|
||||
if requests, ok := requestsGeneral.([]*kyvernov1alpha2.ReportChangeRequest); ok {
|
||||
|
|
|
@ -70,6 +70,7 @@ func NewReportChangeRequestGenerator(client kyvernoclient.Interface,
|
|||
cpolInformer kyvernov1informers.ClusterPolicyInformer,
|
||||
polInformer kyvernov1informers.PolicyInformer,
|
||||
changeRequestLimit int,
|
||||
splitPolicyReport bool,
|
||||
log logr.Logger,
|
||||
) *Generator {
|
||||
gen := Generator{
|
||||
|
@ -81,9 +82,9 @@ func NewReportChangeRequestGenerator(client kyvernoclient.Interface,
|
|||
polLister: polInformer.Lister(),
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName),
|
||||
dataStore: newDataStore(),
|
||||
requestCreator: newChangeRequestCreator(client, 3*time.Second, log.WithName("requestCreator")),
|
||||
changeRequestLimit: changeRequestLimit,
|
||||
CleanupChangeRequest: make(chan ReconcileInfo, 10),
|
||||
requestCreator: newChangeRequestCreator(client, 3*time.Second, splitPolicyReport, log.WithName("requestCreator")),
|
||||
log: log,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue