cmpatino's picture
cmpatino HF Staff
deploy controller (2e85dec)
3ed7ad0 verified
Raw History Blame Contribute Delete
2.61 kB
"""Controller entry point: `flock-controller`."""
from __future__ import annotations
import asyncio
import logging
import os
from flock.controller.api import Notifier, create_app
from flock.controller.jobs import FakeJobsClient, HubJobsClient, JobsClient
from flock.controller.reconcile import Settings, run_reconciler
from flock.controller.store import Store
log = logging.getLogger("flock.controller")
STATS_LOG_INTERVAL = 60.0
def _public_url(port: int) -> str:
if os.environ.get("FLOCK_URL"):
return os.environ["FLOCK_URL"]
if os.environ.get("SPACE_HOST"): # set by HF Spaces
return f"https://{os.environ['SPACE_HOST']}"
return f"http://localhost:{port}"
def parse_labels(raw: str) -> dict[str, str]:
"""'a=1,b=2' -> {'a': '1', 'b': '2'} (FLOCK_JOB_LABELS)."""
out = {}
for part in raw.split(","):
if part.strip():
k, _, v = part.partition("=")
out[k.strip()] = v.strip()
return out
def build_app():
port = int(os.environ.get("PORT", "7860"))
token = os.environ.get("FLOCK_TOKEN", "")
if not token:
raise SystemExit("FLOCK_TOKEN must be set")
store = Store(os.environ.get("FLOCK_DATA", "./data"))
jobs: JobsClient
if os.environ.get("FLOCK_FAKE_JOBS") == "1":
jobs = FakeJobsClient()
else:
jobs = HubJobsClient(token=os.environ.get("HF_TOKEN"))
settings = Settings(
public_url=_public_url(port),
token=token,
job_labels=parse_labels(os.environ.get("FLOCK_JOB_LABELS", "")),
)
interval = float(os.environ.get("FLOCK_RECONCILE_INTERVAL", "5"))
def stats() -> dict:
calls = getattr(jobs, "calls", None)
return {"hub_calls": dict(calls)} if calls is not None else {}
async def log_stats() -> None:
while True:
await asyncio.sleep(STATS_LOG_INTERVAL)
log.info("hub calls: %s", stats().get("hub_calls", {}))
async def background(notifier: Notifier) -> None:
stats_task = asyncio.create_task(log_stats())
try:
await run_reconciler(
store, jobs, settings, interval, wake=lambda t: notifier.wait("reconcile:", t)
)
finally:
stats_task.cancel()
return create_app(store, token, background=background, stats=stats), port
def main() -> None:
import uvicorn
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s")
app, port = build_app()
uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
if __name__ == "__main__":
main()