from __future__ import annotations import json import logging from pathlib import Path from app.models import Mirror from app.services.host import host_path, run_on_host logger = logging.getLogger(__name__) DAEMON_JSON = "etc/docker/daemon.json" INVALID_DAEMON_KEYS = ("_mirror_manager",) def sanitize_daemon_config(config: dict) -> dict: """Remove keys that Docker daemon does not accept.""" for key in INVALID_DAEMON_KEYS: config.pop(key, None) return config def sanitize_daemon_json_file(path: Path | None = None) -> bool: """Strip invalid keys from daemon.json on disk. Returns True if file was modified.""" path = path or host_path(DAEMON_JSON) if not path.exists(): return False try: config = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: return False cleaned = sanitize_daemon_config(dict(config)) if cleaned == config: return False path.write_text(json.dumps(cleaned, indent=2, ensure_ascii=False), encoding="utf-8") return True def verify_docker_running() -> dict: active = run_on_host(["systemctl", "is-active", "docker"], timeout=30) if active.returncode != 0: return { "success": False, "error": "Docker بعد از restart بالا نیامد — تنظیمات daemon.json را بررسی کنید", } info = run_on_host(["docker", "info"], timeout=30) if info.returncode != 0: return { "success": False, "error": (info.stderr or info.stdout or "docker info ناموفق")[:500], } return {"success": True} def read_current_docker() -> dict: path = host_path(DAEMON_JSON) if not path.exists(): return {"exists": False, "config": {}} try: config = json.loads(path.read_text(encoding="utf-8")) has_invalid = any(key in config for key in INVALID_DAEMON_KEYS) return { "exists": True, "config": config, "has_invalid_keys": has_invalid, } except json.JSONDecodeError: return {"exists": True, "config": {}, "parse_error": True} def apply_docker(mirror: Mirror) -> dict: if not mirror.url: return {"success": False, "error": "URL رجیstry داکر تعریف نشده"} path = host_path(DAEMON_JSON) path.parent.mkdir(parents=True, exist_ok=True) config: dict = {} if path.exists(): try: config = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: config = {} config = sanitize_daemon_config(config) config["registry-mirrors"] = [mirror.url.rstrip("/")] path.write_text(json.dumps(config, indent=2, ensure_ascii=False), encoding="utf-8") result = run_on_host(["systemctl", "restart", "docker"], timeout=120) if result.returncode != 0: return { "success": False, "error": (result.stderr or result.stdout or "خطا در restart docker")[:500], } health = verify_docker_running() if not health.get("success"): return health return { "success": True, "message": f"Docker registry mirror اعمال شد: {mirror.name}", "url": mirror.url, "warning": "سرویس Docker restart شد — containerها موقتاً قطع می‌شوند", }