From 6dc0f2cacfcd3cb9686d986b75105da924ef220a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Moreno=20Garc=C3=ADa?= Date: Wed, 11 Sep 2024 14:51:20 +0200 Subject: [PATCH] feat(RELEASE-1124): validate Application reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new validation function to ensure that the Application referenced in the ReleasePlan and in the Snapshot is the same. Signed-off-by: David Moreno GarcĂ­a --- controllers/release/adapter.go | 29 ++++++++++++++ controllers/release/adapter_test.go | 60 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/controllers/release/adapter.go b/controllers/release/adapter.go index 8915c851..75514472 100644 --- a/controllers/release/adapter.go +++ b/controllers/release/adapter.go @@ -68,6 +68,7 @@ func newAdapter(ctx context.Context, client client.Client, release *v1alpha1.Rel releaseAdapter.validations = []controller.ValidationFunction{ releaseAdapter.validateProcessingResources, + releaseAdapter.validateApplication, releaseAdapter.validateAuthor, releaseAdapter.validatePipelineRef, releaseAdapter.validateSinglePipeline, @@ -582,6 +583,34 @@ func (a *adapter) registerProcessingStatus(pipelineRun *tektonv1.PipelineRun) er return nil } +// validateApplication will ensure that the same Application is used in both, the Snapshot and the ReleasePlan. If the +// resources reference different Applications, an error will be returned. +func (a *adapter) validateApplication() *controller.ValidationResult { + releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) + if err != nil { + if errors.IsNotFound(err) { + a.release.MarkValidationFailed(err.Error()) + return &controller.ValidationResult{Valid: false} + } + return &controller.ValidationResult{Err: err} + } + + snapshot, err := a.loader.GetSnapshot(a.ctx, a.client, a.release) + if err != nil { + if errors.IsNotFound(err) { + a.release.MarkValidationFailed(err.Error()) + return &controller.ValidationResult{Valid: false} + } + return &controller.ValidationResult{Err: err} + } + + if releasePlan.Spec.Application != snapshot.Spec.Application { + return &controller.ValidationResult{Err: fmt.Errorf("different Application referenced in ReleasePlan and Snapshot")} + } + + return &controller.ValidationResult{Valid: true} +} + // validateAuthor will ensure that a valid author exists for the Release and add it to its status. If the Release // has the automated label but doesn't have automated set in its status, this function will return an error so the // operation knows to requeue the Release. diff --git a/controllers/release/adapter_test.go b/controllers/release/adapter_test.go index 9ef39030..bbc1dcfe 100644 --- a/controllers/release/adapter_test.go +++ b/controllers/release/adapter_test.go @@ -1498,6 +1498,66 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("validateAuthor is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + }) + + It("returns valid and no error if the Application match", func() { + result := adapter.validateApplication() + Expect(result.Valid).To(BeTrue()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("returns invalid and error if the Application doesn't match", func() { + newReleasePlan := releasePlan.DeepCopy() + newReleasePlan.Spec.Application = "non-existent" + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: newReleasePlan, + }, + }) + + result := adapter.validateApplication() + Expect(result.Valid).To(BeFalse()) + Expect(result.Err).To(HaveOccurred()) + Expect(result.Err.Error()).To(Equal("different Application referenced in ReleasePlan and Snapshot")) + }) + + It("returns invalid if the ReleasePlan is not found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Err: errors.NewNotFound(schema.GroupResource{}, ""), + }, + }) + + result := adapter.validateApplication() + Expect(result.Valid).To(BeFalse()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("returns invalid if the Snapshot is not found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.SnapshotContextKey, + Err: errors.NewNotFound(schema.GroupResource{}, ""), + }, + }) + + result := adapter.validateApplication() + Expect(result.Valid).To(BeFalse()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + }) + When("calling validateAuthor", func() { var adapter *adapter var conditionMsg string