Skip to content

Commit 9662284

Browse files
Add Device watch to controllers for Paused field changes
Add watches on Device resources to all controllers that use annotations.IsPaused to trigger reconciliation when a Device's Paused spec field changes. This ensures resources are re-reconciled when their parent Device is paused or unpaused. Each controller now: - Watches Device create/delete events (default behavior) - Watches Device update events only when Spec.Paused changes - Ignores Generic events for Device watches - Uses deviceTo* map functions to find associated resources by label
1 parent 218f2ca commit 9662284

24 files changed

Lines changed: 1252 additions & 15 deletions

internal/controller/cisco/nx/bordergateway_controller.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,23 @@ func (r *BorderGatewayReconciler) SetupWithManager(ctx context.Context, mgr ctrl
287287
},
288288
}),
289289
).
290+
// Watches enqueues BorderGateways for updates in referenced Device resources.
291+
// Triggers on create, delete, and update events when the Paused spec field changes.
292+
Watches(
293+
&v1alpha1.Device{},
294+
handler.EnqueueRequestsFromMapFunc(r.deviceToBorderGateways),
295+
builder.WithPredicates(predicate.Funcs{
296+
UpdateFunc: func(e event.UpdateEvent) bool {
297+
oldDevice := e.ObjectOld.(*v1alpha1.Device)
298+
newDevice := e.ObjectNew.(*v1alpha1.Device)
299+
// Only trigger when Paused spec field changes.
300+
return !equality.Semantic.DeepEqual(oldDevice.Spec.Paused, newDevice.Spec.Paused)
301+
},
302+
GenericFunc: func(e event.GenericEvent) bool {
303+
return false
304+
},
305+
}),
306+
).
290307
Complete(r)
291308
}
292309

@@ -593,3 +610,36 @@ func (r *BorderGatewayReconciler) bgpPeerToBorderGateway(ctx context.Context, ob
593610

594611
return requests
595612
}
613+
614+
// deviceToBorderGateways is a [handler.MapFunc] to be used to enqueue requests for reconciliation
615+
// for BorderGateways when their referenced Device's Paused spec field changes.
616+
func (r *BorderGatewayReconciler) deviceToBorderGateways(ctx context.Context, obj client.Object) []ctrl.Request {
617+
device, ok := obj.(*v1alpha1.Device)
618+
if !ok {
619+
panic(fmt.Sprintf("Expected a Device but got a %T", obj))
620+
}
621+
622+
log := ctrl.LoggerFrom(ctx, "Device", klog.KObj(device))
623+
624+
list := new(nxv1alpha1.BorderGatewayList)
625+
if err := r.List(ctx, list,
626+
client.InNamespace(device.Namespace),
627+
client.MatchingLabels{v1alpha1.DeviceLabel: device.Name},
628+
); err != nil {
629+
log.Error(err, "Failed to list BorderGateways")
630+
return nil
631+
}
632+
633+
requests := make([]ctrl.Request, 0, len(list.Items))
634+
for _, i := range list.Items {
635+
log.Info("Enqueuing BorderGateway for reconciliation", "BorderGateway", klog.KObj(&i))
636+
requests = append(requests, ctrl.Request{
637+
NamespacedName: client.ObjectKey{
638+
Name: i.Name,
639+
Namespace: i.Namespace,
640+
},
641+
})
642+
}
643+
644+
return requests
645+
}

