#!/usr/bin/env bash
#
# Kubernetes-Knoten vorbereiten — Debian 13 (trixie)
# Gehoert zu Folge 05: https://lab.boothtml.com/folge/linux-vorbereiten
#
# Macht genau das, was vor "kubeadm init" bzw. "kubeadm join" noetig ist:
#   swap aus, Kernelmodule, sysctl, containerd mit SystemdCgroup,
#   kubeadm/kubelet/kubectl in fester Version.
#
# Das Skript ist wiederholbar: Es aendert nur, was noch nicht stimmt, und
# loescht nichts. Ein zweiter Durchlauf ist unschaedlich.
#
# Voraussetzung: curl. Beim minimalen Debian ist es nicht dabei:
#   apt update && apt install -y curl
#
# Aufruf (als root):
#   bash node-vorbereiten.sh
# Andere Kubernetes-Version:
#   K8S_MINOR=v1.33 bash node-vorbereiten.sh

set -euo pipefail

# Zwei Dinge, die sonst je nach Umgebung stillschweigend schiefgehen:
#
# 1. PATH: Wer mit "su" statt "su -" root wird, behaelt den PATH des normalen
#    Benutzers - und darin fehlt /usr/sbin. Dann findet die Shell weder
#    swapoff noch sysctl noch modprobe.
# 2. Sprache: Auf einem deutschen System gibt "free" die Zeile "Speicher:"
#    aus statt "Mem:". Jede Auswertung nach englischen Woertern geht ins Leere.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export LC_ALL=C

sagen() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
warnen() { printf '\033[1;33m !  %s\033[0m\n' "$*"; }

[ "$(id -u)" -eq 0 ] || { echo "Bitte als root ausfuehren (sudo)."; exit 1; }

# --- 0. Lage pruefen -------------------------------------------------------
sagen "Rechner: $(hostname) — $(nproc) Kerne, $(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo) MB RAM"

if [ "$(nproc)" -lt 2 ]; then
    warnen "Weniger als 2 CPU-Kerne — kubeadm bricht die Vorflugpruefung ab."
fi
if [ "$(hostname)" != "$(hostname | tr '[:upper:]' '[:lower:]')" ]; then
    warnen "Der Hostname enthaelt Grossbuchstaben. Kubernetes-Knotennamen"
    warnen "muessen klein geschrieben sein. Vorher aendern:"
    warnen "    hostnamectl set-hostname $(hostname | tr '[:upper:]' '[:lower:]')"
    exit 1
fi

# --- 1. swap aus -----------------------------------------------------------
sagen "swap abschalten"
swapoff -a
if grep -qE '^[^#].*\sswap\s' /etc/fstab; then
    cp /etc/fstab "/etc/fstab.vor-k8s-$(date +%Y%m%d-%H%M%S)"
    sed -i -E 's|^([^#].*\sswap\s.*)$|# \1  # von node-vorbereiten.sh abgeschaltet|' /etc/fstab
    echo "   /etc/fstab angepasst (Sicherung daneben abgelegt)"
else
    echo "   /etc/fstab hatte schon keinen aktiven swap-Eintrag"
fi

# --- 2. Kernelmodule -------------------------------------------------------
sagen "Kernelmodule overlay und br_netfilter"
cat >/etc/modules-load.d/k8s.conf <<'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter

# --- 3. sysctl -------------------------------------------------------------
sagen "Netzwerkschalter setzen"
cat >/etc/sysctl.d/k8s.conf <<'EOF'
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system >/dev/null
echo "   ip_forward = $(cat /proc/sys/net/ipv4/ip_forward)"

# --- 4. containerd ---------------------------------------------------------
sagen "containerd installieren und einstellen"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq containerd apt-transport-https ca-certificates curl gpg

mkdir -p /etc/containerd
if [ ! -s /etc/containerd/config.toml ] || ! grep -q SystemdCgroup /etc/containerd/config.toml; then
    containerd config default >/etc/containerd/config.toml
