f8bbb783b0
Nieuw bestand control/app_volumes.py met Libpod volume operaties:
- GET /volumes — lijst alle volumes (optioneel ?filters=key=value)
- POST /volumes — volume aanmaken (name, driver, labels, driverOpts)
- GET /volumes/{name} — details van één volume
- GET /volumes/{name}/exists — bestaanskontrolle (204 → true, 404 → false)
- DELETE /volumes/{name} — volume verwijderen (?force=true optioneel)
- POST /volumes/prune — ⚠️ verwijdert alle ongebruikte volumes
Filters: key=value formaat wordt automatisch omgezet naar
{"key":["value"]} JSON dat de Libpod API verwacht.
Containerfile: COPY app_volumes.py toegevoegd.
app.py: init_volumes_router geregistreerd.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Dict, Optional
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
|
|
|
|
def _normalize_filters(filters: str) -> str:
|
|
"""Zet key=value formaat om naar {"key":["value"]} JSON dat Libpod verwacht.
|
|
Als de waarde al met '{' begint, wordt hij ongewijzigd doorgegeven."""
|
|
if filters.startswith("{"):
|
|
return filters
|
|
# key=value → {"key": ["value"]}
|
|
if "=" in filters:
|
|
key, _, value = filters.partition("=")
|
|
return json.dumps({key.strip(): [value.strip()]})
|
|
# Alleen een key zonder waarde → {"key": ["true"]}
|
|
return json.dumps({filters.strip(): ["true"]})
|
|
|
|
|
|
class VolumeCreateRequest(BaseModel):
|
|
name: str
|
|
driver: str = "local"
|
|
driverOpts: Optional[Dict[str, str]] = None
|
|
labels: Optional[Dict[str, str]] = None
|
|
|
|
|
|
def _raise_on_error(resp):
|
|
if 200 <= resp.status_code < 300:
|
|
return
|
|
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
|
|
|
|
def init_volumes_router(session, podman_api_base: str) -> APIRouter:
|
|
router = APIRouter(prefix="/volumes", tags=["volumes"])
|
|
|
|
@router.get("")
|
|
def list_volumes(filters: Optional[str] = Query(None)):
|
|
url = f"{podman_api_base}/libpod/volumes/json"
|
|
params = {}
|
|
if filters is not None:
|
|
params["filters"] = _normalize_filters(filters)
|
|
resp = session.get(url, params=params)
|
|
_raise_on_error(resp)
|
|
return resp.json()
|
|
|
|
@router.post("")
|
|
def create_volume(req: VolumeCreateRequest):
|
|
url = f"{podman_api_base}/libpod/volumes/create"
|
|
body: dict = {"name": req.name, "driver": req.driver}
|
|
if req.driverOpts:
|
|
body["driverOpts"] = req.driverOpts
|
|
if req.labels:
|
|
body["labels"] = req.labels
|
|
resp = session.post(url, json=body)
|
|
_raise_on_error(resp)
|
|
return resp.json()
|
|
|
|
@router.post("/prune")
|
|
def prune_volumes():
|
|
"""⚠️ Destructief: verwijdert alle ongebruikte volumes permanent. Niet terug te draaien."""
|
|
url = f"{podman_api_base}/libpod/volumes/prune"
|
|
resp = session.post(url)
|
|
_raise_on_error(resp)
|
|
return resp.json()
|
|
|
|
@router.get("/{name}/exists")
|
|
def volume_exists(name: str):
|
|
url = f"{podman_api_base}/libpod/volumes/{name}/exists"
|
|
resp = session.get(url)
|
|
if resp.status_code == 204:
|
|
return {"exists": True}
|
|
if resp.status_code == 404:
|
|
return {"exists": False}
|
|
_raise_on_error(resp)
|
|
|
|
@router.get("/{name}")
|
|
def get_volume(name: str):
|
|
url = f"{podman_api_base}/libpod/volumes/{name}/json"
|
|
resp = session.get(url)
|
|
_raise_on_error(resp)
|
|
return resp.json()
|
|
|
|
@router.delete("/{name}")
|
|
def remove_volume(name: str, force: bool = Query(False)):
|
|
"""⚠️ Destructief: verwijdert een volume permanent. Niet terug te draaien als het volume data bevat."""
|
|
url = f"{podman_api_base}/libpod/volumes/{name}"
|
|
params = {"force": str(force).lower()}
|
|
resp = session.delete(url, params=params)
|
|
if resp.status_code == 204:
|
|
return {"ok": True}
|
|
_raise_on_error(resp)
|
|
|
|
return router
|