110 lines
3.3 KiB
Python
110 lines
3.3 KiB
Python
|
|
"""Docker по SSH: команды docker с JSON-выводом. Не требует docker CLI на клиенте."""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
from typing import List, Optional
|
||
|
|
|
||
|
|
from app.core.ssh import SSHClient
|
||
|
|
|
||
|
|
|
||
|
|
async def _docker(c: SSHClient, args: str, timeout: int = 120) -> str:
|
||
|
|
r = await c.sudo(f"docker {args}", timeout=timeout)
|
||
|
|
return r.stdout
|
||
|
|
|
||
|
|
|
||
|
|
async def available(c: SSHClient) -> bool:
|
||
|
|
r = await c.sudo("docker version --format '{{.Server.Version}}'", timeout=30)
|
||
|
|
return r.ok and bool(r.stdout.strip())
|
||
|
|
|
||
|
|
|
||
|
|
async def server_version(c: SSHClient) -> str:
|
||
|
|
r = await c.sudo("docker version --format '{{.Server.Version}}'", timeout=30)
|
||
|
|
return r.stdout.strip()
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- контейнеры ----------
|
||
|
|
|
||
|
|
async def list_containers(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(c, "ps -a --format '{{json .}}'")
|
||
|
|
return [json.loads(l) for l in out.splitlines() if l.strip()]
|
||
|
|
|
||
|
|
|
||
|
|
async def container_action(c: SSHClient, cid: str, action: str) -> str:
|
||
|
|
assert action in ("start", "stop", "restart", "kill", "rm")
|
||
|
|
return await _docker(c, f"{action} {cid}")
|
||
|
|
|
||
|
|
|
||
|
|
async def container_logs(c: SSHClient, cid: str, tail: int = 200) -> str:
|
||
|
|
return await _docker(c, f"logs --tail {tail} {cid}")
|
||
|
|
|
||
|
|
|
||
|
|
async def container_inspect(c: SSHClient, cid: str) -> dict:
|
||
|
|
out = await _docker(c, f"inspect {cid}")
|
||
|
|
data = json.loads(out)
|
||
|
|
return data[0] if data else {}
|
||
|
|
|
||
|
|
|
||
|
|
async def container_stats(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(
|
||
|
|
c, "stats --no-stream --format '{{json .}}'", timeout=60
|
||
|
|
)
|
||
|
|
return [json.loads(l) for l in out.splitlines() if l.strip()]
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- образы ----------
|
||
|
|
|
||
|
|
async def list_images(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(c, "images --format '{{json .}}'")
|
||
|
|
return [json.loads(l) for l in out.splitlines() if l.strip()]
|
||
|
|
|
||
|
|
|
||
|
|
async def pull(c: SSHClient, image: str):
|
||
|
|
return c.run_stream(f"docker pull {image}", use_sudo=True)
|
||
|
|
|
||
|
|
|
||
|
|
async def remove_image(c: SSHClient, image: str) -> str:
|
||
|
|
return await _docker(c, f"rmi {image}")
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- тома ----------
|
||
|
|
|
||
|
|
async def list_volumes(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(c, "volume ls --format '{{json .}}'")
|
||
|
|
return [json.loads(l) for l in out.splitlines() if l.strip()]
|
||
|
|
|
||
|
|
|
||
|
|
async def remove_volume(c: SSHClient, name: str) -> str:
|
||
|
|
return await _docker(c, f"volume rm {name}")
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- сети ----------
|
||
|
|
|
||
|
|
async def list_networks(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(c, "network ls --format '{{json .}}'")
|
||
|
|
return [json.loads(l) for l in out.splitlines() if l.strip()]
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- система ----------
|
||
|
|
|
||
|
|
async def system_df(c: SSHClient) -> str:
|
||
|
|
return await _docker(c, "system df")
|
||
|
|
|
||
|
|
|
||
|
|
async def system_prune(c: SSHClient) -> str:
|
||
|
|
return await _docker(c, "system prune -f", timeout=600)
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- compose ----------
|
||
|
|
|
||
|
|
async def compose_projects(c: SSHClient) -> List[dict]:
|
||
|
|
out = await _docker(c, "compose ls --format json")
|
||
|
|
try:
|
||
|
|
return json.loads(out)
|
||
|
|
except json.JSONDecodeError:
|
||
|
|
return []
|
||
|
|
|
||
|
|
|
||
|
|
async def compose_action(c: SSHClient, workdir: str, action: str, timeout: int = 600) -> str:
|
||
|
|
assert action in ("up -d", "down", "restart", "pull", "ps", "logs")
|
||
|
|
return await _docker(c, f"compose -f {workdir}/docker-compose.yml {action}", timeout)
|