176 lines
3.9 KiB
Bash
Executable File
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")\""
|
|
}
|