diff --git a/api/v1alpha1/release_conditions.go b/api/v1alpha1/release_conditions.go index 2de7f0ea..b678a112 100644 --- a/api/v1alpha1/release_conditions.go +++ b/api/v1alpha1/release_conditions.go @@ -3,12 +3,12 @@ package v1alpha1 import "github.com/konflux-ci/operator-toolkit/conditions" const ( + // finalProcessedConditionType is the type used to track the status of a Release Final Pipeline processing + finalProcessedConditionType conditions.ConditionType = "FinalPipelineProcessed" + // managedProcessedConditionType is the type used to track the status of a Release Managed Pipeline processing managedProcessedConditionType conditions.ConditionType = "ManagedPipelineProcessed" - // postActionsExecutedConditionType is the type used to track the status of Release post-actions - postActionsExecutedConditionType conditions.ConditionType = "PostActionsExecuted" - // tenantProcessedConditionType is the type used to track the status of a Release Tenant Pipeline processing tenantProcessedConditionType conditions.ConditionType = "TenantPipelineProcessed" diff --git a/api/v1alpha1/release_types.go b/api/v1alpha1/release_types.go index 60f83c71..d98e32d8 100644 --- a/api/v1alpha1/release_types.go +++ b/api/v1alpha1/release_types.go @@ -67,13 +67,13 @@ type ReleaseStatus struct { // +optional Conditions []metav1.Condition `json:"conditions"` - // ManagedProcessing contains information about the release managed processing + // FinalProcessing contains information about the release final processing // +optional - ManagedProcessing PipelineInfo `json:"managedProcessing,omitempty"` + FinalProcessing PipelineInfo `json:"finalProcessing,omitempty"` - // PostActionsExecution contains information about the post-actions execution + // ManagedProcessing contains information about the release managed processing // +optional - PostActionsExecution PipelineInfo `json:"postActionsExecution,omitempty"` + ManagedProcessing PipelineInfo `json:"managedProcessing,omitempty"` // TenantProcessing contains information about the release tenant processing // +optional @@ -165,10 +165,9 @@ type Release struct { Status ReleaseStatus `json:"status,omitempty"` } -// HasEveryPostActionExecutionFinished checks whether the Release post-actions execution has finished, -// regardless of the result. -func (r *Release) HasEveryPostActionExecutionFinished() bool { - return r.hasPhaseFinished(postActionsExecutedConditionType) +// Has FinalPipelineProcessingFinished checks whether the Release Final Pipeline processing has finished, regardless of the result. +func (r *Release) HasFinalPipelineProcessingFinished() bool { + return r.hasPhaseFinished(finalProcessedConditionType) } // HasManagedPipelineProcessingFinished checks whether the Release Managed Pipeline processing has finished, regardless of the result. @@ -196,14 +195,9 @@ func (r *Release) IsAutomated() bool { return r.Status.Automated } -// IsEveryPostActionExecuted checks whether the Release post-actions were successfully executed. -func (r *Release) IsEveryPostActionExecuted() bool { - return meta.IsStatusConditionTrue(r.Status.Conditions, postActionsExecutedConditionType.String()) -} - -// IsEachPostActionExecuting checks whether the Release post-actions are in progress. -func (r *Release) IsEachPostActionExecuting() bool { - return r.isPhaseProgressing(postActionsExecutedConditionType) +// IsFinalPipelineProcessed checks whether the Release Final Pipeline was successfully processed. +func (r *Release) IsFinalPipelineProcessed() bool { + return meta.IsStatusConditionTrue(r.Status.Conditions, finalProcessedConditionType.String()) } // IsManagedPipelineProcessed checks whether the Release Managed Pipeline was successfully processed. @@ -216,6 +210,11 @@ func (r *Release) IsTenantPipelineProcessed() bool { return meta.IsStatusConditionTrue(r.Status.Conditions, tenantProcessedConditionType.String()) } +// IsFinalPipelineProcessing checks whether the Release Final Pipeline processing is in progress. +func (r *Release) IsFinalPipelineProcessing() bool { + return r.isPhaseProgressing(finalProcessedConditionType) +} + // IsManagedPipelineProcessing checks whether the Release Managed Pipeline processing is in progress. func (r *Release) IsManagedPipelineProcessing() bool { return r.isPhaseProgressing(managedProcessedConditionType) @@ -241,6 +240,24 @@ func (r *Release) IsValid() bool { return meta.IsStatusConditionTrue(r.Status.Conditions, validatedConditionType.String()) } +// MarkFinalPipelineProcessed marks the Release Final Pipeline as processed. +func (r *Release) MarkFinalPipelineProcessed() { + if !r.IsFinalPipelineProcessing() || r.HasFinalPipelineProcessingFinished() { + return + } + + r.Status.FinalProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetCondition(&r.Status.Conditions, finalProcessedConditionType, metav1.ConditionTrue, SucceededReason) + + go metrics.RegisterCompletedReleasePipelineProcessing( + r.Status.FinalProcessing.StartTime, + r.Status.FinalProcessing.CompletionTime, + SucceededReason.String(), + r.Status.Target, + metadata.FinalPipelineType, + ) +} + // MarkManagedPipelineProcessed marks the Release Managed Pipeline as processed. func (r *Release) MarkManagedPipelineProcessed() { if !r.IsManagedPipelineProcessing() || r.HasManagedPipelineProcessingFinished() { @@ -277,6 +294,27 @@ func (r *Release) MarkTenantPipelineProcessed() { ) } +// MarkFinalPipelineProcessing marks the Release Final Pipeline as processing. +func (r *Release) MarkFinalPipelineProcessing() { + if r.HasFinalPipelineProcessingFinished() { + return + } + + if !r.IsFinalPipelineProcessing() { + r.Status.FinalProcessing.StartTime = &metav1.Time{Time: time.Now()} + } + + conditions.SetCondition(&r.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + + go metrics.RegisterNewReleasePipelineProcessing( + r.Status.FinalProcessing.StartTime, + r.Status.StartTime, + ProgressingReason.String(), + r.Status.Target, + metadata.FinalPipelineType, + ) +} + // MarkManagedPipelineProcessing marks the Release Managed Pipeline as processing. func (r *Release) MarkManagedPipelineProcessing() { if r.HasManagedPipelineProcessingFinished() { @@ -319,6 +357,24 @@ func (r *Release) MarkTenantPipelineProcessing() { ) } +// MarkFinalPipelineProcessingFailed marks the Release Final Pipeline processing as failed. +func (r *Release) MarkFinalPipelineProcessingFailed(message string) { + if !r.IsFinalPipelineProcessing() || r.HasFinalPipelineProcessingFinished() { + return + } + + r.Status.FinalProcessing.CompletionTime = &metav1.Time{Time: time.Now()} + conditions.SetConditionWithMessage(&r.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, FailedReason, message) + + go metrics.RegisterCompletedReleasePipelineProcessing( + r.Status.FinalProcessing.StartTime, + r.Status.FinalProcessing.CompletionTime, + FailedReason.String(), + r.Status.Target, + metadata.FinalPipelineType, + ) +} + // MarkManagedPipelineProcessingFailed marks the Release Managed Pipeline processing as failed. func (r *Release) MarkManagedPipelineProcessingFailed(message string) { if !r.IsManagedPipelineProcessing() || r.HasManagedPipelineProcessingFinished() { @@ -355,6 +411,15 @@ func (r *Release) MarkTenantPipelineProcessingFailed(message string) { ) } +// MarkFinalPipelineProcessingSkipped marks the Release Final Pipeline processing as skipped. +func (r *Release) MarkFinalPipelineProcessingSkipped() { + if r.HasFinalPipelineProcessingFinished() { + return + } + + conditions.SetCondition(&r.Status.Conditions, finalProcessedConditionType, metav1.ConditionTrue, SkippedReason) +} + // MarkManagedPipelineProcessingSkipped marks the Release Managed Pipeline processing as skipped. func (r *Release) MarkManagedPipelineProcessingSkipped() { if r.HasManagedPipelineProcessingFinished() { @@ -373,53 +438,6 @@ func (r *Release) MarkTenantPipelineProcessingSkipped() { 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() { - return - } - - r.Status.PostActionsExecution.CompletionTime = &metav1.Time{Time: time.Now()} - conditions.SetCondition(&r.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionTrue, SucceededReason) - - go metrics.RegisterCompletedReleasePostActionsExecuted( - r.Status.PostActionsExecution.StartTime, - r.Status.PostActionsExecution.CompletionTime, - SucceededReason.String(), - ) -} - -// MarkPostActionsExecuting marks the Release post-actions as executing. -func (r *Release) MarkPostActionsExecuting(message string) { - if r.HasEveryPostActionExecutionFinished() { - return - } - - if !r.IsEachPostActionExecuting() { - r.Status.PostActionsExecution.StartTime = &metav1.Time{Time: time.Now()} - } - - conditions.SetConditionWithMessage(&r.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, ProgressingReason, message) - - go metrics.RegisterNewReleasePostActionsExecution() -} - -// MarkPostActionsExecutionFailed marks the Release post-actions execution as failed. -func (r *Release) MarkPostActionsExecutionFailed(message string) { - if !r.IsEachPostActionExecuting() || r.HasEveryPostActionExecutionFinished() { - return - } - - r.Status.PostActionsExecution.CompletionTime = &metav1.Time{Time: time.Now()} - conditions.SetConditionWithMessage(&r.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, FailedReason, message) - - go metrics.RegisterCompletedReleasePostActionsExecuted( - r.Status.PostActionsExecution.StartTime, - r.Status.PostActionsExecution.CompletionTime, - FailedReason.String(), - ) -} - // MarkReleased marks the Release as released. func (r *Release) MarkReleased() { if !r.IsReleasing() || r.HasReleaseFinished() { @@ -433,10 +451,10 @@ func (r *Release) MarkReleased() { r.Status.StartTime, r.Status.CompletionTime, r.getPhaseReason(managedProcessedConditionType), - r.getPhaseReason(postActionsExecutedConditionType), SucceededReason.String(), r.Status.Target, r.getPhaseReason(tenantProcessedConditionType), + r.getPhaseReason(finalProcessedConditionType), r.getPhaseReason(validatedConditionType), ) } @@ -468,9 +486,9 @@ func (r *Release) MarkReleaseFailed(message string) { go metrics.RegisterCompletedRelease( r.Status.StartTime, r.Status.CompletionTime, - r.getPhaseReason(postActionsExecutedConditionType), r.getPhaseReason(tenantProcessedConditionType), r.getPhaseReason(managedProcessedConditionType), + r.getPhaseReason(finalProcessedConditionType), 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 378d9f54..7ced1e91 100644 --- a/api/v1alpha1/release_types_test.go +++ b/api/v1alpha1/release_types_test.go @@ -30,35 +30,31 @@ import ( var _ = Describe("Release type", func() { - When("HasEveryPostActionExecutionFinished method is called", func() { + When("HasFinalPipelineProcessingFinshed method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should return false when the post-actions executed condition is missing", func() { - Expect(release.HasEveryPostActionExecutionFinished()).To(BeFalse()) + It("should return true when the final pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.HasFinalPipelineProcessingFinished()).To(BeTrue()) }) - It("should return true when the post-actions executed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.HasEveryPostActionExecutionFinished()).To(BeTrue()) + It("should return false when the final pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.HasFinalPipelineProcessingFinished()).To(BeFalse()) }) - It("should return false when the post-actions executed condition status is False and the reason is Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, ProgressingReason) - Expect(release.HasEveryPostActionExecutionFinished()).To(BeFalse()) + It("Should return true when the final pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.HasFinalPipelineProcessingFinished()).To(BeTrue()) }) - It("should return true when the post-actions executed condition status is False and the reason is not Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, FailedReason) - Expect(release.HasEveryPostActionExecutionFinished()).To(BeTrue()) - }) - - It("should return false when the post-actions executed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionUnknown, ProgressingReason) - Expect(release.HasEveryPostActionExecutionFinished()).To(BeFalse()) + It("should return false when the final pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionUnknown, ProgressingReason) + Expect(release.HasFinalPipelineProcessingFinished()).To(BeFalse()) }) }) @@ -189,63 +185,32 @@ var _ = Describe("Release type", func() { }) }) - When("IsEveryPostActionExecuted method is called", func() { + When("IsFinalPipelineProcessed method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should return true when the post-actions executed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.IsEveryPostActionExecuted()).To(BeTrue()) - }) - - It("should return false when the post-actions executed condition status is False", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, SucceededReason) - Expect(release.IsEveryPostActionExecuted()).To(BeFalse()) + It("should return true when the final pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsFinalPipelineProcessed()).To(BeTrue()) }) - It("should return false when the post-actions executed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionUnknown, SucceededReason) - Expect(release.IsEveryPostActionExecuted()).To(BeFalse()) - }) - - It("should return false when the post-actions executed condition is missing", func() { - Expect(release.IsEveryPostActionExecuted()).To(BeFalse()) - }) - }) - - When("IsEachPostActionExecuting method is called", func() { - var release *Release - - BeforeEach(func() { - release = &Release{} + It("should return false when the final pipeline processed condition status is False", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, SucceededReason) + Expect(release.IsFinalPipelineProcessed()).To(BeFalse()) }) - It("should return false when the post-actions executed condition is missing", func() { - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) + It("should return false when the final pipeline processed condition status is Unknown", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionUnknown, SucceededReason) + Expect(release.IsFinalPipelineProcessed()).To(BeFalse()) }) - It("should return false when the post-actions executed condition status is True", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionTrue, SucceededReason) - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) + It("should return false when the final pipeline processed condition is missing", func() { + Expect(release.IsFinalPipelineProcessed()).To(BeFalse()) }) - It("should return true when the post-actions executed condition status is False and the reason is Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, ProgressingReason) - Expect(release.IsEachPostActionExecuting()).To(BeTrue()) - }) - - It("should return false when the post-actions executed condition status is False and the reason is not Progressing", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionFalse, FailedReason) - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) - }) - - It("should return false when the post-actions executed condition status is Unknown", func() { - conditions.SetCondition(&release.Status.Conditions, postActionsExecutedConditionType, metav1.ConditionUnknown, ProgressingReason) - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) - }) }) When("IsManagedPipelineProcessed method is called", func() { @@ -302,6 +267,34 @@ var _ = Describe("Release type", func() { }) }) + When("IsFinalPipelineProcessing method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should return false when the final pipeline processed condition is missing", func() { + Expect(release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should return false when the final pipeline processed condition status is True", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionTrue, SucceededReason) + Expect(release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should return true when the final pipeline processed condition status is False and the reason is Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, ProgressingReason) + Expect(release.IsFinalPipelineProcessing()).To(BeTrue()) + }) + + It("should return false when the final pipeline processed condition status is False and the reason is not Progressing", func() { + conditions.SetCondition(&release.Status.Conditions, finalProcessedConditionType, metav1.ConditionFalse, FailedReason) + Expect(release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + }) + When("IsManagedPipelineProcessing method is called", func() { var release *Release @@ -452,6 +445,48 @@ var _ = Describe("Release type", func() { }) }) + When("MarkFinalPipelineProcessed method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release final pipeline processing has not started", func() { + release.MarkFinalPipelineProcessed() + Expect(release.Status.FinalProcessing.CompletionTime).To(BeNil()) + }) + + It("should do nothing if the Release final pipeline processing finished", func() { + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessed() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.FinalProcessing.CompletionTime = &metav1.Time{} + release.MarkFinalPipelineProcessed() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeTrue()) + }) + + It("should register the completion time", func() { + release.MarkFinalPipelineProcessing() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkFinalPipelineProcessed() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeFalse()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessed() + + condition := meta.FindStatusCondition(release.Status.Conditions, finalProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(SucceededReason.String()), + "Status": Equal(metav1.ConditionTrue), + })) + }) + }) + When("MarkManagedPipelineProcessed method is called", func() { var release *Release @@ -536,6 +571,50 @@ var _ = Describe("Release type", func() { }) }) + When("MarkFinalPipelineProcessing method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release final pipeline processing finished", func() { + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessed() + Expect(release.IsFinalPipelineProcessing()).To(BeFalse()) + release.MarkFinalPipelineProcessing() + Expect(release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should register the start time if the final pipeline is not processing", func() { + Expect(release.Status.FinalProcessing.StartTime).To(BeNil()) + release.MarkFinalPipelineProcessing() + Expect(release.Status.FinalProcessing.StartTime).NotTo(BeNil()) + }) + + It("should not register the start time if the final pipeline is processing already", func() { + Expect(release.Status.FinalProcessing.StartTime).To(BeNil()) + release.MarkFinalPipelineProcessing() + release.Status.FinalProcessing.StartTime = &metav1.Time{} + Expect(release.Status.FinalProcessing.StartTime.IsZero()).To(BeTrue()) + release.MarkFinalPipelineProcessing() + Expect(release.Status.FinalProcessing.StartTime.IsZero()).To(BeTrue()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkFinalPipelineProcessing() + + condition := meta.FindStatusCondition(release.Status.Conditions, finalProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Reason": Equal(ProgressingReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + + }) + When("MarkManagedPipelineProcessing method is called", func() { var release *Release @@ -622,6 +701,50 @@ var _ = Describe("Release type", func() { }) }) + When("MarkFinalPipelineProcessingFailed method is called", func() { + var release *Release + + BeforeEach(func() { + release = &Release{} + }) + + It("should do nothing if the Release final pipeline processing has not started", func() { + release.MarkFinalPipelineProcessingFailed("") + Expect(release.Status.FinalProcessing.CompletionTime).To(BeNil()) + }) + + It("should do nothing if the Release final pipeline processing finished", func() { + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessed() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeFalse()) + release.Status.FinalProcessing.CompletionTime = &metav1.Time{} + release.MarkFinalPipelineProcessingFailed("") + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeTrue()) + }) + + It("should register the completion time", func() { + release.MarkFinalPipelineProcessing() + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeTrue()) + release.MarkFinalPipelineProcessingFailed("") + Expect(release.Status.FinalProcessing.CompletionTime.IsZero()).To(BeFalse()) + }) + + It("should register the condition", func() { + Expect(release.Status.Conditions).To(HaveLen(0)) + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessingFailed("foo") + + condition := meta.FindStatusCondition(release.Status.Conditions, finalProcessedConditionType.String()) + Expect(condition).NotTo(BeNil()) + Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ + "Message": Equal("foo"), + "Reason": Equal(FailedReason.String()), + "Status": Equal(metav1.ConditionFalse), + })) + }) + + }) + When("MarkManagedPipelineProcessingFailed method is called", func() { var release *Release @@ -708,19 +831,19 @@ var _ = Describe("Release type", func() { }) }) - When("MarkManagedPipelineProcessingSkipped method is called", func() { + When("MarkFinalPipelineProcessingSkipped 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() + It("should do nothing if the Release final pipeline processing finished already", func() { + release.MarkFinalPipelineProcessing() + release.MarkFinalPipelineProcessingFailed("error") + release.MarkFinalPipelineProcessingSkipped() - condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, finalProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Message": Equal("error"), @@ -731,9 +854,9 @@ var _ = Describe("Release type", func() { It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkManagedPipelineProcessingSkipped() + release.MarkFinalPipelineProcessingSkipped() - condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, finalProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Reason": Equal(SkippedReason.String()), @@ -742,19 +865,19 @@ var _ = Describe("Release type", func() { }) }) - When("MarkTenantPipelineProcessingSkipped method is called", func() { + When("MarkManagedPipelineProcessingSkipped 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() + 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, tenantProcessedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Message": Equal("error"), @@ -765,9 +888,9 @@ var _ = Describe("Release type", func() { It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkTenantPipelineProcessingSkipped() + release.MarkManagedPipelineProcessingSkipped() - condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, managedProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ "Reason": Equal(SkippedReason.String()), @@ -776,131 +899,36 @@ var _ = Describe("Release type", func() { }) }) - When("MarkPostActionsExecuted method is called", func() { - var release *Release - - BeforeEach(func() { - release = &Release{} - }) - - It("should do nothing if the Release post-actions execution has not started", func() { - release.MarkPostActionsExecuted() - Expect(release.Status.PostActionsExecution.CompletionTime).To(BeNil()) - }) - - It("should do nothing if the Release post-actions execution finished", func() { - release.MarkPostActionsExecuting("") - release.MarkPostActionsExecuted() - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeFalse()) - release.Status.PostActionsExecution.CompletionTime = &metav1.Time{} - release.MarkPostActionsExecuted() - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeTrue()) - }) - - It("should register the completion time", func() { - release.MarkPostActionsExecuting("") - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeTrue()) - release.MarkPostActionsExecuted() - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeFalse()) - }) - - It("should register the condition", func() { - Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkPostActionsExecuting("") - release.MarkPostActionsExecuted() - - condition := meta.FindStatusCondition(release.Status.Conditions, postActionsExecutedConditionType.String()) - Expect(condition).NotTo(BeNil()) - Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ - "Reason": Equal(SucceededReason.String()), - "Status": Equal(metav1.ConditionTrue), - })) - }) - }) - - When("MarkPostActionsExecuting method is called", func() { + When("MarkTenantPipelineProcessingSkipped method is called", func() { var release *Release BeforeEach(func() { release = &Release{} }) - It("should do nothing if the Release post-actions execution finished", func() { - release.MarkPostActionsExecuting("") - release.MarkPostActionsExecuted() - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) - release.MarkPostActionsExecuting("") - Expect(release.IsEachPostActionExecuting()).To(BeFalse()) - }) - - It("should register the start time if it's not executing post-actions", func() { - Expect(release.Status.PostActionsExecution.StartTime).To(BeNil()) - release.MarkPostActionsExecuting("") - Expect(release.Status.PostActionsExecution.StartTime).NotTo(BeNil()) - }) - - It("should not register the start time if it's executing post-actions already", func() { - Expect(release.Status.PostActionsExecution.StartTime).To(BeNil()) - release.MarkPostActionsExecuting("") - release.Status.PostActionsExecution.StartTime = &metav1.Time{} - Expect(release.Status.PostActionsExecution.StartTime.IsZero()).To(BeTrue()) - release.MarkPostActionsExecuting("") - Expect(release.Status.PostActionsExecution.StartTime.IsZero()).To(BeTrue()) - }) - - It("should register the condition", func() { - Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkPostActionsExecuting("foo") + 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, postActionsExecutedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ - "Message": Equal("foo"), - "Reason": Equal(ProgressingReason.String()), + "Message": Equal("error"), + "Reason": Equal(FailedReason.String()), "Status": Equal(metav1.ConditionFalse), })) }) - }) - - When("MarkPostActionsExecutionFailed method is called", func() { - var release *Release - - BeforeEach(func() { - release = &Release{} - }) - - It("should do nothing if the Release post-actions execution has not started", func() { - release.MarkPostActionsExecutionFailed("") - Expect(release.Status.PostActionsExecution.CompletionTime).To(BeNil()) - }) - - It("should do nothing if the Release post-actions execution finished", func() { - release.MarkPostActionsExecuting("") - release.MarkPostActionsExecuted() - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeFalse()) - release.Status.PostActionsExecution.CompletionTime = &metav1.Time{} - release.MarkPostActionsExecutionFailed("") - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeTrue()) - }) - - It("should register the completion time", func() { - release.MarkPostActionsExecuting("") - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeTrue()) - release.MarkPostActionsExecutionFailed("") - Expect(release.Status.PostActionsExecution.CompletionTime.IsZero()).To(BeFalse()) - }) It("should register the condition", func() { Expect(release.Status.Conditions).To(HaveLen(0)) - release.MarkPostActionsExecuting("") - release.MarkPostActionsExecutionFailed("foo") + release.MarkTenantPipelineProcessingSkipped() - condition := meta.FindStatusCondition(release.Status.Conditions, postActionsExecutedConditionType.String()) + condition := meta.FindStatusCondition(release.Status.Conditions, tenantProcessedConditionType.String()) Expect(condition).NotTo(BeNil()) Expect(*condition).To(MatchFields(IgnoreExtras, Fields{ - "Message": Equal("foo"), - "Reason": Equal(FailedReason.String()), - "Status": Equal(metav1.ConditionFalse), + "Reason": Equal(SkippedReason.String()), + "Status": Equal(metav1.ConditionTrue), })) }) }) diff --git a/api/v1alpha1/releaseplan_types.go b/api/v1alpha1/releaseplan_types.go index 5f25aea7..d1ab9698 100644 --- a/api/v1alpha1/releaseplan_types.go +++ b/api/v1alpha1/releaseplan_types.go @@ -44,9 +44,13 @@ type ReleasePlanSpec struct { // +optional Data *runtime.RawExtension `json:"data,omitempty"` - // Pipeline contains all the information about the tenant Pipeline + // TenantPipeline contains all the information about the tenant Pipeline // +optional - Pipeline *tektonutils.ParameterizedPipeline `json:"pipeline,omitempty"` + TenantPipeline *tektonutils.ParameterizedPipeline `json:"tenantPipeline,omitempty"` + + // FinalPipeline contains all the information about the final Pipeline + // +optional + FinalPipeline *tektonutils.ParameterizedPipeline `json:"finalPipeline,omitempty"` // ReleaseGracePeriodDays is the number of days a Release should be kept // This value is used to define the Release ExpirationTime diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ec2bbd57..b02fbd9f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -385,8 +385,13 @@ func (in *ReleasePlanSpec) DeepCopyInto(out *ReleasePlanSpec) { *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } - if in.Pipeline != nil { - in, out := &in.Pipeline, &out.Pipeline + if in.TenantPipeline != nil { + in, out := &in.TenantPipeline, &out.TenantPipeline + *out = new(utils.ParameterizedPipeline) + (*in).DeepCopyInto(*out) + } + if in.FinalPipeline != nil { + in, out := &in.FinalPipeline, &out.FinalPipeline *out = new(utils.ParameterizedPipeline) (*in).DeepCopyInto(*out) } @@ -551,8 +556,8 @@ func (in *ReleaseStatus) DeepCopyInto(out *ReleaseStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.FinalProcessing.DeepCopyInto(&out.FinalProcessing) in.ManagedProcessing.DeepCopyInto(&out.ManagedProcessing) - in.PostActionsExecution.DeepCopyInto(&out.PostActionsExecution) in.TenantProcessing.DeepCopyInto(&out.TenantProcessing) in.Validation.DeepCopyInto(&out.Validation) if in.CompletionTime != nil { diff --git a/config/crd/bases/appstudio.redhat.com_releaseplans.yaml b/config/crd/bases/appstudio.redhat.com_releaseplans.yaml index 515d36b2..a1b2cdd3 100644 --- a/config/crd/bases/appstudio.redhat.com_releaseplans.yaml +++ b/config/crd/bases/appstudio.redhat.com_releaseplans.yaml @@ -95,9 +95,9 @@ spec: the managed Release Pipeline type: object x-kubernetes-preserve-unknown-fields: true - pipeline: - description: Pipeline contains all the information about the tenant - Pipeline + finalPipeline: + description: FinalPipeline contains all the information about the + final Pipeline properties: params: description: Params is a slice of parameters for a given resolver @@ -180,6 +180,81 @@ spec: description: Target references where to send the release requests pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + tenantPipeline: + description: TenantPipeline contains all the information about the + tenant Pipeline + properties: + params: + description: Params is a slice of parameters for a given resolver + items: + description: Param defines the parameters for a given resolver + in PipelineRef + properties: + name: + description: Name is the name of the parameter + type: string + value: + description: Value is the value of the parameter + type: string + required: + - name + - value + type: object + type: array + pipelineRef: + description: PipelineRef is the reference to the Pipeline + properties: + params: + description: Params is a slice of parameters for a given resolver + items: + description: Param defines the parameters for a given resolver + in PipelineRef + properties: + name: + description: Name is the name of the parameter + type: string + value: + description: Value is the value of the parameter + type: string + required: + - name + - value + type: object + type: array + resolver: + description: Resolver is the name of a Tekton resolver to + be used (e.g. git) + type: string + required: + - params + - resolver + type: object + serviceAccountName: + description: ServiceAccountName is the ServiceAccount to use during + the execution of the Pipeline + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + timeouts: + description: Timeouts defines the different Timeouts to use in + the PipelineRun execution + properties: + finally: + description: Finally sets the maximum allowed duration of + this pipeline's finally + type: string + pipeline: + description: Pipeline sets the maximum allowed duration for + execution of the entire pipeline. The sum of individual + timeouts for tasks and finally must not exceed this value. + type: string + tasks: + description: Tasks sets the maximum allowed duration of this + pipeline's tasks + type: string + type: object + required: + - pipelineRef + type: object required: - application type: object diff --git a/config/crd/bases/appstudio.redhat.com_releases.yaml b/config/crd/bases/appstudio.redhat.com_releases.yaml index 4aa36bc5..8e9433ff 100644 --- a/config/crd/bases/appstudio.redhat.com_releases.yaml +++ b/config/crd/bases/appstudio.redhat.com_releases.yaml @@ -180,9 +180,9 @@ spec: description: ExpirationTime is the time when a Release can be purged format: date-time type: string - managedProcessing: - description: ManagedProcessing contains information about the release - managed processing + finalProcessing: + description: FinalProcessing contains information about the release + final processing properties: completionTime: description: CompletionTime is the time when the Release processing @@ -206,9 +206,9 @@ spec: format: date-time type: string type: object - 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 processing diff --git a/controllers/release/adapter.go b/controllers/release/adapter.go index 5ab000a0..0b987bd6 100644 --- a/controllers/release/adapter.go +++ b/controllers/release/adapter.go @@ -157,8 +157,8 @@ func (a *adapter) EnsureReleaseIsCompleted() (controller.OperationResult, error) return controller.ContinueProcessing() } - // The managed pipeline processing has to complete for a Release to be completed - if !a.release.HasManagedPipelineProcessingFinished() { + // The final pipeline processing has to complete for a Release to be completed + if !a.release.IsFinalPipelineProcessed() { return controller.ContinueProcessing() } @@ -201,7 +201,7 @@ func (a *adapter) EnsureTenantPipelineIsProcessed() (controller.OperationResult, return controller.RequeueWithError(err) } - if releasePlan.Spec.Pipeline == nil { + if releasePlan.Spec.TenantPipeline == nil { // no tenant pipeline to run patch := client.MergeFrom(a.release.DeepCopy()) a.release.MarkTenantPipelineProcessingSkipped() @@ -290,6 +290,52 @@ func (a *adapter) EnsureManagedPipelineIsProcessed() (controller.OperationResult return controller.ContinueProcessing() } +// EnsureFinalPipelineIsProcessed is an operation that will ensure that a Final Release PipelineRun associated to the Release +// being processed exist. Otherwise, it will be created. +func (a *adapter) EnsureFinalPipelineIsProcessed() (controller.OperationResult, error) { + if a.release.HasFinalPipelineProcessingFinished() || !a.release.HasManagedPipelineProcessingFinished() { + return controller.ContinueProcessing() + } + + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.FinalPipelineType) + if err != nil && !errors.IsNotFound(err) { + return controller.RequeueWithError(err) + } + + if pipelineRun == nil || !a.release.IsFinalPipelineProcessing() { + releasePlan, err := a.loader.GetReleasePlan(a.ctx, a.client, a.release) + if err != nil { + return controller.RequeueWithError(err) + } + + if releasePlan.Spec.FinalPipeline == nil { + // no final pipeline to run in the ReleasePlan + patch := client.MergeFrom(a.release.DeepCopy()) + a.release.MarkFinalPipelineProcessingSkipped() + return controller.RequeueOnErrorOrContinue(a.client.Status().Patch(a.ctx, a.release, patch)) + } + + if pipelineRun == nil { + snapshot, err := a.loader.GetSnapshot(a.ctx, a.client, a.release) + if err != nil { + return controller.RequeueWithError(err) + } + + pipelineRun, err = a.createFinalPipelineRun(releasePlan, snapshot) + if err != nil { + return controller.RequeueWithError(err) + } + + a.logger.Info(fmt.Sprintf("Created %s Release PipelineRun", metadata.FinalPipelineType), + "PipelineRun.Name", pipelineRun.Name, "PipelineRun.Namespace", pipelineRun.Namespace) + } + + return controller.RequeueOnErrorOrContinue(a.registerFinalProcessingData(pipelineRun)) + } + + return controller.ContinueProcessing() +} + // EnsureReleaseExpirationTimeIsAdded is an operation that ensures that a Release has the ExpirationTime set. func (a *adapter) EnsureReleaseExpirationTimeIsAdded() (controller.OperationResult, error) { if a.release.Status.ExpirationTime == nil { @@ -374,11 +420,32 @@ func (a *adapter) EnsureManagedPipelineProcessingIsTracked() (controller.Operati return controller.ContinueProcessing() } +// EnsureFinalPipelineProcessingIsTracked is an operation that will ensure that the Release Final PipelineRun status +// is tracked in the Release being processed. +func (a *adapter) EnsureFinalPipelineProcessingIsTracked() (controller.OperationResult, error) { + if !a.release.IsFinalPipelineProcessing() || a.release.HasFinalPipelineProcessingFinished() { + return controller.ContinueProcessing() + } + + pipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.FinalPipelineType) + if err != nil { + return controller.RequeueWithError(err) + } + if pipelineRun != nil { + err = a.registerFinalProcessingStatus(pipelineRun) + if err != nil { + 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() { + if !a.release.HasTenantPipelineProcessingFinished() || !a.release.HasManagedPipelineProcessingFinished() || !a.release.HasFinalPipelineProcessingFinished() { return controller.ContinueProcessing() } @@ -412,6 +479,45 @@ func (a *adapter) cleanupProcessingResources(pipelineRun *tektonv1.PipelineRun, return nil } +// createFinalPipelineRun creates and returns a new Final Release PipelineRun. The new PipelineRun will include owner +// annotations, so it triggers Release reconciles whenever it changes. The Pipeline information and the parameters to it +// will be extracted from the given ReleasePlan. The Release's Snapshot will also be passed to the release +// PipelineRun. +func (a *adapter) createFinalPipelineRun(releasePlan *v1alpha1.ReleasePlan, snapshot *applicationapiv1alpha1.Snapshot) (*tektonv1.PipelineRun, error) { + pipelineRun, err := utils.NewPipelineRunBuilder(metadata.FinalPipelineType, releasePlan.Namespace). + WithAnnotations(metadata.GetAnnotationsWithPrefix(a.release, integrationgitops.PipelinesAsCodePrefix)). + WithFinalizer(metadata.ReleaseFinalizer). + WithLabels(map[string]string{ + metadata.ApplicationNameLabel: releasePlan.Spec.Application, + metadata.PipelinesTypeLabel: metadata.FinalPipelineType, + metadata.ReleaseNameLabel: a.release.Name, + metadata.ReleaseNamespaceLabel: a.release.Namespace, + metadata.ReleaseSnapshotLabel: a.release.Spec.Snapshot, + }). + WithObjectReferences(a.release, releasePlan, snapshot). + WithParams(releasePlan.Spec.FinalPipeline.GetTektonParams()...). + WithOwner(a.release). + WithPipelineRef(releasePlan.Spec.FinalPipeline.PipelineRef.ToTektonPipelineRef()). + WithServiceAccount(releasePlan.Spec.FinalPipeline.ServiceAccountName). + WithTimeouts(&releasePlan.Spec.FinalPipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts). + WithWorkspaceFromVolumeTemplate( + os.Getenv("DEFAULT_RELEASE_WORKSPACE_NAME"), + os.Getenv("DEFAULT_RELEASE_WORKSPACE_SIZE"), + ). + Build() + + if err != nil { + return nil, err + } + + err = a.client.Create(a.ctx, pipelineRun) + if err != nil { + return nil, err + } + + return pipelineRun, nil +} + // createManagedPipelineRun creates and returns a new managed Release PipelineRun. The new PipelineRun will include owner // annotations, so it triggers Release reconciles whenever it changes. The Pipeline information and the parameters to it // will be extracted from the given ReleasePlanAdmission. The Release's Snapshot will also be passed to the release @@ -469,11 +575,11 @@ func (a *adapter) createTenantPipelineRun(releasePlan *v1alpha1.ReleasePlan, sna metadata.ReleaseSnapshotLabel: a.release.Spec.Snapshot, }). WithObjectReferences(a.release, releasePlan, snapshot). - WithParams(releasePlan.Spec.Pipeline.GetTektonParams()...). + WithParams(releasePlan.Spec.TenantPipeline.GetTektonParams()...). WithOwner(a.release). - WithPipelineRef(releasePlan.Spec.Pipeline.PipelineRef.ToTektonPipelineRef()). - WithServiceAccount(releasePlan.Spec.Pipeline.ServiceAccountName). - WithTimeouts(&releasePlan.Spec.Pipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts). + WithPipelineRef(releasePlan.Spec.TenantPipeline.PipelineRef.ToTektonPipelineRef()). + WithServiceAccount(releasePlan.Spec.TenantPipeline.ServiceAccountName). + WithTimeouts(&releasePlan.Spec.TenantPipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts). WithWorkspaceFromVolumeTemplate( os.Getenv("DEFAULT_RELEASE_WORKSPACE_NAME"), os.Getenv("DEFAULT_RELEASE_WORKSPACE_SIZE"), @@ -575,6 +681,24 @@ func (a *adapter) finalizeRelease(delete bool) error { } } + // Cleanup Final Processing Resources + finalPipelineRun, err := a.loader.GetReleasePipelineRun(a.ctx, a.client, a.release, metadata.FinalPipelineType) + if err != nil && !errors.IsNotFound(err) { + return err + } + + err = a.cleanupProcessingResources(finalPipelineRun, nil) + if err != nil { + return err + } + + if delete && finalPipelineRun != nil { + err = a.client.Delete(a.ctx, finalPipelineRun) + if err != nil && !errors.IsNotFound(err) { + return err + } + } + a.logger.Info("Successfully finalized Release") return nil @@ -608,6 +732,22 @@ func (a *adapter) registerTenantProcessingData(releasePipelineRun *tektonv1.Pipe return a.client.Status().Patch(a.ctx, a.release, patch) } +// registerFinalProcessingData adds all the Release Final processing information to its Status and marks it as final processing. +func (a *adapter) registerFinalProcessingData(releasePipelineRun *tektonv1.PipelineRun) error { + if releasePipelineRun == nil { + return nil + } + + patch := client.MergeFrom(a.release.DeepCopy()) + + a.release.Status.FinalProcessing.PipelineRun = fmt.Sprintf("%s%c%s", + releasePipelineRun.Namespace, types.Separator, releasePipelineRun.Name) + + a.release.MarkFinalPipelineProcessing() + + 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 { @@ -671,6 +811,27 @@ func (a *adapter) registerManagedProcessingStatus(pipelineRun *tektonv1.Pipeline return a.client.Status().Patch(a.ctx, a.release, patch) } +// registerFinalProcessingStatus updates the status of the Release being processed by monitoring the status of the +// associated final 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) registerFinalProcessingStatus(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.MarkFinalPipelineProcessed() + } else { + a.release.MarkFinalPipelineProcessingFailed(condition.Message) + a.release.MarkReleaseFailed("Release processing failed on final pipelineRun") + } + + 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 // has the automated label but doesn't have automated set in its status, this function will return an error so the // operation knows to requeue the Release. @@ -729,7 +890,7 @@ func (a *adapter) validateProcessingResources() *controller.ValidationResult { return &controller.ValidationResult{Err: err} } - if releasePlan.Spec.Pipeline == nil { + if releasePlan.Spec.TenantPipeline == nil { resources, err := a.loader.GetProcessingResources(a.ctx, a.client, a.release) if err != nil { if resources == nil || resources.ReleasePlan == nil || resources.ReleasePlanAdmission == nil || errors.IsNotFound(err) { @@ -751,17 +912,27 @@ func (a *adapter) validatePipelineSource() *controller.ValidationResult { return a.validationError(err) } - if releasePlan.Spec.Pipeline != nil { - pipelineRef = releasePlan.Spec.Pipeline.PipelineRef - } else { - releasePlanAdmission, err := a.loader.GetActiveReleasePlanAdmissionFromRelease(a.ctx, a.client, a.release) - if err != nil { - return a.validationError(err) + if releasePlan.Spec.TenantPipeline != nil { + pipelineRef = releasePlan.Spec.TenantPipeline.PipelineRef + if !a.releaseServiceConfig.Spec.Debug && pipelineRef.IsClusterScoped() { + a.release.MarkValidationFailed("tried using debug only options while debug mode is disabled in the ReleaseServiceConfig") + return &controller.ValidationResult{Valid: false} } + } - pipelineRef = releasePlanAdmission.Spec.Pipeline.PipelineRef + if releasePlan.Spec.FinalPipeline != nil { + pipelineRef = releasePlan.Spec.FinalPipeline.PipelineRef + if !a.releaseServiceConfig.Spec.Debug && pipelineRef.IsClusterScoped() { + a.release.MarkValidationFailed("tried using debug only options while debug mode is disabled in the ReleaseServiceConfig") + return &controller.ValidationResult{Valid: false} + } } + releasePlanAdmission, err := a.loader.GetActiveReleasePlanAdmissionFromRelease(a.ctx, a.client, a.release) + if err != nil { + return a.validationError(err) + } + pipelineRef = releasePlanAdmission.Spec.Pipeline.PipelineRef if !a.releaseServiceConfig.Spec.Debug && pipelineRef.IsClusterScoped() { a.release.MarkValidationFailed("tried using debug only options while debug mode is disabled in the ReleaseServiceConfig") return &controller.ValidationResult{Valid: false} @@ -788,7 +959,7 @@ func (a *adapter) validatePipelineDefined() *controller.ValidationResult { a.release.Status.Target = releasePlan.Spec.Target } - if releasePlan.Spec.Pipeline == nil { + if releasePlan.Spec.TenantPipeline == 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) diff --git a/controllers/release/adapter_test.go b/controllers/release/adapter_test.go index 0fce4c69..78d4da1b 100644 --- a/controllers/release/adapter_test.go +++ b/controllers/release/adapter_test.go @@ -179,6 +179,10 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(!result.RequeueRequest && result.CancelRequest).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) + result, err = adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && result.CancelRequest).To(BeFalse()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.client.Delete(adapter.ctx, adapter.release)).To(Succeed()) adapter.release, err = adapter.loader.GetRelease(adapter.ctx, adapter.client, adapter.release.Name, adapter.release.Namespace) Expect(adapter.release).NotTo(BeNil()) @@ -197,6 +201,10 @@ var _ = Describe("Release adapter", Ordered, func() { 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.FinalPipelineType) + Expect(pipelineRun).To(Or(BeNil(), HaveField("DeletionTimestamp", Not(BeNil())))) + Expect(err).NotTo(HaveOccurred()) + _, err = adapter.loader.GetRelease(adapter.ctx, adapter.client, adapter.release.Name, adapter.release.Namespace) Expect(err).To(HaveOccurred()) Expect(errors.IsNotFound(err)).To(BeTrue()) @@ -262,7 +270,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(adapter.release.IsReleased()).To(BeFalse()) }) - It("should do nothing if the managed processing has not completed", func() { + It("should do nothing if the final processing has not completed", func() { result, err := adapter.EnsureReleaseIsCompleted() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -272,12 +280,12 @@ var _ = Describe("Release adapter", Ordered, func() { It("should complete the release if all the required phases have completed", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { - ContextKey: loader.ReleasePlanAdmissionContextKey, - Resource: releasePlanAdmission, + ContextKey: loader.ReleasePlanContextKey, + Resource: releasePlan, }, }) - adapter.release.MarkManagedPipelineProcessing() - adapter.release.MarkManagedPipelineProcessed() + adapter.release.MarkFinalPipelineProcessing() + adapter.release.MarkFinalPipelineProcessed() result, err := adapter.EnsureReleaseIsCompleted() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -322,6 +330,192 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("EnsureFinalPipelineIsProcessed is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + adapter.releaseServiceConfig = releaseServiceConfig + }) + + It("should do nothing if the Release final pipeline is complete", func() { + adapter.release.MarkFinalPipelineProcessing() + adapter.release.MarkFinalPipelineProcessed() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should do nothing if the Release managed pipeline processing has not yet completed", func() { + adapter.release.MarkManagedPipelineProcessing() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should do nothing if the Release tenant pipeline processing has not yet completed", func() { + adapter.release.MarkTenantPipelineProcessed() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should requeue with error if fetching the Release final pipeline returns an error besides not found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePipelineRunContextKey, + Err: fmt.Errorf("some error"), + }, + }) + adapter.release.MarkManagedPipelineProcessingSkipped() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).To(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + }) + + It("should mark the Final Pipeline Processing as Skipped if the ReleasePlan isn't found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ProcessingResourcesContextKey, + Err: fmt.Errorf("no ReleasePlan can be found"), + }, + }) + adapter.release.MarkManagedPipelineProcessingSkipped() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + Expect(adapter.release.IsFinalPipelineProcessed()).To(BeTrue()) + + }) + + It("should continue and mark final processing as skipped if the ReleasePlan has no Final Pipeline set", func() { + newReleasePlan := releasePlan.DeepCopy() + newReleasePlan.Spec.FinalPipeline = nil + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ProcessingResourcesContextKey, + Resource: &loader.ProcessingResources{ + ReleasePlan: newReleasePlan, + }, + }, + }) + adapter.release.MarkManagedPipelineProcessingSkipped() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeFalse()) + Expect(adapter.release.IsFinalPipelineProcessed()).To(BeTrue()) + }) + + It("should register the processing data if the PipelineRun already exists", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePipelineRunContextKey, + Resource: &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline-run", + Namespace: "default", + }, + }, + }, + }) + adapter.release.MarkFinalPipelineProcessing() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeTrue()) + }) + + It("should requeue the Release if any of the resources is not found", func() { + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Err: fmt.Errorf("Not Found"), + Resource: nil, + }, + }) + adapter.release.MarkManagedPipelineProcessingSkipped() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).To(HaveOccurred()) + }) + + It("should create a pipelineRun and register the processing data if all the required resources are present", func() { + releasePlan := &v1alpha1.ReleasePlan{} + + 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}, + } + + releasePlan = &v1alpha1.ReleasePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "release-plan", + Namespace: "default", + }, + Spec: v1alpha1.ReleasePlanSpec{ + Application: application.Name, + FinalPipeline: ¶meterizedPipeline, + ReleaseGracePeriodDays: 6, + }, + } + releasePlan.Kind = "ReleasePlan" + + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: releasePlan, + }, + { + ContextKey: loader.SnapshotContextKey, + Resource: snapshot, + }, + }) + + adapter.release.MarkManagedPipelineProcessingSkipped() + + result, err := adapter.EnsureFinalPipelineIsProcessed() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.IsFinalPipelineProcessing()).To(BeTrue()) + + pipelineRun, err := adapter.loader.GetReleasePipelineRun(adapter.ctx, adapter.client, adapter.release, metadata.FinalPipelineType) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.client.Delete(adapter.ctx, pipelineRun)).To(Succeed()) + }) + + }) + When("EnsureManagedPipelineIsProcessed is called", func() { var adapter *adapter @@ -686,9 +880,9 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(adapter.release.IsTenantPipelineProcessing()).To(BeFalse()) }) - It("should continue and mark tenant processing as skipped if the ReleasePlan has no Pipeline set", func() { + It("should continue and mark tenant processing as skipped if the ReleasePlan has no Tenant Pipeline set", func() { newReleasePlan := releasePlan.DeepCopy() - newReleasePlan.Spec.Pipeline = nil + newReleasePlan.Spec.TenantPipeline = nil adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { ContextKey: loader.ProcessingResourcesContextKey, @@ -766,7 +960,8 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: ¶meterizedPipeline, + TenantPipeline: ¶meterizedPipeline, + FinalPipeline: ¶meterizedPipeline, ReleaseGracePeriodDays: 6, }, } @@ -913,7 +1108,7 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) - When("EnsureManagedPipelineProcessingIsTracked is called", func() { + When("EnsureManagedProcessingIsTracked is called", func() { var adapter *adapter AfterEach(func() { @@ -974,6 +1169,65 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("EnsureFinalProcessingIsTracked is called", func() { + var adapter *adapter + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + }) + + It("should continue if the Release final pipeline processing has not started", func() { + result, err := adapter.EnsureFinalPipelineProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should continue if the Release final pipeline processing has finished", func() { + adapter.release.MarkFinalPipelineProcessing() + adapter.release.MarkFinalPipelineProcessed() + + result, err := adapter.EnsureFinalPipelineProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should track the status if the PipelineRun exists", func() { + adapter.release.MarkFinalPipelineProcessing() + + 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.EnsureFinalPipelineProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(adapter.release.HasFinalPipelineProcessingFinished()).To(BeTrue()) + }) + + It("should continue if the PipelineRun doesn't exist", func() { + adapter.release.MarkFinalPipelineProcessing() + + result, err := adapter.EnsureFinalPipelineProcessingIsTracked() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + + }) + When("EnsureReleaseExpirationTimeIsAdded is called", func() { var adapter *adapter var newReleasePlan *v1alpha1.ReleasePlan @@ -1064,6 +1318,13 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(err).NotTo(HaveOccurred()) }) + It("Should continure if the Release final processing has not finished", func() { + adapter.release.MarkFinalPipelineProcessingSkipped() + result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() + Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) + It("should call finalizeRelease with false if all release processing is complete", func() { adapter.releaseServiceConfig = releaseServiceConfig resources := &loader.ProcessingResources{ @@ -1097,13 +1358,14 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: ¶meterizedPipeline, + TenantPipeline: ¶meterizedPipeline, + FinalPipeline: ¶meterizedPipeline, ReleaseGracePeriodDays: 6, }, } newReleasePlan.Kind = "ReleasePlan" - // Create tenant and managed pipelineRuns + // Create tenant, managed, final and pipelineRuns pipelineRun, err := adapter.createTenantPipelineRun(newReleasePlan, snapshot) Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) @@ -1112,12 +1374,18 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(pipelineRun).NotTo(BeNil()) Expect(err).NotTo(HaveOccurred()) + pipelineRun, err = adapter.createFinalPipelineRun(newReleasePlan, snapshot) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + adapter.release.MarkTenantPipelineProcessing() adapter.release.MarkTenantPipelineProcessed() adapter.release.MarkManagedPipelineProcessing() adapter.release.MarkManagedPipelineProcessed() + adapter.release.MarkFinalPipelineProcessing() + adapter.release.MarkFinalPipelineProcessed() - // Ensure both pipelineRuns have finalizers removed + // Ensure all pipelineRuns have finalizers removed result, err := adapter.EnsureReleaseProcessingResourcesAreCleanedUp() Expect(!result.RequeueRequest && !result.CancelRequest).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) @@ -1133,6 +1401,12 @@ var _ = Describe("Release adapter", Ordered, func() { 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.FinalPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).NotTo(BeNil()) + Expect(pipelineRun.Finalizers).To(HaveLen(0)) + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) }) }) @@ -1241,7 +1515,7 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: ¶meterizedPipeline, + TenantPipeline: ¶meterizedPipeline, ReleaseGracePeriodDays: 6, }, } @@ -1323,7 +1597,7 @@ var _ = Describe("Release adapter", Ordered, func() { }) It("contains the proper timeout value", func() { - Expect(pipelineRun.Spec.Timeouts.Pipeline).To(Equal(newReleasePlan.Spec.Pipeline.Timeouts.Pipeline)) + Expect(pipelineRun.Spec.Timeouts.Pipeline).To(Equal(newReleasePlan.Spec.TenantPipeline.Timeouts.Pipeline)) }) }) @@ -1462,6 +1736,133 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("createFinalPipelineRun is called", func() { + var ( + adapter *adapter + pipelineRun *tektonv1.PipelineRun + newReleasePlan *v1alpha1.ReleasePlan + ) + + AfterEach(func() { + _ = adapter.client.Delete(ctx, adapter.release) + + Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) + }) + + BeforeEach(func() { + adapter = createReleaseAndAdapter() + + 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, + FinalPipeline: ¶meterizedPipeline, + ReleaseGracePeriodDays: 6, + }, + } + newReleasePlan.Kind = "ReleasePlan" + + adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ + { + ContextKey: loader.ReleasePlanContextKey, + Resource: newReleasePlan, + }, + }) + + var err error + pipelineRun, err = adapter.createFinalPipelineRun(newReleasePlan, snapshot) + Expect(pipelineRun).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("returns a PipelineRun with the right prefix", func() { + Expect(reflect.TypeOf(pipelineRun)).To(Equal(reflect.TypeOf(&tektonv1.PipelineRun{}))) + Expect(pipelineRun.Name).To(HavePrefix("final")) + }) + + It("has the release reference", func() { + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", strings.ToLower(adapter.release.Kind)))) + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", + fmt.Sprintf("%s%c%s", adapter.release.Namespace, types.Separator, adapter.release.Name)))) + }) + + It("has the releasePlan reference", func() { + name := []rune(releasePlan.Kind) + name[0] = unicode.ToLower(name[0]) + + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", string(name)))) + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", + fmt.Sprintf("%s%c%s", releasePlan.Namespace, types.Separator, releasePlan.Name)))) + }) + + It("has the snapshot reference", func() { + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", strings.ToLower(snapshot.Kind)))) + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", + fmt.Sprintf("%s%c%s", snapshot.Namespace, types.Separator, snapshot.Name)))) + }) + + It("has owner annotations", func() { + Expect(pipelineRun.GetAnnotations()[handler.NamespacedNameAnnotation]).To(ContainSubstring(adapter.release.Name)) + Expect(pipelineRun.GetAnnotations()[handler.TypeAnnotation]).To(ContainSubstring("Release")) + }) + + It("has release labels", func() { + Expect(pipelineRun.GetLabels()[metadata.PipelinesTypeLabel]).To(Equal(metadata.FinalPipelineType)) + Expect(pipelineRun.GetLabels()[metadata.ReleaseNameLabel]).To(Equal(adapter.release.Name)) + Expect(pipelineRun.GetLabels()[metadata.ReleaseNamespaceLabel]).To(Equal(testNamespace)) + Expect(pipelineRun.GetLabels()[metadata.ReleaseSnapshotLabel]).To(Equal(adapter.release.Spec.Snapshot)) + }) + + It("contains a parameter with the taskGitUrl", func() { + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", "taskGitUrl"))) + var url string + resolverParams := pipelineRun.Spec.PipelineRef.ResolverRef.Params + for i := range resolverParams { + if resolverParams[i].Name == "url" { + url = resolverParams[i].Value.StringVal + } + } + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", url))) + }) + + It("contains a parameter with the taskGitRevision", func() { + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", "taskGitRevision"))) + var revision string + resolverParams := pipelineRun.Spec.PipelineRef.ResolverRef.Params + for i := range resolverParams { + if resolverParams[i].Name == "revision" { + revision = resolverParams[i].Value.StringVal + } + } + Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", revision))) + }) + + It("contains the proper timeout value", func() { + Expect(pipelineRun.Spec.Timeouts.Pipeline).To(Equal(newReleasePlan.Spec.FinalPipeline.Timeouts.Pipeline)) + }) + + }) + When("createRoleBindingForClusterRole is called", func() { var adapter *adapter @@ -1552,7 +1953,8 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, + FinalPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, }, } @@ -1593,6 +1995,19 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed()) }) + It("finalizes the Release and removes the finalizer from the Final PipelineRun when called with false", func() { + pipelineRun, err := adapter.createFinalPipelineRun(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.FinalPipelineType) + 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()) @@ -1622,6 +2037,17 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(err).NotTo(HaveOccurred()) Expect(pipelineRun).To(BeNil()) }) + + It("finalizes the Release and deletes the Final PipelineRun when called with true", func() { + pipelineRun, err := adapter.createFinalPipelineRun(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.FinalPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(pipelineRun).To(BeNil()) + }) }) When("getEmptyReleaseServiceConfig is called", func() { @@ -1725,6 +2151,36 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("registerFinalProcessingData 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.registerFinalProcessingData(nil)).To(Succeed()) + Expect(adapter.release.Status.FinalProcessing.PipelineRun).To(BeEmpty()) + }) + + It("registers the Release final processing data", func() { + pipelineRun := &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline-run", + Namespace: "default", + }, + } + Expect(adapter.registerFinalProcessingData(pipelineRun)).To(Succeed()) + Expect(adapter.release.Status.FinalProcessing.PipelineRun).To(Equal(fmt.Sprintf("%s%c%s", + pipelineRun.Namespace, types.Separator, pipelineRun.Name))) + + }) + }) + When("registerTenantProcessingStatus is called", func() { var adapter *adapter @@ -1810,6 +2266,49 @@ var _ = Describe("Release adapter", Ordered, func() { }) }) + When("registerFinalProcessingStatus 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.registerFinalProcessingStatus(nil)).To(Succeed()) + Expect(adapter.release.Status.FinalProcessing.CompletionTime).To(BeNil()) + }) + + It("does nothing if the PipelineRun is not done", func() { + pipelineRun := &tektonv1.PipelineRun{} + Expect(adapter.registerFinalProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.Status.FinalProcessing.CompletionTime).To(BeNil()) + }) + + It("sets the Release as Final Processed if the PipelineRun succeeded", func() { + pipelineRun := &tektonv1.PipelineRun{} + pipelineRun.Status.MarkSucceeded("", "") + adapter.release.MarkFinalPipelineProcessing() + + Expect(adapter.registerFinalProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.IsFinalPipelineProcessed()).To(BeTrue()) + }) + + It("sets the Release as Final Processing failed if the PipelineRun didn't succeed", func() { + pipelineRun := &tektonv1.PipelineRun{} + pipelineRun.Status.MarkFailed("", "") + adapter.release.MarkFinalPipelineProcessing() + + Expect(adapter.registerFinalProcessingStatus(pipelineRun)).To(Succeed()) + Expect(adapter.release.HasFinalPipelineProcessingFinished()).To(BeTrue()) + Expect(adapter.release.IsFinalPipelineProcessed()).To(BeFalse()) + }) + + }) + When("calling validateAuthor", func() { var adapter *adapter var conditionMsg string @@ -2210,7 +2709,8 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, + FinalPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, Target: "default", }, @@ -2234,7 +2734,8 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, + FinalPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, Target: "default", }, @@ -2247,7 +2748,7 @@ var _ = Describe("Release adapter", Ordered, func() { Expect(result.Err).NotTo(HaveOccurred()) }) - It("should return true if only ReleasePlan has Pipeline Set and it has no Target", func() { + It("should return true if only ReleasePlan has TenantPipeline Set and it has no Target", func() { adapter.ctx = toolkit.GetMockedContext(ctx, []toolkit.MockData{ { ContextKey: loader.ReleasePlanContextKey, @@ -2258,7 +2759,7 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, }, }, @@ -2393,7 +2894,8 @@ var _ = Describe("Release adapter", Ordered, func() { }, Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, + FinalPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, }, }, diff --git a/controllers/release/controller.go b/controllers/release/controller.go index 0c215c41..424f127b 100644 --- a/controllers/release/controller.go +++ b/controllers/release/controller.go @@ -85,6 +85,8 @@ func (c *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu adapter.EnsureTenantPipelineProcessingIsTracked, adapter.EnsureManagedPipelineIsProcessed, adapter.EnsureManagedPipelineProcessingIsTracked, + adapter.EnsureFinalPipelineIsProcessed, + adapter.EnsureFinalPipelineProcessingIsTracked, adapter.EnsureReleaseProcessingResourcesAreCleanedUp, adapter.EnsureReleaseIsCompleted, }) diff --git a/controllers/releaseplan/adapter_test.go b/controllers/releaseplan/adapter_test.go index 18813d79..cb9c6777 100644 --- a/controllers/releaseplan/adapter_test.go +++ b/controllers/releaseplan/adapter_test.go @@ -214,7 +214,7 @@ var _ = Describe("ReleasePlan adapter", Ordered, func() { Spec: v1alpha1.ReleasePlanSpec{ Application: application.Name, Target: "default", - Pipeline: parameterizedPipeline, + TenantPipeline: parameterizedPipeline, ReleaseGracePeriodDays: 6, }, } diff --git a/loader/loader.go b/loader/loader.go index bf06e347..f4314f25 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -245,7 +245,7 @@ func (l *loader) GetRoleBindingFromReleaseStatus(ctx context.Context, cli client // 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 { + if pipelineType != metadata.ManagedPipelineType && pipelineType != metadata.TenantPipelineType && pipelineType != metadata.FinalPipelineType { return nil, fmt.Errorf("cannot fetch Release PipelineRun with invalid type %s", pipelineType) } diff --git a/loader/loader_test.go b/loader/loader_test.go index b84c8e96..604d6039 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -34,6 +34,7 @@ var _ = Describe("Release Adapter", Ordered, func() { component *applicationapiv1alpha1.Component enterpriseContractConfigMap *corev1.ConfigMap enterpriseContractPolicy *ecapiv1alpha1.EnterpriseContractPolicy + finalPipelineRun *tektonv1.PipelineRun managedPipelineRun *tektonv1.PipelineRun tenantPipelineRun *tektonv1.PipelineRun release *v1alpha1.Release @@ -366,6 +367,14 @@ var _ = Describe("Release Adapter", Ordered, func() { }) When("calling GetReleasePipelineRun", func() { + + It("returns a Final PipelineRun if the labels match with the release data", func() { + returnedObject, err := loader.GetReleasePipelineRun(ctx, k8sClient, release, metadata.FinalPipelineType) + Expect(err).NotTo(HaveOccurred()) + Expect(returnedObject).NotTo(Equal(&tektonv1.PipelineRun{})) + Expect(returnedObject.Name).To(Equal(finalPipelineRun.Name)) + }) + 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()) @@ -569,6 +578,19 @@ var _ = Describe("Release Adapter", Ordered, func() { } Expect(k8sClient.Create(ctx, release)).To(Succeed()) + finalPipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + metadata.ReleaseNameLabel: release.Name, + metadata.ReleaseNamespaceLabel: release.Namespace, + metadata.PipelinesTypeLabel: metadata.FinalPipelineType, + }, + Name: "final-pipeline-run", + Namespace: "default", + }, + } + Expect(k8sClient.Create(ctx, finalPipelineRun)).To(Succeed()) + managedPipelineRun = &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ @@ -600,6 +622,7 @@ var _ = Describe("Release Adapter", Ordered, 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, finalPipelineRun)).To(Succeed()) Expect(k8sClient.Delete(ctx, managedPipelineRun)).To(Succeed()) Expect(k8sClient.Delete(ctx, tenantPipelineRun)).To(Succeed()) Expect(k8sClient.Delete(ctx, release)).To(Succeed()) diff --git a/metadata/labels.go b/metadata/labels.go index 81a89d36..0f7c272d 100644 --- a/metadata/labels.go +++ b/metadata/labels.go @@ -59,6 +59,9 @@ var ( // ApplicationNameLabel is the label used to specify the application associated with the PipelineRun ApplicationNameLabel = fmt.Sprintf("%s/%s", rhtapDomain, "application") + // FinalPipelineType is the value to be used in the PipelinesTypeLabel for final Pipelines + FinalPipelineType = "final" + // ManagedPipelineType is the value to be used in the PipelinesTypeLabel for managed Pipelines ManagedPipelineType = "managed" diff --git a/metrics/release.go b/metrics/release.go index 49623d36..99467443 100644 --- a/metrics/release.go +++ b/metrics/release.go @@ -31,14 +31,6 @@ var ( []string{}, ) - ReleaseConcurrentPostActionsExecutionsTotal = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "release_concurrent_post_actions_executions_total", - Help: "Total number of concurrent release post actions executions attempts", - }, - []string{}, - ) - ReleaseConcurrentProcessingsTotal = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "release_concurrent_processings_total", @@ -82,8 +74,8 @@ var ( ) // Prometheus fails if these are not in alphabetical order releaseDurationSecondsLabels = []string{ + "final_pipeline_processing_reason", "managed_pipeline_processing_reason", - "post_actions_reason", "release_reason", "target", "tenant_pipeline_processing_reason", @@ -95,19 +87,6 @@ var ( Buckets: []float64{60, 150, 300, 450, 600, 750, 900, 1050, 1200, 1800, 3600}, } - ReleasePostActionsExecutionDurationSeconds = prometheus.NewHistogramVec( - releasePostActionsExecutionDurationSecondsOpts, - releasePostActionsExecutionDurationSecondsLabels, - ) - releasePostActionsExecutionDurationSecondsLabels = []string{ - "reason", - } - releasePostActionsExecutionDurationSecondsOpts = prometheus.HistogramOpts{ - Name: "release_post_actions_execution_duration_seconds", - Help: "How long in seconds Release post-actions take to complete", - Buckets: []float64{60, 150, 300, 450, 600, 750, 900, 1050, 1200, 1800, 3600}, - } - ReleaseProcessingDurationSeconds = prometheus.NewHistogramVec( releaseProcessingDurationSecondsOpts, releaseProcessingDurationSecondsLabels, @@ -129,8 +108,8 @@ var ( ) // Prometheus fails if these are not in alphabetical order releaseTotalLabels = []string{ + "final_pipeline_processing_reason", "managed_pipeline_processing_reason", - "post_actions_reason", "release_reason", "target", "tenant_pipeline_processing_reason", @@ -146,15 +125,15 @@ 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, - managedProcessingReason, postActionsReason, releaseReason, target, tenantProcessingReason, validationReason string) { + finalProcessingReason, managedProcessingReason, releaseReason, target, tenantProcessingReason, validationReason string) { if startTime == nil || completionTime == nil { return } // Prometheus fails if these are not in alphabetical order labels := prometheus.Labels{ + "final_pipeline_processing_reason": finalProcessingReason, "managed_pipeline_processing_reason": managedProcessingReason, - "post_actions_reason": postActionsReason, "release_reason": releaseReason, "target": target, "tenant_pipeline_processing_reason": tenantProcessingReason, @@ -167,22 +146,6 @@ func RegisterCompletedRelease(startTime, completionTime *metav1.Time, ReleaseTotal.With(labels).Inc() } -// RegisterCompletedReleasePostActionsExecuted registers a Release post-actions execution as complete, adding a new -// observation for the Release post-actions execution duration and decreasing the number of concurrent executions. -// If either the startTime or the completionTime parameters are nil, no action will be taken. -func RegisterCompletedReleasePostActionsExecuted(startTime, completionTime *metav1.Time, reason string) { - if startTime == nil || completionTime == nil { - return - } - - ReleasePostActionsExecutionDurationSeconds. - With(prometheus.Labels{ - "reason": reason, - }). - Observe(completionTime.Sub(startTime.Time).Seconds()) - ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues().Dec() -} - // RegisterCompletedReleasePipelineProcessing registers a Release pipeline processing as complete, adding a // new observation for the Release processing duration with the specific type and decreasing the number of // concurent processings. If either the startTime or the completionTime parameters are nil, no action will be taken. @@ -241,21 +204,13 @@ func RegisterNewReleasePipelineProcessing(startTime, processingStartTime *metav1 ReleaseConcurrentProcessingsTotal.WithLabelValues().Inc() } -// RegisterNewReleasePostActionsExecution register a new Release post-actions execution, increasing the number of -// concurrent executions. -func RegisterNewReleasePostActionsExecution() { - ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues().Inc() -} - func init() { metrics.Registry.MustRegister( ReleaseConcurrentTotal, ReleaseConcurrentProcessingsTotal, - ReleaseConcurrentPostActionsExecutionsTotal, ReleasePreProcessingDurationSeconds, ReleaseValidationDurationSeconds, ReleaseDurationSeconds, - ReleasePostActionsExecutionDurationSeconds, ReleaseProcessingDurationSeconds, ReleaseTotal, ) diff --git a/metrics/release_test.go b/metrics/release_test.go index 05420ba5..a6289d5b 100644 --- a/metrics/release_test.go +++ b/metrics/release_test.go @@ -93,47 +93,6 @@ var _ = Describe("Release metrics", Ordered, func() { }) }) - When("RegisterCompletedReleasePostActionsExecuted 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(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleasePostActionsExecuted(nil, completionTime, "") - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - }) - - It("does nothing if the completion time is nil", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleasePostActionsExecuted(startTime, nil, "") - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - }) - - It("decrements ReleaseConcurrentPostActionsExecutionsTotal", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterCompletedReleasePostActionsExecuted(startTime, completionTime, "") - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(-1))) - }) - - It("adds an observation to ReleasePostActionsExecutionDurationSeconds", func() { - RegisterCompletedReleasePostActionsExecuted(startTime, completionTime, - releasePostActionsExecutionDurationSecondsLabels[0], - ) - Expect(testutil.CollectAndCompare(ReleasePostActionsExecutionDurationSeconds, - test.NewHistogramReader( - releasePostActionsExecutionDurationSecondsOpts, - releasePostActionsExecutionDurationSecondsLabels, - startTime, completionTime, - ))).To(Succeed()) - }) - }) - When("RegisterCompletedReleasePipelineProcessing is called", func() { var completionTime, startTime *metav1.Time @@ -268,27 +227,13 @@ var _ = Describe("Release metrics", Ordered, func() { }) }) - When("RegisterNewReleasePostActionsExecution is called", func() { - BeforeEach(func() { - initializeMetrics() - }) - - It("increments ReleaseConcurrentPostActionsExecutionsTotal", func() { - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(0))) - RegisterNewReleasePostActionsExecution() - Expect(testutil.ToFloat64(ReleaseConcurrentPostActionsExecutionsTotal.WithLabelValues())).To(Equal(float64(1))) - }) - }) - initializeMetrics = func() { ReleaseConcurrentTotal.Reset() ReleaseConcurrentProcessingsTotal.Reset() - ReleaseConcurrentPostActionsExecutionsTotal.Reset() ReleaseValidationDurationSeconds.Reset() ReleasePreProcessingDurationSeconds.Reset() ReleaseDurationSeconds.Reset() ReleaseProcessingDurationSeconds.Reset() - ReleasePostActionsExecutionDurationSeconds.Reset() ReleaseTotal.Reset() } diff --git a/tekton/utils.go b/tekton/utils.go index b1144027..b7047204 100644 --- a/tekton/utils.go +++ b/tekton/utils.go @@ -23,7 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -// isReleasePipelineRun returns a boolean indicating whether the object passed is a Managed or a Tenant Release PipelineRun. +// isReleasePipelineRun returns a boolean indicating whether the object passed is a Final, Managed or a Tenant Release PipelineRun. func isReleasePipelineRun(object client.Object) bool { _, ok := object.(*tektonv1.PipelineRun) if !ok { @@ -32,7 +32,7 @@ func isReleasePipelineRun(object client.Object) bool { labelValue, found := object.GetLabels()[metadata.PipelinesTypeLabel] - return found && (labelValue == metadata.ManagedPipelineType || labelValue == metadata.TenantPipelineType) + return found && (labelValue == metadata.FinalPipelineType || labelValue == metadata.ManagedPipelineType || labelValue == metadata.TenantPipelineType) } // hasPipelineSucceeded returns a boolean indicating whether the PipelineRun succeeded or not. diff --git a/tekton/utils_test.go b/tekton/utils_test.go index ecb04ed6..c1caad22 100644 --- a/tekton/utils_test.go +++ b/tekton/utils_test.go @@ -25,12 +25,20 @@ import ( var _ = Describe("Utils", Ordered, func() { When("isReleasePipelineRun is called", func() { - It("should return false when the PipelineRun is not of type 'managed' or 'tenant", func() { + It("should return false when the PipelineRun is not of type 'Final', 'managed', or 'tenant'", func() { pipelineRun, err := utils.NewPipelineRunBuilder("pipeline-run", "default").Build() Expect(err).NotTo(HaveOccurred()) Expect(isReleasePipelineRun(pipelineRun)).To(BeFalse()) }) + It("should return true when the PipelineRun is of type 'final'", func() { + pipelineRun, err := utils.NewPipelineRunBuilder("pipeline-run", "default"). + WithLabels(map[string]string{metadata.PipelinesTypeLabel: metadata.FinalPipelineType}). + Build() + Expect(err).NotTo(HaveOccurred()) + Expect(isReleasePipelineRun(pipelineRun)).To(BeTrue()) + }) + It("should return true when the PipelineRun is of type 'managed'", func() { pipelineRun, err := utils.NewPipelineRunBuilder("pipeline-run", "default"). WithLabels(map[string]string{metadata.PipelinesTypeLabel: metadata.ManagedPipelineType}).