fix(smartbadge-watch): handle null stdout from RMM and add diagnostic context
jq -r '.stdout' returns the literal string "null" when the API field is JSON null, causing the RESULT: grep to fail and fire a false drift alert. Fixes: - Use `.stdout // empty` so null becomes empty string - Add FINAL_ST tracking; treat non-terminal status as INFRA-ERROR, not drift - Increase poll window from 20x4s=80s to 30x4s=120s for slow commands - Read .stderr and .exit_code; include them in the no-RESULT diagnostic Live check 2026-06-02: KSTEENBB2025 is PASS (today's alert was a false positive). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,12 +55,26 @@ CID=$(curl -s -X POST "$RMM/api/agents/$KSTEEN/command" -H "Authorization: Beare
|
||||
-H "Content-Type: application/json" -d "$PAYLOAD" | jq -r '.command_id // empty')
|
||||
[ -z "$CID" ] && fail_exit "command dispatch failed"
|
||||
|
||||
for i in $(seq 1 20); do
|
||||
ST=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.status')
|
||||
case "$ST" in completed|failed|cancelled|interrupted) break;; esac
|
||||
FINAL_ST=""
|
||||
for i in $(seq 1 30); do
|
||||
FINAL_ST=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.status')
|
||||
case "$FINAL_ST" in completed|failed|cancelled|interrupted) break;; esac
|
||||
sleep 4
|
||||
done
|
||||
OUT=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.stdout')
|
||||
|
||||
CMD_JSON=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN")
|
||||
OUT=$(printf '%s' "$CMD_JSON" | jq -r '.stdout // empty')
|
||||
ERR=$(printf '%s' "$CMD_JSON" | jq -r '.stderr // empty')
|
||||
EXIT_CODE=$(printf '%s' "$CMD_JSON" | jq -r '.exit_code // empty')
|
||||
|
||||
# If the command never completed, treat as a transient infrastructure error (not drift)
|
||||
if [[ "$FINAL_ST" != "completed" && "$FINAL_ST" != "failed" ]]; then
|
||||
INFRA_REASON="command status=$FINAL_ST after poll window (exit_code=${EXIT_CODE:-?})"
|
||||
echo "$(ts) INFRA-ERROR | $INFRA_REASON" >> "$LOG"
|
||||
alert "[SMARTBADGE-WATCH] INFRA-ERROR KSTEENBB2025 | $INFRA_REASON - NOT counted as drift"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
RESULT=$(printf '%s' "$OUT" | grep -m1 'RESULT:')
|
||||
|
||||
if printf '%s' "$RESULT" | grep -q 'RESULT: PASS'; then
|
||||
@@ -68,10 +82,17 @@ if printf '%s' "$RESULT" | grep -q 'RESULT: PASS'; then
|
||||
alert "[SMARTBADGE-WATCH] KSTEENBB2025 PASS - Datto Workplace v10 + SmartBadge _CC add-in aligned"
|
||||
else
|
||||
REASON=$(printf '%s' "$RESULT" | sed 's/^RESULT: //')
|
||||
[ -z "$REASON" ] && REASON="no RESULT line returned: ${OUT:0:200}"
|
||||
if [ -z "$REASON" ]; then
|
||||
# stdout was null/empty - use stderr or command status as diagnostic
|
||||
if [ -n "$ERR" ]; then
|
||||
REASON="no RESULT line; status=$FINAL_ST exit_code=${EXIT_CODE:-?}; stderr: ${ERR:0:300}"
|
||||
else
|
||||
REASON="no RESULT line; status=$FINAL_ST exit_code=${EXIT_CODE:-?}; stdout: ${OUT:0:200}"
|
||||
fi
|
||||
fi
|
||||
echo "$(ts) FAIL | $REASON" >> "$LOG"
|
||||
alert "[SMARTBADGE-WATCH] *** DRIFT *** KSTEENBB2025 FAIL | $REASON"
|
||||
alert "[SMARTBADGE-WATCH] DRIFT KSTEENBB2025 FAIL | $REASON"
|
||||
curl -s -X POST "$COORD/messages" -H "Content-Type: application/json" --data-binary @- <<JSON >/dev/null 2>&1
|
||||
{"from_session":"GURU-5070/smartbadge-watch","to_user":"mike","subject":"KSTEENBB2025 SmartBadge drift detected","body":"Daily watch found drift on Kristin Steen's machine: $REASON. Re-run the SmartBadge remediation (.claude/scripts ksteen-smartbadge-verify.ps1 + datto-fix.ps1).","priority":"high"}
|
||||
{"from_session":"GURU-5070/smartbadge-watch","to_user":"mike","subject":"KSTEENBB2025 SmartBadge drift detected","body":"Daily watch found drift on Kristin Steen's machine: $REASON. Re-run the SmartBadge remediation (.claude/scripts/ksteen-smartbadge-verify.ps1 + ksteen-smartbadge-fix.ps1).","priority":"high"}
|
||||
JSON
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user