15 KiB
Анализ репозитория fission-console
Составлен: 2026-05-21. Основан только на чтении исходного кода + истории git.
Что это
Web UI + API proxy для управления Fission (Kubernetes-native serverless). Написан на Go. Работает внутри K8s-кластера. Пользователи аутентифицируются через Deck API (облачный SSO).
Архитектура — поток данных
Browser (SPA) → Console API → K8s API (CRD — dynamic client)
→ Fission Router (invoke proxy — /fission-function)
→ storagesvc (архивы → S3 ngcloud msk-1, бакет sless-functions)
→ Deck API (auth — prod/dev/test)
→ PostgreSQL (billing invocations)
→ Grafana (дашборды — по одному Org на namespace)
Пакеты
| Пакет | Роль |
|---|---|
cmd/server/ |
Точка входа: парсинг env vars, создание зависимостей, запуск HTTP |
internal/api/ |
Ядро: все HTTP-хендлеры (29 файлов) |
internal/auth/ |
Аутентификация: Deck API + Demo (test fallback) |
internal/cloud/ |
Namespace lifecycle: квата, NetworkPolicy, одноразовый создаватель |
internal/fission/ |
Константы GVR + SetupFissionNamespace + EnsureEnvironment |
internal/model/ |
Общие типы: CreateFunctionRequest, LangEnvMap |
internal/runtime/ |
Упаковка кода: zip, ESM-обёртка NodeJS, python/phh/ruby/go |
internal/billing/ |
Статистика: PostgreSQL (NoopStore если BILLING_DSN пуст) |
internal/stats/ |
Grafana: Organization/dashboard per namespace |
ui/ |
Embed SPA (index.html), отдельный /cron/ UI |
Фичи — детальный разбор
1. Аутентификация
Файлы: auth.go, middleware.go, handlers.go (строка handleAuth)
POST /console/api/auth— тело{token, env}. Валидация через Deck API. Возвращает{ok, env, namespace, email}.- Middleware — каждый приватный роут →
Authenticator.Authenticate(token, env). Без токена → 401. - Namespace =
"fission-"+ hex(SHA256(sub)[:8]). Для test-пользователей:"fission-test-"+ hex(SHA256(sub)[:8]). - DeckAuthenticator — HTTP-запрос к
deck-api-{env}.ngcloud.ru/api/v1/user. Кеш вsync.Map(ключ =env:token, значение = expiry time). Таймаут 5s. - DemoAuthenticator — для тестовых стендов: sub = email = token, без проверки.
- MultiAuthenticator — последовательно пробует JWT → Demo.
2. Управление Namespace
Файлы: cloud/tenant.go, cloud/quota.go, cloud/network.go
- EnsureUserNS — единая точка входа. Singleflight (параллельные запросы для одного NS ждут на chan). Семафор: не более 3 одновременных созданий. In-memory кэш уже созданных NS.
- Что создаётся при первом запросе пользователя:
- Namespace с лейблами
managed-by=fission-console,fission.io/managed=true - ServiceAccounts:
fission-fetcher,fission-builder - RoleBindings: для executor, router, buildermgr, kubewatcher, timer, fetcher, builder
- ResourceQuota (из env vars, по умолчанию: CPU req 1/lim 12, mem 2Gi/6Gi, pods 30, functions 20)
- LimitRange (дефолтный CPU 500m, mem 256Mi на контейнер)
- NetworkPolicy
deny-cross-tenant— ingress только из своего NS, fission NS, kube-system
- Namespace с лейблами
- ExpiryReaper — горутина в main, раз в 5 минут (REAPER_INTERVAL). Удаляет
функции с аннотацией
expires_atв прошлом. - NSReconciler — сверяет FISSION_RESOURCE_NAMESPACES.
3. CRUD функций
Файлы: function_code.go, function_archive.go, function_crud.go,
function_clone.go, storagesvc.go
Создание из кода (POST /console/api/functions, JSON)
- Тело:
{name, language, code, deps?, entrypoint?, route?, methods?, timeout?, ttl?} - Валидация: имя
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$, ≤57 символов, код ≤1MB. - Языки: python, nodejs, php, ruby, go.
- Порядок: Package CRD → Function CRD → HTTPTrigger CRD. При ошибке откат.
- Типы пакетов:
- Python: zip с кодом + опционально requirements.txt (если deps не пуст)
- NodeJS: ESM-wrapped zip (package.json + main.js с
__invokedispatcher) - PHP: zip с
main.php+ опционально composer.json - Ruby: zip с
handler.rb+ опционально Gemfile - Go: source package (builder компилирует .so плагин)
- Загрузка: через storagesvc → S3 (ngcloud msk-1, бакет
sless-functions). Fallback на type:literal если storagesvcURL пуст. - TTL: парсит
1h,30m,7d,24h. Ставит аннотациюexpires_atна Function CRD вmetadata.annotations. - Env vars: сохраняются в аннотации
fission-console/env-vars(JSON).
Создание из архива (POST /console/api/functions, multipart/form-data)
- Поле формы:
archive(zip-файл). - Проверка magic bytes:
PK(0x50 0x4B). - Размер ≤32MB.
- Загружается в storagesvc как есть, без модификации.
- Аннотация
fission-console/source-type: "archive". - Поля формы:
name, language/environment, entrypoint, route, methods, timeout, ttl.
GET функции (GET /console/api/functions/:name)
- Возвращает: name, namespace, environment, package, entrypoint, timeout, created_at, updated_at, code, deps, source_type, archive_filename, route, methods, env_vars, raw CRD.
- Извлечение кода: пробует source.literal → deployment.literal → URL
(через storagesvc download для type:url). Для NodeJS распознаёт и анрапает
ESM-обёртку (
decodeArchiveBytesToSourceвpackage.go).
Обновление кода (PUT /console/api/functions/:name/code)
- Тело:
{code, deps?, timeout?} - Создаёт новый Package (новое имя = cache miss в executor). Старый удаляется.
- Та же сборка архива что и при создании (buildDeployArchive).
Обновление архива (PUT /console/api/functions/:name/archive)
- multipart/form-data, поле
archive. Загружается в storagesvc, новый Package.
Клонирование (POST /console/api/functions/:name/clone)
- Тело:
{new_name, route?} - Для type:literal — копирует base64 literal в новый Package.
- Для type:url — скачивает архив из storagesvc → заливает новый → новый Package.
- Создаёт новый Function + HTTPTrigger. Для Go копирует source-пакет.
- При ошибке откатывает созданные объекты.
DELETE (DELETE /console/api/functions/:name)
- Каскадное: удаляет HTTPTrigger → Package CRD → удаляет архив из storagesvc (DELETE /v1/archive?id=... через storagesvc).
- Проверяет что функция реально удалена из K8s.
Timeout, Logs, EnvVars
PUT /functions/:name/timeout— патчит толькоspec.functionTimeout(без замены кода/архива).GET /functions/:name/logs— читает логи пода функции через K8s API (rest client,pod/log).GET /functions/:name/envvars/PUT /functions/:name/envvars— аннотацияfission-console/env-vars.
4. Invoke (вызов функций)
Файл: function_invoke.go
POST /console/api/functions/:name/invoke— для UI. Проксирует через Fission Router. URL:routerURL/fission-function/<ns>/<name>. Ответ:{status, body, headers, cold_start, response_raw, pod, duration_ms}. Записывает в billing./fn/<route>— публичный gateway. Строит URL по маршруту, проксирует тело/заголовки (кроме служебных). Таймаут изspec.functionTimeout./fission-function/<ns>/<name>— внутренний gateway для cron/timer/webhook.- JWT-токен router: ServiceAccount token из
/var/run/secrets/...(или SA_TOKEN_PATH), кешируется на 12ч вsync.Mutex.
5. Триггеры
Файлы: timetriggers.go, mqtriggers.go, kwtriggers.go
- HTTP Triggers — создаются автоматически при создании функции.
Чтение списка: GET
/console/api/httptriggers. - Time Triggers — CRUD через
/console/api/timetriggers. Поля:name, functionName, cron, method, subpath. Создаются какfission.io/v1 TimeTriggerCRD. - MQ Triggers —
/console/api/mqtriggers. Поля:name, functionName, queue, sqsEndpoint, accessKey, secretKey. Создаёт Deployment (sqs-consumer)- Secret с credentials в NS пользователя. sqs-consumer поллит SQS → вызывает функцию через JWT-authenticated router.
- Kubernetes Watch Triggers —
/console/api/kwtriggers. Поля:name, functionName, resourceType, namespace, labelSelector.
6. AI/Lint фичи
Файлы: ai_check.go, lint_archive.go, explain_archive.go, ai_prompts.go
POST /ai/check— синтаксическая проверка кода. Реальные линтеры (не LLM):node --check,python3 -m py_compile,ruby -c,php -l, Go черезgo/parserпрямо в процессе.POST /ai/lint-archive— принимает zip, линтит каждый файл с известным расширением. Защита от zip bomb: max 100KB архив, max 100KB распакованных. Опционально: проверка entrypoint и соответствия языка.POST /ai/explain-archive— извлекает код из архива (max 20KB), отправляет в LLM через OpenRouter API с вопросом "что делает?".POST /ai/ask— chat/генерация кода через LLM. 4 режима codegen: генерация с нуля, объяснение, исправление ошибки, универсальный. Плюс explain и chat режимы.- Prompts (
ai_prompts.go) — шаблоны для каждого языка с сигнатурами функций (Python:def main(event, context), NodeJS:module.exports, PHP:function handler(...), Ruby:def handler(context), Go:func Handler(...)).
7. Billing (статистика)
Файлы: billing/billing.go, billing/pg.go, billing/noop.go,
billing/factory.go
- Интерфейс Store —
RecordInvocation(Invocation). Асинхронный, fire-and-forget. - pgStore — PostgreSQL через pgxpool. Таблица
invocations. Пулы: max 4 conn, min 1. Каждая запись в своей горутине. - NoopStore — если
BILLING_DSNне задан или не удалось подключиться — статистика молча отключена. - Поля Invocation: Namespace, FunctionName, TriggerType (http/cron/console/event), Route, HTTPMethod, StartedAt, DurationMS, StatusCode, ColdStart, RequestBytes, ResponseBytes, ErrorMsg, RecordedBy ("console"/"router"), EventType (create/delete/clone/update/invoke).
- Где записывается: handleInvokeFunction, handleInvokeRoute, handleFissionFunctionGateway, handleCreateFunction, handleDeleteFunction, handleUpdateFunctionCode, handleCloneFunction.
8. Stats / Grafana
Файлы: stats/grafana.go, stats/factory.go, stats/noop.go,
stats/provider.go
- GrafanaProvider — для каждого namespace создаёт Grafana Organization (имя = namespace), PostgreSQL datasource (тот же DSN что billing, uid="fission-user-pg"), hardcoded dashboard (SQL: WHERE namespace='...'), public dashboard link (accessToken).
- Кеш:
sync.RWMutex+ per-namespace кеш orgID и accessToken. Идемпотентно — если Org уже есть, повторно не создаётся. - NoopProvider — если
GRAFANA_INTERNAL_URLне задан. - Провизия: вызывается в
handleAuth(fire-and-forget горутина), и вhandleStatsDashboard.
9. UI
Файлы: ui/embed.go, ui/cron.go
- Embedded SPA —
//go:embed index.html. Раздаётся на/console/. - Cron UI — отдельный handler для
/cron/(Cron метрики, статика). - CORS:
Access-Control-Allow-Origin: *(все операции защищены токеном, не cookie). - Security headers: nosniff, X-Frame-Options DENY, CSP (default-src 'self'), Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy (нет камере/микрофону).
10. Мониторинг
/health,/console/health—ok\n/console/api/ns/status— статус namespace (существует, кол-во функций)/console/api/ns/debug— дебаг-инфо (NS, user, labels)/console/api/stats/dashboard-url— возвращает Grafana public dashboard URL
Эволюция storagesvc (история git)
- До 19 мая 2026: S3 (изначальная архитектура)
- 19 мая:
3eb4f56 feat: PV storage backend— перешли на PVC (временное решение) - 20 мая:
7539680 chore: switch storagesvc to S3 backend (ngcloud msk-1)— вернулись на S3. PV-конфиг сохранён вhelm/fission-values-pv.yamlдля быстрого отката. - Текущее состояние (21 мая): S3, бакет
sless-functions, endpointhttps://s3.msk-1.ngcloud.ru. Веткаfeat/s3-replicated.
Чего нет
- Нет аутентификации через cookie — только токен в заголовке.
- Нет rate limiting — только ResourceQuota на уровне K8s.
- Нет WebSocket — только REST.
- Нет аудит-лога — billing таблица не immutable, можно переписать.
- Нет swagger/OpenAPI — документация только в doc/.
- Нет gRPC — только HTTP/JSON.
- Нет метрик Prometheus — только billing в PostgreSQL.
- Нет CI/CD — тесты запускаются вручную.