feat(gateway): expose /api/v1/queue on chromebox-gateway and configure Cloudflare ingress
This commit is contained in:
@@ -240,8 +240,42 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.wfile.write(body)
|
self.wfile.write(body)
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
if urlparse(self.path).path == "/health":
|
p = urlparse(self.path).path
|
||||||
self._json(200, {"status": "ok", "ops": sorted(ALLOWLIST)})
|
if p == "/health":
|
||||||
|
self._json(200, {"status": "ok", "ops": sorted(ALLOWLIST), "endpoints": ["/health", "/api/v1/queue", "/api/v1/op"]})
|
||||||
|
return
|
||||||
|
if p == "/api/v1/queue":
|
||||||
|
auth = self.headers.get("Authorization", "")
|
||||||
|
token = auth[7:] if auth.startswith("Bearer ") else ""
|
||||||
|
identity = check_token(token)
|
||||||
|
if not identity:
|
||||||
|
self._json(401, {"error": "unauthorized"})
|
||||||
|
return
|
||||||
|
if not rate_ok(identity):
|
||||||
|
audit({"identity": identity, "op": "queue", "result": "rate_limited"})
|
||||||
|
self._json(429, {"error": "rate_limited"})
|
||||||
|
return
|
||||||
|
|
||||||
|
tasks_dir = os.path.join(os.path.dirname(BIN_DIR), "fleet", "tasks")
|
||||||
|
try:
|
||||||
|
if BIN_DIR not in sys.path:
|
||||||
|
sys.path.insert(0, BIN_DIR)
|
||||||
|
import runtime_reconcile as rec
|
||||||
|
tasks = rec.list_tasks(tasks_dir)
|
||||||
|
counts = {"pending": 0, "claimed": 0, "done": 0}
|
||||||
|
for t in tasks:
|
||||||
|
q = t.get("queue")
|
||||||
|
if q in counts:
|
||||||
|
counts[q] += 1
|
||||||
|
self._json(200, {
|
||||||
|
"ok": True,
|
||||||
|
"tasks": tasks,
|
||||||
|
"counts": counts,
|
||||||
|
})
|
||||||
|
audit({"identity": identity, "op": "queue", "result": "ok"})
|
||||||
|
except Exception as e:
|
||||||
|
audit({"identity": identity, "op": "queue", "result": "error", "detail": str(e)[:120]})
|
||||||
|
self._json(500, {"ok": False, "error": str(e)})
|
||||||
return
|
return
|
||||||
self._json(404, {"error": "not_found"})
|
self._json(404, {"error": "not_found"})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""Test for chromebox-gateway /api/v1/queue endpoint."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
BIN_DIR = os.path.join(REPO_ROOT, "bin")
|
||||||
|
if BIN_DIR not in sys.path:
|
||||||
|
sys.path.insert(0, BIN_DIR)
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
spec = importlib.util.spec_from_file_location("chromebox_gateway", os.path.join(BIN_DIR, "chromebox-gateway.py"))
|
||||||
|
cbg = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(cbg)
|
||||||
|
|
||||||
|
class TestChromeboxGatewayQueue(unittest.TestCase):
|
||||||
|
def test_health_endpoints_list(self):
|
||||||
|
handler = MagicMock()
|
||||||
|
handler.path = "/health"
|
||||||
|
cbg.Handler.do_GET(handler)
|
||||||
|
handler._json.assert_called_once()
|
||||||
|
args, _ = handler._json.call_args
|
||||||
|
self.assertEqual(args[0], 200)
|
||||||
|
self.assertIn("/api/v1/queue", args[1].get("endpoints", []))
|
||||||
|
|
||||||
|
@patch.object(cbg, "check_token", return_value=None)
|
||||||
|
def test_queue_unauthorized(self, mock_tok):
|
||||||
|
handler = MagicMock()
|
||||||
|
handler.path = "/api/v1/queue"
|
||||||
|
handler.headers = {"Authorization": "Bearer invalid"}
|
||||||
|
cbg.Handler.do_GET(handler)
|
||||||
|
handler._json.assert_called_once_with(401, {"error": "unauthorized"})
|
||||||
|
|
||||||
|
@patch.object(cbg, "check_token", return_value="master")
|
||||||
|
@patch.object(cbg, "rate_ok", return_value=True)
|
||||||
|
def test_queue_authorized_success(self, mock_rate, mock_tok):
|
||||||
|
handler = MagicMock()
|
||||||
|
handler.path = "/api/v1/queue"
|
||||||
|
handler.headers = {"Authorization": "Bearer master-token"}
|
||||||
|
cbg.Handler.do_GET(handler)
|
||||||
|
handler._json.assert_called_once()
|
||||||
|
code, body = handler._json.call_args[0]
|
||||||
|
self.assertEqual(code, 200)
|
||||||
|
self.assertTrue(body.get("ok"))
|
||||||
|
self.assertIn("counts", body)
|
||||||
|
self.assertIn("tasks", body)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user