Skip to content

Commit f0d4357

Browse files
committed
Add watch-filter label
1 parent 0d4e430 commit f0d4357

15 files changed

+243
-32
lines changed

api/v1alpha4/common_types.go

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ const (
5353
// on the reconciled object.
5454
PausedAnnotation = "cluster.x-k8s.io/paused"
5555

56+
// WatchLabel is a label othat can be applied to any Cluster API object.
57+
//
58+
// Controllers which allow for selective reconciliation may check this label and proceed
59+
// with reconciliation of the object only if this label and a configured value is present.
60+
WatchLabel = "cluster.x-k8s.io/watch-filter"
61+
5662
// DeleteMachineAnnotation marks control plane and worker nodes that will be given priority for deletion
5763
// when KCP or a machineset scales down. This annotation is given top priority on all delete policies.
5864
DeleteMachineAnnotation = "cluster.x-k8s.io/delete-machine"

controllers/cluster_controller.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ const (
6363

6464
// ClusterReconciler reconciles a Cluster object
6565
type ClusterReconciler struct {
66-
Client client.Client
66+
Client client.Client
67+
WatchFilterValue string
6768

6869
restConfig *rest.Config
6970
recorder record.EventRecorder
@@ -78,7 +79,7 @@ func (r *ClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manag
7879
handler.EnqueueRequestsFromMapFunc(r.controlPlaneMachineToCluster),
7980
).
8081
WithOptions(options).
81-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
82+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
8283
Build(r)
8384

8485
if err != nil {

controllers/machine_controller.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ var (
7474

7575
// MachineReconciler reconciles a Machine object
7676
type MachineReconciler struct {
77-
Client client.Client
78-
Tracker *remote.ClusterCacheTracker
77+
Client client.Client
78+
Tracker *remote.ClusterCacheTracker
79+
WatchFilterValue string
7980

8081
controller controller.Controller
8182
restConfig *rest.Config
@@ -92,7 +93,7 @@ func (r *MachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manag
9293
controller, err := ctrl.NewControllerManagedBy(mgr).
9394
For(&clusterv1.Machine{}).
9495
WithOptions(options).
95-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
96+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
9697
Build(r)
9798
if err != nil {
9899
return errors.Wrap(err, "failed setting up with a controller manager")

controllers/machinedeployment_controller.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ var (
5353

5454
// MachineDeploymentReconciler reconciles a MachineDeployment object
5555
type MachineDeploymentReconciler struct {
56-
Client client.Client
56+
Client client.Client
57+
WatchFilterValue string
5758

5859
recorder record.EventRecorder
5960
restConfig *rest.Config
@@ -73,7 +74,7 @@ func (r *MachineDeploymentReconciler) SetupWithManager(ctx context.Context, mgr
7374
handler.EnqueueRequestsFromMapFunc(r.MachineSetToDeployments),
7475
).
7576
WithOptions(options).
76-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
77+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
7778
Build(r)
7879
if err != nil {
7980
return errors.Wrap(err, "failed setting up with a controller manager")

controllers/machinehealthcheck_controller.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ const (
6565

6666
// MachineHealthCheckReconciler reconciles a MachineHealthCheck object
6767
type MachineHealthCheckReconciler struct {
68-
Client client.Client
69-
Tracker *remote.ClusterCacheTracker
68+
Client client.Client
69+
Tracker *remote.ClusterCacheTracker
70+
WatchFilterValue string
7071

7172
controller controller.Controller
7273
recorder record.EventRecorder
@@ -80,7 +81,7 @@ func (r *MachineHealthCheckReconciler) SetupWithManager(ctx context.Context, mgr
8081
handler.EnqueueRequestsFromMapFunc(r.machineToMachineHealthCheck),
8182
).
8283
WithOptions(options).
83-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
84+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
8485
Build(r)
8586
if err != nil {
8687
return errors.Wrap(err, "failed setting up with a controller manager")

controllers/machineset_controller.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ var (
6767

6868
// MachineSetReconciler reconciles a MachineSet object
6969
type MachineSetReconciler struct {
70-
Client client.Client
71-
Tracker *remote.ClusterCacheTracker
70+
Client client.Client
71+
Tracker *remote.ClusterCacheTracker
72+
WatchFilterValue string
7273

7374
recorder record.EventRecorder
7475
restConfig *rest.Config
@@ -88,7 +89,7 @@ func (r *MachineSetReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
8889
handler.EnqueueRequestsFromMapFunc(r.MachineToMachineSets),
8990
).
9091
WithOptions(options).
91-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
92+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
9293
Build(r)
9394
if err != nil {
9495
return errors.Wrap(err, "failed setting up with a controller manager")

docs/book/src/developer/architecture/controllers/support-multiple-instances.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ in case the above limitations/extra complexity are acceptable for them.
2424
In order to make it possible for users to deploy multiple instances of the same provider:
2525

2626
- Providers MUST support the `--namespace` flag in their controllers.
27+
- Providers MUST support the `--watch-filter` flag in their controllers.
2728

2829
⚠️ Users selecting this deployment model, please be aware:
2930

docs/book/src/developer/providers/v1alpha3-to-v1alpha4.md

+25
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,28 @@ Specific changes related to this topic will be detailed in this document.
5858
## Change types with arrays of pointers to custom objects
5959

6060
The conversion-gen code from the `1.20.x` release onward generates incorrect conversion functions for types having arrays of pointers to custom objects. Change the existing types to contain objects instead of pointer references.
61+
62+
## Support the cluster.x-k8s.io/watch-filter label and watch-filter flag.
63+
64+
- A new label `cluster.x-k8s.io/watch-filter` provides the ability to filter the controllers to only reconcile objects with a specific label.
65+
- A new flag `watch-filter` enables users to specify the label value for the `cluster.x-k8s.io/watch-filter` label on controller boot.
66+
- The flag which enables users to set the flag value can be structured like this:
67+
```go
68+
fs.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controller watches to reconcile cluster-api objects. Label key is always %s. If unspecified, the controller watches for all cluster-api objects.", clusterv1.WatchLabel))
69+
```
70+
- The `ResourceNotPausedAndHasFilterLabel` predicate is a useful helper to check for the pause annotation and the filter label easily:
71+
```go
72+
c, err := ctrl.NewControllerManagedBy(mgr).
73+
For(&clusterv1.MachineSet{}).
74+
Owns(&clusterv1.Machine{}).
75+
Watches(
76+
&source.Kind{Type: &clusterv1.Machine{}},
77+
handler.EnqueueRequestsFromMapFunc(r.MachineToMachineSets),
78+
).
79+
WithOptions(options).
80+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
81+
Build(r)
82+
if err != nil {
83+
return errors.Wrap(err, "failed setting up with a controller manager")
84+
}
85+
```

exp/addons/controllers/clusterresourceset_controller.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ var (
6161

6262
// ClusterResourceSetReconciler reconciles a ClusterResourceSet object
6363
type ClusterResourceSetReconciler struct {
64-
Client client.Client
65-
Tracker *remote.ClusterCacheTracker
64+
Client client.Client
65+
Tracker *remote.ClusterCacheTracker
66+
WatchFilterValue string
6667
}
6768

6869
func (r *ClusterResourceSetReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
@@ -89,7 +90,7 @@ func (r *ClusterResourceSetReconciler) SetupWithManager(ctx context.Context, mgr
8990
),
9091
).
9192
WithOptions(options).
92-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
93+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
9394
Complete(r)
9495

9596
if err != nil {

exp/addons/controllers/clusterresourcesetbinding_controller.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ import (
3737

3838
// ClusterResourceSetBindingReconciler reconciles a ClusterResourceSetBinding object
3939
type ClusterResourceSetBindingReconciler struct {
40-
Client client.Client
40+
Client client.Client
41+
WatchFilterValue string
4142
}
4243

4344
func (r *ClusterResourceSetBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
@@ -48,7 +49,7 @@ func (r *ClusterResourceSetBindingReconciler) SetupWithManager(ctx context.Conte
4849
handler.EnqueueRequestsFromMapFunc(r.clusterToClusterResourceSetBinding),
4950
).
5051
WithOptions(options).
51-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
52+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
5253
Build(r)
5354
if err != nil {
5455
return errors.Wrap(err, "failed setting up with a controller manager")

exp/controllers/machinepool_controller.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ const (
5858

5959
// MachinePoolReconciler reconciles a MachinePool object
6060
type MachinePoolReconciler struct {
61-
Client client.Client
61+
Client client.Client
62+
WatchFilterValue string
6263

6364
config *rest.Config
6465
controller controller.Controller
@@ -75,7 +76,7 @@ func (r *MachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M
7576
c, err := ctrl.NewControllerManagedBy(mgr).
7677
For(&expv1.MachinePool{}).
7778
WithOptions(options).
78-
WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))).
79+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
7980
Build(r)
8081
if err != nil {
8182
return errors.Wrap(err, "failed setting up with a controller manager")

main.go

+25-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818
import (
1919
"context"
2020
"flag"
21+
"fmt"
2122
"math/rand"
2223
"net/http"
2324
_ "net/http/pprof"
@@ -58,6 +59,7 @@ var (
5859
leaderElectionRenewDeadline time.Duration
5960
leaderElectionRetryPeriod time.Duration
6061
watchNamespace string
62+
watchFilterValue string
6163
profilerAddress string
6264
clusterConcurrency int
6365
machineConcurrency int
@@ -102,6 +104,9 @@ func InitFlags(fs *pflag.FlagSet) {
102104
fs.StringVar(&watchNamespace, "namespace", "",
103105
"Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.")
104106

107+
fs.StringVar(&watchFilterValue, "watch-filter", "",
108+
fmt.Sprintf("Label value that the controller watches to reconcile cluster-api objects. Label key is always %s. If unspecified, the controller watches for all cluster-api objects.", clusterv1.WatchLabel))
109+
105110
fs.StringVar(&profilerAddress, "profiler-address", "",
106111
"Bind address to expose the pprof profiler (e.g. localhost:6060)")
107112

@@ -228,35 +233,40 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager) {
228233
}
229234

230235
if err := (&controllers.ClusterReconciler{
231-
Client: mgr.GetClient(),
236+
Client: mgr.GetClient(),
237+
WatchFilterValue: watchFilterValue,
232238
}).SetupWithManager(ctx, mgr, concurrency(clusterConcurrency)); err != nil {
233239
setupLog.Error(err, "unable to create controller", "controller", "Cluster")
234240
os.Exit(1)
235241
}
236242
if err := (&controllers.MachineReconciler{
237-
Client: mgr.GetClient(),
238-
Tracker: tracker,
243+
Client: mgr.GetClient(),
244+
Tracker: tracker,
245+
WatchFilterValue: watchFilterValue,
239246
}).SetupWithManager(ctx, mgr, concurrency(machineConcurrency)); err != nil {
240247
setupLog.Error(err, "unable to create controller", "controller", "Machine")
241248
os.Exit(1)
242249
}
243250
if err := (&controllers.MachineSetReconciler{
244-
Client: mgr.GetClient(),
245-
Tracker: tracker,
251+
Client: mgr.GetClient(),
252+
Tracker: tracker,
253+
WatchFilterValue: watchFilterValue,
246254
}).SetupWithManager(ctx, mgr, concurrency(machineSetConcurrency)); err != nil {
247255
setupLog.Error(err, "unable to create controller", "controller", "MachineSet")
248256
os.Exit(1)
249257
}
250258
if err := (&controllers.MachineDeploymentReconciler{
251-
Client: mgr.GetClient(),
259+
Client: mgr.GetClient(),
260+
WatchFilterValue: watchFilterValue,
252261
}).SetupWithManager(ctx, mgr, concurrency(machineDeploymentConcurrency)); err != nil {
253262
setupLog.Error(err, "unable to create controller", "controller", "MachineDeployment")
254263
os.Exit(1)
255264
}
256265

257266
if feature.Gates.Enabled(feature.MachinePool) {
258267
if err := (&expcontrollers.MachinePoolReconciler{
259-
Client: mgr.GetClient(),
268+
Client: mgr.GetClient(),
269+
WatchFilterValue: watchFilterValue,
260270
}).SetupWithManager(ctx, mgr, concurrency(machinePoolConcurrency)); err != nil {
261271
setupLog.Error(err, "unable to create controller", "controller", "MachinePool")
262272
os.Exit(1)
@@ -265,23 +275,26 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager) {
265275

266276
if feature.Gates.Enabled(feature.ClusterResourceSet) {
267277
if err := (&addonscontrollers.ClusterResourceSetReconciler{
268-
Client: mgr.GetClient(),
269-
Tracker: tracker,
278+
Client: mgr.GetClient(),
279+
Tracker: tracker,
280+
WatchFilterValue: watchFilterValue,
270281
}).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil {
271282
setupLog.Error(err, "unable to create controller", "controller", "ClusterResourceSet")
272283
os.Exit(1)
273284
}
274285
if err := (&addonscontrollers.ClusterResourceSetBindingReconciler{
275-
Client: mgr.GetClient(),
286+
Client: mgr.GetClient(),
287+
WatchFilterValue: watchFilterValue,
276288
}).SetupWithManager(ctx, mgr, concurrency(clusterResourceSetConcurrency)); err != nil {
277289
setupLog.Error(err, "unable to create controller", "controller", "ClusterResourceSetBinding")
278290
os.Exit(1)
279291
}
280292
}
281293

282294
if err := (&controllers.MachineHealthCheckReconciler{
283-
Client: mgr.GetClient(),
284-
Tracker: tracker,
295+
Client: mgr.GetClient(),
296+
Tracker: tracker,
297+
WatchFilterValue: watchFilterValue,
285298
}).SetupWithManager(ctx, mgr, concurrency(machineHealthCheckConcurrency)); err != nil {
286299
setupLog.Error(err, "unable to create controller", "controller", "MachineHealthCheck")
287300
os.Exit(1)

util/labels/helpers.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2021 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 labels
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
22+
)
23+
24+
// HasWatchLabel returns true if the object has a label with the WatchLabel key matching the given value.
25+
func HasWatchLabel(o metav1.Object, labelValue string) bool {
26+
val, ok := o.GetLabels()[clusterv1.WatchLabel]
27+
if !ok {
28+
return false
29+
}
30+
return val == labelValue
31+
}

0 commit comments

Comments
 (0)