Gitfed
bastien-mrq/gitfed / cmd / gitfed-ctl / health.go
package main

import (
	"context"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// checkKubectl reports whether kubectl can currently reach the cluster —
// this is the exact failure observed live: a freshly root-owned
// /etc/rancher/k3s/k3s.yaml with no ~/.kube/config for root to fall back
// to, so every kubectl call prompts nothing and just fails.
func checkKubectl(ctx context.Context) bool {
	_, err := runQuiet(ctx, "", "kubectl", "get", "ns", "gitfed", "--no-headers")
	return err == nil
}

// k3sYAMLPath is where a fresh k3s install always writes its kubeconfig,
// root-owned — same path cmd/gitfed-install/actions.go's setupKubeconfig
// reads from for the non-root case.
const k3sYAMLPath = "/etc/rancher/k3s/k3s.yaml"

func k3sYAMLExists() bool {
	_, err := os.Stat(k3sYAMLPath)
	return err == nil
}

// fixKubeconfig is the root-side equivalent of cmd/gitfed-install's
// setupKubeconfig: gitfed-ctl already runs as root (see main.go), so there
// is no sudo subshell to invoke — just copy the file directly into root's
// own kube config location and point KUBECONFIG at it for the rest of this
// process's lifetime.
func fixKubeconfig() error {
	data, err := os.ReadFile(k3sYAMLPath)
	if err != nil {
		return err
	}
	if err := os.MkdirAll("/root/.kube", 0700); err != nil {
		return err
	}
	if err := os.WriteFile("/root/.kube/config", data, 0600); err != nil {
		return err
	}
	os.Setenv("KUBECONFIG", "/root/.kube/config")
	return nil
}

// podSummary is a one-line human-readable status of the gitfed pod for the
// dashboard — not a full stuck-pod diagnosis (that's cmd/gitfed-install's
// job during first install); here it's only "is it healthy right now".
func podSummary(ctx context.Context) (summary string, healthy bool, err error) {
	out, err := runQuiet(ctx, "", "kubectl", "-n", "gitfed", "get", "pods", "--no-headers")
	if err != nil {
		return "", false, err
	}
	for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
		fields := strings.Fields(line)
		if len(fields) < 3 || !strings.HasPrefix(fields[0], "gitfed-") {
			continue
		}
		status := fields[2]
		ready, total, ok := readyFraction(fields[1])
		healthy = status == "Running" && ok && ready == total && total > 0
		return fmt.Sprintf("%s — %s (%s)", fields[0], status, fields[1]), healthy, nil
	}
	return "aucun pod trouvé dans le namespace gitfed", false, nil
}

func readyFraction(s string) (ready, total int, ok bool) {
	a, b, found := strings.Cut(s, "/")
	if !found {
		return 0, 0, false
	}
	ready, err1 := strconv.Atoi(a)
	total, err2 := strconv.Atoi(b)
	return ready, total, err1 == nil && err2 == nil
}