pkg/webhook/codebase_webhook.go (94 lines of code) (raw):
package webhook
import (
"context"
"fmt"
"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
v1 "github.com/epam/edp-codebase-operator/v2/api/v1"
"github.com/epam/edp-codebase-operator/v2/pkg/util"
)
const listLimit = 1000
//+kubebuilder:webhook:path=/validate-v2-edp-epam-com-v1-codebase,mutating=false,failurePolicy=fail,sideEffects=None,groups=v2.edp.epam.com,resources=codebases,verbs=create;update,versions=v1,name=vcodebase.kb.io,admissionReviewVersions=v1
// CodebaseValidationWebhook is a webhook for validating Codebase CRD.
type CodebaseValidationWebhook struct {
client client.Client
log logr.Logger
}
// NewCodebaseValidationWebhook creates a new webhook for validating Codebase CR.
func NewCodebaseValidationWebhook(k8sClient client.Client, log logr.Logger) *CodebaseValidationWebhook {
return &CodebaseValidationWebhook{client: k8sClient, log: log.WithName("codebase-webhook")}
}
// SetupWebhookWithManager sets up the webhook with the manager for Codebase CR.
func (r *CodebaseValidationWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error {
err := ctrl.NewWebhookManagedBy(mgr).
For(&v1.Codebase{}).
WithValidator(r).
Complete()
if err != nil {
return fmt.Errorf("failed to build Codebase validation webhook: %w", err)
}
return nil
}
var _ webhook.CustomValidator = &CodebaseValidationWebhook{}
// ValidateCreate is a webhook for validating the creation of the Codebase CR.
func (r *CodebaseValidationWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) {
req, err := admission.RequestFromContext(ctx)
if err != nil {
return nil, apierrors.NewBadRequest(fmt.Errorf("expected admission.Request in ctx: %w", err).Error())
}
r.log.Info("validate create", "name", req.Name)
createdCodebase, ok := obj.(*v1.Codebase)
if !ok {
r.log.Info("the wrong object given, skipping validation")
return nil, nil
}
if err = validateCodBaseName(createdCodebase.Name); err != nil {
return nil, err
}
if err = IsCodebaseValid(createdCodebase); err != nil {
return nil, fmt.Errorf("codebase %s is invalid: %w", createdCodebase.Name, err)
}
gitUrlPathToValidate := util.TrimGitFromURL(createdCodebase.Spec.GitUrlPath)
if gitUrlPathToValidate == "" {
return nil, fmt.Errorf("gitUrlPath %s is invalid", createdCodebase.Spec.GitUrlPath)
}
codeBases := &v1.CodebaseList{}
if err := r.client.List(ctx, codeBases, client.InNamespace(req.Namespace), client.Limit(listLimit)); err != nil {
return nil, fmt.Errorf("failed to list codebases: %w", err)
}
for i := range codeBases.Items {
gitUrlPath := codeBases.Items[i].Spec.GitUrlPath
if gitUrlPath == gitUrlPathToValidate {
return nil, fmt.Errorf(
"codebase %s with GitUrlPath %s already exists",
codeBases.Items[i].Name,
gitUrlPath,
)
}
}
return nil, nil
}
// ValidateUpdate is a webhook for validating the updating of the Codebase CR.
func (r *CodebaseValidationWebhook) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (warnings admission.Warnings, err error) {
req, err := admission.RequestFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("expected admission.Request in ctx: %w", err)
}
r.log.Info("validate update", "name", req.Name)
updatedCodebase, ok := newObj.(*v1.Codebase)
if !ok {
r.log.Info("the wrong object given, skipping validation")
return nil, nil
}
if err = IsCodebaseValid(updatedCodebase); err != nil {
return nil, fmt.Errorf("codebase %s is invalid: %w", updatedCodebase.Name, err)
}
return nil, nil
}
// ValidateDelete is a webhook for validating the deleting of the Codebase CR.
// It is skipped for now. Add kubebuilder:webhook:verbs=delete to enable it.
func (r *CodebaseValidationWebhook) ValidateDelete(ctx context.Context, _ runtime.Object) (warnings admission.Warnings, err error) {
req, err := admission.RequestFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("expected admission.Request in ctx: %w", err)
}
r.log.Info("validate delete", "name", req.Name)
return nil, nil
}