diff --git a/cmd/main.go b/cmd/main.go index 00e652f1..5233c528 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -28,7 +28,6 @@ import ( apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/dynamic" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" @@ -185,12 +184,6 @@ func main() { os.Exit(1) } - dc, err := dynamic.NewForConfig(mgr.GetConfig()) - if err != nil { - setupLog.Error(err, "failed to create dynamic client") - os.Exit(1) - } - ctx := ctrl.SetupSignalHandler() if err = kcmv1.SetupIndexers(ctx, mgr); err != nil { setupLog.Error(err, "unable to setup indexers") @@ -228,20 +221,7 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ProviderTemplate") os.Exit(1) } - if err = (&controller.ClusterDeploymentReconciler{ - Client: mgr.GetClient(), - Config: mgr.GetConfig(), - DynamicClient: dc, - SystemNamespace: currentNamespace, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ClusterDeployment") - os.Exit(1) - } if err = (&controller.ManagementReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), - DynamicClient: dc, SystemNamespace: currentNamespace, CreateAccessManagement: createAccessManagement, }).SetupWithManager(mgr); err != nil { @@ -310,13 +290,6 @@ func main() { os.Exit(1) } - if err = (&controller.MultiClusterServiceReconciler{ - Client: mgr.GetClient(), - SystemNamespace: currentNamespace, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "MultiClusterService") - os.Exit(1) - } // TODO (zerospiel): disabled until the #605 // if err = (&controller.BackupReconciler{ // Client: mgr.GetClient(), diff --git a/internal/controller/clusterdeployment_controller.go b/internal/controller/clusterdeployment_controller.go index 790cc9ff..6487b940 100644 --- a/internal/controller/clusterdeployment_controller.go +++ b/internal/controller/clusterdeployment_controller.go @@ -66,7 +66,7 @@ type helmActor interface { // ClusterDeploymentReconciler reconciles a ClusterDeployment object type ClusterDeploymentReconciler struct { - client.Client + Client client.Client helmActor Config *rest.Config DynamicClient *dynamic.DynamicClient @@ -80,7 +80,7 @@ func (r *ClusterDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Re l.Info("Reconciling ClusterDeployment") clusterDeployment := &kcm.ClusterDeployment{} - if err := r.Get(ctx, req.NamespacedName, clusterDeployment); err != nil { + if err := r.Client.Get(ctx, req.NamespacedName, clusterDeployment); err != nil { if apierrors.IsNotFound(err) { l.Info("ClusterDeployment not found, ignoring since object must be deleted") return ctrl.Result{}, nil @@ -98,7 +98,7 @@ func (r *ClusterDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Re if clusterDeployment.Status.ObservedGeneration == 0 { mgmt := &kcm.Management{} mgmtRef := client.ObjectKey{Name: kcm.ManagementName} - if err := r.Get(ctx, mgmtRef, mgmt); err != nil { + if err := r.Client.Get(ctx, mgmtRef, mgmt); err != nil { l.Error(err, "Failed to get Management object") return ctrl.Result{}, err } @@ -167,7 +167,7 @@ func (r *ClusterDeploymentReconciler) reconcileUpdate(ctx context.Context, mc *k err = errors.Join(err, r.updateStatus(ctx, mc, clusterTpl)) }() - if err = r.Get(ctx, client.ObjectKey{Name: mc.Spec.Template, Namespace: mc.Namespace}, clusterTpl); err != nil { + if err = r.Client.Get(ctx, client.ObjectKey{Name: mc.Spec.Template, Namespace: mc.Namespace}, clusterTpl); err != nil { l.Error(err, "Failed to get Template") errMsg := fmt.Sprintf("failed to get provided template: %s", err) if apierrors.IsNotFound(err) { @@ -489,7 +489,7 @@ func (r *ClusterDeploymentReconciler) updateServices(ctx context.Context, mc *kc // will ultimately return the error in servicesErr instead of nil. profile := sveltosv1beta1.Profile{} profileRef := client.ObjectKey{Name: mc.Name, Namespace: mc.Namespace} - if servicesErr = r.Get(ctx, profileRef, &profile); servicesErr != nil { + if servicesErr = r.Client.Get(ctx, profileRef, &profile); servicesErr != nil { servicesErr = fmt.Errorf("failed to get Profile %s to fetch status from its associated ClusterSummary: %w", profileRef.String(), servicesErr) return ctrl.Result{}, nil } @@ -514,7 +514,7 @@ func (r *ClusterDeploymentReconciler) updateStatus(ctx context.Context, clusterD return errors.New("failed to set available upgrades") } - if err := r.Status().Update(ctx, clusterDeployment); err != nil { + if err := r.Client.Status().Update(ctx, clusterDeployment); err != nil { return fmt.Errorf("failed to update status for clusterDeployment %s/%s: %w", clusterDeployment.Namespace, clusterDeployment.Name, err) } @@ -538,7 +538,7 @@ func (r *ClusterDeploymentReconciler) Delete(ctx context.Context, clusterDeploym hr := &hcv2.HelmRelease{} - if err := r.Get(ctx, client.ObjectKeyFromObject(clusterDeployment), hr); err != nil { + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(clusterDeployment), hr); err != nil { if !apierrors.IsNotFound(err) { return ctrl.Result{}, err } @@ -637,7 +637,7 @@ func (r *ClusterDeploymentReconciler) releaseCluster(ctx context.Context, namesp func (r *ClusterDeploymentReconciler) getInfraProvidersNames(ctx context.Context, templateNamespace, templateName string) ([]string, error) { template := &kcm.ClusterTemplate{} templateRef := client.ObjectKey{Name: templateName, Namespace: templateNamespace} - if err := r.Get(ctx, templateRef, template); err != nil { + if err := r.Client.Get(ctx, templateRef, template); err != nil { ctrl.LoggerFrom(ctx).Error(err, "Failed to get ClusterTemplate", "template namespace", templateNamespace, "template name", templateName) return nil, err } @@ -806,7 +806,7 @@ func (r *ClusterDeploymentReconciler) setAvailableUpgrades(ctx context.Context, return nil } chains := &kcm.ClusterTemplateChainList{} - err := r.List(ctx, chains, + err := r.Client.List(ctx, chains, client.InNamespace(template.Namespace), client.MatchingFields{kcm.TemplateChainSupportedTemplatesIndexKey: template.GetName()}, ) @@ -835,7 +835,10 @@ func (r *ClusterDeploymentReconciler) setAvailableUpgrades(ctx context.Context, // SetupWithManager sets up the controller with the Manager. func (r *ClusterDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error { - r.helmActor = helm.NewActor(r.Config, r.RESTMapper()) + r.Client = mgr.GetClient() + r.Config = mgr.GetConfig() + + r.helmActor = helm.NewActor(r.Config, r.Client.RESTMapper()) return ctrl.NewControllerManagedBy(mgr). For(&kcm.ClusterDeployment{}). diff --git a/internal/controller/management_controller.go b/internal/controller/management_controller.go index e4b041f9..8875cfd4 100644 --- a/internal/controller/management_controller.go +++ b/internal/controller/management_controller.go @@ -39,6 +39,7 @@ 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/manager" kcm "github.com/K0rdent/kcm/api/v1alpha1" "github.com/K0rdent/kcm/internal/certmanager" @@ -49,12 +50,14 @@ import ( // ManagementReconciler reconciles a Management object type ManagementReconciler struct { - client.Client - Scheme *runtime.Scheme - Config *rest.Config - DynamicClient *dynamic.DynamicClient - SystemNamespace string - CreateAccessManagement bool + Client client.Client + Manager manager.Manager + Scheme *runtime.Scheme + Config *rest.Config + DynamicClient *dynamic.DynamicClient + SystemNamespace string + CreateAccessManagement bool + sveltosDependentControllersStarted bool } func (r *ManagementReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -62,7 +65,7 @@ func (r *ManagementReconciler) Reconcile(ctx context.Context, req ctrl.Request) l.Info("Reconciling Management") management := &kcm.Management{} - if err := r.Get(ctx, req.NamespacedName, management); err != nil { + if err := r.Client.Get(ctx, req.NamespacedName, management); err != nil { if apierrors.IsNotFound(err) { l.Info("Management not found, ignoring since object must be deleted") return ctrl.Result{}, nil @@ -145,7 +148,7 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *kcm.Manag continue } template := new(kcm.ProviderTemplate) - if err := r.Get(ctx, client.ObjectKey{Name: component.Template}, template); err != nil { + if err := r.Client.Get(ctx, client.ObjectKey{Name: component.Template}, template); err != nil { errMsg := fmt.Sprintf("Failed to get ProviderTemplate %s: %s", component.Template, err) updateComponentsStatus(statusAccumulator, component, nil, errMsg) errs = errors.Join(errs, errors.New(errMsg)) @@ -196,7 +199,15 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *kcm.Manag management.Status.ObservedGeneration = management.Generation management.Status.Release = management.Spec.Release - if err := r.Status().Update(ctx, management); err != nil { + shouldRequeue, err := r.startDependentControllers(ctx, management) + if err != nil { + return ctrl.Result{}, err + } + if shouldRequeue { + requeue = true + } + + if err := r.Client.Status().Update(ctx, management); err != nil { errs = errors.Join(errs, fmt.Errorf("failed to update status for Management %s: %w", management.Name, err)) } @@ -211,6 +222,44 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *kcm.Manag return ctrl.Result{}, nil } +// startDependentControllers starts controllers that cannot be started +// at process startup because of some dependency like CRDs being present. +func (r *ManagementReconciler) startDependentControllers(ctx context.Context, management *kcm.Management) (requue bool, err error) { + l := ctrl.LoggerFrom(ctx) + + if r.sveltosDependentControllersStarted { + // Only need to start controllers once. + return false, nil + } + + if !management.Status.Components[kcm.ProviderSveltosName].Success { + l.Info(fmt.Sprintf("Waiting for %s provider to be ready to setup contollers dependent on it", kcm.ProviderSveltosName)) + return true, nil + } + + currentNamespace := utils.CurrentNamespace() + + l.Info(fmt.Sprintf("Provider %s has been successfully installed, so setting up controller for ClusterDeployment", kcm.ProviderSveltosName)) + if err = (&ClusterDeploymentReconciler{ + DynamicClient: r.DynamicClient, + SystemNamespace: currentNamespace, + }).SetupWithManager(r.Manager); err != nil { + return false, fmt.Errorf("failed to setup controller for ClusterDeployment: %w", err) + } + l.Info("Setup for ClusterDeployment controller successful") + + l.Info(fmt.Sprintf("Provider %s has been successfully installed, so setting up controller for MultiClusterService", kcm.ProviderSveltosName)) + if err = (&MultiClusterServiceReconciler{ + SystemNamespace: currentNamespace, + }).SetupWithManager(r.Manager); err != nil { + return false, fmt.Errorf("failed to setup controller for MultiClusterService: %w", err) + } + l.Info("Setup for MultiClusterService controller successful") + + r.sveltosDependentControllersStarted = true + return false, nil +} + func (r *ManagementReconciler) cleanupRemovedComponents(ctx context.Context, management *kcm.Management) error { var ( errs error @@ -270,7 +319,7 @@ func (r *ManagementReconciler) ensureAccessManagement(ctx context.Context, mgmt }, }, } - err := r.Get(ctx, client.ObjectKey{ + err := r.Client.Get(ctx, client.ObjectKey{ Name: kcm.AccessManagementName, }, amObj) if err == nil { @@ -279,7 +328,7 @@ func (r *ManagementReconciler) ensureAccessManagement(ctx context.Context, mgmt if !apierrors.IsNotFound(err) { return fmt.Errorf("failed to get %s AccessManagement object: %w", kcm.AccessManagementName, err) } - err = r.Create(ctx, amObj) + err = r.Client.Create(ctx, amObj) if err != nil { return fmt.Errorf("failed to create %s AccessManagement object: %w", kcm.AccessManagementName, err) } @@ -294,7 +343,7 @@ func (r *ManagementReconciler) ensureAccessManagement(ctx context.Context, mgmt func (r *ManagementReconciler) checkProviderStatus(ctx context.Context, component component) error { helmReleaseName := component.helmReleaseName hr := &fluxv2.HelmRelease{} - err := r.Get(ctx, types.NamespacedName{Namespace: r.SystemNamespace, Name: helmReleaseName}, hr) + err := r.Client.Get(ctx, types.NamespacedName{Namespace: r.SystemNamespace, Name: helmReleaseName}, hr) if err != nil { return fmt.Errorf("failed to check provider status: %w", err) } @@ -622,6 +671,17 @@ func updateComponentsStatus( // SetupWithManager sets up the controller with the Manager. func (r *ManagementReconciler) SetupWithManager(mgr ctrl.Manager) error { + dc, err := dynamic.NewForConfig(mgr.GetConfig()) + if err != nil { + return fmt.Errorf("failed to create dynamic client: %w", err) + } + + r.Manager = mgr + r.Client = mgr.GetClient() + r.Scheme = mgr.GetScheme() + r.Config = mgr.GetConfig() + r.DynamicClient = dc + return ctrl.NewControllerManagedBy(mgr). For(&kcm.Management{}). Complete(r) diff --git a/internal/controller/management_controller_test.go b/internal/controller/management_controller_test.go index 2c455014..8671c17a 100644 --- a/internal/controller/management_controller_test.go +++ b/internal/controller/management_controller_test.go @@ -277,8 +277,8 @@ var _ = Describe("Management Controller", func() { controllerReconciler := &ManagementReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), - SystemNamespace: utils.DefaultSystemNamespace, DynamicClient: dynamicClient, + SystemNamespace: utils.DefaultSystemNamespace, } _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/multiclusterservice_controller.go b/internal/controller/multiclusterservice_controller.go index b17d53ed..d87d45f9 100644 --- a/internal/controller/multiclusterservice_controller.go +++ b/internal/controller/multiclusterservice_controller.go @@ -42,7 +42,7 @@ import ( // MultiClusterServiceReconciler reconciles a MultiClusterService object type MultiClusterServiceReconciler struct { - client.Client + Client client.Client SystemNamespace string } @@ -52,7 +52,7 @@ func (r *MultiClusterServiceReconciler) Reconcile(ctx context.Context, req ctrl. l.Info("Reconciling MultiClusterService") mcs := &kcm.MultiClusterService{} - err := r.Get(ctx, req.NamespacedName, mcs) + err := r.Client.Get(ctx, req.NamespacedName, mcs) if apierrors.IsNotFound(err) { l.Info("MultiClusterService not found, ignoring since object must be deleted") return ctrl.Result{}, nil @@ -72,7 +72,7 @@ func (r *MultiClusterServiceReconciler) Reconcile(ctx context.Context, req ctrl. func (r *MultiClusterServiceReconciler) reconcileUpdate(ctx context.Context, mcs *kcm.MultiClusterService) (_ ctrl.Result, err error) { if utils.AddLabel(mcs, kcm.GenericComponentLabelName, kcm.GenericComponentLabelValueKCM) { - if err := r.Update(ctx, mcs); err != nil { + if err := r.Client.Update(ctx, mcs); err != nil { return ctrl.Result{}, fmt.Errorf("failed to update labels: %w", err) } return ctrl.Result{Requeue: true}, nil // generation has not changed, need explicit requeue @@ -153,7 +153,7 @@ func (r *MultiClusterServiceReconciler) reconcileUpdate(ctx context.Context, mcs // will ultimately return the error in servicesErr instead of nil. profile := sveltosv1beta1.ClusterProfile{} profileRef := client.ObjectKey{Name: mcs.Name} - if servicesErr = r.Get(ctx, profileRef, &profile); servicesErr != nil { + if servicesErr = r.Client.Get(ctx, profileRef, &profile); servicesErr != nil { servicesErr = fmt.Errorf("failed to get ClusterProfile %s to fetch status from its associated ClusterSummary: %w", profileRef.String(), servicesErr) return ctrl.Result{}, nil } @@ -173,7 +173,7 @@ func (r *MultiClusterServiceReconciler) updateStatus(ctx context.Context, mcs *k mcs.Status.ObservedGeneration = mcs.Generation mcs.Status.Conditions = updateStatusConditions(mcs.Status.Conditions, "MultiClusterService is ready") - if err := r.Status().Update(ctx, mcs); err != nil { + if err := r.Client.Status().Update(ctx, mcs); err != nil { return fmt.Errorf("failed to update status for MultiClusterService %s/%s: %w", mcs.Namespace, mcs.Name, err) } @@ -311,6 +311,8 @@ func requeueSveltosProfileForClusterSummary(ctx context.Context, obj client.Obje // SetupWithManager sets up the controller with the Manager. func (r *MultiClusterServiceReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + return ctrl.NewControllerManagedBy(mgr). For(&kcm.MultiClusterService{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Watches(&sveltosv1beta1.ClusterSummary{},