Веб-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 и ядра кластеров.
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
# Описание REST API веб-интерфейса kind-k8s-develop
|
||||
|
||||
**Базовый префикс:** `/api/v1`
|
||||
**Автор:** Сергей Антропов — [devops.org.ru](https://devops.org.ru)
|
||||
|
||||
Интерактивная документация OpenAPI: после запуска `make docker up` откройте [http://127.0.0.1:6000/docs](http://127.0.0.1:6000/docs).
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/health
|
||||
|
||||
Проверка: `kind`/`kubectl` в PATH и ответ движка контейнеров (`docker info` / `podman info` по `CONTAINER_CLI`).
|
||||
`status`: `ok` — всё готово к созданию кластеров; `degraded` — чего-то не хватает (см. поля ниже).
|
||||
|
||||
**Пример ответа 200 (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"kind_in_path": true,
|
||||
"kubectl_in_path": true,
|
||||
"container_cli": "docker",
|
||||
"container_engine_ok": true,
|
||||
"container_engine_detail": null
|
||||
}
|
||||
```
|
||||
|
||||
**Если сокет Docker недоступен:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "degraded",
|
||||
"kind_in_path": true,
|
||||
"kubectl_in_path": true,
|
||||
"container_cli": "docker",
|
||||
"container_engine_ok": false,
|
||||
"container_engine_detail": "Cannot connect to the Docker daemon..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/versions
|
||||
|
||||
Список стабильных тегов `kindest/node` с Docker Hub (для выпадающего списка в UI).
|
||||
При `KIND_K8S_SKIP_VERSION_LIST=1` список пустой.
|
||||
|
||||
**Пример ответа 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tags": ["v1.32.0", "v1.31.4"],
|
||||
"skipped": false
|
||||
}
|
||||
```
|
||||
|
||||
**Пример при пропуске загрузки:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tags": [],
|
||||
"skipped": true,
|
||||
"reason": "KIND_K8S_SKIP_VERSION_LIST"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/stats
|
||||
|
||||
Сводная статистика для дашборда.
|
||||
|
||||
**Пример ответа 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"kind_clusters_count": 2,
|
||||
"local_cluster_dirs_count": 2,
|
||||
"total_workers_from_meta": 4,
|
||||
"jobs_total": 5,
|
||||
"jobs_recent_failed": 1
|
||||
}
|
||||
```
|
||||
|
||||
Поле `total_workers_from_meta` может быть `null`, если ни в одном `meta.json` нет `worker_nodes`.
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/clusters
|
||||
|
||||
Список имён: объединение `kind get clusters` и подкаталогов `clusters/*`.
|
||||
|
||||
**Пример ответа 200 (массив):**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "dev",
|
||||
"registered_in_kind": true,
|
||||
"has_local_kubeconfig": true,
|
||||
"meta": {
|
||||
"cluster_name": "dev",
|
||||
"kubernetes_version_tag": "v1.29.4",
|
||||
"node_image": "kindest/node:v1.29.4",
|
||||
"worker_nodes": 2,
|
||||
"kubeconfig_patched_for_host": true
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/jobs
|
||||
|
||||
Список последних фоновых заданий (создание кластера), от новых к старым. Данные только в памяти процесса.
|
||||
|
||||
**Query:** `limit` (1–200, по умолчанию 30).
|
||||
|
||||
**Пример ответа 200 (массив `JobView`):**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"job_id": "abc123",
|
||||
"kind": "create_cluster",
|
||||
"status": "success",
|
||||
"cluster_name": "dev",
|
||||
"created_at_utc": "2026-04-04T12:00:00+00:00",
|
||||
"message": "Кластер создан",
|
||||
"result": { "cluster_name": "dev", "kubernetes_version_tag": "v1.29.4" }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/clusters/{name}/kubeconfig
|
||||
|
||||
Скачать файл `kubeconfig` (ответ — тело файла, `Content-Disposition` с именем `kubeconfig-{name}.yaml`).
|
||||
|
||||
**Ошибка 404:** файла нет в `clusters/{name}/`.
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/clusters/{name}/workloads
|
||||
|
||||
`kubectl get nodes -o wide` и `kubectl get pods -A` по сохранённому kubeconfig.
|
||||
|
||||
**Пример ответа 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"cluster_name": "dev",
|
||||
"nodes_rc": 0,
|
||||
"nodes_output": "NAME STATUS ROLES ...",
|
||||
"pods_rc": 0,
|
||||
"pods_output": "NAMESPACE NAME READY STATUS ...",
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
Если kubeconfig нет: `"error": "Нет сохранённого kubeconfig..."`, остальные поля подов/узлов — `null`.
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/clusters/{name}
|
||||
|
||||
Детали и попытка `kubectl get nodes -o wide` с **сохранённого** `clusters/{name}/kubeconfig` (если файл есть).
|
||||
|
||||
**Пример ответа 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dev",
|
||||
"registered_in_kind": true,
|
||||
"has_local_kubeconfig": true,
|
||||
"kubeconfig_path": "/work/clusters/dev/kubeconfig",
|
||||
"meta": { "worker_nodes": 2 },
|
||||
"kubectl_get_nodes_rc": 0,
|
||||
"kubectl_get_nodes": "NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME\n..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## POST /api/v1/clusters
|
||||
|
||||
Создание кластера **в фоне** (ответ 202).
|
||||
|
||||
**Тело запроса (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dev",
|
||||
"kubernetes_version": "1.29.4",
|
||||
"workers": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Пример ответа 202:**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "a1b2c3d4e5f6...",
|
||||
"status": "queued",
|
||||
"message": "Создание кластера выполняется в фоне; опросите GET /api/v1/jobs/{job_id}"
|
||||
}
|
||||
```
|
||||
|
||||
**Ошибка 409 (кластер уже есть в kind):**
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Кластер с таким именем уже есть в kind"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/jobs/{job_id}
|
||||
|
||||
Статус фонового задания создания.
|
||||
|
||||
**В процессе (пример 200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "a1b2...",
|
||||
"kind": "create_cluster",
|
||||
"status": "running",
|
||||
"cluster_name": "dev",
|
||||
"created_at_utc": "2026-04-04T12:00:00+00:00",
|
||||
"message": null,
|
||||
"result": null
|
||||
}
|
||||
```
|
||||
|
||||
**Успех (пример 200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "a1b2...",
|
||||
"kind": "create_cluster",
|
||||
"status": "success",
|
||||
"cluster_name": "dev",
|
||||
"created_at_utc": "2026-04-04T12:00:00+00:00",
|
||||
"message": "Кластер создан",
|
||||
"result": {
|
||||
"cluster_name": "dev",
|
||||
"kubernetes_version_tag": "v1.29.4",
|
||||
"node_image": "kindest/node:v1.29.4",
|
||||
"workers": 2,
|
||||
"kubeconfig_path": "/work/clusters/dev/kubeconfig",
|
||||
"kubeconfig_patched_for_host": true,
|
||||
"nodes_ready": true,
|
||||
"nodes_ready_message": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Ошибка 404:**
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Задание не найдено"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DELETE /api/v1/clusters/{name}
|
||||
|
||||
`kind delete cluster` и удаление каталога `clusters/{name}/`.
|
||||
|
||||
**Пример ответа 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dev",
|
||||
"kind_delete_ok": true,
|
||||
"summary": "kind delete: OK; удалена папка /work/clusters/dev"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /
|
||||
|
||||
HTML-дашборд (не JSON): форма создания, таблица кластеров, ссылки на Swagger.
|
||||
Reference in New Issue
Block a user