#!/usr/bin/env bash # Gemeinsame Konstanten und Hilfsfunktionen der deploy-Skripte (wird gesourced). APP_USER=checkbot APP_GROUP=checkbot APP_DIR=/opt/checkbot CONF_DIR=/etc/checkbot DATA_DIR=/var/lib/checkbot BACKUP_DIR=/var/backups/checkbot ENV_FILE=$CONF_DIR/checkbot.env VENV_DIR=$APP_DIR/.venv SERVICE_NAME=checkbot SYSTEMD_DIR=/etc/systemd/system UNIT_FILES=(checkbot.service checkbot-backup.service checkbot-backup.timer) DEFAULT_DB_FILE=$DATA_DIR/checkbot.db DEFAULT_BIND_HOST=127.0.0.1 DEFAULT_BIND_PORT=8080 MIN_PYTHON="3.11" HEALTH_WAIT_SECONDS=60 HEALTH_CURL_TIMEOUT=15 DEPLOYED_REV_FILE=$DATA_DIR/deployed-rev COMMON_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) DEPLOY_DIR=$(dirname "$COMMON_DIR") REPO_DIR=$(dirname "$DEPLOY_DIR") shopt -u patsub_replacement 2>/dev/null || true log() { printf '[checkbot] %s\n' "$*"; } warn() { printf '[checkbot] WARNUNG: %s\n' "$*" >&2; } die() { printf '[checkbot] FEHLER: %s\n' "$*" >&2; exit 1; } require_root() { [[ $EUID -eq 0 ]] || die "Bitte als root ausführen." } env_value() { local key=$1 default=$2 line value="" if [[ -r $ENV_FILE ]]; then while IFS= read -r line || [[ -n $line ]]; do if [[ $line == "$key="* ]]; then value=${line#*=}; fi done < "$ENV_FILE" fi value=${value%\"} value=${value#\"} printf '%s' "${value:-$default}" } db_file() { env_value CHECKBOT_DB "$DEFAULT_DB_FILE"; } health_url() { local host port host=$(env_value CHECKBOT_BIND_HOST "$DEFAULT_BIND_HOST") port=$(env_value CHECKBOT_BIND_PORT "$DEFAULT_BIND_PORT") case $host in 0.0.0.0|::|"") host=127.0.0.1 ;; esac [[ $host == *:* ]] && host="[$host]" printf 'http://%s:%s/health' "$host" "$port" } check_python_version() { python3 -c "import sys; sys.exit(0 if sys.version_info >= tuple(map(int, '$MIN_PYTHON'.split('.'))) else 1)" \ || die "Python $MIN_PYTHON oder neuer wird benötigt (Debian 12 oder 13)." } sync_code() { [[ -f $REPO_DIR/requirements.txt && -d $REPO_DIR/app ]] \ || die "Kein checkbot-Checkout unter $REPO_DIR (requirements.txt oder app/ fehlt)." [[ $(realpath "$REPO_DIR") != "$(realpath -m "$APP_DIR")" ]] \ || die "Das Skript läuft bereits aus $APP_DIR. Bitte aus dem Git-Checkout starten." install -d -m 0755 -o root -g root "$APP_DIR" rsync -a --delete --chown=root:root --chmod='u=rwX,go=rX' \ --exclude='.git' --exclude='/tests' --exclude='/.venv' --exclude='__pycache__' \ --exclude='*.pyc' --exclude='.pytest_cache' \ "$REPO_DIR/" "$APP_DIR/" chmod 0755 "$APP_DIR"/deploy/*.sh "$APP_DIR/deploy/checkbot-cli" "$APP_DIR/deploy/checkmk/checkbot-local-check" } ensure_venv() { if [[ ! -x $VENV_DIR/bin/python ]]; then log "Lege virtuelle Umgebung an." python3 -m venv "$VENV_DIR" fi log "Installiere Python-Abhängigkeiten." PYTHONDONTWRITEBYTECODE=1 "$VENV_DIR/bin/pip" install --disable-pip-version-check --quiet \ -r "$APP_DIR/requirements.txt" } install_units() { local unit changed=0 for unit in "${UNIT_FILES[@]}"; do if ! cmp -s "$DEPLOY_DIR/systemd/$unit" "$SYSTEMD_DIR/$unit"; then install -m 0644 -o root -g root "$DEPLOY_DIR/systemd/$unit" "$SYSTEMD_DIR/$unit" changed=1 fi done if (( changed )); then systemctl daemon-reload log "systemd-Units aktualisiert." fi } HEALTH_EVAL=' import json, sys try: checks = json.load(sys.stdin)["checks"] db_ok = bool(checks["db"]["ok"]) except Exception: sys.exit(2) print(" ".join(sorted(n for n, c in checks.items() if not c.get("ok")))) sys.exit(0 if db_ok else 1) ' # /health liefert 503, solange nicht alles konfiguriert ist; der Start gilt als gelungen, wenn die DB-Prüfung ok ist. wait_health() { local url body bad deadline=$((SECONDS + HEALTH_WAIT_SECONDS)) url=$(health_url) while (( SECONDS < deadline )); do if body=$(curl -sS --max-time "$HEALTH_CURL_TIMEOUT" "$url" 2>/dev/null) \ && bad=$(printf '%s' "$body" | python3 -I -c "$HEALTH_EVAL"); then if [[ -z $bad ]]; then log "Health-Check ok ($url)." else log "Bot läuft, DB ok. Teilchecks nicht ok: $bad (vor der Einrichtung normal, Details in der UI oder per curl $url)." fi return 0 fi sleep 2 done return 1 } latest_backup() { find "$BACKUP_DIR" -maxdepth 1 -type f -name 'checkbot-*.db' 2>/dev/null | sort | tail -n 1 }