1
0
Fork 0
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:
shivkumar dudhani 2019-07-15 14:49:22 -07:00
parent 29a89d20ad
commit a5817f5863
7 changed files with 207 additions and 47 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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()
}

View file

@ -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
}

View file

@ -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)