internal/controller/cisco/nx/system_controller.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ import (
1616
"k8s.io/apimachinery/pkg/runtime"
1717
kerrors "k8s.io/apimachinery/pkg/util/errors"
1818
"k8s.io/client-go/tools/record"
19+
"k8s.io/klog/v2"
1920
ctrl "sigs.k8s.io/controller-runtime"
21+
"sigs.k8s.io/controller-runtime/pkg/builder"
2022
"sigs.k8s.io/controller-runtime/pkg/client"
2123
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
24+
"sigs.k8s.io/controller-runtime/pkg/event"
25+
"sigs.k8s.io/controller-runtime/pkg/handler"
2226
"sigs.k8s.io/controller-runtime/pkg/predicate"
2327

2428
nxv1alpha1 "github.com/ironcore-dev/network-operator/api/cisco/nx/v1alpha1"
@@ -203,6 +207,23 @@ func (r *SystemReconciler) SetupWithManager(mgr ctrl.Manager) error {
203207
For(&nxv1alpha1.System{}).
204208
Named("system").
205209
WithEventFilter(filter).
210+
// Watches enqueues Systems for updates in referenced Device resources.
211+
// Triggers on create, delete, and update events when the Paused spec field changes.
212+
Watches(
213+
&v1alpha1.Device{},
214+
handler.EnqueueRequestsFromMapFunc(r.deviceToSystems),
215+
builder.WithPredicates(predicate.Funcs{
216+
UpdateFunc: func(e event.UpdateEvent) bool {
217+
oldDevice := e.ObjectOld.(*v1alpha1.Device)
218+
newDevice := e.ObjectNew.(*v1alpha1.Device)
219+
// Only trigger when Paused spec field changes.
220+
return !equality.Semantic.DeepEqual(oldDevice.Spec.Paused, newDevice.Spec.Paused)
221+
},
222+
GenericFunc: func(e event.GenericEvent) bool {
223+
return false
224+
},
225+
}),
226+
).
206227
Complete(r)
207228
}
208229

@@ -260,3 +281,36 @@ func (r *SystemReconciler) finalize(ctx context.Context, s *systemScope) (reterr
260281

261282
return s.Provider.ResetSystemSettings(ctx)
262283
}
284+
285+
// deviceToSystems is a [handler.MapFunc] to be used to enqueue requests for reconciliation
286+
// for Systems when their referenced Device's Paused spec field changes.
287+
func (r *SystemReconciler) deviceToSystems(ctx context.Context, obj client.Object) []ctrl.Request {
288+
device, ok := obj.(*v1alpha1.Device)
289+
if !ok {
290+
panic(fmt.Sprintf("Expected a Device but got a %T", obj))
291+
}
292+
293+
log := ctrl.LoggerFrom(ctx, "Device", klog.KObj(device))
294+
295+
list := new(nxv1alpha1.SystemList)
296+
if err := r.List(ctx, list,
297+
client.InNamespace(device.Namespace),
298+
client.MatchingLabels{v1alpha1.DeviceLabel: device.Name},
299+
); err != nil {
300+
log.Error(err, "Failed to list Systems")
301+
return nil
302+
}
303+
304+
requests := make([]ctrl.Request, 0, len(list.Items))
305+
for _, i := range list.Items {
306+
log.Info("Enqueuing System for reconciliation", "System", klog.KObj(&i))
307+
requests = append(requests, ctrl.Request{
308+
NamespacedName: client.ObjectKey{
309+
Name: i.Name,
310+
Namespace: i.Namespace,
311+
},
312+
})
313+
}
314+
315+
return requests
316+
}

internal/controller/cisco/nx/vpcdomain_controller.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"k8s.io/apimachinery/pkg/runtime"
1818
kerrors "k8s.io/apimachinery/pkg/util/errors"
1919
"k8s.io/client-go/tools/record"
20+
"k8s.io/klog/v2"
2021
ctrl "sigs.k8s.io/controller-runtime"
2122
"sigs.k8s.io/controller-runtime/pkg/builder"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -457,6 +458,23 @@ func (r *VPCDomainReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
457458
},
458459
}),
459460
).
461+
// Watches enqueues VPCDomains for updates in referenced Device resources.
462+
// Triggers on create, delete, and update events when the Paused spec field changes.
463+
Watches(
464+
&corev1.Device{},
465+
handler.EnqueueRequestsFromMapFunc(r.deviceToVPCDomains),
466+
builder.WithPredicates(predicate.Funcs{
467+
UpdateFunc: func(e event.UpdateEvent) bool {
468+
oldDevice := e.ObjectOld.(*corev1.Device)
469+
newDevice := e.ObjectNew.(*corev1.Device)
470+
// Only trigger when Paused spec field changes.
471+
return !equality.Semantic.DeepEqual(oldDevice.Spec.Paused, newDevice.Spec.Paused)
472+
},
473+
GenericFunc: func(e event.GenericEvent) bool {
474+
return false
475+
},
476+
}),
477+
).
460478
Complete(r)
461479
}
462480

@@ -499,6 +517,39 @@ func (r *VPCDomainReconciler) finalize(ctx context.Context, s *vpcdomainScope) (
499517
return s.Provider.DeleteVPCDomain(ctx)
500518
}
501519

