1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 10:28:36 +00:00

refactor: cleanup controller validating webhook (#5756)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-12-22 07:13:32 +01:00 committed by GitHub
parent 0f5a0d492f
commit 3c997d88a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 203 deletions

View file

@ -1,190 +0,0 @@
package main
import (
"context"
"fmt"
"time"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/controllers"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/tls"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
corev1informers "k8s.io/client-go/informers/core/v1"
admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/util/workqueue"
)
const (
// Workers is the number of workers for this controller
Workers = 2
ControllerName = "webhook-controller"
maxRetries = 10
managedByLabel = "webhook.kyverno.io/managed-by"
)
var (
none = admissionregistrationv1.SideEffectClassNone
fail = admissionregistrationv1.Fail
)
var logger = logging.ControllerLogger(ControllerName)
type controller struct {
// clients
vwcClient controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration]
// listers
vwcLister admissionregistrationv1listers.ValidatingWebhookConfigurationLister
secretLister corev1listers.SecretNamespaceLister
// queue
queue workqueue.RateLimitingInterface
// config
webhookName string
server string
}
func NewController(
vwcClient controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration],
vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
secretInformer corev1informers.SecretInformer,
webhookName string,
server string,
) controllers.Controller {
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), ControllerName)
c := controller{
vwcClient: vwcClient,
vwcLister: vwcInformer.Lister(),
secretLister: secretInformer.Lister().Secrets(config.KyvernoNamespace()),
queue: queue,
webhookName: webhookName,
server: server,
}
controllerutils.AddDefaultEventHandlers(logger, vwcInformer.Informer(), queue)
controllerutils.AddEventHandlersT(
secretInformer.Informer(),
func(obj *corev1.Secret) {
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
c.enqueue()
}
},
func(_, obj *corev1.Secret) {
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
c.enqueue()
}
},
func(obj *corev1.Secret) {
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == tls.GenerateRootCASecretName() {
c.enqueue()
}
},
)
return &c
}
func (c *controller) Run(ctx context.Context, workers int) {
c.enqueue()
controllerutils.Run(ctx, logger, ControllerName, time.Second, c.queue, workers, maxRetries, c.reconcile)
}
func (c *controller) enqueue() {
c.queue.Add(c.webhookName)
}
func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, _, _ string) error {
if key != c.webhookName {
return nil
}
caData, err := tls.ReadRootCASecret(c.secretLister)
if err != nil {
return err
}
desired, err := c.build(caData)
if err != nil {
return err
}
observed, err := c.vwcLister.Get(desired.Name)
if err != nil {
if apierrors.IsNotFound(err) {
_, err := c.vwcClient.Create(ctx, desired, metav1.CreateOptions{})
return err
}
return err
}
_, err = controllerutils.Update(ctx, observed, c.vwcClient, func(w *admissionregistrationv1.ValidatingWebhookConfiguration) error {
w.Labels = desired.Labels
w.OwnerReferences = desired.OwnerReferences
w.Webhooks = desired.Webhooks
return nil
})
return err
}
func objectMeta(name string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
managedByLabel: kyvernov1.ValueKyvernoApp,
},
OwnerReferences: owner,
}
}
func (c *controller) build(caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
return &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: objectMeta(c.webhookName),
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
Name: fmt.Sprintf("%s.%s.svc", config.KyvernoServiceName(), config.KyvernoNamespace()),
ClientConfig: c.clientConfig(caBundle, validatingWebhookServicePath),
Rules: []admissionregistrationv1.RuleWithOperations{{
Rule: admissionregistrationv1.Rule{
APIGroups: []string{
"kyverno.io",
},
APIVersions: []string{
"v2alpha1",
},
Resources: []string{
"cleanuppolicies/*",
"clustercleanuppolicies/*",
},
},
Operations: []admissionregistrationv1.OperationType{
admissionregistrationv1.Create,
admissionregistrationv1.Update,
},
}},
FailurePolicy: &fail,
SideEffects: &none,
AdmissionReviewVersions: []string{"v1"},
}},
},
nil
}
func (c *controller) clientConfig(caBundle []byte, path string) admissionregistrationv1.WebhookClientConfig {
clientConfig := admissionregistrationv1.WebhookClientConfig{
CABundle: caBundle,
}
if c.server == "" {
clientConfig.Service = &admissionregistrationv1.ServiceReference{
Namespace: config.KyvernoNamespace(),
Name: config.KyvernoServiceName(),
Path: &path,
}
} else {
url := fmt.Sprintf("https://%s%s", c.server, path)
clientConfig.URL = &url
}
return clientConfig
}

