- Implement UpdateService for image version checking and atomic updates - Add DockerComposeManager for centralized docker-compose management - Create 12 docker-compose references in /home/innotex/Docker - Add 13 new API endpoints (6 for images, 7 for compose management) - Add comprehensive documentation and examples
274 lines
8.7 KiB
Python
274 lines
8.7 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from typing import List
|
|
from app.core.security import get_current_user, User
|
|
from app.services.docker_service import DockerService, ContainerInfo
|
|
from app.services.update_service import UpdateService, ImageInfo, ImageUpdate
|
|
|
|
router = APIRouter()
|
|
docker_service = DockerService()
|
|
update_service = UpdateService()
|
|
|
|
|
|
@router.get("/status")
|
|
async def get_docker_status(current_user: User = Depends(get_current_user)):
|
|
"""Vérifie le statut de la connexion Docker"""
|
|
return {
|
|
"connected": docker_service.is_connected(),
|
|
"message": "Docker est accessible" if docker_service.is_connected() else "Docker n'est pas accessible"
|
|
}
|
|
|
|
|
|
@router.get("/containers", response_model=List[ContainerInfo])
|
|
async def list_containers(
|
|
all: bool = True,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Liste tous les conteneurs Docker"""
|
|
if not docker_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
return docker_service.get_containers(all=all)
|
|
|
|
|
|
@router.post("/containers/{container_id}/start")
|
|
async def start_container(
|
|
container_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Démarre un conteneur"""
|
|
if not docker_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
success = docker_service.start_container(container_id)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Impossible de démarrer le conteneur"
|
|
)
|
|
|
|
return {"status": "success", "message": f"Conteneur {container_id} démarré"}
|
|
|
|
|
|
@router.post("/containers/{container_id}/stop")
|
|
async def stop_container(
|
|
container_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Arrête un conteneur"""
|
|
if not docker_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
success = docker_service.stop_container(container_id)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Impossible d'arrêter le conteneur"
|
|
)
|
|
|
|
return {"status": "success", "message": f"Conteneur {container_id} arrêté"}
|
|
|
|
|
|
@router.post("/containers/{container_id}/restart")
|
|
async def restart_container(
|
|
container_id: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Redémarre un conteneur"""
|
|
if not docker_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
success = docker_service.restart_container(container_id)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Impossible de redémarrer le conteneur"
|
|
)
|
|
|
|
return {"status": "success", "message": f"Conteneur {container_id} redémarré"}
|
|
|
|
|
|
@router.delete("/containers/{container_id}")
|
|
async def delete_container(
|
|
container_id: str,
|
|
force: bool = False,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Supprime un conteneur"""
|
|
if not docker_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
success = docker_service.remove_container(container_id, force=force)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Impossible de supprimer le conteneur"
|
|
)
|
|
|
|
return {"status": "success", "message": f"Conteneur {container_id} supprimé"}
|
|
|
|
|
|
# ===== ENDPOINTS DE MISE À JOUR =====
|
|
|
|
|
|
@router.get("/images", response_model=List[ImageInfo])
|
|
async def list_images(current_user: User = Depends(get_current_user)):
|
|
"""Liste toutes les images Docker locales"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
return update_service.get_all_images_info()
|
|
|
|
|
|
@router.get("/images/check-update/{image_name}")
|
|
async def check_image_update(
|
|
image_name: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Vérifie si une image a une mise à jour disponible"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
update_info = update_service.check_image_updates(image_name)
|
|
if not update_info:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Impossible de vérifier les mises à jour pour {image_name}"
|
|
)
|
|
|
|
return update_info
|
|
|
|
|
|
@router.get("/images/check-all-updates")
|
|
async def check_all_updates(current_user: User = Depends(get_current_user)):
|
|
"""Vérifie les mises à jour de toutes les images en cours d'utilisation"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
try:
|
|
from app.services.docker_service import DockerService
|
|
ds = DockerService()
|
|
containers = ds.get_containers(all=False) # Conteneurs actifs uniquement
|
|
|
|
updates = []
|
|
for container in containers:
|
|
image_name = container.image
|
|
update_info = update_service.check_image_updates(image_name)
|
|
if update_info:
|
|
updates.append({
|
|
"container": container.name,
|
|
"update": update_info
|
|
})
|
|
|
|
return {
|
|
"total_containers": len(containers),
|
|
"containers_with_updates": len(updates),
|
|
"updates": updates
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Erreur lors de la vérification des mises à jour: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.post("/images/pull")
|
|
async def pull_image(
|
|
image: str,
|
|
tag: str = "latest",
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Télécharge une image Docker"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
success = update_service.pull_image(image, tag)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Impossible de télécharger l'image {image}:{tag}"
|
|
)
|
|
|
|
return {
|
|
"status": "success",
|
|
"message": f"Image {image}:{tag} téléchargée avec succès"
|
|
}
|
|
|
|
|
|
@router.post("/containers/{container_id}/update-image")
|
|
async def update_container_image(
|
|
container_id: str,
|
|
new_image: str,
|
|
new_tag: str = "latest",
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Met à jour l'image d'un conteneur avec la dernière version
|
|
|
|
Processus inspiré de TrueNAS Scale:
|
|
1. Arrête le conteneur
|
|
2. Télécharge la nouvelle image
|
|
3. Redémarre le conteneur
|
|
"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
result = update_service.update_container_image(container_id, new_image, new_tag)
|
|
|
|
if not result.get("success"):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=result.get("message", "Erreur lors de la mise à jour")
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
@router.post("/images/prune")
|
|
async def prune_images(
|
|
dangling_only: bool = True,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Nettoie les images inutilisées (similaire à TrueNAS Scale)"""
|
|
if not update_service.is_connected():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="Docker n'est pas accessible"
|
|
)
|
|
|
|
result = update_service.prune_unused_images(dangling_only=dangling_only)
|
|
|
|
if not result.get("success"):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=result.get("message", "Erreur lors du nettoyage")
|
|
)
|
|
|
|
return result
|