1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/controllers/exceptions/controller.go
Mariam Fahmy 94d9bbe73f
chore: use v2 clients for policy exceptions (#10530)
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
2024-06-24 16:36:55 +00:00

188 lines
5.2 KiB
Go

package exceptions
import (
"cmp"
"context"
"slices"
"sync"
"time"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
"github.com/kyverno/kyverno/pkg/autogen"
kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
kyvernov2informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v2"
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
kyvernov2listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/util/workqueue"
)
type ruleIndex = map[string][]*kyvernov2.PolicyException
type policyIndex = map[string]ruleIndex
type controller struct {
// listers
cpolLister kyvernov1listers.ClusterPolicyLister
polLister kyvernov1listers.PolicyLister
polexLister kyvernov2listers.PolicyExceptionLister
// queue
queue workqueue.RateLimitingInterface
// state
lock sync.RWMutex
index policyIndex
namespace string
}
const (
maxRetries = 10
Workers = 3
ControllerName = "exceptions-controller"
)
func NewController(
cpolInformer kyvernov1informers.ClusterPolicyInformer,
polInformer kyvernov1informers.PolicyInformer,
polexInformer kyvernov2informers.PolicyExceptionInformer,
namespace string,
) *controller {
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), ControllerName)
if _, _, err := controllerutils.AddDefaultEventHandlers(logger, cpolInformer.Informer(), queue); err != nil {
logger.Error(err, "failed to register event handlers")
}
if _, _, err := controllerutils.AddDefaultEventHandlers(logger, polInformer.Informer(), queue); err != nil {
logger.Error(err, "failed to register event handlers")
}
c := &controller{
cpolLister: cpolInformer.Lister(),
polLister: polInformer.Lister(),
polexLister: polexInformer.Lister(),
queue: queue,
index: policyIndex{},
namespace: namespace,
}
if _, err := controllerutils.AddEventHandlersT(polexInformer.Informer(), c.addPolex, c.updatePolex, c.deletePolex); err != nil {
logger.Error(err, "failed to register event handlers")
}
return c
}
func (c *controller) Run(ctx context.Context, workers int) {
controllerutils.Run(ctx, logger.V(3), ControllerName, time.Second, c.queue, workers, maxRetries, c.reconcile)
}
func (c *controller) Find(policyName string, ruleName string) ([]*kyvernov2.PolicyException, error) {
c.lock.RLock()
defer c.lock.RUnlock()
return c.index[policyName][ruleName], nil
}
func (c *controller) addPolex(polex *kyvernov2.PolicyException) {
names := sets.New[string]()
for _, ex := range polex.Spec.Exceptions {
names.Insert(ex.PolicyName)
}
for name := range names {
c.queue.Add(name)
}
}
func (c *controller) updatePolex(old *kyvernov2.PolicyException, new *kyvernov2.PolicyException) {
names := sets.New[string]()
for _, ex := range old.Spec.Exceptions {
names.Insert(ex.PolicyName)
}
for _, ex := range new.Spec.Exceptions {
names.Insert(ex.PolicyName)
}
for name := range names {
c.queue.Add(name)
}
}
func (c *controller) deletePolex(polex *kyvernov2.PolicyException) {
names := sets.New[string]()
for _, ex := range polex.Spec.Exceptions {
names.Insert(ex.PolicyName)
}
for name := range names {
c.queue.Add(name)
}
}
func (c *controller) getPolicy(namespace, name string) (kyvernov1.PolicyInterface, error) {
if namespace == "" {
cpolicy, err := c.cpolLister.Get(name)
if err != nil {
return nil, err
}
return cpolicy, nil
} else {
policy, err := c.polLister.Policies(namespace).Get(name)
if err != nil {
return nil, err
}
return policy, nil
}
}
func (c *controller) listExceptions() ([]*kyvernov2.PolicyException, error) {
if c.namespace == "" {
return c.polexLister.List(labels.Everything())
}
return c.polexLister.PolicyExceptions(c.namespace).List(labels.Everything())
}
func (c *controller) buildRuleIndex(key string, policy kyvernov1.PolicyInterface) (ruleIndex, error) {
polexList, err := c.listExceptions()
if err != nil {
return nil, err
}
slices.SortFunc(polexList, func(a, b *kyvernov2.PolicyException) int {
if cmp := cmp.Compare(a.Namespace, b.Namespace); cmp != 0 {
return cmp
}
if cmp := cmp.Compare(a.Name, b.Name); cmp != 0 {
return cmp
}
return 0
})
index := ruleIndex{}
for _, rule := range autogen.ComputeRules(policy, "") {
for _, polex := range polexList {
if polex.Contains(key, rule.Name) {
index[rule.Name] = append(index[rule.Name], polex)
}
}
}
return index, nil
}
func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error {
policy, err := c.getPolicy(namespace, name)
if err != nil {
if !apierrors.IsNotFound(err) {
logger.Error(err, "unable to get the policy from policy informer")
return err
}
c.lock.Lock()
defer c.lock.Unlock()
delete(c.index, key)
return nil
}
ruleIndex, err := c.buildRuleIndex(key, policy)
if err != nil {
return err
}
c.lock.Lock()
defer c.lock.Unlock()
c.index[key] = ruleIndex
return nil
}