UI: карточка установленных аддонов на странице кластера; API аддонов и правки стилей
- Отдельная section.card после сводки ресурсов узлов, выравнивание бейджа Helm с заголовком - Расширение эндпоинтов аддонов, схемы, helm_addons, cluster_addons.js/html - Обновление api_routes.md
This commit is contained in:
+181
-11
@@ -358,18 +358,188 @@ def helm_list_all(cluster_name: str) -> list[dict[str, Any]]:
|
||||
return []
|
||||
|
||||
|
||||
def addon_status(cluster_name: str) -> dict[str, bool]:
|
||||
"""Какие аддоны установлены (по имени релиза и namespace)."""
|
||||
lst = helm_list_all(cluster_name)
|
||||
names_ns = {(str(x.get("name", "")), str(x.get("namespace", ""))) for x in lst if isinstance(x, dict)}
|
||||
return {
|
||||
"ingress_nginx": ("ingress-nginx", "ingress-nginx") in names_ns,
|
||||
"kube_prometheus_stack": ("kube-prometheus-stack", "monitoring") in names_ns,
|
||||
"metrics_server": ("metrics-server", "kube-system") in names_ns,
|
||||
"istio_base": ("istio-base", "istio-system") in names_ns,
|
||||
"istiod": ("istiod", "istio-system") in names_ns,
|
||||
"kiali": ("kiali-server", "istio-system") in names_ns,
|
||||
def helm_release_entry(
|
||||
lst: list[dict[str, Any]],
|
||||
release: str,
|
||||
namespace: str,
|
||||
) -> dict[str, Any] | None:
|
||||
"""Одна запись ``helm list -o json`` по имени релиза и namespace."""
|
||||
for x in lst:
|
||||
if not isinstance(x, dict):
|
||||
continue
|
||||
if str(x.get("name", "")) == release and str(x.get("namespace", "")) == namespace:
|
||||
return x
|
||||
return None
|
||||
|
||||
|
||||
def _chart_version_from_list_field(chart: str, release_name: str) -> str | None:
|
||||
"""
|
||||
Поле ``chart`` в выводе ``helm list`` имеет вид ``<имя_чарта>-<версия>`` (например ``ingress-nginx-4.11.2``).
|
||||
Для релиза Istio base в списке часто ``base-1.22.3``.
|
||||
"""
|
||||
chart = (chart or "").strip()
|
||||
if not chart:
|
||||
return None
|
||||
prefixes: dict[str, str] = {
|
||||
"ingress-nginx": "ingress-nginx-",
|
||||
"kube-prometheus-stack": "kube-prometheus-stack-",
|
||||
"metrics-server": "metrics-server-",
|
||||
"istio-base": "base-",
|
||||
"istiod": "istiod-",
|
||||
"kiali-server": "kiali-server-",
|
||||
}
|
||||
p = prefixes.get(release_name)
|
||||
if p and chart.startswith(p):
|
||||
v = chart[len(p) :].strip()
|
||||
return v or None
|
||||
return chart
|
||||
|
||||
|
||||
def helm_get_values_all_yaml(cluster_name: str, release: str, namespace: str) -> str:
|
||||
"""
|
||||
Эффективные values релиза: ``helm get values --all -o yaml`` (для правки при upgrade).
|
||||
"""
|
||||
kcfg = str(_kubeconfig_runtime_path(cluster_name))
|
||||
rc, out, err = _run_cmd(
|
||||
[
|
||||
"helm",
|
||||
"get",
|
||||
"values",
|
||||
release,
|
||||
"-n",
|
||||
namespace,
|
||||
"--all",
|
||||
"-o",
|
||||
"yaml",
|
||||
"--kubeconfig",
|
||||
kcfg,
|
||||
],
|
||||
timeout=120,
|
||||
)
|
||||
if rc != 0:
|
||||
raise HelmAddonError(
|
||||
(err or out or f"helm get values {release}/{namespace} код {rc}")[:8000],
|
||||
exit_code=rc,
|
||||
)
|
||||
return (out or "").strip()
|
||||
|
||||
|
||||
_VALUES_YAML_API_MAX = 131_072
|
||||
|
||||
|
||||
def trim_values_yaml_for_api(raw: str) -> str:
|
||||
"""Ограничение размера YAML в JSON-ответе API."""
|
||||
if len(raw) <= _VALUES_YAML_API_MAX:
|
||||
return raw
|
||||
return raw[: _VALUES_YAML_API_MAX - 120] + "\n\n# … YAML обрезан (лимит ответа API).\n"
|
||||
|
||||
|
||||
def helm_addon_snapshot(cluster_name: str) -> dict[str, Any]:
|
||||
"""
|
||||
Флаги установки, версии чартов из ``helm list`` и признак полного mesh Istio+Kiali.
|
||||
"""
|
||||
lst = helm_list_all(cluster_name)
|
||||
|
||||
def pair(rel: str, ns: str) -> tuple[bool, str | None]:
|
||||
e = helm_release_entry(lst, rel, ns)
|
||||
if not e:
|
||||
return False, None
|
||||
ch = str(e.get("chart") or "")
|
||||
ver = _chart_version_from_list_field(ch, rel)
|
||||
return True, ver
|
||||
|
||||
ing_on, ing_v = pair("ingress-nginx", "ingress-nginx")
|
||||
prom_on, prom_v = pair("kube-prometheus-stack", "monitoring")
|
||||
ms_on, ms_v = pair("metrics-server", "kube-system")
|
||||
base_on, base_v = pair("istio-base", "istio-system")
|
||||
istiod_on, istiod_v = pair("istiod", "istio-system")
|
||||
kiali_on, kiali_v = pair("kiali-server", "istio-system")
|
||||
mesh = bool(base_on and istiod_on and kiali_on)
|
||||
return {
|
||||
"ingress_nginx": ing_on,
|
||||
"ingress_nginx_chart_version": ing_v if ing_on else None,
|
||||
"kube_prometheus_stack": prom_on,
|
||||
"kube_prometheus_stack_chart_version": prom_v if prom_on else None,
|
||||
"metrics_server": ms_on,
|
||||
"metrics_server_chart_version": ms_v if ms_on else None,
|
||||
"istio_base": base_on,
|
||||
"istiod": istiod_on,
|
||||
"kiali_server": kiali_on,
|
||||
"istio_base_chart_version": base_v if base_on else None,
|
||||
"istiod_chart_version": istiod_v if istiod_on else None,
|
||||
"kiali_server_chart_version": kiali_v if kiali_on else None,
|
||||
"istio_mesh_ready": mesh,
|
||||
}
|
||||
|
||||
|
||||
def addon_status(cluster_name: str) -> dict[str, bool]:
|
||||
"""Какие аддоны установлены (по имени релиза и namespace). Совместимость со старым кодом."""
|
||||
s = helm_addon_snapshot(cluster_name)
|
||||
return {
|
||||
"ingress_nginx": bool(s["ingress_nginx"]),
|
||||
"kube_prometheus_stack": bool(s["kube_prometheus_stack"]),
|
||||
"metrics_server": bool(s["metrics_server"]),
|
||||
"istio_base": bool(s["istio_base"]),
|
||||
"istiod": bool(s["istiod"]),
|
||||
"kiali": bool(s["kiali_server"]),
|
||||
}
|
||||
|
||||
|
||||
def collect_installed_addons_values(cluster_name: str) -> dict[str, Any]:
|
||||
"""
|
||||
Для установленных релизов — версия чарта (как в snapshot) и YAML ``helm get values --all``.
|
||||
Ключи совпадают с логическими аддонами страницы UI.
|
||||
"""
|
||||
s = helm_addon_snapshot(cluster_name)
|
||||
out: dict[str, Any] = {}
|
||||
|
||||
if s["ingress_nginx"]:
|
||||
try:
|
||||
y = trim_values_yaml_for_api(
|
||||
helm_get_values_all_yaml(cluster_name, "ingress-nginx", "ingress-nginx"),
|
||||
)
|
||||
except HelmAddonError as e:
|
||||
y = f"# Не удалось прочитать values: {e}\n"
|
||||
out["ingress_nginx"] = {"chart_version": s["ingress_nginx_chart_version"], "values_yaml": y}
|
||||
|
||||
if s["kube_prometheus_stack"]:
|
||||
try:
|
||||
y = trim_values_yaml_for_api(
|
||||
helm_get_values_all_yaml(cluster_name, "kube-prometheus-stack", "monitoring"),
|
||||
)
|
||||
except HelmAddonError as e:
|
||||
y = f"# Не удалось прочитать values: {e}\n"
|
||||
out["kube_prometheus_stack"] = {"chart_version": s["kube_prometheus_stack_chart_version"], "values_yaml": y}
|
||||
|
||||
if s["metrics_server"]:
|
||||
try:
|
||||
y = trim_values_yaml_for_api(
|
||||
helm_get_values_all_yaml(cluster_name, "metrics-server", "kube-system"),
|
||||
)
|
||||
except HelmAddonError as e:
|
||||
y = f"# Не удалось прочитать values: {e}\n"
|
||||
out["metrics_server"] = {"chart_version": s["metrics_server_chart_version"], "values_yaml": y}
|
||||
|
||||
if s["istio_mesh_ready"]:
|
||||
try:
|
||||
kb = trim_values_yaml_for_api(
|
||||
helm_get_values_all_yaml(cluster_name, "istio-base", "istio-system"),
|
||||
)
|
||||
ki = trim_values_yaml_for_api(helm_get_values_all_yaml(cluster_name, "istiod", "istio-system"))
|
||||
kk = trim_values_yaml_for_api(
|
||||
helm_get_values_all_yaml(cluster_name, "kiali-server", "istio-system"),
|
||||
)
|
||||
except HelmAddonError as e:
|
||||
kb = ki = kk = f"# Не удалось прочитать values: {e}\n"
|
||||
out["istio_kiali"] = {
|
||||
"istio_chart_version": s["istiod_chart_version"] or s["istio_base_chart_version"],
|
||||
"kiali_chart_version": s["kiali_server_chart_version"],
|
||||
"istio_base_values_yaml": kb,
|
||||
"istiod_values_yaml": ki,
|
||||
"kiali_values_yaml": kk,
|
||||
}
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def _kubectl_apply_manifest(cluster_name: str, manifest: dict[str, Any]) -> tuple[int, str]:
|
||||
|
||||
Reference in New Issue
Block a user