Files
orca/scripts/orca-drift-notify.sh
Jon Chery 03f3585f16 feat(P10b): drift detection (R-018/R-019/R-020, REQ-103..113)
internal/drift/drift.go: Detector (Watch via iter.Seq2, Aggregate,
Remediate with cooldown-on-success, Acknowledge), Config with tiered
cadence (critical 5s + Path units, standard 30s, default 60s).
internal/cli/drift.go: orca drift {show,watch,acknowledge,remediate,
config}. internal/emitter/drift_path.go: systemd Path+service unit
emitter (User=orca, ProtectSystem=strict). scripts/orca-drift-notify.sh
(sha256 event JSON), orca-remediate.sh (cooldown-on-success, transient
retry). Pre-flight gate (R-020, --force + per-ns scoping). orca
system user (REQ-111), NFS detection (D-233), orca job restart for
EnvironmentFile drift (D-235).

---ci---
project: orca
phase: 10b
milestone: v0.11
status: execute
---/ci---
2026-08-07 07:17:41 +00:00

100 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# orca-drift-notify.sh — peer-side drift event recorder (P10b, REQ-106).
#
# Invoked by systemd Path units (orca-drift-<name>.service) when a
# critical path changes. Receives the changed path as $1 (from systemd
# %f). Computes sha256sum of the file (or "DELETED" if absent), reads
# the latest applied txn from /etc/orca/state/latest-applied-txn, and
# writes an event JSON to /etc/orca/state/drift-events/<event-id>.json.
# Uses flock for serialization. R-001-clean: pure bash + sha256sum.
#
# Usage: orca-drift-notify.sh <path>
#
# Exit codes: 0 = event recorded; 1 = bad args / write failure.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/orca-log.sh
. "$SCRIPT_DIR/lib/orca-log.sh"
ORCA_LOG_ACTOR="spiffe://orca/cli/drift-notify"
STATE_DIR="${ORCA_STATE_DIR:-/etc/orca/state}"
EVENTS_DIR="$STATE_DIR/drift-events"
LATEST_TXN_FILE="$STATE_DIR/latest-applied-txn"
LOCK_FILE="$STATE_DIR/drift-events.lock"
if [ "$#" -lt 1 ]; then
orca_log_error "drift-notify" "-" "failed" "missing path argument"
echo "usage: orca-drift-notify.sh <path>" >&2
exit 1
fi
PATH_ARG="$1"
mkdir -p "$EVENTS_DIR"
compute_sha() {
local p="$1"
if [ ! -e "$p" ]; then
echo "DELETED"
return
fi
sha256sum "$p" 2>/dev/null | awk '{print $1}' || echo "ERROR"
}
read_latest_txn() {
if [ -f "$LATEST_TXN_FILE" ]; then
cat "$LATEST_TXN_FILE" 2>/dev/null || true
fi
}
gen_event_id() {
local ts_us random_suffix
ts_us="$(date -u +%Y%m%d%H%M%S%6N)"
random_suffix="$(head -c 4 /dev/urandom 2>/dev/null | od -An -tx1 | tr -d ' \n' || echo "0000")"
echo "EVT-${ts_us}-${random_suffix}"
}
NEW_SHA="$(compute_sha "$PATH_ARG")"
LATEST_TXN="$(read_latest_txn || true)"
EVENT_ID="$(gen_event_id)"
TS="$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
HOST="$(hostname 2>/dev/null || echo unknown)"
if [ "$NEW_SHA" = "DELETED" ]; then
STATUS="deleted"
elif [ ! -f "$PATH_ARG" ]; then
STATUS="created"
else
STATUS="modified"
fi
escape_json() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
printf '%s' "$s"
}
PATH_ESC="$(escape_json "$PATH_ARG")"
HOST_ESC="$(escape_json "$HOST")"
EVENT_JSON=$(cat <<JSON
{"event_id":"$EVENT_ID","ts":"$TS","host":"$HOST_ESC","path":"$PATH_ESC","status":"$STATUS","new_sha256":"$NEW_SHA","latest_txn":"$LATEST_TXN","drift_confirmed":false,"action":"reported","action_result":"skipped"}
JSON
)
EVENT_FILE="$EVENTS_DIR/${EVENT_ID}.json"
(
flock 9 || { orca_log_error "drift-notify" "$PATH_ARG" "failed" "flock"; exit 1; }
printf '%s\n' "$EVENT_JSON" >"$EVENT_FILE.tmp"
mv "$EVENT_FILE.tmp" "$EVENT_FILE"
) 9>"$LOCK_FILE"
orca_log_info "drift-notify" "$PATH_ARG" "ok" "event=$EVENT_ID status=$STATUS sha=$NEW_SHA"
echo "event=$EVENT_ID"
exit 0