package controller import ( "context" _ "embed" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" 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/log" k8sprovisionerv1alpha1 "vanderlande.com/appstack/k8s-provisioner/api/v1alpha1" "vanderlande.com/appstack/k8s-provisioner/internal/harvester" "vanderlande.com/appstack/k8s-provisioner/internal/helm" "vanderlande.com/appstack/k8s-provisioner/internal/templates" "vanderlande.com/appstack/k8s-provisioner/internal/values" ) type ClusterReconciler struct { client.Client Scheme *runtime.Scheme } // Internal Struct for mapping NodePools to Helm Values type HelmNodePool struct { Name string `json:"name"` DisplayName string `json:"displayName"` Quantity int `json:"quantity"` Etcd bool `json:"etcd"` ControlPlane bool `json:"controlplane"` Worker bool `json:"worker"` Paused bool `json:"paused"` CpuCount int `json:"cpuCount"` DiskSize int `json:"diskSize"` ImageName string `json:"imageName"` MemorySize int `json:"memorySize"` NetworkName string `json:"networkName"` SshUser string `json:"sshUser"` VmNamespace string `json:"vmNamespace"` UserData string `json:"userData"` } // +kubebuilder:rbac:groups=k8sprovisioner.appstack.io,resources=clusters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=k8sprovisioner.appstack.io,resources=clusters/status,verbs=get;update;patch // +kubebuilder:rbac:groups=k8sprovisioner.appstack.io,resources=infras,verbs=get;list;watch // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete const clusterFinalizer = "k8sprovisioner.appstack.io/finalizer" func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx) // Initialize Managers hvManager := harvester.NewIdentityManager(r.Client, r.Scheme) // 1. Fetch Cluster var cluster k8sprovisionerv1alpha1.Cluster if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // 2. Handle Deletion if !cluster.ObjectMeta.DeletionTimestamp.IsZero() { if controllerutil.ContainsFinalizer(&cluster, clusterFinalizer) { l.Info("Processing Cluster Deletion...") // A. Uninstall Helm helmCfg := helm.Config{Namespace: req.Namespace, ReleaseName: req.Name} if err := helm.Uninstall(helmCfg); err != nil { return ctrl.Result{}, err } // B. Cleanup Harvester (Using Manager) hvManager.Cleanup(ctx, &cluster) // C. Remove Finalizer controllerutil.RemoveFinalizer(&cluster, clusterFinalizer) if err := r.Update(ctx, &cluster); err != nil { return ctrl.Result{}, err } } return ctrl.Result{}, nil } // 3. Add Finalizer if !controllerutil.ContainsFinalizer(&cluster, clusterFinalizer) { controllerutil.AddFinalizer(&cluster, clusterFinalizer) if err := r.Update(ctx, &cluster); err != nil { return ctrl.Result{}, err } } // 4. Fetch Infra var infra k8sprovisionerv1alpha1.Infra if err := r.Get(ctx, types.NamespacedName{Name: cluster.Spec.InfraRef, Namespace: req.Namespace}, &infra); err != nil { return ctrl.Result{}, err } // ========================================================= // 5. SECURE HARVESTER IDENTITY (Simplified) // ========================================================= // The manager handles looking up Rancher creds, minting tokens, // saving secrets, and updating the Cluster status. generatedSecretName, err := hvManager.Ensure(ctx, &cluster, &infra) if err != nil { return ctrl.Result{}, err } // ========================================================= // 6. HELM VALUES GENERATION // ========================================================= vb := values.NewBuilder( &cluster, &infra, templates.BaseValuesYAML, generatedSecretName, req.Namespace, ) helmValues, err := vb.Build() if err != nil { l.Error(err, "Failed to generate helm values") return ctrl.Result{}, err } chartSpec := vb.GetChartConfig() // 7. Trigger Helm Apply l.Info("Syncing Helm Release", "Release", req.Name) helmCfg := helm.Config{ Namespace: req.Namespace, ReleaseName: req.Name, RepoURL: chartSpec.Repo, ChartName: chartSpec.Name, Version: chartSpec.Version, Values: helmValues, } if err := helm.Apply(helmCfg); err != nil { l.Error(err, "Helm Apply Failed") return ctrl.Result{}, err } return ctrl.Result{}, nil } func (r *ClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&k8sprovisionerv1alpha1.Cluster{}). Complete(r) }