2024-04-03 18:56:48 +00:00
|
|
|
package exceptions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"cmp"
|
|
|
|
"context"
|
|
|
|
"slices"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2024-06-24 16:36:55 +00:00
|
|
|
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
|
2024-04-03 18:56:48 +00:00
|
|
|
"github.com/kyverno/kyverno/pkg/autogen"
|
|
|
|
kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
2024-06-24 16:36:55 +00:00
|
|
|
kyvernov2informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v2"
|
2024-04-03 18:56:48 +00:00
|
|
|
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
2024-06-24 16:36:55 +00:00
|
|
|
kyvernov2listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2"
|
2024-04-03 18:56:48 +00:00
|
|
|
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"
|
|
|
|
)
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
type ruleIndex = map[string][]*kyvernov2.PolicyException
|
2024-04-03 18:56:48 +00:00
|
|
|
|
|
|
|
type policyIndex = map[string]ruleIndex
|
|
|
|
|
|
|
|
type controller struct {
|
|
|
|
// listers
|
|
|
|
cpolLister kyvernov1listers.ClusterPolicyLister
|
|
|
|
polLister kyvernov1listers.PolicyLister
|
2024-06-24 16:36:55 +00:00
|
|
|
polexLister kyvernov2listers.PolicyExceptionLister
|
2024-04-03 18:56:48 +00:00
|
|
|
|
|
|
|
// 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,
|
2024-06-24 16:36:55 +00:00
|
|
|
polexInformer kyvernov2informers.PolicyExceptionInformer,
|
2024-04-03 18:56:48 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
func (c *controller) Find(policyName string, ruleName string) ([]*kyvernov2.PolicyException, error) {
|
2024-04-03 18:56:48 +00:00
|
|
|
c.lock.RLock()
|
|
|
|
defer c.lock.RUnlock()
|
|
|
|
return c.index[policyName][ruleName], nil
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
func (c *controller) addPolex(polex *kyvernov2.PolicyException) {
|
2024-04-03 18:56:48 +00:00
|
|
|
names := sets.New[string]()
|
|
|
|
for _, ex := range polex.Spec.Exceptions {
|
|
|
|
names.Insert(ex.PolicyName)
|
|
|
|
}
|
|
|
|
for name := range names {
|
|
|
|
c.queue.Add(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
func (c *controller) updatePolex(old *kyvernov2.PolicyException, new *kyvernov2.PolicyException) {
|
2024-04-03 18:56:48 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
func (c *controller) deletePolex(polex *kyvernov2.PolicyException) {
|
2024-04-03 18:56:48 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:36:55 +00:00
|
|
|
func (c *controller) listExceptions() ([]*kyvernov2.PolicyException, error) {
|
2024-04-03 18:56:48 +00:00
|
|
|
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
|
|
|
|
}
|
2024-06-24 16:36:55 +00:00
|
|
|
slices.SortFunc(polexList, func(a, b *kyvernov2.PolicyException) int {
|
2024-04-03 18:56:48 +00:00
|
|
|
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{}
|
2024-04-04 08:09:30 +00:00
|
|
|
for _, rule := range autogen.ComputeRules(policy, "") {
|
2024-04-03 18:56:48 +00:00
|
|
|
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
|
|
|
|
}
|