Files
KindClustersDashboard/app/main.py
T
inecs 73ae5d7032 Веб-UI FastAPI, REST API v1, интерактивный setup без env.example
- Дашборд (Jinja2 + static), управление кластерами kind, задания и kubeconfig.
- API: health, stats, clusters CRUD, versions, jobs; документация app/docs/api_routes.md.
- Docker Compose: том app, uvicorn reload, KIND_K8S_PATCH_KUBECONFIG по умолчанию 1.
- setup_env_interactive.py: список переменных в скрипте, удалён env.example.
- Makefile: явный префикс docker/podman; прочие правки CLI и ядра кластеров.
2026-04-04 05:39:53 +03:00

81 lines
2.9 KiB
Python

"""Веб-интерфейс и REST API для управления локальными кластерами kind (порт по умолчанию 6000).
Запуск в контейнере: ``python3 -m uvicorn main:app --host 0.0.0.0 --port 6000`` из каталога ``/opt/kind-k8s/app``
или через ``make docker up`` / ``make podman up``.
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
from __future__ import annotations
import logging
import os
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from api.v1.router import api_router
from core.config import get_settings
_BASE = Path(__file__).resolve().parent
logger = logging.getLogger("kind_k8s.web")
def _configure_logging() -> None:
"""Единая настройка логов для uvicorn и модулей kind-k8s."""
if logging.root.handlers:
return
level = logging.DEBUG if os.environ.get("KIND_K8S_DEBUG", "").strip().lower() in ("1", "true", "yes", "да") else logging.INFO
logging.basicConfig(level=level, format="%(levelname)s %(name)s: %(message)s")
logger.info("Логирование инициализировано, уровень=%s", logging.getLevelName(level))
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Старт и остановка приложения."""
_configure_logging()
settings = get_settings()
logger.info("Запуск FastAPI «%s»", settings.app_title)
yield
logger.info("Остановка FastAPI")
settings = get_settings()
app = FastAPI(title=settings.app_title, lifespan=lifespan)
app.include_router(api_router, prefix="/api/v1")
_templates_dir = _BASE / "templates"
_static_dir = _BASE / "static"
if _static_dir.is_dir():
app.mount("/static", StaticFiles(directory=str(_static_dir)), name="static")
templates = Jinja2Templates(directory=str(_templates_dir))
@app.get("/", response_class=HTMLResponse, summary="Веб-интерфейс")
async def dashboard(request: Request) -> HTMLResponse:
"""Главная страница с формой создания и таблицей кластеров."""
if not _templates_dir.is_dir():
return HTMLResponse(
content="<p>Шаблоны не найдены. Ожидается каталог app/templates/</p>",
status_code=500,
)
return templates.TemplateResponse(
"dashboard.html",
{
"request": request,
"app_title": settings.app_title,
},
)
@app.get("/ui", include_in_schema=False)
async def ui_redirect() -> RedirectResponse:
"""Удобный алиас на корень UI."""
return RedirectResponse(url="/", status_code=307)