homelab/scripts/bootstrap.sh
2026-04-22 16:51:54 -07:00

147 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ─── Configuration ───────────────────────────────────────────────────────────
K3S_VERSION="v1.31.4+k3s1"
LONGHORN_VERSION="1.7.2"
LONGHORN_REPO="https://charts.longhorn.io"
# ─── Helper ──────────────────────────────────────────────────────────────────
info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
error() { printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2; exit 1; }
usage() {
cat <<EOF
Usage: $(basename "$0") <command>
Commands:
server Install k3s server (manager node)
agent Install k3s agent (worker node)
longhorn Install Longhorn via Helm
sops-keygen Generate an age keypair for SOPS
apply-secrets Decrypt and apply SOPS secrets to the cluster
deploy Helm-install all charts
all Run: server → longhorn → sops-keygen → apply-secrets → deploy
EOF
}
# ─── k3s server ──────────────────────────────────────────────────────────────
cmd_server() {
info "Installing k3s server ${K3S_VERSION}"
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="${K3S_VERSION}" sh -s - server \
--write-kubeconfig-mode 644
info "Waiting for node to be Ready …"
until kubectl get node "$(hostname)" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | grep -q True; do
sleep 2
done
info "Server node is Ready."
local token
token=$(cat /var/lib/rancher/k3s/server/node-token)
local ip
ip=$(hostname -I | awk '{print $1}')
info "Worker join command:"
echo " curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=\"${K3S_VERSION}\" K3S_URL=\"https://${ip}:6443\" K3S_TOKEN=\"${token}\" sh -"
}
# ─── k3s agent ───────────────────────────────────────────────────────────────
cmd_agent() {
if [[ -z "${K3S_URL:-}" || -z "${K3S_TOKEN:-}" ]]; then
error "Set K3S_URL and K3S_TOKEN environment variables first.\n K3S_URL=https://<manager-ip>:6443 K3S_TOKEN=<token> $0 agent"
fi
info "Installing k3s agent ${K3S_VERSION}"
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="${K3S_VERSION}" K3S_URL="${K3S_URL}" K3S_TOKEN="${K3S_TOKEN}" sh -
info "Agent installed. It will appear as a node shortly."
}
# ─── Longhorn ────────────────────────────────────────────────────────────────
cmd_longhorn() {
info "Installing Longhorn ${LONGHORN_VERSION} via Helm …"
helm repo add longhorn "${LONGHORN_REPO}" 2>/dev/null || true
helm repo update longhorn
kubectl create namespace longhorn-system 2>/dev/null || true
helm upgrade --install longhorn longhorn/longhorn \
--namespace longhorn-system \
--version "${LONGHORN_VERSION}" \
--set defaultSettings.defaultReplicaCount=2 \
--wait
info "Longhorn installed."
}
# ─── SOPS / age ─────────────────────────────────────────────────────────────
cmd_sops_keygen() {
local keydir="/etc/sops/age"
local keyfile="${keydir}/keys.txt"
if [[ -f "${keyfile}" ]]; then
warn "Age key already exists at ${keyfile} — skipping generation."
else
info "Generating age keypair …"
sudo mkdir -p "${keydir}"
age-keygen -o "${keyfile}" 2>&1
sudo chmod 600 "${keyfile}"
info "Key written to ${keyfile}"
fi
info "Public key (put this in .sops.yaml):"
grep 'public key' "${keyfile}" | awk '{print $NF}'
}
cmd_apply_secrets() {
local secrets_dir
secrets_dir="$(cd "$(dirname "$0")/.." && pwd)/secrets"
info "Decrypting and applying secrets …"
local decrypted
decrypted="$(sops -d "${secrets_dir}/secrets.enc.yaml")"
local ns
for ns in $(printf '%s\n' "${decrypted}" | grep '^\s*namespace:' | awk '{print $2}' | sort -u); do
kubectl create namespace "${ns}" 2>/dev/null || true
done
printf '%s\n' "${decrypted}" | kubectl apply -f -
info "Secrets applied."
}
# ─── Deploy all charts ──────────────────────────────────────────────────────
cmd_deploy() {
local charts_dir
charts_dir="$(cd "$(dirname "$0")/.." && pwd)/charts"
local -a chart_order=(traefik-config media paperless mealie dashboards utils headlamp gitea)
local -A chart_ns=(
[traefik-config]=kube-system
[media]=media
[paperless]=apps
[mealie]=apps
[dashboards]=apps
[utils]=apps
[headlamp]=apps
[gitea]=apps
)
for chart in "${chart_order[@]}"; do
local ns="${chart_ns[$chart]}"
info "Deploying ${chart} → namespace ${ns}"
kubectl create namespace "${ns}" 2>/dev/null || true
helm upgrade --install "${chart}" "${charts_dir}/${chart}" \
--namespace "${ns}" \
--wait --timeout 5m
done
info "All charts deployed."
}
# ─── Main ────────────────────────────────────────────────────────────────────
[[ $# -lt 1 ]] && { usage; exit 1; }
case "$1" in
server) cmd_server ;;
agent) cmd_agent ;;
longhorn) cmd_longhorn ;;
sops-keygen) cmd_sops_keygen ;;
apply-secrets) cmd_apply_secrets ;;
deploy) cmd_deploy ;;
all) cmd_server; cmd_longhorn; cmd_sops_keygen; cmd_apply_secrets; cmd_deploy ;;
*) usage; exit 1 ;;
esac