62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
"""Image listing — shared by the central images router and the agent."""
|
|
from __future__ import annotations
|
|
|
|
from docker_client import DockerError, get_client, safe_call
|
|
from services import update_service
|
|
|
|
COMPOSE_PROJECT_LABEL = "com.docker.compose.project"
|
|
|
|
|
|
def list_images() -> list[dict]:
|
|
"""Return one row per image tag, annotated with using-stacks + update status."""
|
|
try:
|
|
client = get_client()
|
|
images = safe_call(client.images.list)
|
|
containers = safe_call(client.containers.list, all=True)
|
|
except DockerError:
|
|
return []
|
|
|
|
# Map image ref -> stacks using it.
|
|
usage: dict[str, set[str]] = {}
|
|
for c in containers:
|
|
ref = c.attrs.get("Config", {}).get("Image")
|
|
stack = c.labels.get(COMPOSE_PROJECT_LABEL)
|
|
if ref:
|
|
usage.setdefault(ref, set())
|
|
if stack:
|
|
usage[ref].add(stack)
|
|
|
|
cache = update_service.get_cache()
|
|
result = []
|
|
for img in images:
|
|
tags = img.tags or []
|
|
if not tags:
|
|
continue
|
|
for tag in tags:
|
|
result.append(
|
|
{
|
|
"id": img.short_id,
|
|
"tag": tag,
|
|
"size": img.attrs.get("Size", 0),
|
|
"created": img.attrs.get("Created"),
|
|
"stacks": sorted(usage.get(tag, set())),
|
|
"update": cache.get(tag),
|
|
}
|
|
)
|
|
result.sort(key=lambda r: r["tag"])
|
|
return result
|
|
|
|
|
|
def prune_images(all_unused: bool = False) -> dict:
|
|
"""Remove unused images. By default only dangling (untagged) images are
|
|
removed; ``all_unused=True`` removes every image not referenced by a
|
|
container (``docker image prune -a``)."""
|
|
client = get_client()
|
|
# dangling=false tells the engine to also consider tagged-but-unused images.
|
|
filters = {"dangling": False} if all_unused else {"dangling": True}
|
|
result = safe_call(client.images.prune, filters=filters)
|
|
return {
|
|
"ImagesDeleted": result.get("ImagesDeleted") or [],
|
|
"SpaceReclaimed": result.get("SpaceReclaimed", 0),
|
|
}
|