mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
bde90340a6
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
402 lines
12 KiB
Go
402 lines
12 KiB
Go
package event
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
|
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
)
|
|
|
|
func NewPolicyFailEvent(source Source, reason Reason, engineResponse engineapi.EngineResponse, ruleResp engineapi.RuleResponse, blocked bool) Info {
|
|
action := ResourcePassed
|
|
if blocked {
|
|
action = ResourceBlocked
|
|
}
|
|
pol := engineResponse.Policy()
|
|
regarding := corev1.ObjectReference{
|
|
APIVersion: pol.GetAPIVersion(),
|
|
Kind: pol.GetKind(),
|
|
Name: pol.GetName(),
|
|
Namespace: pol.GetNamespace(),
|
|
UID: pol.MetaObject().GetUID(),
|
|
}
|
|
related := engineResponse.GetResourceSpec()
|
|
return Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: related.APIVersion,
|
|
Kind: related.Kind,
|
|
Name: related.Name,
|
|
Namespace: related.Namespace,
|
|
UID: types.UID(related.UID),
|
|
},
|
|
Reason: reason,
|
|
Source: source,
|
|
Message: buildPolicyEventMessage(ruleResp, engineResponse.GetResourceSpec(), blocked),
|
|
Action: action,
|
|
}
|
|
}
|
|
|
|
func buildPolicyEventMessage(resp engineapi.RuleResponse, resource engineapi.ResourceSpec, blocked bool) string {
|
|
var b strings.Builder
|
|
if resource.Namespace != "" {
|
|
fmt.Fprintf(&b, "%s %s/%s", resource.Kind, resource.Namespace, resource.Name)
|
|
} else {
|
|
fmt.Fprintf(&b, "%s %s", resource.Kind, resource.Name)
|
|
}
|
|
|
|
fmt.Fprintf(&b, ": [%s] %s", resp.Name(), resp.Status())
|
|
if blocked {
|
|
fmt.Fprintf(&b, " (blocked)")
|
|
}
|
|
|
|
if resp.Message() != "" {
|
|
fmt.Fprintf(&b, "; %s", resp.Message())
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func NewPolicyAppliedEvent(source Source, engineResponse engineapi.EngineResponse) Info {
|
|
resource := engineResponse.Resource
|
|
var bldr strings.Builder
|
|
defer bldr.Reset()
|
|
|
|
var res string
|
|
if resource.GetNamespace() != "" {
|
|
res = fmt.Sprintf("%s %s/%s", resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
|
} else {
|
|
res = fmt.Sprintf("%s %s", resource.GetKind(), resource.GetName())
|
|
}
|
|
|
|
var action Action
|
|
policy := engineResponse.Policy()
|
|
if policy.GetType() == engineapi.KyvernoPolicyType {
|
|
pol := engineResponse.Policy().AsKyvernoPolicy()
|
|
hasValidate := pol.GetSpec().HasValidate()
|
|
hasVerifyImages := pol.GetSpec().HasVerifyImages()
|
|
hasMutate := pol.GetSpec().HasMutate()
|
|
if hasValidate || hasVerifyImages {
|
|
fmt.Fprintf(&bldr, "%s: pass", res)
|
|
action = ResourcePassed
|
|
} else if hasMutate {
|
|
fmt.Fprintf(&bldr, "%s is successfully mutated", res)
|
|
action = ResourceMutated
|
|
}
|
|
} else {
|
|
fmt.Fprintf(&bldr, "%s: pass", res)
|
|
action = ResourcePassed
|
|
}
|
|
regarding := corev1.ObjectReference{
|
|
APIVersion: policy.GetAPIVersion(),
|
|
Kind: policy.GetKind(),
|
|
Name: policy.GetName(),
|
|
Namespace: policy.GetNamespace(),
|
|
UID: policy.MetaObject().GetUID(),
|
|
}
|
|
related := engineResponse.GetResourceSpec()
|
|
return Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: related.APIVersion,
|
|
Kind: related.Kind,
|
|
Name: related.Name,
|
|
Namespace: related.Namespace,
|
|
UID: types.UID(related.UID),
|
|
},
|
|
Reason: PolicyApplied,
|
|
Source: source,
|
|
Message: bldr.String(),
|
|
Action: action,
|
|
}
|
|
}
|
|
|
|
func NewResourceViolationEvent(source Source, reason Reason, engineResponse engineapi.EngineResponse, ruleResp engineapi.RuleResponse) Info {
|
|
var bldr strings.Builder
|
|
defer bldr.Reset()
|
|
|
|
pol := engineResponse.Policy()
|
|
fmt.Fprintf(&bldr, "policy %s/%s %s: %s", pol.GetName(),
|
|
ruleResp.Name(), ruleResp.Status(), ruleResp.Message())
|
|
resource := engineResponse.GetResourceSpec()
|
|
regarding := corev1.ObjectReference{
|
|
APIVersion: resource.APIVersion,
|
|
Kind: resource.Kind,
|
|
Name: resource.Name,
|
|
Namespace: resource.Namespace,
|
|
UID: types.UID(resource.UID),
|
|
}
|
|
return Info{
|
|
Regarding: regarding,
|
|
Reason: reason,
|
|
Source: source,
|
|
Message: bldr.String(),
|
|
Action: ResourcePassed,
|
|
}
|
|
}
|
|
|
|
func NewResourceGenerationEvent(policy, rule string, source Source, resource kyvernov1.ResourceSpec) Info {
|
|
msg := fmt.Sprintf("Created %s %s as a result of applying policy %s/%s", resource.GetKind(), resource.GetName(), policy, rule)
|
|
regarding := corev1.ObjectReference{
|
|
APIVersion: resource.APIVersion,
|
|
Kind: resource.Kind,
|
|
Name: resource.Name,
|
|
Namespace: resource.Namespace,
|
|
UID: resource.UID,
|
|
}
|
|
return Info{
|
|
Regarding: regarding,
|
|
Source: source,
|
|
Reason: PolicyApplied,
|
|
Message: msg,
|
|
Action: None,
|
|
}
|
|
}
|
|
|
|
func NewBackgroundFailedEvent(err error, policy kyvernov1.PolicyInterface, rule string, source Source, resource kyvernov1.ResourceSpec) []Info {
|
|
var events []Info
|
|
regarding := corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v1",
|
|
Kind: policy.GetKind(),
|
|
Name: policy.GetName(),
|
|
Namespace: policy.GetNamespace(),
|
|
UID: policy.GetUID(),
|
|
}
|
|
var msg string
|
|
if rule == "" {
|
|
msg = fmt.Sprintf("policy %s error: %v", policy.GetName(), err)
|
|
} else {
|
|
msg = fmt.Sprintf("policy %s/%s error: %v", policy.GetName(), rule, err)
|
|
}
|
|
events = append(events, Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: resource.APIVersion,
|
|
Kind: resource.Kind,
|
|
Name: resource.Name,
|
|
Namespace: resource.Namespace,
|
|
UID: resource.UID,
|
|
},
|
|
Source: source,
|
|
Reason: PolicyError,
|
|
Message: msg,
|
|
Action: None,
|
|
})
|
|
|
|
return events
|
|
}
|
|
|
|
func NewBackgroundSuccessEvent(source Source, policy kyvernov1.PolicyInterface, resources []kyvernov1.ResourceSpec) []Info {
|
|
events := make([]Info, 0, len(resources))
|
|
msg := "resource generated"
|
|
action := ResourceGenerated
|
|
if source == MutateExistingController {
|
|
msg = "resource mutated"
|
|
action = ResourceMutated
|
|
}
|
|
regarding := corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v1",
|
|
Kind: policy.GetKind(),
|
|
Name: policy.GetName(),
|
|
Namespace: policy.GetNamespace(),
|
|
UID: policy.GetUID(),
|
|
}
|
|
for _, res := range resources {
|
|
events = append(events, Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: res.APIVersion,
|
|
Kind: res.Kind,
|
|
Name: res.Name,
|
|
Namespace: res.Namespace,
|
|
UID: res.UID,
|
|
},
|
|
Source: source,
|
|
Reason: PolicyApplied,
|
|
Message: msg,
|
|
Action: action,
|
|
})
|
|
}
|
|
|
|
return events
|
|
}
|
|
|
|
func NewPolicyExceptionEvents(engineResponse engineapi.EngineResponse, ruleResp engineapi.RuleResponse, source Source) []Info {
|
|
var exceptionMessage string
|
|
exceptions := ruleResp.Exceptions()
|
|
exceptionNames := make([]string, 0, len(exceptions))
|
|
events := make([]Info, 0, len(exceptions))
|
|
|
|
// build the events of the policy exceptions
|
|
pol := engineResponse.Policy().AsKyvernoPolicy()
|
|
if pol.GetNamespace() == "" {
|
|
exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s", resourceKey(engineResponse.PatchedResource), pol.GetName(), ruleResp.Name())
|
|
} else {
|
|
exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s/%s", resourceKey(engineResponse.PatchedResource), pol.GetNamespace(), pol.GetName(), ruleResp.Name())
|
|
}
|
|
|
|
related := engineResponse.GetResourceSpec()
|
|
for _, exception := range exceptions {
|
|
ns := exception.GetNamespace()
|
|
name := exception.GetName()
|
|
exceptionNames = append(exceptionNames, ns+"/"+name)
|
|
|
|
exceptionEvent := Info{
|
|
Regarding: corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v2",
|
|
Kind: "PolicyException",
|
|
Name: name,
|
|
Namespace: ns,
|
|
UID: exception.GetUID(),
|
|
},
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: related.APIVersion,
|
|
Kind: related.Kind,
|
|
Name: related.Name,
|
|
Namespace: related.Namespace,
|
|
UID: types.UID(related.UID),
|
|
},
|
|
Reason: PolicySkipped,
|
|
Message: exceptionMessage,
|
|
Source: source,
|
|
Action: ResourcePassed,
|
|
}
|
|
events = append(events, exceptionEvent)
|
|
}
|
|
|
|
// build the policy events
|
|
policyMessage := fmt.Sprintf("resource %s was skipped from rule %s due to policy exceptions %s", resourceKey(engineResponse.PatchedResource), ruleResp.Name(), strings.Join(exceptionNames, ", "))
|
|
regarding := corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v1",
|
|
Kind: pol.GetKind(),
|
|
Name: pol.GetName(),
|
|
Namespace: pol.GetNamespace(),
|
|
UID: pol.GetUID(),
|
|
}
|
|
policyEvent := Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: related.APIVersion,
|
|
Kind: related.Kind,
|
|
Name: related.Name,
|
|
Namespace: related.Namespace,
|
|
UID: types.UID(related.UID),
|
|
},
|
|
Reason: PolicySkipped,
|
|
Message: policyMessage,
|
|
Source: source,
|
|
Action: ResourcePassed,
|
|
}
|
|
events = append(events, policyEvent)
|
|
return events
|
|
}
|
|
|
|
func NewCleanupPolicyEvent(policy kyvernov2.CleanupPolicyInterface, resource unstructured.Unstructured, err error) Info {
|
|
regarding := corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v2",
|
|
Kind: policy.GetKind(),
|
|
Name: policy.GetName(),
|
|
Namespace: policy.GetNamespace(),
|
|
UID: policy.GetUID(),
|
|
}
|
|
related := &corev1.ObjectReference{
|
|
APIVersion: resource.GetAPIVersion(),
|
|
Kind: resource.GetKind(),
|
|
Namespace: resource.GetNamespace(),
|
|
Name: resource.GetName(),
|
|
}
|
|
if err == nil {
|
|
return Info{
|
|
Regarding: regarding,
|
|
Related: related,
|
|
Source: CleanupController,
|
|
Action: ResourceCleanedUp,
|
|
Reason: PolicyApplied,
|
|
Message: fmt.Sprintf("successfully cleaned up the target resource %v/%v/%v", resource.GetKind(), resource.GetNamespace(), resource.GetName()),
|
|
}
|
|
} else {
|
|
return Info{
|
|
Regarding: regarding,
|
|
Related: related,
|
|
Source: CleanupController,
|
|
Action: None,
|
|
Reason: PolicyError,
|
|
Message: fmt.Sprintf("failed to clean up the target resource %v/%v/%v: %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err.Error()),
|
|
}
|
|
}
|
|
}
|
|
|
|
func NewValidatingAdmissionPolicyEvent(policy kyvernov1.PolicyInterface, vapName, vapBindingName string) []Info {
|
|
regarding := corev1.ObjectReference{
|
|
// TODO: iirc it's not safe to assume api version is set
|
|
APIVersion: "kyverno.io/v1",
|
|
Kind: policy.GetKind(),
|
|
Name: policy.GetName(),
|
|
Namespace: policy.GetNamespace(),
|
|
UID: policy.GetUID(),
|
|
}
|
|
vapEvent := Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: "admissionregistration.k8s.io/v1beta1",
|
|
Kind: "ValidatingAdmissionPolicy",
|
|
Name: vapName,
|
|
},
|
|
Source: GeneratePolicyController,
|
|
Action: ResourceGenerated,
|
|
Reason: PolicyApplied,
|
|
Message: fmt.Sprintf("successfully generated validating admission policy %s from policy %s", vapName, policy.GetName()),
|
|
}
|
|
vapBindingEvent := Info{
|
|
Regarding: regarding,
|
|
Related: &corev1.ObjectReference{
|
|
APIVersion: "admissionregistration.k8s.io/v1beta1",
|
|
Kind: "ValidatingAdmissionPolicyBinding",
|
|
Name: vapBindingName,
|
|
},
|
|
Source: GeneratePolicyController,
|
|
Action: ResourceGenerated,
|
|
Reason: PolicyApplied,
|
|
Message: fmt.Sprintf("successfully generated validating admission policy binding %s from policy %s", vapBindingName, policy.GetName()),
|
|
}
|
|
return []Info{vapEvent, vapBindingEvent}
|
|
}
|
|
|
|
func NewFailedEvent(err error, policy, rule string, source Source, resource kyvernov1.ResourceSpec) Info {
|
|
var msg string
|
|
if rule == "" {
|
|
msg = fmt.Sprintf("policy %s error: %v", policy, err)
|
|
} else {
|
|
msg = fmt.Sprintf("policy %s/%s error: %v", policy, rule, err)
|
|
}
|
|
return Info{
|
|
Regarding: corev1.ObjectReference{
|
|
APIVersion: resource.APIVersion,
|
|
Kind: resource.Kind,
|
|
Name: resource.Name,
|
|
Namespace: resource.Namespace,
|
|
UID: resource.UID,
|
|
},
|
|
Source: source,
|
|
Reason: PolicyError,
|
|
Message: msg,
|
|
Action: None,
|
|
}
|
|
}
|
|
|
|
func resourceKey(resource unstructured.Unstructured) string {
|
|
if resource.GetNamespace() != "" {
|
|
return strings.Join([]string{resource.GetKind(), resource.GetNamespace(), resource.GetName()}, "/")
|
|
}
|
|
|
|
return strings.Join([]string{resource.GetKind(), resource.GetName()}, "/")
|
|
}
|