From 2a3745effaed2d825024566f8f876a3f25d8127f Mon Sep 17 00:00:00 2001 From: Karthik Bhat Date: Mon, 4 Nov 2024 15:01:28 +0530 Subject: [PATCH] Add support for pausing cluster reconcilation (#1769) --- controllers/ibmpowervscluster_controller.go | 19 ++++- controllers/ibmpowervsmachine_controller.go | 84 +++++++++++++++++++-- main.go | 2 +- 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/controllers/ibmpowervscluster_controller.go b/controllers/ibmpowervscluster_controller.go index 6021492e3..74d035151 100644 --- a/controllers/ibmpowervscluster_controller.go +++ b/controllers/ibmpowervscluster_controller.go @@ -36,7 +36,9 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util" @@ -562,8 +564,21 @@ func (c clusterDescendants) filterOwnedDescendants(cluster *infrav1beta2.IBMPowe // SetupWithManager creates a new IBMPowerVSCluster controller for a manager. func (r *IBMPowerVSClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). + controller, err := ctrl.NewControllerManagedBy(mgr). For(&infrav1beta2.IBMPowerVSCluster{}). WithEventFilter(predicates.ResourceIsNotExternallyManaged(ctrl.LoggerFrom(ctx))). - Complete(r) + WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). + Build(r) + if err != nil { + return errors.Wrap(err, "error creating controller") + } + // Add a watch on capiv1beta1.Cluster object for unpause notifications. + if err = controller.Watch( + source.Kind[client.Object](mgr.GetCache(), &capiv1beta1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(util.ClusterToInfrastructureMapFunc(ctx, infrav1beta2.GroupVersion.WithKind("IBMPowerVSCluster"), mgr.GetClient(), &infrav1beta2.IBMPowerVSCluster{})), + predicates.ClusterUnpaused(ctrl.LoggerFrom(ctx)), + )); err != nil { + return errors.Wrap(err, "failed adding a watch for ready clusters") + } + return nil } diff --git a/controllers/ibmpowervsmachine_controller.go b/controllers/ibmpowervsmachine_controller.go index 41c32c94b..24beb6a51 100644 --- a/controllers/ibmpowervsmachine_controller.go +++ b/controllers/ibmpowervsmachine_controller.go @@ -22,6 +22,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/pkg/errors" "github.com/IBM-Cloud/power-go-client/power/models" @@ -34,11 +35,14 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/source" capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" capierrors "sigs.k8s.io/cluster-api/errors" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/conditions" + "sigs.k8s.io/cluster-api/util/predicates" infrav1beta2 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2" "sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope" @@ -156,13 +160,6 @@ func (r *IBMPowerVSMachineReconciler) Reconcile(ctx context.Context, req ctrl.Re return r.reconcileNormal(machineScope) } -// SetupWithManager creates a new IBMPowerVSMachine controller for a manager. -func (r *IBMPowerVSMachineReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&infrav1beta2.IBMPowerVSMachine{}). - Complete(r) -} - func (r *IBMPowerVSMachineReconciler) reconcileDelete(scope *scope.PowerVSMachineScope) (_ ctrl.Result, reterr error) { scope.Info("Handling deleted IBMPowerVSMachine") @@ -311,3 +308,76 @@ func (r *IBMPowerVSMachineReconciler) reconcileNormal(machineScope *scope.PowerV return ctrl.Result{}, nil } + +// IBMPowerVSClusterToIBMPowerVSMachines is a handler.ToRequestsFunc to be used to enqeue requests for reconciliation +// of IBMPowerVSMachines. +func (r *IBMPowerVSMachineReconciler) IBMPowerVSClusterToIBMPowerVSMachines(ctx context.Context) handler.MapFunc { + log := ctrl.LoggerFrom(ctx) + return func(mapCtx context.Context, o client.Object) []ctrl.Request { + result := []ctrl.Request{} + + c, ok := o.(*infrav1beta2.IBMPowerVSCluster) + if !ok { + log.Error(errors.Errorf("expected a IBMPowerVSCluster but got a %T", o), "failed to get IBMPowerVSMachines for IBMPowerVSCluster") + return nil + } + + cluster, err := util.GetOwnerCluster(mapCtx, r.Client, c.ObjectMeta) + switch { + case apierrors.IsNotFound(err) || cluster == nil: + return result + case err != nil: + log.Error(err, "failed to get owning cluster") + return result + } + + labels := map[string]string{capiv1beta1.ClusterNameLabel: cluster.Name} + machineList := &capiv1beta1.MachineList{} + if err := r.List(mapCtx, machineList, client.InNamespace(c.Namespace), client.MatchingLabels(labels)); err != nil { + log.Error(err, "failed to list Machines") + return nil + } + for _, m := range machineList.Items { + if m.Spec.InfrastructureRef.Name == "" { + continue + } + name := client.ObjectKey{Namespace: m.Namespace, Name: m.Spec.InfrastructureRef.Name} + result = append(result, ctrl.Request{NamespacedName: name}) + } + + return result + } +} + +// SetupWithManager creates a new IBMVPCMachine controller for a manager. +func (r *IBMPowerVSMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + controller, err := ctrl.NewControllerManagedBy(mgr). + For(&infrav1beta2.IBMPowerVSMachine{}). + WithEventFilter(predicates.ResourceNotPaused(ctrl.LoggerFrom(ctx))). + Watches( + &capiv1beta1.Machine{}, + handler.EnqueueRequestsFromMapFunc(util.MachineToInfrastructureMapFunc(infrav1beta2.GroupVersion.WithKind("IBMPowerVSMachine"))), + ). + Watches( + &infrav1beta2.IBMPowerVSCluster{}, + handler.EnqueueRequestsFromMapFunc(r.IBMPowerVSClusterToIBMPowerVSMachines(ctx)), + ). + Build(r) + if err != nil { + return errors.Wrap(err, "error creating controller") + } + + clusterToObjectFunc, err := util.ClusterToTypedObjectsMapper(r.Client, &infrav1beta2.IBMPowerVSMachineList{}, mgr.GetScheme()) + if err != nil { + return errors.Wrap(err, "failed to create mapper for Cluster to IBMPowerVSMachines") + } + // Add a watch on capiv1beta1.Cluster object for unpause & ready notifications. + if err := controller.Watch( + source.Kind[client.Object](mgr.GetCache(), &capiv1beta1.Cluster{}, + handler.EnqueueRequestsFromMapFunc(clusterToObjectFunc), + predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)), + )); err != nil { + return errors.Wrap(err, "failed adding a watch for ready clusters") + } + return nil +} diff --git a/main.go b/main.go index dc765c9e9..f8ab2e5ff 100644 --- a/main.go +++ b/main.go @@ -285,7 +285,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, serviceEndpoint []e Recorder: mgr.GetEventRecorderFor("ibmpowervsmachine-controller"), ServiceEndpoint: serviceEndpoint, Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { + }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "IBMPowerVSMachine") os.Exit(1) }