@@ -19,22 +19,28 @@ package cluster
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "reflect"
22
23
"time"
23
24
24
25
"github.com/go-logr/logr"
25
26
"github.com/pkg/errors"
26
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
+ "k8s.io/apimachinery/pkg/runtime"
27
29
"k8s.io/apimachinery/pkg/types"
28
30
kerrors "k8s.io/apimachinery/pkg/util/errors"
29
31
"k8s.io/client-go/tools/record"
32
+ "k8s.io/klog/v2"
30
33
"k8s.io/utils/ptr"
31
34
ctrl "sigs.k8s.io/controller-runtime"
32
35
"sigs.k8s.io/controller-runtime/pkg/builder"
33
36
"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
34
37
"sigs.k8s.io/controller-runtime/pkg/client"
38
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
35
39
"sigs.k8s.io/controller-runtime/pkg/controller"
40
+ "sigs.k8s.io/controller-runtime/pkg/event"
36
41
"sigs.k8s.io/controller-runtime/pkg/handler"
37
42
"sigs.k8s.io/controller-runtime/pkg/log"
43
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
38
44
"sigs.k8s.io/controller-runtime/pkg/reconcile"
39
45
40
46
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -103,8 +109,11 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt
103
109
predicateLog := ctrl .LoggerFrom (ctx ).WithValues ("controller" , "topology/cluster" )
104
110
c , err := ctrl .NewControllerManagedBy (mgr ).
105
111
For (& clusterv1.Cluster {}, builder .WithPredicates (
106
- // Only reconcile Cluster with topology.
107
- predicates .ClusterHasTopology (mgr .GetScheme (), predicateLog ),
112
+ // Only reconcile Cluster with topology and with changes relevant for this controller.
113
+ predicates .All (mgr .GetScheme (), predicateLog ,
114
+ predicates .ClusterHasTopology (mgr .GetScheme (), predicateLog ),
115
+ clusterChangeIsRelevant (mgr .GetScheme (), predicateLog ),
116
+ ),
108
117
)).
109
118
Named ("topology/cluster" ).
110
119
WatchesRawSource (r .ClusterCache .GetClusterSource ("topology/cluster" , func (_ context.Context , o client.Object ) []ctrl.Request {
@@ -118,16 +127,17 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt
118
127
Watches (
119
128
& clusterv1.MachineDeployment {},
120
129
handler .EnqueueRequestsFromMapFunc (r .machineDeploymentToCluster ),
121
- // Only trigger Cluster reconciliation if the MachineDeployment is topology owned.
130
+ // Only trigger Cluster reconciliation if the MachineDeployment is topology owned, the resource is changed, and the change is relevant .
122
131
builder .WithPredicates (predicates .All (mgr .GetScheme (), predicateLog ,
123
132
predicates .ResourceIsChanged (mgr .GetScheme (), predicateLog ),
124
133
predicates .ResourceIsTopologyOwned (mgr .GetScheme (), predicateLog ),
134
+ machineDeploymentChangeIsRelevant (mgr .GetScheme (), predicateLog ),
125
135
)),
126
136
).
127
137
Watches (
128
138
& expv1.MachinePool {},
129
139
handler .EnqueueRequestsFromMapFunc (r .machinePoolToCluster ),
130
- // Only trigger Cluster reconciliation if the MachinePool is topology owned.
140
+ // Only trigger Cluster reconciliation if the MachinePool is topology owned, the resource is changed .
131
141
builder .WithPredicates (predicates .All (mgr .GetScheme (), predicateLog ,
132
142
predicates .ResourceIsChanged (mgr .GetScheme (), predicateLog ),
133
143
predicates .ResourceIsTopologyOwned (mgr .GetScheme (), predicateLog ),
@@ -155,6 +165,105 @@ func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opt
155
165
return nil
156
166
}
157
167
168
+ func clusterChangeIsRelevant (scheme * runtime.Scheme , logger logr.Logger ) predicate.Funcs {
169
+ dropNotRelevant := func (cluster * clusterv1.Cluster ) * clusterv1.Cluster {
170
+ c := cluster .DeepCopy ()
171
+ // Drop metadata fields which are impacted by not relevant changes.
172
+ c .ObjectMeta .ManagedFields = nil
173
+ c .ObjectMeta .ResourceVersion = ""
174
+
175
+ // Drop changes on v1beta2 conditions; when v1beta2 conditions will be moved top level, we will review this
176
+ // selectively drop changes not relevant for this controller.
177
+ c .Status .V1Beta2 = nil
178
+ return c
179
+ }
180
+
181
+ return predicate.Funcs {
182
+ UpdateFunc : func (e event.UpdateEvent ) bool {
183
+ log := logger .WithValues ("predicate" , "ClusterChangeIsRelevant" , "eventType" , "update" )
184
+ if gvk , err := apiutil .GVKForObject (e .ObjectOld , scheme ); err == nil {
185
+ log = log .WithValues (gvk .Kind , klog .KObj (e .ObjectOld ))
186
+ }
187
+
188
+ if e .ObjectOld .GetResourceVersion () == e .ObjectNew .GetResourceVersion () {
189
+ log .V (6 ).Info ("Cluster resync event, allowing further processing" )
190
+ return true
191
+ }
192
+
193
+ oldObj , ok := e .ObjectOld .(* clusterv1.Cluster )
194
+ if ! ok {
195
+ log .V (4 ).Info ("Expected Cluster" , "type" , fmt .Sprintf ("%T" , e .ObjectOld ))
196
+ return false
197
+ }
198
+ oldObj = dropNotRelevant (oldObj )
199
+
200
+ newObj := e .ObjectNew .(* clusterv1.Cluster )
201
+ if ! ok {
202
+ log .V (4 ).Info ("Expected Cluster" , "type" , fmt .Sprintf ("%T" , e .ObjectNew ))
203
+ return false
204
+ }
205
+ newObj = dropNotRelevant (newObj )
206
+
207
+ if reflect .DeepEqual (oldObj , newObj ) {
208
+ log .V (6 ).Info ("Cluster does not have relevant changes, blocking further processing" )
209
+ return false
210
+ }
211
+ log .V (6 ).Info ("Cluster has relevant changes, allowing further processing" )
212
+ return true
213
+ },
214
+ CreateFunc : func (event.CreateEvent ) bool { return true },
215
+ DeleteFunc : func (event.DeleteEvent ) bool { return true },
216
+ GenericFunc : func (event.GenericEvent ) bool { return true },
217
+ }
218
+ }
219
+
220
+ func machineDeploymentChangeIsRelevant (scheme * runtime.Scheme , logger logr.Logger ) predicate.Funcs {
221
+ dropNotRelevant := func (machineDeployment * clusterv1.MachineDeployment ) * clusterv1.MachineDeployment {
222
+ md := machineDeployment .DeepCopy ()
223
+ // Drop metadata fields which are impacted by not relevant changes.
224
+ md .ObjectMeta .ManagedFields = nil
225
+ md .ObjectMeta .ResourceVersion = ""
226
+
227
+ // Drop changes on v1beta2 conditions; when v1beta2 conditions will be moved top level, we will review this
228
+ // selectively drop changes not relevant for this controller.
229
+ md .Status .V1Beta2 = nil
230
+ return md
231
+ }
232
+
233
+ return predicate.Funcs {
234
+ UpdateFunc : func (e event.UpdateEvent ) bool {
235
+ log := logger .WithValues ("predicate" , "MachineDeploymentChangeIsRelevant" , "eventType" , "update" )
236
+ if gvk , err := apiutil .GVKForObject (e .ObjectOld , scheme ); err == nil {
237
+ log = log .WithValues (gvk .Kind , klog .KObj (e .ObjectOld ))
238
+ }
239
+
240
+ oldObj , ok := e .ObjectOld .(* clusterv1.MachineDeployment )
241
+ if ! ok {
242
+ log .V (4 ).Info ("Expected MachineDeployment" , "type" , fmt .Sprintf ("%T" , e .ObjectOld ))
243
+ return false
244
+ }
245
+ oldObj = dropNotRelevant (oldObj )
246
+
247
+ newObj := e .ObjectNew .(* clusterv1.MachineDeployment )
248
+ if ! ok {
249
+ log .V (4 ).Info ("Expected MachineDeployment" , "type" , fmt .Sprintf ("%T" , e .ObjectNew ))
250
+ return false
251
+ }
252
+ newObj = dropNotRelevant (newObj )
253
+
254
+ if reflect .DeepEqual (oldObj , newObj ) {
255
+ log .V (6 ).Info ("MachineDeployment does not have relevant changes, blocking further processing" )
256
+ return false
257
+ }
258
+ log .V (6 ).Info ("MachineDeployment has relevant changes, allowing further processing" )
259
+ return true
260
+ },
261
+ CreateFunc : func (event.CreateEvent ) bool { return true },
262
+ DeleteFunc : func (event.DeleteEvent ) bool { return true },
263
+ GenericFunc : func (event.GenericEvent ) bool { return true },
264
+ }
265
+ }
266
+
158
267
// SetupForDryRun prepares the Reconciler for a dry run execution.
159
268
func (r * Reconciler ) SetupForDryRun (recorder record.EventRecorder ) {
160
269
r .desiredStateGenerator = desiredstate .NewGenerator (r .Client , r .ClusterCache , r .RuntimeClient )
0 commit comments