This is an automated email from the ASF dual-hosted git repository.

squakez pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new 1c71a4c9a fix(ctrl): ignore NotFound when a Build is deleted 
mid-reconcile
1c71a4c9a is described below

commit 1c71a4c9a31068468cde0c1a4f183a6beb7a30ee
Author: Ravi <[email protected]>
AuthorDate: Sun Jun 7 02:42:57 2026 +0530

    fix(ctrl): ignore NotFound when a Build is deleted mid-reconcile
    
    Signed-off-by: Ravi <[email protected]>
---
 pkg/controller/build/build_controller.go      |  9 ++++
 pkg/controller/build/build_controller_test.go | 65 +++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/pkg/controller/build/build_controller.go 
b/pkg/controller/build/build_controller.go
index bc93643ad..998f7164e 100644
--- a/pkg/controller/build/build_controller.go
+++ b/pkg/controller/build/build_controller.go
@@ -196,6 +196,11 @@ func (r *reconcileBuild) Reconcile(ctx context.Context, 
request reconcile.Reques
 
                newTarget, err := a.Handle(ctx, target)
                if err != nil {
+                       // NotFound means the Build was deleted mid-reconcile 
(stale cache); benign, not an error.
+                       if k8serrors.IsNotFound(err) {
+                               return reconcile.Result{}, nil
+                       }
+
                        camelevent.NotifyError(r.recorder, &instance, target, 
instance.Name, instance.Kind, err)
 
                        return reconcile.Result{}, err
@@ -204,6 +209,10 @@ func (r *reconcileBuild) Reconcile(ctx context.Context, 
request reconcile.Reques
                if newTarget != nil {
                        err := r.update(ctx, targetLog, &instance, newTarget)
                        if err != nil {
+                               if k8serrors.IsNotFound(err) {
+                                       return reconcile.Result{}, nil
+                               }
+
                                return reconcile.Result{}, err
                        }
 
diff --git a/pkg/controller/build/build_controller_test.go 
b/pkg/controller/build/build_controller_test.go
index c77f92bc1..46ff33319 100644
--- a/pkg/controller/build/build_controller_test.go
+++ b/pkg/controller/build/build_controller_test.go
@@ -25,8 +25,11 @@ import (
        "github.com/apache/camel-k/v2/pkg/internal"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/types"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
@@ -82,3 +85,65 @@ func TestReconcileBuild(t *testing.T) {
        assert.NotNil(t, updated.Status, "status should not be nil")
        assert.NotEmpty(t, updated.Status.Phase, "phase should be set")
 }
+
+// TestReconcileBuildDeletedDuringReconcile verifies that a Build deleted 
mid-reconcile does not
+// surface as a reconcile error: a NotFound from the status write is benign 
(#6620).
+func TestReconcileBuildDeletedDuringReconcile(t *testing.T) {
+       ctx := context.TODO()
+
+       build := &v1.Build{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "test-build",
+                       Namespace: "default",
+               },
+               Spec: v1.BuildSpec{
+                       Tasks: []v1.Task{
+                               {
+                                       Builder: &v1.BuilderTask{
+                                               BaseTask: v1.BaseTask{
+                                                       Name: "builder",
+                                                       Configuration: 
v1.BuildConfiguration{
+                                                               Strategy: 
v1.BuildStrategyRoutine,
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       c, err := internal.NewFakeClient(build)
+       require.NoError(t, err)
+
+       // Simulate the Build being deleted concurrently. The fake client 
routes a status patch
+       // through Update, so the NotFound is injected there.
+       fakeClient := c.(*internal.FakeClient)
+       fakeClient.Intercept(&interceptor.Funcs{
+               Update: func(ctx context.Context, cl ctrl.WithWatch, obj 
ctrl.Object, opts ...ctrl.UpdateOption) error {
+                       if _, ok := obj.(*v1.Build); ok {
+                               return 
k8serrors.NewNotFound(v1.Resource("builds"), obj.GetName())
+                       }
+
+                       return cl.Update(ctx, obj, opts...)
+               },
+       })
+
+       r := &reconcileBuild{
+               client:   c,
+               reader:   c,
+               recorder: &internal.FakeRecorder{},
+       }
+
+       req := reconcile.Request{
+               NamespacedName: types.NamespacedName{
+                       Name:      "test-build",
+                       Namespace: "default",
+               },
+       }
+
+       result, err := r.Reconcile(ctx, req)
+
+       // The NotFound must be swallowed: no error, and no requeue since the 
Build is gone.
+       require.NoError(t, err)
+       assert.Equal(t, reconcile.Result{}, result)
+}

Reply via email to