feat: Initial code
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 1m4s
Build Docker / BuildImage (push) Successful in 1m26s

This commit is contained in:
2026-03-26 23:07:05 +11:00
parent d8ede00901
commit 0ce79935d7
24 changed files with 2527 additions and 143 deletions

3
services/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from .scheduler import CollectionScheduler, should_autostart_scheduler
__all__ = ["CollectionScheduler", "should_autostart_scheduler"]

57
services/scheduler.py Normal file
View File

@@ -0,0 +1,57 @@
import fcntl
import logging
import os
import threading
from collectors import InventoryCollectorOrchestrator
from config import AppConfig
LOGGER = logging.getLogger(__name__)
class CollectionScheduler:
def __init__(self, config: AppConfig, orchestrator: InventoryCollectorOrchestrator):
self.config = config
self.orchestrator = orchestrator
self._stop_event = threading.Event()
self._thread: threading.Thread | None = None
self._leader_file = None
def start(self) -> bool:
if not self.config.scheduler_enabled:
LOGGER.info("Scheduler disabled via config")
return False
leader_file = open("/tmp/inventory-scheduler.lock", "w", encoding="utf-8")
try:
fcntl.flock(leader_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError:
leader_file.close()
LOGGER.info("Another worker owns scheduler lock")
return False
self._leader_file = leader_file
self._thread = threading.Thread(target=self._run_loop, name="inventory-scheduler", daemon=True)
self._thread.start()
LOGGER.info("Scheduler started with %s second interval", self.config.poll_interval_seconds)
return True
def _run_loop(self) -> None:
while not self._stop_event.is_set():
self.orchestrator.collect_once()
self._stop_event.wait(self.config.poll_interval_seconds)
def shutdown(self) -> None:
self._stop_event.set()
if self._thread and self._thread.is_alive():
self._thread.join(timeout=1.0)
if self._leader_file:
fcntl.flock(self._leader_file.fileno(), fcntl.LOCK_UN)
self._leader_file.close()
self._leader_file = None
def should_autostart_scheduler() -> bool:
# Do not auto-start on Flask's reloader child process.
return os.getenv("WERKZEUG_RUN_MAIN", "true") != "false"