diff --git a/src/k8s/pkg/k8sd/api/capi_access_handler.go b/src/k8s/pkg/k8sd/api/capi_access_handler.go index c83733ecc..49dc1d48e 100644 --- a/src/k8s/pkg/k8sd/api/capi_access_handler.go +++ b/src/k8s/pkg/k8sd/api/capi_access_handler.go @@ -23,7 +23,7 @@ func ValidateCAPIAuthTokenAccessHandler(tokenHeaderName string) func(s state.Sta var err error tokenIsValid, err = database.ValidateClusterAPIToken(ctx, tx, token) if err != nil { - return fmt.Errorf("failed to check CAPI auth token: %w", err) + return fmt.Errorf("ffailed to check CAPI auth token: %w", err) } return nil }); err != nil { diff --git a/src/k8s/pkg/k8sd/app/app.go b/src/k8s/pkg/k8sd/app/app.go index c24bc76c7..db83f8ceb 100644 --- a/src/k8s/pkg/k8sd/app/app.go +++ b/src/k8s/pkg/k8sd/app/app.go @@ -19,11 +19,7 @@ import ( "github.com/canonical/microcluster/v2/client" "github.com/canonical/microcluster/v2/microcluster" "github.com/canonical/microcluster/v2/state" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" ) // Config defines configuration for the k8sd app. @@ -127,16 +123,10 @@ func New(cfg Config) (*App, error) { } if !cfg.DisableUpdateNodeConfigController { - mgr, err := setupManager(cfg) - app.manager = mgr - if err != nil { - return nil, fmt.Errorf("failed to setup manager: %w", err) - } - ctrller, err := setupControllers(app, mgr, cfg) - if err != nil { - return nil, fmt.Errorf("failed to setup controllers: %w", err) - } - app.nodeConfigReconciler = ctrller + app.nodeConfigReconciler = controllers.NewNodeConfigurationReconciler( + app.config.Snap, + app.readyWg.Wait, + ) } else { log.L().Info("update-node-config-controller disabled via config") } @@ -275,41 +265,3 @@ func (a *App) markNodeReady(ctx context.Context, s state.State) error { a.readyWg.Done() return nil } - -func setupManager(cfg Config) (manager.Manager, error) { - scheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - k8sClient, err := cfg.Snap.KubernetesClient("kube-system") - if err != nil { - return nil, fmt.Errorf("failed to create kubernetes client: %w", err) - } - - options := ctrl.Options{ - Scheme: scheme, - } - - mgr, err := ctrl.NewManager(k8sClient.RESTConfig(), options) - - return mgr, nil -} - -func setupControllers(app *App, mgr manager.Manager, cfg Config) (*controllers.NodeConfigurationReconciler, error) { - scheme := mgr.GetScheme() - - if !cfg.DisableUpdateNodeConfigController { - controller := controllers.NewNodeConfigurationReconciler( - mgr.GetClient(), - scheme, - cfg.Snap, - app.readyWg.Wait, - ) - - if err := controller.SetupWithManager(mgr); err != nil { - return nil, fmt.Errorf("failed to setup node configuration reconciler: %w", err) - } - return controller, nil - } - - return nil, nil -} diff --git a/src/k8s/pkg/k8sd/app/hooks_start.go b/src/k8s/pkg/k8sd/app/hooks_start.go index ec3fa69ac..996a8cc83 100644 --- a/src/k8s/pkg/k8sd/app/hooks_start.go +++ b/src/k8s/pkg/k8sd/app/hooks_start.go @@ -7,6 +7,7 @@ import ( "fmt" "time" + "github.com/canonical/k8s/pkg/k8sd/controllers" "github.com/canonical/k8s/pkg/k8sd/database" databaseutil "github.com/canonical/k8s/pkg/k8sd/database/util" "github.com/canonical/k8s/pkg/k8sd/types" @@ -14,6 +15,11 @@ import ( "github.com/canonical/k8s/pkg/utils" pkiutil "github.com/canonical/k8s/pkg/utils/pki" "github.com/canonical/microcluster/v2/state" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" ) func (a *App) onStart(ctx context.Context, s state.State) error { @@ -120,11 +126,68 @@ func (a *App) onStart(ctx context.Context, s state.State) error { ) } - if a.manager != nil { - if err := a.manager.Start(ctx); err != nil { - return fmt.Errorf("failed to start manager: %w", err) + go func() { + mgr, err := setupManager(ctx, a) + if err != nil { + log.FromContext(ctx).Error(err, "Failed to setup manager") + return } - } + + a.manager = mgr + log.FromContext(ctx).Info("Starting controller manager") + if err := mgr.Start(ctx); err != nil { + log.FromContext(ctx).Error(err, "Manager failed to start") + } + }() return nil } + +func setupManager(ctx context.Context, app *App) (manager.Manager, error) { + log.FromContext(ctx).Info("Setting up controller manager, waiting for ready signal") + app.readyWg.Wait() + log.FromContext(ctx).Info("Received ready signal, setting up controller manager") + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + k8sClient, err := app.config.Snap.KubernetesClient("kube-system") + if err != nil { + return nil, fmt.Errorf("failed to create kubernetes client: %w", err) + } + log.FromContext(ctx).Info("Created kubernetes client") + + options := ctrl.Options{ + Scheme: scheme, + } + + mgr, err := ctrl.NewManager(k8sClient.RESTConfig(), options) + if err != nil { + return nil, fmt.Errorf("failed to create manager: %w", err) + } + + log.FromContext(ctx).Info("Created controller manager") + + if _, err := setupControllers(ctx, app, mgr); err != nil { + return nil, fmt.Errorf("failed to setup controllers: %w", err) + } + + return mgr, nil +} + +func setupControllers(ctx context.Context, app *App, mgr manager.Manager) (*controllers.NodeConfigurationReconciler, error) { + log.FromContext(ctx).Info("Setting up controllers") + if app.nodeConfigReconciler != nil { + log.FromContext(ctx).Info("Setting up node configuration reconciler") + + app.nodeConfigReconciler.SetClient(mgr.GetClient()) + app.nodeConfigReconciler.SetScheme(mgr.GetScheme()) + + if err := app.nodeConfigReconciler.SetupWithManager(mgr); err != nil { + return nil, fmt.Errorf("failed to setup node configuration reconciler: %w", err) + } + return app.nodeConfigReconciler, nil + } + + return nil, nil +} diff --git a/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler.go b/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler.go index 558928f7d..2306e486c 100644 --- a/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler.go +++ b/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler.go @@ -22,8 +22,8 @@ import ( type NodeConfigurationReconciler struct { client.Client - scheme *runtime.Scheme snap snap.Snap + scheme *runtime.Scheme waitReady func() @@ -33,14 +33,10 @@ type NodeConfigurationReconciler struct { } func NewNodeConfigurationReconciler( - client client.Client, - scheme *runtime.Scheme, snap snap.Snap, waitReady func(), ) *NodeConfigurationReconciler { return &NodeConfigurationReconciler{ - Client: client, - scheme: scheme, snap: snap, waitReady: waitReady, reconciledCh: make(chan struct{}, 1), @@ -48,6 +44,17 @@ func NewNodeConfigurationReconciler( } func (c *NodeConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error { + if c.Client == nil { + return fmt.Errorf("client must be set before setting up with manager") + } + if c.scheme == nil { + return fmt.Errorf("scheme must be set before setting up with manager") + } + + if err := mgr.Add(c); err != nil { // This registers the Start method + return err + } + return ctrl.NewControllerManagedBy(mgr). For(&corev1.ConfigMap{}).WithEventFilter(predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { @@ -75,6 +82,8 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re "configmap", req.NamespacedName, ) + logger.Info("Reconciling node configuration") + // Check if we're running on a worker node if isWorker, err := snaputil.IsWorker(c.snap); err != nil { logger.Error(err, "Failed to check if running on a worker node") @@ -91,6 +100,8 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re return reconcile.Result{RequeueAfter: time.Second * 30}, err } + logger.Info("Retrieved cluster configuration") + // Load and process certificates keyPEM := config.Certificates.GetK8sdPrivateKey() key, err := pkiutil.LoadRSAPrivateKey(keyPEM) @@ -104,6 +115,8 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re return reconcile.Result{}, fmt.Errorf("failed to format kubelet configmap data: %w", err) } + logger.Info("Generated ConfigMap data") + // Get existing ConfigMap cm := &corev1.ConfigMap{} if err := c.Get(ctx, req.NamespacedName, cm); err != nil { @@ -111,6 +124,7 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re logger.Error(err, "Failed to get ConfigMap") return reconcile.Result{}, err } else { + logger.Info("ConfigMap not found, creating new ConfigMap") cm = &corev1.ConfigMap{ ObjectMeta: ctrl.ObjectMeta{ Name: req.Name, @@ -122,10 +136,13 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re logger.Error(err, "Failed to create ConfigMap") return reconcile.Result{}, err } + logger.Info("Created ConfigMap") return reconcile.Result{}, nil } } + logger.Info("Retrieved existing ConfigMap, will update") + // Update ConfigMap cm.Data = cmData if err := c.Update(ctx, cm); err != nil { @@ -133,6 +150,8 @@ func (c *NodeConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Re return reconcile.Result{}, err } + logger.Info("Updated ConfigMap, reconcile complete") + // Notify that reconciliation is complete select { case c.reconciledCh <- struct{}{}: @@ -151,10 +170,24 @@ func (r *NodeConfigurationReconciler) SetConfigGetter(getter func(context.Contex r.getClusterConfig = getter } -func (c *NodeConfigurationReconciler) Start(ctx context.Context) error { - logger := log.FromContext(ctx) - logger.V(1).Info("Waiting for node to be ready") - c.waitReady() - logger.V(1).Info("Starting update node configuration controller") +func (r *NodeConfigurationReconciler) SetScheme(scheme *runtime.Scheme) { + r.scheme = scheme +} + +func (r *NodeConfigurationReconciler) SetClient(client client.Client) { + r.Client = client +} + +func (r *NodeConfigurationReconciler) Start(ctx context.Context) error { + // Trigger initial reconciliation + _, err := r.Reconcile(ctx, ctrl.Request{ + NamespacedName: client.ObjectKey{ + Name: "k8sd-config", + Namespace: "kube-system", + }, + }) + if err != nil { + return err + } return nil } diff --git a/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler_test.go b/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler_test.go index 1adcaf4ea..c4edbd83f 100644 --- a/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler_test.go +++ b/src/k8s/pkg/k8sd/controllers/node_configuration_reconciler_test.go @@ -84,12 +84,13 @@ func TestNodeConfigurationReconciler(t *testing.T) { // Create controller reconciler := controllers.NewNodeConfigurationReconciler( - k8sClient, - scheme, s, func() {}, // Mock ready function ) + reconciler.SetClient(k8sClient) + reconciler.SetScheme(scheme) + // Set the config getter reconciler.SetConfigGetter(func(context.Context) (types.ClusterConfig, error) { return tc.expectedConfig, nil