Skip to content

Commit f8f8eb2

Browse files
committed
fix(KONFLUX-10903): add retry logic for PipelineRun finalizer addition
add RetryOnConflict wrapper to EnsurePipelineIsFinalized handle etcd timeout errors when adding finalizers.Modified AddFinalizerToPipelineRun to return unwrapped errors so RetryOnConflict can properly detect and retry transient errors and conflicts. Signed-off-by: Kasem Alem <[email protected]>
1 parent e938730 commit f8f8eb2

File tree

4 files changed

+53
-5
lines changed

4 files changed

+53
-5
lines changed

helpers/integration.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,10 @@ func AddFinalizerToPipelineRun(ctx context.Context, adapterClient client.Client,
444444
if ok := controllerutil.AddFinalizer(pipelineRun, finalizer); ok {
445445
err := adapterClient.Patch(ctx, pipelineRun, patch)
446446
if err != nil {
447-
return fmt.Errorf("error occurred while patching the updated PipelineRun after finalizer addition: %w", err)
447+
logger.Error(err, "error occurred while patching the updated PipelineRun after finalizer addition",
448+
"pipelineRun.Name", pipelineRun.Name)
449+
// don't return wrapped err, so we can use RetryOnConflict
450+
return err
448451
}
449452

450453
logger.LogAuditEvent("Added Finalizer to the PipelineRun", pipelineRun, LogActionUpdate, "finalizer", finalizer)

internal/controller/buildpipeline/buildpipeline_adapter.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,32 @@ func (a *Adapter) EnsurePipelineIsFinalized() (controller.OperationResult, error
193193
return controller.ContinueProcessing()
194194
}
195195

196-
// if pipelinerun has been deleted, do not add finalilzer
196+
// if pipelinerun has been deleted, do not add finalizer
197197
if a.pipelineRun.GetDeletionTimestamp() != nil {
198198
return controller.ContinueProcessing()
199199
}
200200

201-
err := h.AddFinalizerToPipelineRun(a.context, a.client, a.logger, a.pipelineRun, h.IntegrationPipelineRunFinalizer)
201+
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
202+
var err error
203+
// Refetch the PipelineRun to get the latest version before patching
204+
a.pipelineRun, err = a.loader.GetPipelineRun(a.context, a.client, a.pipelineRun.Name, a.pipelineRun.Namespace)
205+
if err != nil {
206+
return err
207+
}
208+
209+
// if pipelinerun has been deleted, do not add finalizer
210+
if a.pipelineRun.GetDeletionTimestamp() != nil {
211+
return nil
212+
}
213+
214+
// Check again if finalizer was already added (might have been added by another reconcile)
215+
if controllerutil.ContainsFinalizer(a.pipelineRun, h.IntegrationPipelineRunFinalizer) {
216+
return nil
217+
}
218+
219+
err = h.AddFinalizerToPipelineRun(a.context, a.client, a.logger, a.pipelineRun, h.IntegrationPipelineRunFinalizer)
220+
return err
221+
})
202222
if err != nil {
203223
// if IsNotFound error, do not log error or requeue
204224
if errors.IsNotFound(err) {

internal/controller/buildpipeline/buildpipeline_adapter_test.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1087,9 +1087,25 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
10871087

10881088
// Ensure PLR has finalizer
10891089
Eventually(func() bool {
1090-
return slices.Contains(buildPipelineRun.ObjectMeta.Finalizers, helpers.IntegrationPipelineRunFinalizer)
1090+
// Refetch the PipelineRun from the cluster to get the updated finalizer
1091+
existingBuildPLR := new(tektonv1.PipelineRun)
1092+
err := k8sClient.Get(ctx, types.NamespacedName{
1093+
Namespace: buildPipelineRun.Namespace,
1094+
Name: buildPipelineRun.Name,
1095+
}, existingBuildPLR)
1096+
if err != nil {
1097+
return false
1098+
}
1099+
return slices.Contains(existingBuildPLR.ObjectMeta.Finalizers, helpers.IntegrationPipelineRunFinalizer)
10911100
}, time.Second*10).Should(BeTrue())
10921101

1102+
// Refetch the PipelineRun to get the latest ResourceVersion before updating status
1103+
err = k8sClient.Get(ctx, types.NamespacedName{
1104+
Namespace: buildPipelineRun.Namespace,
1105+
Name: buildPipelineRun.Name,
1106+
}, buildPipelineRun)
1107+
Expect(err).ToNot(HaveOccurred())
1108+
10931109
// Update build PLR as completed
10941110
buildPipelineRun.Status = tektonv1.PipelineRunStatus{
10951111
PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{

internal/controller/buildpipeline/buildpipeline_controller.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
8787
})
8888
if err != nil {
8989
if errors.IsNotFound(err) {
90-
if err := helpers.RemoveFinalizerFromPipelineRun(ctx, r.Client, logger, pipelineRun, helpers.IntegrationPipelineRunFinalizer); err != nil {
90+
// Use retry logic to handle etcd timeouts when removing finalizer
91+
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
92+
// Refetch the PipelineRun to get the latest version before removing finalizer
93+
err := r.Get(ctx, req.NamespacedName, pipelineRun)
94+
if err != nil {
95+
return err
96+
}
97+
return helpers.RemoveFinalizerFromPipelineRun(ctx, r.Client, logger, pipelineRun, helpers.IntegrationPipelineRunFinalizer)
98+
})
99+
if err != nil {
91100
return ctrl.Result{}, err
92101
}
93102
}

0 commit comments

Comments
 (0)