1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/controller/controller.go

315 lines
9.5 KiB
Go
Raw Normal View History

2019-05-13 18:17:28 -07:00
package controller
2019-05-10 00:05:21 -07:00
import (
"fmt"
"reflect"
2019-05-10 00:05:21 -07:00
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/nirmata/kyverno/pkg/annotations"
2019-06-26 12:19:11 -07:00
"github.com/nirmata/kyverno/pkg/info"
2019-06-25 23:58:28 -07:00
"github.com/nirmata/kyverno/pkg/engine"
2019-05-31 17:59:36 -07:00
"github.com/golang/glog"
2019-07-15 14:49:22 -07:00
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
2019-05-21 11:00:09 -07:00
lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/event"
2019-05-21 11:00:09 -07:00
"github.com/nirmata/kyverno/pkg/sharedinformer"
violation "github.com/nirmata/kyverno/pkg/violation"
2019-05-10 00:05:21 -07:00
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
2019-05-10 10:38:38 -07:00
//PolicyController to manage Policy CRD
2019-05-10 00:05:21 -07:00
type PolicyController struct {
client *client.Client
policyLister lister.PolicyLister
policySynced cache.InformerSynced
violationBuilder violation.Generator
eventController event.Generator
annotationsController annotations.Controller
queue workqueue.RateLimitingInterface
2019-05-10 00:05:21 -07:00
}
// NewPolicyController from cmd args
2019-05-15 11:24:27 -07:00
func NewPolicyController(client *client.Client,
2019-05-15 12:29:09 -07:00
policyInformer sharedinformer.PolicyInformer,
2019-05-13 18:17:28 -07:00
violationBuilder violation.Generator,
eventController event.Generator,
annotationsController annotations.Controller) *PolicyController {
2019-05-10 00:05:21 -07:00
controller := &PolicyController{
client: client,
policyLister: policyInformer.GetLister(),
policySynced: policyInformer.GetInfomer().HasSynced,
violationBuilder: violationBuilder,
eventController: eventController,
annotationsController: annotationsController,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName),
2019-05-10 00:05:21 -07:00
}
2019-05-15 12:29:09 -07:00
policyInformer.GetInfomer().AddEventHandler(cache.ResourceEventHandlerFuncs{
2019-05-10 00:05:21 -07:00
AddFunc: controller.createPolicyHandler,
UpdateFunc: controller.updatePolicyHandler,
DeleteFunc: controller.deletePolicyHandler,
})
return controller
}
func (pc *PolicyController) createPolicyHandler(resource interface{}) {
pc.enqueuePolicy(resource)
}
func (pc *PolicyController) updatePolicyHandler(oldResource, newResource interface{}) {
2019-07-15 14:49:22 -07:00
newPolicy := newResource.(*v1alpha1.Policy)
oldPolicy := oldResource.(*v1alpha1.Policy)
newPolicy.Status = v1alpha1.Status{}
oldPolicy.Status = v1alpha1.Status{}
newPolicy.ResourceVersion = ""
oldPolicy.ResourceVersion = ""
2019-07-15 14:49:22 -07:00
if reflect.DeepEqual(newPolicy, oldPolicy) {
2019-05-10 00:05:21 -07:00
return
}
pc.enqueuePolicy(newResource)
}
2019-05-10 10:38:38 -07:00
2019-05-10 00:05:21 -07:00
func (pc *PolicyController) deletePolicyHandler(resource interface{}) {
var object metav1.Object
var ok bool
if object, ok = resource.(metav1.Object); !ok {
glog.Error("error decoding object, invalid type")
2019-05-10 00:05:21 -07:00
return
}
//TODO: need to clear annotations on the resources
2019-07-17 23:13:28 -07:00
cleanAnnotations(pc.client, resource)
2019-05-31 17:59:36 -07:00
glog.Infof("policy deleted: %s", object.GetName())
2019-05-10 00:05:21 -07:00
}
func (pc *PolicyController) enqueuePolicy(obj interface{}) {
var key string
var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
glog.Error(err)
2019-05-10 00:05:21 -07:00
return
}
pc.queue.Add(key)
}
// Run is main controller thread
func (pc *PolicyController) Run(stopCh <-chan struct{}) error {
defer utilruntime.HandleCrash()
if ok := cache.WaitForCacheSync(stopCh, pc.policySynced); !ok {
return fmt.Errorf("failed to wait for caches to sync")
}
for i := 0; i < policyControllerWorkerCount; i++ {
go wait.Until(pc.runWorker, time.Second, stopCh)
}
2019-05-31 17:59:36 -07:00
glog.Info("started policy controller workers")
2019-05-10 00:05:21 -07:00
return nil
}
2019-06-05 17:43:59 -07:00
//Stop to perform actions when controller is stopped
func (pc *PolicyController) Stop() {
2019-06-20 16:50:54 -07:00
defer pc.queue.ShutDown()
2019-05-31 17:59:36 -07:00
glog.Info("shutting down policy controller workers")
}
2019-05-10 00:05:21 -07:00
func (pc *PolicyController) runWorker() {
for pc.processNextWorkItem() {
}
}
func (pc *PolicyController) processNextWorkItem() bool {
obj, shutdown := pc.queue.Get()
if shutdown {
return false
}
err := func(obj interface{}) error {
defer pc.queue.Done(obj)
err := pc.syncHandler(obj)
pc.handleErr(err, obj)
return nil
}(obj)
if err != nil {
glog.Error(err)
2019-05-10 00:05:21 -07:00
return true
}
return true
}
func (pc *PolicyController) handleErr(err error, key interface{}) {
if err == nil {
pc.queue.Forget(key)
return
}
2019-05-10 10:38:38 -07:00
// This controller retries if something goes wrong. After that, it stops trying.
2019-05-10 00:05:21 -07:00
if pc.queue.NumRequeues(key) < policyWorkQueueRetryLimit {
2019-05-31 17:59:36 -07:00
glog.Warningf("Error syncing events %v: %v", key, err)
2019-05-10 00:05:21 -07:00
// Re-enqueue the key rate limited. Based on the rate limiter on the
// queue and the re-enqueue history, the key will be processed later again.
pc.queue.AddRateLimited(key)
return
}
pc.queue.Forget(key)
glog.Error(err)
2019-06-05 17:43:59 -07:00
glog.Warningf("Dropping the key %q out of the queue: %v", key, err)
2019-05-10 00:05:21 -07:00
}
func (pc *PolicyController) syncHandler(obj interface{}) error {
var key string
var ok bool
if key, ok = obj.(string); !ok {
return fmt.Errorf("expected string in workqueue but got %#v", obj)
}
_, name, err := cache.SplitMetaNamespaceKey(key)
2019-05-10 00:05:21 -07:00
if err != nil {
glog.Errorf("invalid policy key: %s", key)
2019-05-10 00:05:21 -07:00
return nil
}
// Get Policy resource with namespace/name
policy, err := pc.policyLister.Get(name)
2019-05-10 00:05:21 -07:00
if err != nil {
if errors.IsNotFound(err) {
glog.Errorf("policy '%s' in work queue no longer exists", key)
2019-05-10 00:05:21 -07:00
return nil
}
return err
}
// process policy on existing resource
// get the violations and pass to violation Builder
// get the events and pass to event Builder
2019-05-15 11:24:27 -07:00
//TODO: processPolicy
2019-06-26 12:19:11 -07:00
glog.Infof("process policy %s on existing resources", policy.GetName())
2019-06-25 23:58:28 -07:00
policyInfos := engine.ProcessExisting(pc.client, policy)
2019-07-17 23:13:28 -07:00
events, _ := pc.createEventsAndViolations(policyInfos)
2019-06-27 11:43:07 -07:00
pc.eventController.Add(events...)
2019-07-17 23:13:28 -07:00
// err = pc.violationBuilder.Add(violations...)
// if err != nil {
// glog.Error(err)
// }
// add annotations to resources
pc.createAnnotations(policyInfos)
2019-06-26 12:19:11 -07:00
return nil
}
func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
for _, pi := range policyInfos {
var patch []byte
//get resource
obj, err := pc.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
if err != nil {
glog.Error(err)
continue
}
// add annotation for policy application
ann := obj.GetAnnotations()
// Mutation rules
ann, mpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation)
if err != nil {
glog.Error(err)
continue
}
// Validation rules
ann, vpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation)
if err != nil {
glog.Error(err)
}
if mpatch == nil && mpatch == nil {
//nothing to patch
continue
}
// merge the patches
if mpatch != nil && vpatch != nil {
patch, err = jsonpatch.MergePatch(mpatch, vpatch)
if err != nil {
glog.Error(err)
continue
}
}
if mpatch == nil {
patch = vpatch
} else {
2019-07-17 23:13:28 -07:00
patch = mpatch
}
2019-07-17 23:13:28 -07:00
// add the anotation to the resource
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
if err != nil {
glog.Error(err)
continue
}
}
}
2019-07-15 14:49:22 -07:00
func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) {
events := []*event.Info{}
2019-06-27 11:43:07 -07:00
violations := []*violation.Info{}
2019-06-25 23:58:28 -07:00
// Create events from the policyInfo
for _, policyInfo := range policyInfos {
2019-07-15 14:49:22 -07:00
frules := []v1alpha1.FailedRule{}
2019-06-26 12:19:11 -07:00
sruleNames := []string{}
2019-06-26 15:31:18 -07:00
for _, rule := range policyInfo.Rules {
if !rule.IsSuccessful() {
e := &event.Info{}
2019-07-15 14:49:22 -07:00
frule := v1alpha1.FailedRule{Name: rule.Name}
2019-06-26 15:31:18 -07:00
switch rule.RuleType {
case info.Mutation, info.Validation, info.Generation:
2019-06-27 11:43:07 -07:00
// Events
2019-06-26 15:31:18 -07:00
e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation, event.FProcessRule, rule.Name, policyInfo.Name)
2019-07-15 14:49:22 -07:00
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()
}
2019-06-26 15:31:18 -07:00
default:
glog.Info("Unsupported Rule type")
2019-06-26 12:19:11 -07:00
}
2019-07-15 14:49:22 -07:00
frules = append(frules, frule)
2019-06-26 15:31:18 -07:00
events = append(events, e)
} else {
2019-06-26 12:19:11 -07:00
sruleNames = append(sruleNames, rule.Name)
}
2019-06-26 15:31:18 -07:00
}
if !policyInfo.IsSuccessful() {
2019-07-15 14:49:22 -07:00
e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules))
2019-06-26 12:19:11 -07:00
events = append(events, e)
2019-06-27 11:43:07 -07:00
// Violation
// TODO: Violation is currently create at policy, level not resource level
// As we create violation, we check if the
2019-07-15 14:49:22 -07:00
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), frules)
2019-06-27 11:43:07 -07:00
violations = append(violations, v)
2019-07-15 14:49:22 -07:00
} 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)
2019-06-25 23:58:28 -07:00
}
2019-07-15 14:49:22 -07:00
2019-06-27 11:43:07 -07:00
// else {
// // Policy was processed succesfully
// e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyApplied, event.SPolicyApply, policyInfo.Name)
// events = append(events, e)
// // Policy applied succesfully on resource
// e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyApplied, event.SRuleApply, strings.Join(sruleNames, ";"), policyInfo.RName)
// events = append(events, e)
// }
2019-06-25 23:58:28 -07:00
}
2019-06-27 11:43:07 -07:00
return events, violations
2019-06-05 17:43:59 -07:00
}