Files
light-docker-api/app/routes/containers.py
T
2026-02-08 16:38:58 +01:00

172 lines
5.4 KiB
Python

"""Container management API routes."""
from typing import Optional
from fastapi import APIRouter, Body, Depends, Query
from app.docker_client import DockerManager, get_docker_manager
from app.models import (
ContainerCreate,
ContainerCreateResponse,
ContainerListResponse,
ContainerLogsResponse,
ContainerResponse,
ContainerStatsResponse,
MessageResponse,
)
from app.security import validate_container_request
router = APIRouter(prefix="/containers", tags=["containers"])
@router.get("", response_model=ContainerListResponse)
async def list_containers(
all: bool = Query(True, description="Include stopped containers"),
docker: DockerManager = Depends(get_docker_manager),
) -> ContainerListResponse:
"""
List all containers managed by this API.
Only containers created through this API (with managed labels) will be returned.
"""
containers = docker.list_managed(all_containers=all)
return ContainerListResponse(containers=containers, count=len(containers))
@router.post("/create", response_model=ContainerCreateResponse)
async def create_container(
request: ContainerCreate = Body(...),
docker: DockerManager = Depends(get_docker_manager),
) -> ContainerCreateResponse:
"""
Create a new container.
The container will be labeled as managed by this API and will only be accessible
through this API's endpoints.
Security restrictions apply:
- Privileged mode is not allowed
- Host network mode is not allowed
- Host PID mode is not allowed
- Certain volume mounts are blocked
- Device mappings are not allowed
"""
# Validate security
validate_container_request(request)
# Create the container
return docker.create_container(request)
@router.get("/{container_id}", response_model=ContainerResponse)
async def inspect_container(
container_id: str,
docker: DockerManager = Depends(get_docker_manager),
) -> ContainerResponse:
"""
Get detailed information about a container.
Only managed containers can be inspected.
"""
return docker.inspect_container(container_id)
@router.post("/{container_id}/start", response_model=MessageResponse)
async def start_container(
container_id: str,
docker: DockerManager = Depends(get_docker_manager),
) -> MessageResponse:
"""
Start a stopped container.
Only managed containers can be started.
"""
docker.start_container(container_id)
return MessageResponse(message="Container started", container_id=container_id)
@router.post("/{container_id}/stop", response_model=MessageResponse)
async def stop_container(
container_id: str,
timeout: int = Query(10, ge=0, description="Seconds to wait before killing"),
docker: DockerManager = Depends(get_docker_manager),
) -> MessageResponse:
"""
Stop a running container.
Only managed containers can be stopped.
"""
docker.stop_container(container_id, timeout=timeout)
return MessageResponse(message="Container stopped", container_id=container_id)
@router.post("/{container_id}/restart", response_model=MessageResponse)
async def restart_container(
container_id: str,
timeout: int = Query(10, ge=0, description="Seconds to wait before killing"),
docker: DockerManager = Depends(get_docker_manager),
) -> MessageResponse:
"""
Restart a container.
Only managed containers can be restarted.
"""
docker.restart_container(container_id, timeout=timeout)
return MessageResponse(message="Container restarted", container_id=container_id)
@router.delete("/{container_id}", response_model=MessageResponse)
async def remove_container(
container_id: str,
force: bool = Query(False, description="Force removal of running container"),
v: bool = Query(False, description="Remove associated volumes"),
docker: DockerManager = Depends(get_docker_manager),
) -> MessageResponse:
"""
Remove a container.
Only managed containers can be removed.
"""
docker.remove_container(container_id, force=force, v=v)
return MessageResponse(message="Container removed", container_id=container_id)
@router.get("/{container_id}/logs", response_model=ContainerLogsResponse)
async def get_container_logs(
container_id: str,
stdout: bool = Query(True, description="Include stdout"),
stderr: bool = Query(True, description="Include stderr"),
tail: int = Query(100, ge=0, description="Number of lines from end (0 for all)"),
since: Optional[int] = Query(None, description="Unix timestamp to start from"),
until: Optional[int] = Query(None, description="Unix timestamp to end at"),
docker: DockerManager = Depends(get_docker_manager),
) -> ContainerLogsResponse:
"""
Get logs from a container.
Only managed containers can have their logs retrieved.
"""
tail_value = "all" if tail == 0 else tail
return docker.get_logs(
container_id,
stdout=stdout,
stderr=stderr,
tail=tail_value,
since=since,
until=until,
)
@router.get("/{container_id}/stats", response_model=ContainerStatsResponse)
async def get_container_stats(
container_id: str,
docker: DockerManager = Depends(get_docker_manager),
) -> ContainerStatsResponse:
"""
Get resource usage statistics for a container.
Returns CPU, memory, and network statistics.
Only managed containers can have their stats retrieved.
"""
return docker.get_stats(container_id)