From f3418161122a5faada4dcd09a5de5de31d0842be Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Sat, 28 Feb 2026 16:39:04 +0000 Subject: [PATCH] feat: run kubeadm reconcile after terraform apply on master --- .gitea/workflows/terraform-apply.yml | 68 +++++++++++++++++++ nixos/kubeadm/README.md | 5 ++ .../kubeadm/scripts/rebuild-and-bootstrap.sh | 58 +++++++++++++--- 3 files changed, 120 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/terraform-apply.yml b/.gitea/workflows/terraform-apply.yml index a3d2ab6..e7696ff 100644 --- a/.gitea/workflows/terraform-apply.yml +++ b/.gitea/workflows/terraform-apply.yml @@ -71,3 +71,71 @@ jobs: - name: Terraform Apply working-directory: terraform run: terraform apply -auto-approve tfplan + + - name: Create SSH key + run: | + install -m 0700 -d ~/.ssh + KEY_CONTENT="$(printf '%s' "${{ secrets.KUBEADM_SSH_PRIVATE_KEY }}")" + if [ -z "$KEY_CONTENT" ]; then + KEY_CONTENT="$(printf '%s' "${{ secrets.SSH_KEY_PRIVATE }}")" + fi + + if [ -z "$KEY_CONTENT" ]; then + echo "Missing SSH private key secret. Set KUBEADM_SSH_PRIVATE_KEY or SSH_KEY_PRIVATE." + exit 1 + fi + + printf '%s\n' "$KEY_CONTENT" > ~/.ssh/id_ed25519 + chmod 0600 ~/.ssh/id_ed25519 + + - name: Create kubeadm inventory from Terraform outputs + run: | + TF_OUTPUT_JSON="$(terraform -chdir=terraform output -json)" + + CP_1="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["control_plane_vm_ipv4"]["value"]["cp-1"])' <<< "$TF_OUTPUT_JSON")" + CP_2="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["control_plane_vm_ipv4"]["value"]["cp-2"])' <<< "$TF_OUTPUT_JSON")" + CP_3="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["control_plane_vm_ipv4"]["value"]["cp-3"])' <<< "$TF_OUTPUT_JSON")" + WK_1="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["worker_vm_ipv4"]["value"]["wk-1"])' <<< "$TF_OUTPUT_JSON")" + WK_2="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["worker_vm_ipv4"]["value"]["wk-2"])' <<< "$TF_OUTPUT_JSON")" + WK_3="$(python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(d["worker_vm_ipv4"]["value"]["wk-3"])' <<< "$TF_OUTPUT_JSON")" + + SSH_USER="$(printf '%s' "${{ secrets.KUBEADM_SSH_USER }}")" + if [ -z "$SSH_USER" ]; then + SSH_USER="micqdf" + fi + + cat > nixos/kubeadm/scripts/inventory.env << EOF + SSH_USER=$SSH_USER + CP_1=$CP_1 + CP_2=$CP_2 + CP_3=$CP_3 + WK_1=$WK_1 + WK_2=$WK_2 + WK_3=$WK_3 + EOF + + - name: Ensure nix and nixos-rebuild + env: + NIX_CONFIG: experimental-features = nix-command flakes + run: | + if [ ! -x /nix/var/nix/profiles/default/bin/nix ] && ! command -v nix >/dev/null 2>&1; then + sh <(curl -L https://nixos.org/nix/install) --no-daemon + fi + + if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then + . "$HOME/.nix-profile/etc/profile.d/nix.sh" + fi + + nix --version + nix profile install nixpkgs#nixos-rebuild + + - name: Rebuild and bootstrap/reconcile kubeadm cluster + env: + NIX_CONFIG: experimental-features = nix-command flakes + PATH: $HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:${{ env.PATH }} + run: | + if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then + . "$HOME/.nix-profile/etc/profile.d/nix.sh" + fi + + ./nixos/kubeadm/scripts/rebuild-and-bootstrap.sh diff --git a/nixos/kubeadm/README.md b/nixos/kubeadm/README.md index 30e2873..79e5f3e 100644 --- a/nixos/kubeadm/README.md +++ b/nixos/kubeadm/README.md @@ -117,6 +117,11 @@ For a full nuke/recreate lifecycle: ## Optional Gitea workflow automation +Primary flow: + +- Push to `master` triggers `.gitea/workflows/terraform-apply.yml` +- That workflow now does Terraform apply and then runs kubeadm rebuild/bootstrap reconciliation automatically + Manual dispatch workflows are available: - `.gitea/workflows/kubeadm-bootstrap.yml` diff --git a/nixos/kubeadm/scripts/rebuild-and-bootstrap.sh b/nixos/kubeadm/scripts/rebuild-and-bootstrap.sh index d38c2bc..8992b5a 100755 --- a/nixos/kubeadm/scripts/rebuild-and-bootstrap.sh +++ b/nixos/kubeadm/scripts/rebuild-and-bootstrap.sh @@ -25,6 +25,15 @@ for key in "${required[@]}"; do fi done +cluster_has_node() { + local node_name="$1" + remote "$CP_1" "sudo kubectl --kubeconfig /etc/kubernetes/admin.conf get node $node_name >/dev/null 2>&1" +} + +cluster_ready() { + remote "$CP_1" "test -f /etc/kubernetes/admin.conf && sudo kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes >/dev/null 2>&1" +} + remote() { local host_ip="$1" local cmd="$2" @@ -49,13 +58,17 @@ for node in cp-1 cp-2 cp-3 wk-1 wk-2 wk-3; do done echo "==> Initializing control plane on cp-1" -remote "$CP_1" "sudo th-kubeadm-init" +if cluster_ready; then + echo "==> Existing cluster detected on cp-1; skipping kubeadm init" +else + remote "$CP_1" "sudo th-kubeadm-init" -echo "==> Installing Cilium on cp-1" -remote "$CP_1" "helm repo add cilium https://helm.cilium.io >/dev/null 2>&1 || true" -remote "$CP_1" "helm repo update >/dev/null" -remote "$CP_1" "kubectl create namespace kube-system >/dev/null 2>&1 || true" -remote "$CP_1" "helm upgrade --install cilium cilium/cilium --namespace kube-system --set kubeProxyReplacement=true" + echo "==> Installing Cilium on cp-1" + remote "$CP_1" "helm repo add cilium https://helm.cilium.io >/dev/null 2>&1 || true" + remote "$CP_1" "helm repo update >/dev/null" + remote "$CP_1" "kubectl create namespace kube-system >/dev/null 2>&1 || true" + remote "$CP_1" "helm upgrade --install cilium cilium/cilium --namespace kube-system --set kubeProxyReplacement=true" +fi echo "==> Building kubeadm join commands" JOIN_CMD="$(remote "$CP_1" "sudo kubeadm token create --print-join-command")" @@ -77,13 +90,36 @@ join_worker() { } echo "==> Joining remaining control planes" -join_control_plane "$CP_2" -join_control_plane "$CP_3" +if cluster_has_node "cp-2"; then + echo "cp-2 already joined; skipping" +else + join_control_plane "$CP_2" +fi + +if cluster_has_node "cp-3"; then + echo "cp-3 already joined; skipping" +else + join_control_plane "$CP_3" +fi echo "==> Joining workers" -join_worker "$WK_1" -join_worker "$WK_2" -join_worker "$WK_3" +if cluster_has_node "wk-1"; then + echo "wk-1 already joined; skipping" +else + join_worker "$WK_1" +fi + +if cluster_has_node "wk-2"; then + echo "wk-2 already joined; skipping" +else + join_worker "$WK_2" +fi + +if cluster_has_node "wk-3"; then + echo "wk-3 already joined; skipping" +else + join_worker "$WK_3" +fi echo "==> Final node list" remote "$CP_1" "kubectl get nodes -o wide"