#!/bin/bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" [ -f "${SCRIPT_DIR}/env-surrealdb" ] && source "${SCRIPT_DIR}/env-surrealdb" [ -f "${SCRIPT_DIR}/_credentials.env" ] && source "${SCRIPT_DIR}/_credentials.env" SURREALDB_NAMESPACE="${SURREALDB_NAMESPACE:-data}" SURREALDB_IMAGE="${SURREALDB_IMAGE:-surrealdb/surrealdb:v3.0.5}" SURREALDB_PORT="${SURREALDB_PORT:-8000}" SURREALDB_DATA_DIR="${SURREALDB_DATA_DIR:-/data}" SURREALDB_STORAGE_CLASS="${SURREALDB_STORAGE_CLASS:-longhorn-retain}" SURREALDB_STORAGE_SIZE="${SURREALDB_STORAGE_SIZE:-2Gi}" KUBECONFIG="${KUBECONFIG:-/etc/kubernetes/admin.conf}" export KUBECONFIG CMD_TSK="${CMD_TSK:-${1:-install}}" _kubectl() { if command -v kubectl >/dev/null 2>&1; then command kubectl "$@" elif command -v k0s >/dev/null 2>&1; then k0s kubectl "$@" else echo "ERROR: no kubectl or k0s binary found" >&2; exit 127 fi } _require_env() { local var="$1" if [ -z "${!var:-}" ]; then echo "ERROR: required variable $var is not set" >&2 exit 1 fi } _namespace_exists() { _kubectl get namespace "$SURREALDB_NAMESPACE" >/dev/null 2>&1 } _pod_name() { _kubectl get pod \ --namespace "$SURREALDB_NAMESPACE" \ --selector "app.kubernetes.io/name=surrealdb" \ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null } _wait_for_pod() { local timeout="${1:-180}" _kubectl wait pod \ --namespace "$SURREALDB_NAMESPACE" \ --selector "app.kubernetes.io/name=surrealdb" \ --for=condition=Ready \ --timeout="${timeout}s" } _do_install() { _require_env SURREAL_USER _require_env SURREAL_PASS if ! _namespace_exists; then _kubectl create namespace "$SURREALDB_NAMESPACE" fi _kubectl create secret generic surrealdb-credentials \ --namespace "$SURREALDB_NAMESPACE" \ --from-literal=SURREAL_USER="$SURREAL_USER" \ --from-literal=SURREAL_PASS="$SURREAL_PASS" \ --dry-run=client -o yaml | _kubectl apply -f - for manifest in storageclass.yaml namespace.yaml pvc.yaml statefulset.yaml service.yaml; do local path="$SCRIPT_DIR/manifests/${manifest}" [ -f "$path" ] && _kubectl apply -f "$path" done echo "Waiting for SurrealDB pod to be ready..." _wait_for_pod 180 local post_install="$SCRIPT_DIR/manifests/post-install.sh" if [ -f "$post_install" ]; then bash "$post_install" fi echo "SurrealDB installed in namespace $SURREALDB_NAMESPACE" } _do_update() { _kubectl rollout restart statefulset/surrealdb --namespace "$SURREALDB_NAMESPACE" echo "Waiting for SurrealDB to restart..." _wait_for_pod 120 echo "SurrealDB updated" } _do_delete() { if _namespace_exists; then _kubectl delete namespace "$SURREALDB_NAMESPACE" --timeout=60s echo "Namespace $SURREALDB_NAMESPACE deleted" else echo "Namespace $SURREALDB_NAMESPACE does not exist, nothing to delete" fi } _do_backup() { _require_env BACKUP_DIR _require_env SURREAL_USER _require_env SURREAL_PASS local pod timestamp backup_file pod=$(_pod_name) timestamp=$(date +%Y%m%d_%H%M%S) backup_file="${BACKUP_DIR}/surrealdb_${timestamp}.surql.gz" mkdir -p "$BACKUP_DIR" _kubectl exec --namespace "$SURREALDB_NAMESPACE" "$pod" -- \ surreal export \ --endpoint "http://localhost:${SURREALDB_PORT}" \ --username "$SURREAL_USER" \ --password "$SURREAL_PASS" \ - | gzip > "$backup_file" echo "Backup written to $backup_file" } _do_restore() { _require_env BACKUP_FILE _require_env SURREAL_USER _require_env SURREAL_PASS local pod pod=$(_pod_name) if [[ "$BACKUP_FILE" == *.gz ]]; then gunzip -c "$BACKUP_FILE" | _kubectl exec --stdin --namespace "$SURREALDB_NAMESPACE" "$pod" -- \ surreal import \ --endpoint "http://localhost:${SURREALDB_PORT}" \ --username "$SURREAL_USER" \ --password "$SURREAL_PASS" \ - else _kubectl exec --stdin --namespace "$SURREALDB_NAMESPACE" "$pod" -- \ surreal import \ --endpoint "http://localhost:${SURREALDB_PORT}" \ --username "$SURREAL_USER" \ --password "$SURREAL_PASS" \ - < "$BACKUP_FILE" fi echo "Restore completed from $BACKUP_FILE" } _do_health() { local pod pod=$(_pod_name 2>/dev/null || true) if [ -z "$pod" ]; then echo "UNHEALTHY: no surrealdb pod found in namespace $SURREALDB_NAMESPACE" >&2 exit 1 fi local status_code status_code=$(_kubectl exec --namespace "$SURREALDB_NAMESPACE" "$pod" -- \ sh -c "wget -qO /dev/null -S http://localhost:${SURREALDB_PORT}/health 2>&1 | grep 'HTTP/' | awk '{print \$2}'" \ 2>/dev/null || echo "0") if [ "$status_code" = "200" ]; then echo "HEALTHY: surrealdb /health returned 200" else echo "UNHEALTHY: surrealdb /health returned $status_code" >&2 exit 1 fi } case "$CMD_TSK" in install) _do_install ;; update) _do_update ;; delete) _do_delete ;; backup) _do_backup ;; restore) _do_restore ;; health) _do_health ;; *) echo "ERROR: unknown CMD_TSK='$CMD_TSK'. Valid: install|update|delete|backup|restore|health" >&2 exit 1 ;; esac