520+
// deviceToVPCDomains is a [handler.MapFunc] to be used to enqueue requests for reconciliation
521+
// for VPCDomains when their referenced Device's Paused spec field changes.
522+
func (r *VPCDomainReconciler) deviceToVPCDomains(ctx context.Context, obj client.Object) []ctrl.Request {
523+
device, ok := obj.(*corev1.Device)
524+
if !ok {
525+
panic(fmt.Sprintf("Expected a Device but got a %T", obj))
526+
}
527+
528+
log := ctrl.LoggerFrom(ctx, "Device", klog.KObj(device))
529+
530+
list := new(nxv1.VPCDomainList)
531+
if err := r.List(ctx, list,
532+
client.InNamespace(device.Namespace),
533+
client.MatchingLabels{corev1.DeviceLabel: device.Name},
534+
); err != nil {
535+
log.Error(err, "Failed to list VPCDomains")
536+
return nil
537+
}
538+
539+
requests := make([]ctrl.Request, 0, len(list.Items))
540+
for _, i := range list.Items {
541+
log.Info("Enqueuing VPCDomain for reconciliation", "VPCDomain", klog.KObj(&i))
542+
requests = append(requests, ctrl.Request{
543+
NamespacedName: client.ObjectKey{
544+
Name: i.Name,
545+
Namespace: i.Namespace,
546+
},
547+
})
548+
}
549+
550+
return requests
551+
}
552+
502553
func conditionChanged(oldConds, newConds []metav1.Condition, t string) bool {
503554
o := meta.FindStatusCondition(oldConds, t)
504555
n := meta.FindStatusCondition(newConds, t)

internal/controller/core/acl_controller.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sigs.k8s.io/controller-runtime/pkg/builder"
2424
"sigs.k8s.io/controller-runtime/pkg/client"
2525
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
26+
"sigs.k8s.io/controller-runtime/pkg/event"
2627
"sigs.k8s.io/controller-runtime/pkg/handler"
2728
"sigs.k8s.io/controller-runtime/pkg/predicate"
2829
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -229,7 +230,25 @@ func (r *AccessControlListReconciler) SetupWithManager(mgr ctrl.Manager) error {
229230
)
230231
}
231232

232-
return bldr.Complete(r)
233+
return bldr.
234+
// Watches enqueues AccessControlLists for updates in referenced Device resources.
235+
// Triggers on create, delete, and update events when the Paused spec field changes.
236+
Watches(
237+
&v1alpha1.Device{},
238+
handler.EnqueueRequestsFromMapFunc(r.deviceToAccessControlLists),
239+
builder.WithPredicates(predicate.Funcs{
240+
UpdateFunc: func(e event.UpdateEvent) bool {
241+
oldDevice := e.ObjectOld.(*v1alpha1.Device)
242+
newDevice := e.ObjectNew.(*v1alpha1.Device)
243+
// Only trigger when Paused spec field changes.
244+
return !equality.Semantic.DeepEqual(oldDevice.Spec.Paused, newDevice.Spec.Paused)
245+
},
246+
GenericFunc: func(e event.GenericEvent) bool {
247+
return false
248+
},
249+
}),
250+
).
251+
Complete(r)
233252
}
234253

235254
// scope holds the different objects that are read and used during the reconcile.
@@ -300,6 +319,39 @@ func (r *AccessControlListReconciler) finalize(ctx context.Context, s *aclScope)
300319
})
301320
}
302321

