52 lines
1.4 KiB
Python
52 lines
1.4 KiB
Python
"""Phase 0 worker stub: a heartbeat line every 30 s and a touched beat file; exits on SIGTERM."""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import os
|
||
|
|
import signal
|
||
|
|
import threading
|
||
|
|
import time
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
log = logging.getLogger("packetstation.worker")
|
||
|
|
|
||
|
|
BEAT_SECONDS = 30
|
||
|
|
STALE_SECONDS = 90
|
||
|
|
|
||
|
|
|
||
|
|
def touch(path: Path) -> None:
|
||
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
path.touch(exist_ok=True)
|
||
|
|
now = time.time()
|
||
|
|
os.utime(path, (now, now))
|
||
|
|
|
||
|
|
|
||
|
|
def run(beat_file: Path, build: str, stop: threading.Event | None = None, interval: float = BEAT_SECONDS) -> int:
|
||
|
|
stop = stop or threading.Event()
|
||
|
|
|
||
|
|
def _stop(signum: int, _frame: object) -> None:
|
||
|
|
log.info("worker stopping on signal %s", signum)
|
||
|
|
stop.set()
|
||
|
|
|
||
|
|
if threading.current_thread() is threading.main_thread():
|
||
|
|
signal.signal(signal.SIGTERM, _stop)
|
||
|
|
signal.signal(signal.SIGINT, _stop)
|
||
|
|
log.info("worker started", extra={"ps": {"build": build, "beat_file": str(beat_file)}})
|
||
|
|
while not stop.is_set():
|
||
|
|
try:
|
||
|
|
touch(beat_file)
|
||
|
|
except OSError as e:
|
||
|
|
log.error("can't touch beat file %s: %s", beat_file, e.strerror)
|
||
|
|
log.info("heartbeat", extra={"ps": {"build": build}})
|
||
|
|
stop.wait(interval)
|
||
|
|
log.info("worker stopped")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
def beat_age(beat_file: Path) -> float | None:
|
||
|
|
try:
|
||
|
|
return time.time() - beat_file.stat().st_mtime
|
||
|
|
except OSError:
|
||
|
|
return None
|