mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-09 02:29:22 +00:00
violation clean up
This commit is contained in:
parent
29a89d20ad
commit
a5817f5863
7 changed files with 207 additions and 47 deletions
2
main.go
2
main.go
|
@ -55,7 +55,7 @@ func main() {
|
|||
if err != nil {
|
||||
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
|
||||
}
|
||||
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, filterK8Kinds)
|
||||
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, filterK8Kinds)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create webhook server: %v\n", err)
|
||||
}
|
||||
|
|
|
@ -83,11 +83,17 @@ type Status struct {
|
|||
|
||||
// Violation for the policy
|
||||
type Violation struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Rules []string `json:"rules"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Rules []FailedRule `json:"rules"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// FailedRule stored info and type of failed rules
|
||||
type FailedRule struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` //Mutation, Validation, Genertaion
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
|
|
@ -41,6 +41,22 @@ func (in *CloneFrom) DeepCopy() *CloneFrom {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FailedRule) DeepCopyInto(out *FailedRule) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailedRule.
|
||||
func (in *FailedRule) DeepCopy() *FailedRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FailedRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generation.
|
||||
func (in *Generation) DeepCopy() *Generation {
|
||||
if in == nil {
|
||||
|
@ -248,7 +264,7 @@ func (in *Violation) DeepCopyInto(out *Violation) {
|
|||
*out = *in
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]string, len(*in))
|
||||
*out = make([]FailedRule, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
|
|
|
@ -3,7 +3,6 @@ package controller
|
|||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
|
@ -11,7 +10,7 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
|
@ -63,13 +62,13 @@ func (pc *PolicyController) createPolicyHandler(resource interface{}) {
|
|||
}
|
||||
|
||||
func (pc *PolicyController) updatePolicyHandler(oldResource, newResource interface{}) {
|
||||
newPolicy := newResource.(*types.Policy)
|
||||
oldPolicy := oldResource.(*types.Policy)
|
||||
newPolicy.Status = types.Status{}
|
||||
oldPolicy.Status = types.Status{}
|
||||
newPolicy := newResource.(*v1alpha1.Policy)
|
||||
oldPolicy := oldResource.(*v1alpha1.Policy)
|
||||
newPolicy.Status = v1alpha1.Status{}
|
||||
oldPolicy.Status = v1alpha1.Status{}
|
||||
newPolicy.ResourceVersion = ""
|
||||
oldPolicy.ResourceVersion = ""
|
||||
if reflect.DeepEqual(newPolicy.ResourceVersion, oldPolicy.ResourceVersion) {
|
||||
if reflect.DeepEqual(newPolicy, oldPolicy) {
|
||||
return
|
||||
}
|
||||
pc.enqueuePolicy(newResource)
|
||||
|
@ -185,7 +184,7 @@ func (pc *PolicyController) syncHandler(obj interface{}) error {
|
|||
//TODO: processPolicy
|
||||
glog.Infof("process policy %s on existing resources", policy.GetName())
|
||||
policyInfos := engine.ProcessExisting(pc.client, policy)
|
||||
events, violations := createEventsAndViolations(pc.eventController, policyInfos)
|
||||
events, violations := pc.createEventsAndViolations(policyInfos)
|
||||
pc.eventController.Add(events...)
|
||||
err = pc.violationBuilder.Add(violations...)
|
||||
if err != nil {
|
||||
|
@ -194,25 +193,34 @@ func (pc *PolicyController) syncHandler(obj interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func createEventsAndViolations(eventController event.Generator, policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) {
|
||||
func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) {
|
||||
events := []*event.Info{}
|
||||
violations := []*violation.Info{}
|
||||
// Create events from the policyInfo
|
||||
for _, policyInfo := range policyInfos {
|
||||
fruleNames := []string{}
|
||||
frules := []v1alpha1.FailedRule{}
|
||||
sruleNames := []string{}
|
||||
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if !rule.IsSuccessful() {
|
||||
e := &event.Info{}
|
||||
fruleNames = append(fruleNames, rule.Name)
|
||||
frule := v1alpha1.FailedRule{Name: rule.Name}
|
||||
switch rule.RuleType {
|
||||
case info.Mutation, info.Validation, info.Generation:
|
||||
// Events
|
||||
e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation, event.FProcessRule, rule.Name, policyInfo.Name)
|
||||
switch rule.RuleType {
|
||||
case info.Mutation:
|
||||
frule.Type = info.Mutation.String()
|
||||
case info.Validation:
|
||||
frule.Type = info.Validation.String()
|
||||
case info.Generation:
|
||||
frule.Type = info.Generation.String()
|
||||
}
|
||||
default:
|
||||
glog.Info("Unsupported Rule type")
|
||||
}
|
||||
frules = append(frules, frule)
|
||||
events = append(events, e)
|
||||
} else {
|
||||
sruleNames = append(sruleNames, rule.Name)
|
||||
|
@ -222,14 +230,20 @@ func createEventsAndViolations(eventController event.Generator, policyInfos []*i
|
|||
if !policyInfo.IsSuccessful() {
|
||||
// Event
|
||||
// list of failed rules : ruleNames
|
||||
e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, strings.Join(fruleNames, ";"))
|
||||
|
||||
e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules))
|
||||
events = append(events, e)
|
||||
// Violation
|
||||
// TODO: Violation is currently create at policy, level not resource level
|
||||
// As we create violation, we check if the
|
||||
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), fruleNames)
|
||||
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), frules)
|
||||
violations = append(violations, v)
|
||||
} else {
|
||||
// clean up violations
|
||||
pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Mutation)
|
||||
pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Validation)
|
||||
}
|
||||
|
||||
// else {
|
||||
// // Policy was processed succesfully
|
||||
// e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyApplied, event.SPolicyApply, policyInfo.Name)
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
)
|
||||
|
||||
const policyWorkQueueName = "policyworkqueue"
|
||||
|
||||
const policyWorkQueueRetryLimit = 5
|
||||
|
||||
const policyControllerWorkerCount = 2
|
||||
|
||||
func concatFailedRules(frules []v1alpha1.FailedRule) string {
|
||||
var buffer bytes.Buffer
|
||||
for _, frule := range frules {
|
||||
buffer.WriteString(frule.Name + ";")
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
|
|
@ -2,24 +2,27 @@ package violation
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/glog"
|
||||
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
event "github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/sharedinformer"
|
||||
)
|
||||
|
||||
//Generator to generate policy violation
|
||||
type Generator interface {
|
||||
Add(infos ...*Info) error
|
||||
RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
client *client.Client
|
||||
policyLister v1alpha1.PolicyLister
|
||||
policyLister lister.PolicyLister
|
||||
eventBuilder event.Generator
|
||||
}
|
||||
|
||||
|
@ -155,7 +158,7 @@ func (b *builder) isActive(kind, rname, rnamespace string) (bool, error) {
|
|||
//NewViolation return new policy violation
|
||||
func NewViolation(reason event.Reason, policyName, kind, rname, rnamespace, msg string) *Info {
|
||||
return &Info{Policy: policyName,
|
||||
Violation: types.Violation{
|
||||
Violation: v1alpha1.Violation{
|
||||
Kind: kind,
|
||||
Name: rname,
|
||||
Namespace: rnamespace,
|
||||
|
@ -178,29 +181,61 @@ func NewViolation(reason event.Reason, policyName, kind, rname, rnamespace, msg
|
|||
// }
|
||||
// }
|
||||
// Build a new Violation
|
||||
func BuldNewViolation(pName string, rKind string, rNs string, rName string, reason string, rules []string) *Info {
|
||||
func BuldNewViolation(pName string, rKind string, rNs string, rName string, reason string, frules []v1alpha1.FailedRule) *Info {
|
||||
return &Info{
|
||||
Policy: pName,
|
||||
Violation: types.Violation{
|
||||
Violation: v1alpha1.Violation{
|
||||
Kind: rKind,
|
||||
Namespace: rNs,
|
||||
Name: rName,
|
||||
Reason: reason,
|
||||
Rules: rules,
|
||||
Rules: frules,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func isRuleNamesEqual(currRules []interface{}, newRules []string) bool {
|
||||
func removeRuleTypes(currRules []interface{}, ruleType info.RuleType) ([]interface{}, error) {
|
||||
//rules := []v1alpha1.FailedRule{}
|
||||
var rules []interface{}
|
||||
// removedRuleCount := 0
|
||||
for _, r := range currRules {
|
||||
glog.Info(reflect.TypeOf(r))
|
||||
rfule, ok := r.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("incorrect type")
|
||||
}
|
||||
glog.Info(reflect.TypeOf(rfule["type"]))
|
||||
rtype, ok := rfule["type"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("incorrect type")
|
||||
}
|
||||
name, ok := rfule["name"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("incorrect type")
|
||||
}
|
||||
if rtype != ruleType.String() {
|
||||
rules = append(rules, v1alpha1.FailedRule{Name: name, Type: rtype})
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func isRuleNamesEqual(currRules []interface{}, newRules []v1alpha1.FailedRule) bool {
|
||||
if len(currRules) != len(newRules) {
|
||||
return false
|
||||
}
|
||||
for i, r := range currRules {
|
||||
name, ok := r.(string)
|
||||
glog.Info(reflect.TypeOf(r))
|
||||
rfule, ok := r.(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if name != newRules[i] {
|
||||
glog.Info(reflect.TypeOf(rfule["name"]))
|
||||
name, ok := rfule["name"].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if name != newRules[i].Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -208,10 +243,76 @@ func isRuleNamesEqual(currRules []interface{}, newRules []string) bool {
|
|||
}
|
||||
|
||||
//RemoveViolation will remove the violation for the resource if there was one
|
||||
func RemoveViolation(policy *types.Policy, rKind string, rNs string, rName string) {
|
||||
func (b *builder) RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error {
|
||||
// Remove the <resource, Violation> pair from map
|
||||
if policy.Status.Violations != nil {
|
||||
glog.Infof("Cleaning up violalation for policy %s, resource %s/%s/%s", policy.Name, rKind, rNs, rName)
|
||||
delete(policy.Status.Violations, BuildKey(rKind, rNs, rName))
|
||||
statusMap := map[string]interface{}{}
|
||||
currVs := map[string]interface{}{}
|
||||
// Get the policy
|
||||
p, err := b.client.GetResource("Policy", "", policy, "status")
|
||||
if err != nil {
|
||||
glog.Infof("policy %s not found", policy)
|
||||
return err
|
||||
}
|
||||
unstr := p.UnstructuredContent()
|
||||
|
||||
// check if "status" field exists
|
||||
status, ok := unstr["status"]
|
||||
if ok {
|
||||
// status is already present then we append violations
|
||||
if statusMap, ok = status.(map[string]interface{}); !ok {
|
||||
return errors.New("Unable to parse status subresource")
|
||||
}
|
||||
violations, ok := statusMap["violations"]
|
||||
if !ok {
|
||||
glog.Info("violation not present")
|
||||
}
|
||||
glog.Info(reflect.TypeOf(violations))
|
||||
if currVs, ok = violations.(map[string]interface{}); !ok {
|
||||
return errors.New("Unable to parse violations")
|
||||
}
|
||||
currV, ok := currVs[BuildKey(rKind, rNs, rName)]
|
||||
if !ok {
|
||||
// No Violation present
|
||||
return nil
|
||||
}
|
||||
glog.Info(reflect.TypeOf(currV))
|
||||
v, ok := currV.(map[string]interface{})
|
||||
if !ok {
|
||||
glog.Info("type not matching")
|
||||
}
|
||||
// get rules
|
||||
rules, ok := v["rules"]
|
||||
if !ok {
|
||||
glog.Info("rules not found")
|
||||
}
|
||||
glog.Info(reflect.TypeOf(rules))
|
||||
rs, ok := rules.([]interface{})
|
||||
if !ok {
|
||||
glog.Info("type not matching")
|
||||
}
|
||||
// Remove rules of defined type
|
||||
newrs, err := removeRuleTypes(rs, ruleType)
|
||||
if err != nil {
|
||||
glog.Info(err)
|
||||
}
|
||||
if newrs == nil {
|
||||
// all records are removed and is empty
|
||||
glog.Info("can remove the record")
|
||||
delete(currVs, BuildKey(rKind, rNs, rName))
|
||||
} else {
|
||||
v["rules"] = newrs
|
||||
// update the violation with new rule
|
||||
currVs[BuildKey(rKind, rNs, rName)] = v
|
||||
}
|
||||
// update violations
|
||||
statusMap["violations"] = currVs
|
||||
// update status
|
||||
unstr["status"] = statusMap
|
||||
p.SetUnstructuredContent(unstr)
|
||||
_, err = b.client.UpdateStatusResource("Policy", "", p, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,11 +33,12 @@ const policyKind = "Policy"
|
|||
// WebhookServer contains configured TLS server with MutationWebhook.
|
||||
// MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient.
|
||||
type WebhookServer struct {
|
||||
server http.Server
|
||||
client *client.Client
|
||||
policyLister v1alpha1.PolicyLister
|
||||
eventController event.Generator
|
||||
filterKinds []string
|
||||
server http.Server
|
||||
client *client.Client
|
||||
policyLister v1alpha1.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
filterKinds []string
|
||||
}
|
||||
|
||||
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
|
||||
|
@ -47,6 +48,7 @@ func NewWebhookServer(
|
|||
tlsPair *tlsutils.TlsPemPair,
|
||||
shareInformer sharedinformer.PolicyInformer,
|
||||
eventController event.Generator,
|
||||
violationBuilder violation.Generator,
|
||||
filterKinds []string) (*WebhookServer, error) {
|
||||
|
||||
if tlsPair == nil {
|
||||
|
@ -61,10 +63,11 @@ func NewWebhookServer(
|
|||
tlsConfig.Certificates = []tls.Certificate{pair}
|
||||
|
||||
ws := &WebhookServer{
|
||||
client: client,
|
||||
policyLister: shareInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
filterKinds: parseKinds(filterKinds),
|
||||
client: client,
|
||||
policyLister: shareInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
filterKinds: parseKinds(filterKinds),
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
|
||||
|
@ -184,9 +187,12 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
|||
glog.Warning(r.Msgs)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("cleanup")
|
||||
// CleanUp Violations if exists
|
||||
violation.RemoveViolation(policy, request.Kind.Kind, rns, rname)
|
||||
err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Mutation)
|
||||
if err != nil {
|
||||
glog.Info(err)
|
||||
}
|
||||
|
||||
if len(policyPatches) > 0 {
|
||||
allPatches = append(allPatches, policyPatches...)
|
||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns)
|
||||
|
@ -264,7 +270,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
glog.V(3).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, rns, rname, request.UID, request.Operation)
|
||||
|
||||
glog.Infof("Validating resource with policy %s with %d rules", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
glog.Infof("Validating resource %s/%s/%s with policy %s with %d rules", rkind, rns, rname, policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
ruleInfos, err := engine.Validate(*policy, request.Object.Raw, request.Kind)
|
||||
if err != nil {
|
||||
// This is not policy error
|
||||
|
@ -282,7 +288,10 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
}
|
||||
} else {
|
||||
// CleanUp Violations if exists
|
||||
violation.RemoveViolation(policy, request.Kind.Kind, rns, rname)
|
||||
err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Validation)
|
||||
if err != nil {
|
||||
glog.Info(err)
|
||||
}
|
||||
|
||||
if len(ruleInfos) > 0 {
|
||||
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns)
|
||||
|
|
Loading…
Add table
Reference in a new issue