Files
nas-tools/dashboard/backend/routers/docker_router.py
T

84 lines
2.5 KiB
Python

import docker
from fastapi import APIRouter, Depends, HTTPException, Query
from config import DOCKER_HOST
from rbac import require_admin
import time
import logging
logger = logging.getLogger(__name__)
def docker_retry(fn, max_retries=3, base_delay=1):
"""Execute fn with exponential backoff. Returns (result, True) or (None, False)."""
for attempt in range(max_retries):
try:
result = fn()
return result
except Exception as e:
delay = base_delay * (2 ** attempt)
logger.warning("Docker API error (attempt %d/%d): %s. Retrying in %ds...", attempt+1, max_retries, e, delay)
if attempt < max_retries - 1:
time.sleep(delay)
logger.error("Docker API failed after %d attempts", max_retries)
return None
router = APIRouter()
_client = None
def get_docker_client():
"""Get or create Docker client (lazy initialization)."""
global _client
if _client is None:
_client = docker.DockerClient(base_url=DOCKER_HOST)
return _client
@router.get("/containers")
def list_containers():
client = get_docker_client()
result = docker_retry(lambda: client.containers.list(all=True))
if result is None:
return []
return [
{
"id": c.short_id,
"name": c.name,
"status": c.status,
"health": (c.attrs.get("State", {}).get("Health", {}) or {}).get("Status", ""),
"image": c.image.tags[0] if c.image.tags else str(c.image.id)[:20],
"ports": c.ports,
}
for c in result
]
@router.post("/containers/{container_id}/{action}", dependencies=[Depends(require_admin())])
def container_action(container_id: str, action: str):
if action not in ("start", "stop", "restart"):
raise HTTPException(status_code=400, detail="Invalid action. Must be one of: start, stop, restart")
client = get_docker_client()
def _do():
c = client.containers.get(container_id)
getattr(c, action)()
return True
if docker_retry(_do) is None:
raise HTTPException(status_code=503, detail="Docker API unavailable")
return {"ok": True}
@router.get("/containers/{container_id}/logs")
def container_logs(container_id: str, tail: int = Query(200, le=10000)):
client = get_docker_client()
def _do():
c = client.containers.get(container_id)
return {"logs": c.logs(tail=tail, timestamps=True).decode(errors="replace")}
result = docker_retry(_do)
if result is None:
return {"logs": ""}
return result