fi
# Der haeufigste Fehler ueberhaupt: kubelet nutzt systemd als cgroup-Treiber,
# containerd per Vorgabe nicht. Die Pods bleiben dann haengen, ohne dass die
# Meldung von cgroups spricht.
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable --quiet containerd
echo "   SystemdCgroup = $(grep -c 'SystemdCgroup = true' /etc/containerd/config.toml) mal gesetzt"

# --- 4b. CNI-Verzeichnis angleichen ----------------------------------------
# Debian traegt bin_dir = "/usr/lib/cni" ein, die gesamte Kubernetes-Welt legt
# ihre Plugins aber nach /opt/cni/bin. Folge: Knoten melden "Ready" (dafuer
# genuegt eine CNI-Konfigurationsdatei), aber kein einziger Pod startet:
#   failed to find plugin "cilium-cni" in path [/usr/lib/cni]
sagen "CNI-Verzeichnis auf /opt/cni/bin setzen"
apt-get install -y -qq containernetworking-plugins
mkdir -p /opt/cni/bin
cp -n /usr/lib/cni/* /opt/cni/bin/ 2>/dev/null || true
sed -i 's|bin_dir = "/usr/lib/cni"|bin_dir = "/opt/cni/bin"|' /etc/containerd/config.toml
systemctl restart containerd
grep -m1 bin_dir /etc/containerd/config.toml | sed 's/^/   /'

# --- 5. kubeadm, kubelet, kubectl -----------------------------------------
if [ -z "${K8S_MINOR:-}" ]; then
    STABIL="$(curl -fsSL https://dl.k8s.io/release/stable.txt)"   # z. B. v1.34.2
    K8S_MINOR="${STABIL%.*}"                                      # -> v1.34
fi
sagen "Kubernetes ${K8S_MINOR} installieren"
warnen "Auf ALLEN Knoten dieselbe Version verwenden — sonst scheitert der Beitritt."

mkdir -p /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/kubernetes-apt-keyring.gpg ]; then
    curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
        | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
fi
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/ /" \
    >/etc/apt/sources.list.d/kubernetes.list

apt-get update -qq
apt-get install -y -qq kubelet kubeadm kubectl
# Ohne hold zieht ein spaeteres "apt upgrade" die Version unter dem laufenden
# Cluster weg — das ist eine der haesslichsten Arten, ein Cluster zu zerlegen.
apt-mark hold kubelet kubeadm kubectl >/dev/null
systemctl enable --quiet kubelet

# --- 5b. Pausenimage angleichen -------------------------------------------
# containerd bringt in der Debian-Fassung ein aelteres pause-Image mit als
# kubeadm erwartet. Das ist nur eine Warnung, fuehrt aber dazu, dass zwei
# Images fuer dieselbe Aufgabe im Cluster liegen. Der richtige Wert steht in
# kubeadm selbst - deshalb erst jetzt, nach der Installation.
if PAUSE="$(kubeadm config images list 2>/dev/null | grep -m1 '/pause:')"; then
    sagen "Pausenimage auf ${PAUSE} setzen"
    sed -i "s|sandbox_image = \".*\"|sandbox_image = \"${PAUSE}\"|" /etc/containerd/config.toml
    systemctl restart containerd
    grep -m1 sandbox_image /etc/containerd/config.toml | sed 's/^/   /'
fi

# --- 6. Ergebnis -----------------------------------------------------------
sagen "Fertig auf $(hostname)"
echo "   kubeadm:    $(kubeadm version -o short)"
echo "   containerd: $(containerd --version | awk '{print $3}')"
echo "   swap:       $(swapon --show | wc -l) Eintraege aktiv (0 ist richtig)"
echo "   IP:         $(hostname -I | awk '{print $1}')"
echo
echo "Naechster Schritt:"
echo "  Control Plane:  kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint=<name-oder-ip>"
echo "  Worker:         den join-Befehl der Control Plane ausfuehren"
