Files
customer-installer/libsupabase.sh
2026-01-11 12:36:12 +01:00

176 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# libsupabase.sh
set -Eeuo pipefail
# ---------- logging (ALLES nach STDERR) ----------
_ts() { date '+%F %T'; }
_log() {
local level="$1"; shift
# stderr, damit stdout sauber bleibt (für JSON am Ende)
echo "[$(_ts)] ${level}: $*" >&2
}
info() { _log "INFO" "$*"; }
warn() { _log "WARN" "$*"; }
err() { _log "ERROR" "$*"; }
die() {
err "$*"
exit 1
}
need_cmd() {
local c
for c in "$@"; do
command -v "$c" >/dev/null 2>&1 || die "Missing command: $c"
done
}
on_error() {
local lineno="$1" cmd="$2" code="$3"
err "Failed at line ${lineno}: ${cmd} (exit=${code})"
exit 1
}
setup_traps() {
trap 'on_error "${LINENO}" "${BASH_COMMAND}" "$?"' ERR
}
# ---------- helpers ----------
is_int() { [[ "${1:-}" =~ ^[0-9]+$ ]]; }
# pct exec wrapper
# - setzt Locale für apt/Perl stabil auf C.UTF-8 während Provisioning
pct_exec() {
local ctid="$1"; shift
need_cmd pct
# shellcheck disable=SC2029
pct exec "${ctid}" -- bash -lc "export LANG=C.UTF-8 LC_ALL=C.UTF-8; $*"
}
# wait for IP via pct (DHCP)
pct_wait_for_ip() {
local ctid="$1"
need_cmd pct awk grep
local i ip
for i in {1..60}; do
# pct exec ip addr ist zuverlässiger als pct config parsing
ip="$(pct exec "${ctid}" -- bash -lc "ip -4 -o addr show scope global | awk '{print \$4}' | head -n1 | cut -d/ -f1" 2>/dev/null || true)"
if [[ -n "${ip}" ]]; then
echo "${ip}"
return 0
fi
sleep 1
done
return 1
}
# Proxmox checks
pve_storage_exists() {
local st="$1"
need_cmd pvesm
pvesm status --storage "${st}" >/dev/null 2>&1
}
pve_bridge_exists() {
local br="$1"
[[ -d "/sys/class/net/${br}/bridge" ]]
}
# Build net0 string (supports optional VLAN tag)
pve_build_net0() {
local bridge="$1"
local ipcfg="$2" # dhcp OR CIDR
local vlan="${3:-}" # optional integer
local net="name=eth0,bridge=${bridge}"
if [[ -n "${vlan}" ]]; then
is_int "${vlan}" || die "--vlan must be integer"
net="${net},tag=${vlan}"
fi
if [[ "${ipcfg}" == "dhcp" ]]; then
net="${net},ip=dhcp"
else
net="${net},ip=${ipcfg}"
fi
echo "${net}"
}
# Template ensure (stdout darf NUR den Template-Pfad liefern!)
pve_template_ensure_debian12() {
local preferred_store="$1"
local tpl="debian-12-standard_12.12-1_amd64.tar.zst"
need_cmd pveam awk grep
local store="${preferred_store}"
# pveam kann oft nur "local"
if ! pveam list "${store}" >/dev/null 2>&1; then
warn "pveam storage '${store}' not available for templates; falling back to 'local'"
store="local"
fi
# update list
pveam update >/dev/null 2>&1 || true
# download if missing
if ! pveam list "${store}" 2>/dev/null | awk '{print $2}' | grep -qx "${tpl}"; then
info "Downloading CT template to ${store}: ${tpl}"
pveam download "${store}" "${tpl}" >/dev/null
fi
echo "${store}:vztmpl/${tpl}"
}
# ---- CTID selection (customer-safe): (unix_time - 1_000_000_000) ----
# Keine Cluster-Abfrage mehr nötig. Nur lokale Node-Check, + increment falls belegt.
pve_select_customer_ctid() {
need_cmd pct date
local base
base="$(($(date +%s) - 1000000000))"
# falls negative (sollte nicht passieren), fallback
if (( base < 100 )); then
base=100000
fi
local ctid="${base}"
# nur lokal prüfen (pct list ist node-lokal)
while pct status "${ctid}" >/dev/null 2>&1; do
ctid="$((ctid + 1))"
done
echo "${ctid}"
}
# Secrets ohne tr/head Pipes (kein Broken pipe)
gen_hex() {
local nbytes="$1"
is_int "${nbytes}" || die "gen_hex expects integer bytes"
need_cmd openssl
openssl rand -hex "${nbytes}"
}
# JSON escaper (minimal, reicht für unsere Werte)
json_escape() {
local s="${1:-}"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//$'\n'/\\n}"
s="${s//$'\r'/\\r}"
s="${s//$'\t'/\\t}"
echo -n "${s}"
}
# Print a JSON object (stdout). Use at the VERY end.
print_json_kv() {
# args: key value
local k="$1" v="$2"
echo -n "\"$(json_escape "$k")\":\"$(json_escape "$v")\""
}