From 9ea119d1b9d45689aa2e891a8e0aedc304f2473f Mon Sep 17 00:00:00 2001 From: Elliott Baron Date: Wed, 18 Dec 2024 12:31:29 -0500 Subject: [PATCH] test(webhook): add test for Cryostat defaulter webhook (#988) --- internal/webhooks/cryostat_webhook.go | 6 +- internal/webhooks/defaulter_test.go | 117 ++++++++++++++++++++++++++ internal/webhooks/validator.go | 20 ++--- internal/webhooks/validator_test.go | 12 +-- 4 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 internal/webhooks/defaulter_test.go diff --git a/internal/webhooks/cryostat_webhook.go b/internal/webhooks/cryostat_webhook.go index 64da3fa40..92ecefbb5 100644 --- a/internal/webhooks/cryostat_webhook.go +++ b/internal/webhooks/cryostat_webhook.go @@ -33,9 +33,9 @@ var cryostatlog = logf.Log.WithName("cryostat-resource") func SetupWebhookWithManager(mgr ctrl.Manager, apiType runtime.Object) error { return ctrl.NewWebhookManagedBy(mgr). For(apiType). - WithValidator(&CryostatValidator{ - Client: mgr.GetClient(), - Log: &cryostatlog, + WithValidator(&cryostatValidator{ + client: mgr.GetClient(), + log: &cryostatlog, }). WithDefaulter(&cryostatDefaulter{ log: &cryostatlog, diff --git a/internal/webhooks/defaulter_test.go b/internal/webhooks/defaulter_test.go new file mode 100644 index 000000000..19812f156 --- /dev/null +++ b/internal/webhooks/defaulter_test.go @@ -0,0 +1,117 @@ +// Copyright The Cryostat Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhooks_test + +import ( + "context" + "strconv" + + operatorv1beta2 "github.com/cryostatio/cryostat-operator/api/v1beta2" + "github.com/cryostatio/cryostat-operator/internal/controllers/model" + "github.com/cryostatio/cryostat-operator/internal/test" + webhooktests "github.com/cryostatio/cryostat-operator/internal/webhooks/test" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +type defaulterTestInput struct { + client ctrlclient.Client + objs []ctrlclient.Object + *webhooktests.WebhookTestResources +} + +var _ = Describe("CryostatDefaulter", func() { + var t *defaulterTestInput + var otherNS string + count := 0 + + namespaceWithSuffix := func(name string) string { + return name + "-defaulter-" + strconv.Itoa(count) + } + + BeforeEach(func() { + ns := namespaceWithSuffix("test") + otherNS = namespaceWithSuffix("other") + t = &defaulterTestInput{ + WebhookTestResources: &webhooktests.WebhookTestResources{ + TestResources: &test.TestResources{ + Name: "cryostat", + Namespace: ns, + }, + }, + } + t.objs = []ctrlclient.Object{ + t.NewNamespace(), t.NewOtherNamespace(otherNS), + } + }) + + JustBeforeEach(func() { + logger := zap.New() + logf.SetLogger(logger) + + t.client = k8sClient + for _, obj := range t.objs { + err := t.client.Create(ctx, obj) + Expect(err).ToNot(HaveOccurred()) + } + }) + + JustAfterEach(func() { + for _, obj := range t.objs { + err := ctrlclient.IgnoreNotFound(t.client.Delete(ctx, obj)) + Expect(err).ToNot(HaveOccurred()) + } + }) + + AfterEach(func() { + count++ + }) + + Context("without target namespace", func() { + BeforeEach(func() { + t.objs = append(t.objs, t.NewCryostat().Object) + }) + + It("should set default target namespace", func() { + result := t.getCryostatInstance() + Expect(result.TargetNamespaces).To(ConsistOf(t.Namespace)) + }) + }) + + Context("with target namespace", func() { + BeforeEach(func() { + t.TargetNamespaces = []string{otherNS} + t.objs = append(t.objs, t.NewCryostat().Object) + }) + + It("should do nothing", func() { + result := t.getCryostatInstance() + Expect(result.TargetNamespaces).To(ConsistOf(otherNS)) + }) + }) + +}) + +func (t *defaulterTestInput) getCryostatInstance() *model.CryostatInstance { + cr := &operatorv1beta2.Cryostat{} + err := t.client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, cr) + Expect(err).ToNot(HaveOccurred()) + return t.ConvertNamespacedToModel(cr) +} diff --git a/internal/webhooks/validator.go b/internal/webhooks/validator.go index 5b66d5482..8b5a9e3ab 100644 --- a/internal/webhooks/validator.go +++ b/internal/webhooks/validator.go @@ -27,25 +27,25 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) -type CryostatValidator struct { - Client client.Client - Log *logr.Logger +type cryostatValidator struct { + client client.Client + log *logr.Logger } -var _ admission.CustomValidator = &CryostatValidator{} +var _ admission.CustomValidator = &cryostatValidator{} // ValidateCreate validates a Create operation on a Cryostat -func (r *CryostatValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { +func (r *cryostatValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { return r.validate(ctx, obj, "create") } // ValidateCreate validates an Update operation on a Cryostat -func (r *CryostatValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { +func (r *cryostatValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { return r.validate(ctx, newObj, "update") } // ValidateCreate validates a Delete operation on a Cryostat -func (r *CryostatValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { +func (r *cryostatValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { // Nothing to validate on deletion return nil, nil } @@ -68,12 +68,12 @@ func (e *ErrNotPermitted) Error() string { var _ error = &ErrNotPermitted{} -func (r *CryostatValidator) validate(ctx context.Context, obj runtime.Object, op string) (admission.Warnings, error) { +func (r *cryostatValidator) validate(ctx context.Context, obj runtime.Object, op string) (admission.Warnings, error) { cr, ok := obj.(*operatorv1beta2.Cryostat) if !ok { return nil, fmt.Errorf("expected a Cryostat, but received a %T", obj) } - r.Log.Info(fmt.Sprintf("validate %s", op), "name", cr.Name, "namespace", cr.Namespace) + r.log.Info(fmt.Sprintf("validate %s", op), "name", cr.Name, "namespace", cr.Namespace) // Look up the user who made this request req, err := admission.RequestFromContext(ctx) @@ -101,7 +101,7 @@ func (r *CryostatValidator) validate(ctx context.Context, obj runtime.Object, op }, } - err := r.Client.Create(ctx, sar) + err := r.client.Create(ctx, sar) if err != nil { return nil, fmt.Errorf("failed to check permissions: %w", err) } diff --git a/internal/webhooks/validator_test.go b/internal/webhooks/validator_test.go index c370c56d2..4d6bcadaa 100644 --- a/internal/webhooks/validator_test.go +++ b/internal/webhooks/validator_test.go @@ -34,9 +34,8 @@ import ( ) type validatorTestInput struct { - client ctrlclient.Client - validator *webhooks.CryostatValidator - objs []ctrlclient.Object + client ctrlclient.Client + objs []ctrlclient.Object *webhooktests.WebhookTestResources } @@ -46,7 +45,7 @@ var _ = Describe("CryostatValidator", func() { count := 0 namespaceWithSuffix := func(name string) string { - return name + "-" + strconv.Itoa(count) + return name + "-validator-" + strconv.Itoa(count) } BeforeEach(func() { @@ -73,11 +72,6 @@ var _ = Describe("CryostatValidator", func() { logf.SetLogger(logger) t.client = k8sClient - t.validator = &webhooks.CryostatValidator{ - Client: k8sClient, - Log: &logger, - } - for _, obj := range t.objs { err := t.client.Create(ctx, obj) Expect(err).ToNot(HaveOccurred())