Files
KindClustersDashboard/scripts/detect_podman_socket.py
Sergey Antropoff eb063aec20 Веб-интерфейс: страница /clusters, навигация и крошки для кластеров
- Выделена страница списка кластеров, панель упрощена; nav_active и крошки
  ведут в раздел Кластеры; theme.js синхронизирует активную пилюлю по URL.
- Доработки дашборда, аддонов, журнала, стилей и API-документации.
- Поддержка Podman: docker-compose.podman.yml, скрипты сокета; Makefile и env.
2026-04-04 13:42:21 +03:00

186 lines
6.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Печатает в stdout путь к ``podman.sock`` или ``uid:gid`` владельца сокета.
Режимы:
* без аргументов — путь (для ``$(shell ...)`` в Makefile);
* ``--print-owner`` — ``uid:gid`` по ``stat`` найденного сокета (для ``KIND_K8S_CONTAINER_UIDGID``).
Логика пути: ``podman info`` (схема ``unix://`` снимается), ``podman machine inspect`` (без путей
в ``/var/folders/…`` на macOS — там API-сокет, **compose не смонтирует**: ``operation not supported``),
стабильные ``*.sock`` под ``~/.local/share/containers/podman/machine/``, затем типичные пути пользователя.
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
from __future__ import annotations
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
def _compose_cannot_bind_mount_socket(path_str: str) -> bool:
"""
На macOS сокет в ``/var/folders/…/T/podman/`` (например ``*-api.sock`` из machine inspect)
существует на диске, но bind-mount в контейнер через compose часто даёт
``mkdir … operation not supported`` — такой путь в ``CONTAINER_SOCKET`` использовать нельзя.
"""
if sys.platform != "darwin":
return False
norm = os.path.normpath(path_str)
return "/var/folders/" in norm
def _strip_unix_scheme(raw: str) -> str:
"""Путь на диске без префикса ``unix://`` / ``unix:`` (как в выводе ``podman info``)."""
s = (raw or "").strip()
if s.startswith("unix://"):
return s[7:].strip() or s
if s.startswith("unix:"):
rest = s[5:].lstrip("/")
return "/" + rest if rest else s
return s
def _podman_machine_socket_paths(podman_bin: str) -> list[str]:
"""
Пути к сокету на **хосте** из ``podman machine inspect`` (актуально для macOS/Windows).
На Linux без machine обычно возвращается ``[]``.
"""
try:
proc = subprocess.run(
[podman_bin, "machine", "inspect"],
capture_output=True,
text=True,
timeout=20,
)
if proc.returncode != 0 or not (proc.stdout or "").strip():
return []
data = json.loads(proc.stdout)
if not isinstance(data, list):
return []
out: list[str] = []
for m in data:
if not isinstance(m, dict):
continue
ci = m.get("ConnectionInfo") or {}
if not isinstance(ci, dict):
continue
ps = ci.get("PodmanSocket")
pth: str | None = None
if isinstance(ps, dict):
raw = ps.get("Path")
pth = str(raw).strip() if raw else None
elif isinstance(ps, str) and ps.strip():
pth = ps.strip()
if pth:
cleaned = _strip_unix_scheme(pth)
if not _compose_cannot_bind_mount_socket(cleaned):
out.append(cleaned)
return out
except (OSError, subprocess.TimeoutExpired, json.JSONDecodeError, TypeError, ValueError):
return []
def _darwin_stable_machine_sockets() -> list[str]:
"""Подкаталог machine на macOS: сокеты вне временного ``/var/folders``."""
base = Path.home() / ".local/share/containers/podman/machine"
if sys.platform != "darwin" or not base.is_dir():
return []
out: list[str] = []
try:
for p in sorted(base.rglob("*.sock"), key=lambda x: (len(x.parts), str(x))):
s = str(p)
if _compose_cannot_bind_mount_socket(s):
continue
out.append(s)
except OSError:
return []
return out
def _podman_socket_path_for_user() -> str:
xdg = (os.environ.get("XDG_RUNTIME_DIR") or "").strip()
if xdg:
return f"{xdg.rstrip('/')}/podman/podman.sock"
try:
return f"/run/user/{os.getuid()}/podman/podman.sock"
except AttributeError:
return "/run/user/1000/podman/podman.sock"
def podman_socket_owner_uidgid() -> str:
"""
UID:GID владельца найденного ``podman.sock`` на диске (``stat``).
Нужен для ``user:`` в compose: при Podman на macOS и в VM владелец сокета часто
**не совпадает** с ``id -u`` / ``id -g`` процесса ``make`` на хосте — иначе
``permission denied`` на смонтированный сокет внутри контейнера.
Если сокет недоступен — fallback на UID:GID текущего процесса.
"""
path_str = detect_podman_socket_path()
p = Path(path_str)
try:
if p.is_socket():
st = p.stat()
return f"{st.st_uid}:{st.st_gid}"
except OSError:
pass
try:
return f"{os.getuid()}:{os.getgid()}"
except AttributeError:
return "1000:1000"
def detect_podman_socket_path() -> str:
"""Путь к существующему сокету или типичный путь по умолчанию."""
cands: list[str] = []
podman_bin = shutil.which("podman")
if podman_bin:
try:
proc = subprocess.run(
[podman_bin, "info", "-f", "{{.Host.RemoteSocket.Path}}"],
capture_output=True,
text=True,
timeout=15,
)
if proc.returncode == 0:
line = (proc.stdout or "").strip().splitlines()
candidate = (line[0] if line else "").strip()
if candidate and "<no value>" not in candidate.lower():
cands.append(_strip_unix_scheme(candidate))
except (OSError, subprocess.TimeoutExpired):
pass
# Реальный файл на хосте (без macOS temp api.sock — см. _compose_cannot_bind_mount_socket).
cands.extend(_podman_machine_socket_paths(podman_bin))
cands.extend(_darwin_stable_machine_sockets())
cands.append(_podman_socket_path_for_user())
for path in cands:
cleaned = _strip_unix_scheme(path)
if _compose_cannot_bind_mount_socket(cleaned):
continue
p = Path(cleaned).expanduser()
try:
if p.is_socket():
return str(p.resolve())
except OSError:
continue
return str(Path(_strip_unix_scheme(_podman_socket_path_for_user())).expanduser())
def main() -> None:
if len(sys.argv) > 1 and sys.argv[1] == "--print-owner":
sys.stdout.write(podman_socket_owner_uidgid())
return
sys.stdout.write(detect_podman_socket_path())
if __name__ == "__main__":
main()