03f3585f16
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---
100 lines
2.7 KiB
Bash
Executable File
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
|