package bootstrap import ( "context" "fmt" monov1alpha1 "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1" "undecided.project/monok8s/pkg/node" "undecided.project/monok8s/pkg/system" ) type Runner struct { NodeCtx *node.NodeContext Registry *Registry initSteps []StepInfo } type StepInfo struct { RegKey string Name string Desc string } type StepSelection struct { Indices []int // 1-based } func NewRunner(cfg *monov1alpha1.MonoKSConfig) *Runner { runnerCfg := system.RunnerConfig{} nctx := &node.NodeContext{ Config: cfg, SystemRunner: system.NewRunner(runnerCfg), } return &Runner{ NodeCtx: nctx, Registry: NewRegistry(nctx), initSteps: []StepInfo{ { RegKey: "configure_hostname", Name: "Configure hostname", Desc: "Set system hostname according to cluster configuration", }, { RegKey: "configure_mgmt_interface", Name: "Configure management interface", Desc: "Configure management network interface, IP address, and gateway", }, { RegKey: "configure_dns", Name: "Configure DNS", Desc: "Set system DNS resolver configuration for cluster and external access", }, { RegKey: "ensure_ip_forward", Name: "Ensure IP forwarding", Desc: "Enable kernel IP forwarding required for pod networking", }, { RegKey: "configure_default_cni", Name: "Configure default CNI", Desc: "Install or configure default container networking (CNI bridge, IPAM, etc.)", }, { RegKey: "start_crio", Name: "Start CRI-O runtime", Desc: "Start container runtime and verify it is ready for Kubernetes workloads", }, { RegKey: "validate_required_images", Name: "Validate required images", Desc: "Ensure all required Kubernetes images are present or available locally", }, { RegKey: "validate_network_requirements", Name: "Validate network requirements", Desc: "Ensure required kernel networking features (iptables/nftables, bridge, forwarding) are available", }, { RegKey: "detect_local_cluster_state", Name: "Detect local cluster state", Desc: "Inspect local node to determine existing Kubernetes membership and configuration", }, { RegKey: "classify_bootstrap_action", Name: "Classify bootstrap action", Desc: "Decide whether to init, join, upgrade, or reconcile based on local state and desired version", }, { RegKey: "wait_for_existing_cluster_if_needed", Name: "Wait for existing cluster", Desc: "Block until control plane is reachable when joining or reconciling an existing cluster", }, { RegKey: "generate_kubeadm_config", Name: "Generate kubeadm config", Desc: "Render kubeadm configuration for init, join, or upgrade operations", }, { RegKey: "apply_local_node_metadata_if_possible", Name: "Apply node metadata", Desc: "Apply labels/annotations to the local node if API server is reachable", }, { RegKey: "allow_single_node_scheduling", Name: "Allow single-node scheduling", Desc: "Remove control-plane taints to allow workloads on single-node clusters", }, { RegKey: "reconcile_control_plane", Name: "Reconcile control plane", Desc: "Ensure control plane components match desired state without full reinitialization", }, { RegKey: "check_upgrade_prereqs", Name: "Check upgrade prerequisites", Desc: "Validate cluster state and version compatibility before upgrade", }, { RegKey: "run_kubeadm_upgrade_apply", Name: "Run kubeadm upgrade apply", Desc: "Upgrade control plane components using kubeadm", }, { RegKey: "run_kubeadm_init", Name: "Run kubeadm init", Desc: "Initialize a new Kubernetes control plane using kubeadm", }, { RegKey: "run_kubeadm_join", Name: "Run kubeadm join", Desc: "Join node to existing cluster as worker or control-plane", }, { RegKey: "reconcile_node", Name: "Reconcile node state", Desc: "Ensure node configuration matches desired state after join or upgrade", }, { RegKey: "run_kubeadm_upgrade_node", Name: "Run kubeadm upgrade node", Desc: "Upgrade node components (kubelet, config) to match control plane", }, { RegKey: "print_summary", Name: "Print summary", Desc: "Output final bootstrap summary and detected state", }, }, } } func (r *Runner) RunNamedStep(ctx context.Context, name string) error { step, err := r.Registry.Get(name) if err != nil { return err } return step(ctx, r.NodeCtx) } func (r *Runner) InitSteps() []StepInfo { return r.initSteps } func (r *Runner) Init(ctx context.Context) error { for i, step := range r.initSteps { if err := r.RunNamedStep(ctx, step.RegKey); err != nil { return fmt.Errorf("step %d (%s): %w", i+1, step.Name, err) } } return nil } func (r *Runner) InitSelected(ctx context.Context, sel StepSelection) error { for _, idx := range sel.Indices { step := r.initSteps[idx-1] if err := r.RunNamedStep(ctx, step.RegKey); err != nil { return fmt.Errorf("step %d (%s): %w", idx, step.Name, err) } } return nil }