View file

@ -18,16 +18,20 @@ import (
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/controllers/certmanager"
"github.com/kyverno/kyverno/pkg/controllers/cleanup"
genericwebhookcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/webhook"
"github.com/kyverno/kyverno/pkg/leaderelection"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/tls"
"github.com/kyverno/kyverno/pkg/webhooks"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
kubeinformers "k8s.io/client-go/informers"
)
const (
resyncPeriod = 15 * time.Minute
resyncPeriod = 15 * time.Minute
webhookWorkers = 2
webhookControllerName = "webhook-controller"
)
// TODO:
@ -49,10 +53,12 @@ func main() {
var (
leaderElectionRetryPeriod time.Duration
dumpPayload bool
serverIP string
)
flagset := flag.NewFlagSet("cleanup-controller", flag.ExitOnError)
flagset.BoolVar(&dumpPayload, "dumpPayload", false, "Set this flag to activate/deactivate debug mode.")
flagset.DurationVar(&leaderElectionRetryPeriod, "leaderElectionRetryPeriod", leaderelection.DefaultRetryPeriod, "Configure leader election retry period.")
flagset.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
// config
appConfig := internal.NewConfiguration(
internal.WithProfiling(),
@ -109,15 +115,33 @@ func main() {
certmanager.Workers,
)
webhookController := internal.NewController(
ControllerName,
NewController(
webhookControllerName,
genericwebhookcontroller.NewController(
webhookControllerName,
kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
kubeKyvernoInformer.Core().V1().Secrets(),
"kyverno-cleanup-policies",
"",
config.CleanupValidatingWebhookConfigurationName,
config.CleanupValidatingWebhookServicePath,
serverIP,
[]admissionregistrationv1.RuleWithOperations{{
Rule: admissionregistrationv1.Rule{
APIGroups: []string{"kyverno.io"},
APIVersions: []string{"v2alpha1"},
Resources: []string{
"cleanuppolicies/*",
"clustercleanuppolicies/*",
},
},
Operations: []admissionregistrationv1.OperationType{
admissionregistrationv1.Create,
admissionregistrationv1.Update,
},
}},
genericwebhookcontroller.Fail,
genericwebhookcontroller.None,
),
Workers,
webhookWorkers,
)
cleanupController := internal.NewController(
cleanup.ControllerName,

View file

@ -18,11 +18,6 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
)
const (
// validatingWebhookServicePath is the path for validation webhook
validatingWebhookServicePath = "/validate"
)
type Server interface {
// Run TLS server in separate thread and returns control immediately
Run(<-chan struct{})
@ -73,7 +68,7 @@ func NewServer(
mux := httprouter.New()
mux.HandlerFunc(
"POST",
validatingWebhookServicePath,
config.CleanupValidatingWebhookServicePath,
handlers.FromAdmissionFunc("VALIDATE", validationHandler).
WithDump(debugModeOpts.DumpPayload).
WithSubResourceFilter().

View file

@ -21,8 +21,10 @@ const (
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
// ValidatingWebhookConfigurationName ...
ValidatingWebhookConfigurationName = "kyverno-resource-validating-webhook-cfg"
// PolicyValidatingWebhookConfigurationName default policy validating webhook configuration name
// ExceptionValidatingWebhookConfigurationName ...
ExceptionValidatingWebhookConfigurationName = "kyverno-exception-validating-webhook-cfg"
// CleanupValidatingWebhookConfigurationName ...
CleanupValidatingWebhookConfigurationName = "kyverno-cleanup-validating-webhook-cfg"
// PolicyMutatingWebhookConfigurationName default policy mutating webhook configuration name
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
// MutatingWebhookConfigurationName default resource mutating webhook configuration name
@ -53,6 +55,8 @@ const (
ValidatingWebhookServicePath = "/validate"
// ExceptionValidatingWebhookServicePath is the path for policy exception validation webhook(used to validate policy exception resource)
ExceptionValidatingWebhookServicePath = "/exceptionvalidate"
// CleanupValidatingWebhookServicePath is the path for cleanup policy validation webhook(used to validate cleanup policy resource)
CleanupValidatingWebhookServicePath = "/validate"
// PolicyMutatingWebhookServicePath is the path for policy mutation webhook(used to default)
PolicyMutatingWebhookServicePath = "/policymutate"
// MutatingWebhookServicePath is the path for mutation webhook