Files
customer-installer/libsupabase.sh
T

158 lines
4.0 KiB
Bash
Raw Normal View History

2026-01-09 17:12:49 +01:00
#!/usr/bin/env bash
set -Eeuo pipefail
2026-01-09 20:09:29 +01:00
# ---------------------------
# Logging / helpers
# ---------------------------
2026-01-09 17:12:49 +01:00
_ts() { date '+%F %T'; }
2026-01-09 20:09:29 +01:00
_is_tty() { [[ -t 1 ]]; }
2026-01-09 18:53:26 +01:00
log() { echo "[$(_ts)] $*" >&2; }
2026-01-09 17:12:49 +01:00
info() { log "INFO: $*"; }
warn() { log "WARN: $*"; }
die() { log "ERROR: $*"; exit 1; }
need_cmd() {
2026-01-09 18:53:26 +01:00
local c
2026-01-09 17:12:49 +01:00
for c in "$@"; do
command -v "$c" >/dev/null 2>&1 || die "Missing command: $c"
done
}
2026-01-09 18:53:26 +01:00
on_error() {
local lineno="$1" cmd="$2" code="$3"
die "Failed at line ${lineno}: ${cmd} (exit=${code})"
}
setup_traps() {
trap 'on_error "${LINENO}" "${BASH_COMMAND}" "$?"' ERR
}
2026-01-09 20:09:29 +01:00
# ---------------------------
2026-01-09 17:12:49 +01:00
# Proxmox helpers
2026-01-09 20:09:29 +01:00
# ---------------------------
2026-01-09 18:53:26 +01:00
pve_storage_exists() {
local st="$1"
pvesm status --storage "$st" >/dev/null 2>&1
}
2026-01-09 17:12:49 +01:00
pve_bridge_exists() {
2026-01-09 18:53:26 +01:00
local br="$1"
[[ -d "/sys/class/net/${br}/bridge" ]]
2026-01-09 17:12:49 +01:00
}
2026-01-09 20:09:29 +01:00
# Ensure we return ONLY the template reference on stdout.
# All logs go to stderr to avoid contaminating command substitution.
2026-01-09 17:12:49 +01:00
pve_template_ensure_debian12() {
2026-01-09 20:09:29 +01:00
local preferred_store="$1"
2026-01-09 18:53:26 +01:00
local tpl="debian-12-standard_12.12-1_amd64.tar.zst"
2026-01-09 20:09:29 +01:00
local store="$preferred_store"
need_cmd pveam awk grep
2026-01-09 17:12:49 +01:00
2026-01-09 20:09:29 +01:00
# Some storages (e.g. local-zfs) are not template storages for pveam.
2026-01-09 17:12:49 +01:00
if ! pveam list "$store" >/dev/null 2>&1; then
2026-01-09 20:09:29 +01:00
warn "pveam storage '${store}' not available for templates; falling back to 'local'"
2026-01-09 17:12:49 +01:00
store="local"
fi
2026-01-09 20:09:29 +01:00
# Update list and download if missing
2026-01-09 18:53:26 +01:00
pveam update >/dev/null 2>&1 || true
2026-01-09 20:09:29 +01:00
if ! pveam list "$store" | awk '{print $2}' | grep -qx "$tpl"; then
2026-01-09 18:53:26 +01:00
info "Downloading CT template to ${store}: ${tpl}"
pveam download "$store" "$tpl" >/dev/null
2026-01-09 17:12:49 +01:00
fi
2026-01-09 18:53:26 +01:00
echo "${store}:vztmpl/${tpl}"
2026-01-09 17:12:49 +01:00
}
2026-01-09 18:53:26 +01:00
pve_build_net0() {
2026-01-09 20:09:29 +01:00
local bridge="$1" ip="$2"
2026-01-09 18:53:26 +01:00
if [[ "$ip" == "dhcp" ]]; then
echo "name=eth0,bridge=${bridge},ip=dhcp"
else
2026-01-09 20:09:29 +01:00
echo "name=eth0,bridge=${bridge},ip=${ip},gw=$(echo "$ip" | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+)\.[0-9]+\/[0-9]+/\1.1/')"
2026-01-09 18:53:26 +01:00
fi
}
2026-01-09 17:12:49 +01:00
2026-01-09 20:09:29 +01:00
# Execute inside CT with sane locale env to avoid perl warnings mid-run.
pct_exec() {
local ctid="$1"; shift
local cmd="$*"
2026-01-09 18:53:26 +01:00
need_cmd pct
2026-01-09 20:09:29 +01:00
pct exec "$ctid" -- bash -lc "export LANG=C.UTF-8 LC_ALL=C.UTF-8; ${cmd}"
2026-01-09 18:53:26 +01:00
}
2026-01-09 17:12:49 +01:00
2026-01-09 20:09:29 +01:00
pct_wait_for_ip() {
2026-01-09 18:53:26 +01:00
local ctid="$1"
2026-01-09 20:09:29 +01:00
local i ip
2026-01-09 18:53:26 +01:00
2026-01-09 20:09:29 +01:00
for i in {1..60}; do
# Try eth0 first (standard CT)
ip="$(pct exec "$ctid" -- bash -lc "ip -4 -o addr show dev eth0 2>/dev/null | awk '{print \$4}' | cut -d/ -f1 | head -n1" 2>/dev/null || true)"
2026-01-09 18:53:26 +01:00
if [[ -n "$ip" ]]; then
echo "$ip"
return 0
2026-01-09 17:12:49 +01:00
fi
2026-01-09 20:09:29 +01:00
sleep 1
2026-01-09 17:12:49 +01:00
done
2026-01-09 18:53:26 +01:00
return 1
2026-01-09 17:12:49 +01:00
}
2026-01-09 20:09:29 +01:00
# ---------------------------
# Cluster VMID discovery (robust)
# ---------------------------
# Outputs one VMID per line (containers + VMs), cluster-wide if possible.
pve_cluster_vmids() {
need_cmd pvesh awk sed grep
# Try cluster resources; output-format json gives stable machine output.
# If pvesh fails (no cluster or temporary glitch), fall back to local IDs.
if pvesh get /cluster/resources --output-format json >/dev/null 2>&1; then
# No python/jq parsing. Extract "vmid": <num> safely via awk/grep.
pvesh get /cluster/resources --output-format json \
| tr -d '\n' \
| sed 's/},{/}\n{/g' \
| grep -oE '"vmid":[0-9]+' \
| grep -oE '[0-9]+' \
| sort -n -u
return 0
fi
# Fallback: local node only
{
pct list 2>/dev/null | awk 'NR>1 {print $1}'
qm list 2>/dev/null | awk 'NR>1 {print $1}'
} | sort -n -u
2026-01-09 18:53:26 +01:00
}
2026-01-09 17:12:49 +01:00
2026-01-09 20:09:29 +01:00
pve_vmid_exists_cluster() {
local id="$1"
pve_cluster_vmids | awk -v x="$id" '$0==x {found=1} END{exit(found?0:1)}'
}
# Customer CTID: unix_time - 1_000_000_000 (good until 2038),
# then increment until free cluster-wide.
pve_select_customer_ctid() {
local base now ctid tries
now="$(date +%s)"
base=$(( now - 1000000000 ))
# keep it within a sane positive range
if (( base < 100 )); then
base=100
fi
ctid="$base"
for tries in {1..2000}; do
if ! pve_vmid_exists_cluster "$ctid"; then
echo "$ctid"
return 0
fi
ctid=$((ctid + 1))
done
die "CTID selection failed: tried ${base}..$((base+1999)) (all occupied?)"
2026-01-09 17:12:49 +01:00
}