From 7fd342ee22ac3ba2c900fc053f5aa5a3335e8aae Mon Sep 17 00:00:00 2001 From: Karthik K N Date: Thu, 9 May 2024 16:47:46 +0530 Subject: [PATCH] Add support for pausing cluster reconcilation --- 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 a3e785f48..6bba56b99 100644 --- a/controllers/ibmpowervscluster_controller.go +++ b/controllers/ibmpowervscluster_controller.go @@ -34,7 +34,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" @@ -455,8 +457,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(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 3049139f6..ec6a09f9a 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" @@ -157,13 +161,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") @@ -299,3 +296,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.IBMVPCMachine{}). + 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 GCPMachines") + } + // Add a watch on capiv1beta1.Cluster object for unpause & ready notifications. + if err := controller.Watch( + source.Kind(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 945cfc93d..bbb58bdd1 100644 --- a/main.go +++ b/main.go @@ -271,7 +271,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) }