Files
homelab/bootstrap.sh
T
Story Crater Bot dafccd5d72 feat: complete GitOps migration, storage HA verification, and cluster fixes
Major accomplishments from comprehensive cluster review:

## Storage HA (answering "are volumes replicated?")
- Verified 3-node Longhorn HA: ALL 17 volumes have 3 replicas
- Fixed CLAUDE.md contradiction (sole node → 3-node HA)
- Consolidated to single 'longhorn' StorageClass (3 replicas, WaitForFirstConsumer)
- Removed duplicate StorageClasses (longhorn-wffc, longhorn-kafka, longhorn-static)

## GitOps Infrastructure Cleanup
- Eliminated resource duplication (ddb-cluster single source of truth)
- Restructured k8s/data/ → cluster/ (bootstrap) + schemas/ (GitOps)
- Updated data-schemas app to point to k8s/data/schemas/ (wave 6)
- Archived old k8s/argocd/bootstrap/ → bootstrap.archived/

## Bootstrap Dependencies Fixed
- Added 05-wait-for-databases.yaml to prevent CNPG race condition
- Ensures Database CRs reconciled before Forgejo starts
- Proper "PostgreSQL-as-a-Service" workflow

## Longhorn CSI Plugin Fixed
- Added patch-csi-tolerations-job.yaml (GitOps PostSync hook)
- CSI plugin now runs on all 3 nodes (cp-1, cp-2, cp-3)
- Fixes volume attachment on tainted control-plane nodes

## Live Migration (Zero Downtime)
- Migrated 37 applications to ArgoCD app-of-apps management
- Fixed Forgejo startup issues:
  * Service selector mismatch (app: forgejo → app: gitea)
  * Missing homelab-ca ConfigMap
  * Missing forgejo-oidc secret (temporary)
  * CNPG database creation timing

## Documentation (10 comprehensive files)
- WHATS-NEXT.md - Daily GitOps workflow
- MIGRATION-STATUS.md - Cluster health report
- REVIEW-SUMMARY.md - Session overview
- GITOPS-REBUILD-PLAN.md - Architecture reference
- DDB-REVIEW.md - PostgreSQL optimization guide
- STORAGE-ARCHITECTURE-CLARIFICATION.md - Storage HA investigation
- BOOTSTRAP-DEPENDENCY-FIX.md - CNPG race condition fix
- STORAGECLASS-CONSOLIDATION.md - Single StorageClass rationale
- IMPLEMENTATION-CHECKLIST.md - Migration checklist
- bootstrap.sh - Automated bootstrap script

## Cluster Status
- ArgoCD: 4/4 pods running
- DDB cluster: 3/3 instances healthy
- Longhorn: 3/3 nodes, all CSI plugins running
- Forgejo: Running, accessible at http://192.168.1.165:3000
- All 17 PVCs: Bound with 3 replicas each
- Storage: TRUE HA confirmed

All future changes via git push only (100% GitOps).
2026-07-22 23:56:34 -07:00

131 lines
4.9 KiB
Bash
Executable File

#!/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 -
# 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 ""