Files
KindClustersDashboard/app/kubeconfig_patch.py
Sergey Antropoff ae961ef5fe first commit
2026-04-04 05:15:54 +03:00

158 lines
5.0 KiB
Python
Raw 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.

"""Правка kubeconfig kind для доступа к API с хоста (после create из контейнера).
Kind внутри Docker видит другой адрес apiserver; на хосте нужен 127.0.0.1:<порт>
из проброса `docker port <cluster>-control-plane 6443/tcp` (Podman — тот же клиент
`docker` к docker-совместимому сокету).
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
from __future__ import annotations
import logging
import os
import subprocess
from pathlib import Path
logger = logging.getLogger("kind_k8s.kubeconfig_patch")
def _container_cli() -> str:
"""CLI для `port` (обычно docker к сокету Docker или Podman)."""
return (os.environ.get("CONTAINER_CLI") or "docker").strip() or "docker"
def _control_plane_container_name(cluster_name: str) -> str:
return f"{cluster_name}-control-plane"
def _parse_docker_port_line(line: str) -> tuple[str, str] | None:
"""Строка вида '0.0.0.0:32768' или '127.0.0.1:32768' -> (host, port)."""
line = line.strip()
if ":" not in line:
return None
# IPv6 [::]:port
if line.startswith("["):
rb = line.rfind("]")
if rb == -1:
return None
host = line[1:rb]
rest = line[rb + 1 :].lstrip(":")
if not rest.isdigit():
return None
return host, rest
host, _, port = line.rpartition(":")
if not port.isdigit():
return None
host = host.strip()
return host, port
def _host_bind_for_kubeconfig(host: str) -> str:
if host in ("0.0.0.0", "::", ""):
return "127.0.0.1"
if host == "[::]":
return "127.0.0.1"
return host
def get_apiserver_host_port(cluster_name: str) -> tuple[str, str] | None:
"""Узнать (host, port) с хоста для доступа к apiserver."""
cli = _container_cli()
ctr = _control_plane_container_name(cluster_name)
p = subprocess.run(
[cli, "port", ctr, "6443/tcp"],
capture_output=True,
text=True,
)
if p.returncode != 0:
logger.info(
"Не удалось %s port %s 6443/tcp: %s",
cli,
ctr,
(p.stderr or p.stdout or "").strip(),
)
return None
for raw in (p.stdout or "").splitlines():
parsed = _parse_docker_port_line(raw)
if not parsed:
continue
h, port = parsed
return _host_bind_for_kubeconfig(h), port
return None
def _kubeconfig_cluster_name(kube_path: Path, logical_name: str) -> str:
"""Имя блока cluster в kubeconfig (обычно kind-<имя>)."""
p = subprocess.run(
[
"kubectl",
"--kubeconfig",
str(kube_path),
"config",
"view",
"-o",
'jsonpath={range .clusters[*]}{.name}{"\n"}{end}',
],
capture_output=True,
text=True,
)
names = [x.strip() for x in (p.stdout or "").splitlines() if x.strip()]
want = f"kind-{logical_name}"
if want in names:
return want
for n in names:
if n.startswith("kind-"):
return n
return want
def patch_kubeconfig_server_for_host(
*,
cluster_name: str,
kube_path: Path,
) -> bool:
"""
Подставить в kubeconfig server=https://<хост>:<порт> для доступа с хоста.
Порт берётся из ``docker port`` / аналога к сокету; для Podman — тот же клиент
при ``DOCKER_HOST`` на podman.sock.
"""
hp = get_apiserver_host_port(cluster_name)
if not hp:
print(
"Предупреждение: не удалось получить порт apiserver с хоста; "
"kubeconfig оставлен как выдал kind (с хоста может не открываться).",
)
return False
host, port = hp
server = f"https://{host}:{port}"
cluster_id = _kubeconfig_cluster_name(kube_path, cluster_name)
p = subprocess.run(
[
"kubectl",
"--kubeconfig",
str(kube_path),
"config",
"set-cluster",
cluster_id,
f"--server={server}",
],
capture_output=True,
text=True,
)
if p.returncode != 0:
err = (p.stderr or p.stdout or "").strip()
print(f"Предупреждение: kubectl config set-cluster не удался: {err}")
return False
print(f"Kubeconfig для хоста: apiserver → {server}")
return True
def should_patch_after_create() -> bool:
"""Патчить после create, если задано явно или kind шёл из контейнера."""
if os.environ.get("KIND_K8S_PATCH_KUBECONFIG", "").strip().lower() in ("1", "true", "yes", "да"):
return True
return os.environ.get("KIND_K8S_IN_CONTAINER", "").strip() == "1"