1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00

Configurable success events on policies & resources. Generating failure events on policies by default. (#1939)

* Remove unused event.Reason const

Signed-off-by: Velkov <valentin.velkov@sap.com>

* Generate failure events on policies

Signed-off-by: Velkov <valentin.velkov@sap.com>

* Generate success events on policy

Signed-off-by: Velkov <valentin.velkov@sap.com>

* Introduce 'generateSuccessEvents' flag

Signed-off-by: Velkov <valentin.velkov@sap.com>

* Unit tests & chart fix

Signed-off-by: Velkov <valentin.velkov@sap.com>
This commit is contained in:
Valentin Velkov 2021-06-30 00:43:11 +03:00 committed by GitHub
parent 436d44050b
commit 63f4c9a884
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 336 additions and 113 deletions

View file

@ -20,4 +20,7 @@ data:
{{- if .Values.config.webhooks }}
webhooks: {{ .Values.config.webhooks | toJson | quote }}
{{- end -}}
{{- if .Values.config.generateSuccessEvents }}
generateSuccessEvents: {{ .Values.config.generateSuccessEvents | quote }}
{{- end -}}
{{- end -}}

View file

@ -146,7 +146,7 @@ config:
# will be forwarded to the webhookconfigurations.
webhooks:
# webhooks: [{"namespaceSelector":{"matchExpressions":[{"key":"environment","operator":"In","values":["prod"]}]}}]
generateSuccessEvents: 'false'
# existingConfig: init-config
service:

View file

@ -179,6 +179,7 @@ func main() {
eventGenerator := event.NewEventGenerator(
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
rCache,
log.Log.WithName("EventGenerator"))

View file

@ -3462,6 +3462,7 @@ subjects:
apiVersion: v1
data:
excludeGroupRole: system:serviceaccounts:kube-system,system:nodes,system:kube-scheduler
generateSuccessEvents: "false"
resourceFilters: '[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][SelfSubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*][ReportChangeRequest,*,*][ClusterReportChangeRequest,*,*][PolicyReport,*,*][ClusterPolicyReport,*,*]'
kind: ConfigMap
metadata:

View file

@ -3311,6 +3311,7 @@ subjects:
apiVersion: v1
data:
excludeGroupRole: system:serviceaccounts:kube-system,system:nodes,system:kube-scheduler
generateSuccessEvents: "false"
resourceFilters: '[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][SelfSubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*][ReportChangeRequest,*,*][ClusterReportChangeRequest,*,*][PolicyReport,*,*][ClusterPolicyReport,*,*]'
kind: ConfigMap
metadata:

View file

@ -2,6 +2,7 @@ apiVersion: v1
data:
resourceFilters: '[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][SelfSubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*][ReportChangeRequest,*,*][ClusterReportChangeRequest,*,*][PolicyReport,*,*][ClusterPolicyReport,*,*]'
excludeGroupRole: 'system:serviceaccounts:kube-system,system:nodes,system:kube-scheduler'
generateSuccessEvents: 'false'
kind: ConfigMap
metadata:
labels:

1
go.mod
View file

@ -25,6 +25,7 @@ require (
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/minio/pkg v1.0.4
github.com/mitchellh/mapstructure v1.3.2 // indirect
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.10.2
github.com/opencontainers/go-digest v1.0.0 // indirect

2
go.sum
View file

@ -816,6 +816,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View file

@ -5,6 +5,7 @@ import (
"os"
"reflect"
"regexp"
"strconv"
"strings"
"sync"
@ -37,6 +38,7 @@ type ConfigData struct {
excludeUsername []string
restrictDevelopmentUsername []string
webhooks []WebhookConfig
generateSuccessEvents bool
cmSycned cache.InformerSynced
reconcilePolicyReport chan<- bool
updateWebhookConfigurations chan<- bool
@ -83,6 +85,13 @@ func (cd *ConfigData) GetExcludeUsername() []string {
return cd.excludeUsername
}
// GetGenerateSuccessEvents return if should generate success events
func (cd *ConfigData) GetGenerateSuccessEvents() bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.generateSuccessEvents
}
// FilterNamespaces filters exclude namespace
func (cd *ConfigData) FilterNamespaces(namespaces []string) []string {
var results []string
@ -110,6 +119,7 @@ type Interface interface {
ToFilter(kind, namespace, name string) bool
GetExcludeGroupRole() []string
GetExcludeUsername() []string
GetGenerateSuccessEvents() bool
RestrictDevelopmentUsername() []string
FilterNamespaces(namespaces []string) []string
GetWebhooks() []WebhookConfig
@ -290,6 +300,23 @@ func (cd *ConfigData) load(cm v1.ConfigMap) (reconcilePolicyReport, updateWebhoo
updateWebhook = true
}
}
generateSuccessEvents, ok := cm.Data["generateSuccessEvents"]
if !ok {
logger.V(4).Info("configuration: No generateSuccessEvents defined in ConfigMap")
} else {
generateSuccessEvents, err := strconv.ParseBool(generateSuccessEvents)
if err != nil {
logger.V(4).Info("configuration: generateSuccessEvents must be either true/false")
} else if generateSuccessEvents == cd.generateSuccessEvents {
logger.V(4).Info("generateSuccessEvents did not change")
} else {
logger.V(2).Info("Updated generateSuccessEvents", "oldGenerateSuccessEvents", cd.generateSuccessEvents, "newGenerateSuccessEvents", generateSuccessEvents)
cd.generateSuccessEvents = generateSuccessEvents
reconcilePolicyReport = true
}
}
return
}
@ -333,6 +360,7 @@ func (cd *ConfigData) unload(cm v1.ConfigMap) {
cd.excludeGroupRole = []string{}
cd.excludeGroupRole = append(cd.excludeGroupRole, defaultExcludeGroupRole...)
cd.excludeUsername = []string{}
cd.generateSuccessEvents = false
}
type k8Resource struct {

View file

@ -25,7 +25,10 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En
apiVersion := policyContext.NewResource.GetAPIVersion()
resp := &response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: policyContext.Policy.Name,
Policy: response.PolicySpec{
Name: policyContext.Policy.GetName(),
Namespace: policyContext.Policy.GetNamespace(),
},
PolicyStats: response.PolicyStats{
PolicyExecutionTimestamp: startTime.Unix(),
},

View file

@ -134,7 +134,8 @@ func startMutateResultResponse(resp *response.EngineResponse, policy kyverno.Clu
return
}
resp.PolicyResponse.Policy = policy.Name
resp.PolicyResponse.Policy.Name = policy.GetName()
resp.PolicyResponse.Policy.Namespace = policy.GetNamespace()
resp.PolicyResponse.Resource.Name = resource.GetName()
resp.PolicyResponse.Resource.Namespace = resource.GetNamespace()
resp.PolicyResponse.Resource.Kind = resource.GetKind()

View file

@ -17,8 +17,8 @@ type EngineResponse struct {
//PolicyResponse policy application response
type PolicyResponse struct {
// policy name
Policy string `json:"policy"`
// policy details
Policy PolicySpec `json:"policy"`
// resource details
Resource ResourceSpec `json:"resource"`
// policy statistics
@ -29,6 +29,12 @@ type PolicyResponse struct {
ValidationFailureAction string
}
//PolicySpec policy
type PolicySpec struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}
//ResourceSpec resource action applied on
type ResourceSpec struct {
Kind string `json:"kind"`
@ -95,6 +101,16 @@ func (er EngineResponse) IsSuccessful() bool {
return true
}
//IsFailed checks if any rule has succeeded or not
func (er EngineResponse) IsFailed() bool {
for _, r := range er.PolicyResponse.Rules {
if r.Success {
return false
}
}
return true
}
//GetPatches returns all the patches joined
func (er EngineResponse) GetPatches() [][]byte {
var patches [][]byte

View file

@ -60,7 +60,8 @@ func buildResponse(logger logr.Logger, ctx *PolicyContext, resp *response.Engine
resp.PatchedResource = resource
}
resp.PolicyResponse.Policy = ctx.Policy.Name
resp.PolicyResponse.Policy.Name = ctx.Policy.GetName()
resp.PolicyResponse.Policy.Namespace = ctx.Policy.GetNamespace()
resp.PolicyResponse.Resource.Name = resp.PatchedResource.GetName()
resp.PolicyResponse.Resource.Namespace = resp.PatchedResource.GetNamespace()
resp.PolicyResponse.Resource.Kind = resp.PatchedResource.GetKind()

View file

@ -11,7 +11,7 @@ func TestPositive(t *testing.T) {
resourceName := "test_resource"
ruleName := "test_rule"
expectedMsg := fmt.Sprintf("Rule(s) '%s' failed to apply on resource %s", ruleName, resourceName)
msg, err := getEventMsg(FPolicyApplyFailed, ruleName, resourceName)
msg, err := getEventMsg(FPolicyApply, ruleName, resourceName)
assert.NilError(t, err)
assert.Equal(t, expectedMsg, msg)
}
@ -19,6 +19,6 @@ func TestPositive(t *testing.T) {
// passing incorrect args
func TestIncorrectArgs(t *testing.T) {
resourceName := "test_resource"
_, err := getEventMsg(FPolicyApplyFailed, resourceName, "extra_args1", "extra_args2")
_, err := getEventMsg(FPolicyApply, resourceName, "extra_args1", "extra_args2")
assert.Error(t, err, "message expects 2 arguments, but 3 arguments passed")
}

View file

@ -26,8 +26,12 @@ import (
type Generator struct {
client *client.Client
// list/get cluster policy
pLister kyvernolister.ClusterPolicyLister
cpLister kyvernolister.ClusterPolicyLister
// returns true if the cluster policy store has been synced at least once
cpSynced cache.InformerSynced
// list/get policy
pLister kyvernolister.PolicyLister
// returns true if the policy store has been synced at least once
pSynced cache.InformerSynced
// queue to store event generation requests
queue workqueue.RateLimitingInterface
@ -47,13 +51,15 @@ type Interface interface {
}
//NewEventGenerator to generate a new event controller
func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, resCache resourcecache.ResourceCache, log logr.Logger) *Generator {
func NewEventGenerator(client *client.Client, cpInformer kyvernoinformer.ClusterPolicyInformer, pInformer kyvernoinformer.PolicyInformer, resCache resourcecache.ResourceCache, log logr.Logger) *Generator {
gen := Generator{
client: client,
cpLister: cpInformer.Lister(),
cpSynced: cpInformer.Informer().HasSynced,
pLister: pInformer.Lister(),
queue: workqueue.NewNamedRateLimitingQueue(rateLimiter(), eventWorkQueueName),
pSynced: pInformer.Informer().HasSynced,
queue: workqueue.NewNamedRateLimitingQueue(rateLimiter(), eventWorkQueueName),
policyCtrRecorder: initRecorder(client, PolicyController, log),
admissionCtrRecorder: initRecorder(client, AdmissionController, log),
genPolicyRecorder: initRecorder(client, GeneratePolicyController, log),
@ -112,7 +118,7 @@ func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
logger.Info("start")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, gen.pSynced) {
if !cache.WaitForCacheSync(stopCh, gen.cpSynced, gen.pSynced) {
logger.Info("failed to sync informer cache")
}
@ -183,7 +189,13 @@ func (gen *Generator) syncHandler(key Info) error {
switch key.Kind {
case "ClusterPolicy":
//TODO: policy is clustered resource so wont need namespace
robj, err = gen.pLister.Get(key.Name)
robj, err = gen.cpLister.Get(key.Name)
if err != nil {
logger.Error(err, "failed to get cluster policy", "name", key.Name)
return err
}
case "Policy":
robj, err = gen.pLister.Policies(key.Namespace).Get(key.Name)
if err != nil {
logger.Error(err, "failed to get policy", "name", key.Name)
return err
@ -200,6 +212,9 @@ func (gen *Generator) syncHandler(key Info) error {
// set the event type based on reason
eventType := v1.EventTypeWarning
if key.Reason == PolicyApplied.String() {
eventType = v1.EventTypeNormal
}
// based on the source of event generation, use different event recorders
switch key.Source {

View file

@ -10,20 +10,16 @@ type MsgKey int
//Message id for pre-defined messages
const (
FPolicyApplyBlockCreate MsgKey = iota
FPolicyApplyBlockUpdate
FPolicyBlockResourceUpdate
FPolicyApplyFailed
FResourcePolicyFailed
FPolicyApply = iota
FResourcePolicyApply
SPolicyApply
)
func (k MsgKey) String() string {
return [...]string{
"Resource %s creation blocked by rule(s) %s",
"Rule(s) '%s' of policy '%s' blocked update of the resource",
"Resource %s update blocked by rule(s) %s",
"Rule(s) '%s' failed to apply on resource %s",
"Rule(s) '%s' of policy '%s' failed to apply on the resource",
"Rule(s) '%s' successfully applied on resource %s",
}[k]
}

View file

@ -6,8 +6,8 @@ type Reason int
const (
//PolicyViolation there is a violation of policy
PolicyViolation Reason = iota
//RequestBlocked the request to create/update the resource was blocked( generated from admission-controller)
RequestBlocked
//PolicyApplied policy applied
PolicyApplied
//PolicyFailed policy failed
PolicyFailed
)
@ -15,7 +15,7 @@ const (
func (r Reason) String() string {
return [...]string{
"PolicyViolation",
"RequestBlocked",
"PolicyApplied",
"PolicyFailed",
}[r]
}

View file

@ -152,7 +152,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
if !r.Success {
logger.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy,
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy.Name,
"generate.kyverno.io/resource-name": engineResponse.PolicyResponse.Resource.Name,
"generate.kyverno.io/resource-kind": engineResponse.PolicyResponse.Resource.Kind,
"generate.kyverno.io/resource-namespace": engineResponse.PolicyResponse.Resource.Namespace,

View file

@ -9,7 +9,6 @@ import (
)
func failedEvents(err error, gr kyverno.GenerateRequest, resource unstructured.Unstructured) []event.Info {
re := event.Info{}
re.Kind = resource.GetKind()
re.Namespace = resource.GetNamespace()

View file

@ -26,7 +26,7 @@ var engineResponses = []*response.EngineResponse{
},
},
PolicyResponse: response.PolicyResponse{
Policy: "policy1",
Policy: response.PolicySpec{Name: "policy1"},
Resource: response.ResourceSpec{Name: "policy1-pod"},
Rules: []response.RuleResponse{
{
@ -52,7 +52,7 @@ var engineResponses = []*response.EngineResponse{
},
},
PolicyResponse: response.PolicyResponse{
Policy: "clusterpolicy2",
Policy: response.PolicySpec{Name: "clusterpolicy2"},
Resource: response.ResourceSpec{Name: "policy2-clusterrole"},
Rules: []response.RuleResponse{
{

View file

@ -225,7 +225,7 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
results := make(map[string]report.PolicyReportResult)
infos := policyreport.GeneratePRsFromEngineResponse(resps, log.Log)
for _, resp := range resps {
policyName := resp.PolicyResponse.Policy
policyName := resp.PolicyResponse.Policy.Name
resourceName := resp.PolicyResponse.Resource.Name
var rules []string
for _, rule := range resp.PolicyResponse.Rules {

View file

@ -95,7 +95,7 @@ func (pc *PolicyController) applyAndReportPerNamespace(policy *kyverno.ClusterPo
*metricAlreadyRegistered = true
}
pc.report(policy.Name, engineResponses, logger)
pc.report(engineResponses, logger)
}
func (pc *PolicyController) registerPolicyRuleResultsMetricValidationBS(logger logr.Logger, policy kyverno.ClusterPolicy, engineResponse response.EngineResponse, backgroundScanTimestamp int64) {

View file

@ -17,10 +17,15 @@ import (
"k8s.io/apimachinery/pkg/labels"
)
func (pc *PolicyController) report(policy string, engineResponses []*response.EngineResponse, logger logr.Logger) {
eventInfos := generateEvents(logger, engineResponses)
func (pc *PolicyController) report(engineResponses []*response.EngineResponse, logger logr.Logger) {
eventInfos := generateFailEvents(logger, engineResponses)
pc.eventGen.Add(eventInfos...)
if pc.configHandler.GetGenerateSuccessEvents() {
successEventInfos := generateSuccessEvents(logger, engineResponses)
pc.eventGen.Add(successEventInfos...)
}
pvInfos := policyreport.GeneratePRsFromEngineResponse(engineResponses, logger)
// as engineResponses holds the results for all matched resources in one namespace
@ -131,22 +136,43 @@ func (pc *PolicyController) requeuePolicies() {
}
}
func generateEvents(log logr.Logger, ers []*response.EngineResponse) []event.Info {
var eventInfos []event.Info
func generateSuccessEvents(log logr.Logger, ers []*response.EngineResponse) (eventInfos []event.Info) {
for _, er := range ers {
if er.IsSuccessful() {
continue
logger := log.WithValues("policy", er.PolicyResponse.Policy, "kind", er.PolicyResponse.Resource.Kind, "namespace", er.PolicyResponse.Resource.Namespace, "name", er.PolicyResponse.Resource.Name)
logger.V(4).Info("reporting success results for policy")
if !er.IsFailed() {
// generate event on policy for success rules
logger.V(4).Info("generating event on policy for success rules")
e := event.Info{}
kind := "ClusterPolicy"
if er.PolicyResponse.Policy.Namespace != "" {
kind = "Policy"
}
e.Kind = kind
e.Namespace = er.PolicyResponse.Policy.Namespace
e.Name = er.PolicyResponse.Policy.Name
e.Reason = event.PolicyApplied.String()
e.Source = event.PolicyController
e.Message = fmt.Sprintf("rules '%v' successfully applied on resource '%s/%s/%s'", er.GetSuccessRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
eventInfos = append(eventInfos, e)
}
eventInfos = append(eventInfos, generateEventsPerEr(log, er)...)
}
return eventInfos
}
func generateEventsPerEr(log logr.Logger, er *response.EngineResponse) []event.Info {
func generateFailEvents(log logr.Logger, ers []*response.EngineResponse) (eventInfos []event.Info) {
for _, er := range ers {
eventInfos = append(eventInfos, generateFailEventsPerEr(log, er)...)
}
return eventInfos
}
func generateFailEventsPerEr(log logr.Logger, er *response.EngineResponse) []event.Info {
var eventInfos []event.Info
logger := log.WithValues("policy", er.PolicyResponse.Policy, "kind", er.PolicyResponse.Resource.Kind, "namespace", er.PolicyResponse.Resource.Namespace, "name", er.PolicyResponse.Resource.Name)
logger.V(4).Info("reporting results for policy")
logger := log.WithValues("policy", er.PolicyResponse.Policy.Name, "kind", er.PolicyResponse.Resource.Kind, "namespace", er.PolicyResponse.Resource.Namespace, "name", er.PolicyResponse.Resource.Name)
logger.V(4).Info("reporting fail results for policy")
for _, rule := range er.PolicyResponse.Rules {
if rule.Success {
@ -160,10 +186,43 @@ func generateEventsPerEr(log logr.Logger, er *response.EngineResponse) []event.I
e.Name = er.PolicyResponse.Resource.Name
e.Reason = event.PolicyViolation.String()
e.Source = event.PolicyController
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' failed. %v", er.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' failed. %v", er.PolicyResponse.Policy.Name, rule.Type, rule.Name, rule.Message)
eventInfos = append(eventInfos, e)
}
if !er.IsFailed() {
// generate event on policy for success rules
logger.V(4).Info("generating event on policy for success rules")
e := event.Info{}
kind := "ClusterPolicy"
if er.PolicyResponse.Policy.Namespace != "" {
kind = "Policy"
}
e.Kind = kind
e.Namespace = er.PolicyResponse.Policy.Namespace
e.Name = er.PolicyResponse.Policy.Name
e.Reason = event.PolicyApplied.String()
e.Source = event.PolicyController
e.Message = fmt.Sprintf("rules '%v' successfully applied on resource '%s/%s/%s'", er.GetSuccessRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
eventInfos = append(eventInfos, e)
}
if !er.IsSuccessful() {
// generate event on policy for failed rules
logger.V(4).Info("generating event on policy")
e := event.Info{}
kind := "ClusterPolicy"
if er.PolicyResponse.Policy.Namespace != "" {
kind = "Policy"
}
e.Kind = kind
e.Name = er.PolicyResponse.Policy.Name
e.Namespace = er.PolicyResponse.Policy.Namespace
e.Reason = event.PolicyViolation.String()
e.Source = event.PolicyController
e.Message = fmt.Sprintf("rules '%v' not satisfied on resource '%s/%s/%s'", er.GetFailedRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
eventInfos = append(eventInfos, e)
}
return eventInfos
}

View file

@ -230,7 +230,7 @@ func calculateSummary(results []*report.PolicyReportResult) (summary report.Poli
func buildPVInfo(er *response.EngineResponse) Info {
info := Info{
PolicyName: er.PolicyResponse.Policy,
PolicyName: er.PolicyResponse.Policy.Name,
Namespace: er.PatchedResource.GetNamespace(),
Results: []EngineResponseResult{
{

View file

@ -235,10 +235,8 @@ func validateResponse(t *testing.T, er response.PolicyResponse, expected respons
// cant do deepEquals and the stats will be different, or we nil those fields and then do a comparison
// forcing only the fields that are specified to be comprared
// doing a field by fields comparison will allow us to provied more details logs and granular error reporting
// check policy name is same :P
if er.Policy != expected.Policy {
t.Errorf("Policy name: expected %s, received %s", expected.Policy, er.Policy)
}
// compare policy spec
comparePolicySpec(t, er.Policy, expected.Policy)
// compare resource spec
compareResourceSpec(t, er.Resource, expected.Resource)
// //TODO stats
@ -260,6 +258,17 @@ func validateResponse(t *testing.T, er response.PolicyResponse, expected respons
}
}
func comparePolicySpec(t *testing.T, policy response.PolicySpec, expectedPolicy response.PolicySpec) {
// namespace
if policy.Namespace != expectedPolicy.Namespace {
t.Errorf("namespace: expected %s, received %s", expectedPolicy.Namespace, policy.Namespace)
}
// name
if policy.Name != expectedPolicy.Name {
t.Errorf("name: expected %s, received %s", expectedPolicy.Name, policy.Name)
}
}
func compareResourceSpec(t *testing.T, resource response.ResourceSpec, expectedResource response.ResourceSpec) {
// kind
if resource.Kind != expectedResource.Kind {

View file

@ -97,7 +97,7 @@ func annotationFromEngineResponses(engineResponses []*response.EngineResponse, l
var annotationContent = make(map[string]string)
for _, engineResponse := range engineResponses {
if !engineResponse.IsSuccessful() {
log.V(3).Info("skip building annotation; policy failed to apply", "policy", engineResponse.PolicyResponse.Policy)
log.V(3).Info("skip building annotation; policy failed to apply", "policy", engineResponse.PolicyResponse.Policy.Name)
continue
}
@ -106,7 +106,7 @@ func annotationFromEngineResponses(engineResponses []*response.EngineResponse, l
continue
}
policyName := engineResponse.PolicyResponse.Policy
policyName := engineResponse.PolicyResponse.Policy.Name
for _, rulePatch := range rulePatches {
annotationContent[rulePatch.RuleName+"."+policyName+".kyverno.io"] = operationToPastTense[rulePatch.Op] + " " + rulePatch.Path
}

View file

@ -16,7 +16,7 @@ func newPolicyResponse(policy, rule string, patchesStr []string, success bool) r
}
return response.PolicyResponse{
Policy: policy,
Policy: response.PolicySpec{Name: policy},
Rules: []response.RuleResponse{
{
Name: rule,

View file

@ -30,7 +30,7 @@ func isResponseSuccessful(engineReponses []*response.EngineResponse) bool {
func toBlockResource(engineReponses []*response.EngineResponse, log logr.Logger) bool {
for _, er := range engineReponses {
if !er.IsSuccessful() && er.PolicyResponse.ValidationFailureAction == common.Enforce {
log.Info("spec.ValidationFailureAction set to enforce blocking resource request", "policy", er.PolicyResponse.Policy)
log.Info("spec.ValidationFailureAction set to enforce blocking resource request", "policy", er.PolicyResponse.Policy.Name)
return true
}
}
@ -52,7 +52,7 @@ func getEnforceFailureErrorMsg(engineResponses []*response.EngineResponse) strin
}
resourceName = fmt.Sprintf("%s/%s/%s", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
policyToRule[er.PolicyResponse.Policy] = ruleToReason
policyToRule[er.PolicyResponse.Policy.Name] = ruleToReason
}
}
@ -69,7 +69,7 @@ func getErrorMsg(engineReponses []*response.EngineResponse) string {
if !er.IsSuccessful() {
// resource in engineReponses is identical as this was called per admission request
resourceInfo = fmt.Sprintf("%s/%s/%s", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
str = append(str, fmt.Sprintf("failed policy %s:", er.PolicyResponse.Policy))
str = append(str, fmt.Sprintf("failed policy %s:", er.PolicyResponse.Policy.Name))
for _, rule := range er.PolicyResponse.Rules {
if !rule.Success {
str = append(str, rule.ToString())

View file

@ -366,7 +366,7 @@ func (ws *WebhookServer) handleDelete(request *v1beta1.AdmissionRequest) {
func (ws *WebhookServer) deleteGR(logger logr.Logger, engineResponse *response.EngineResponse) {
logger.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy,
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy.Name,
"generate.kyverno.io/resource-name": engineResponse.PolicyResponse.Resource.Name,
"generate.kyverno.io/resource-kind": engineResponse.PolicyResponse.Resource.Kind,
"generate.kyverno.io/resource-namespace": engineResponse.PolicyResponse.Resource.Namespace,
@ -401,7 +401,7 @@ func applyGenerateRequest(gnGenerator generate.GenerateRequests, userRequestInfo
func transform(userRequestInfo kyverno.RequestInfo, er *response.EngineResponse) kyverno.GenerateRequestSpec {
gr := kyverno.GenerateRequestSpec{
Policy: er.PolicyResponse.Policy,
Policy: er.PolicyResponse.Policy.Name,
Resource: kyverno.ResourceSpec{
Kind: er.PolicyResponse.Resource.Kind,
Namespace: er.PolicyResponse.Resource.Namespace,
@ -421,7 +421,7 @@ type generateStats struct {
}
func (gs generateStats) PolicyName() string {
return gs.resp.PolicyResponse.Policy
return gs.resp.PolicyResponse.Policy.Name
}
func (gs generateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {

View file

@ -180,9 +180,9 @@ type mutateStats struct {
func (ms mutateStats) PolicyName() string {
if ms.namespace == "" {
return ms.resp.PolicyResponse.Policy
return ms.resp.PolicyResponse.Policy.Name
}
return ms.namespace + "/" + ms.resp.PolicyResponse.Policy
return ms.namespace + "/" + ms.resp.PolicyResponse.Policy.Name
}
func (ms mutateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {

View file

@ -19,7 +19,7 @@ func Test_GenerateStats(t *testing.T) {
generateStats: []*response.EngineResponse{
{
PolicyResponse: response.PolicyResponse{
Policy: "policy1",
Policy: response.PolicySpec{Name: "policy1"},
Rules: []response.RuleResponse{
{
Name: "rule5",
@ -40,7 +40,7 @@ func Test_GenerateStats(t *testing.T) {
},
{
PolicyResponse: response.PolicyResponse{
Policy: "policy2",
Policy: response.PolicySpec{Name: "policy2"},
Rules: []response.RuleResponse{
{
Name: "rule5",
@ -86,7 +86,7 @@ func Test_MutateStats(t *testing.T) {
mutateStats: []*response.EngineResponse{
{
PolicyResponse: response.PolicyResponse{
Policy: "policy1",
Policy: response.PolicySpec{Name: "policy1"},
Rules: []response.RuleResponse{
{
Name: "rule1",
@ -107,7 +107,7 @@ func Test_MutateStats(t *testing.T) {
},
{
PolicyResponse: response.PolicyResponse{
Policy: "policy2",
Policy: response.PolicySpec{Name: "policy2"},
Rules: []response.RuleResponse{
{
Name: "rule1",
@ -152,7 +152,7 @@ func Test_ValidateStats(t *testing.T) {
validateStats: []*response.EngineResponse{
{
PolicyResponse: response.PolicyResponse{
Policy: "policy1",
Policy: response.PolicySpec{Name: "policy1"},
ValidationFailureAction: "enforce",
Rules: []response.RuleResponse{
{
@ -174,7 +174,7 @@ func Test_ValidateStats(t *testing.T) {
},
{
PolicyResponse: response.PolicyResponse{
Policy: "policy2",
Policy: response.PolicySpec{Name: "policy2"},
Rules: []response.RuleResponse{
{
Name: "rule3",

View file

@ -1,10 +1,10 @@
package webhooks
import (
"strings"
"github.com/go-logr/logr"
kyvernov1alpha1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1alpha1"
"github.com/kyverno/kyverno/pkg/engine/response"
"strings"
"github.com/kyverno/kyverno/pkg/event"
)
@ -15,33 +15,75 @@ func generateEvents(engineResponses []*response.EngineResponse, blocked, onUpdat
// - Admission-Response is SUCCESS
// - Some/All policies failed (policy violations generated)
// - report event on resource that failed
// - report failure event on policy
// - report failure event on resource
// - Some/All policies succeeded
// - report success event on policy
// - report success event on resource
for _, er := range engineResponses {
if er.IsSuccessful() {
// do not create event on rules that were successful
continue
if !er.IsSuccessful() {
// Rules that failed
failedRules := er.GetFailedRules()
failedRulesStr := strings.Join(failedRules, ";")
// Event on the policy
kind := "ClusterPolicy"
if er.PolicyResponse.Policy.Namespace != "" {
kind = "Policy"
}
pe := event.NewEvent(
log,
kind,
kyvernov1alpha1.SchemeGroupVersion.String(),
er.PolicyResponse.Policy.Namespace,
er.PolicyResponse.Policy.Name,
event.PolicyViolation.String(),
event.AdmissionController,
event.FPolicyApply,
failedRulesStr,
er.PolicyResponse.Resource.GetKey(),
)
// Event on the resource
re := event.NewEvent(
log,
er.PolicyResponse.Resource.Kind,
er.PolicyResponse.Resource.APIVersion,
er.PolicyResponse.Resource.Namespace,
er.PolicyResponse.Resource.Name,
event.PolicyViolation.String(),
event.AdmissionController,
event.FResourcePolicyApply,
failedRulesStr,
er.PolicyResponse.Policy.Name,
)
events = append(events, pe, re)
}
// Rules that failed
failedRules := er.GetFailedRules()
filedRulesStr := strings.Join(failedRules, ";")
// Event on the resource
// event on resource
e := event.NewEvent(
log,
er.PolicyResponse.Resource.Kind,
er.PolicyResponse.Resource.APIVersion,
er.PolicyResponse.Resource.Namespace,
er.PolicyResponse.Resource.Name,
event.PolicyViolation.String(),
event.AdmissionController,
event.FResourcePolicyFailed,
filedRulesStr,
er.PolicyResponse.Policy,
)
events = append(events, e)
if !er.IsFailed() {
successRules := er.GetSuccessRules()
successRulesStr := strings.Join(successRules, ";")
// Event on the policy
kind := "ClusterPolicy"
if er.PolicyResponse.Policy.Namespace != "" {
kind = "Policy"
}
e := event.NewEvent(
log,
kind,
kyvernov1alpha1.SchemeGroupVersion.String(),
er.PolicyResponse.Policy.Namespace,
er.PolicyResponse.Policy.Name,
event.PolicyApplied.String(),
event.AdmissionController,
event.SPolicyApply,
successRulesStr,
er.PolicyResponse.Resource.GetKey(),
)
events = append(events, e)
}
}
return events
}

View file

@ -218,9 +218,9 @@ type validateStats struct {
func (vs validateStats) PolicyName() string {
if vs.namespace == "" {
return vs.resp.PolicyResponse.Policy
return vs.resp.PolicyResponse.Policy.Name
}
return vs.namespace + "/" + vs.resp.PolicyResponse.Policy
return vs.namespace + "/" + vs.resp.PolicyResponse.Policy.Name
}

View file

@ -6,7 +6,9 @@ expected:
mutation:
patchedresource: test/output/output_mutate_endpoint.yaml
policyresponse:
policy: policy-endpoints
policy:
namespace: ''
name: policy-endpoints
resource:
kind: Endpoints
apiVersion: v1

View file

@ -6,7 +6,8 @@ expected:
mutation:
patchedresource: test/output/output_mutate_pod_spec.yaml
policyresponse:
policy: mutate-pods-spec
policy:
name: mutate-pods-spec
resource:
kind: Deployment
apiVersion: apps/v1

View file

@ -6,7 +6,9 @@ expected:
mutation:
patchedresource: test/output/output_mutate_validate_qos.yaml
policyresponse:
policy: policy-qos
policy:
namespace: ''
name: policy-qos
resource:
kind: Deployment
apiVersion: apps/v1
@ -19,7 +21,9 @@ expected:
message: successfully processed strategic merge patch
validation:
policyresponse:
policy: policy-qos
policy:
namespace: ''
name: policy-qos
resource:
kind: Deployment
apiVersion: apps/v1

View file

@ -6,7 +6,9 @@ input:
expected:
validation:
policyresponse:
policy: validate-default-proc-mount
policy:
namespace: ''
name: validate-default-proc-mount
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: validate-disallow-default-serviceaccount
policy:
namespace: ''
name: validate-disallow-default-serviceaccount
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: check-probe-exists
policy:
namespace: ''
name: check-probe-exists
resource:
kind: Pod
apiVersion: v1

View file

@ -6,7 +6,8 @@ input:
expected:
validation:
policyresponse:
policy: validate-selinux-options
policy:
name: validate-selinux-options
resource:
kind: Pod
apiVersion: v1

View file

@ -6,7 +6,9 @@ input:
expected:
validation:
policyresponse:
policy: validate-volumes-whitelist
policy:
namespace: ''
name: validate-volumes-whitelist
resource:
kind: Pod
apiVersion: v1

View file

@ -9,7 +9,9 @@ expected:
kind: NetworkPolicy
namespace: devtest
policyresponse:
policy: add-networkpolicy
policy:
namespace: ''
name: add-networkpolicy
resource:
kind: Namespace
apiVersion: v1

View file

@ -9,7 +9,9 @@ expected:
kind: ResourceQuota
namespace: test-namespace-quota
policyresponse:
policy: add-ns-quota
policy:
namespace: ''
name: add-ns-quota
resource:
kind: Namespace
apiVersion: v1

View file

@ -6,7 +6,9 @@ expected:
mutation:
patchedresource: test/output/pod-with-emptydir.yaml
policyresponse:
policy: add-safe-to-evict
policy:
namespace: ''
name: add-safe-to-evict
resource:
kind: Pod
apiVersion: v1

View file

@ -6,7 +6,9 @@ expected:
mutation:
patchedresource: test/output/pod-with-hostpath.yaml
policyresponse:
policy: add-safe-to-evict
policy:
namespace: ''
name: add-safe-to-evict
resource:
kind: Pod
apiVersion: v1

View file

@ -6,7 +6,9 @@ expected:
mutation:
patchedresource: test/resources/pod-with-default-volume.yaml
policyresponse:
policy: add-safe-to-evict
policy:
namespace: ''
name: add-safe-to-evict
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-bind-mounts
policy:
namespace: ''
name: disallow-bind-mounts
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-bind-mounts
policy:
namespace: ''
name: disallow-bind-mounts
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-host-network-port
policy:
namespace: ''
name: disallow-host-network-port
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-host-pid-ipc
policy:
namespace: ''
name: disallow-host-pid-ipc
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-privileged
policy:
namespace: ''
name: disallow-privileged
resource:
kind: Pod
apiVersion: v1

View file

@ -6,7 +6,9 @@ input:
expected:
validation:
policyresponse:
policy: disallow-sysctls
policy:
namespace: ''
name: disallow-sysctls
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: restrict-automount-sa-token
policy:
namespace: ''
name: restrict-automount-sa-token
resource:
kind: Pod
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: restrict-ingress-classes
policy:
namespace: ''
name: restrict-ingress-classes
resource:
kind: Ingress
apiVersion: v1

View file

@ -5,7 +5,9 @@ input:
expected:
validation:
policyresponse:
policy: restrict-ingress-classes
policy:
namespace: ''
name: restrict-ingress-classes
resource:
kind: Ingress
apiVersion: v1