"""Справочник версий Helm-чартов и сборка YAML values для редактора на странице аддонов. Автор: Сергей Антропов Сайт: https://devops.org.ru """ from __future__ import annotations import asyncio import logging from fastapi import APIRouter, HTTPException from core.helm_addons import ( HelmAddonError, compose_ingress_effective_values, compose_istio_mesh_preview_values, compose_kube_prometheus_effective_values, compose_metrics_server_effective_values, dump_helm_values_yaml, helm_addon_chart_versions_all, helm_repo_ensure, require_helm_binary, ) from models.schemas import ( HelmAddonChartVersionsResponse, HelmAddonComposeBatchRequest, HelmAddonComposeBatchResponse, HelmAddonComposeValuesRequest, HelmAddonComposeValuesResponse, ) logger = logging.getLogger("kind_k8s.api.helm_charts") router = APIRouter(tags=["helm-charts"]) def _compose_values_sync(req: HelmAddonComposeValuesRequest) -> HelmAddonComposeValuesResponse: """Синхронная сборка YAML (вызов из ``asyncio.to_thread``): ``helm show values`` + оверлеи.""" helm_repo_ensure() addon = req.addon if addon == "ingress-nginx": data = compose_ingress_effective_values(req.chart_version) return HelmAddonComposeValuesResponse(values_yaml=dump_helm_values_yaml(data)) if addon == "kube-prometheus-stack": u = (req.grafana_admin_user or "admin").strip() or "admin" pw = (req.grafana_admin_password or "").strip() or "ChangeMe12345" data = compose_kube_prometheus_effective_values(req.chart_version, u, pw) return HelmAddonComposeValuesResponse(values_yaml=dump_helm_values_yaml(data)) if addon == "metrics-server": data = compose_metrics_server_effective_values(req.chart_version) return HelmAddonComposeValuesResponse(values_yaml=dump_helm_values_yaml(data)) if addon == "istio-kiali": k, ib, ir = compose_istio_mesh_preview_values(req.istio_chart_version, req.kiali_chart_version) return HelmAddonComposeValuesResponse( values_yaml=dump_helm_values_yaml(k), istio_base_values_yaml=dump_helm_values_yaml(ib), istio_istiod_values_yaml=dump_helm_values_yaml(ir), ) raise RuntimeError(f"неизвестный addon: {addon}") def _compose_batch_sync(body: HelmAddonComposeBatchRequest) -> HelmAddonComposeBatchResponse: """Один ``helm repo update`` и шесть выводов ``helm show values`` (с оверлеями где нужно).""" helm_repo_ensure() u = (body.grafana_admin_user or "admin").strip() or "admin" pw = (body.grafana_admin_password or "").strip() or "ChangeMe12345" ing = dump_helm_values_yaml(compose_ingress_effective_values(body.ingress_chart_version)) prom = dump_helm_values_yaml( compose_kube_prometheus_effective_values(body.kube_prometheus_chart_version, u, pw), ) ms = dump_helm_values_yaml(compose_metrics_server_effective_values(body.metrics_server_chart_version)) k, ib, ir = compose_istio_mesh_preview_values(body.istio_chart_version, body.kiali_chart_version) return HelmAddonComposeBatchResponse( ingress_nginx_values_yaml=ing, kube_prometheus_values_yaml=prom, metrics_server_values_yaml=ms, kiali_values_yaml=dump_helm_values_yaml(k), istio_base_values_yaml=dump_helm_values_yaml(ib), istio_istiod_values_yaml=dump_helm_values_yaml(ir), ) @router.get( "/helm/chart-versions", response_model=HelmAddonChartVersionsResponse, summary="Версии чартов аддонов (для выпадающих списков)", ) async def get_helm_addon_chart_versions() -> HelmAddonChartVersionsResponse: """ Выполняет ``helm repo update`` и для каждого аддона — ``helm search repo --versions``. Результат кэшируется на ``KIND_K8S_HELM_VERSIONS_CACHE_SEC`` (по чарту). Пустой список — если helm недоступен или репозиторий не ответил. """ try: require_helm_binary() except HelmAddonError as e: logger.warning("helm недоступен для списка версий: %s", e) raise HTTPException(status_code=503, detail=str(e)) from e try: data = await asyncio.to_thread(helm_addon_chart_versions_all) except HelmAddonError as e: logger.exception("Ошибка получения версий чартов") raise HTTPException(status_code=500, detail=str(e)) from e return HelmAddonChartVersionsResponse(**data) @router.post( "/helm/addons/compose-values", response_model=HelmAddonComposeValuesResponse, summary="Собрать YAML values для редактора аддонов (helm show values + форма)", ) async def post_helm_addon_compose_values( body: HelmAddonComposeValuesRequest, ) -> HelmAddonComposeValuesResponse: """ Выполняет ``helm repo update`` (внутри compose-функций) и ``helm show values`` для выбранного аддона. Для **kube-prometheus-stack** в YAML подставляются логин и пароль Grafana из тела запроса; если пароль пуст, в preview используется временная строка ``ChangeMe12345`` (замените перед установкой). Для **istio-kiali** возвращаются три блока YAML: **kiali-server**, **istio/base**, **istio/istiod**. """ try: require_helm_binary() except HelmAddonError as e: raise HTTPException(status_code=503, detail=str(e)) from e try: return await asyncio.to_thread(_compose_values_sync, body) except HelmAddonError as e: logger.exception("compose-values: ошибка helm") raise HTTPException(status_code=500, detail=str(e)) from e @router.post( "/helm/addons/compose-values-batch", response_model=HelmAddonComposeBatchResponse, summary="Собрать все YAML values для страницы аддонов одним запросом", ) async def post_helm_addon_compose_values_batch( body: HelmAddonComposeBatchRequest, ) -> HelmAddonComposeBatchResponse: """ Один проход **``helm repo update``** и подстановка версий из селектов / полей формы на странице аддонов. Удобнее, чем четыре отдельных **compose-values**, когда UI обновляет все карточки сразу. """ try: require_helm_binary() except HelmAddonError as e: raise HTTPException(status_code=503, detail=str(e)) from e try: return await asyncio.to_thread(_compose_batch_sync, body) except HelmAddonError as e: logger.exception("compose-values-batch: ошибка helm") raise HTTPException(status_code=500, detail=str(e)) from e