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---
168 lines
5.3 KiB
Bash
168 lines
5.3 KiB
Bash
#!/usr/bin/env bats
|
|
# Tests for scripts/orca-pull.sh — the C-09 failure contract + C-23
|
|
# cluster-wide vs namespace-scoped distinction.
|
|
|
|
load test_helper
|
|
|
|
PULL="$SCRIPTS_DIR/orca-pull.sh"
|
|
TMP_TXN=""
|
|
|
|
setup() {
|
|
TMP_TXN="$(mktemp -d)"
|
|
# Minimal apply/verify/rollback scripts that succeed.
|
|
cat >"$TMP_TXN/apply.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
touch "$TMP_TXN/.applied-marker"
|
|
exit 0
|
|
EOF
|
|
# A real marker the orca-pull.sh checks for.
|
|
cat >"$TMP_TXN/apply.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
touch "$(dirname "$0")/.applied"
|
|
exit 0
|
|
EOF
|
|
cat >"$TMP_TXN/verify.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
EOF
|
|
cat >"$TMP_TXN/rollback.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
rm -f "$(dirname "$0")/.applied"
|
|
exit 0
|
|
EOF
|
|
chmod +x "$TMP_TXN"/*.sh
|
|
}
|
|
|
|
teardown() {
|
|
[ -n "$TMP_TXN" ] && rm -rf "$TMP_TXN"
|
|
}
|
|
|
|
@test "orca-pull.sh exists and is executable" {
|
|
[ -f "$PULL" ]
|
|
[ -x "$PULL" ]
|
|
}
|
|
|
|
@test "orca-pull.sh refuses without --txn-dir (exit 4)" {
|
|
run "$PULL" --force --i-understand-the-risk
|
|
assert_status 4 "$status"
|
|
}
|
|
|
|
@test "orca-pull.sh cluster-wide without --force exits 4 (C-23)" {
|
|
run "$PULL" --txn-dir "$TMP_TXN"
|
|
assert_status 4 "$status"
|
|
assert_contains "$output" "requires --force"
|
|
}
|
|
|
|
@test "orca-pull.sh cluster-wide with --force but no ack exits 4 (C-23)" {
|
|
run "$PULL" --txn-dir "$TMP_TXN" --force
|
|
assert_status 4 "$status"
|
|
assert_contains "$output" "--i-understand-the-risk"
|
|
}
|
|
|
|
@test "orca-pull.sh cluster-wide with --force --i-understand-the-risk applies" {
|
|
run "$PULL" --txn-dir "$TMP_TXN" --force --i-understand-the-risk
|
|
assert_status 0 "$status"
|
|
assert_contains "$output" "applied"
|
|
[ -f "$TMP_TXN/.applied" ]
|
|
}
|
|
|
|
@test "orca-pull.sh namespace-scoped applies without --force (C-23)" {
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 0 "$status"
|
|
assert_contains "$output" "applied"
|
|
[ -f "$TMP_TXN/.applied" ]
|
|
}
|
|
|
|
@test "orca-pull.sh idempotent re-run exits 5 (already-applied)" {
|
|
# First apply.
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 0 "$status"
|
|
# Re-run: should be a no-op (exit 5).
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 5 "$status"
|
|
assert_contains "$output" "already-applied"
|
|
}
|
|
|
|
@test "orca-pull.sh apply failure runs rollback and exits 1" {
|
|
# Make apply.sh fail.
|
|
cat >"$TMP_TXN/apply.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 1
|
|
EOF
|
|
chmod +x "$TMP_TXN/apply.sh"
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
@test "orca-pull.sh verify failure runs rollback and exits 2" {
|
|
# apply succeeds, verify fails.
|
|
cat >"$TMP_TXN/verify.sh" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 1
|
|
EOF
|
|
chmod +x "$TMP_TXN/verify.sh"
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
[ "$status" -eq 2 ]
|
|
}
|
|
|
|
@test "orca-pull.sh missing bundle file exits 4" {
|
|
rm -f "$TMP_TXN/verify.sh"
|
|
run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 4 "$status"
|
|
}
|
|
|
|
@test "orca-pull.sh drift gate refuses with exit 6 when drift detected (R-020)" {
|
|
# Create a drift-events-aggregated.json with confirmed drift.
|
|
mkdir -p "$TMP_TXN/state"
|
|
DRIFT_JSON="$TMP_TXN/state/drift-events-aggregated.json"
|
|
cat >"$DRIFT_JSON" <<EOF
|
|
{"ts":"2026-01-01T00:00:00Z","events":[{"event_id":"E1","host":"p","path":"/etc/orca/actual/default/x","status":"modified","drift_confirmed":true}]}
|
|
EOF
|
|
ORCA_DRIFT_AGG_JSON="$DRIFT_JSON" run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 6 "$status"
|
|
assert_contains "$output" "drift detected"
|
|
}
|
|
|
|
@test "orca-pull.sh drift gate passes when no drift" {
|
|
# Empty aggregated doc -> no drift -> applies.
|
|
mkdir -p "$TMP_TXN/state"
|
|
DRIFT_JSON="$TMP_TXN/state/drift-events-aggregated.json"
|
|
echo '{"ts":"2026-01-01T00:00:00Z","events":[]}' >"$DRIFT_JSON"
|
|
ORCA_DRIFT_AGG_JSON="$DRIFT_JSON" run "$PULL" --txn-dir "$TMP_TXN" --namespace default
|
|
assert_status 0 "$status"
|
|
}
|
|
|
|
@test "orca-pull.sh drift gate --force overrides (R-020)" {
|
|
# Create drift, then apply with --force --i-understand-the-risk.
|
|
mkdir -p "$TMP_TXN/state"
|
|
DRIFT_JSON="$TMP_TXN/state/drift-events-aggregated.json"
|
|
cat >"$DRIFT_JSON" <<EOF
|
|
{"ts":"2026-01-01T00:00:00Z","events":[{"event_id":"E1","host":"p","path":"/etc/orca/actual/default/x","status":"modified","drift_confirmed":true}]}
|
|
EOF
|
|
ORCA_DRIFT_AGG_JSON="$DRIFT_JSON" run "$PULL" --txn-dir "$TMP_TXN" --namespace default --force --i-understand-the-risk
|
|
assert_status 0 "$status"
|
|
}
|
|
|
|
@test "orca-pull.sh drift gate per-namespace scoping (ns-A drift does not block ns-B)" {
|
|
# Drift in ns-A; apply to ns-B should succeed.
|
|
mkdir -p "$TMP_TXN/state"
|
|
DRIFT_JSON="$TMP_TXN/state/drift-events-aggregated.json"
|
|
cat >"$DRIFT_JSON" <<EOF
|
|
{"ts":"2026-01-01T00:00:00Z","events":[{"event_id":"E1","host":"p","path":"/etc/orca/actual/ns-a/x","status":"modified","drift_confirmed":true}]}
|
|
EOF
|
|
ORCA_DRIFT_AGG_JSON="$DRIFT_JSON" run "$PULL" --txn-dir "$TMP_TXN" --namespace ns-b
|
|
assert_status 0 "$status"
|
|
}
|
|
|
|
@test "orca-pull.sh drift gate cluster-wide checks all confirmed drift" {
|
|
# No --namespace; cluster-wide requires --force. When --force given
|
|
# AND drift present, gate is skipped (force bypasses drift too).
|
|
mkdir -p "$TMP_TXN/state"
|
|
DRIFT_JSON="$TMP_TXN/state/drift-events-aggregated.json"
|
|
cat >"$DRIFT_JSON" <<EOF
|
|
{"ts":"2026-01-01T00:00:00Z","events":[{"event_id":"E1","host":"p","path":"/etc/traefik/dynamic/orca.yml","status":"modified","drift_confirmed":true}]}
|
|
EOF
|
|
ORCA_DRIFT_AGG_JSON="$DRIFT_JSON" run "$PULL" --txn-dir "$TMP_TXN" --force --i-understand-the-risk
|
|
assert_status 0 "$status"
|
|
}
|