mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-13 19:28:55 +00:00
- update report summary to optional; - generate clusterPolicyReport; - remove reportRequests after merged to report
This commit is contained in:
parent
0c46b5700b
commit
63a8d89c8d
15 changed files with 308 additions and 162 deletions
|
@ -592,12 +592,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1002,12 +996,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1665,12 +1653,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -2075,12 +2057,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -321,12 +321,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -318,12 +318,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -320,12 +320,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -318,12 +318,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -597,12 +597,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1007,12 +1001,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1670,12 +1658,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -2080,12 +2062,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
|
@ -597,12 +597,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1007,12 +1001,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -1670,12 +1658,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
@ -2080,12 +2062,6 @@ spec:
|
|||
description: Warn provides the count of unscored policies whose requirements
|
||||
were not met
|
||||
type: integer
|
||||
required:
|
||||
- Error
|
||||
- Fail
|
||||
- Pass
|
||||
- Skip
|
||||
- Warn
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
|
|
1
go.mod
1
go.mod
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/aws/aws-sdk-go v1.28.9 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b // indirect
|
||||
github.com/cornelk/hashmap v1.0.1
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -128,6 +128,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
|||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cornelk/hashmap v1.0.1 h1:RXGcy29hEdLLV8T6aK4s+BAd4tq4+3Hq50N2GoG0uIg=
|
||||
github.com/cornelk/hashmap v1.0.1/go.mod h1:8wbysTUDnwJGrPZ1Iwsou3m+An6sldFrJItjRhfegCw=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
|
@ -135,6 +137,8 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/siphash v1.1.0 h1:1Rs9eTUlZLPBEvV+2sTaM8O0NWn0ppbgqS7p11aWawI=
|
||||
github.com/dchest/siphash v1.1.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
|
|
|
@ -22,26 +22,34 @@ import (
|
|||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
const (
|
||||
StatusPass = "Pass"
|
||||
StatusFail = "Fail"
|
||||
StatusPass = "Pass"
|
||||
StatusFail = "Fail"
|
||||
StatusWarn = "Warn"
|
||||
StatusError = "Error"
|
||||
StatusSkip = "Skip"
|
||||
)
|
||||
|
||||
// PolicyReportSummary provides a status count summary
|
||||
type PolicyReportSummary struct {
|
||||
|
||||
// Pass provides the count of policies whose requirements were met
|
||||
// +optional
|
||||
Pass int `json:"Pass"`
|
||||
|
||||
// Fail provides the count of policies whose requirements were not met
|
||||
// +optional
|
||||
Fail int `json:"Fail"`
|
||||
|
||||
// Warn provides the count of unscored policies whose requirements were not met
|
||||
// +optional
|
||||
Warn int `json:"Warn"`
|
||||
|
||||
// Error provides the count of policies that could not be evaluated
|
||||
// +optional
|
||||
Error int `json:"Error"`
|
||||
|
||||
// Skip indicates the count of policies that were not selected for evaluation
|
||||
// +optional
|
||||
Skip int `json:"Skip"`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
@ -37,6 +53,7 @@ func (in *ClusterPolicyReport) DeepCopyInto(out *ClusterPolicyReport) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPolicyReport.
|
||||
|
@ -69,6 +86,7 @@ func (in *ClusterPolicyReportList) DeepCopyInto(out *ClusterPolicyReportList) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPolicyReportList.
|
||||
|
@ -116,6 +134,7 @@ func (in *ClusterReportRequest) DeepCopyInto(out *ClusterReportRequest) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterReportRequest.
|
||||
|
@ -148,6 +167,7 @@ func (in *ClusterReportRequestList) DeepCopyInto(out *ClusterReportRequestList)
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterReportRequestList.
|
||||
|
@ -195,6 +215,7 @@ func (in *PolicyReport) DeepCopyInto(out *PolicyReport) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyReport.
|
||||
|
@ -227,6 +248,7 @@ func (in *PolicyReportList) DeepCopyInto(out *PolicyReportList) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyReportList.
|
||||
|
@ -273,6 +295,7 @@ func (in *PolicyReportResult) DeepCopyInto(out *PolicyReportResult) {
|
|||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyReportResult.
|
||||
|
@ -288,6 +311,7 @@ func (in *PolicyReportResult) DeepCopy() *PolicyReportResult {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PolicyReportSummary) DeepCopyInto(out *PolicyReportSummary) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyReportSummary.
|
||||
|
@ -327,6 +351,7 @@ func (in *ReportRequest) DeepCopyInto(out *ReportRequest) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReportRequest.
|
||||
|
@ -359,6 +384,7 @@ func (in *ReportRequestList) DeepCopyInto(out *ReportRequestList) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReportRequestList.
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha1"
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -61,6 +62,10 @@ func NewBuilder() *requestBuilder {
|
|||
func (pvb *requestBuilder) build(info Info) (*unstructured.Unstructured, error) {
|
||||
results := []*report.PolicyReportResult{}
|
||||
for _, rule := range info.Rules {
|
||||
if rule.Type != utils.Validation.String() {
|
||||
continue
|
||||
}
|
||||
|
||||
result := &report.PolicyReportResult{
|
||||
Policy: info.PolicyName,
|
||||
Resources: []*v1.ObjectReference{
|
||||
|
|
110
pkg/policyreport/policyreport.go
Normal file
110
pkg/policyreport/policyreport.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package policyreport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/cornelk/hashmap"
|
||||
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func updateResults(oldReport, newReport map[string]interface{}) (map[string]interface{}, error) {
|
||||
oldResults := hashResults(oldReport)
|
||||
newresults := newReport["results"].([]interface{})
|
||||
|
||||
for _, res := range newresults {
|
||||
resMap := res.(map[string]interface{})
|
||||
if key, ok := generateHashKey(resMap); ok {
|
||||
oldResults.Set(key, res)
|
||||
}
|
||||
}
|
||||
|
||||
results := getResultsFromHash(oldResults)
|
||||
if err := unstructured.SetNestedSlice(newReport, results, "results"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
summary := updateSummary(results)
|
||||
if err := unstructured.SetNestedMap(newReport, summary, "summary"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReport, nil
|
||||
}
|
||||
|
||||
func hashResults(policyReport map[string]interface{}) *hashmap.HashMap {
|
||||
resultsHash := &hashmap.HashMap{}
|
||||
|
||||
results, ok := policyReport["results"]
|
||||
if !ok {
|
||||
return resultsHash
|
||||
}
|
||||
|
||||
for _, result := range results.([]interface{}) {
|
||||
if key, ok := generateHashKey(result.(map[string]interface{})); ok {
|
||||
resultsHash.Set(key, result)
|
||||
}
|
||||
}
|
||||
return resultsHash
|
||||
}
|
||||
|
||||
func getResultsFromHash(resHash *hashmap.HashMap) []interface{} {
|
||||
results := make([]interface{}, 0)
|
||||
|
||||
for result := range resHash.Iter() {
|
||||
if reflect.DeepEqual(result, hashmap.KeyValue{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, result.Value.(map[string]interface{}))
|
||||
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func generateHashKey(result map[string]interface{}) (string, bool) {
|
||||
resources := result["resources"].([]interface{})
|
||||
if len(resources) < 1 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
resource := resources[0].(map[string]interface{})
|
||||
return fmt.Sprintf(
|
||||
"%s-%s-%s-%s-%s",
|
||||
result["policy"],
|
||||
result["rule"],
|
||||
resource["name"],
|
||||
resource["namespace"],
|
||||
resource["name"]), true
|
||||
}
|
||||
|
||||
func updateSummary(results []interface{}) map[string]interface{} {
|
||||
summary := make(map[string]interface{}, 5)
|
||||
|
||||
for _, result := range results {
|
||||
typedResult, ok := result.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch typedResult["status"].(string) {
|
||||
case report.StatusPass:
|
||||
pass, _ := summary["Pass"].(int64)
|
||||
summary["Pass"] = pass + 1
|
||||
case report.StatusFail:
|
||||
fail, _ := summary["Fail"].(int64)
|
||||
summary["Fail"] = fail + 1
|
||||
case "Warn":
|
||||
warn, _ := summary["Warn"].(int64)
|
||||
summary["warn"] = warn + 1
|
||||
case "Error":
|
||||
e, _ := summary["Error"].(int64)
|
||||
summary["Error"] = e + 1
|
||||
case "Skip":
|
||||
skip, _ := summary["Skip"].(int64)
|
||||
summary["Skip"] = skip + 1
|
||||
}
|
||||
}
|
||||
|
||||
return summary
|
||||
}
|
|
@ -75,14 +75,12 @@ func NewReportGenerator(
|
|||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: gen.addReportRequest,
|
||||
UpdateFunc: gen.updateReportRequest,
|
||||
DeleteFunc: gen.deleteReportRequest,
|
||||
})
|
||||
|
||||
clusterReportReqInformer.Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: gen.addClusterReportRequest,
|
||||
UpdateFunc: gen.updateClusterReportRequest,
|
||||
DeleteFunc: gen.deleteClusterReportRequest,
|
||||
})
|
||||
|
||||
gen.clusterReportLister = clusterReportInformer.Lister()
|
||||
|
@ -123,19 +121,21 @@ func (g *ReportGenerator) updateReportRequest(old interface{}, cur interface{})
|
|||
g.queue.Add(ns)
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) deleteReportRequest(obj interface{}) {
|
||||
r := obj.(*report.ReportRequest)
|
||||
ns := r.GetNamespace()
|
||||
if ns == "" {
|
||||
ns = "default"
|
||||
}
|
||||
|
||||
g.queue.Add(ns)
|
||||
func (g *ReportGenerator) addClusterReportRequest(obj interface{}) {
|
||||
_ = obj.(*report.ClusterReportRequest)
|
||||
g.queue.Add("")
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) addClusterReportRequest(obj interface{}) {}
|
||||
func (g *ReportGenerator) updateClusterReportRequest(old interface{}, cur interface{}) {}
|
||||
func (g *ReportGenerator) deleteClusterReportRequest(obj interface{}) {}
|
||||
func (g *ReportGenerator) updateClusterReportRequest(old interface{}, cur interface{}) {
|
||||
oldReq := old.(*report.ClusterReportRequest)
|
||||
curReq := cur.(*report.ClusterReportRequest)
|
||||
|
||||
if reflect.DeepEqual(oldReq.Results, curReq.Results) {
|
||||
return
|
||||
}
|
||||
|
||||
g.queue.Add("")
|
||||
}
|
||||
|
||||
// Run starts the workers
|
||||
func (g *ReportGenerator) Run(workers int, stopCh <-chan struct{}) {
|
||||
|
@ -201,66 +201,124 @@ func (g *ReportGenerator) handleErr(err error, key interface{}) {
|
|||
logger.Error(err, "dropping key out of the queue", "key", key)
|
||||
}
|
||||
|
||||
// syncHandler reconciles clusterPolicyReport if namespace == ""
|
||||
// otherwise it updates policyrReport
|
||||
func (g *ReportGenerator) syncHandler(namespace string) error {
|
||||
log := g.log.WithName("sync")
|
||||
|
||||
// cluster policy report
|
||||
if namespace == clusterpolicyreport {
|
||||
return nil
|
||||
}
|
||||
|
||||
// policy report
|
||||
ns, err := g.nsLister.Get(namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get namespace %s: %v", ns.GetName(), err)
|
||||
}
|
||||
|
||||
new, err := g.aggregateReports(ns)
|
||||
new, aggregatedRequests, err := g.aggregateReports(namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to aggregate reportRequest results %v", err)
|
||||
}
|
||||
|
||||
old, err := g.reportLister.PolicyReports(namespace).Get(generatePolicyReportName((namespace)))
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) && new != nil {
|
||||
if _, err := g.dclient.CreateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false); err != nil {
|
||||
return fmt.Errorf("failed to create policyReport: %v", err)
|
||||
var old interface{}
|
||||
if namespace != "" {
|
||||
old, err = g.reportLister.PolicyReports(namespace).Get(generatePolicyReportName((namespace)))
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) && new != nil {
|
||||
if _, err := g.dclient.CreateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false); err != nil {
|
||||
return fmt.Errorf("failed to create policyReport: %v", err)
|
||||
}
|
||||
|
||||
log.V(2).Info("successfully created policyReport", "namespace", new.GetNamespace(), "name", new.GetName())
|
||||
g.cleanupReportRequets(aggregatedRequests)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.V(1).Info("successfully created policyReport", "namespace", new.GetNamespace(), "name", new.GetName())
|
||||
return nil
|
||||
return fmt.Errorf("unable to get policyReport: %v", err)
|
||||
}
|
||||
} else {
|
||||
old, err = g.clusterReportLister.Get(generatePolicyReportName((namespace)))
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) && new != nil {
|
||||
if _, err := g.dclient.CreateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false); err != nil {
|
||||
return fmt.Errorf("failed to create ClusterPolicyReport: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to get policyReport: %v", err)
|
||||
log.V(2).Info("successfully created ClusterPolicyReport")
|
||||
g.cleanupReportRequets(aggregatedRequests)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to get ClusterPolicyReport: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return g.updateReport(old, new)
|
||||
if err := g.updateReport(old, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.cleanupReportRequets(aggregatedRequests)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) aggregateReports(ns *v1.Namespace) (*unstructured.Unstructured, error) {
|
||||
report := &unstructured.Unstructured{}
|
||||
defer report.SetCreationTimestamp(metav1.Now())
|
||||
if ns == nil {
|
||||
func (g *ReportGenerator) aggregateReports(namespace string) (
|
||||
report *unstructured.Unstructured, aggregatedRequests interface{}, err error) {
|
||||
|
||||
if namespace == "" {
|
||||
requests, err := g.clusterReportLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to list ClusterReportRequests within: %v", err)
|
||||
}
|
||||
|
||||
if report, aggregatedRequests, err = mergeRequests(nil, requests); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to merge ClusterReportRequests results: %v", err)
|
||||
}
|
||||
} else {
|
||||
ns, err := g.nsLister.Get(namespace)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get namespace %s: %v", ns.GetName(), err)
|
||||
}
|
||||
|
||||
requests, err := g.reportRequestLister.ReportRequests(ns.GetName()).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to list reportRequests within namespace %s: %v", ns, err)
|
||||
return nil, nil, fmt.Errorf("unable to list reportRequests within namespace %s: %v", ns, err)
|
||||
}
|
||||
|
||||
if report, err = mergeRequests(ns, requests); err != nil {
|
||||
return nil, fmt.Errorf("unable to merge results: %v", err)
|
||||
if report, aggregatedRequests, err = mergeRequests(ns, requests); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to merge results: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return report, nil
|
||||
return report, aggregatedRequests, nil
|
||||
}
|
||||
|
||||
func mergeRequests(ns *v1.Namespace, requests []*report.ReportRequest) (*unstructured.Unstructured, error) {
|
||||
func mergeRequests(ns *v1.Namespace, requestsGeneral interface{}) (*unstructured.Unstructured, interface{}, error) {
|
||||
results := []*report.PolicyReportResult{}
|
||||
if len(requests) > 0 {
|
||||
|
||||
if requests, ok := requestsGeneral.([]*report.ClusterReportRequest); ok {
|
||||
aggregatedRequests := []*report.ClusterReportRequest{}
|
||||
for _, request := range requests {
|
||||
if request.GetDeletionTimestamp() != nil {
|
||||
continue
|
||||
}
|
||||
results = append(results, request.Results...)
|
||||
aggregatedRequests = append(aggregatedRequests, request)
|
||||
}
|
||||
|
||||
report := &report.ClusterPolicyReport{
|
||||
Results: results,
|
||||
Summary: calculateSummary(results),
|
||||
}
|
||||
|
||||
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(report)
|
||||
if err != nil {
|
||||
return nil, aggregatedRequests, err
|
||||
}
|
||||
|
||||
req := &unstructured.Unstructured{Object: obj}
|
||||
setReport(req, nil)
|
||||
return req, aggregatedRequests, nil
|
||||
}
|
||||
|
||||
if requests, ok := requestsGeneral.([]*report.ReportRequest); ok {
|
||||
aggregatedRequests := []*report.ReportRequest{}
|
||||
for _, request := range requests {
|
||||
if request.GetDeletionTimestamp() != nil {
|
||||
continue
|
||||
}
|
||||
results = append(results, request.Results...)
|
||||
aggregatedRequests = append(aggregatedRequests, request)
|
||||
}
|
||||
|
||||
report := &report.PolicyReport{
|
||||
|
@ -270,15 +328,15 @@ func mergeRequests(ns *v1.Namespace, requests []*report.ReportRequest) (*unstruc
|
|||
|
||||
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(report)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, aggregatedRequests, err
|
||||
}
|
||||
|
||||
req := &unstructured.Unstructured{Object: obj}
|
||||
setReport(req, ns)
|
||||
return req, nil
|
||||
return req, aggregatedRequests, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func setReport(report *unstructured.Unstructured, ns *v1.Namespace) {
|
||||
|
@ -310,9 +368,25 @@ func setReport(report *unstructured.Unstructured, ns *v1.Namespace) {
|
|||
}
|
||||
|
||||
func (g *ReportGenerator) updateReport(old interface{}, new *unstructured.Unstructured) (err error) {
|
||||
if new == nil {
|
||||
g.log.V(4).Info("empty report to update")
|
||||
return nil
|
||||
}
|
||||
|
||||
oldUnstructed := make(map[string]interface{})
|
||||
if oldTyped, ok := old.(*report.PolicyReport); ok {
|
||||
if oldTyped.GetDeletionTimestamp() != nil || new == nil { // no report request in ns
|
||||
|
||||
if oldTyped, ok := old.(*report.ClusterPolicyReport); ok {
|
||||
if oldTyped.GetDeletionTimestamp() != nil {
|
||||
return g.dclient.DeleteResource(oldTyped.APIVersion, "ClusterPolicyReport", oldTyped.Namespace, oldTyped.Name, false)
|
||||
}
|
||||
|
||||
if oldUnstructed, err = runtime.DefaultUnstructuredConverter.ToUnstructured(oldTyped); err != nil {
|
||||
return fmt.Errorf("unable to convert clusterPolicyReport: %v", err)
|
||||
}
|
||||
new.SetUID(oldTyped.GetUID())
|
||||
new.SetResourceVersion(oldTyped.GetResourceVersion())
|
||||
} else if oldTyped, ok := old.(*report.PolicyReport); ok {
|
||||
if oldTyped.GetDeletionTimestamp() != nil {
|
||||
return g.dclient.DeleteResource(oldTyped.APIVersion, "PolicyReport", oldTyped.Namespace, oldTyped.Name, false)
|
||||
}
|
||||
|
||||
|
@ -320,21 +394,16 @@ func (g *ReportGenerator) updateReport(old interface{}, new *unstructured.Unstru
|
|||
return fmt.Errorf("unable to convert policyReport: %v", err)
|
||||
}
|
||||
|
||||
new.SetUID(oldTyped.GetUID())
|
||||
new.SetResourceVersion(oldTyped.GetResourceVersion())
|
||||
} else {
|
||||
oldTyped := old.(*report.ClusterReportRequest)
|
||||
if oldTyped.GetDeletionTimestamp() != nil || new == nil {
|
||||
return g.dclient.DeleteResource(oldTyped.APIVersion, "ClusterPolicyReport", oldTyped.Namespace, oldTyped.Name, false)
|
||||
}
|
||||
|
||||
if oldUnstructed, err = runtime.DefaultUnstructuredConverter.ToUnstructured(oldTyped); err != nil {
|
||||
return fmt.Errorf("unable to convert clusterPolicyReport: %v", err)
|
||||
}
|
||||
new.SetUID(oldTyped.GetUID())
|
||||
new.SetResourceVersion(oldTyped.GetResourceVersion())
|
||||
}
|
||||
|
||||
obj, err := updateResults(oldUnstructed, new.UnstructuredContent())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update results entry: %v", err)
|
||||
}
|
||||
new.Object = obj
|
||||
|
||||
if !hasResultsChanged(oldUnstructed, new.UnstructuredContent()) {
|
||||
g.log.V(4).Info("unchanged policy report", "namespace", new.GetNamespace(), "name", new.GetName())
|
||||
return nil
|
||||
|
@ -344,6 +413,25 @@ func (g *ReportGenerator) updateReport(old interface{}, new *unstructured.Unstru
|
|||
return fmt.Errorf("failed to update policy report: %v", err)
|
||||
}
|
||||
|
||||
g.log.V(1).Info("successfully updated policy report", "kind", new.GetKind(), "namespace", new.GetNamespace(), "name", new.GetName())
|
||||
g.log.V(3).Info("successfully updated policy report", "kind", new.GetKind(), "namespace", new.GetNamespace(), "name", new.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
func (g *ReportGenerator) cleanupReportRequets(requestsGeneral interface{}) {
|
||||
defer g.log.V(2).Info("successfully cleaned up report requests ")
|
||||
if requests, ok := requestsGeneral.([]*report.ReportRequest); ok {
|
||||
for _, request := range requests {
|
||||
if err := g.dclient.DeleteResource(request.APIVersion, "ReportRequest", request.Namespace, request.Name, false); err != nil {
|
||||
g.log.Error(err, "failed to delete report request")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if requests, ok := requestsGeneral.([]*report.ClusterReportRequest); ok {
|
||||
for _, request := range requests {
|
||||
if err := g.dclient.DeleteResource(request.APIVersion, "ClusterReportRequest", request.Namespace, request.Name, false); err != nil {
|
||||
g.log.Error(err, "failed to delete clusterReportRequest")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ 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 os.Getenv("POLICY-TYPE") == common.PolicyViolation {
|
||||
if rule.Success {
|
||||
continue
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue