#!/usr/bin/env bash # # Homelab Cluster Bootstrap (Phase 0) # Bootstrap a fresh Talos cluster to GitOps-ready state (ArgoCD + Forgejo). # Run once from local checkout, then all future changes via git push. # # Prerequisites: # - Talos cluster up (terraform apply completed) # - kubectl configured (KUBECONFIG points at cluster) # - SOPS age key at ~/.sops/homelab-age.key # - ArgoCD CLI installed (for final sync) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" K8S_DIR="$SCRIPT_DIR/k8s" BOOTSTRAP_DIR="$K8S_DIR/bootstrap-local" SOPS_KEY="${SOPS_KEY:-$HOME/.sops/homelab-age.key}" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log() { echo -e "${GREEN}[$(date +'%H:%M:%S')]${NC} $*"; } warn() { echo -e "${YELLOW}[$(date +'%H:%M:%S')]${NC} $*"; } error() { echo -e "${RED}[$(date +'%H:%M:%S')]${NC} $*"; exit 1; } # Preflight checks log "Running preflight checks..." kubectl cluster-info > /dev/null || error "kubectl not configured or cluster unreachable" [[ -f "$SOPS_KEY" ]] || error "SOPS age key not found at $SOPS_KEY" command -v argocd > /dev/null || warn "ArgoCD CLI not found - manual sync required at end" # 1. Install ArgoCD itself (if not already present) if ! kubectl get namespace argocd &>/dev/null; then log "Installing ArgoCD..." kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml log "Waiting for ArgoCD to be ready..." kubectl wait --for=condition=available --timeout=300s deployment/argocd-server -n argocd else log "ArgoCD already installed, skipping..." fi # 2. Create SOPS age secret (NEVER commit this to git) log "Creating SOPS age secret..." kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f - kubectl create secret generic sops-age \ -n argocd \ --from-file=keys.txt="$SOPS_KEY" \ --dry-run=client -o yaml | kubectl apply -f - # 3. Apply bootstrap bundle (namespaces, CNPG, DDB, Forgejo) log "Applying bootstrap bundle..." kubectl apply -k "$BOOTSTRAP_DIR" --server-side # 4. Wait for CNPG operator log "Waiting for CNPG operator..." kubectl wait --for=condition=available --timeout=300s \ deployment/cnpg-controller-manager -n ddb 2>/dev/null || { warn "CNPG operator not found - checking if it exists as different deployment name..." kubectl get deployments -n ddb } # 5. Wait for DDB cluster log "Waiting for PostgreSQL cluster (ddb-cluster) to be ready..." for i in {1..60}; do STATUS=$(kubectl get cluster ddb-cluster -n ddb -o jsonpath='{.status.phase}' 2>/dev/null || echo "NotFound") if [[ "$STATUS" == "Cluster in healthy state" ]]; then log "DDB cluster is ready!" break fi [[ $i -eq 60 ]] && error "Timeout waiting for ddb-cluster" sleep 5 done # 6. Copy DB secret from ddb to cicd namespace log "Copying ddb-cluster-app secret to cicd namespace..." kubectl get secret ddb-cluster-app -n ddb -o yaml \ | sed 's/namespace: ddb/namespace: cicd/' \ | kubectl apply -f - # Copy DB secret to iam namespace (for authentik) log "Copying ddb-cluster-app secret to iam namespace..." kubectl get secret ddb-cluster-app -n ddb -o yaml \ | sed 's/namespace: ddb/namespace: iam/' \ | kubectl apply -f - # 7. Wait for Forgejo log "Waiting for Forgejo to be ready..." kubectl wait --for=condition=available --timeout=600s \ deployment/forgejo -n cicd 2>/dev/null || { warn "Forgejo not found as deployment - checking StatefulSet..." kubectl wait --for=condition=available --timeout=600s \ statefulset/forgejo -n cicd || warn "Could not find Forgejo - check manually" } # 8. Apply Longhorn 3-node configuration (if it exists) if [[ -d "$K8S_DIR/infrastructure/longhorn" ]]; then log "Applying Longhorn 3-node HA configuration..." kubectl apply -k "$K8S_DIR/infrastructure/longhorn/" || warn "Longhorn config failed - may need manual intervention" else warn "Longhorn config not found at k8s/infrastructure/longhorn/ - storage may be single-node only!" fi # 9. Get Forgejo LoadBalancer IP FORGEJO_IP=$(kubectl get svc forgejo-http -n cicd -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "unknown") log "Forgejo available at: http://$FORGEJO_IP:3000 (or https://forgejo.riotpiao.com)" echo "" log "${GREEN}========================================${NC}" log "${GREEN}✅ Bootstrap Complete!${NC}" log "${GREEN}========================================${NC}" echo "" echo "Next steps:" echo "" echo " 1. Push this repo to Forgejo:" echo " git remote add forgejo https://forgejo.riotpiao.com/riotpiao.com/homelab.git" echo " git push forgejo main" echo "" echo " 2. Apply app-of-apps root:" echo " kubectl apply -k k8s/argocd/root" echo "" echo " 3. VERIFY STORAGE REPLICATION (CRITICAL!):" echo " See STORAGE-ARCHITECTURE-CLARIFICATION.md" echo " kubectl get nodes.longhorn.io -n longhorn-system" echo " kubectl get volumes.longhorn.io -n longhorn-system -o wide" echo "" echo " 4. Sync all applications:" echo " argocd app sync homelab-root" echo " # Or via UI: https://argocd.riotpiao.com" echo "" echo " 5. All future changes: git commit → git push (ArgoCD auto-syncs)" echo ""