127 lines
3.8 KiB
Python
127 lines
3.8 KiB
Python
|
|
"""启动时 Docker 能力检测(用 python docker SDK)。
|
|||
|
|
|
|||
|
|
返回三档:
|
|||
|
|
full — docker.sock 可访问,日志和 inspect 都能用
|
|||
|
|
partial — 能连 socket 但权限受限
|
|||
|
|
none — 完全不可用(没挂 socket / 包没装)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
from datetime import datetime, timezone
|
|||
|
|
from typing import Any, Dict, List, Optional
|
|||
|
|
|
|||
|
|
|
|||
|
|
_PROBE_CONTAINER = "meshsafe-api"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _try_client() -> Optional["docker.DockerClient"]:
|
|||
|
|
"""尝试创建 Docker client。失败返回 None。"""
|
|||
|
|
try:
|
|||
|
|
import docker # noqa: F401
|
|||
|
|
except ImportError:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
import docker
|
|||
|
|
# from_env() 会自动读 DOCKER_HOST,没设的话走 unix:///var/run/docker.sock
|
|||
|
|
client = docker.from_env(timeout=5)
|
|||
|
|
client.ping() # 真正连一下
|
|||
|
|
return client
|
|||
|
|
except Exception: # noqa: BLE001
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def detect_docker_access(mode: str = "auto") -> Dict[str, Any]:
|
|||
|
|
now = datetime.now(timezone.utc).isoformat()
|
|||
|
|
|
|||
|
|
sock_exists = os.path.exists("/var/run/docker.sock")
|
|||
|
|
client = _try_client()
|
|||
|
|
|
|||
|
|
if mode == "false":
|
|||
|
|
return {
|
|||
|
|
"mode": "false", "status": "none",
|
|||
|
|
"docker.sock_exists": sock_exists,
|
|||
|
|
"docker_python_available": client is not None,
|
|||
|
|
"can_logs": False, "can_inspect": False,
|
|||
|
|
"containers": None,
|
|||
|
|
"message": "配置强制关闭 Docker 能力(diagnostics.docker.enabled=false)",
|
|||
|
|
"detected_at": now,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if client is None:
|
|||
|
|
if mode == "true":
|
|||
|
|
msg = "配置强制启用但 Docker Python SDK 不可用 — pip install docker"
|
|||
|
|
else:
|
|||
|
|
msg = "Docker SDK 未就绪 — 日志匹配/容器状态类规则已跳过"
|
|||
|
|
return {
|
|||
|
|
"mode": mode, "status": "none",
|
|||
|
|
"docker.sock_exists": sock_exists,
|
|||
|
|
"docker_python_available": False,
|
|||
|
|
"can_logs": False, "can_inspect": False,
|
|||
|
|
"containers": None,
|
|||
|
|
"message": msg,
|
|||
|
|
"detected_at": now,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 真测
|
|||
|
|
can_logs = False
|
|||
|
|
can_inspect = False
|
|||
|
|
containers: Optional[List[str]] = None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
c = client.containers.get(_PROBE_CONTAINER)
|
|||
|
|
c.logs(tail=1)
|
|||
|
|
can_logs = True
|
|||
|
|
c.attrs # 读取属性
|
|||
|
|
can_inspect = True
|
|||
|
|
except Exception: # noqa: BLE001
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
containers = [c.name for c in client.containers.list(all=True)]
|
|||
|
|
except Exception: # noqa: BLE001
|
|||
|
|
containers = None
|
|||
|
|
|
|||
|
|
if can_logs and can_inspect:
|
|||
|
|
status = "full"
|
|||
|
|
msg = "Docker 访问正常 — 日志匹配 + 容器状态检测全部启用"
|
|||
|
|
elif can_logs or can_inspect:
|
|||
|
|
status = "partial"
|
|||
|
|
msg = f"Docker SDK 可用但权限受限(logs={can_logs}, inspect={can_inspect})"
|
|||
|
|
else:
|
|||
|
|
status = "none"
|
|||
|
|
msg = "docker.sock 未挂或权限不足 — 日志匹配类规则已跳过"
|
|||
|
|
|
|||
|
|
if mode == "true" and status != "full":
|
|||
|
|
msg = f"配置强制启用但检测结果为 {status} — 检查 docker.sock 挂载"
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"mode": mode, "status": status,
|
|||
|
|
"docker.sock_exists": sock_exists,
|
|||
|
|
"docker_python_available": True,
|
|||
|
|
"can_logs": can_logs,
|
|||
|
|
"can_inspect": can_inspect,
|
|||
|
|
"containers": containers,
|
|||
|
|
"message": msg,
|
|||
|
|
"detected_at": now,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def status_message(status: str) -> str:
|
|||
|
|
return {
|
|||
|
|
"full": "✅ Docker 能力完整",
|
|||
|
|
"partial": "⚠️ Docker 访问受限",
|
|||
|
|
"none": "ℹ️ Docker 日志检测未启用",
|
|||
|
|
}.get(status, status)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_client():
|
|||
|
|
"""获取共享 docker client(给 dumper / rules 用)。失败返回 None。"""
|
|||
|
|
try:
|
|||
|
|
import docker
|
|||
|
|
return docker.from_env(timeout=10)
|
|||
|
|
except Exception: # noqa: BLE001
|
|||
|
|
return None
|