feat: supaspin, on-demand throwaway Supabase instances on microk8s
Adds a terminal app that creates, lists, shows and deletes numbered Supabase instances on a single-node microk8s server, for testing and staging copies of any app. Nothing in it is specific to one app. - supaspin: single-file Python app (menu and commands: create, list, show, env, tunnel, delete, mail, setup). Instance N gets namespace supaspin-N, hostname slot N, Postgres on <LAN IP>:544N and Studio on <LAN IP>:300N. Lowest free number is reused; a refused create says why. - Disk: fixed-size ext4 image files, loop-mounted from /etc/fstab. db 2 GB, storage 1 GB, functions/snippets/deno/pgsodium 256 MB each. imgproxy mounts the storage volume (the chart's own imgproxy volume cannot see uploads, so image resizing failed). Empty mount points are made read-only until mounted so a pod started early after a reboot cannot initialise a database there. - Memory: limits 5.625 GiB, requests 1.875 GiB per instance, all 12 services. Half of a production setup, except analytics (768 MiB) and realtime (384 MiB), which were killed for running out of memory at half of it. - Priority class supaspin-low (-100, never preempts) on every instance pod. - Public HTTPS through the existing nginx ingress with Let's Encrypt. The Studio and pg-meta routes are cut out of the Kong gateway config by a post-renderer, which also pins the db and studio pods to fixed host ports. - Certificates: delete keeps the TLS secret (with its cert-manager issuer annotations) in namespace supaspin-certs and create restores it, so create/delete cycles do not hit Let's Encrypt's 5-per-week limit. - Secrets generated per instance: JWT secret, anon and service keys, Postgres and Studio passwords, realtime/meta/logflare keys. The rendered values file is removed after install; secrets are never written through kubectl apply. - Instance .env (mode 600) with the usual Supabase variable names, plus an SSH tunnel command and DATABASE_URL help. - Delete is idempotent and finds the real mounts instead of trusting a list, so half-created and older instances clean up completely. A failed create offers to run it. - Mail settings: saved defaults changeable from the menu; create asks to use them or enter different ones for that instance. - install.sh: installs the app; accepts a Gitea token or a local checkout because git.didgibot.com requires sign-in even for public repositories. uninstall.sh: removes the app and, after confirmation, every instance. - tests: 30 unittest cases (numbering, ports, JWT signing, values, .env, fstab handling, post-renderer, certificate secret handling). - README: install, first-run setup, menu, tunnel, troubleshooting, uninstall. Pinned chart: supabase-community/supabase-kubernetes 0.8.0 (Postgres 17.6.1.136). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #1.
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
11802ce8cd
commit
cfbc4f1612
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-line install (run on the Kubernetes server):
|
||||
# curl -fsSL https://git.didgibot.com/igor/supaspin/raw/branch/main/install.sh | sudo bash
|
||||
# Installs /usr/local/bin/supaspin and its Helm values template. Safe to run again to upgrade.
|
||||
#
|
||||
# If your Gitea requires sign-in even for public repositories, give a read-only token:
|
||||
# curl -fsSL -H "Authorization: token $TOKEN" .../install.sh | sudo SUPASPIN_TOKEN=$TOKEN bash
|
||||
# or clone the repository and run `sudo bash install.sh` from inside the checkout.
|
||||
#
|
||||
# SUPASPIN_REF=<branch or tag> installs that version instead of main
|
||||
# SUPASPIN_SRC=<folder> installs from a local checkout instead of downloading
|
||||
# SUPASPIN_TOKEN=<token> Gitea token (read access to the repository) for the download
|
||||
set -euo pipefail
|
||||
|
||||
REPO="https://git.didgibot.com/igor/supaspin"
|
||||
REF="${SUPASPIN_REF:-main}"
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || { echo "Run as root: curl -fsSL $REPO/raw/branch/main/install.sh | sudo bash" >&2; exit 1; }
|
||||
|
||||
# python3 and its YAML module are the only runtime dependencies.
|
||||
command -v python3 >/dev/null || { echo "python3 is required" >&2; exit 1; }
|
||||
if ! python3 -c 'import yaml' 2>/dev/null; then
|
||||
echo "Installing python3-yaml with apt..."
|
||||
apt-get install -y python3-yaml
|
||||
fi
|
||||
|
||||
# kubectl and helm: the microk8s snap commands, or plain ones.
|
||||
if [ ! -x /snap/bin/microk8s.kubectl ] && ! command -v kubectl >/dev/null; then
|
||||
echo "kubectl not found (on microk8s: sudo snap install microk8s --classic)" >&2; exit 1
|
||||
fi
|
||||
if [ ! -x /snap/bin/microk8s.helm3 ] && ! command -v helm >/dev/null; then
|
||||
echo "helm not found (on microk8s: sudo microk8s enable helm3)" >&2; exit 1
|
||||
fi
|
||||
|
||||
# Where to install from: an explicit folder, the checkout this script sits in, or a download.
|
||||
here="$(cd "$(dirname "${BASH_SOURCE[0]:-/nonexistent}")" 2>/dev/null && pwd || true)"
|
||||
if [ -n "${SUPASPIN_SRC:-}" ]; then
|
||||
src="$SUPASPIN_SRC"
|
||||
elif [ -n "$here" ] && [ -f "$here/supaspin" ] && [ -f "$here/templates/values.yaml.tpl" ]; then
|
||||
src="$here"
|
||||
else
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
||||
echo "Downloading supaspin ($REF)..."
|
||||
auth=()
|
||||
[ -n "${SUPASPIN_TOKEN:-}" ] && auth=(-H "Authorization: token $SUPASPIN_TOKEN")
|
||||
curl -fsSL "${auth[@]}" -o "$tmp/src.tar.gz" "$REPO/archive/$REF.tar.gz" || true
|
||||
if ! gzip -t "$tmp/src.tar.gz" 2>/dev/null; then
|
||||
echo "Could not download $REPO/archive/$REF.tar.gz." >&2
|
||||
echo "If the server asks for sign-in, rerun with SUPASPIN_TOKEN=<read-only token>," >&2
|
||||
echo "or clone the repository and run 'sudo bash install.sh' inside it." >&2
|
||||
exit 1
|
||||
fi
|
||||
tar -xzf "$tmp/src.tar.gz" -C "$tmp"
|
||||
src="$(find "$tmp" -mindepth 1 -maxdepth 1 -type d | head -1)"
|
||||
fi
|
||||
|
||||
for f in supaspin templates/values.yaml.tpl uninstall.sh; do
|
||||
[ -f "$src/$f" ] || { echo "$src/$f is missing: not a supaspin checkout" >&2; exit 1; }
|
||||
done
|
||||
|
||||
install -m 0755 "$src/supaspin" /usr/local/bin/supaspin
|
||||
install -d -m 0755 /usr/share/supaspin/templates
|
||||
install -m 0644 "$src/templates/values.yaml.tpl" /usr/share/supaspin/templates/values.yaml.tpl
|
||||
install -m 0755 "$src/uninstall.sh" /usr/share/supaspin/uninstall.sh
|
||||
install -d -m 0700 /etc/supaspin /var/lib/supaspin
|
||||
|
||||
echo "Installed supaspin $(/usr/local/bin/supaspin --version)."
|
||||
echo "Next: run 'sudo supaspin' - the first run asks for your settings."
|
||||
echo "To remove it later: sudo bash /usr/share/supaspin/uninstall.sh"
|
||||
Reference in New Issue
Block a user