feat: exclusive pilot — spool flush and verify script
Replay spool on each monitor loop; sac_post_payload refactor; pilot-verify-exclusive.sh and --check-sac hints for exclusive mode.
This commit is contained in:
+74
-25
@@ -70,6 +70,74 @@ sac_spool_remove() {
|
||||
rm -f "${dir}/${event_id}.json"
|
||||
}
|
||||
|
||||
# POST готового JSON (ingest / повтор из spool). Возврат 0 при HTTP 202.
|
||||
sac_post_payload() {
|
||||
local payload="$1"
|
||||
local event_id http_code tmp resp event_type
|
||||
|
||||
if ! sac_is_configured; then
|
||||
return 1
|
||||
fi
|
||||
if [ "${DRY_RUN:-0}" = "1" ]; then
|
||||
printf '[DRY-RUN SAC POST] %s\n' "$(printf '%s' "$payload" | head -c 120)" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
event_id="$(printf '%s' "$payload" | python3 -c 'import json,sys; print(json.load(sys.stdin)["event_id"])')" || return 1
|
||||
event_type="$(printf '%s' "$payload" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("type","?"))')" || event_type="?"
|
||||
|
||||
tmp="$(mktemp)"
|
||||
resp="$(mktemp)"
|
||||
printf '%s' "$payload" >"$tmp"
|
||||
http_code="$(curl -sS -o "$resp" -w '%{http_code}' \
|
||||
-X POST "$(sac_ingest_url)" \
|
||||
-H "Authorization: Bearer ${SAC_API_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Idempotency-Key: ${event_id}" \
|
||||
--connect-timeout "${SAC_TIMEOUT_SEC:-12}" \
|
||||
--max-time "${SAC_TIMEOUT_SEC:-12}" \
|
||||
-d @"$tmp" 2>/dev/null || echo 000)"
|
||||
rm -f "$tmp"
|
||||
|
||||
if [ "$http_code" = "202" ]; then
|
||||
sac_spool_remove "$event_id" 2>/dev/null || true
|
||||
write_log "SAC: принято event_id=$event_id type=$event_type"
|
||||
return 0
|
||||
fi
|
||||
|
||||
write_log "WARN: SAC POST HTTP $http_code: $(tr '\n' ' ' <"$resp" | head -c 200)"
|
||||
sac_spool_write "$event_id" "$payload"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Повторная отправка файлов из SAC_SPOOL_DIR (до max_files за вызов).
|
||||
sac_flush_spool() {
|
||||
local mode max_files="${1:-20}"
|
||||
mode="$(sac_normalize_mode "${UseSAC:-off}")"
|
||||
case "$mode" in
|
||||
off) return 0 ;;
|
||||
esac
|
||||
if ! sac_is_configured; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local dir="${SAC_SPOOL_DIR:-/var/lib/ssh-monitor/sac-spool}"
|
||||
[ -d "$dir" ] || return 0
|
||||
|
||||
local f count=0
|
||||
shopt -s nullglob
|
||||
for f in "$dir"/*.json; do
|
||||
[ -f "$f" ] || continue
|
||||
count=$((count + 1))
|
||||
[ "$count" -gt "$max_files" ] && break
|
||||
local payload
|
||||
payload="$(cat "$f")"
|
||||
sac_post_payload "$payload" || true
|
||||
done
|
||||
shopt -u nullglob
|
||||
return 0
|
||||
}
|
||||
|
||||
# send_sac_event type severity title summary [details_json]
|
||||
sac_send_event() {
|
||||
local event_type="$1"
|
||||
@@ -146,31 +214,7 @@ print(json.dumps(payload, ensure_ascii=False))
|
||||
PY
|
||||
)" || return 1
|
||||
|
||||
event_id="$(printf '%s' "$payload" | python3 -c 'import json,sys; print(json.load(sys.stdin)["event_id"])')"
|
||||
|
||||
local tmp resp
|
||||
tmp="$(mktemp)"
|
||||
resp="$(mktemp)"
|
||||
printf '%s' "$payload" >"$tmp"
|
||||
http_code="$(curl -sS -o "$resp" -w '%{http_code}' \
|
||||
-X POST "$(sac_ingest_url)" \
|
||||
-H "Authorization: Bearer ${SAC_API_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Idempotency-Key: ${event_id}" \
|
||||
--connect-timeout "${SAC_TIMEOUT_SEC:-12}" \
|
||||
--max-time "${SAC_TIMEOUT_SEC:-12}" \
|
||||
-d @"$tmp" 2>/dev/null || echo 000)"
|
||||
rm -f "$tmp"
|
||||
|
||||
if [ "$http_code" = "202" ]; then
|
||||
sac_spool_remove "$event_id" 2>/dev/null || true
|
||||
write_log "SAC: принято event_id=$event_id type=$event_type"
|
||||
return 0
|
||||
fi
|
||||
|
||||
write_log "WARN: SAC POST HTTP $http_code: $(tr '\n' ' ' <"$resp" | head -c 200)"
|
||||
sac_spool_write "$event_id" "$payload"
|
||||
return 1
|
||||
sac_post_payload "$payload"
|
||||
}
|
||||
|
||||
# JSON details из переменных SAC_D_* (опционально)
|
||||
@@ -227,6 +271,11 @@ notify_or_sac() {
|
||||
run_check_sac() {
|
||||
printf 'Проверка SAC (ssh-monitor)\n'
|
||||
printf 'UseSAC=%s\n' "${UseSAC:-off}"
|
||||
case "$(sac_normalize_mode "${UseSAC:-off}")" in
|
||||
exclusive) printf 'Режим exclusive: уведомления только в SAC (Telegram/email с агента не отправляются)\n' ;;
|
||||
dual) printf 'Режим dual: SAC + локальные каналы NOTIFY_CHAIN\n' ;;
|
||||
fallback) printf 'Режим fallback: SAC, при сбое — NOTIFY_CHAIN\n' ;;
|
||||
esac
|
||||
printf 'SAC_URL=%s\n' "${SAC_URL:-}"
|
||||
if sac_is_configured; then
|
||||
printf 'SAC ingest URL=%s\n' "$(sac_ingest_url)"
|
||||
|
||||
Reference in New Issue
Block a user