Skip to content

Commit ff4b6dd

Browse files
committed
Add WithClusterNotFoundWrapper to controller builder
On-behalf-of: SAP <[email protected]> Signed-off-by: Marvin Beckers <[email protected]>
1 parent 216ff38 commit ff4b6dd

File tree

5 files changed

+71
-15
lines changed

5 files changed

+71
-15
lines changed

examples/cluster-api/main.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import (
4040

4141
mcbuilder "sigs.k8s.io/multicluster-runtime/pkg/builder"
4242
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"
43-
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
4443
mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile"
4544
capi "sigs.k8s.io/multicluster-runtime/providers/cluster-api"
4645
)
@@ -104,10 +103,6 @@ func main() {
104103

105104
cl, err := mcMgr.GetCluster(ctx, req.ClusterName)
106105
if err != nil {
107-
if errors.Is(err, multicluster.ErrClusterNotFound) {
108-
log.Info("Cluster not found, might have been disengaged")
109-
return reconcile.Result{}, nil
110-
}
111106
return reconcile.Result{}, err
112107
}
113108

examples/kind/main.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535

3636
mcbuilder "sigs.k8s.io/multicluster-runtime/pkg/builder"
3737
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"
38-
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
3938
mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile"
4039
"sigs.k8s.io/multicluster-runtime/providers/kind"
4140
)
@@ -62,10 +61,7 @@ func main() {
6261

6362
cl, err := mgr.GetCluster(ctx, req.ClusterName)
6463
if err != nil {
65-
if errors.Is(err, multicluster.ErrClusterNotFound) {
66-
log.Info("Cluster not found, might have been disengaged")
67-
return reconcile.Result{}, nil
68-
}
64+
return reconcile.Result{}, err
6965
}
7066

7167
cm := &corev1.ConfigMap{}

examples/namespace/main.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import (
4343

4444
mcbuilder "sigs.k8s.io/multicluster-runtime/pkg/builder"
4545
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"
46-
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
4746
mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile"
4847
"sigs.k8s.io/multicluster-runtime/providers/namespace"
4948
)
@@ -128,10 +127,6 @@ func main() {
128127

129128
cl, err := mgr.GetCluster(ctx, req.ClusterName)
130129
if err != nil {
131-
if errors.Is(err, multicluster.ErrClusterNotFound) {
132-
log.Info("Cluster not found, might have been disengaged")
133-
return reconcile.Result{}, nil
134-
}
135130
return reconcile.Result{}, err
136131
}
137132

pkg/builder/forked_controller.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ type TypedBuilder[request mcreconcile.ClusterAware[request]] struct {
7373
ctrlOptions controller.TypedOptions[request]
7474
name string
7575
newController func(name string, mgr mcmanager.Manager, options controller.TypedOptions[request]) (mccontroller.TypedController[request], error)
76+
77+
enableClusterNotFoundWrapper *bool
7678
}
7779

7880
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager.
@@ -260,6 +262,15 @@ func (blder *TypedBuilder[request]) WithLogConstructor(logConstructor func(*requ
260262
return blder
261263
}
262264

265+
// WithClusterNotFoundWrapper enables or disables a reconciler that is wrapped around the original reconciler
266+
// added to this builder. [reconcile.ClusterNotFoundWrapper] will stop reconcile results with [multicluster.ErrClusterNotFound]
267+
// as error from requeuing by marking them as successfully reconciled. This wrapper is enabled by default
268+
// and can be disabled with this builder method by setting it to false.
269+
func (blder *TypedBuilder[request]) WithClusterNotFoundWrapper(enabled bool) *TypedBuilder[request] {
270+
blder.enableClusterNotFoundWrapper = ptr.To(enabled)
271+
return blder
272+
}
273+
263274
// Named sets the name of the controller to the given name. The name shows up
264275
// in metrics, among other things, and thus should be a prometheus compatible name
265276
// (underscores and alphanumeric characters only).
@@ -450,6 +461,11 @@ func (blder *TypedBuilder[request]) doController(r reconcile.TypedReconciler[req
450461
ctrlOptions.Reconciler = r
451462
}
452463

464+
// the ClusterNotFound wrapper is enabled by default, but can be disabled with WithClusterNotFoundWrapper(false).
465+
if ptr.Deref(blder.enableClusterNotFoundWrapper, true) {
466+
ctrlOptions.Reconciler = mcreconcile.NewClusterNotFoundWrapper(ctrlOptions.Reconciler)
467+
}
468+
453469
// Retrieve the GVK from the object we're reconciling
454470
// to pre-populate logger information, and to optionally generate a default name.
455471
var gvk schema.GroupVersionKind

pkg/reconcile/wrapper.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package reconcile
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
24+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
25+
26+
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
27+
)
28+
29+
// ClusterNotFoundWrapper wraps an existing [reconcile.TypedReconciler] and ignores [multicluster.ErrClusterNotFound] errors.
30+
type ClusterNotFoundWrapper[request comparable] struct {
31+
wrapped reconcile.TypedReconciler[request]
32+
}
33+
34+
// NewClusterNotFoundWrapper creates a new [ClusterNotFoundWrapper].
35+
func NewClusterNotFoundWrapper[request comparable](w reconcile.TypedReconciler[request]) reconcile.TypedReconciler[request] {
36+
return &ClusterNotFoundWrapper[request]{wrapped: w}
37+
}
38+
39+
// Reconcile implements [reconcile.TypedReconciler].
40+
func (r *ClusterNotFoundWrapper[request]) Reconcile(ctx context.Context, req request) (reconcile.Result, error) {
41+
res, err := r.wrapped.Reconcile(ctx, req)
42+
43+
// if the error returned by the reconciler is ErrClusterNotFound, we return without requeuing.
44+
if errors.Is(err, multicluster.ErrClusterNotFound) {
45+
return reconcile.Result{}, nil
46+
}
47+
48+
return res, err
49+
}
50+
51+
// String returns a string representation of the wrapped reconciler.
52+
func (r *ClusterNotFoundWrapper[request]) String() string {
53+
return fmt.Sprintf("%v", r.wrapped)
54+
}

0 commit comments

Comments
 (0)