From 6ca7b1ee8f37f8a620416f2f1bf97a4a47bc6b9e Mon Sep 17 00:00:00 2001 From: Johnny Bieren Date: Tue, 27 Aug 2024 20:53:38 -0400 Subject: [PATCH] feat(RELEASE-1109): allow tenant and managed pipelines to run in same release This commit modifies the release controller to execute both a tenant and managed pipelineRun in the same Release if both are defined. Signed-off-by: Johnny Bieren --- api/v1alpha1/release_conditions.go | 14 +- api/v1alpha1/release_types.go | 188 ++-- api/v1alpha1/release_types_test.go | 486 ++++++++-- api/v1alpha1/releaseplanadmission_types.go | 4 +- api/v1alpha1/zz_generated.deepcopy.go | 35 +- ...udio.redhat.com_releaseplanadmissions.yaml | 86 +- .../appstudio.redhat.com_releaseplans.yaml | 95 +- .../bases/appstudio.redhat.com_releases.yaml | 152 +-- ...udio.redhat.com_releaseserviceconfigs.yaml | 37 +- config/rbac/role.yaml | 1 + config/webhook/manifests.yaml | 2 + controllers/release/adapter.go | 260 ++++-- controllers/release/adapter_test.go | 869 ++++++++++++++---- controllers/release/controller.go | 3 +- loader/loader.go | 27 +- loader/loader_mock.go | 4 +- loader/loader_mock_test.go | 3 +- loader/loader_test.go | 53 +- metrics/release.go | 123 ++- metrics/release_test.go | 155 +++- tekton/utils/zz_generated.deepcopy.go | 1 + 21 files changed, 1910 insertions(+), 688 deletions(-) diff --git a/api/v1alpha1/release_conditions.go b/api/v1alpha1/release_conditions.go index 76cda271..df13144a 100644 --- a/api/v1alpha1/release_conditions.go +++ b/api/v1alpha1/release_conditions.go @@ -6,8 +6,11 @@ const ( // postActionsExecutedConditionType is the type used to track the status of Release post-actions postActionsExecutedConditionType conditions.ConditionType = "PostActionsExecuted" - // processedConditionType is the type used to track the status of a Release processing - processedConditionType conditions.ConditionType = "Processed" + // managedProcessedConditionType is the type used to track the status of a Release Managed Pipeline processing + managedProcessedConditionType conditions.ConditionType = "ManagedPipelineProcessed" + + // tenantProcessedConditionType is the type used to track the status of a Release Tenant Pipeline processing + tenantProcessedConditionType conditions.ConditionType = "TenantPipelineProcessed" // releasedConditionType is the type used to track the status of a Release releasedConditionType conditions.ConditionType = "Released" @@ -20,9 +23,12 @@ const ( // FailedReason is the reason set when a failure occurs FailedReason conditions.ConditionReason = "Failed" - // ProgressingReason is the reason set when an action is progressing + // ProgressingReason is the reason set when a phase is progressing ProgressingReason conditions.ConditionReason = "Progressing" - // SucceededReason is the reason set when an action succeeds + // SucceededReason is the reason set when a phase succeeds SucceededReason conditions.ConditionReason = "Succeeded" + + // SkippedReason is the reason set when a phase is skipped + SkippedReason conditions.ConditionReason = "Skipped" ) diff --git a/api/v1alpha1/release_types.go b/api/v1alpha1/release_types.go index 08d3b6f0..8da42b66 100644 --- a/api/v1alpha1/release_types.go +++ b/api/v1alpha1/release_types.go @@ -68,11 +68,15 @@ type ReleaseStatus struct { // PostActionsExecution contains information about the post-actions execution // +optional - PostActionsExecution PostActionsExecutionInfo `json:"postActionsExecution,omitempty"` + PostActionsExecution PipelineInfo `json:"postActionsExecution,omitempty"` - // Processing contains information about the release processing + // ManagedProcessing contains information about the release managed processing // +optional - Processing ProcessingInfo `json:"processing,omitempty"` + ManagedProcessing PipelineInfo `json:"managedProcessing,omitempty"` + + // TenantProcessing contains information about the release tenant processing + // +optional + TenantProcessing PipelineInfo `json:"tenantProcessing,omitempty"` // Validation contains information about the release validation // +optional @@ -111,19 +115,8 @@ type AttributionInfo struct { StandingAuthorization bool `json:"standingAuthorization,omitempty"` } -// PostActionsExecutionInfo defines the observed state of the post-actions execution. -type PostActionsExecutionInfo struct { - // CompletionTime is the time when the Release post-actions execution was completed - // +optional - CompletionTime *metav1.Time `json:"completionTime,omitempty"` - - // StartTime is the time when the Release post-actions execution started - // +optional - StartTime *metav1.Time `json:"startTime,omitempty"` -} - -// ProcessingInfo defines the observed state of the release processing. -type ProcessingInfo struct { +// PipelineInfo defines the observed state of the release pipeline processing. +type PipelineInfo struct { // CompletionTime is the time when the Release processing was completed // +optional CompletionTime *metav1.Time `json:"completionTime,omitempty"` @@ -177,9 +170,14 @@ func (r *Release) HasEveryPostActionExecutionFinished() bool { return r.hasPhaseFinished(postActionsExecutedConditionType) } -// HasProcessingFinished checks whether the Release processing has finished, regardless of the result. -func (r *Release) HasProcessingFinished() bool { - return r.hasPhaseFinished(processedConditionType) +// HasManagedPipelineProcessingFinished checks whether the Release Managed Pipeline processing has finished, regardless of the result. +func (r *Release) HasManagedPipelineProcessingFinished() bool { + return r.hasPhaseFinished(managedProcessedConditionType) +} + +// HasTenantPipelineProcessingFinished checks whether the Release Tenant Pipeline processing has finished, regardless of the result. +func (r *Release) HasTenantPipelineProcessingFinished() bool { + return r.hasPhaseFinished(tenantProcessedConditionType) } // HasReleaseFinished checks whether the Release has finished, regardless of the result. @@ -207,14 +205,24 @@ func (r *Release) IsEachPostActionExecuting() bool { return r.isPhaseProgressing(postActionsExecutedConditionType) } -// IsProcessed checks whether the Release was successfully processed. -func (r *Release) IsProcessed() bool { - return meta.IsStatusConditionTrue(r.Status.Conditions, processedConditionType.String()) +// IsManagedPipelineProcessed checks whether the Release Managed Pipeline was successfully processed. +func (r *Release) IsManagedPipelineProcessed() bool { + return meta.IsStatusConditionTrue(r.Status.Conditions, managedProcessedConditionType.String()) +} + +// IsTenantPipelineProcessed checks whether the Release Tenant Pipeline was successfully processed. +func (r *Release) IsTenantPipelineProcessed() bool { + return meta.IsStatusConditionTrue(r.Status.Conditions, tenantProcessedConditionType.String()) +} + +// IsManagedPipelineProcessing checks whether the Release Managed Pipeline processing is in progress. +func (r *Release) IsManagedPipelineProcessing() bool { + return r.isPhaseProgressing(managedProcessedConditionType) } -// IsProcessing checks whether the Release processing is in progress. -func (r *Release) IsProcessing() bool { - return r.isPhaseProgressing(processedConditionType) +// IsTenantPipelineProcessing checks whether the Release Tenant Pipeline processing is in progress. +func (r *Release) IsTenantPipelineProcessing() bool { + return r.isPhaseProgressing(tenantProcessedConditionType) } // IsReleased checks whether the Release has finished successfully. @@ -232,60 +240,132 @@ func (r *Release) IsValid() bool { return meta.IsStatusConditionTrue(r.Status.Conditions, validatedConditionType.String()) } -// MarkProcessed marks the Release as processed. -func (r *Release) MarkProcessed() { - if !r.IsProcessing() || r.HasProcessingFinished() { +// MarkManagedPipelineProcessed marks the Release Managed Pipeline as processed. +func (r *Release) MarkManagedPipelineProcessed() { + if !r.IsManagedPipelineProcessing() || r.HasManagedPipelineProcessingFinished() { return } - r.Status.Processing.CompletionTime = &metav1.Time{Time: time.Now()} - conditions.SetCondition(&r.Status.Conditions, processedConditionType, metav1.ConditionTrue, SucceededReason) + r.Status.ManagedProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetCondition(&r.Status.Conditions, managedProcessedConditionType, metav1.ConditionTrue, SucceededReason) - go metrics.RegisterCompletedReleaseProcessing( - r.Status.Processing.StartTime, - r.Status.Processing.CompletionTime, + go metrics.RegisterCompletedReleaseManagedPipelineProcessing( + r.Status.ManagedProcessing.StartTime, + r.Status.ManagedProcessing.CompletionTime, SucceededReason.String(), r.Status.Target, ) } -// MarkProcessing marks the Release as processing. -func (r *Release) MarkProcessing(message string) { - if r.HasProcessingFinished() { +// MarkTenantPipelineProcessed marks the Release Tenant Pipeline as processed. +func (r *Release) MarkTenantPipelineProcessed() { + if !r.IsTenantPipelineProcessing() || r.HasTenantPipelineProcessingFinished() { return } - if !r.IsProcessing() { - r.Status.Processing.StartTime = &metav1.Time{Time: time.Now()} + r.Status.TenantProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetCondition(&r.Status.Conditions, tenantProcessedConditionType, metav1.ConditionTrue, SucceededReason) + + go metrics.RegisterCompletedReleaseTenantPipelineProcessing( + r.Status.TenantProcessing.StartTime, + r.Status.TenantProcessing.CompletionTime, + SucceededReason.String(), + r.Status.Target, + ) +} + +// MarkManagedPipelineProcessing marks the Release Managed Pipeline as processing. +func (r *Release) MarkManagedPipelineProcessing() { + if r.HasManagedPipelineProcessingFinished() { + return } - conditions.SetConditionWithMessage(&r.Status.Conditions, processedConditionType, metav1.ConditionFalse, ProgressingReason, message) + if !r.IsManagedPipelineProcessing() { + r.Status.ManagedProcessing.StartTime = &metav1.Time{Time: time.Now()} + } + + conditions.SetCondition(&r.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, ProgressingReason) - go metrics.RegisterNewReleaseProcessing( - r.Status.Processing.StartTime, + go metrics.RegisterNewReleaseManagedPipelineProcessing( + r.Status.ManagedProcessing.StartTime, r.Status.StartTime, ProgressingReason.String(), r.Status.Target, ) } -// MarkProcessingFailed marks the Release processing as failed. -func (r *Release) MarkProcessingFailed(message string) { - if !r.IsProcessing() || r.HasProcessingFinished() { +// MarkTenantPipelineProcessing marks the Release Tenant Pipeline as processing. +func (r *Release) MarkTenantPipelineProcessing() { + if r.HasTenantPipelineProcessingFinished() { return } - r.Status.Processing.CompletionTime = &metav1.Time{Time: time.Now()} - conditions.SetConditionWithMessage(&r.Status.Conditions, processedConditionType, metav1.ConditionFalse, FailedReason, message) + if !r.IsTenantPipelineProcessing() { + r.Status.TenantProcessing.StartTime = &metav1.Time{Time: time.Now()} + } + + conditions.SetCondition(&r.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, ProgressingReason) - go metrics.RegisterCompletedReleaseProcessing( - r.Status.Processing.StartTime, - r.Status.Processing.CompletionTime, + go metrics.RegisterNewReleaseTenantPipelineProcessing( + r.Status.TenantProcessing.StartTime, + r.Status.StartTime, + ProgressingReason.String(), + r.Status.Target, + ) +} + +// MarkManagedPipelineProcessingFailed marks the Release Managed Pipeline processing as failed. +func (r *Release) MarkManagedPipelineProcessingFailed(message string) { + if !r.IsManagedPipelineProcessing() || r.HasManagedPipelineProcessingFinished() { + return + } + + r.Status.ManagedProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetConditionWithMessage(&r.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, FailedReason, message) + + go metrics.RegisterCompletedReleaseManagedPipelineProcessing( + r.Status.ManagedProcessing.StartTime, + r.Status.ManagedProcessing.CompletionTime, + FailedReason.String(), + r.Status.Target, + ) +} + +// MarkTenantPipelineProcessingFailed marks the Release Tenant Pipeline processing as failed. +func (r *Release) MarkTenantPipelineProcessingFailed(message string) { + if !r.IsTenantPipelineProcessing() || r.HasTenantPipelineProcessingFinished() { + return + } + + r.Status.TenantProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetConditionWithMessage(&r.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, FailedReason, message) + + go metrics.RegisterCompletedReleaseTenantPipelineProcessing( + r.Status.TenantProcessing.StartTime, + r.Status.TenantProcessing.CompletionTime, FailedReason.String(), r.Status.Target, ) } +// MarkManagedPipelineProcessingSkipped marks the Release Managed Pipeline processing as skipped. +func (r *Release) MarkManagedPipelineProcessingSkipped() { + if r.HasManagedPipelineProcessingFinished() { + return + } + + conditions.SetCondition(&r.Status.Conditions, managedProcessedConditionType, metav1.ConditionTrue, SkippedReason) +} + +// MarkTenantPipelineProcessingSkipped marks the Release Tenant Pipeline processing as skipped. +func (r *Release) MarkTenantPipelineProcessingSkipped() { + if r.HasTenantPipelineProcessingFinished() { + return + } + + conditions.SetCondition(&r.Status.Conditions, tenantProcessedConditionType, metav1.ConditionTrue, SkippedReason) +} + // MarkPostActionsExecuted marks the Release post-actions as executed. func (r *Release) MarkPostActionsExecuted() { if !r.IsEachPostActionExecuting() || r.HasEveryPostActionExecutionFinished() { @@ -327,8 +407,8 @@ func (r *Release) MarkPostActionsExecutionFailed(message string) { conditions.SetConditionWithMessage(&r.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, FailedReason, message) go metrics.RegisterCompletedReleasePostActionsExecuted( - r.Status.Processing.StartTime, - r.Status.Processing.CompletionTime, + r.Status.PostActionsExecution.StartTime, + r.Status.PostActionsExecution.CompletionTime, FailedReason.String(), ) } @@ -345,10 +425,11 @@ func (r *Release) MarkReleased() { go metrics.RegisterCompletedRelease( r.Status.StartTime, r.Status.CompletionTime, + r.getPhaseReason(managedProcessedConditionType), r.getPhaseReason(postActionsExecutedConditionType), - r.getPhaseReason(processedConditionType), SucceededReason.String(), r.Status.Target, + r.getPhaseReason(tenantProcessedConditionType), r.getPhaseReason(validatedConditionType), ) } @@ -381,7 +462,8 @@ func (r *Release) MarkReleaseFailed(message string) { r.Status.StartTime, r.Status.CompletionTime, r.getPhaseReason(postActionsExecutedConditionType), - r.getPhaseReason(processedConditionType), + r.getPhaseReason(tenantProcessedConditionType), + r.getPhaseReason(managedProcessedConditionType), FailedReason.String(), r.Status.Target, r.getPhaseReason(validatedConditionType), diff --git a/api/v1alpha1/release_types_test.go b/api/v1alpha1/release_types_test.go index f63a7289..378d9f54 100644 --- a/api/v1alpha1/release_types_test.go +++ b/api/v1alpha1/release_types_test.go @@ -62,35 +62,59 @@ var _ = Describe("Release type", func() { }) }) - When("HasProcessingFinished method is called", func() { + When("HasManagedPipelineProcessingFinished method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should return false when the deployed condition is missing", func() { - Expect(release.HasProcessingFinished()).To(BeFalse()) + It("should return true when the managed pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.HasManagedPipelineProcessingFinished()).To(BeTrue()) }) - It("should return true when the processed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.HasProcessingFinished()).To(BeTrue()) + It("should return false when the managed pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.HasManagedPipelineProcessingFinished()).To(BeFalse()) }) - It("should return false when the processed condition status is False and the reason is Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionFalse, ProgressingReason) - Expect(release.HasProcessingFinished()).To(BeFalse()) + It("should return true when the managed pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.HasManagedPipelineProcessingFinished()).To(BeTrue()) }) - It("should return true when the processed condition status is False and the reason is not Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionFalse, FailedReason) - Expect(release.HasProcessingFinished()).To(BeTrue()) + It("should return false when the managed pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionUnknown, ProgressingReason) + Expect(release.HasManagedPipelineProcessingFinished()).To(BeFalse()) + }) + }) + + When("HasTenantPipelineProcessingFinished method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should return true when the tenant pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.HasTenantPipelineProcessingFinished()).To(BeTrue()) + }) + + It("should return false when the tenant pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.HasTenantPipelineProcessingFinished()).To(BeFalse()) }) - It("should return false when the processed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionUnknown, ProgressingReason) - Expect(release.HasProcessingFinished()).To(BeFalse()) + It("should return true when the tenant pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.HasTenantPipelineProcessingFinished()).To(BeTrue()) + }) + + It("should return false when the tenant pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionUnknown, ProgressingReason) + Expect(release.HasTenantPipelineProcessingFinished()).To(BeFalse()) }) }) @@ -224,62 +248,121 @@ var _ = Describe("Release type", func() { }) }) - When("IsProcessed method is called", func() { + When("IsManagedPipelineProcessed method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should return true when the managed pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsManagedPipelineProcessed()).To(BeTrue()) + }) + + It("should return false when the managed pipeline processed condition status is False", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, SucceededReason) + Expect(release.IsManagedPipelineProcessed()).To(BeFalse()) + }) + + It("should return false when the managed pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionUnknown, SucceededReason) + Expect(release.IsManagedPipelineProcessed()).To(BeFalse()) + }) + + It("should return false when the managed pipeline processed condition is missing", func() { + Expect(release.IsManagedPipelineProcessed()).To(BeFalse()) + }) + }) + + When("IsTenantPipelineProcessed method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should return true when the tenant pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsTenantPipelineProcessed()).To(BeTrue()) + }) + + It("should return false when the tenant pipeline processed condition status is False", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, SucceededReason) + Expect(release.IsTenantPipelineProcessed()).To(BeFalse()) + }) + + It("should return false when the tenant pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionUnknown, SucceededReason) + Expect(release.IsTenantPipelineProcessed()).To(BeFalse()) + }) + + It("should return false when the tenant pipeline processed condition is missing", func() { + Expect(release.IsTenantPipelineProcessed()).To(BeFalse()) + }) + }) + + When("IsManagedPipelineProcessing method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should return true when the processed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.IsProcessed()).To(BeTrue()) + It("should return false when the managed pipeline processed condition is missing", func() { + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) + }) + + It("should return false when the managed pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) }) - It("should return false when the processed condition status is False", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionFalse, SucceededReason) - Expect(release.IsProcessed()).To(BeFalse()) + It("should return true when the managed pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.IsManagedPipelineProcessing()).To(BeTrue()) }) - It("should return false when the processed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionUnknown, SucceededReason) - Expect(release.IsProcessed()).To(BeFalse()) + It("should return false when the managed pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) }) - It("should return false when the processed condition is missing", func() { - Expect(release.IsProcessed()).To(BeFalse()) + It("should return false when the managed pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, managedProcessedConditionType, metav1.ConditionUnknown, ProgressingReason) + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) }) }) - When("IsProcessing method is called", func() { + When("IsTenantPipelineProcessing method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should return false when the processed condition is missing", func() { - Expect(release.IsProcessing()).To(BeFalse()) + It("should return false when the tenant pipeline processed condition is missing", func() { + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should return false when the processed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.IsProcessing()).To(BeFalse()) + It("should return false when the tenant pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should return true when the processed condition status is False and the reason is Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionFalse, ProgressingReason) - Expect(release.IsProcessing()).To(BeTrue()) + It("should return true when the tenant pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.IsTenantPipelineProcessing()).To(BeTrue()) }) - It("should return false when the processed condition status is False and the reason is not Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionFalse, FailedReason) - Expect(release.IsProcessing()).To(BeFalse()) + It("should return false when the tenant pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should return false when the processed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, processedConditionType, metav1.ConditionUnknown, ProgressingReason) - Expect(release.IsProcessing()).To(BeFalse()) + It("should return false when the tenant pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, tenantProcessedConditionType, metav1.ConditionUnknown, ProgressingReason) + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) }) }) @@ -369,40 +452,82 @@ var _ = Describe("Release type", func() { }) }) - When("MarkProcessed method is called", func() { + When("MarkManagedPipelineProcessed method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release managed pipeline processing has not started", func() { + release.MarkManagedPipelineProcessed() + Expect(release.Status.ManagedProcessing.CompletionTime).To(BeNil()) + }) + + It("should do nothing if the Release managed pipeline processing finished", func() { + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessed() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.ManagedProcessing.CompletionTime = &metav1.Time{} + release.MarkManagedPipelineProcessed() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeTrue()) + }) + + It("should register the completion time", func() { + release.MarkManagedPipelineProcessing() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkManagedPipelineProcessed() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeFalse()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessed() + + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(SucceededReason.String()), + "Status": Equal(metav1.ConditionTrue), + })) + }) + }) + + When("MarkTenantPipelineProcessed method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should do nothing if the Release processing has not started", func() { - release.MarkProcessed() - Expect(release.Status.Processing.CompletionTime).To(BeNil()) + It("should do nothing if the Release tenant pipeline processing has not started", func() { + release.MarkTenantPipelineProcessed() + Expect(release.Status.TenantProcessing.CompletionTime).To(BeNil()) }) - It("should do nothing if the Release processing finished", func() { - release.MarkProcessing("") - release.MarkProcessed() - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeFalse()) - release.Status.Processing.CompletionTime = &metav1.Time{} - release.MarkProcessed() - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeTrue()) + It("should do nothing if the Release tenant pipeline processing finished", func() { + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessed() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.TenantProcessing.CompletionTime = &metav1.Time{} + release.MarkTenantPipelineProcessed() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeTrue()) }) It("should register the completion time", func() { - release.MarkProcessing("") - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeTrue()) - release.MarkProcessed() - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeFalse()) + release.MarkTenantPipelineProcessing() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkTenantPipelineProcessed() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeFalse()) }) It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkProcessing("") - release.MarkProcessed() + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessed() - condition := meta.FindStatusCondition(release.Status.Conditions, processedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Reason": Equal(SucceededReason.String()), @@ -411,84 +536,169 @@ var _ = Describe("Release type", func() { }) }) - When("MarkProcessing method is called", func() { + When("MarkManagedPipelineProcessing method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release managed pipeline processing finished", func() { + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessed() + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) + release.MarkManagedPipelineProcessing() + Expect(release.IsManagedPipelineProcessing()).To(BeFalse()) + }) + + It("should register the start time if the managed pipeline is not processing", func() { + Expect(release.Status.ManagedProcessing.StartTime).To(BeNil()) + release.MarkManagedPipelineProcessing() + Expect(release.Status.ManagedProcessing.StartTime).NotTo(BeNil()) + }) + + It("should not register the start time if the managed pipeline is processing already", func() { + Expect(release.Status.ManagedProcessing.StartTime).To(BeNil()) + release.MarkManagedPipelineProcessing() + release.Status.ManagedProcessing.StartTime = &metav1.Time{} + Expect(release.Status.ManagedProcessing.StartTime.IsZero()).To(BeTrue()) + release.MarkManagedPipelineProcessing() + Expect(release.Status.ManagedProcessing.StartTime.IsZero()).To(BeTrue()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkManagedPipelineProcessing() + + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(ProgressingReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + }) + + When("MarkTenantPipelineProcessing method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release tenant pipeline processing finished", func() { + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessed() + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) + release.MarkTenantPipelineProcessing() + Expect(release.IsTenantPipelineProcessing()).To(BeFalse()) + }) + + It("should register the start time if the tenant pipeline is not processing", func() { + Expect(release.Status.TenantProcessing.StartTime).To(BeNil()) + release.MarkTenantPipelineProcessing() + Expect(release.Status.TenantProcessing.StartTime).NotTo(BeNil()) + }) + + It("should not register the start time if the tenant pipeline is processing already", func() { + Expect(release.Status.TenantProcessing.StartTime).To(BeNil()) + release.MarkTenantPipelineProcessing() + release.Status.TenantProcessing.StartTime = &metav1.Time{} + Expect(release.Status.TenantProcessing.StartTime.IsZero()).To(BeTrue()) + release.MarkTenantPipelineProcessing() + Expect(release.Status.TenantProcessing.StartTime.IsZero()).To(BeTrue()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkTenantPipelineProcessing() + + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(ProgressingReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + }) + + When("MarkManagedPipelineProcessingFailed method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should do nothing if the Release processing finished", func() { - release.MarkProcessing("") - release.MarkProcessed() - Expect(release.IsProcessing()).To(BeFalse()) - release.MarkProcessing("") - Expect(release.IsProcessing()).To(BeFalse()) + It("should do nothing if the Release managed pipeline processing has not started", func() { + release.MarkManagedPipelineProcessingFailed("") + Expect(release.Status.ManagedProcessing.CompletionTime).To(BeNil()) }) - It("should register the start time if it's not processing", func() { - Expect(release.Status.Processing.StartTime).To(BeNil()) - release.MarkProcessing("") - Expect(release.Status.Processing.StartTime).NotTo(BeNil()) + It("should do nothing if the Release managed pipeline processing finished", func() { + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessed() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.ManagedProcessing.CompletionTime = &metav1.Time{} + release.MarkManagedPipelineProcessingFailed("") + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeTrue()) }) - It("should not register the start time if it's processing already", func() { - Expect(release.Status.Processing.StartTime).To(BeNil()) - release.MarkProcessing("") - release.Status.Processing.StartTime = &metav1.Time{} - Expect(release.Status.Processing.StartTime.IsZero()).To(BeTrue()) - release.MarkProcessing("") - Expect(release.Status.Processing.StartTime.IsZero()).To(BeTrue()) + It("should register the completion time", func() { + release.MarkManagedPipelineProcessing() + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkManagedPipelineProcessingFailed("") + Expect(release.Status.ManagedProcessing.CompletionTime.IsZero()).To(BeFalse()) }) It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkProcessing("foo") + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessingFailed("foo") - condition := meta.FindStatusCondition(release.Status.Conditions, processedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Message": Equal("foo"), - "Reason": Equal(ProgressingReason.String()), + "Reason": Equal(FailedReason.String()), "Status": Equal(metav1.ConditionFalse), })) }) }) - When("MarkProcessingFailed method is called", func() { + When("MarkTenantPipelineProcessingFailed method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should do nothing if the Release processing has not started", func() { - release.MarkProcessingFailed("") - Expect(release.Status.Processing.CompletionTime).To(BeNil()) + It("should do nothing if the Release tenant pipeline processing has not started", func() { + release.MarkTenantPipelineProcessingFailed("") + Expect(release.Status.TenantProcessing.CompletionTime).To(BeNil()) }) - It("should do nothing if the Release processing finished", func() { - release.MarkProcessing("") - release.MarkProcessed() - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeFalse()) - release.Status.Processing.CompletionTime = &metav1.Time{} - release.MarkProcessingFailed("") - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeTrue()) + It("should do nothing if the Release tenant pipeline processing finished", func() { + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessed() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.TenantProcessing.CompletionTime = &metav1.Time{} + release.MarkTenantPipelineProcessingFailed("") + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeTrue()) }) It("should register the completion time", func() { - release.MarkProcessing("") - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeTrue()) - release.MarkProcessingFailed("") - Expect(release.Status.Processing.CompletionTime.IsZero()).To(BeFalse()) + release.MarkTenantPipelineProcessing() + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkTenantPipelineProcessingFailed("") + Expect(release.Status.TenantProcessing.CompletionTime.IsZero()).To(BeFalse()) }) It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkProcessing("") - release.MarkProcessingFailed("foo") + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessingFailed("foo") - condition := meta.FindStatusCondition(release.Status.Conditions, processedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Message": Equal("foo"), @@ -498,6 +708,74 @@ var _ = Describe("Release type", func() { }) }) + When("MarkManagedPipelineProcessingSkipped method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release managed pipeline processing finished already", func() { + release.MarkManagedPipelineProcessing() + release.MarkManagedPipelineProcessingFailed("error") + release.MarkManagedPipelineProcessingSkipped() + + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Message": Equal("error"), + "Reason": Equal(FailedReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkManagedPipelineProcessingSkipped() + + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(SkippedReason.String()), + "Status": Equal(metav1.ConditionTrue), + })) + }) + }) + + When("MarkTenantPipelineProcessingSkipped method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release tenant pipeline processing finished already", func() { + release.MarkTenantPipelineProcessing() + release.MarkTenantPipelineProcessingFailed("error") + release.MarkTenantPipelineProcessingSkipped() + + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Message": Equal("error"), + "Reason": Equal(FailedReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkTenantPipelineProcessingSkipped() + + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(SkippedReason.String()), + "Status": Equal(metav1.ConditionTrue), + })) + }) + }) + When("MarkPostActionsExecuted method is called", func() { var release *Release @@ -874,6 +1152,11 @@ var _ = Describe("Release type", func() { Expect(release.hasPhaseFinished(validatedConditionType)).To(BeTrue()) }) + It("should return true when the condition status is True and the reason is Skipped", func() { + conditions.SetCondition(&release.Status.Conditions, validatedConditionType, metav1.ConditionTrue, SkippedReason) + Expect(release.hasPhaseFinished(validatedConditionType)).To(BeTrue()) + }) + It("should return false when the condition status is False and the reason is Progressing", func() { conditions.SetCondition(&release.Status.Conditions, validatedConditionType, metav1.ConditionFalse, ProgressingReason) Expect(release.hasPhaseFinished(validatedConditionType)).To(BeFalse()) @@ -906,6 +1189,11 @@ var _ = Describe("Release type", func() { Expect(release.isPhaseProgressing(validatedConditionType)).To(BeFalse()) }) + It("should return false when the condition status is True and the reason is Skipped", func() { + conditions.SetCondition(&release.Status.Conditions, validatedConditionType, metav1.ConditionTrue, SkippedReason) + Expect(release.isPhaseProgressing(validatedConditionType)).To(BeFalse()) + }) + It("should return true when the condition status is False and the reason is Progressing", func() { conditions.SetCondition(&release.Status.Conditions, validatedConditionType, metav1.ConditionFalse, ProgressingReason) Expect(release.isPhaseProgressing(validatedConditionType)).To(BeTrue()) diff --git a/api/v1alpha1/releaseplanadmission_types.go b/api/v1alpha1/releaseplanadmission_types.go index 86ec5a13..4445c2bc 100644 --- a/api/v1alpha1/releaseplanadmission_types.go +++ b/api/v1alpha1/releaseplanadmission_types.go @@ -54,8 +54,8 @@ type ReleasePlanAdmissionSpec struct { Origin string `json:"origin"` // Pipeline contains all the information about the managed Pipeline - // +required - Pipeline *tektonutils.Pipeline `json:"pipeline"` + // +optional + Pipeline *tektonutils.Pipeline `json:"pipeline,omitempty"` // Policy to validate before releasing an artifact // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a2879bd1..e6f58ccb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,4 +1,5 @@ //go:build !ignore_autogenerated +// +build !ignore_autogenerated /* Copyright 2022. @@ -107,7 +108,7 @@ func (in *Param) DeepCopy() *Param { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PostActionsExecutionInfo) DeepCopyInto(out *PostActionsExecutionInfo) { +func (in *PipelineInfo) DeepCopyInto(out *PipelineInfo) { *out = *in if in.CompletionTime != nil { in, out := &in.CompletionTime, &out.CompletionTime @@ -119,35 +120,12 @@ func (in *PostActionsExecutionInfo) DeepCopyInto(out *PostActionsExecutionInfo) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostActionsExecutionInfo. -func (in *PostActionsExecutionInfo) DeepCopy() *PostActionsExecutionInfo { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineInfo. +func (in *PipelineInfo) DeepCopy() *PipelineInfo { if in == nil { return nil } - out := new(PostActionsExecutionInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProcessingInfo) DeepCopyInto(out *ProcessingInfo) { - *out = *in - if in.CompletionTime != nil { - in, out := &in.CompletionTime, &out.CompletionTime - *out = (*in).DeepCopy() - } - if in.StartTime != nil { - in, out := &in.StartTime, &out.StartTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProcessingInfo. -func (in *ProcessingInfo) DeepCopy() *ProcessingInfo { - if in == nil { - return nil - } - out := new(ProcessingInfo) + out := new(PipelineInfo) in.DeepCopyInto(out) return out } @@ -575,7 +553,8 @@ func (in *ReleaseStatus) DeepCopyInto(out *ReleaseStatus) { } } in.PostActionsExecution.DeepCopyInto(&out.PostActionsExecution) - in.Processing.DeepCopyInto(&out.Processing) + in.ManagedProcessing.DeepCopyInto(&out.ManagedProcessing) + in.TenantProcessing.DeepCopyInto(&out.TenantProcessing) in.Validation.DeepCopyInto(&out.Validation) if in.CompletionTime != nil { in, out := &in.CompletionTime, &out.CompletionTime diff --git a/config/crd/bases/appstudio.redhat.com_releaseplanadmissions.yaml b/config/crd/bases/appstudio.redhat.com_releaseplanadmissions.yaml index 710fcad6..12397082 100644 --- a/config/crd/bases/appstudio.redhat.com_releaseplanadmissions.yaml +++ b/config/crd/bases/appstudio.redhat.com_releaseplanadmissions.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: releaseplanadmissions.appstudio.redhat.com spec: group: appstudio.redhat.com @@ -30,19 +31,14 @@ spec: API. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -172,7 +168,6 @@ spec: required: - applications - origin - - pipeline - policy type: object status: @@ -184,42 +179,42 @@ spec: for the releasePlanAdmission items: description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -233,12 +228,11 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -273,3 +267,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/appstudio.redhat.com_releaseplans.yaml b/config/crd/bases/appstudio.redhat.com_releaseplans.yaml index 515d36b2..d53dab04 100644 --- a/config/crd/bases/appstudio.redhat.com_releaseplans.yaml +++ b/config/crd/bases/appstudio.redhat.com_releaseplans.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: releaseplans.appstudio.redhat.com spec: group: appstudio.redhat.com @@ -29,19 +30,14 @@ spec: description: ReleasePlan is the Schema for the ReleasePlans API. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -172,9 +168,8 @@ spec: type: object releaseGracePeriodDays: default: 7 - description: |- - ReleaseGracePeriodDays is the number of days a Release should be kept - This value is used to define the Release ExpirationTime + description: ReleaseGracePeriodDays is the number of days a Release + should be kept This value is used to define the Release ExpirationTime type: integer target: description: Target references where to send the release requests @@ -191,42 +186,42 @@ spec: for the releasePlan items: description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -240,12 +235,11 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -258,9 +252,8 @@ spec: type: object type: array releasePlanAdmission: - description: |- - ReleasePlanAdmission contains the information of the releasePlanAdmission this ReleasePlan is - matched to + description: ReleasePlanAdmission contains the information of the + releasePlanAdmission this ReleasePlan is matched to properties: active: description: Active indicates whether the ReleasePlanAdmission @@ -276,3 +269,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/appstudio.redhat.com_releases.yaml b/config/crd/bases/appstudio.redhat.com_releases.yaml index d299227d..3a12d0a4 100644 --- a/config/crd/bases/appstudio.redhat.com_releases.yaml +++ b/config/crd/bases/appstudio.redhat.com_releases.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: releases.appstudio.redhat.com spec: group: appstudio.redhat.com @@ -35,19 +36,14 @@ spec: description: Release is the Schema for the releases API properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -60,9 +56,8 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true gracePeriodDays: - description: |- - GracePeriodDays is the number of days a Release should be kept - This value is used to define the Release ExpirationTime + description: GracePeriodDays is the number of days a Release should + be kept This value is used to define the Release ExpirationTime type: integer releasePlan: description: ReleasePlan to use for this particular Release @@ -110,42 +105,42 @@ spec: for the release items: description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -159,12 +154,11 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -180,23 +174,35 @@ spec: description: ExpirationTime is the time when a Release can be purged format: date-time type: string - postActionsExecution: - description: PostActionsExecution contains information about the post-actions - execution + managedProcessing: + description: ManagedProcessing contains information about the release + managed processing properties: completionTime: - description: CompletionTime is the time when the Release post-actions - execution was completed + description: CompletionTime is the time when the Release processing + was completed format: date-time type: string + pipelineRun: + description: PipelineRun contains the namespaced name of the managed + Release PipelineRun executed as part of this release + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + roleBinding: + description: RoleBinding contains the namespaced name of the roleBinding + created for the managed Release PipelineRun executed as part + of this release + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string startTime: - description: StartTime is the time when the Release post-actions - execution started + description: StartTime is the time when the Release processing + started format: date-time type: string type: object - processing: - description: Processing contains information about the release processing + postActionsExecution: + description: PostActionsExecution contains information about the post-actions + execution properties: completionTime: description: CompletionTime is the time when the Release processing @@ -209,9 +215,9 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string roleBinding: - description: |- - RoleBinding contains the namespaced name of the roleBinding created for the managed Release PipelineRun - executed as part of this release + description: RoleBinding contains the namespaced name of the roleBinding + created for the managed Release PipelineRun executed as part + of this release pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string startTime: @@ -229,6 +235,32 @@ spec: released to pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + tenantProcessing: + description: TenantProcessing contains information about the release + tenant processing + properties: + completionTime: + description: CompletionTime is the time when the Release processing + was completed + format: date-time + type: string + pipelineRun: + description: PipelineRun contains the namespaced name of the managed + Release PipelineRun executed as part of this release + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + roleBinding: + description: RoleBinding contains the namespaced name of the roleBinding + created for the managed Release PipelineRun executed as part + of this release + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?\/[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + startTime: + description: StartTime is the time when the Release processing + started + format: date-time + type: string + type: object validation: description: Validation contains information about the release validation properties: @@ -248,3 +280,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/appstudio.redhat.com_releaseserviceconfigs.yaml b/config/crd/bases/appstudio.redhat.com_releaseserviceconfigs.yaml index 4ae0787c..c7cb89bc 100644 --- a/config/crd/bases/appstudio.redhat.com_releaseserviceconfigs.yaml +++ b/config/crd/bases/appstudio.redhat.com_releaseserviceconfigs.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null name: releaseserviceconfigs.appstudio.redhat.com spec: group: appstudio.redhat.com @@ -23,19 +24,14 @@ spec: API properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -47,14 +43,13 @@ spec: the managed release PipelineRun type: string debug: - description: |- - Debug is the boolean that specifies whether or not the Release Service should run - in debug mode + description: Debug is the boolean that specifies whether or not the + Release Service should run in debug mode type: boolean defaultTimeouts: - description: |- - DefaultTimeouts contain the default Tekton timeouts to be used in case they are - not specified in the ReleasePlanAdmission resource. + description: DefaultTimeouts contain the default Tekton timeouts to + be used in case they are not specified in the ReleasePlanAdmission + resource. properties: finally: description: Finally sets the maximum allowed duration of this @@ -80,3 +75,9 @@ spec: storage: true subresources: status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index bc3d9ecb..f3320bf3 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,6 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index e397c149..2b597696 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -2,6 +2,7 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: + creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -86,6 +87,7 @@ webhooks: apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: + creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/controllers/release/adapter.go b/controllers/release/adapter.go index 8915c851..5b8d83fb 100644 --- a/controllers/release/adapter.go +++ b/controllers/release/adapter.go @@ -67,10 +67,10 @@ func newAdapter(ctx context.Context, client client.Client, release *v1alpha1.Rel } releaseAdapter.validations = []controller.ValidationFunction{ + releaseAdapter.validatePipelineDefined, releaseAdapter.validateProcessingResources, releaseAdapter.validateAuthor, releaseAdapter.validatePipelineRef, - releaseAdapter.validateSinglePipeline, } return releaseAdapter @@ -111,7 +111,8 @@ func (a *adapter) EnsureFinalizersAreCalled() (controller.OperationResult, error } if controllerutil.ContainsFinalizer(a.release, metadata.ReleaseFinalizer) { - if err := a.finalizeRelease(); err != nil { + // call finalizeRelease in case Release is deleted before processing finishes + if err := a.finalizeRelease(true); err != nil { return controller.RequeueWithError(err) } @@ -156,8 +157,8 @@ func (a *adapter) EnsureReleaseIsCompleted() (controller.OperationResult, error) return controller.ContinueProcessing() } - // The processing has to complete for a Release to be completed - if !a.release.HasProcessingFinished() { + // The managed pipeline processing has to complete for a Release to be completed + if !a.release.HasManagedPipelineProcessingFinished() { return controller.ContinueProcessing() } @@ -185,16 +186,16 @@ func (a *adapter) EnsureReleaseIsRunning() (controller.OperationResult, error) { // EnsureTenantPipelineIsProcessed is an operation that will ensure that a Tenant Release PipelineRun associated to the Release // being processed exist. Otherwise, it will be created. func (a *adapter) EnsureTenantPipelineIsProcessed() (controller.OperationResult, error) { - if a.release.HasProcessingFinished() { + if a.release.HasTenantPipelineProcessingFinished() { return controller.ContinueProcessing() } - pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release) + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.TenantPipelineType) if err != nil && !errors.IsNotFound(err) { return controller.RequeueWithError(err) } - if pipelineRun == nil || !a.release.IsProcessing() { + if pipelineRun == nil || !a.release.IsTenantPipelineProcessing() { releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) if err != nil { return controller.RequeueWithError(err) @@ -202,7 +203,9 @@ func (a *adapter) EnsureTenantPipelineIsProcessed() (controller.OperationResult, if releasePlan.Spec.Pipeline == nil { // no tenant pipeline to run - return controller.ContinueProcessing() + patch := client.MergeFrom(a.release.DeepCopy()) + a.release.MarkTenantPipelineProcessingSkipped() + return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) } if pipelineRun == nil { @@ -220,7 +223,7 @@ func (a *adapter) EnsureTenantPipelineIsProcessed() (controller.OperationResult, "PipelineRun.Name", pipelineRun.Name, "PipelineRun.Namespace", pipelineRun.Namespace) } - return controller.RequeueOnErrorOrContinue(a.registerProcessingData(pipelineRun, nil)) + return controller.RequeueOnErrorOrContinue(a.registerTenantProcessingData(pipelineRun)) } return controller.ContinueProcessing() @@ -229,11 +232,11 @@ func (a *adapter) EnsureTenantPipelineIsProcessed() (controller.OperationResult, // EnsureManagedPipelineIsProcessed is an operation that will ensure that a managed Release PipelineRun associated to the Release // being processed and a RoleBinding to grant its serviceAccount permissions exist. Otherwise, it will create them. func (a *adapter) EnsureManagedPipelineIsProcessed() (controller.OperationResult, error) { - if a.release.HasProcessingFinished() { + if a.release.HasManagedPipelineProcessingFinished() || !a.release.HasTenantPipelineProcessingFinished() { return controller.ContinueProcessing() } - pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release) + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.ManagedPipelineType) if err != nil && !errors.IsNotFound(err) { return controller.RequeueWithError(err) } @@ -243,13 +246,26 @@ func (a *adapter) EnsureManagedPipelineIsProcessed() (controller.OperationResult return controller.RequeueWithError(err) } - if pipelineRun == nil || !a.release.IsProcessing() { + if pipelineRun == nil || !a.release.IsManagedPipelineProcessing() { resources, err := a.loader.GetProcessingResources(a.ctx, a.client, a.release) if err != nil { + if strings.Contains(err.Error(), "no ReleasePlanAdmissions can be found") { + // No ReleasePlanAdmission, so no managed pipeline to run + patch := client.MergeFrom(a.release.DeepCopy()) + a.release.MarkManagedPipelineProcessingSkipped() + return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) + } return controller.RequeueWithError(err) } if pipelineRun == nil { + if resources.ReleasePlanAdmission.Spec.Pipeline == nil { + // no managed pipeline to run + patch := client.MergeFrom(a.release.DeepCopy()) + a.release.MarkManagedPipelineProcessingSkipped() + return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) + } + // Only create a RoleBinding if a ServiceAccount is specified if roleBinding == nil && resources.ReleasePlanAdmission.Spec.Pipeline.ServiceAccountName != "" { // This string should probably be a constant somewhere @@ -268,7 +284,7 @@ func (a *adapter) EnsureManagedPipelineIsProcessed() (controller.OperationResult "PipelineRun.Name", pipelineRun.Name, "PipelineRun.Namespace", pipelineRun.Namespace) } - return controller.RequeueOnErrorOrContinue(a.registerProcessingData(pipelineRun, roleBinding)) + return controller.RequeueOnErrorOrContinue(a.registerManagedProcessingData(pipelineRun, roleBinding)) } return controller.ContinueProcessing() @@ -316,19 +332,19 @@ func (a *adapter) EnsureReleaseIsValid() (controller.OperationResult, error) { return controller.RequeueOnErrorOrStop(a.client.Status().Patch(a.ctx, a.release, patch)) } -// EnsureReleaseProcessingIsTracked is an operation that will ensure that the Release PipelineRun status is tracked -// in the Release being processed. -func (a *adapter) EnsureReleaseProcessingIsTracked() (controller.OperationResult, error) { - if !a.release.IsProcessing() || a.release.HasProcessingFinished() { +// EnsureReleaseTenantProcessingIsTracked is an operation that will ensure that the Release Tenant PipelineRun status +// is tracked in the Release being processed. +func (a *adapter) EnsureReleaseTenantProcessingIsTracked() (controller.OperationResult, error) { + if !a.release.IsTenantPipelineProcessing() || a.release.HasTenantPipelineProcessingFinished() { return controller.ContinueProcessing() } - pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release) + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.TenantPipelineType) if err != nil { return controller.RequeueWithError(err) } if pipelineRun != nil { - err = a.registerProcessingStatus(pipelineRun) + err = a.registerTenantProcessingStatus(pipelineRun) if err != nil { return controller.RequeueWithError(err) } @@ -337,28 +353,40 @@ func (a *adapter) EnsureReleaseProcessingIsTracked() (controller.OperationResult return controller.ContinueProcessing() } -// EnsureReleaseProcessingResourcesAreCleanedUp is an operation that will ensure that the resources created for the Release -// Processing step are cleaned up once processing is finished. -func (a *adapter) EnsureReleaseProcessingResourcesAreCleanedUp() (controller.OperationResult, error) { - if !a.release.HasProcessingFinished() { +// EnsureReleaseManagedProcessingIsTracked is an operation that will ensure that the Release Managed PipelineRun status +// is tracked in the Release being processed. +func (a *adapter) EnsureReleaseManagedProcessingIsTracked() (controller.OperationResult, error) { + if !a.release.IsManagedPipelineProcessing() || a.release.HasManagedPipelineProcessingFinished() { return controller.ContinueProcessing() } - pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release) - if err != nil && !errors.IsNotFound(err) { + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.ManagedPipelineType) + if err != nil { return controller.RequeueWithError(err) } + if pipelineRun != nil { + err = a.registerManagedProcessingStatus(pipelineRun) + if err != nil { + return controller.RequeueWithError(err) + } + } - roleBinding, err := a.loader.GetRoleBindingFromReleaseStatus(a.ctx, a.client, a.release) - if err != nil && !errors.IsNotFound(err) && !strings.Contains(err.Error(), "valid reference to a RoleBinding") { - return controller.RequeueWithError(err) + return controller.ContinueProcessing() +} + +// EnsureReleaseProcessingResourcesAreCleanedUp is an operation that will ensure that the resources created for the Release +// Processing step are cleaned up once processing is finished. This exists in conjunction with EnsureFinalizersAreCalled because +// the finalizers should be removed from the pipelineRuns even if the Release is not marked for deletion for quota reasons. +func (a *adapter) EnsureReleaseProcessingResourcesAreCleanedUp() (controller.OperationResult, error) { + if !a.release.HasTenantPipelineProcessingFinished() || !a.release.HasManagedPipelineProcessingFinished() { + return controller.ContinueProcessing() } - return controller.RequeueOnErrorOrContinue(a.cleanupProcessingResources(pipelineRun, roleBinding)) + return controller.RequeueOnErrorOrContinue(a.finalizeRelease(false)) } -// cleanupProcessingResources cleans up the PipelineRun created for the Release Processing -// and all resources that were created in order for the PipelineRun to succeed. +// cleanupProcessingResources removes the finalizer from the PipelineRun created for the Release Processing +// and removes the roleBinding that was created in order for the PipelineRun to succeed. func (a *adapter) cleanupProcessingResources(pipelineRun *tektonv1.PipelineRun, roleBinding *rbac.RoleBinding) error { if roleBinding != nil { err := a.client.Delete(a.ctx, roleBinding) @@ -370,8 +398,14 @@ func (a *adapter) cleanupProcessingResources(pipelineRun *tektonv1.PipelineRun, if pipelineRun != nil { if controllerutil.ContainsFinalizer(pipelineRun, metadata.ReleaseFinalizer) { patch := client.MergeFrom(pipelineRun.DeepCopy()) - controllerutil.RemoveFinalizer(pipelineRun, metadata.ReleaseFinalizer) - return a.client.Patch(a.ctx, pipelineRun, patch) + removedFinalizer := controllerutil.RemoveFinalizer(pipelineRun, metadata.ReleaseFinalizer) + if !removedFinalizer { + return fmt.Errorf("finalizer not removed") + } + err := a.client.Patch(a.ctx, pipelineRun, patch) + if err != nil { + return err + } } } @@ -495,29 +529,47 @@ func (a *adapter) createRoleBindingForClusterRole(clusterRole string, releasePla return roleBinding, nil } -// finalizeRelease will finalize the Release being processed, removing the associated resources. -func (a *adapter) finalizeRelease() error { - pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release) +// finalizeRelease will finalize the Release being processed, removing the associated resources. The pipelineRuns are optionally +// deleted so that EnsureReleaseProcessingResourcesAreCleanedUp can call this and just remove the finalizers, but +// EnsureFinalizersAreCalled will remove the finalizers and delete the pipelineRuns. If the pipelineRuns were deleted in +// EnsureReleaseProcessingResourcesAreCleanedUp, they could be removed before all the tracking data is saved. +func (a *adapter) finalizeRelease(delete bool) error { + // Cleanup Tenant Processing Resources + tenantPipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.TenantPipelineType) + if err != nil && !errors.IsNotFound(err) { + return err + } + + err = a.cleanupProcessingResources(tenantPipelineRun, nil) if err != nil { return err } - if pipelineRun != nil { - // The finalizer could still exist at this point in the case of the PipelineRun not having succeeded at the time - // of finalizing the Release. - if controllerutil.ContainsFinalizer(pipelineRun, metadata.ReleaseFinalizer) { - patch := client.MergeFrom(pipelineRun.DeepCopy()) - removedFinalizer := controllerutil.RemoveFinalizer(pipelineRun, metadata.ReleaseFinalizer) - if !removedFinalizer { - return fmt.Errorf("finalizer not removed") - } - err := a.client.Patch(a.ctx, pipelineRun, patch) - if err != nil { - return err - } + if delete && tenantPipelineRun != nil { + err = a.client.Delete(a.ctx, tenantPipelineRun) + if err != nil && !errors.IsNotFound(err) { + return err } + } - err = a.client.Delete(a.ctx, pipelineRun) + // Cleanup Managed Processing Resources + managedPipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.ManagedPipelineType) + if err != nil && !errors.IsNotFound(err) { + return err + } + + roleBinding, err := a.loader.GetRoleBindingFromReleaseStatus(a.ctx, a.client, a.release) + if err != nil && !errors.IsNotFound(err) && !strings.Contains(err.Error(), "valid reference to a RoleBinding") { + return err + } + + err = a.cleanupProcessingResources(managedPipelineRun, roleBinding) + if err != nil { + return err + } + + if delete && managedPipelineRun != nil { + err = a.client.Delete(a.ctx, managedPipelineRun) if err != nil && !errors.IsNotFound(err) { return err } @@ -540,46 +592,83 @@ func (a *adapter) getEmptyReleaseServiceConfig(namespace string) *v1alpha1.Relea return releaseServiceConfig } -// registerProcessingData adds all the Release processing information to its Status and marks it as processing. -func (a *adapter) registerProcessingData(releasePipelineRun *tektonv1.PipelineRun, roleBinding *rbac.RoleBinding) error { +// registerTenantProcessingData adds all the Release Tenant processing information to its Status and marks it as tenant processing. +func (a *adapter) registerTenantProcessingData(releasePipelineRun *tektonv1.PipelineRun) error { if releasePipelineRun == nil { return nil } patch := client.MergeFrom(a.release.DeepCopy()) - a.release.Status.Processing.PipelineRun = fmt.Sprintf("%s%c%s", + a.release.Status.TenantProcessing.PipelineRun = fmt.Sprintf("%s%c%s", + releasePipelineRun.Namespace, types.Separator, releasePipelineRun.Name) + + a.release.MarkTenantPipelineProcessing() + + return a.client.Status().Patch(a.ctx, a.release, patch) +} + +// registerProcessingData adds all the Release Managed processing information to its Status and marks it as managed processing. +func (a *adapter) registerManagedProcessingData(releasePipelineRun *tektonv1.PipelineRun, roleBinding *rbac.RoleBinding) error { + if releasePipelineRun == nil { + return nil + } + + patch := client.MergeFrom(a.release.DeepCopy()) + + a.release.Status.ManagedProcessing.PipelineRun = fmt.Sprintf("%s%c%s", releasePipelineRun.Namespace, types.Separator, releasePipelineRun.Name) if roleBinding != nil { - a.release.Status.Processing.RoleBinding = fmt.Sprintf("%s%c%s", + a.release.Status.ManagedProcessing.RoleBinding = fmt.Sprintf("%s%c%s", roleBinding.Namespace, types.Separator, roleBinding.Name) } - a.release.Status.Target = releasePipelineRun.Namespace - a.release.MarkProcessing("") + a.release.MarkManagedPipelineProcessing() + + return a.client.Status().Patch(a.ctx, a.release, patch) +} + +// registerTenantProcessingStatus updates the status of the Release being processed by monitoring the status of the +// associated tenant Release PipelineRun and setting the appropriate state in the Release. If the PipelineRun hasn't +// started/succeeded, no action will be taken. +func (a *adapter) registerTenantProcessingStatus(pipelineRun *tektonv1.PipelineRun) error { + if pipelineRun == nil || !pipelineRun.IsDone() { + return nil + } + + patch := client.MergeFrom(a.release.DeepCopy()) + + condition := pipelineRun.Status.GetCondition(apis.ConditionSucceeded) + if condition.IsTrue() { + a.release.MarkTenantPipelineProcessed() + } else { + a.release.MarkTenantPipelineProcessingFailed(condition.Message) + a.release.MarkManagedPipelineProcessingSkipped() // Do not run managed pipeline if tenant pipeline fails + a.release.MarkReleaseFailed("Release processing failed on tenant pipelineRun") + } return a.client.Status().Patch(a.ctx, a.release, patch) } -// registerProcessingStatus updates the status of the Release being processed by monitoring the status of the +// registerManagedProcessingStatus updates the status of the Release being processed by monitoring the status of the // associated managed Release PipelineRun and setting the appropriate state in the Release. If the PipelineRun hasn't // started/succeeded, no action will be taken. -func (a *adapter) registerProcessingStatus(pipelineRun *tektonv1.PipelineRun) error { - if pipelineRun != nil && pipelineRun.IsDone() { - patch := client.MergeFrom(a.release.DeepCopy()) +func (a *adapter) registerManagedProcessingStatus(pipelineRun *tektonv1.PipelineRun) error { + if pipelineRun == nil || !pipelineRun.IsDone() { + return nil + } - condition := pipelineRun.Status.GetCondition(apis.ConditionSucceeded) - if condition.IsTrue() { - a.release.MarkProcessed() - } else { - a.release.MarkProcessingFailed(condition.Message) - a.release.MarkReleaseFailed("Release processing failed") - } + patch := client.MergeFrom(a.release.DeepCopy()) - return a.client.Status().Patch(a.ctx, a.release, patch) + condition := pipelineRun.Status.GetCondition(apis.ConditionSucceeded) + if condition.IsTrue() { + a.release.MarkManagedPipelineProcessed() + } else { + a.release.MarkManagedPipelineProcessingFailed(condition.Message) + a.release.MarkReleaseFailed("Release processing failed on managed pipelineRun") } - return nil + return a.client.Status().Patch(a.ctx, a.release, patch) } // validateAuthor will ensure that a valid author exists for the Release and add it to its status. If the Release @@ -681,8 +770,8 @@ func (a *adapter) validatePipelineRef() *controller.ValidationResult { return &controller.ValidationResult{Valid: true} } -// validateSinglePipeline checks that the Pipeline is defined exclusively in the ReleasePlan or in the ReleasePlanAdmission. -func (a *adapter) validateSinglePipeline() *controller.ValidationResult { +// validatePipelineDefined checks that a Pipeline is defined in either the ReleasePlan or in the ReleasePlanAdmission. +func (a *adapter) validatePipelineDefined() *controller.ValidationResult { releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) if err != nil { if errors.IsNotFound(err) { @@ -693,9 +782,32 @@ func (a *adapter) validateSinglePipeline() *controller.ValidationResult { return &controller.ValidationResult{Err: err} } - if releasePlan.Spec.Pipeline != nil && releasePlan.Spec.Target != "" { - a.release.MarkValidationFailed("pipeline should be set only in the ReleasePlan or in the ReleasePlanAdmission") - return &controller.ValidationResult{Valid: false} + if releasePlan.Spec.Target == "" { + a.release.Status.Target = releasePlan.Namespace + } else { + a.release.Status.Target = releasePlan.Spec.Target + } + + if releasePlan.Spec.Pipeline == nil { + if releasePlan.Spec.Target == "" { + errString := "releasePlan has no pipeline or target. Each Release should define a tenant pipeline, managed pipeline, or both" + a.release.MarkValidationFailed(errString) + return &controller.ValidationResult{Valid: false} + } + releasePlanAdmission, err := a.loader.GetActiveReleasePlanAdmissionFromRelease(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 releasePlanAdmission.Spec.Pipeline == nil { + errString := "releasePlan and releasePlanAdmission both have no pipeline. Each Release should define a tenant pipeline, managed pipeline, or both" + a.release.MarkValidationFailed(errString) + return &controller.ValidationResult{Valid: false} + } } return &controller.ValidationResult{Valid: true} diff --git a/controllers/release/adapter_test.go b/controllers/release/adapter_test.go index 9ef39030..8d786484 100644 --- a/controllers/release/adapter_test.go +++ b/controllers/release/adapter_test.go @@ -143,6 +143,14 @@ var _ = Describe("Release adapter", Ordered, func() { It("should finalize the Release if it's set to be deleted and it has a finalizer", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: releasePlan, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: snapshot, + }, { ContextKey: loader.ProcessingResourcesContextKey, Resource: &loader.ProcessingResources{ @@ -163,6 +171,10 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(err).NotTo(HaveOccurred()) Expect(adapter.release.Finalizers).To(HaveLen(1)) + result, err = adapter.EnsureTenantPipelineIsProcessed() + Expect(!result.RequeueRequest && result.CancelRequest).To(BeFalse()) + Expect(err).NotTo(HaveOccurred()) + result, err = adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && result.CancelRequest).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) @@ -177,7 +189,11 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.TenantPipelineType) + Expect(pipelineRun).To(Or(BeNil(), HaveField("DeletionTimestamp", Not(BeNil())))) + Expect(err).NotTo(HaveOccurred()) + + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) Expect(pipelineRun).To(Or(BeNil(), HaveField("DeletionTimestamp", Not(BeNil())))) Expect(err).NotTo(HaveOccurred()) @@ -236,8 +252,8 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: releasePlanAdmission, }, }) - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkManagedPipelineProcessed() adapter.release.MarkReleaseFailed("") result, err := adapter.EnsureReleaseIsCompleted() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) @@ -246,7 +262,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(adapter.release.IsReleased()).To(BeFalse()) }) - It("should do nothing if the processing has not completed", func() { + It("should do nothing if the managed processing has not completed", func() { result, err := adapter.EnsureReleaseIsCompleted() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -260,8 +276,8 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: releasePlanAdmission, }, }) - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkManagedPipelineProcessed() result, err := adapter.EnsureReleaseIsCompleted() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -318,17 +334,100 @@ var _ = Describe("Release adapter", Ordered, func() { adapter.releaseServiceConfig = releaseServiceConfig }) - It("should do nothing if the Release is already processed", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() + It("should do nothing if the Release managed pipeline is already complete", func() { + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkManagedPipelineProcessed() + adapter.release.MarkTenantPipelineProcessingSkipped() + + result, err := adapter.EnsureManagedPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeFalse()) + }) + + It("should do nothing if the Release tenant pipeline processing has not yet completed", func() { + adapter.release.MarkTenantPipelineProcessing() + + result, err := adapter.EnsureManagedPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeFalse()) + }) + + It("should requeue with error if fetching the Release managed pipeline returns an error besides not found", func() { + adapter.release.MarkTenantPipelineProcessingSkipped() + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePipelineRunContextKey, + Err: fmt.Errorf("some error"), + }, + }) + + result, err := adapter.EnsureManagedPipelineIsProcessed() + Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).To(HaveOccurred()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeFalse()) + }) + + It("should mark the Managed Pipeline Processing as Skipped if the ReleasePlanAdmission isn't found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ProcessingResourcesContextKey, + Err: fmt.Errorf("no ReleasePlanAdmissions can be found"), + }, + { + ContextKey: loader.RoleBindingContextKey, + Resource: nil, + }, + }) + adapter.release.MarkTenantPipelineProcessingSkipped() + + result, err := adapter.EnsureManagedPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeFalse()) + Expect(adapter.release.IsManagedPipelineProcessed()).To(BeTrue()) + }) + + It("should mark the Managed Pipeline Processing as Skipped if the ReleasePlanAdmission has no pipeline", func() { + newReleasePlanAdmission := &v1alpha1.ReleasePlanAdmission{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan-admission", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanAdmissionSpec{ + Applications: []string{application.Name}, + Origin: "default", + Policy: enterpriseContractPolicy.Name, + }, + } + newReleasePlanAdmission.Kind = "ReleasePlanAdmission" + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ProcessingResourcesContextKey, + Resource: &loader.ProcessingResources{ + EnterpriseContractConfigMap: enterpriseContractConfigMap, + EnterpriseContractPolicy: enterpriseContractPolicy, + ReleasePlan: releasePlan, + ReleasePlanAdmission: newReleasePlanAdmission, + Snapshot: snapshot, + }, + }, + { + ContextKey: loader.RoleBindingContextKey, + Resource: nil, + }, + }) + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeFalse()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeFalse()) + Expect(adapter.release.IsManagedPipelineProcessed()).To(BeTrue()) }) - It("should continue if the PipelineRun exists and the release processing has started", func() { + It("should continue if the PipelineRun exists and the release managed pipeline processing has started", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { ContextKey: loader.ReleasePipelineRunContextKey, @@ -344,7 +443,8 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: roleBinding, }, }) - adapter.release.MarkProcessing("") + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) @@ -377,11 +477,12 @@ var _ = Describe("Release adapter", Ordered, func() { }, }, }) + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeTrue()) }) It("should requeue the Release if any of the resources is not found", func() { @@ -391,6 +492,7 @@ var _ = Describe("Release adapter", Ordered, func() { Err: fmt.Errorf("not found"), }, }) + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) @@ -414,8 +516,9 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: nil, }, }) + adapter.release.MarkTenantPipelineProcessingSkipped() - Expect(adapter.release.Status.Processing.RoleBinding).To(BeEmpty()) + Expect(adapter.release.Status.ManagedProcessing.RoleBinding).To(BeEmpty()) result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -439,7 +542,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(err).NotTo(HaveOccurred()) Expect(adapter.client.Delete(adapter.ctx, roleBinding)).To(Succeed()) // Still need to cleanup the PipelineRun - pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) @@ -485,8 +588,9 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: nil, }, }) + adapter.release.MarkTenantPipelineProcessingSkipped() - Expect(adapter.release.Status.Processing.RoleBinding).To(BeEmpty()) + Expect(adapter.release.Status.ManagedProcessing.RoleBinding).To(BeEmpty()) result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -509,7 +613,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(roleBinding).To(BeNil()) Expect(err).To(HaveOccurred()) // Still need to cleanup the PipelineRun - pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) @@ -532,13 +636,14 @@ var _ = Describe("Release adapter", Ordered, func() { Resource: roleBinding, }, }) + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureManagedPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeTrue()) - pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) @@ -557,50 +662,47 @@ var _ = Describe("Release adapter", Ordered, func() { adapter.releaseServiceConfig = releaseServiceConfig }) - It("should do nothing if the Release is already processed", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() + It("should do nothing if the Release tenant pipeline is complete", func() { + adapter.release.MarkTenantPipelineProcessing() + adapter.release.MarkTenantPipelineProcessed() result, err := adapter.EnsureTenantPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeFalse()) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should continue if the ReleasePlan has no Pipeline set", func() { - newReleasePlan := releasePlan.DeepCopy() - newReleasePlan.Spec.Pipeline = nil + It("should requeue with error if fetching the Release managed pipeline returns an error besides not found", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { - ContextKey: loader.ProcessingResourcesContextKey, - Resource: &loader.ProcessingResources{ - ReleasePlan: newReleasePlan, - }, + ContextKey: loader.ReleasePipelineRunContextKey, + Err: fmt.Errorf("some error"), }, }) result, err := adapter.EnsureTenantPipelineIsProcessed() - Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) - Expect(err).NotTo(HaveOccurred()) + Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).To(HaveOccurred()) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should continue if the PipelineRun exists and the release processing has started", func() { + It("should continue and mark tenant processing as skipped if the ReleasePlan has no Pipeline set", func() { + newReleasePlan := releasePlan.DeepCopy() + newReleasePlan.Spec.Pipeline = nil adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { - ContextKey: loader.ReleasePipelineRunContextKey, - Resource: &tektonv1.PipelineRun{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pipeline-run", - Namespace: "default", - }, + ContextKey: loader.ProcessingResourcesContextKey, + Resource: &loader.ProcessingResources{ + ReleasePlan: newReleasePlan, }, }, }) - adapter.release.MarkProcessing("") result, err := adapter.EnsureTenantPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeFalse()) + Expect(adapter.release.IsTenantPipelineProcessed()).To(BeTrue()) }) It("should register the processing data if the PipelineRun already exists", func() { @@ -614,20 +716,13 @@ var _ = Describe("Release adapter", Ordered, func() { }, }, }, - { - ContextKey: loader.ProcessingResourcesContextKey, - Resource: &loader.ProcessingResources{ - ReleasePlan: releasePlan, - Snapshot: snapshot, - }, - }, }) - adapter.release.MarkProcessing("") + adapter.release.MarkTenantPipelineProcessing() result, err := adapter.EnsureTenantPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeTrue()) }) It("should requeue the Release if any of the resources is not found", func() { @@ -691,9 +786,9 @@ var _ = Describe("Release adapter", Ordered, func() { result, err := adapter.EnsureTenantPipelineIsProcessed() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeTrue()) - pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.TenantPipelineType) Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) @@ -760,7 +855,65 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) - When("EnsureReleaseProcessingIsTracked is called", func() { + When("EnsureReleaseTenantProcessingIsTracked is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + }) + + It("should continue if the Release tenant pipeline processing has not started", func() { + result, err := adapter.EnsureReleaseTenantProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should continue if the Release tenant pipeline processing has finished", func() { + adapter.release.MarkTenantPipelineProcessing() + adapter.release.MarkTenantPipelineProcessed() + + result, err := adapter.EnsureReleaseTenantProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should track the status if the PipelineRun exists", func() { + adapter.release.MarkTenantPipelineProcessing() + + pipelineRun := &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline-run", + Namespace: "default", + }, + } + pipelineRun.Status.MarkSucceeded("", "") + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePipelineRunContextKey, + Resource: pipelineRun, + }, + }) + + result, err := adapter.EnsureReleaseTenantProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.HasTenantPipelineProcessingFinished()).To(BeTrue()) + }) + + It("should continue if the PipelineRun doesn't exist", func() { + adapter.release.MarkTenantPipelineProcessing() + + result, err := adapter.EnsureReleaseTenantProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + When("EnsureReleaseManagedProcessingIsTracked is called", func() { var adapter *adapter AfterEach(func() { @@ -771,23 +924,23 @@ var _ = Describe("Release adapter", Ordered, func() { adapter = createReleaseAndAdapter() }) - It("should continue if the Release processing has not started", func() { - result, err := adapter.EnsureReleaseProcessingIsTracked() + It("should continue if the Release managed pipeline processing has not started", func() { + result, err := adapter.EnsureReleaseManagedProcessingIsTracked() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) - It("should continue if the Release processing has finished", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() + It("should continue if the Release managed pipeline processing has finished", func() { + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkManagedPipelineProcessed() - result, err := adapter.EnsureReleaseProcessingIsTracked() + result, err := adapter.EnsureReleaseManagedProcessingIsTracked() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) It("should track the status if the PipelineRun exists", func() { - adapter.release.MarkProcessing("") + adapter.release.MarkManagedPipelineProcessing() pipelineRun := &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ @@ -806,16 +959,16 @@ var _ = Describe("Release adapter", Ordered, func() { }, }) - result, err := adapter.EnsureReleaseProcessingIsTracked() + result, err := adapter.EnsureReleaseManagedProcessingIsTracked() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) - Expect(adapter.release.HasProcessingFinished()).To(BeTrue()) + Expect(adapter.release.HasManagedPipelineProcessingFinished()).To(BeTrue()) }) It("should continue if the PipelineRun doesn't exist", func() { - adapter.release.MarkProcessing("") + adapter.release.MarkManagedPipelineProcessing() - result, err := adapter.EnsureReleaseProcessingIsTracked() + result, err := adapter.EnsureReleaseManagedProcessingIsTracked() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) @@ -897,78 +1050,89 @@ var _ = Describe("Release adapter", Ordered, func() { adapter = createReleaseAndAdapter() }) - It("should continue if the Release processing has not finished", func() { + It("should continue if the Release tenant processing has not finished", func() { + adapter.release.MarkManagedPipelineProcessingSkipped() result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) - It("should requeue the release if an error occurs fetching the managed pipelineRun", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() - adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ - { - ContextKey: loader.ReleasePipelineRunContextKey, - Err: fmt.Errorf("error"), - }, - }) + It("should continue if the Release managed processing has not finished", func() { + adapter.release.MarkTenantPipelineProcessingSkipped() result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() - Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) - Expect(err).To(HaveOccurred()) + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) }) - It("should requeue the release if an error occurs fetching the roleBinding", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() - pipelineRun := &tektonv1.PipelineRun{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pipeline-run", - Namespace: "default", - }, + It("should call finalizeRelease with false if all release processing is complete", func() { + adapter.releaseServiceConfig = releaseServiceConfig + resources := &loader.ProcessingResources{ + ReleasePlan: releasePlan, + ReleasePlanAdmission: releasePlanAdmission, + EnterpriseContractConfigMap: enterpriseContractConfigMap, + EnterpriseContractPolicy: enterpriseContractPolicy, + Snapshot: snapshot, } - adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ - { - ContextKey: loader.ReleasePipelineRunContextKey, - Resource: pipelineRun, - }, - { - ContextKey: loader.RoleBindingContextKey, - Err: fmt.Errorf("error"), + parameterizedPipeline := tektonutils.ParameterizedPipeline{} + parameterizedPipeline.PipelineRef = tektonutils.PipelineRef{ + Resolver: "git", + Params: []tektonutils.Param{ + {Name: "url", Value: "my-url"}, + {Name: "revision", Value: "my-revision"}, + {Name: "pathInRepo", Value: "my-path"}, }, - }) - adapter.release.Status.Processing.RoleBinding = "one/two" - result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() - Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) - Expect(err).To(HaveOccurred()) - }) + } + parameterizedPipeline.Params = []tektonutils.Param{ + {Name: "parameter1", Value: "value1"}, + {Name: "parameter2", Value: "value2"}, + } + parameterizedPipeline.Timeouts = tektonv1.TimeoutFields{ + Pipeline: &metav1.Duration{Duration: 1 * time.Hour}, + } - It("should call cleanupManagedPipelineRunResources if all the resources are present", func() { - adapter.release.MarkProcessing("") - adapter.release.MarkProcessed() - pipelineRun := &tektonv1.PipelineRun{ + newReleasePlan := &v1alpha1.ReleasePlan{ ObjectMeta: metav1.ObjectMeta{ - Name: "pipeline-run", + Name: "release-plan", Namespace: "default", }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + Pipeline: ¶meterizedPipeline, + ReleaseGracePeriodDays: 6, + }, } - newRoleBinding := roleBinding.DeepCopy() + newReleasePlan.Kind = "ReleasePlan" - adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ - { - ContextKey: loader.ReleasePipelineRunContextKey, - Resource: pipelineRun, - }, - { - ContextKey: loader.RoleBindingContextKey, - Resource: newRoleBinding, - }, - }) - adapter.release.Status.Processing.RoleBinding = fmt.Sprintf("%s%c%s", - newRoleBinding.Namespace, types.Separator, newRoleBinding.Name) + // Create tenant and managed pipelineRuns + pipelineRun, err := adapter.createTenantPipelineRun(newReleasePlan, snapshot) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + + pipelineRun, err = adapter.createManagedPipelineRun(resources) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + adapter.release.MarkTenantPipelineProcessing() + adapter.release.MarkTenantPipelineProcessed() + adapter.release.MarkManagedPipelineProcessing() + adapter.release.MarkManagedPipelineProcessed() + + // Ensure both pipelineRuns have finalizers removed result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) + + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.TenantPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).NotTo(BeNil()) + Expect(pipelineRun.Finalizers).To(HaveLen(0)) + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) + + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).NotTo(BeNil()) + Expect(pipelineRun.Finalizers).To(HaveLen(0)) + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) }) }) @@ -1007,7 +1171,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(errors.IsNotFound(err)).To(BeTrue()) }) - It("removes the pipelineRun finalizer if present", func() { + It("removes the pipelineRun if present", func() { pipelineRun := &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pipeline-run", @@ -1020,10 +1184,15 @@ var _ = Describe("Release adapter", Ordered, func() { err := adapter.cleanupProcessingResources(pipelineRun, nil) Expect(err).NotTo(HaveOccurred()) - Expect(pipelineRun.Finalizers).To(BeEmpty()) - // Clean up at the end - Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) + checkPipelineRun := &tektonv1.PipelineRun{} + err = toolkit.GetObject(pipelineRun.Name, pipelineRun.Namespace, adapter.client, adapter.ctx, checkPipelineRun) + Expect(err).NotTo(HaveOccurred()) + Expect(checkPipelineRun).NotTo(BeNil()) + Expect(checkPipelineRun.Finalizers).To(HaveLen(0)) + + // Cleanup as only the finalizer was removed + Expect(adapter.client.Delete(adapter.ctx, checkPipelineRun)).To(Succeed()) }) It("should not error if either resource is nil", func() { @@ -1351,6 +1520,8 @@ var _ = Describe("Release adapter", Ordered, func() { When("finalizeRelease is called", func() { var adapter *adapter + var parameterizedPipeline *tektonutils.ParameterizedPipeline + var newReleasePlan *v1alpha1.ReleasePlan AfterEach(func() { _ = adapter.client.Delete(ctx, adapter.release) @@ -1358,29 +1529,98 @@ var _ = Describe("Release adapter", Ordered, func() { BeforeEach(func() { adapter = createReleaseAndAdapter() - }) - - It("finalizes the Release successfully", func() { - Expect(adapter.finalizeRelease()).To(Succeed()) - }) - - It("finalizes the Release and deletes the PipelineRun", func() { - adapter.releaseServiceConfig = releaseServiceConfig - resources := &loader.ProcessingResources{ - ReleasePlan: releasePlan, - ReleasePlanAdmission: releasePlanAdmission, - EnterpriseContractConfigMap: enterpriseContractConfigMap, - EnterpriseContractPolicy: enterpriseContractPolicy, - Snapshot: snapshot, - } - pipelineRun, err := adapter.createManagedPipelineRun(resources) - Expect(pipelineRun).NotTo(BeNil()) - Expect(err).NotTo(HaveOccurred()) - - Expect(adapter.finalizeRelease()).To(Succeed()) - pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release) - Expect(err).NotTo(HaveOccurred()) - Expect(pipelineRun).To(BeNil()) + parameterizedPipeline = &tektonutils.ParameterizedPipeline{} + parameterizedPipeline.PipelineRef = tektonutils.PipelineRef{ + Resolver: "git", + Params: []tektonutils.Param{ + {Name: "url", Value: "my-url"}, + {Name: "revision", Value: "my-revision"}, + {Name: "pathInRepo", Value: "my-path"}, + }, + } + parameterizedPipeline.Params = []tektonutils.Param{ + {Name: "parameter1", Value: "value1"}, + {Name: "parameter2", Value: "value2"}, + } + parameterizedPipeline.Timeouts = tektonv1.TimeoutFields{ + Pipeline: &metav1.Duration{Duration: 1 * time.Hour}, + } + newReleasePlan = &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + Pipeline: parameterizedPipeline, + ReleaseGracePeriodDays: 6, + }, + } + newReleasePlan.Kind = "ReleasePlan" + }) + + It("finalizes the Release and removes the finalizer from the Tenant PipelineRun when called with false", func() { + pipelineRun, err := adapter.createTenantPipelineRun(newReleasePlan, snapshot) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + + Expect(adapter.finalizeRelease(false)).To(Succeed()) + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.TenantPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).NotTo(BeNil()) + Expect(pipelineRun.Finalizers).To(HaveLen(0)) + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) + }) + + It("finalizes the Release and removes the finalizer from the Managed PipelineRun when called with false", func() { + adapter.releaseServiceConfig = releaseServiceConfig + resources := &loader.ProcessingResources{ + ReleasePlan: releasePlan, + ReleasePlanAdmission: releasePlanAdmission, + EnterpriseContractConfigMap: enterpriseContractConfigMap, + EnterpriseContractPolicy: enterpriseContractPolicy, + Snapshot: snapshot, + } + pipelineRun, err := adapter.createManagedPipelineRun(resources) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + + Expect(adapter.finalizeRelease(false)).To(Succeed()) + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).NotTo(BeNil()) + Expect(pipelineRun.Finalizers).To(HaveLen(0)) + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) + }) + + It("finalizes the Release and deletes the Tenant PipelineRun when called with true", func() { + pipelineRun, err := adapter.createTenantPipelineRun(newReleasePlan, snapshot) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + + Expect(adapter.finalizeRelease(true)).To(Succeed()) + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.TenantPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).To(BeNil()) + }) + + It("finalizes the Release and deletes the Managed PipelineRun when called with true", func() { + adapter.releaseServiceConfig = releaseServiceConfig + resources := &loader.ProcessingResources{ + ReleasePlan: releasePlan, + ReleasePlanAdmission: releasePlanAdmission, + EnterpriseContractConfigMap: enterpriseContractConfigMap, + EnterpriseContractPolicy: enterpriseContractPolicy, + Snapshot: snapshot, + } + pipelineRun, err := adapter.createManagedPipelineRun(resources) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + + Expect(adapter.finalizeRelease(true)).To(Succeed()) + pipelineRun, err = adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.ManagedPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).To(BeNil()) }) }) @@ -1404,7 +1644,7 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) - When("registerProcessingData is called", func() { + When("registerTenantProcessingData is called", func() { var adapter *adapter AfterEach(func() { @@ -1416,11 +1656,41 @@ var _ = Describe("Release adapter", Ordered, func() { }) It("does nothing if there is no PipelineRun", func() { - Expect(adapter.registerProcessingData(nil, nil)).To(Succeed()) - Expect(adapter.release.Status.Processing.PipelineRun).To(BeEmpty()) + Expect(adapter.registerTenantProcessingData(nil)).To(Succeed()) + Expect(adapter.release.Status.TenantProcessing.PipelineRun).To(BeEmpty()) }) - It("registers the Release processing data", func() { + It("registers the Release tenant processing data", func() { + pipelineRun := &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline-run", + Namespace: "default", + }, + } + Expect(adapter.registerTenantProcessingData(pipelineRun)).To(Succeed()) + Expect(adapter.release.Status.TenantProcessing.PipelineRun).To(Equal(fmt.Sprintf("%s%c%s", + pipelineRun.Namespace, types.Separator, pipelineRun.Name))) + Expect(adapter.release.IsTenantPipelineProcessing()).To(BeTrue()) + }) + }) + + When("registerManagedProcessingData is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + }) + + It("does nothing if there is no PipelineRun", func() { + Expect(adapter.registerManagedProcessingData(nil, nil)).To(Succeed()) + Expect(adapter.release.Status.ManagedProcessing.PipelineRun).To(BeEmpty()) + }) + + It("registers the Release managed processing data", func() { pipelineRun := &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "pipeline-run", @@ -1433,13 +1703,12 @@ var _ = Describe("Release adapter", Ordered, func() { Namespace: "default", }, } - Expect(adapter.registerProcessingData(pipelineRun, roleBinding)).To(Succeed()) - Expect(adapter.release.Status.Processing.PipelineRun).To(Equal(fmt.Sprintf("%s%c%s", + Expect(adapter.registerManagedProcessingData(pipelineRun, roleBinding)).To(Succeed()) + Expect(adapter.release.Status.ManagedProcessing.PipelineRun).To(Equal(fmt.Sprintf("%s%c%s", pipelineRun.Namespace, types.Separator, pipelineRun.Name))) - Expect(adapter.release.Status.Processing.RoleBinding).To(Equal(fmt.Sprintf("%s%c%s", + Expect(adapter.release.Status.ManagedProcessing.RoleBinding).To(Equal(fmt.Sprintf("%s%c%s", roleBinding.Namespace, types.Separator, roleBinding.Name))) - Expect(adapter.release.Status.Target).To(Equal(pipelineRun.Namespace)) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeTrue()) }) It("does not set RoleBinding when no RoleBinding is passed", func() { @@ -1450,13 +1719,13 @@ var _ = Describe("Release adapter", Ordered, func() { }, } - Expect(adapter.registerProcessingData(pipelineRun, nil)).To(Succeed()) - Expect(adapter.release.Status.Processing.RoleBinding).To(BeEmpty()) - Expect(adapter.release.IsProcessing()).To(BeTrue()) + Expect(adapter.registerManagedProcessingData(pipelineRun, nil)).To(Succeed()) + Expect(adapter.release.Status.ManagedProcessing.RoleBinding).To(BeEmpty()) + Expect(adapter.release.IsManagedPipelineProcessing()).To(BeTrue()) }) }) - When("registerProcessingStatus is called", func() { + When("registerTenantProcessingStatus is called", func() { var adapter *adapter AfterEach(func() { @@ -1468,33 +1737,76 @@ var _ = Describe("Release adapter", Ordered, func() { }) It("does nothing if there is no PipelineRun", func() { - Expect(adapter.registerProcessingStatus(nil)).To(Succeed()) - Expect(adapter.release.Status.Processing.CompletionTime).To(BeNil()) + Expect(adapter.registerTenantProcessingStatus(nil)).To(Succeed()) + Expect(adapter.release.Status.TenantProcessing.CompletionTime).To(BeNil()) }) It("does nothing if the PipelineRun is not done", func() { pipelineRun := &tektonv1.PipelineRun{} - Expect(adapter.registerProcessingStatus(pipelineRun)).To(Succeed()) - Expect(adapter.release.Status.Processing.CompletionTime).To(BeNil()) + Expect(adapter.registerTenantProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.Status.TenantProcessing.CompletionTime).To(BeNil()) }) - It("sets the Release as succeeded if the PipelineRun succeeded", func() { + It("sets the Release as Tenant Processed if the PipelineRun succeeded", func() { pipelineRun := &tektonv1.PipelineRun{} pipelineRun.Status.MarkSucceeded("", "") - adapter.release.MarkProcessing("") + adapter.release.MarkTenantPipelineProcessing() - Expect(adapter.registerProcessingStatus(pipelineRun)).To(Succeed()) - Expect(adapter.release.IsProcessed()).To(BeTrue()) + Expect(adapter.registerTenantProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.IsTenantPipelineProcessed()).To(BeTrue()) }) - It("sets the Release as failed if the PipelineRun didn't succeed", func() { + It("sets the Release as Tenant Processing failed and Managed Processing skipped if the PipelineRun didn't succeed", func() { pipelineRun := &tektonv1.PipelineRun{} pipelineRun.Status.MarkFailed("", "") - adapter.release.MarkProcessing("") + adapter.release.MarkTenantPipelineProcessing() - Expect(adapter.registerProcessingStatus(pipelineRun)).To(Succeed()) - Expect(adapter.release.HasProcessingFinished()).To(BeTrue()) - Expect(adapter.release.IsProcessed()).To(BeFalse()) + Expect(adapter.registerTenantProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.HasTenantPipelineProcessingFinished()).To(BeTrue()) + Expect(adapter.release.IsTenantPipelineProcessed()).To(BeFalse()) + Expect(adapter.release.IsManagedPipelineProcessed()).To(BeTrue()) + }) + }) + + When("registerManagedProcessingStatus is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + }) + + It("does nothing if there is no PipelineRun", func() { + Expect(adapter.registerManagedProcessingStatus(nil)).To(Succeed()) + Expect(adapter.release.Status.ManagedProcessing.CompletionTime).To(BeNil()) + }) + + It("does nothing if the PipelineRun is not done", func() { + pipelineRun := &tektonv1.PipelineRun{} + Expect(adapter.registerManagedProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.Status.ManagedProcessing.CompletionTime).To(BeNil()) + }) + + It("sets the Release as Managed Processed if the PipelineRun succeeded", func() { + pipelineRun := &tektonv1.PipelineRun{} + pipelineRun.Status.MarkSucceeded("", "") + adapter.release.MarkManagedPipelineProcessing() + + Expect(adapter.registerManagedProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.IsManagedPipelineProcessed()).To(BeTrue()) + }) + + It("sets the Release as Managed Processing failed if the PipelineRun didn't succeed", func() { + pipelineRun := &tektonv1.PipelineRun{} + pipelineRun.Status.MarkFailed("", "") + adapter.release.MarkManagedPipelineProcessing() + + Expect(adapter.registerManagedProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.HasManagedPipelineProcessingFinished()).To(BeTrue()) + Expect(adapter.release.IsManagedPipelineProcessed()).To(BeFalse()) }) }) @@ -1832,7 +2144,7 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) - When("validateSinglePipeline is called", func() { + When("validatePipelineDefined is called", func() { var adapter *adapter var parameterizedPipeline *tektonutils.ParameterizedPipeline @@ -1841,12 +2153,7 @@ var _ = Describe("Release adapter", Ordered, func() { }) BeforeEach(func() { - adapter = createReleaseAndAdapter() - releaseServiceConfig.Spec = v1alpha1.ReleaseServiceConfigSpec{} - adapter.releaseServiceConfig = releaseServiceConfig - adapter.release.MarkReleasing("") - parameterizedPipeline = &tektonutils.ParameterizedPipeline{} parameterizedPipeline.PipelineRef = tektonutils.PipelineRef{ Resolver: "git", @@ -1865,7 +2172,7 @@ var _ = Describe("Release adapter", Ordered, func() { } }) - It("should return false if ReleasePlanAdmission and ReleasePlan has Pipeline Set", func() { + It("should return true if ReleasePlanAdmission and ReleasePlan have Pipeline Set", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { ContextKey: loader.ReleasePlanAdmissionContextKey, @@ -1905,18 +2212,222 @@ var _ = Describe("Release adapter", Ordered, func() { Application: application.Name, Pipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, - Target: "default", // the target should cause an error + Target: "default", }, }, }, }) - adapter.releaseServiceConfig.Spec.Debug = false + result := adapter.validatePipelineDefined() + Expect(result.Valid).To(BeTrue()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("should return true if only ReleasePlan has Pipeline Set", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + Pipeline: parameterizedPipeline, + ReleaseGracePeriodDays: 6, + Target: "default", + }, + }, + }, + }) + + result := adapter.validatePipelineDefined() + Expect(result.Valid).To(BeTrue()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("should return true if only ReleasePlan has Pipeline Set and it has no Target", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + Pipeline: parameterizedPipeline, + ReleaseGracePeriodDays: 6, + }, + }, + }, + }) + + result := adapter.validatePipelineDefined() + Expect(result.Valid).To(BeTrue()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("should return true if only ReleasePlanAdmission has Pipeline Set", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanAdmissionContextKey, + Resource: &v1alpha1.ReleasePlanAdmission{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan-admission", + Namespace: "default", + Labels: map[string]string{ + metadata.AutoReleaseLabel: "true", + }, + }, + Spec: v1alpha1.ReleasePlanAdmissionSpec{ + Applications: []string{application.Name}, + Origin: "default", + Pipeline: &tektonutils.Pipeline{ + PipelineRef: tektonutils.PipelineRef{ + Resolver: "cluster", + Params: []tektonutils.Param{ + {Name: "name", Value: "release-pipeline"}, + {Name: "namespace", Value: "default"}, + {Name: "kind", Value: "pipeline"}, + }, + }, + }, + Policy: enterpriseContractPolicy.Name, + }, + }, + }, + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + ReleaseGracePeriodDays: 6, + Target: "default", + }, + }, + }, + }) + + result := adapter.validatePipelineDefined() + Expect(result.Valid).To(BeTrue()) + Expect(result.Err).NotTo(HaveOccurred()) + }) - result := adapter.validateSinglePipeline() + It("should return false if ReleasePlan has no Pipeline or Target set", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + ReleaseGracePeriodDays: 6, + }, + }, + }, + }) + + result := adapter.validatePipelineDefined() Expect(result.Valid).To(BeFalse()) Expect(result.Err).NotTo(HaveOccurred()) - Expect(adapter.release.IsValid()).To(BeFalse()) + }) + + It("should return false if neither ReleasePlanAdmission nor ReleasePlan have Pipeline Set", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanAdmissionContextKey, + Resource: &v1alpha1.ReleasePlanAdmission{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan-admission", + Namespace: "default", + Labels: map[string]string{ + metadata.AutoReleaseLabel: "true", + }, + }, + Spec: v1alpha1.ReleasePlanAdmissionSpec{ + Applications: []string{application.Name}, + Origin: "default", + Policy: enterpriseContractPolicy.Name, + }, + }, + }, + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + ReleaseGracePeriodDays: 6, + Target: "default", + }, + }, + }, + }) + + result := adapter.validatePipelineDefined() + Expect(result.Valid).To(BeFalse()) + Expect(result.Err).NotTo(HaveOccurred()) + }) + + It("should set the target in the release status to the ReleasePlan namespace if no target is defined", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + Pipeline: parameterizedPipeline, + ReleaseGracePeriodDays: 6, + }, + }, + }, + }) + + _ = adapter.validatePipelineDefined() + Expect(adapter.release.Status.Target).To(Equal("default")) + }) + + It("should set the target in the release status to the ReleasePlan target if one is defined", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + ReleaseGracePeriodDays: 6, + Target: "foo", + }, + }, + }, + { + ContextKey: loader.ReleasePlanAdmissionContextKey, + Resource: releasePlanAdmission, + }, + }) + + _ = adapter.validatePipelineDefined() + Expect(adapter.release.Status.Target).To(Equal("foo")) }) }) diff --git a/controllers/release/controller.go b/controllers/release/controller.go index d6d569a2..eef5dad3 100644 --- a/controllers/release/controller.go +++ b/controllers/release/controller.go @@ -82,8 +82,9 @@ func (c *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu adapter.EnsureFinalizerIsAdded, adapter.EnsureReleaseExpirationTimeIsAdded, adapter.EnsureTenantPipelineIsProcessed, + adapter.EnsureReleaseTenantProcessingIsTracked, adapter.EnsureManagedPipelineIsProcessed, - adapter.EnsureReleaseProcessingIsTracked, + adapter.EnsureReleaseManagedProcessingIsTracked, adapter.EnsureReleaseProcessingResourcesAreCleanedUp, adapter.EnsureReleaseIsCompleted, }) diff --git a/loader/loader.go b/loader/loader.go index b7b309a4..bf8f08f4 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -3,11 +3,12 @@ package loader import ( "context" "fmt" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime/schema" "os" "strings" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/strings/slices" toolkit "github.com/konflux-ci/operator-toolkit/loader" @@ -34,7 +35,7 @@ type ObjectLoader interface { GetPreviousRelease(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*v1alpha1.Release, error) GetRelease(ctx context.Context, cli client.Client, name, namespace string) (*v1alpha1.Release, error) GetRoleBindingFromReleaseStatus(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*rbac.RoleBinding, error) - GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*tektonv1.PipelineRun, error) + GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release, pipelineType string) (*tektonv1.PipelineRun, error) GetReleasePlan(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*v1alpha1.ReleasePlan, error) GetReleaseServiceConfig(ctx context.Context, cli client.Client, name, namespace string) (*v1alpha1.ReleaseServiceConfig, error) GetSnapshot(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*applicationapiv1alpha1.Snapshot, error) @@ -119,6 +120,11 @@ func (l *loader) GetMatchingReleasePlanAdmission(ctx context.Context, cli client return releasePlanAdmission, toolkit.GetObject(designatedReleasePlanAdmissionName, releasePlan.Spec.Target, cli, ctx, releasePlanAdmission) } + // This is a temporary hack to work around RELEASE-1150. It should improved when that ticket is complete. + if releasePlan.Spec.Target == "" { + return nil, fmt.Errorf("releasePlan has no target, so no ReleasePlanAdmissions can be found") + } + releasePlanAdmissions := &v1alpha1.ReleasePlanAdmissionList{} err := cli.List(ctx, releasePlanAdmissions, client.InNamespace(releasePlan.Spec.Target), @@ -220,10 +226,10 @@ func (l *loader) GetRelease(ctx context.Context, cli client.Client, name, namesp // by the namespaced name stored in the Release's status. func (l *loader) GetRoleBindingFromReleaseStatus(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*rbac.RoleBinding, error) { roleBinding := &rbac.RoleBinding{} - roleBindingNamespacedName := strings.Split(release.Status.Processing.RoleBinding, string(types.Separator)) + roleBindingNamespacedName := strings.Split(release.Status.ManagedProcessing.RoleBinding, string(types.Separator)) if len(roleBindingNamespacedName) != 2 { return nil, fmt.Errorf("release doesn't contain a valid reference to a RoleBinding ('%s')", - release.Status.Processing.RoleBinding) + release.Status.ManagedProcessing.RoleBinding) } err := cli.Get(ctx, types.NamespacedName{ @@ -237,15 +243,20 @@ func (l *loader) GetRoleBindingFromReleaseStatus(ctx context.Context, cli client return roleBinding, nil } -// GetReleasePipelineRun returns the Release PipelineRun referenced by the given Release or nil if it's not found. In the case -// the List operation fails, an error will be returned. -func (l *loader) GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*tektonv1.PipelineRun, error) { +// GetReleasePipelineRun returns the Release PipelineRun of the specified type referenced by the given Release +// or nil if it's not found. In the case the List operation fails, an error will be returned. +func (l *loader) GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release, pipelineType string) (*tektonv1.PipelineRun, error) { + if pipelineType != metadata.ManagedPipelineType && pipelineType != metadata.TenantPipelineType { + return nil, fmt.Errorf("cannot fetch Release PipelineRun with invalid type %s", pipelineType) + } + pipelineRuns := &tektonv1.PipelineRunList{} err := cli.List(ctx, pipelineRuns, client.Limit(1), client.MatchingLabels{ metadata.ReleaseNameLabel: release.Name, metadata.ReleaseNamespaceLabel: release.Namespace, + metadata.PipelinesTypeLabel: pipelineType, }) if err == nil && len(pipelineRuns.Items) > 0 { return &pipelineRuns.Items[0], nil diff --git a/loader/loader_mock.go b/loader/loader_mock.go index ce90d6e1..7b716004 100644 --- a/loader/loader_mock.go +++ b/loader/loader_mock.go @@ -123,9 +123,9 @@ func (l *mockLoader) GetRoleBindingFromReleaseStatus(ctx context.Context, cli cl } // GetReleasePipelineRun returns the resource and error passed as values of the context. -func (l *mockLoader) GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release) (*tektonv1.PipelineRun, error) { +func (l *mockLoader) GetReleasePipelineRun(ctx context.Context, cli client.Client, release *v1alpha1.Release, pipelineType string) (*tektonv1.PipelineRun, error) { if ctx.Value(ReleasePipelineRunContextKey) == nil { - return l.loader.GetReleasePipelineRun(ctx, cli, release) + return l.loader.GetReleasePipelineRun(ctx, cli, release, pipelineType) } return toolkit.GetMockedResourceAndErrorFromContext(ctx, ReleasePipelineRunContextKey, &tektonv1.PipelineRun{}) } diff --git a/loader/loader_mock_test.go b/loader/loader_mock_test.go index b8296c3c..50424a0d 100644 --- a/loader/loader_mock_test.go +++ b/loader/loader_mock_test.go @@ -5,6 +5,7 @@ import ( v1alpha12 "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1" "github.com/konflux-ci/release-service/api/v1alpha1" + "github.com/konflux-ci/release-service/metadata" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" applicationapiv1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" @@ -165,7 +166,7 @@ var _ = Describe("Release Adapter", Ordered, func() { Resource: pipelineRun, }, }) - resource, err := loader.GetReleasePipelineRun(mockContext, nil, nil) + resource, err := loader.GetReleasePipelineRun(mockContext, nil, nil, metadata.ManagedPipelineType) Expect(resource).To(Equal(pipelineRun)) Expect(err).To(BeNil()) }) diff --git a/loader/loader_test.go b/loader/loader_test.go index 591af9b5..b84c8e96 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -34,7 +34,8 @@ var _ = Describe("Release Adapter", Ordered, func() { component *applicationapiv1alpha1.Component enterpriseContractConfigMap *corev1.ConfigMap enterpriseContractPolicy *ecapiv1alpha1.EnterpriseContractPolicy - pipelineRun *tektonv1.PipelineRun + managedPipelineRun *tektonv1.PipelineRun + tenantPipelineRun *tektonv1.PipelineRun release *v1alpha1.Release releasePlan *v1alpha1.ReleasePlan releasePlanAdmission *v1alpha1.ReleasePlanAdmission @@ -184,6 +185,16 @@ var _ = Describe("Release Adapter", Ordered, func() { Expect(returnedObject).To(BeNil()) }) + It("fails to return a release plan admission if the target is nil", func() { + modifiedReleasePlan := releasePlan.DeepCopy() + modifiedReleasePlan.Spec.Target = "" + + returnedObject, err := loader.GetMatchingReleasePlanAdmission(ctx, k8sClient, modifiedReleasePlan) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("has no target")) + Expect(returnedObject).To(BeNil()) + }) + It("fails to return a release plan admission if multiple matches are found", func() { newReleasePlanAdmission := releasePlanAdmission.DeepCopy() newReleasePlanAdmission.Name = "new-release-plan-admission" @@ -335,7 +346,7 @@ var _ = Describe("Release Adapter", Ordered, func() { It("fails to return a RoleBinding if the roleBinding does not exist", func() { modifiedRelease := release.DeepCopy() - modifiedRelease.Status.Processing.RoleBinding = "foo/bar" + modifiedRelease.Status.ManagedProcessing.RoleBinding = "foo/bar" returnedObject, err := loader.GetRoleBindingFromReleaseStatus(ctx, k8sClient, modifiedRelease) Expect(returnedObject).To(BeNil()) @@ -344,7 +355,7 @@ var _ = Describe("Release Adapter", Ordered, func() { It("returns the requested resource", func() { modifiedRelease := release.DeepCopy() - modifiedRelease.Status.Processing.RoleBinding = fmt.Sprintf("%s%c%s", roleBinding.Namespace, + modifiedRelease.Status.ManagedProcessing.RoleBinding = fmt.Sprintf("%s%c%s", roleBinding.Namespace, types.Separator, roleBinding.Name) returnedObject, err := loader.GetRoleBindingFromReleaseStatus(ctx, k8sClient, modifiedRelease) @@ -355,18 +366,25 @@ var _ = Describe("Release Adapter", Ordered, func() { }) When("calling GetReleasePipelineRun", func() { - It("returns a PipelineRun if the labels match with the release data", func() { - returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, release) + It("returns a Managed PipelineRun if the labels match with the release data", func() { + returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, release, metadata.ManagedPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(returnedObject).NotTo(Equal(&tektonv1.PipelineRun{})) + Expect(returnedObject.Name).To(Equal(managedPipelineRun.Name)) + }) + + It("returns a Tenant PipelineRun if the labels match with the release data", func() { + returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, release, metadata.TenantPipelineType) Expect(err).NotTo(HaveOccurred()) Expect(returnedObject).NotTo(Equal(&tektonv1.PipelineRun{})) - Expect(returnedObject.Name).To(Equal(pipelineRun.Name)) + Expect(returnedObject.Name).To(Equal(tenantPipelineRun.Name)) }) It("fails to return a PipelineRun if the labels don't match with the release data", func() { modifiedRelease := release.DeepCopy() modifiedRelease.Name = "non-existing-release" - returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, modifiedRelease) + returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, modifiedRelease, metadata.ManagedPipelineType) Expect(err).NotTo(HaveOccurred()) Expect(returnedObject).To(BeNil()) }) @@ -551,24 +569,39 @@ var _ = Describe("Release Adapter", Ordered, func() { } Expect(k8sClient.Create(ctx, release)).To(Succeed()) - pipelineRun = &tektonv1.PipelineRun{ + managedPipelineRun = &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ metadata.ReleaseNameLabel: release.Name, metadata.ReleaseNamespaceLabel: release.Namespace, + metadata.PipelinesTypeLabel: metadata.ManagedPipelineType, }, Name: "pipeline-run", Namespace: "default", }, } - Expect(k8sClient.Create(ctx, pipelineRun)).To(Succeed()) + Expect(k8sClient.Create(ctx, managedPipelineRun)).To(Succeed()) + + tenantPipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + metadata.ReleaseNameLabel: release.Name, + metadata.ReleaseNamespaceLabel: release.Namespace, + metadata.PipelinesTypeLabel: metadata.TenantPipelineType, + }, + Name: "tenant-pipeline-run", + Namespace: "default", + }, + } + Expect(k8sClient.Create(ctx, tenantPipelineRun)).To(Succeed()) } deleteResources = func() { Expect(k8sClient.Delete(ctx, application)).To(Succeed()) Expect(k8sClient.Delete(ctx, component)).To(Succeed()) Expect(k8sClient.Delete(ctx, enterpriseContractPolicy)).To(Succeed()) - Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) + Expect(k8sClient.Delete(ctx, managedPipelineRun)).To(Succeed()) + Expect(k8sClient.Delete(ctx, tenantPipelineRun)).To(Succeed()) Expect(k8sClient.Delete(ctx, release)).To(Succeed()) Expect(k8sClient.Delete(ctx, releasePlan)).To(Succeed()) Expect(k8sClient.Delete(ctx, releasePlanAdmission)).To(Succeed()) diff --git a/metrics/release.go b/metrics/release.go index 2ed7db6a..9ebc4caa 100644 --- a/metrics/release.go +++ b/metrics/release.go @@ -39,10 +39,18 @@ var ( []string{}, ) - ReleaseConcurrentProcessingsTotal = prometheus.NewGaugeVec( + ReleaseConcurrentManagedPipelineProcessingsTotal = prometheus.NewGaugeVec( prometheus.GaugeOpts{ - Name: "release_concurrent_processings_total", - Help: "Total number of concurrent release processing attempts", + Name: "release_concurrent_managed_pipeline_processings_total", + Help: "Total number of concurrent release managed pipeline processing attempts", + }, + []string{}, + ) + + ReleaseConcurrentTenantPipelineProcessingsTotal = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "release_concurrent_tenant_pipeline_processings_total", + Help: "Total number of concurrent release tenant pipeline processing attempts", }, []string{}, ) @@ -79,11 +87,13 @@ var ( releaseDurationSecondsOpts, releaseDurationSecondsLabels, ) + // Prometheus fails if these are not in alphabetical order releaseDurationSecondsLabels = []string{ + "managed_pipeline_processing_reason", "post_actions_reason", - "processing_reason", "release_reason", "target", + "tenant_pipeline_processing_reason", "validation_reason", } releaseDurationSecondsOpts = prometheus.HistogramOpts{ @@ -105,17 +115,27 @@ var ( Buckets: []float64{60, 150, 300, 450, 600, 750, 900, 1050, 1200, 1800, 3600}, } - ReleaseProcessingDurationSeconds = prometheus.NewHistogramVec( - releaseProcessingDurationSecondsOpts, + ReleaseManagedPipelineProcessingDurationSeconds = prometheus.NewHistogramVec( + releaseManagedPipelineProcessingDurationSecondsOpts, releaseProcessingDurationSecondsLabels, ) releaseProcessingDurationSecondsLabels = []string{ "reason", "target", } - releaseProcessingDurationSecondsOpts = prometheus.HistogramOpts{ - Name: "release_processing_duration_seconds", - Help: "How long in seconds a Release processing takes to complete", + releaseManagedPipelineProcessingDurationSecondsOpts = prometheus.HistogramOpts{ + Name: "release_managed_pipeline_processing_duration_seconds", + Help: "How long in seconds a Release Managed Pipeline processing takes to complete", + Buckets: []float64{60, 150, 300, 450, 600, 750, 900, 1050, 1200, 1800, 3600}, + } + + ReleaseTenantPipelineProcessingDurationSeconds = prometheus.NewHistogramVec( + releaseTenantPipelineProcessingDurationSecondsOpts, + releaseProcessingDurationSecondsLabels, + ) + releaseTenantPipelineProcessingDurationSecondsOpts = prometheus.HistogramOpts{ + Name: "release_tenant_pipeline_processing_duration_seconds", + Help: "How long in seconds a Release Tenant Pipeline processing takes to complete", Buckets: []float64{60, 150, 300, 450, 600, 750, 900, 1050, 1200, 1800, 3600}, } @@ -123,11 +143,13 @@ var ( releaseTotalOpts, releaseTotalLabels, ) + // Prometheus fails if these are not in alphabetical order releaseTotalLabels = []string{ + "managed_pipeline_processing_reason", "post_actions_reason", - "processing_reason", "release_reason", "target", + "tenant_pipeline_processing_reason", "validation_reason", } releaseTotalOpts = prometheus.CounterOpts{ @@ -140,17 +162,19 @@ var ( // observation for the Release duration and increasing the total number of releases. If either the startTime or the // completionTime parameters are nil, no action will be taken. func RegisterCompletedRelease(startTime, completionTime *metav1.Time, - postActionsReason, processingReason, releaseReason, target, validationReason string) { + managedProcessingReason, postActionsReason, releaseReason, target, tenantProcessingReason, validationReason string) { if startTime == nil || completionTime == nil { return } + // Prometheus fails if these are not in alphabetical order labels := prometheus.Labels{ - "post_actions_reason": postActionsReason, - "processing_reason": processingReason, - "release_reason": releaseReason, - "target": target, - "validation_reason": validationReason, + "managed_pipeline_processing_reason": managedProcessingReason, + "post_actions_reason": postActionsReason, + "release_reason": releaseReason, + "target": target, + "tenant_pipeline_processing_reason": tenantProcessingReason, + "validation_reason": validationReason, } ReleaseConcurrentTotal.WithLabelValues().Dec() ReleaseDurationSeconds. @@ -175,21 +199,40 @@ func RegisterCompletedReleasePostActionsExecuted(startTime, completionTime *meta ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues().Dec() } -// RegisterCompletedReleaseProcessing registers a Release processing as complete, adding a new observation for the -// Release processing duration and decreasing the number of concurrent processings. If either the startTime or the -// completionTime parameters are nil, no action will be taken. -func RegisterCompletedReleaseProcessing(startTime, completionTime *metav1.Time, reason, target string) { +// RegisterCompletedReleaseManagedPipelineProcessing registers a Release processing as complete, adding a +// new observation for the Release Managed Pipeline processing duration and decreasing the number of +// concurrent processings. If either the startTime or the completionTime parameters are nil, no action +// will be taken. +func RegisterCompletedReleaseManagedPipelineProcessing(startTime, completionTime *metav1.Time, reason, target string) { if startTime == nil || completionTime == nil { return } - ReleaseProcessingDurationSeconds. + ReleaseManagedPipelineProcessingDurationSeconds. With(prometheus.Labels{ "reason": reason, "target": target, }). Observe(completionTime.Sub(startTime.Time).Seconds()) - ReleaseConcurrentProcessingsTotal.WithLabelValues().Dec() + ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues().Dec() +} + +// RegisterCompletedReleaseTenantPipelineProcessing registers a Release processing as complete, adding a +// new observation for the Release Tenant Pipeline processing duration and decreasing the number of +// concurrent processings. If either the startTime or the completionTime parameters are nil, no action +// will be taken. +func RegisterCompletedReleaseTenantPipelineProcessing(startTime, completionTime *metav1.Time, reason, target string) { + if startTime == nil || completionTime == nil { + return + } + + ReleaseTenantPipelineProcessingDurationSeconds. + With(prometheus.Labels{ + "reason": reason, + "target": target, + }). + Observe(completionTime.Sub(startTime.Time).Seconds()) + ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues().Dec() } // RegisterValidatedRelease registers a Release as validated, adding a new observation for the @@ -213,10 +256,30 @@ func RegisterNewRelease() { ReleaseConcurrentTotal.WithLabelValues().Inc() } -// RegisterNewReleaseProcessing registers a new Release processing, adding a new observation for the -// Release start processing duration and increasing the number of concurrent processings. If either the -// startTime or the processingStartTime are nil, no action will be taken. -func RegisterNewReleaseProcessing(startTime, processingStartTime *metav1.Time, reason, target string) { +// RegisterNewReleaseManagedPipelineProcessing registers a new Release Managed Pipeline processing, +// adding a new observation for the Release start managed pipeline processing duration and increasing +// the number of concurrent processings. If either the startTime or the processingStartTime are nil, no +// action will be taken. +func RegisterNewReleaseManagedPipelineProcessing(startTime, processingStartTime *metav1.Time, reason, target string) { + if startTime == nil || processingStartTime == nil { + return + } + + ReleasePreProcessingDurationSeconds. + With(prometheus.Labels{ + "reason": reason, + "target": target, + }). + Observe(processingStartTime.Sub(startTime.Time).Seconds()) + + ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues().Inc() +} + +// RegisterNewReleaseTenantPipelineProcessing registers a new Release Tenant Pipeline processing, +// adding a new observation for the Release start tenant pipeline processing duration and increasing +// the number of concurrent processings. If either the startTime or the processingStartTime are nil, no +// action will be taken. +func RegisterNewReleaseTenantPipelineProcessing(startTime, processingStartTime *metav1.Time, reason, target string) { if startTime == nil || processingStartTime == nil { return } @@ -228,7 +291,7 @@ func RegisterNewReleaseProcessing(startTime, processingStartTime *metav1.Time, r }). Observe(processingStartTime.Sub(startTime.Time).Seconds()) - ReleaseConcurrentProcessingsTotal.WithLabelValues().Inc() + ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues().Inc() } // RegisterNewReleasePostActionsExecution register a new Release post-actions execution, increasing the number of @@ -240,13 +303,15 @@ func RegisterNewReleasePostActionsExecution() { func init() { metrics.Registry.MustRegister( ReleaseConcurrentTotal, - ReleaseConcurrentProcessingsTotal, + ReleaseConcurrentManagedPipelineProcessingsTotal, + ReleaseConcurrentTenantPipelineProcessingsTotal, ReleaseConcurrentPostActionsExecutionsTotal, ReleasePreProcessingDurationSeconds, ReleaseValidationDurationSeconds, ReleaseDurationSeconds, ReleasePostActionsExecutionDurationSeconds, - ReleaseProcessingDurationSeconds, + ReleaseManagedPipelineProcessingDurationSeconds, + ReleaseTenantPipelineProcessingDurationSeconds, ReleaseTotal, ) } diff --git a/metrics/release_test.go b/metrics/release_test.go index c446b9ee..1d31e175 100644 --- a/metrics/release_test.go +++ b/metrics/release_test.go @@ -43,19 +43,19 @@ var _ = Describe("Release metrics", Ordered, func() { It("does nothing if the start time is nil", func() { Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedRelease(nil, completionTime, "", "", "", "", "") + RegisterCompletedRelease(nil, completionTime, "", "", "", "", "", "") Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(0))) }) It("does nothing if the completion time is nil", func() { Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedRelease(startTime, nil, "", "", "", "", "") + RegisterCompletedRelease(startTime, nil, "", "", "", "", "", "") Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(0))) }) It("decrements ReleaseConcurrentTotal", func() { Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedRelease(startTime, completionTime, "", "", "", "", "") + RegisterCompletedRelease(startTime, completionTime, "", "", "", "", "", "") Expect(testutil.ToFloat64(ReleaseConcurrentTotal.WithLabelValues())).To(Equal(float64(-1))) }) @@ -66,6 +66,7 @@ var _ = Describe("Release metrics", Ordered, func() { releaseDurationSecondsLabels[2], releaseDurationSecondsLabels[3], releaseDurationSecondsLabels[4], + releaseDurationSecondsLabels[5], ) Expect(testutil.CollectAndCompare(ReleaseDurationSeconds, test.NewHistogramReader( @@ -82,6 +83,7 @@ var _ = Describe("Release metrics", Ordered, func() { releaseTotalLabels[2], releaseTotalLabels[3], releaseTotalLabels[4], + releaseTotalLabels[5], ) Expect(testutil.CollectAndCompare(ReleaseTotal, test.NewCounterReader( @@ -132,7 +134,7 @@ var _ = Describe("Release metrics", Ordered, func() { }) }) - When("RegisterCompletedReleaseProcessing is called", func() { + When("RegisterCompletedReleaseManagedPipelineProcessing is called", func() { var completionTime, startTime *metav1.Time BeforeEach(func() { @@ -143,31 +145,73 @@ var _ = Describe("Release metrics", Ordered, func() { }) It("does nothing if the start time is nil", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleaseProcessing(nil, completionTime, "", "") - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseManagedPipelineProcessing(nil, completionTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) }) It("does nothing if the completion time is nil", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleaseProcessing(startTime, nil, "", "") - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseManagedPipelineProcessing(startTime, nil, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) }) - It("decrements ReleaseConcurrentProcessingsTotal", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleaseProcessing(startTime, completionTime, "", "") - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(-1))) + It("decrements ReleaseConcurrentManagedPipelineProcessingsTotal", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseManagedPipelineProcessing(startTime, completionTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(-1))) }) - It("adds an observation to ReleaseProcessingDurationSeconds", func() { - RegisterCompletedReleaseProcessing(startTime, completionTime, + It("adds an observation to ReleaseManagedPipelineProcessingDurationSeconds", func() { + RegisterCompletedReleaseManagedPipelineProcessing(startTime, completionTime, releaseProcessingDurationSecondsLabels[0], releaseProcessingDurationSecondsLabels[1], ) - Expect(testutil.CollectAndCompare(ReleaseProcessingDurationSeconds, + Expect(testutil.CollectAndCompare(ReleaseManagedPipelineProcessingDurationSeconds, test.NewHistogramReader( - releaseProcessingDurationSecondsOpts, + releaseManagedPipelineProcessingDurationSecondsOpts, + releaseProcessingDurationSecondsLabels, + startTime, completionTime, + ))).To(Succeed()) + }) + }) + + When("RegisterCompletedReleaseTenantPipelineProcessing is called", func() { + var completionTime, startTime *metav1.Time + + BeforeEach(func() { + initializeMetrics() + + completionTime = &metav1.Time{} + startTime = &metav1.Time{Time: completionTime.Add(-60 * time.Second)} + }) + + It("does nothing if the start time is nil", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseTenantPipelineProcessing(nil, completionTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + }) + + It("does nothing if the completion time is nil", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseTenantPipelineProcessing(startTime, nil, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + }) + + It("decrements ReleaseConcurrentTenantPipelineProcessingsTotal", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterCompletedReleaseTenantPipelineProcessing(startTime, completionTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(-1))) + }) + + It("adds an observation to ReleaseTenantPipelineProcessingDurationSeconds", func() { + RegisterCompletedReleaseTenantPipelineProcessing(startTime, completionTime, + releaseProcessingDurationSecondsLabels[0], + releaseProcessingDurationSecondsLabels[1], + ) + Expect(testutil.CollectAndCompare(ReleaseTenantPipelineProcessingDurationSeconds, + test.NewHistogramReader( + releaseTenantPipelineProcessingDurationSecondsOpts, releaseProcessingDurationSecondsLabels, startTime, completionTime, ))).To(Succeed()) @@ -218,7 +262,52 @@ var _ = Describe("Release metrics", Ordered, func() { }) }) - When("RegisterNewReleaseProcessing is called", func() { + When("RegisterNewReleaseManagedPipelineProcessing is called", func() { + var processingStartTime, startTime *metav1.Time + + BeforeEach(func() { + initializeMetrics() + + processingStartTime = &metav1.Time{} + startTime = &metav1.Time{Time: processingStartTime.Add(-60 * time.Second)} + }) + + It("does nothing if the processing start time is nil", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseManagedPipelineProcessing(nil, processingStartTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + }) + + It("does nothing if the start time is nil", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseManagedPipelineProcessing(startTime, nil, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + }) + + It("adds an observation to ReleasePreProcessingDurationSeconds", func() { + RegisterNewReleaseManagedPipelineProcessing(startTime, processingStartTime, + releasePreProcessingDurationSecondsLabels[0], + releasePreProcessingDurationSecondsLabels[1], + ) + Expect(testutil.CollectAndCompare(ReleasePreProcessingDurationSeconds, + test.NewHistogramReader( + releasePreProcessingDurationSecondsOpts, + releasePreProcessingDurationSecondsLabels, + startTime, processingStartTime, + ))).To(Succeed()) + }) + + It("increments ReleaseConcurrentManagedPipelineProcessingsTotal", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseManagedPipelineProcessing(startTime, processingStartTime, + releasePreProcessingDurationSecondsLabels[0], + releasePreProcessingDurationSecondsLabels[1], + ) + Expect(testutil.ToFloat64(ReleaseConcurrentManagedPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(1))) + }) + }) + + When("RegisterNewReleaseTenantPipelineProcessing is called", func() { var processingStartTime, startTime *metav1.Time BeforeEach(func() { @@ -229,19 +318,19 @@ var _ = Describe("Release metrics", Ordered, func() { }) It("does nothing if the processing start time is nil", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterNewReleaseProcessing(nil, processingStartTime, "", "") - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseTenantPipelineProcessing(nil, processingStartTime, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) }) It("does nothing if the start time is nil", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterNewReleaseProcessing(startTime, nil, "", "") - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseTenantPipelineProcessing(startTime, nil, "", "") + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) }) It("adds an observation to ReleasePreProcessingDurationSeconds", func() { - RegisterNewReleaseProcessing(startTime, processingStartTime, + RegisterNewReleaseTenantPipelineProcessing(startTime, processingStartTime, releasePreProcessingDurationSecondsLabels[0], releasePreProcessingDurationSecondsLabels[1], ) @@ -253,13 +342,13 @@ var _ = Describe("Release metrics", Ordered, func() { ))).To(Succeed()) }) - It("increments ReleaseConcurrentProcessingsTotal", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterNewReleaseProcessing(startTime, processingStartTime, + It("increments ReleaseConcurrentTenantPipelineProcessingsTotal", func() { + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(0))) + RegisterNewReleaseTenantPipelineProcessing(startTime, processingStartTime, releasePreProcessingDurationSecondsLabels[0], releasePreProcessingDurationSecondsLabels[1], ) - Expect(testutil.ToFloat64(ReleaseConcurrentProcessingsTotal.WithLabelValues())).To(Equal(float64(1))) + Expect(testutil.ToFloat64(ReleaseConcurrentTenantPipelineProcessingsTotal.WithLabelValues())).To(Equal(float64(1))) }) }) @@ -277,12 +366,14 @@ var _ = Describe("Release metrics", Ordered, func() { initializeMetrics = func() { ReleaseConcurrentTotal.Reset() - ReleaseConcurrentProcessingsTotal.Reset() + ReleaseConcurrentManagedPipelineProcessingsTotal.Reset() + ReleaseConcurrentTenantPipelineProcessingsTotal.Reset() ReleaseConcurrentPostActionsExecutionsTotal.Reset() ReleaseValidationDurationSeconds.Reset() ReleasePreProcessingDurationSeconds.Reset() ReleaseDurationSeconds.Reset() - ReleaseProcessingDurationSeconds.Reset() + ReleaseManagedPipelineProcessingDurationSeconds.Reset() + ReleaseTenantPipelineProcessingDurationSeconds.Reset() ReleasePostActionsExecutionDurationSeconds.Reset() ReleaseTotal.Reset() } diff --git a/tekton/utils/zz_generated.deepcopy.go b/tekton/utils/zz_generated.deepcopy.go index 063d5824..aa8d4482 100644 --- a/tekton/utils/zz_generated.deepcopy.go +++ b/tekton/utils/zz_generated.deepcopy.go @@ -1,4 +1,5 @@ //go:build !ignore_autogenerated +// +build !ignore_autogenerated /* Copyright 2022.