182 lines
11 KiB
Markdown
182 lines
11 KiB
Markdown
# План реализации: Coordinator Admin UI и Worker Setup UI
|
||
|
||
> Статус: **реализовано (M1–M3), E2E проверено**. Визуал утверждён — мокапы:
|
||
> [`ui-mockups/coordinator-admin.html`](ui-mockups/coordinator-admin.html),
|
||
> [`ui-mockups/worker-setup.html`](ui-mockups/worker-setup.html).
|
||
> Два UI живут в **разных бинарниках**: админка кластера — в `coordinator`
|
||
> (`/ui/admin`), визард установки воркера — в `worker-agent`
|
||
> (`worker-agent setup`, локально на 127.0.0.1).
|
||
|
||
## 1. Цель
|
||
|
||
1. **Coordinator Admin UI** — консоль админа распределённого кластера:
|
||
System, Jobs, Workers, Users & keys, Workloads, Metrics, Settings.
|
||
Реальные данные, никакой симуляции; только роль `admin`.
|
||
2. **Worker Setup UI** — локальный визард в бинарнике `worker-agent` для
|
||
машины, на которой стоит только воркер: подключение (URL + токен/ключ),
|
||
параметры машины, preflight-проверка, запуск/остановка, статус и лог.
|
||
|
||
## 2. Что уже есть в коде (фундамент)
|
||
|
||
- `requireAdmin` middleware и `/ui/admin` (`internal/transport/http/ui_admin.go`)
|
||
— сейчас минимальная панель: прокси `promote/demote/verify/unverify` в
|
||
userservice.
|
||
- Userservice (`internal/userservice/`): `POST /users/{id}/promote|demote|
|
||
verify|unverify` (admin), `GET/POST/DELETE /worker-keys` (per-user),
|
||
`POST /worker-tokens/exchange`. **Нет**: list users, list all keys.
|
||
- `domain.Worker.TrustLevel` (`trusted`/`untrusted`) + quorum для untrusted —
|
||
колонка в БД есть, нужен только метод смены и отображение.
|
||
- `domain.Job.OwnerID *uuid.UUID` — владелец джобы из JWT `sub` (может быть nil).
|
||
- Каталог ворклоадов `internal/workloads` (embedded `workloads.json`) —
|
||
enable/disable кладём поверх через таблицу настроек.
|
||
- UIReadRepo (`ui_read_repo.go` в обоих движках) — bounded read model для UI.
|
||
- Агент целиком конфигурируется env (таблица в `mkdocs/sdk/worker-integration.md`);
|
||
`--config` маппится на ту же поверхность `agent.Config`.
|
||
|
||
## 3. Coordinator Admin UI
|
||
|
||
### 3.1 Страницы ↔ API ↔ данные
|
||
|
||
Все API — JSON, под `withUISession` + `requireAdmin`, префикс
|
||
`/ui/admin/api/`. Страница `admin.html` — оболочка из мокапа (сайдбар,
|
||
7 разделов), данные подтягивает JS через fetch (паттерн dashboard).
|
||
|
||
| Раздел | API | Данные |
|
||
| --- | --- | --- |
|
||
| System | `GET /system` | version, uptime, storage stats, health (db/userservice/reducer), node info |
|
||
| Jobs | `GET /jobs?status=&page=` | пагинированный список + счётчики по статусам; owner email резолвится через userservice |
|
||
| Workers | `GET /workers`, `POST /workers/{id}/trust` | список + trust, смена trust (trusted/untrusted) |
|
||
| Users & keys | `GET /users`, `POST /users/{id}/role`, `GET /worker-keys`, `POST /worker-keys/{id}/revoke` | прокси/агрегация userservice |
|
||
| Workloads | `GET /workloads`, `POST /workloads/{name}/enabled` | каталог + persisted enabled-флаг |
|
||
| Metrics | `GET /metrics` | jobs/day (7d), jobs by workload, shards, failure rate, avg shard time |
|
||
| Settings | `GET /settings`, `POST /token/reveal` | read-only конфиг + reveal токена (audit-лог) |
|
||
|
||
### 3.2 Userservice — новые эндпоинты
|
||
|
||
- `GET /users` (admin) — `id, email, role, verified, created_at`.
|
||
- `GET /worker-keys/all` (admin) — все ключи + owner email.
|
||
- Репозитории: `ListUsers(ctx)`, `ListWorkerKeysAll(ctx)` в `memstore` и
|
||
`storage/sqlite` (+ тесты). Координатор вызывает их через
|
||
`callUserserviceAuthed` и отдаёт в свой bounded API — браузер юзерсервис
|
||
не касается.
|
||
|
||
### 3.3 Storage координатора — новые методы
|
||
|
||
- `WorkerRepository.SetTrust(ctx, id, trust)` — оба движка + тесты.
|
||
- `UIReadRepo.ListJobsPaginated(ctx, status string, limit, offset int)`
|
||
→ `([]UIJob, total int, counts map[string]int)` — фильтр по статусу,
|
||
счётчики для табов.
|
||
- `UIReadRepo.JobMetrics(ctx, since time.Time)` → jobs/day, by workload,
|
||
shards completed/failed, avg shard duration (из `tasks`).
|
||
- `UIReadRepo.StorageStats(ctx)` → суммы байт по kind артефактов
|
||
(datasets/artifacts) + размер файла БД (sqlite: `page_count*page_size`;
|
||
postgres: `pg_database_size(current_database())`).
|
||
- Миграция `0002_workload_settings` (оба движка):
|
||
`workload_settings(workload TEXT PRIMARY KEY, enabled BOOL NOT NULL,
|
||
updated_at TIMESTAMP NOT NULL)`; отсутствие строки = enabled (default).
|
||
Repo: `WorkloadSettingsRepo{ List, Set }`.
|
||
- **Enforcement**: `SubmitDataset` отклоняет disabled-ворклоад;
|
||
`/ui/api/workloads` и форма new-job помечают disabled (скрываем из выбора).
|
||
|
||
### 3.4 Usecase — `internal/usecase/admin.go`
|
||
|
||
`AdminSystem`, `ListJobsAdmin(status,page)` (+owner emails), `ListWorkersAdmin`,
|
||
`SetWorkerTrust`, `ListUsersAdmin`, `SetUserRole` (через userservice promote/demote),
|
||
`ListWorkerKeysAdmin`, `RevokeWorkerKeyAdmin`, `ListWorkloadsAdmin`,
|
||
`SetWorkloadEnabled`, `AdminMetrics`, `RevealWorkerToken` (читает
|
||
`worker.token`/env, пишет audit-лог).
|
||
|
||
### 3.5 Шаблон
|
||
|
||
`templates/admin.html` заменяется на дизайн мокапа: сайдбар (Operate /
|
||
Access / Platform), topbar с env-бейджем (serve/postgres, addr), 7 разделов,
|
||
рендер через JS. Имя ворклоада — реальное `descriptor-batch`.
|
||
|
||
## 4. Worker Setup UI (бинарник `worker-agent`)
|
||
|
||
### 4.1 Новые файлы
|
||
|
||
```
|
||
coordinator/internal/agent/
|
||
configfile.go # ConfigFile (json), Load/Save (0600), путь по умолчанию
|
||
check.go # CheckCoordinator(url, auth) — health + версии + python/scimesh
|
||
setupui/
|
||
server.go # локальный HTTP 127.0.0.1:12700, API + запуск/остановка
|
||
template.html # визард + статус (по мокапу), go:embed
|
||
coordinator/cmd/worker-agent/main.go # + setup / --config / --check
|
||
```
|
||
|
||
### 4.2 CLI
|
||
|
||
- `worker-agent setup [--port 12700] [--no-open]` — визард; печатает URL,
|
||
открывает браузер.
|
||
- `worker-agent --config <path>` — демон из JSON-конфига; env имеет приоритет.
|
||
- `worker-agent --check [--coordinator-url URL]` — пинг `/health`, auth
|
||
(token → claim-endpoint 401/200 probe или exchange для ключа), python3 +
|
||
`import scimesh`; exit 0/1. Используется визардом на шаге 3.
|
||
|
||
### 4.3 config.json
|
||
|
||
`~/.scimesh-worker/config.json` (переопределяется `WORKER_CONFIG`), права 0600:
|
||
```json
|
||
{
|
||
"coordinator_url": "http://192.168.1.10:8080",
|
||
"token": "…", // или
|
||
"worker_key": "…", "userservice_url": "http://…:8081",
|
||
"work_dir": "…", "worker_name": "emil-laptop",
|
||
"cpu_count": 8, "memory_mb": 16384,
|
||
"task_runner": ["python", "-m", "scimesh.worker.task"]
|
||
}
|
||
```
|
||
|
||
### 4.4 API визарда (только 127.0.0.1, без auth)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/status` | конфиг (секрет маскирован), running (pid alive), статистика из лога, runtime |
|
||
| POST | `/api/config` | валидация + сохранение `config.json` (0600) |
|
||
| POST | `/api/test` | preflight: coordinator reachable, auth ok, python, scimesh |
|
||
| POST | `/api/start` | spawn `worker-agent --config <path>` (лог → `worker.log`, pid-файл) |
|
||
| POST | `/api/stop` | SIGTERM по pid-файлу |
|
||
| GET | `/api/logs?tail=` | хвост `worker.log` |
|
||
|
||
Статистика — парсинг лога (registered/claimed/completed/failed/heartbeat).
|
||
Spawner — интерфейс, в тестах подменяется.
|
||
|
||
## 5. Милестоуны
|
||
|
||
- **M1. Admin-фундамент** — ✅ done: оболочка `admin.html` по мокапу +
|
||
`GET /system` (storage stats, health, node) + `GET /jobs`
|
||
(фильтр+пагинация+счётчики) + `GET /metrics`; тесты на sqlite, 403 для
|
||
не-админа.
|
||
- **M2. Access & Platform** — ✅ done: userservice `ListUsers`/
|
||
`ListWorkerKeysAll` + admin revoke (mem+sqlite+http), `SetTrust` (оба
|
||
движка), users/keys/trust разделы, `workload_settings` (миграции 0002/
|
||
0014) + enforcement в `SubmitDataset` и форме, settings + audited token
|
||
reveal. Все секции админки на живых данных.
|
||
- **M3. Worker Setup** — ✅ done: `configfile.go` (0600), `--config` (env
|
||
wins), `--check`, `setupui` (сервер на 127.0.0.1, 6 API, шаблон по мокапу),
|
||
тесты (roundtrip, права, check, API с fake supervisor).
|
||
- **M4. E2E + docs** — ✅ done: браузерный E2E (admin видит реальные
|
||
данные; визард запустил реального воркера → `wizard-machine online` в
|
||
админке), обновлены `mkdocs/index.md` и `mkdocs/standalone.md`
|
||
(`worker-agent setup`), README, статусы CTX-19/CTX-20 в `PLAN.md`.
|
||
|
||
## 6. Тестирование
|
||
|
||
- **Go unit**: usecase admin (fakes + sqlite), repos обоих движков
|
||
(SetTrust, pagination, metrics, settings, storage stats), userservice
|
||
List/ListAll (mem+sqlite), HTTP permission (user → 403 на все `/ui/admin/api/*`),
|
||
enforcement disabled-ворклоада, configfile/check/setupui агента.
|
||
- **Go integration (postgres)**: миграция 0002 и parity новых методов —
|
||
через существующий docker-хелпер, skip без docker.
|
||
- **E2E браузер**: M4.
|
||
|
||
## 7. Открытые решения (зафиксировано)
|
||
|
||
- Локальный визард без аутентификации — слушает только 127.0.0.1.
|
||
- Settings-раздел v1 — read-only + reveal токена; редактирование конфигурации
|
||
и prune/reset (danger zone) — v2, в UI помечены как таковые.
|
||
- `owner` джобы: email из userservice по `OwnerID`; при nil — «cluster token».
|
||
- Статистика воркера в визарде — из лога, без новых эндпоинтов координатора.
|