diff --git a/PROJECT b/PROJECT index 6b95135b..d7c261c4 100644 --- a/PROJECT +++ b/PROJECT @@ -134,4 +134,7 @@ resources: kind: PhysicalBackup path: github.com/mariadb-operator/mariadb-operator/api/v1alpha1 version: v1alpha1 + webhooks: + validation: true + webhookVersion: v1 version: "3" diff --git a/cmd/controller/webhook.go b/cmd/controller/webhook.go index 284475d3..afff9711 100644 --- a/cmd/controller/webhook.go +++ b/cmd/controller/webhook.go @@ -79,8 +79,12 @@ var webhookCmd = &cobra.Command{ setupLog.Error(err, "Unable to create webhook", "webhook", "Backup") os.Exit(1) } + if err = webhookv1alpha1.SetupPhysicalBackupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "Unable to create webhook", "webhook", "PhysicalBackup") + os.Exit(1) + } if err = webhookv1alpha1.SetupRestoreWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "Unable to create webhook", "webhook", "restore") + setupLog.Error(err, "Unable to create webhook", "webhook", "Restore") os.Exit(1) } if err = webhookv1alpha1.SetupUserWebhookWithManager(mgr); err != nil { diff --git a/deploy/charts/mariadb-operator/templates/webhook/config.yaml b/deploy/charts/mariadb-operator/templates/webhook/config.yaml index a5348405..3f0763fa 100644 --- a/deploy/charts/mariadb-operator/templates/webhook/config.yaml +++ b/deploy/charts/mariadb-operator/templates/webhook/config.yaml @@ -73,6 +73,26 @@ webhooks: resources: - backups sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: {{ $fullName }}-webhook + namespace: {{ .Release.Namespace }} + path: /validate-k8s-mariadb-com-v1alpha1-physicalbackup + failurePolicy: Fail + name: vbackup-v1alpha1.kb.io + rules: + - apiGroups: + - k8s.mariadb.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - physicalbackups + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/internal/webhook/v1alpha1/physicalbackup_webhook.go b/internal/webhook/v1alpha1/physicalbackup_webhook.go new file mode 100644 index 00000000..49888b99 --- /dev/null +++ b/internal/webhook/v1alpha1/physicalbackup_webhook.go @@ -0,0 +1,64 @@ +package v1alpha1 + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + k8sv1alpha1 "github.com/mariadb-operator/mariadb-operator/api/v1alpha1" +) + +// nolint:unused +// log is for logging in this package. +var physicalbackuplog = logf.Log.WithName("physicalbackup-resource") + +// SetupPhysicalBackupWebhookWithManager registers the webhook for PhysicalBackup in the manager. +func SetupPhysicalBackupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&k8sv1alpha1.PhysicalBackup{}). + WithValidator(&PhysicalBackupCustomValidator{}). + Complete() +} + +// PhysicalBackupCustomValidator struct is responsible for validating the PhysicalBackup resource +// when it is created, updated, or deleted. +type PhysicalBackupCustomValidator struct{} + +var _ webhook.CustomValidator = &PhysicalBackupCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type PhysicalBackup. +func (v *PhysicalBackupCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + physicalbackup, ok := obj.(*k8sv1alpha1.PhysicalBackup) + if !ok { + return nil, fmt.Errorf("expected a PhysicalBackup object but got %T", obj) + } + physicalbackuplog.Info("Validation for PhysicalBackup upon creation", "name", physicalbackup.GetName()) + + return nil, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type PhysicalBackup. +func (v *PhysicalBackupCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + physicalbackup, ok := newObj.(*k8sv1alpha1.PhysicalBackup) + if !ok { + return nil, fmt.Errorf("expected a PhysicalBackup object for the newObj but got %T", newObj) + } + physicalbackuplog.Info("Validation for PhysicalBackup upon update", "name", physicalbackup.GetName()) + + return nil, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type PhysicalBackup. +func (v *PhysicalBackupCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + physicalbackup, ok := obj.(*k8sv1alpha1.PhysicalBackup) + if !ok { + return nil, fmt.Errorf("expected a PhysicalBackup object but got %T", obj) + } + physicalbackuplog.Info("Validation for PhysicalBackup upon deletion", "name", physicalbackup.GetName()) + + return nil, nil +} diff --git a/internal/webhook/v1alpha1/physicalbackup_webhook_test.go b/internal/webhook/v1alpha1/physicalbackup_webhook_test.go new file mode 100644 index 00000000..c891017a --- /dev/null +++ b/internal/webhook/v1alpha1/physicalbackup_webhook_test.go @@ -0,0 +1,54 @@ +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + k8sv1alpha1 "github.com/mariadb-operator/mariadb-operator/api/v1alpha1" +) + +var _ = Describe("PhysicalBackup Webhook", func() { + var ( + obj *k8sv1alpha1.PhysicalBackup + oldObj *k8sv1alpha1.PhysicalBackup + validator PhysicalBackupCustomValidator + ) + + BeforeEach(func() { + obj = &k8sv1alpha1.PhysicalBackup{} + oldObj = &k8sv1alpha1.PhysicalBackup{} + validator = PhysicalBackupCustomValidator{} + Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + // TODO (user): Add any setup logic common to all tests + }) + + AfterEach(func() { + // TODO (user): Add any teardown logic common to all tests + }) + + Context("When creating or updating PhysicalBackup under Validating Webhook", func() { + // TODO (user): Add logic for validating webhooks + // Example: + // It("Should deny creation if a required field is missing", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "" + // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) + // }) + // + // It("Should admit creation if all required fields are present", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "valid_value" + // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) + // }) + // + // It("Should validate updates correctly", func() { + // By("simulating a valid update scenario") + // oldObj.SomeRequiredField = "updated_value" + // obj.SomeRequiredField = "updated_value" + // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) + // }) + }) + +}) diff --git a/internal/webhook/v1alpha1/webhook_suite_test.go b/internal/webhook/v1alpha1/webhook_suite_test.go index 70fda61a..07816bad 100644 --- a/internal/webhook/v1alpha1/webhook_suite_test.go +++ b/internal/webhook/v1alpha1/webhook_suite_test.go @@ -112,6 +112,9 @@ var _ = BeforeSuite(func() { err = SetupBackupWebhookWithManager(mgr) Expect(err).NotTo(HaveOccurred()) + err = SetupPhysicalBackupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + err = SetupRestoreWebhookWithManager(mgr) Expect(err).NotTo(HaveOccurred())