1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 23:46:56 +00:00
kyverno/pkg/webhooks/updaterequest/generator.go
shuting 637f830917
feat: apply generate rules on trigger events (#6508)
* - fire generation on trigger deletion, with condition rules;
- delete downstream if trigger no longer matches;
- delete downstream if trigger is deleted, with sync rule

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* trim condition key spaces

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix UR spec

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-create-on-trigger-deletion

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-create-on-trigger-deletion

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-data-sync-delete-trigger

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-data-nosync-delete-trigger

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-data-sync-update-trigger-no-match

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* rename policy

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-data-nosync-update-trigger-no-match

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add debug logs

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-clone-create-on-trigger-deletion

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* update readme

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-clone-sync-delete-trigger

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-clone-nosync-delete-trigger

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-clone-sync-update-trigger-no-match

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* update readme

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add a kuttl test cpol-clone-nosync-update-trigger-no-match

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
2023-03-10 17:17:10 +00:00

118 lines
4.3 KiB
Go

package updaterequest
import (
"context"
"time"
backoff "github.com/cenkalti/backoff"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/background/common"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernov1beta1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1beta1"
kyvernov1beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/config"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)
// Generator provides interface to manage update requests
type Generator interface {
Apply(context.Context, kyvernov1beta1.UpdateRequestSpec) error
}
// generator defines the implementation to manage update request resource
type generator struct {
// clients
client versioned.Interface
// listers
urLister kyvernov1beta1listers.UpdateRequestNamespaceLister
}
// NewGenerator returns a new instance of UpdateRequest resource generator
func NewGenerator(client versioned.Interface, urInformer kyvernov1beta1informers.UpdateRequestInformer) Generator {
return &generator{
client: client,
urLister: urInformer.Lister().UpdateRequests(config.KyvernoNamespace()),
}
}
// Apply creates update request resource
func (g *generator) Apply(ctx context.Context, ur kyvernov1beta1.UpdateRequestSpec) error {
logger.V(4).Info("apply Update Request", "request", ur)
go g.applyResource(context.TODO(), ur)
return nil
}
func (g *generator) applyResource(ctx context.Context, urSpec kyvernov1beta1.UpdateRequestSpec) {
exbackoff := &backoff.ExponentialBackOff{
InitialInterval: 500 * time.Millisecond,
RandomizationFactor: 0.5,
Multiplier: 1.5,
MaxInterval: time.Second,
MaxElapsedTime: 3 * time.Second,
Clock: backoff.SystemClock,
}
exbackoff.Reset()
if err := backoff.Retry(func() error { return g.tryApplyResource(ctx, urSpec) }, exbackoff); err != nil {
logger.Error(err, "failed to update request CR")
}
}
func (g *generator) tryApplyResource(ctx context.Context, urSpec kyvernov1beta1.UpdateRequestSpec) error {
l := logger.WithValues("ruleType", urSpec.GetRequestType(), "resource", urSpec.GetResource().String())
var queryLabels labels.Set
if urSpec.GetRequestType() == kyvernov1beta1.Mutate {
queryLabels = common.MutateLabelsSet(urSpec.Policy, urSpec.GetResource())
} else if urSpec.GetRequestType() == kyvernov1beta1.Generate {
queryLabels = common.GenerateLabelsSet(urSpec.Policy, urSpec.GetResource())
}
urList, err := g.urLister.List(labels.SelectorFromSet(queryLabels))
if err != nil {
l.Error(err, "failed to get update request for the resource", "resource", urSpec.GetResource().String())
return err
}
for _, v := range urList {
l := l.WithValues("name", v.GetName())
l.V(4).Info("updating existing update request")
if _, err := common.Update(g.client, g.urLister, v.GetName(), func(ur *kyvernov1beta1.UpdateRequest) {
v.Spec = urSpec
}); err != nil {
l.V(4).Error(err, "failed to update UpdateRequest")
return err
} else {
l.V(4).Info("successfully updated UpdateRequest")
}
if _, err := common.UpdateStatus(g.client, g.urLister, v.GetName(), kyvernov1beta1.Pending, "", nil); err != nil {
l.V(4).Error(err, "failed to update UpdateRequest status")
return err
}
}
if len(urList) == 0 || urSpec.DeleteDownstream {
l.V(4).Info("creating new UpdateRequest")
ur := kyvernov1beta1.UpdateRequest{
ObjectMeta: metav1.ObjectMeta{
Namespace: config.KyvernoNamespace(),
GenerateName: "ur-",
Labels: queryLabels,
},
Spec: urSpec,
}
created, err := g.client.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Create(ctx, &ur, metav1.CreateOptions{})
if err != nil {
l.V(4).Error(err, "failed to create UpdateRequest, retrying", "name", ur.GetGenerateName(), "namespace", ur.GetNamespace())
return err
}
updated := created.DeepCopy()
updated.Status.State = kyvernov1beta1.Pending
_, err = g.client.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{})
if err != nil {
return err
}
l.V(4).Info("successfully created UpdateRequest", "name", updated.GetName(), "namespace", ur.GetNamespace())
}
return nil
}