# fission-console v0.6.9 — Отчёт о тестировании **Дата:** 2026-04-19 **Версия:** `naeel/fission-console:v0.6.9` **Ветка:** `feat/namespace-isolation` --- ## Что реализовано Managed serverless functions service поверх Fission + Kubernetes. Go-бэкенд (консоль), REST API, k8s dynamic client. Пользователи изолированы по namespace (`fission-{SHA256(sub)[:16]}`). **Поддерживаемые среды выполнения:** - Node.js 22 (ESM, `module.exports` / `export default` / анонимная функция) - Python 3.11 (Flask, `def main():` без аргументов) **Auth:** JWT или TEST_MODE (`X-Test-Sub: user@domain` → SHA256 → namespace) --- ## Баги исправлены в этой сессии (v0.6.8 → v0.6.9) | # | Баг | Симптом | Фикс | |---|-----|---------|------| | 1 | Route collision | Два разных пользователя создавали функцию с одинаковым маршрутом | Префикс последних 12 символов namespace: `/{ns[-12:]}/{fn-name}` | | 2 | INVOKE несуществующей → 200 | Вызов несуществующей функции возвращал HTTP 200 без ошибки | GET из k8s перед invoke, `IsNotFound` → 404 | | 3 | DELETE несуществующей → 200 | Удаление несуществующей функции возвращало `deleted:true` | Инвертирован `if err == nil` → `if err != nil`, 404 | | 4 | Orphan package при невалидном TTL | `parseTTL` вызывался после создания пакета — при ошибке оставался мусорный package в k8s | `parseTTL` перемещён **до** создания k8s-ресурсов | --- ## Результаты тестирования ``` PASS=41 FAIL=0 ``` **Покрытие (16 блоков):** | Блок | Что проверяется | |------|----------------| | A | Регресс v0.6.8: INVOKE/DELETE несуществующей → 404, route isolation | | B | Валидация входных данных → 400 (missing name/code/language, пробелы) | | C | Дублирующее создание → конфликт | | D | UPDATE кода + повторный invoke возвращает новый код | | E | GET функции (200/404) | | F | LIST изоляция — пользователь видит только свои функции | | G | Namespace изоляция DELETE — нельзя удалить чужую функцию | | H | Python `def main():` (без аргументов) → invoke 200 | | I | Node.js: `module.exports`, `export.handler`, `export default` | | J | TTL: expires_at создаётся / не создаётся / невалидный → 400 | | K | `ctx.request.url` доступен внутри функции | | L | Stress: 10 параллельных invoke → 10/10 OK | | M | LIST пустого namespace → `[]` | | N | DELETE полная цепочка: Function + Package + HTTPTrigger удаляются | | O | Запрос без auth-заголовка → 401 | | P | POST /auth: без токена → 400, невалидный токен → 401 | --- ## Архитектурные решения — вопросы для ревью ### 1. Namespace on-demand Namespace создаётся при первом обращении пользователя. Нет отдельного registration flow. **Вопрос:** правильно ли это? Нет ли рисков при параллельном первом запросе от одного пользователя (race condition на создание namespace)? ### 2. Сборка функций Код пользователя пакуется в zip в памяти и передаётся в Fission Package как `literal` (base64). При обновлении создаётся новый Package, старый удаляется. **Вопрос:** нет версионирования. Стоит ли хранить историю версий? ### 3. Reaper Горутина раз в 30 секунд проверяет `expires-at` аннотацию и удаляет протухшие функции вместе с Package и HTTPTrigger. Работает без персистентного стейта — при рестарте пода начинает заново со следующего цикла. **Вопрос:** надёжно ли это? Что если под упал в момент удаления — останется ли мусор? ### 4. Python env и сигнатура main() Официальный `ghcr.io/fission/python-env` вызывает `main(*args)` через Flask. Реально аргументы не передаются (только через route params). При `def main():` работает. При `def main(ctx):` — `TypeError: main() missing 1 required positional argument`. **Вопрос:** патчить env или задокументировать ограничение? ### 5. Валидация имени функции Имя с пробелами возвращает 502 (k8s отклоняет по RFC 1123), а не 400 (явная валидация в API). **Вопрос:** стоит ли добавить regex-валидацию имени на уровне API до обращения в k8s? --- ## Что ещё не сделано - [ ] Версионирование функций - [ ] Явная валидация имени функции (RFC 1123) в API - [ ] Метрики / биллинг - [ ] Документация API (OpenAPI spec) - [ ] Merge в master