feat: Add Docker image update system (TrueNAS Scale inspired)

- 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
This commit is contained in:
innotex
2026-01-16 19:37:23 +01:00
parent 9ec63a8aa2
commit c51592c7ea
23 changed files with 3780 additions and 132 deletions

View File

@@ -2,9 +2,11 @@ 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")
@@ -117,3 +119,155 @@ async def delete_container(
)
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