Files
homelab/bootstrap.sh
T
Story Crater Bot 4ad4df7965 refactor(temporal): adopt unified CNPG pattern - use 'app' user
CHANGES:
  - temporal-values.yaml: user 'app', existingSecret 'ddb-cluster-app'
  - bootstrap.sh: Copy ddb-cluster-app to temporal namespace
  - Removed db-secret-sync directory (obsolete PostSync Job)
  - 60-applications.yaml: Removed db-secret-sync source from temporal Application

PATTERN (same as Forgejo/Authentik):
  1. Database CR: owner app
  2. bootstrap.sh: Copy ddb-cluster-app to temporal namespace
  3. App values: Reference ddb-cluster-app secret
  4. No PostSync Jobs needed

FIXES:
  - Temporal schema CrashLoopBackOff (wrong credentials)
  - Dropped/recreated databases with app owner (clean state)

Following CLAUDE.md CNPG pattern documentation.
2026-07-23 10:57:53 -07:00

143 lines
5.4 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 -
# 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 -
# Copy DB secret to temporal namespace (for temporal)
log "Copying ddb-cluster-app secret to temporal namespace..."
kubectl get secret ddb-cluster-app -n ddb -o yaml \
| sed 's/namespace: ddb/namespace: temporal/' \
| 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 ""