Files
Naeel b86ff3a62e feat(service): timeout_sec без дефолта; 0=нет таймаута; operator v0.1.48
- api/v1alpha1/service_types.go: убрать +kubebuilder:default=30
- invoke.go: TimeoutSec=0 → &http.Client{} (без таймаута)
- services.go: валидация timeout_sec < 0 || > 900 → HTTP 400
- service_resource.go: TF schema Optional (без Computed); 0 → Int64Null()
- deployments/k8s/operator.yaml: v0.1.47 → v0.1.48
- doc/: progress.md + api/design.md (модель Service) + decisions/log.md
- examples/POSTGRES/: bug_hunter.sh, chaos_marathon.sh, chaos_marathon.tf
2026-03-21 16:58:43 +03:00

215 lines
7.5 KiB
Markdown
Raw Permalink 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.
# API Design
Последнее обновление: 2026-03-21
## Базовый URL
```
http://<operator-host>:9090/v1
```
Локально: `http://localhost:9090/v1`
В кластере: `http://sless-operator.sless.svc.cluster.local:9090/v1`
Публично: `https://sless.kube5s.ru/v1/...` (через Ingress)
**Реализованные эндпоинты:**
```
GET /v1/namespaces/{ns}/functions
POST /v1/namespaces/{ns}/functions
GET /v1/namespaces/{ns}/functions/{name}
PUT /v1/namespaces/{ns}/functions/{name}
DELETE /v1/namespaces/{ns}/functions/{name}
POST /v1/namespaces/{ns}/functions/{name}/upload
GET /v1/namespaces/{ns}/functions/{name}/source ← файлы кода из S3 tar.gz (JSON)
GET /v1/namespaces/{ns}/functions/{name}/invocations
GET /v1/namespaces/{ns}/services
POST /v1/namespaces/{ns}/services
GET /v1/namespaces/{ns}/services/{name}
PUT /v1/namespaces/{ns}/services/{name}
DELETE /v1/namespaces/{ns}/services/{name}
GET /v1/namespaces/{ns}/services/{name}/source ← файлы кода из S3 tar.gz (JSON)
GET /v1/namespaces/{ns}/triggers
POST /v1/namespaces/{ns}/triggers
GET /v1/namespaces/{ns}/triggers/{name}
PATCH /v1/namespaces/{ns}/triggers/{name} ← {"enabled": bool}
DELETE /v1/namespaces/{ns}/triggers/{name}
POST /v1/namespaces/{ns}/jobs
GET /v1/namespaces/{ns}/jobs/{name}
DELETE /v1/namespaces/{ns}/jobs/{name}
```
**Вызов функций (публичный, без auth):**
```
POST https://sless.kube5s.ru/fn/{namespace}/{service-name} ← прокси к Deployment
```
**Глобальный сервис funcs (не оператор):**
```
GET https://sless.kube5s.ru/funcs/<namespace> ← plain text (курл) / HTML (браузер)
GET https://sless.kube5s.ru/funcs?token=<jwt> ← редирект по namespace
GET https://sless.kube5s.ru/funcs/<namespace>/source/<fn> ← прокси к GET /source
PATCH https://sless.kube5s.ru/funcs/<namespace>/triggers/<name> ← прокси к PATCH /triggers
GET https://sless.kube5s.ru/health ← liveness probe
```
## Аутентификация
```
Authorization: Bearer <cloud-token>
```
Токен — JWT от `auth-api`. Middleware в операторе:
1. Извлекает `sub` из payload (без проверки подписи — доверяет Ingress)
2. Вычисляет namespace: `SHA256(sub)[:8]` hex → `sless-{16 hex символов}`
3. Проверяет что запрошенный `{namespace}` совпадает с вычисленным
## Ресурсы
### Functions
| Метод | Путь | Описание |
|-------|------|----------|
| GET | /functions | Список функций |
| POST | /functions | Создать функцию |
| GET | /functions/{id} | Получить функцию |
| PUT | /functions/{id} | Обновить функцию |
| DELETE | /functions/{id} | Удалить функцию |
### Versions (код функции)
| Метод | Путь | Описание |
|-------|------|----------|
| GET | /functions/{id}/versions | Список версий |
| POST | /functions/{id}/versions | Загрузить новый код (multipart zip) |
| GET | /functions/{id}/versions/{ver} | Получить версию |
| POST | /functions/{id}/versions/{ver}/activate | Активировать версию |
### Triggers
| Метод | Путь | Описание |
|-------|------|----------|
| GET | /functions/{id}/triggers | Список триггеров |
| POST | /functions/{id}/triggers | Создать триггер (HTTP/Cron) |
| DELETE | /functions/{id}/triggers/{tid} | Удалить триггер |
### Invocations (вызов и логи)
| Метод | Путь | Описание |
|-------|------|----------|
| POST | /functions/{id}/invoke | Синхронный вызов |
| GET | /functions/{id}/invocations | История вызовов |
| GET | /functions/{id}/invocations/{iid} | Детали вызова + логи |
## Upload endpoint
```
POST /v1/namespaces/{namespace}/functions/{name}/upload
Content-Type: multipart/form-data
Authorization: Bearer <token>
field: code = <zip-file>
```
Процесс:
1. Принимает zip (max 32MB)
2. Распаковывает zip
3. Генерирует `Dockerfile` (`FROM naeel/sless-runtime-{runtime}:latest\nCOPY . /app/function/`)
4. Перепаковывает в `tar.gz` (kaniko требует tar format)
5. Загружает в S3: `contexts/{ns}/{name}/{timestamp}.tar.gz`
6. Обновляет `fn.Spec.S3Key` → контроллер видит изменение и запускает kaniko Job
Ответ `200 OK`:
```json
{"message": "build queued", "phase": "Pending", "s3_key": "contexts/..."}
```
## Поддерживаемые runtime (v1)
- `python3.11` — реализован и протестирован
- `nodejs20` — реализован и протестирован
- `go1.23` — реализован
## Модель Function
```json
{
"id": "fn-uuid",
"name": "my-function",
"description": "...",
"runtime": "python3.11",
"entrypoint": "handler.handle",
"memory_mb": 128,
"timeout_sec": 30,
"env_vars": {"KEY": "value"},
"active_version": "1",
"status": "active",
"created_at": "...",
"updated_at": "..."
}
```
## Модель Trigger
```json
{
"id": "tr-uuid",
"type": "http",
"url": "https://sless.api.ngcloud.ru/invoke/fn-uuid",
"created_at": "..."
}
```
```json
{
"id": "tr-uuid",
"type": "cron",
"schedule": "0 * * * *",
"created_at": "..."
}
```
## Модель Service (sless_service — always-on Deployment)
`sless_service` — долгоживущая функция. Деплоится как Kubernetes Deployment + Service + Ingress.
Вызывается через `POST /fn/{namespace}/{name}` без авторизации (прокси напрямую к поду).
```json
{
"name": "pg-info",
"display_name": "PostgreSQL Info",
"description": "Возвращает список таблиц",
"runtime": "python3.11",
"entrypoint": "handler.handle",
"memory_mb": 128,
"timeout_sec": null,
"env_vars": {"DB_HOST": "..."},
"status": "Ready",
"image_ref": "pearlharbor.../naeel/pg-info:abc123",
"invoke_url": "https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae/pg-info"
}
```
### Поле `timeout_sec`
| Значение | Поведение |
|----------|-----------|
| `null` / не задано | Без ограничений — функция выполняется любое время |
| `1900` | Таймаут в секундах (+ 5s grace на стороне прокси) |
| `< 0` или `> 900` | HTTP 400 Bad Request |
> **Примечание:** Поле опциональное (Optional в Terraform). Не указывать = без лимита.
> В Terraform state значение `null` означает "лимит не задан" (не путать с `0`).
> В Kubernetes CRD `TimeoutSec: 0` → лимита нет (поле omitempty).
### Invoke прокси (как работает timeout_sec)
Оператор принимает `POST /fn/{ns}/{name}`, находит Service CRD, проксирует запрос
к `http://{name}.sless-fn-{ns}.svc.cluster.local:8080`.
Если `TimeoutSec > 0` — создаёт `http.Client{Timeout: TimeoutSec*s + 5s}`.
Если `TimeoutSec == 0``http.Client{}` (Go: Timeout=0 → отсутствие дедлайна).