#!/bin/bash # Checkmk local checks for dotnews-paas — installed to # /usr/lib/check_mk_agent/local/paas by bootstrap.sh. # # DYNAMIC: enumerates /srv/paas/apps.d/*.conf, so every `paas create` # automatically yields new Checkmk services at the next service discovery — # no monitoring-side configuration per project. # # Output format: " " REG=/srv/paas/apps.d DB=/srv/paas/paas.db NOW=$(date +%s) JL=$(sudo -u deploy -H bash -c 'cd /srv/paas && pm2 jlist' 2>/dev/null) # platform flags (PHP_DEFAULT for laravel apps without an explicit version) # shellcheck disable=SC1091 [ -f /srv/paas/paas.conf ] && . /srv/paas/paas.conf for conf in "$REG"/*.conf; do [ -e "$conf" ] || continue APP=''; PORT=''; DOMAINS=''; HEALTH_PATH=''; SYSTEM_DOMAIN='' RUNTIME=node; PHP_VERSION=''; QUEUE_WORKER=off # shellcheck disable=SC1090 . "$conf" [ -n "$APP" ] || continue # ---- app health (direct to the app port; works for cached + uncached) ---- read -r code t < <(curl -s -o /dev/null -m 5 -w '%{http_code} %{time_total}' \ "http://127.0.0.1:${PORT}${HEALTH_PATH:-/}" 2>/dev/null || echo "000 0") if [ "$code" = "200" ]; then st=0; else st=2; fi echo "$st paas_health_$APP time=${t}s HTTP $code from ${HEALTH_PATH:-/} on port $PORT" # ---- certificate expiry per domain (SNI against local Caddy) ---- for d in ${DOMAINS//,/ } ${SYSTEM_DOMAIN:-}; do end=$(echo | timeout 5 openssl s_client -servername "$d" -connect 127.0.0.1:443 2>/dev/null \ | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) svc="paas_cert_${APP}_${d//[.-]/_}" if [ -z "$end" ]; then echo "3 $svc - could not read certificate for $d" else days=$(( ( $(date -d "$end" +%s) - NOW ) / 86400 )) st=0; [ "$days" -lt 21 ] && st=1; [ "$days" -lt 7 ] && st=2 echo "$st $svc days=$days certificate for $d expires in $days days" fi done # ---- last deployment (CRIT on failed -> rides the existing alert chain) ---- row=$(sqlite3 "$DB" "SELECT status||'|'||COALESCE(substr(commit_sha,1,8),'-')||'|'||COALESCE(commit_msg, COALESCE(step,''))||'|'||started_at FROM deployments WHERE app='$APP' ORDER BY id DESC LIMIT 1;" 2>/dev/null) if [ -z "$row" ]; then echo "0 paas_deploy_$APP - no deployments yet" else IFS='|' read -r dstatus dsha dmsg dstart <<<"$row" case "$dstatus" in live) echo "0 paas_deploy_$APP - last deploy OK: $dsha ($dmsg) at $dstart UTC" ;; failed) echo "2 paas_deploy_$APP - LAST DEPLOY FAILED: $dsha ($dmsg) at $dstart UTC" ;; *) echo "0 paas_deploy_$APP - deployment in progress: $dstatus" ;; esac fi # ---- process state: pm2 (node) / php-fpm pool (laravel, + optional queue worker) ---- if [ "$RUNTIME" = "laravel" ]; then PV="${PHP_VERSION:-${PHP_DEFAULT:-8.3}}" if [ -S "/run/php/paas-$APP.sock" ] && systemctl is-active --quiet "php$PV-fpm"; then echo "0 paas_fpm_$APP - php$PV-fpm pool paas-$APP listening" elif systemctl is-active --quiet "php$PV-fpm"; then echo "2 paas_fpm_$APP - php$PV-fpm active but socket /run/php/paas-$APP.sock missing (pool not loaded?)" else echo "2 paas_fpm_$APP - php$PV-fpm NOT active" fi if [ "$QUEUE_WORKER" = "on" ]; then qst=$(echo "$JL" | jq -r --arg n "$APP-queue" '[.[]|select(.name==$n)][0].pm2_env.status // "absent"' 2>/dev/null) case "$qst" in online) echo "0 paas_queue_$APP - queue worker online" ;; absent) echo "1 paas_queue_$APP - queue worker enabled but no pm2 process" ;; *) echo "2 paas_queue_$APP - queue worker pm2 status: $qst" ;; esac fi else pmst=$(echo "$JL" | jq -r --arg n "$APP" '[.[]|select(.name==$n)][0].pm2_env.status // "absent"' 2>/dev/null) case "$pmst" in online) echo "0 paas_pm2_$APP - process online" ;; absent) echo "1 paas_pm2_$APP - no pm2 process registered" ;; *) echo "2 paas_pm2_$APP - pm2 status: $pmst" ;; esac fi # ---- 5xx today (from the traffic rollup) ---- s5=$(sqlite3 "$DB" "SELECT COALESCE(SUM(s5xx),0) FROM traffic_daily WHERE app='$APP' AND day=date('now');" 2>/dev/null) s5=${s5:-0} st=0; [ "$s5" -gt 50 ] && st=1; [ "$s5" -gt 200 ] && st=2 echo "$st paas_5xx_$APP errors=$s5 $s5 server errors today (rollup lag up to 5 min)" done # ---- platform-level ---- PLATFORM_SVCS="caddy varnish" # WITH_LARAVEL servers: every installed fpm master + mariadb for u in /etc/systemd/system/multi-user.target.wants/php*-fpm.service; do [ -e "$u" ] && PLATFORM_SVCS="$PLATFORM_SVCS $(basename "$u" .service)" done [ -f /srv/paas/db-admin.cnf ] && PLATFORM_SVCS="$PLATFORM_SVCS mariadb" for svc in $PLATFORM_SVCS; do if systemctl is-active --quiet "$svc"; then echo "0 paas_svc_$svc - service active" else echo "2 paas_svc_$svc - service NOT active" fi done # ---- database backups (WITH_LARAVEL servers with at least one dump) ---- if [ -f /srv/paas/db-admin.cnf ]; then newest=$(sudo -u deploy -H bash -c 'ls -1t /srv/paas/backups/db/*.sql.gz 2>/dev/null | head -1') if [ -n "$newest" ]; then age=$(( NOW - $(sudo -u deploy -H stat -c %Y "$newest") )) st=0; [ "$age" -gt 108000 ] && st=1; [ "$age" -gt 194400 ] && st=2 echo "$st paas_db_backup age=${age}s newest dump $(( age / 3600 ))h ago ($(basename "$newest"))" else echo "0 paas_db_backup - no app databases backed up yet" fi fi STATE_FILE=/srv/paas/logs/caddy/.rollup-state.json if [ -f "$STATE_FILE" ]; then age=$(( NOW - $(stat -c %Y "$STATE_FILE") )) st=0; [ "$age" -gt 900 ] && st=1; [ "$age" -gt 3600 ] && st=2 echo "$st paas_traffic_rollup age=${age}s last rollup ${age}s ago" else echo "1 paas_traffic_rollup - no rollup state yet" fi