Files
fission-console/doc/structure/analysis-2026-05-21.md
T

15 KiB
Raw Blame History

Анализ репозитория 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
  • 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 с __invoke dispatcher)
    • 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 TimeTrigger CRD.
  • 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

  • Интерфейс StoreRecordInvocation(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/healthok\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, endpoint https://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 — тесты запускаются вручную.