Files
cross-py-builder/ctrl/workers.py
T
Brett Williams 7a36ae083c Fix worker removal and improve web UI defaults
- Parse optional http:// scheme prefix on worker specs so a scheme-typed
  IP is stored cleanly and its remove button works.
- Delete workers by their stored DB host/port instead of re-parsing the
  rendered URL, and surface add/remove errors in the UI.
- Default new builds to git URL instead of zip upload.
- Show worker hostname/IP from agent status in the address column.
- Always include DB host/port in worker API output.
2026-08-31 15:40:01 -05:00

71 lines
2.1 KiB
Python

import ctrl.db as db
import ctrl.settings as settings
import requests
def parse_worker(spec):
"""'host:port' -> {'url': 'http://host:port', 'host': host, 'port': port}.
Tolerates an optional http:// or https:// scheme prefix.
"""
s = spec.strip()
for scheme in ("http://", "https://"):
if s.lower().startswith(scheme):
s = s[len(scheme):]
break
host, _, port = s.rpartition(":")
if not host or not port.isdigit():
raise ValueError(f"Invalid worker spec: {spec!r} (expected host:port)")
return {"url": f"http://{host}:{port}", "host": host, "port": int(port)}
def configured_workers():
out = []
for w in db.list_workers():
out.append({
"url": f"http://{w['host']}:{w['port']}",
"host": w["host"],
"port": w["port"],
})
return out
def probe_worker(url, host=None, port=None, timeout=None):
"""Return a normalized worker status dict, or None if unreachable."""
timeout = timeout or settings.WORKER_STATUS_TIMEOUT
try:
resp = requests.get(f"{url}/status", timeout=timeout)
resp.raise_for_status()
info = resp.json()
except (requests.RequestException, ValueError):
return None
info["url"] = url
info["online"] = True
if host is not None:
info["host"] = host
if port is not None:
info["port"] = port
return info
def probe_all():
workers = []
for w in configured_workers():
status = probe_worker(w["url"], host=w["host"], port=w["port"])
if status:
workers.append(status)
else:
workers.append({"url": w["url"], "host": w["host"], "port": w["port"], "online": False})
return workers
def matches(job, worker):
if worker.get("status") != "ready":
return False
if job.get("os_req") and job["os_req"].lower() not in str(worker.get("os", "")).lower():
return False
if job.get("cpu_req") and job["cpu_req"].lower() not in str(worker.get("cpu", "")).lower():
return False
return True