← Files CrowdStrike Falcon FusionARCHIVED FILE

skills/execution/scripts/monitor_execution.py

3.65 KB · Oct 2, 2026 · 00:31 UTC

↓ Download file

"""
Monitor a CrowdStrike Fusion workflow execution until it completes.

Polls the execution-results API at a fixed interval until the execution reaches
a terminal state (succeeded, completed, failed, canceled, nonrecoverable,
actionrequired) or the timeout elapses. Status updates are printed to stderr so
the final result on stdout stays clean for piping.

Usage:
    python monitor_execution.py --execution-id <exec_id>
    python monitor_execution.py --execution-id <exec_id> --interval 10 --timeout 600
    python monitor_execution.py --execution-id <exec_id> --json
"""

import argparse
import json
import time
import sys
import os

# fetch_results and TERMINAL_STATUSES live alongside this script; they own the
# auth client and API-response parsing, so this script needs no direct client.
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from get_execution_results import fetch_results, TERMINAL_STATUSES, SUCCESS_STATUSES  # pylint: disable=wrong-import-position

# Fix Windows console encoding
sys.stdout.reconfigure(encoding="utf-8", errors="replace")


def monitor(execution_id, interval=5, timeout=300):
    """
    Poll an execution until it reaches a terminal state or times out.

    Status updates are written to stderr. Returns the final result dict when a
    terminal state is reached, or None on timeout. Transient poll errors are
    reported to stderr but do not abort the loop — the execution may still be
    progressing server-side.
    """
    start = time.time()
    print(
        f"  Monitoring execution {execution_id} "
        f"(interval: {interval}s, timeout: {timeout}s)...",
        file=sys.stderr,
    )
    while time.time() - start < timeout:
        ok, msg, result = fetch_results(execution_id)
        elapsed = int(time.time() - start)
        if ok and result:
            status = result.get("status", "")
            print(f"    Status: {status} ({elapsed}s elapsed)", file=sys.stderr)
            if status.lower() in TERMINAL_STATUSES:
                return result
        else:
            print(f"    Poll error ({elapsed}s): {msg}", file=sys.stderr)
        time.sleep(interval)

    print(f"  Timeout after {timeout}s — execution may still be running.", file=sys.stderr)
    return None


def main():
    """CLI entry point for monitoring an execution."""
    parser = argparse.ArgumentParser(
        description="Monitor a Fusion workflow execution until completion"
    )
    parser.add_argument(
        "--execution-id", required=True, metavar="EXEC_ID",
        help="Workflow execution ID to monitor",
    )
    parser.add_argument(
        "--interval", type=int, default=5,
        help="Seconds between status polls (default: 5)",
    )
    parser.add_argument(
        "--timeout", type=int, default=300,
        help="Maximum seconds to wait for a terminal state (default: 300)",
    )
    parser.add_argument(
        "--json", action="store_true",
        help="Machine-readable JSON output on stdout",
    )
    args = parser.parse_args()

    result = monitor(args.execution_id, interval=args.interval, timeout=args.timeout)

    if result is None:
        if args.json:
            print(json.dumps({"execution_id": args.execution_id, "status": "timeout"}, indent=2))
        sys.exit(1)

    status = result.get("status", "?")
    if args.json:
        print(json.dumps(result, indent=2))
    else:
        print(f"\n  Execution {status}")
        output = result.get("output", {})
        if output:
            print(f"  Output:\n{json.dumps(output, indent=4)}")

    # Non-zero exit for non-successful terminal states so callers/CI can react.
    sys.exit(0 if status.lower() in SUCCESS_STATUSES else 1)


if __name__ == "__main__":
    main()

SHA-256: 9d3b1924678d929eee1a41ae1c6dea998fe363da3b198aa31830757aea81d4b2