From e3e160e384ed3d6734ede32609a4d060acf42e88 Mon Sep 17 00:00:00 2001 From: Abhijit Mukherjee Date: Wed, 21 Feb 2024 09:14:33 +0530 Subject: [PATCH] Updated validation webhook for blueprints Signed-off-by: Abhijit Mukherjee --- pkg/handler/handler.go | 21 ++++++---- pkg/validatingwebhook/blueprint_handler.go | 45 +++++++++++++--------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 5c8bf751711..764448b3ff0 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -20,14 +20,18 @@ import ( "net/http" "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager/signals" "sigs.k8s.io/controller-runtime/pkg/webhook" + crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1" "github.com/kanisterio/kanister/pkg/validatingwebhook" "github.com/kanisterio/kanister/pkg/version" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) const ( @@ -62,18 +66,21 @@ func (*healthCheckHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // RunWebhookServer starts the validating webhook resources for blueprint kanister resources func RunWebhookServer(c *rest.Config) error { - hookServerOptions := webhook.Options{CertDir: validatingwebhook.WHCertsDir} + log.SetLogger(zap.New()) + mgr, err := manager.New(c, manager.Options{}) + if err != nil { + return errors.Wrapf(err, "Failed to create new webhook manager") + } + hookServerOptions := webhook.Options{CertDir: validatingwebhook.WHCertsDir} hookServer := webhook.NewServer(hookServerOptions) - hookServer.Register(whHandlePath, &webhook.Admission{Handler: &validatingwebhook.BlueprintValidator{}}) + hook := admission.WithCustomValidator(mgr.GetScheme(), &crv1alpha1.Blueprint{}, &validatingwebhook.BlueprintValidator{}) + hookServer.Register(whHandlePath, hook) hookServer.Register(healthCheckPath, &healthCheckHandler{}) hookServer.Register(metricsPath, promhttp.Handler()) - mgr, err := manager.New(c, manager.Options{ - WebhookServer: hookServer, - }) - if err != nil { - return errors.Wrapf(err, "Failed to create new webhook manager") + if err := mgr.Add(hookServer); err != nil { + return errors.Wrapf(err, "Failed to add new webhook server") } if err := mgr.Start(signals.SetupSignalHandler()); err != nil { diff --git a/pkg/validatingwebhook/blueprint_handler.go b/pkg/validatingwebhook/blueprint_handler.go index 4d45172c559..bbc92cd5e53 100644 --- a/pkg/validatingwebhook/blueprint_handler.go +++ b/pkg/validatingwebhook/blueprint_handler.go @@ -3,35 +3,44 @@ package validatingwebhook import ( "context" "fmt" - "net/http" - - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" kanister "github.com/kanisterio/kanister/pkg" crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1" "github.com/kanisterio/kanister/pkg/blueprint/validate" + "k8s.io/apimachinery/pkg/runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) -type BlueprintValidator struct { - decoder *admission.Decoder -} +//+kubebuilder:webhook:path=validate/v1alpha1/blueprint,mutating=false,failurePolicy=fail,sideEffects=None,groups=cr.kanister.io,resources=blueprints,verbs=create,versions=v1alpha1,name=blueprint.cr.kanister.io -func (b *BlueprintValidator) Handle(ctx context.Context, r admission.Request) admission.Response { - bp := &crv1alpha1.Blueprint{} - err := b.decoder.Decode(r, bp) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } +type BlueprintValidator struct{} + +var _ webhook.CustomValidator = &BlueprintValidator{} +func (b *BlueprintValidator) validate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + log := logf.FromContext(ctx) + bp, ok := obj.(*crv1alpha1.Blueprint) + if !ok { + return nil, fmt.Errorf("expected a Blueprint but got a %T", obj) + } + log.Info("Validating blueprint") if err := validate.Do(bp, kanister.DefaultVersion); err != nil { - return admission.Denied(fmt.Sprintf("Invalid blueprint, %s\n", err.Error())) + return nil, fmt.Errorf("invalid blueprint, %s", err.Error()) } - return admission.Allowed("") + return nil, nil +} + +func (b *BlueprintValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + return b.validate(ctx, obj) +} + +func (b *BlueprintValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) { + return b.validate(ctx, newObj) } -// InjectDecoder injects the decoder. -func (b *BlueprintValidator) InjectDecoder(d *admission.Decoder) error { - b.decoder = d - return nil +func (b BlueprintValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + return b.validate(ctx, obj) }