Files
tg-admin-bot/services/docker.py

142 lines
4.4 KiB
Python

import asyncio
from datetime import datetime, timezone
from typing import Dict
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from services.runner import run_cmd
def container_uptime(started_at: str) -> str:
"""
started_at: 2026-02-06T21:14:33.123456789Z
"""
try:
start = datetime.fromisoformat(
started_at.replace("Z", "+00:00")
).astimezone(timezone.utc)
delta = datetime.now(timezone.utc) - start
days = delta.days
hours = delta.seconds // 3600
minutes = (delta.seconds % 3600) // 60
if days > 0:
return f"{days}d {hours}h"
if hours > 0:
return f"{hours}h {minutes}m"
return f"{minutes}m"
except Exception:
return "unknown"
async def build_docker_map(cfg) -> Dict[str, str]:
docker_cfg = cfg.get("docker", {})
result: Dict[str, str] = {}
# 1. autodiscovery
if docker_cfg.get("autodiscovery"):
rc, raw = await docker_cmd(["ps", "--format", "{{.Names}}"], timeout=20)
if rc == 0:
names = raw.splitlines()
patterns = docker_cfg.get("match", [])
for name in names:
if any(p in name for p in patterns):
result[name] = name
# 2. aliases override
aliases = docker_cfg.get("aliases", {})
for alias, real in aliases.items():
result[alias] = real
return result
async def discover_containers(cfg) -> Dict[str, str]:
"""
returns: alias -> real container name
"""
docker_cfg = cfg.get("docker", {})
result: Dict[str, str] = {}
# --- autodiscovery ---
if docker_cfg.get("autodiscovery"):
rc, raw = await docker_cmd(["ps", "--format", "{{.Names}}"], timeout=20)
if rc == 0:
found = raw.splitlines()
label = docker_cfg.get("label")
patterns = docker_cfg.get("match", [])
for name in found:
# label-based discovery
if label:
key, val = label.split("=", 1)
rc2, lbl = await docker_cmd(
["inspect", "-f", f"{{{{ index .Config.Labels \"{key}\" }}}}", name],
timeout=10
)
if rc2 == 0 and lbl.strip() == val:
result[name] = name
continue
# name-pattern discovery
if any(p in name for p in patterns):
result[name] = name
# --- manual aliases ALWAYS override ---
aliases = docker_cfg.get("aliases", {})
for alias, real in aliases.items():
result[alias] = real
return result
async def docker_cmd(args: list[str], timeout: int = 20):
rc, out = await run_cmd(["docker"] + args, timeout=timeout)
if rc == 0:
return rc, out
return await run_cmd(["sudo", "docker"] + args, timeout=timeout)
async def docker_watchdog(container_map, notify, bot, chat_id):
last = {}
while True:
if not last:
for alias, real in container_map.items():
rc, state = await docker_cmd(
["inspect", "-f", "{{.State.Status}}", real],
timeout=10
)
if rc != 0:
state = "error"
last[alias] = state.strip()
await asyncio.sleep(120)
continue
for alias, real in container_map.items():
rc, state = await docker_cmd(
["inspect", "-f", "{{.State.Status}}", real],
timeout=10
)
if rc != 0:
state = "error"
state = state.strip()
if last.get(alias) != state:
if state != "running":
kb = InlineKeyboardMarkup(
inline_keyboard=[[
InlineKeyboardButton(
text="🔄 Restart",
callback_data=f"wdrestart:{alias}"
)
]]
)
await bot.send_message(
chat_id,
f"🐳 {alias}: {state}",
reply_markup=kb,
)
else:
await notify(bot, chat_id, f"🐳 {alias}: {state}")
last[alias] = state
await asyncio.sleep(120)