1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/violation/builder.go

582 lines
16 KiB
Go
Raw Normal View History

2019-05-13 18:17:28 -07:00
package violation
2019-04-26 18:16:22 -07:00
import (
"errors"
2019-07-15 14:49:22 -07:00
"fmt"
"reflect"
"github.com/golang/glog"
2019-07-15 14:49:22 -07:00
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"
2019-05-21 11:00:09 -07:00
event "github.com/nirmata/kyverno/pkg/event"
2019-07-15 14:49:22 -07:00
"github.com/nirmata/kyverno/pkg/info"
2019-05-21 11:00:09 -07:00
"github.com/nirmata/kyverno/pkg/sharedinformer"
2019-07-18 10:22:20 -07:00
"k8s.io/apimachinery/pkg/runtime"
)
2019-05-10 10:38:38 -07:00
//Generator to generate policy violation
type Generator interface {
2019-06-27 11:43:07 -07:00
Add(infos ...*Info) error
2019-07-15 14:49:22 -07:00
RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error
ResourceRemoval(policy, rKind, rNs, rName string) error
2019-04-26 18:16:22 -07:00
}
2019-05-10 10:38:38 -07:00
type builder struct {
2019-05-15 12:29:09 -07:00
client *client.Client
2019-07-15 14:49:22 -07:00
policyLister lister.PolicyLister
2019-05-15 12:29:09 -07:00
eventBuilder event.Generator
}
2019-05-10 10:38:38 -07:00
//Builder is to build policy violations
type Builder interface {
Generator
2019-06-27 11:43:07 -07:00
processViolation(info *Info) error
isActive(kind string, rname string, rnamespace string) (bool, error)
2019-05-10 00:05:21 -07:00
}
2019-05-10 10:38:38 -07:00
//NewPolicyViolationBuilder returns new violation builder
func NewPolicyViolationBuilder(client *client.Client,
2019-05-15 12:29:09 -07:00
sharedInfomer sharedinformer.PolicyInformer,
eventController event.Generator) Builder {
2019-05-10 10:38:38 -07:00
builder := &builder{
2019-05-15 12:29:09 -07:00
client: client,
policyLister: sharedInfomer.GetLister(),
eventBuilder: eventController,
2019-05-06 12:08:31 -07:00
}
2019-05-10 00:05:21 -07:00
return builder
}
2019-06-27 11:43:07 -07:00
func (b *builder) Add(infos ...*Info) error {
2019-07-18 10:22:20 -07:00
if infos == nil {
return nil
}
2019-06-27 11:43:07 -07:00
for _, info := range infos {
return b.processViolation(info)
}
return nil
}
2019-06-27 11:43:07 -07:00
func (b *builder) processViolation(info *Info) error {
statusMap := map[string]interface{}{}
2019-07-18 10:22:20 -07:00
violationsMap := map[string]interface{}{}
violationMap := map[string]interface{}{}
var violations interface{}
var violation interface{}
// Get Policy
obj, err := b.client.GetResource("Policy", "", info.Policy, "status")
if err != nil {
2019-04-30 17:13:44 -07:00
return err
}
2019-07-18 10:22:20 -07:00
unstr := obj.UnstructuredContent()
// get "status" subresource
status, ok := unstr["status"]
if ok {
2019-07-18 10:22:20 -07:00
// status exists
// status is already present then we append violations
if statusMap, ok = status.(map[string]interface{}); !ok {
return errors.New("Unable to parse status subresource")
}
2019-07-18 10:22:20 -07:00
// get policy violations
violations, ok = statusMap["violations"]
if !ok {
2019-07-18 10:22:20 -07:00
return nil
}
2019-07-18 10:22:20 -07:00
violationsMap, ok = violations.(map[string]interface{})
if !ok {
2019-07-18 10:22:20 -07:00
return errors.New("Unable to get status.violations subresource")
}
2019-07-18 10:22:20 -07:00
// check if the resource has a violation
violation, ok = violationsMap[info.getKey()]
if !ok {
2019-07-18 10:22:20 -07:00
// add resource violation
violationsMap[info.getKey()] = info.Violation
statusMap["violations"] = violationsMap
unstr["status"] = statusMap
} else {
violationMap, ok = violation.(map[string]interface{})
if !ok {
return errors.New("Unable to get status.violations.violation subresource")
}
// we check if the new violation updates are different from stored violation info
v := v1alpha1.Violation{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(violationMap, &v)
if err != nil {
return err
}
// compare v & info.Violation
if v.IsEqual(info.Violation) {
// no updates to violation
// do nothing
return nil
}
// update the violation
violationsMap[info.getKey()] = info.Violation
statusMap["violations"] = violationsMap
unstr["status"] = statusMap
}
2019-07-18 10:22:20 -07:00
} else {
violationsMap[info.getKey()] = info.Violation
statusMap["violations"] = violationsMap
unstr["status"] = statusMap
}
obj.SetUnstructuredContent(unstr)
// update the status sub-resource for policy
_, err = b.client.UpdateStatusResource("Policy", "", obj, false)
if err != nil {
return err
}
return nil
}
func (b *builder) RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error {
statusMap := map[string]interface{}{}
violationsMap := map[string]interface{}{}
violationMap := map[string]interface{}{}
var violations interface{}
var violation interface{}
// Get Policy
obj, err := b.client.GetResource("Policy", "", policy, "status")
if err != nil {
return err
}
unstr := obj.UnstructuredContent()
// get "status" subresource
status, ok := unstr["status"]
if !ok {
return nil
}
// status exists
// status is already present then we append violations
if statusMap, ok = status.(map[string]interface{}); !ok {
return errors.New("Unable to parse status subresource")
}
// get policy violations
violations, ok = statusMap["violations"]
if !ok {
return nil
}
violationsMap, ok = violations.(map[string]interface{})
if !ok {
return errors.New("Unable to get status.violations subresource")
}
// check if the resource has a violation
violation, ok = violationsMap[BuildKey(rKind, rNs, rName)]
if !ok {
// no violation for this resource
return nil
}
violationMap, ok = violation.(map[string]interface{})
if !ok {
return errors.New("Unable to get status.violations.violation subresource")
}
// check remove the rules of the given type
// this is called when the policy is applied succesfully, so we can remove the previous failed rules
// if all rules are to be removed, the deleted the violation
v := v1alpha1.Violation{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(violationMap, &v)
if err != nil {
return err
}
if !v.RemoveRulesOfType(ruleType.String()) {
// no rule of given type found,
// no need to remove rule
return nil
}
// if there are no faile rules remove the violation
if len(v.Rules) == 0 {
delete(violationsMap, BuildKey(rKind, rNs, rName))
} else {
// update the rules
violationsMap[BuildKey(rKind, rNs, rName)] = v
}
2019-07-18 10:22:20 -07:00
statusMap["violations"] = violationsMap
unstr["status"] = statusMap
2019-07-18 10:22:20 -07:00
obj.SetUnstructuredContent(unstr)
// update the status sub-resource for policy
_, err = b.client.UpdateStatusResource("Policy", "", obj, false)
2019-05-10 00:05:21 -07:00
if err != nil {
return err
}
return nil
2019-04-30 17:13:44 -07:00
}
2019-07-18 10:22:20 -07:00
func (b *builder) ResourceRemoval(policy, rKind, rNs, rName string) error {
statusMap := map[string]interface{}{}
violationsMap := map[string]interface{}{}
var violations interface{}
// Get Policy
obj, err := b.client.GetResource("Policy", "", policy, "status")
if err != nil {
return err
}
unstr := obj.UnstructuredContent()
// get "status" subresource
status, ok := unstr["status"]
if !ok {
return nil
}
// status exists
// status is already present then we append violations
if statusMap, ok = status.(map[string]interface{}); !ok {
return errors.New("Unable to parse status subresource")
}
// get policy violations
violations, ok = statusMap["violations"]
if !ok {
return nil
}
violationsMap, ok = violations.(map[string]interface{})
if !ok {
return errors.New("Unable to get status.violations subresource")
}
// check if the resource has a violation
_, ok = violationsMap[BuildKey(rKind, rNs, rName)]
if !ok {
// no violation for this resource
return nil
}
// remove the pair from the map
delete(violationsMap, BuildKey(rKind, rNs, rName))
if len(violationsMap) == 0 {
delete(statusMap, "violations")
} else {
statusMap["violations"] = violationsMap
}
unstr["status"] = statusMap
obj.SetUnstructuredContent(unstr)
// update the status sub-resource for policy
_, err = b.client.UpdateStatusResource("Policy", "", obj, false)
if err != nil {
return err
}
return nil
}
// func (b *builder) processViolation(info *Info) error {
// currVs := map[string]interface{}{}
// statusMap := map[string]interface{}{}
// var ok bool
// //TODO: hack get from client
// p1, err := b.client.GetResource("Policy", "", info.Policy, "status")
// if err != nil {
// return err
// }
// unstr := p1.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")
// }
// // Violations map[string][]Violations
// glog.Info(reflect.TypeOf(violations))
// if currVs, ok = violations.(map[string]interface{}); !ok {
// return errors.New("Unable to parse violations")
// }
// }
// // Info:
// // Key - Kind, Namespace, Name
// // policy - Name
// // violation, ok := currVs[info.getKey()]
// // Key -> resource
// // 1> Check if there were any previous violations for the given key
// // 2> If No, create a new one
// if !ok {
// currVs[info.getKey()] = info.Violation
// } else {
// currV := currVs[info.getKey()]
// 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")
// }
// // check if rules are samre
// if isRuleNamesEqual(rs, info.Violation.Rules) {
// return nil
// }
// // else update the errors
// currVs[info.getKey()] = info.Violation
// }
// // newViolation := info.Violation
// // for _, violation := range currentViolations {
// // glog.Info(reflect.TypeOf(violation))
// // if v, ok := violation.(map[string]interface{}); ok {
// // if name, ok := v["name"].(string); ok {
// // if namespace, ok := v["namespace"].(string); ok {
// // ok, err := b.isActive(info.Kind, name, namespace)
// // if err != nil {
// // glog.Error(err)
// // continue
// // }
// // if !ok {
// // //TODO remove the violation as it corresponds to resource that does not exist
// // glog.Info("removed violation")
// // }
// // }
// // }
// // }
// // }
// // currentViolations = append(currentViolations, newViolation)
// // // update violations
// // set the updated status
// statusMap["violations"] = currVs
// unstr["status"] = statusMap
// p1.SetUnstructuredContent(unstr)
// _, err = b.client.UpdateStatusResource("Policy", "", p1, false)
// if err != nil {
// return err
// }
// return nil
// }
2019-07-08 17:51:37 -07:00
func (b *builder) isActive(kind, rname, rnamespace string) (bool, error) {
// Generate Merge Patch
_, err := b.client.GetResource(b.client.DiscoveryClient.GetGVRFromKind(kind).Resource, rnamespace, rname)
if err != nil {
glog.Errorf("unable to get resource %s/%s ", rnamespace, rname)
2019-04-30 17:13:44 -07:00
return false, err
}
2019-04-30 17:13:44 -07:00
return true, nil
}
2019-05-10 12:36:55 -07:00
//NewViolation return new policy violation
2019-07-08 17:51:37 -07:00
func NewViolation(reason event.Reason, policyName, kind, rname, rnamespace, msg string) *Info {
return &Info{Policy: policyName,
2019-07-15 14:49:22 -07:00
Violation: v1alpha1.Violation{
2019-06-26 18:02:01 -07:00
Kind: kind,
Name: rname,
Namespace: rnamespace,
2019-07-08 17:51:37 -07:00
Reason: reason.String(),
2019-06-26 18:02:01 -07:00
},
2019-05-10 12:36:55 -07:00
}
}
2019-06-27 11:43:07 -07:00
// //NewViolationFromEvent returns violation info from event
// func NewViolationFromEvent(e *event.Info, pName, rKind, rName, rnamespace string) *Info {
// return &Info{
// Policy: pName,
// Violation: types.Violation{
// Kind: rKind,
// Name: rName,
// Namespace: rnamespace,
// Reason: e.Reason,
// Message: e.Message,
// },
// }
// }
// Build a new Violation
2019-07-15 14:49:22 -07:00
func BuldNewViolation(pName string, rKind string, rNs string, rName string, reason string, frules []v1alpha1.FailedRule) *Info {
2019-06-27 11:43:07 -07:00
return &Info{
Policy: pName,
2019-07-15 14:49:22 -07:00
Violation: v1alpha1.Violation{
2019-06-27 11:43:07 -07:00
Kind: rKind,
Namespace: rNs,
2019-06-27 11:43:07 -07:00
Name: rName,
Reason: reason,
2019-07-15 14:49:22 -07:00
Rules: frules,
2019-06-27 11:43:07 -07:00
},
}
}
2019-07-15 14:49:22 -07:00
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 {
2019-07-15 14:49:22 -07:00
glog.Info(reflect.TypeOf(r))
rfule, ok := r.(map[string]interface{})
if !ok {
return false
}
2019-07-15 14:49:22 -07:00
glog.Info(reflect.TypeOf(rfule["name"]))
2019-07-16 15:53:14 -07:00
// name
2019-07-15 14:49:22 -07:00
name, ok := rfule["name"].(string)
if !ok {
return false
}
if name != newRules[i].Name {
return false
}
2019-07-16 15:53:14 -07:00
// type
rtype, ok := rfule["type"].(string)
if !ok {
return false
}
if rtype != newRules[i].Type {
return false
}
}
return true
}
2019-07-18 10:22:20 -07:00
// //RemoveViolation will remove the violation for the resource if there was one
// func (b *builder) RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error {
// // Remove the <resource, Violation> pair from map
// 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()
2019-07-15 14:49:22 -07:00
2019-07-18 10:22:20 -07:00
// // 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
// }
2019-07-18 10:22:20 -07:00
// func (b *builder) ResourceRemoval(policy, rKind, rNs, rName string) error {
// // Remove the <resource, Violation> pair from map
// 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")
// }
// _, ok = currVs[BuildKey(rKind, rNs, rName)]
// if !ok {
// // No Violation for this resource
// return nil
// }
// // remove the pair from the map
// delete(currVs, BuildKey(rKind, rNs, rName))
// glog.Info("Removed Violation")
// // 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
// }