mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 07:57:07 +00:00
147 lines
5 KiB
Go
147 lines
5 KiB
Go
package background
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
"github.com/go-logr/logr"
|
|
urkyverno "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
|
"github.com/kyverno/kyverno/pkg/background/common"
|
|
"github.com/kyverno/kyverno/pkg/background/generate"
|
|
"github.com/kyverno/kyverno/pkg/background/mutate"
|
|
"github.com/kyverno/kyverno/pkg/config"
|
|
dclient "github.com/kyverno/kyverno/pkg/dclient"
|
|
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
|
"github.com/pkg/errors"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/util/retry"
|
|
)
|
|
|
|
func (c *Controller) ProcessUR(ur *urkyverno.UpdateRequest) error {
|
|
switch ur.Spec.Type {
|
|
case urkyverno.Mutate:
|
|
ctrl, _ := mutate.NewMutateExistingController(c.kyvernoClient, c.client,
|
|
c.policyLister, c.npolicyLister, c.urLister, c.eventGen, c.log, c.Config)
|
|
return ctrl.ProcessUR(ur)
|
|
|
|
case urkyverno.Generate:
|
|
ctrl, _ := generate.NewGenerateController(c.kyvernoClient, c.client,
|
|
c.policyLister, c.npolicyLister, c.urLister, c.eventGen, c.nsLister, c.log, c.Config,
|
|
)
|
|
return ctrl.ProcessUR(ur)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Controller) MarkUR(ur *urkyverno.UpdateRequest) (*urkyverno.UpdateRequest, bool, error) {
|
|
handler := ur.Status.Handler
|
|
if handler != "" {
|
|
if handler != config.KyvernoPodName() {
|
|
return nil, false, nil
|
|
}
|
|
return ur, true, nil
|
|
}
|
|
handler = config.KyvernoPodName()
|
|
ur.Status.Handler = handler
|
|
var updateRequest *urkyverno.UpdateRequest
|
|
|
|
err := retry.RetryOnConflict(common.DefaultRetry, func() error {
|
|
var retryError error
|
|
updateRequest, retryError = c.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).UpdateStatus(context.TODO(), ur, metav1.UpdateOptions{})
|
|
return retryError
|
|
})
|
|
|
|
return updateRequest, true, err
|
|
}
|
|
|
|
func (c *Controller) UnmarkUR(ur *urkyverno.UpdateRequest) error {
|
|
_, err := c.PatchHandler(ur, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ur.Spec.Type == urkyverno.Mutate && ur.Status.State == urkyverno.Completed {
|
|
return c.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), ur.GetName(), metav1.DeleteOptions{})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Controller) PatchHandler(ur *urkyverno.UpdateRequest, val string) (*urkyverno.UpdateRequest, error) {
|
|
patch := jsonutils.NewPatch(
|
|
"/status/handler",
|
|
"replace",
|
|
val,
|
|
)
|
|
|
|
updateUR, err := common.PatchUpdateRequest(ur, patch, c.kyvernoClient, "status")
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
c.log.Error(err, "failed to patch UpdateRequest: %v", patch)
|
|
if val == "" {
|
|
return nil, errors.Wrapf(err, "failed to patch UpdateRequest to clear /status/handler")
|
|
}
|
|
return nil, errors.Wrapf(err, "failed to patch UpdateRequest to update /status/handler to %s", val)
|
|
}
|
|
return updateUR, nil
|
|
}
|
|
|
|
func (c *Controller) HandleDeleteUR(ur urkyverno.UpdateRequest) error {
|
|
logger := c.log.WithValues("kind", ur.Kind, "namespace", ur.Namespace, "name", ur.Name)
|
|
// 1- Corresponding policy has been deleted
|
|
// then we don't delete the generated resources
|
|
|
|
// 2- The trigger resource is deleted, then delete the generated resources
|
|
if !ownerResourceExists(logger, c.client, ur) {
|
|
deleteUR := false
|
|
// check retry count in annotaion
|
|
urAnnotations := ur.Annotations
|
|
if val, ok := urAnnotations["generate.kyverno.io/retry-count"]; ok {
|
|
retryCount, err := strconv.ParseUint(val, 10, 32)
|
|
if err != nil {
|
|
logger.Error(err, "unable to convert retry-count")
|
|
return err
|
|
}
|
|
|
|
if retryCount >= 5 {
|
|
deleteUR = true
|
|
}
|
|
}
|
|
|
|
if deleteUR {
|
|
if err := deleteGeneratedResources(logger, c.client, ur); err != nil {
|
|
return err
|
|
}
|
|
// - trigger-resource is deleted
|
|
// - generated-resources are deleted
|
|
// - > Now delete the UpdateRequest CR
|
|
return c.kyvernoClient.KyvernoV1beta1().UpdateRequests(config.KyvernoNamespace()).Delete(context.TODO(), ur.Name, metav1.DeleteOptions{})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ownerResourceExists(log logr.Logger, client dclient.Interface, ur urkyverno.UpdateRequest) bool {
|
|
_, err := client.GetResource("", ur.Spec.Resource.Kind, ur.Spec.Resource.Namespace, ur.Spec.Resource.Name)
|
|
// trigger resources has been deleted
|
|
if apierrors.IsNotFound(err) {
|
|
return false
|
|
}
|
|
if err != nil {
|
|
log.Error(err, "failed to get resource", "genKind", ur.Spec.Resource.Kind, "genNamespace", ur.Spec.Resource.Namespace, "genName", ur.Spec.Resource.Name)
|
|
}
|
|
// if there was an error while querying the resources we don't delete the generated resources
|
|
// but expect the deletion in next reconciliation loop
|
|
return true
|
|
}
|
|
|
|
func deleteGeneratedResources(log logr.Logger, client dclient.Interface, ur urkyverno.UpdateRequest) error {
|
|
for _, genResource := range ur.Status.GeneratedResources {
|
|
err := client.DeleteResource("", genResource.Kind, genResource.Namespace, genResource.Name, false)
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
|
|
log.V(3).Info("generated resource deleted", "genKind", ur.Spec.Resource.Kind, "genNamespace", ur.Spec.Resource.Namespace, "genName", ur.Spec.Resource.Name)
|
|
}
|
|
return nil
|
|
}
|