Files
FabledSteward/plugins/http/scheduler.py
T
bvandeusen af60ca446d
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 7s
CI / integration (push) Successful in 2m8s
fix(plugins): complete steward rename across bundled plugins; clear lint debt
The fabledscryer->steward rename had only ever reached host_agent. The other
five bundled plugins (http, snmp, traefik, unifi, docker) still imported
`from fabledscryer.*` (package no longer exists) and read FABLEDSCRYER_* env
vars — so every one of them was broken at import since the original rebrand.
CI stayed green only because none are enabled by default and migrations don't
import plugin modules. Now that they version in-tree, complete the rename:
- fabledscryer.* -> steward.* imports across all five plugins
- FABLEDSCRYER_* -> STEWARD_* in plugin migration env.py files
- author/repository/homepage + user-facing 'Fabled Scryer' strings -> Steward
- snmp/scheduler.py: also drop dead `now`/datetime; record_metric from steward

Adds tests/test_no_legacy_names.py — fails if 'scryer'/'roundtable' ever
reappear in shipped code (the drift bit twice; this stops a third time).

Also clears pre-existing ruff lint debt (unused imports, semicolon statements,
mid-file import) surfaced by the new lint lane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:22:20 -04:00

122 lines
4.2 KiB
Python

# plugins/http/scheduler.py
from __future__ import annotations
import asyncio
import json
import logging
from datetime import datetime, timezone
from steward.core.scheduler import ScheduledTask
from steward.core.alerts import record_metric
logger = logging.getLogger(__name__)
def make_task(app) -> ScheduledTask:
interval = int(
app.config["PLUGINS"]["http"].get("check_interval_seconds", 60)
)
async def check():
await _do_checks(app)
return ScheduledTask(
name="http_check",
coro_factory=check,
interval_seconds=interval,
run_on_startup=True,
)
async def _do_checks(app) -> None:
from sqlalchemy import select
from .models import HttpMonitor, HttpResult
from .checker import run_check
cfg = app.config["PLUGINS"]["http"]
global_interval = int(cfg.get("check_interval_seconds", 60))
global_timeout = int(cfg.get("default_timeout_seconds", 10))
global_follow = bool(cfg.get("follow_redirects", True))
global_verify = bool(cfg.get("verify_ssl", True))
now = datetime.now(timezone.utc)
async with app.db_sessionmaker() as session:
result = await session.execute(
select(HttpMonitor).where(HttpMonitor.enabled == True) # noqa: E712
)
monitors = list(result.scalars())
# Filter to monitors whose next check time has passed
due: list[HttpMonitor] = []
for m in monitors:
effective_interval = m.check_interval_seconds or global_interval
if m.last_checked_at is None:
due.append(m)
elif (now - m.last_checked_at).total_seconds() >= effective_interval:
due.append(m)
if not due:
return
async def _check_one(monitor: HttpMonitor) -> tuple[HttpMonitor, dict]:
try:
headers = json.loads(monitor.headers_json or "{}")
except (ValueError, TypeError):
headers = {}
result = await run_check(
url=monitor.url,
method=monitor.method,
expected_status=monitor.expected_status,
content_match=monitor.content_match,
headers=headers,
timeout_seconds=monitor.timeout_seconds or global_timeout,
follow_redirects=monitor.follow_redirects if monitor.follow_redirects is not None else global_follow,
verify_ssl=monitor.verify_ssl if monitor.verify_ssl is not None else global_verify,
)
return monitor, result
pairs = await asyncio.gather(*[_check_one(m) for m in due], return_exceptions=True)
check_time = datetime.now(timezone.utc)
async with app.db_sessionmaker() as session:
async with session.begin():
for item in pairs:
if isinstance(item, Exception):
logger.error("HTTP check task error: %s", item)
continue
monitor, res = item
session.add(HttpResult(
monitor_id=monitor.id,
checked_at=check_time,
status_code=res["status_code"],
response_ms=res["response_ms"],
is_up=res["is_up"],
content_matched=res["content_matched"],
error_msg=res["error_msg"],
tls_expires_at=res["tls_expires_at"],
))
# Update monitor's last_checked_at for next-run scheduling
db_monitor = await session.get(HttpMonitor, monitor.id)
if db_monitor:
db_monitor.last_checked_at = check_time
# Alert pipeline
if res["response_ms"] is not None:
await record_metric(
session=session,
source_module="http",
resource_name=monitor.name,
metric_name="response_ms",
value=res["response_ms"],
)
await record_metric(
session=session,
source_module="http",
resource_name=monitor.name,
metric_name="is_up",
value=1.0 if res["is_up"] else 0.0,
)