186 lines
6.9 KiB
Python
186 lines
6.9 KiB
Python
"""CRUD operations for Docker containers."""
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from sqlalchemy import case, delete, func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.docker_container import DockerContainer
|
|
|
|
|
|
class DockerContainerRepository:
|
|
"""Repository for Docker container CRUD operations."""
|
|
|
|
def __init__(self, session: AsyncSession):
|
|
self.session = session
|
|
|
|
async def get(self, container_db_id: int) -> Optional[DockerContainer]:
|
|
"""Get a container by its database ID."""
|
|
result = await self.session.execute(
|
|
select(DockerContainer).where(DockerContainer.id == container_db_id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def get_by_container_id(self, host_id: str, container_id: str) -> Optional[DockerContainer]:
|
|
"""Get a container by host ID and Docker container ID."""
|
|
result = await self.session.execute(
|
|
select(DockerContainer).where(
|
|
DockerContainer.host_id == host_id,
|
|
DockerContainer.container_id == container_id
|
|
).limit(1)
|
|
)
|
|
return result.scalars().first()
|
|
|
|
async def get_by_name(self, host_id: str, name: str) -> Optional[DockerContainer]:
|
|
"""Get a container by host ID and container name."""
|
|
result = await self.session.execute(
|
|
select(DockerContainer).where(
|
|
DockerContainer.host_id == host_id,
|
|
DockerContainer.name == name
|
|
).limit(1)
|
|
)
|
|
return result.scalars().first()
|
|
|
|
async def list_by_host(
|
|
self,
|
|
host_id: str,
|
|
state: Optional[str] = None,
|
|
compose_project: Optional[str] = None
|
|
) -> List[DockerContainer]:
|
|
"""List all containers for a host with optional filters."""
|
|
query = select(DockerContainer).where(DockerContainer.host_id == host_id)
|
|
|
|
if state:
|
|
query = query.where(DockerContainer.state == state)
|
|
if compose_project:
|
|
query = query.where(DockerContainer.compose_project == compose_project)
|
|
|
|
query = query.order_by(DockerContainer.name)
|
|
result = await self.session.execute(query)
|
|
return list(result.scalars().all())
|
|
|
|
async def count_by_host(self, host_id: str) -> dict:
|
|
"""Count containers by state for a host."""
|
|
result = await self.session.execute(
|
|
select(
|
|
func.count(DockerContainer.id).label('total'),
|
|
func.sum(case((DockerContainer.state == 'running', 1), else_=0)).label('running')
|
|
).where(DockerContainer.host_id == host_id)
|
|
)
|
|
row = result.one()
|
|
return {
|
|
"total": row.total or 0,
|
|
"running": row.running or 0
|
|
}
|
|
|
|
async def upsert(
|
|
self,
|
|
host_id: str,
|
|
container_id: str,
|
|
name: str,
|
|
image: Optional[str] = None,
|
|
state: str = "unknown",
|
|
status: Optional[str] = None,
|
|
health: Optional[str] = None,
|
|
created_at: Optional[datetime] = None,
|
|
ports: Optional[dict] = None,
|
|
labels: Optional[dict] = None,
|
|
compose_project: Optional[str] = None
|
|
) -> DockerContainer:
|
|
"""Create or update a container."""
|
|
existing = await self.get_by_container_id(host_id, container_id)
|
|
|
|
if existing:
|
|
existing.name = name
|
|
existing.image = image
|
|
existing.state = state
|
|
existing.status = status
|
|
existing.health = health
|
|
existing.created_at = created_at
|
|
existing.ports = ports
|
|
existing.labels = labels
|
|
existing.compose_project = compose_project
|
|
existing.last_update_at = datetime.utcnow()
|
|
return existing
|
|
|
|
container = DockerContainer(
|
|
host_id=host_id,
|
|
container_id=container_id,
|
|
name=name,
|
|
image=image,
|
|
state=state,
|
|
status=status,
|
|
health=health,
|
|
created_at=created_at,
|
|
ports=ports,
|
|
labels=labels,
|
|
compose_project=compose_project
|
|
)
|
|
self.session.add(container)
|
|
return container
|
|
|
|
async def delete_by_host(self, host_id: str) -> int:
|
|
"""Delete all containers for a host."""
|
|
result = await self.session.execute(
|
|
delete(DockerContainer).where(DockerContainer.host_id == host_id)
|
|
)
|
|
return result.rowcount
|
|
|
|
async def delete_stale(self, host_id: str, current_container_ids: List[str]) -> int:
|
|
"""Delete containers that no longer exist on the host."""
|
|
if not current_container_ids:
|
|
return await self.delete_by_host(host_id)
|
|
|
|
result = await self.session.execute(
|
|
delete(DockerContainer).where(
|
|
DockerContainer.host_id == host_id,
|
|
DockerContainer.container_id.notin_(current_container_ids)
|
|
)
|
|
)
|
|
return result.rowcount
|
|
|
|
async def list_all(
|
|
self,
|
|
state: Optional[str] = None,
|
|
compose_project: Optional[str] = None,
|
|
health: Optional[str] = None,
|
|
host_ids: Optional[List[str]] = None
|
|
) -> List[DockerContainer]:
|
|
"""List all containers across all hosts with optional filters."""
|
|
query = select(DockerContainer)
|
|
|
|
if state:
|
|
query = query.where(DockerContainer.state == state)
|
|
if compose_project:
|
|
query = query.where(DockerContainer.compose_project == compose_project)
|
|
if health:
|
|
query = query.where(DockerContainer.health == health)
|
|
if host_ids:
|
|
query = query.where(DockerContainer.host_id.in_(host_ids))
|
|
|
|
query = query.order_by(DockerContainer.host_id, DockerContainer.name)
|
|
result = await self.session.execute(query)
|
|
return list(result.scalars().all())
|
|
|
|
async def count_all(self) -> dict:
|
|
"""Count all containers by state across all hosts."""
|
|
result = await self.session.execute(
|
|
select(
|
|
func.count(DockerContainer.id).label('total'),
|
|
func.sum(case((DockerContainer.state == 'running', 1), else_=0)).label('running'),
|
|
func.sum(case((DockerContainer.state == 'exited', 1), else_=0)).label('stopped'),
|
|
func.sum(case((DockerContainer.state == 'paused', 1), else_=0)).label('paused'),
|
|
func.count(func.distinct(DockerContainer.host_id)).label('hosts_count'),
|
|
func.max(DockerContainer.last_update_at).label('last_update')
|
|
)
|
|
)
|
|
row = result.one()
|
|
return {
|
|
"total": row.total or 0,
|
|
"running": row.running or 0,
|
|
"stopped": row.stopped or 0,
|
|
"paused": row.paused or 0,
|
|
"hosts_count": row.hosts_count or 0,
|
|
"last_update": row.last_update
|
|
}
|