322+
// deviceToAccessControlLists is a [handler.MapFunc] to be used to enqueue requests for reconciliation
323+
// for AccessControlLists when their referenced Device's Paused spec field changes.
324+
func (r *AccessControlListReconciler) deviceToAccessControlLists(ctx context.Context, obj client.Object) []ctrl.Request {
325+
device, ok := obj.(*v1alpha1.Device)
326+
if !ok {
327+
panic(fmt.Sprintf("Expected a Device but got a %T", obj))
328+
}
329+
330+
log := ctrl.LoggerFrom(ctx, "Device", klog.KObj(device))
331+
332+
list := new(v1alpha1.AccessControlListList)
333+
if err := r.List(ctx, list,
334+
client.InNamespace(device.Namespace),
335+
client.MatchingLabels{v1alpha1.DeviceLabel: device.Name},
336+
); err != nil {
337+
log.Error(err, "Failed to list AccessControlLists")
338+
return nil
339+
}
340+
341+
requests := make([]ctrl.Request, 0, len(list.Items))
342+
for _, i := range list.Items {
343+
log.Info("Enqueuing AccessControlList for reconciliation", "AccessControlList", klog.KObj(&i))
344+
requests = append(requests, ctrl.Request{
345+
NamespacedName: client.ObjectKey{
346+
Name: i.Name,
347+
Namespace: i.Namespace,
348+
},
349+
})
350+
}
351+
352+
return requests
353+
}
354+
303355
// accessControlListsForProviderConfig is a [handler.MapFunc] to be used to enqueue requests for reconciliation
304356
// for a AccessControlList to update when one of its referenced provider configurations gets updated.
305357
func (r *AccessControlListReconciler) accessControlListsForProviderConfig(ctx context.Context, obj client.Object) []reconcile.Request {

internal/controller/core/banner_controller.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"sigs.k8s.io/controller-runtime/pkg/builder"
2525
"sigs.k8s.io/controller-runtime/pkg/client"
2626
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
27+
"sigs.k8s.io/controller-runtime/pkg/event"
2728
"sigs.k8s.io/controller-runtime/pkg/handler"
2829
"sigs.k8s.io/controller-runtime/pkg/predicate"
2930
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -245,7 +246,25 @@ func (r *BannerReconciler) SetupWithManager(mgr ctrl.Manager) error {
245246
)
246247
}
247248

248-
return bldr.Complete(r)
249+
return bldr.
250+
// Watches enqueues Banners for updates in referenced Device resources.
251+
// Triggers on create, delete, and update events when the Paused spec field changes.
252+
Watches(
253+
&v1alpha1.Device{},
254+
handler.EnqueueRequestsFromMapFunc(r.deviceToBanners),
255+
builder.WithPredicates(predicate.Funcs{
256+
UpdateFunc: func(e event.UpdateEvent) bool {
257+
oldDevice := e.ObjectOld.(*v1alpha1.Device)
258+
newDevice := e.ObjectNew.(*v1alpha1.Device)
259+
// Only trigger when Paused spec field changes.
260+
return !equality.Semantic.DeepEqual(oldDevice.Spec.Paused, newDevice.Spec.Paused)
261+
},
262+
GenericFunc: func(e event.GenericEvent) bool {
263+
return false
264+
},
265+
}),
266+
).
267+
Complete(r)
249268
}
250269

251270
// scope holds the different objects that are read and used during the reconcile.
@@ -315,6 +334,39 @@ func (r *BannerReconciler) finalize(ctx context.Context, s *bannerScope) (reterr
315334
})
316335
}
317336

337+
// deviceToBanners is a [handler.MapFunc] to be used to enqueue requests for reconciliation
338+
// for Banners when their referenced Device's Paused spec field changes.
339+
func (r *BannerReconciler) deviceToBanners(ctx context.Context, obj client.Object) []ctrl.Request {
340+
device, ok := obj.(*v1alpha1.Device)
341+
if !ok {
342+
panic(fmt.Sprintf("Expected a Device but got a %T", obj))
343+
}
344+
345+
log := ctrl.LoggerFrom(ctx, "Device", klog.KObj(device))
346+
347+
list := new(v1alpha1.BannerList)
348+
if err := r.List(ctx, list,
349+
client.InNamespace(device.Namespace),
350+
client.MatchingLabels{v1alpha1.DeviceLabel: device.Name},
351+
); err != nil {
352+
log.Error(err, "Failed to list Banners")
353+
return nil
354+
}
355+
356+
requests := make([]ctrl.Request, 0, len(list.Items))
357+
for _, i := range list.Items {
358+
log.Info("Enqueuing Banner for reconciliation", "Banner", klog.KObj(&i))
359+
requests = append(requests, ctrl.Request{
360+
NamespacedName: client.ObjectKey{
361+
Name: i.Name,
362+
Namespace: i.Namespace,
363+
},
364+
})
365+
}
366+
367+
return requests
368+
}
369+
318370
// secretToBanner is a [handler.MapFunc] to be used to enqueue requests for reconciliation
319371
// for a Banner to update when one of its referenced Secrets gets updated.
320372
func (r *BannerReconciler) secretToBanner(ctx context.Context, obj client.Object) []ctrl.Request {

0 commit comments

Comments
 (0)