Files
fission-src/doc/api-guide.md
T
“Naeel” b2efefd75a doc: add multi-tenant Fission Console API guide
Полное руководство по REST API мультитенантного Fission Console.
Описывает все эндпоинты: создание namespace (tenant), деплой функций,
управление environment, триггеры, пакеты.
Актуально для нашего форка с мультитенантностью.
2026-05-15 06:59:14 +04:00

645 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fission Console API — Руководство пользователя
> Версия: актуальна для модернизированного Fission с мультитенантностью (ngcloud).
---
## Базовый URL
```
https://fission.kube5s.ru/console/api
```
---
## Аутентификация
### Где взять токен
Сервер поддерживает два типа токенов — определяет автоматически по форме:
| Форма токена | Тип | Описание |
|---|---|---|
| JWT (три части через `.`) | **Production** | JWT из личного кабинета NUBES (Профиль → Токены). Валидируется через Deck API облака |
| Любая строка ≥ 6 символов | **Demo** | Любой произвольный логин — без внешней проверки. Удобно для разработки и тестирования |
| Строка < 6 символов | — | 401 |
**Production (NUBES):** JWT-токен берётся в личном кабинете NUBES → Профиль → Токены.
**Demo:** любая строка ≥ 6 символов — например `myuser@example.com` или `dev-user-1`.
### Передача токена
Два способа — оба равнозначны:
```http
X-Auth-Token: <токен>
```
```http
Authorization: Bearer <токен>
```
### POST /auth
Проверка токена и получение информации о своём namespace.
```bash
curl -X POST https://fission.kube5s.ru/console/api/auth \
-H "Content-Type: application/json" \
-d '{"token": "myuser@example.com", "env": "test"}'
```
**Параметры:**
| Поле | Описание |
|---|---|
| `token` | Токен (JWT или demo-строка) |
| `env` | Стенд: `prod`, `dev`, `test` (только для JWT; по умолчанию `test`) |
**Ответ 200:**
```json
{
"ok": true,
"env": "test",
"namespace": "fission-a3f9c1b2d4e6f8a1",
"email": "user@example.com"
}
```
**Ошибки:**
| Код | Причина |
|-----|---------|
| 400 | Тело не JSON или `token` пустой |
| 401 | Токен < 6 символов или JWT не прошёл валидацию в Deck API |
| 405 | GET вместо POST |
> **Namespace детерминирован**: `fission-` + hex(SHA256(sub)[:8]) — одинаковый токен → всегда один namespace.
> Namespace и RBAC создаются автоматически при первом обращении.
---
## Мультитенантность ★ КЛЮЧЕВОЕ ОТЛИЧИЕ
- Каждый пользователь работает в **изолированном K8s namespace**: `fission-<hash(token)>`
- Все операции (создание, список, вызов, удаление) **автоматически ограничены своим namespace**
- Указать namespace вручную **невозможно**
- Функции другого пользователя **не видны и не доступны** — любая операция над чужим объектом возвращает **404** (не 403, чтобы не раскрывать факт существования)
- **Routes изолированы**: функции разных пользователей с одинаковым именем получают разные HTTP-маршруты
### Квоты (применяются автоматически, значения по умолчанию)
| Ресурс | Лимит |
|--------|-------|
| Функции (`count/functions.fission.io`) | 20 |
| Пакеты (`count/packages.fission.io`) | 40 |
| HTTP Triggers (`count/httptriggers.fission.io`) | 20 |
| Pods | 30 |
| CPU requests (суммарно) | 1 |
| CPU limits (суммарно) | 12 |
| RAM requests (суммарно) | 2 Gi |
| RAM limits (суммарно) | 6 Gi |
> Значения настраиваются env vars (`QUOTA_REQ_CPU`, `QUOTA_PODS`, и т.д.) без пересборки.
---
## Функции
### POST /functions — Создать функцию из кода (JSON)
```bash
curl -X POST https://fission.kube5s.ru/console/api/functions \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{
"name": "my-fn",
"language": "nodejs",
"code": "module.exports = async function(ctx) { return { status: 200, body: \"hello\" }; }"
}'
```
**Параметры запроса:**
| Поле | Тип | Обязательно | Описание |
|------|-----|:-----------:|---------|
| `name` | string | ✓ | Имя функции (см. правила ниже) |
| `language` | string | ✓ | Среда выполнения: `nodejs`, `python`, `go`, `php`, `ruby` |
| `code` | string | ✓ | Исходный код (строка). Максимум 1 MB |
| `entrypoint` | string | — | Точка входа (по умолчанию — зависит от языка) |
| `route` | string | — | HTTP-маршрут (по умолчанию `/<ns-suffix>/<name>`) |
| `methods` | []string | — | HTTP-методы (по умолчанию `["GET"]`) |
| `timeout` | int64 | — | Таймаут функции в секундах |
| `ttl` | string | — | Время жизни функции: `15m`, `1h`, `2d` и т.д. ★ |
**Правила именования (`name`):**
- Только строчные буквы, цифры, дефис
- Не начинается и не заканчивается дефисом
- Максимум **57 символов**
**Ответ 201:**
```json
{
"name": "my-fn",
"package": "my-fn-pkg",
"httptrigger": "my-fn-route",
"route": "/a3f9c1b2d4e6/my-fn",
"expires_at": null
}
```
> `expires_at` — время удаления функции (RFC3339), `null` если TTL не задан.
**Ошибки:**
| Код | Причина |
|-----|---------|
| 400 | Нет `name`/`language`/`code`, невалидное имя, неизвестный язык, код > 1 MB, невалидный TTL |
| 409 | Функция с таким именем уже существует у этого пользователя |
---
### POST /functions — Создать функцию из zip-архива (multipart)
Альтернативный способ: передать архив напрямую при создании функции.
```bash
curl -X POST https://fission.kube5s.ru/console/api/functions \
-H "X-Auth-Token: user@domain.com" \
-F "name=my-fn" \
-F "language=python" \
-F "entrypoint=main.handler" \
-F "archive=@my-function.zip"
```
**Параметры формы (multipart/form-data):**
| Поле | Тип | Обязательно | Описание |
|------|-----|:-----------:|---------|
| `name` | string | ✓ | Имя функции |
| `language` | string | ✓* | Язык (`python`, `nodejs`, `go`, `php`, `ruby`) — или `environment` |
| `environment` | string | ✓* | Явное имя environment (вместо `language`) |
| `archive` | file | ✓ | zip-архив с кодом. Максимум 100 KB |
| `entrypoint` | string | — | Точка входа |
| `route` | string | — | HTTP-маршрут |
| `methods` | string | — | HTTP-методы через запятую (`GET,POST`) |
| `timeout` | string | — | Таймаут в секундах |
| `ttl` | string | — | Время жизни: `15m`, `1h`, `2d` и т.д. |
> Архив должен быть валидным zip (magic bytes `PK`). Максимальный суммарный распакованный размер — 100 KB (защита от zip bomb).
**Ответ 201:**
```json
{
"name": "my-fn",
"namespace": "fission-a3f9c1b2d4e6f8a1",
"environment": "python-env",
"route": "/a3f9c1b2d4e6/my-fn",
"source_type": "archive"
}
```
---
### GET /functions — Список функций
```bash
curl https://fission.kube5s.ru/console/api/functions \
-H "X-Auth-Token: user@domain.com"
```
**Ответ 200** — массив сырых K8s объектов типа `Function`. Новый пользователь → `[]`.
Возвращает **только функции текущего пользователя**.
---
### GET /functions/{name} — Описание функции
```bash
curl https://fission.kube5s.ru/console/api/functions/my-fn \
-H "X-Auth-Token: user@domain.com"
```
**Ответ 200:**
```json
{
"name": "my-fn",
"namespace": "fission-a3f9c1b2d4e6f8a1",
"environment": "nodejs-env",
"package": "my-fn-pkg",
"entrypoint": "main",
"timeout": 60,
"created_at": "2026-05-01T10:00:00Z",
"updated_at": "2026-05-01T12:00:00Z",
"code": "module.exports = async function(ctx) { ... }",
"source_type": "code",
"archive_filename": "",
"route": "/a3f9c1b2d4e6/my-fn",
"methods": ["GET", "POST"],
"raw": {}
}
```
> `code` — исходный код (если хранится как literal). Для функций из архива может быть пустым.
> `source_type` — `"code"` или `"archive"`.
> `raw` — полный K8s объект Function.
**Ошибки:**
| Код | Причина |
|-----|---------|
| 404 | Функция не существует или принадлежит другому пользователю |
---
### POST /functions/{name}/invoke — Вызов функции
```bash
curl -X POST https://fission.kube5s.ru/console/api/functions/my-fn/invoke \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{}'
```
**Ответ 200:**
```json
{
"status": 200,
"latency_ms": 42,
"response_raw": "hello"
}
```
> `response_raw` — тело ответа функции как строка.
> `status` — HTTP-статус ответа функции.
> `latency_ms` — время выполнения в миллисекундах.
> Cold start (первый вызов после создания) может занять **10-60 секунд** — Pod создаётся и прогревается. Последующие вызовы быстрые.
**Ошибки:**
| Код | Причина |
|-----|---------|
| 404 | Функция не существует или принадлежит другому пользователю |
| 502 | Fission router недоступен или функция завершилась с timeout |
---
### PUT /functions/{name}/code — Обновить код функции ★
Обновляет код существующей функции. Создаётся новый Package, executor подхватывает его при следующем вызове.
```bash
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/code \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{
"code": "module.exports = async function(ctx) { return { status: 200, body: \"v2\" }; }"
}'
```
**Параметры:**
| Поле | Тип | Обязательно | Описание |
|------|-----|:-----------:|---------|
| `code` | string | ✓ | Новый исходный код |
| `timeout` | int64 | — | Новый таймаут в секундах |
**Ответ 200:**
```json
{
"updated": true,
"package": "my-fn-pkg-xxxxxx"
}
```
**Ошибки:**
| Код | Причина |
|-----|---------|
| 400 | `code` пустой или только пробелы |
| 404 | Функция не существует или принадлежит другому пользователю |
---
### PUT /functions/{name}/archive — Обновить архив функции ★
Обновляет функцию новым zip-архивом (multipart/form-data, поле `archive`).
```bash
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/archive \
-H "X-Auth-Token: user@domain.com" \
-F "archive=@my-function-v2.zip"
```
**Ответ 200:**
```json
{
"updated": true,
"package": "my-fn-pkg-xxxxxx"
}
```
---
### PUT /functions/{name}/timeout — Обновить таймаут функции ★
Обновляет только таймаут (и опционально entrypoint) без замены кода или архива.
```bash
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/timeout \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{"timeout": 120}'
```
**Параметры:**
| Поле | Тип | Описание |
|------|-----|---------|
| `timeout` | int64 | Новый таймаут в секундах |
| `entrypoint` | string | Новая точка входа (опционально) |
**Ответ 200:**
```json
{
"updated": true,
"timeout": 120
}
```
---
### DELETE /functions/{name} — Удалить функцию
```bash
curl -X DELETE https://fission.kube5s.ru/console/api/functions/my-fn \
-H "X-Auth-Token: user@domain.com"
```
**Ответ 200:**
```json
{
"deleted": true,
"name": "my-fn",
"package": "my-fn-pkg"
}
```
> Удаляются также связанные HTTPTrigger, TimeTrigger и Package.
> Архив в S3 удаляется асинхронно.
> Если язык больше не используется ни одной функцией — environment Pod'ы убираются автоматически.
**Ошибки:**
| Код | Причина |
|-----|---------|
| 404 | Функция не существует или принадлежит другому пользователю |
> Повторное удаление той же функции → **404**.
---
## Прямой вызов по route — GET|POST /fn/{route}
Вызов функции напрямую по HTTP-маршруту без обёртки invoke. Ответ проксируется как есть — без JSON-обёртки.
```bash
curl https://fission.kube5s.ru/fn/a3f9c1b2d4e6/my-fn \
-H "X-Auth-Token: user@domain.com"
```
> Используйте этот endpoint когда нужно получить чистый HTTP-ответ функции, а не JSON-обёртку с `response_raw`.
> Метод запроса (GET/POST/…) проксируется без изменений.
---
## Time Triggers (расписание)
### GET /timetriggers — Список
```bash
curl https://fission.kube5s.ru/console/api/timetriggers \
-H "X-Auth-Token: user@domain.com"
```
Возвращает массив сырых K8s объектов TimeTrigger.
### POST /timetriggers — Создать
```bash
curl -X POST https://fission.kube5s.ru/console/api/timetriggers \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{
"name": "my-cron",
"functionName": "my-fn",
"cron": "*/5 * * * *"
}'
```
**Параметры:**
| Поле | Тип | Обязательно | Описание |
|------|-----|:-----------:|---------|
| `name` | string | ✓ | Имя trigger'а |
| `functionName` | string | ✓ | Имя функции |
| `cron` | string | ✓ | Cron-выражение (стандартный формат) |
| `method` | string | — | HTTP-метод для вызова (по умолчанию `POST`) |
| `subpath` | string | — | Дополнительный путь |
**Ответ 201:**
```json
{
"name": "my-cron",
"namespace": "fission-a3f9c1b2d4e6f8a1",
"cron": "*/5 * * * *",
"method": "POST",
"subpath": "",
"function": "my-fn",
"raw": {}
}
```
### GET /timetriggers/{name} — Описание
**Ответ 200** — та же структура что и при создании.
### PUT /timetriggers/{name} — Обновить
**Ответ 200:**
```json
{
"updated": true,
"trigger": { ...та же структура... }
}
```
### DELETE /timetriggers/{name} — Удалить
**Ответ 200:**
```json
{
"deleted": true,
"name": "my-cron"
}
```
---
## AI-линтер архивов ★
Проверяет zip-архив на синтаксические ошибки до деплоя. Не создаёт функцию.
Поддерживаемые файлы: `.py`, `.js`, `.rb`, `.php`.
### POST /ai/lint-archive
```bash
curl -X POST https://fission.kube5s.ru/console/api/ai/lint-archive \
-H "X-Auth-Token: user@domain.com" \
-F "archive=@my-function.zip" \
-F "entrypoint=main.handler" \
-F "language=python"
```
**Параметры формы:**
| Поле | Описание |
|------|---------|
| `archive` | zip-архив (обязательно) |
| `entrypoint` | Точка входа `module.function` — проверяется что файл и функция существуют в архиве |
| `language` | Язык — проверяется что архив содержит файлы нужного расширения |
**Ответ 200:**
```json
{
"ok": true,
"results": [
{"file": "main.py", "ok": true},
{"file": "helper.py", "ok": false, "output": "SyntaxError: invalid syntax (helper.py, line 5)"},
{"file": "main.handler", "ok": true, "output": "entrypoint 'main.handler' найден"}
]
}
```
> `ok: false` в корне объекта означает что хотя бы один файл не прошёл проверку.
> `output` содержит вывод линтера — присутствует только при ошибке (для entrypoint — всегда).
**Ошибки:**
| Код | Причина |
|-----|---------|
| 400 | Нет поля `archive`, нет поддерживаемых файлов (.py/.js/.rb/.php) в архиве |
| 413 | Архив > 100 KB или суммарный распакованный размер > 100 KB (zip bomb protection) |
---
## TTL — Время жизни функции ★
Функция может быть создана с ограниченным временем жизни. После истечения TTL функция удаляется автоматически.
**Формат:** число + суффикс: `m` (минуты), `h` (часы), `d` (дни).
**Примеры:** `15m`, `1h`, `2d`, `12h`
```bash
curl -X POST .../functions \
-H "X-Auth-Token: user@domain.com" \
-H "Content-Type: application/json" \
-d '{
"name": "temp-fn",
"language": "python",
"code": "def main(event, context): return \"hi\"",
"ttl": "1h"
}'
```
В ответе будет поле `expires_at` (формат RFC3339):
```json
{
"name": "temp-fn",
"package": "temp-fn-pkg",
"httptrigger": "temp-fn-route",
"route": "/...",
"expires_at": "2026-05-06T14:00:00Z"
}
```
Невалидные значения TTL (`0d`, `-1h`, `99z`, `abc`) → **400**.
---
## HTTP-коды — сводная таблица
| Код | Значение |
|-----|---------|
| 200 | Успех (GET, DELETE, PUT) |
| 201 | Объект создан (POST /functions, POST /timetriggers) |
| 400 | Ошибка валидации параметров |
| 401 | Не авторизован (нет токена или < 6 символов) |
| 404 | Объект не найден (или чужой) |
| 405 | Неверный HTTP-метод |
| 409 | Конфликт (дубликат имени) |
| 413 | Тело слишком большое (код > 1 MB, архив > 100 KB) |
| 502 | Ошибка взаимодействия с Fission (router/executor недоступен) |
**Формат ошибки:**
```json
{ "error": "описание ошибки" }
```
---
## Поддерживаемые языки
| `language` | Расширение файла | Entrypoint по умолчанию |
|------------|-----------------|------------------------|
| `python` | `.py` | `main.main` |
| `nodejs` | `.js` | зависит от runtime |
| `go` | `.go` | зависит от runtime |
| `php` | `.php` | зависит от runtime |
| `ruby` | `.rb` | зависит от runtime |
---
## Примеры сценариев
### Быстрый старт (inline-код)
```bash
BASE="https://fission.kube5s.ru/console/api"
TOKEN="myuser@example.com"
# Создать функцию
curl -X POST "$BASE/functions" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"hello","language":"python","code":"def main(event, context): return \"hello world\""}'
# Вызвать
curl -X POST "$BASE/functions/hello/invoke" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" -d '{}'
# Обновить код
curl -X PUT "$BASE/functions/hello/code" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"code":"def main(event, context): return \"v2\""}'
# Удалить
curl -X DELETE "$BASE/functions/hello" -H "X-Auth-Token: $TOKEN"
```
### Создать из архива напрямую
```bash
curl -X POST "$BASE/functions" \
-H "X-Auth-Token: $TOKEN" \
-F "name=my-fn" \
-F "language=python" \
-F "entrypoint=main.handler" \
-F "archive=@my-fn.zip"
```
### Проверить архив перед деплоем
```bash
curl -X POST "$BASE/ai/lint-archive" \
-H "X-Auth-Token: $TOKEN" \
-F "archive=@my-fn.zip" \
-F "entrypoint=main.handler" \
-F "language=python"
```
### Создать временную функцию (исчезнет через 30 минут)
```bash
curl -X POST "$BASE/functions" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"temp-fn","language":"nodejs","code":"module.exports = async () => ({status:200,body:\"tmp\"})","ttl":"30m"}'
```
### Настроить расписание
```bash
# Вызывать my-fn каждые 5 минут
curl -X POST "$BASE/timetriggers" \
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"my-cron","functionName":"my-fn","cron":"*/5 * * * *"}'
```