diff --git a/doc/api/design.md b/doc/api/design.md index a6b8e2b..087139c 100644 --- a/doc/api/design.md +++ b/doc/api/design.md @@ -18,28 +18,29 @@ http://:9090/v1 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}/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} -GET /v1/namespaces/{ns}/functions/{name}/invocations -``` - -**Запланированные эндпоинты (ветка feat/web-console):** - -``` -GET /v1/namespaces/{ns}/functions/{name}/source ← НОВЫЙ: код из S3 zip -PATCH /v1/namespaces/{ns}/triggers/{name} ← НОВЫЙ: enable/disable триггера +POST /v1/namespaces/{ns}/jobs +GET /v1/namespaces/{ns}/jobs/{name} +DELETE /v1/namespaces/{ns}/jobs/{name} ``` **Глобальный сервис funcs (не оператор):** ``` -GET https://sless.kube5s.ru/funcs/ ← без токена, plain text / HTML -GET https://sless.kube5s.ru/funcs?token= ← с токеном -GET https://sless.kube5s.ru/health ← liveness probe +GET https://sless.kube5s.ru/funcs/ ← plain text (курл) / HTML (браузер) +GET https://sless.kube5s.ru/funcs?token= ← редирект по namespace +GET https://sless.kube5s.ru/funcs//source/ ← прокси к GET /source +PATCH https://sless.kube5s.ru/funcs//triggers/ ← прокси к PATCH /triggers +GET https://sless.kube5s.ru/health ← liveness probe ``` ## Аутентификация @@ -116,8 +117,8 @@ field: code = ## Поддерживаемые runtime (v1) - `python3.11` — реализован и протестирован -- `go1.21` — планируется -- `nodejs20` — планируется +- `nodejs20` — реализован и протестирован +- `go1.23` — реализован ## Модель Function diff --git a/doc/architecture/agent-handoff-2026-03-18.md b/doc/architecture/agent-handoff-2026-03-18.md new file mode 100644 index 0000000..e87618c --- /dev/null +++ b/doc/architecture/agent-handoff-2026-03-18.md @@ -0,0 +1,368 @@ +# Agent Handoff — 2026-03-18 (финальное состояние сессии) + +Этот файл — полный срез для нового агента: что сделано, как устроено, как работать. + +--- + +## 1. Идентификация проекта + +- **Репозиторий:** `gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless` +- **Локальная копия:** `/home/naeel/remote_dev/sless/` +- **Remote server:** `naeel@5.172.178.213` (workspace: `~/terra/sless/`) +- **SSH ключ:** `/home/naeel/remote_dev/sless/secrets/naeel_vm_id_ed25519` +- **SSH команда:** `ssh -i <ключ> -o StrictHostKeyChecking=no naeel@5.172.178.213` +- **Активная ветка:** `feat/web-console` (последний коммит `a04dfb2`) +- **Git origin:** `https://gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless.git` + +### Важно про git +Git **не работает локально** (зависает при записи объектов из-за NFS-подобного поведения volume). +Все `git add / commit / push` — **только через SSH на remote machine**. +`scp` для копирования файлов → `ssh` для git-операций. + +--- + +## 2. Что такое проект + +Managed Serverless Functions Service для облачного провайдера **nubes.ru**. + +Пользователь пишет `main.tf` с ресурсами `sless_function`, `sless_trigger`, `sless_job`. +Terraform провайдер собирает zip → загружает в оператор → оператор запускает kaniko → Docker образ → Deployment в k8s. + +**Внешнее API оператора:** `https://sless.kube5s.ru` +**Ingress IP:** `185.247.187.147` +**Namespace оператора в k8s:** `sless` + +--- + +## 3. Стек и компоненты + +| Компонент | Технология | Namespace / где | +|-----------|-----------|-----------------| +| Operator (API + Controllers) | Go (controller-runtime) | k8s namespace `sless` | +| funcs-service (web-консоль) | Go (net/http) | k8s namespace `sless` | +| PostgreSQL | PostgreSQL 16 | k8s namespace `sless` | +| S3 | Ceph (облачный) | `s3.msk-1.ngcloud.ru` | +| Container Registry | DockerHub (`naeel/`) | внешний | +| Builder | kaniko Job | namespace пользователя | +| Function (HTTP trigger) | k8s Deployment + Service | namespace пользователя | +| Function (one-shot) | k8s Job | namespace пользователя | +| Function (cron) | k8s CronJob | namespace пользователя | +| Terraform Provider | Go (plugin-framework v6) | localhost/CI | + +--- + +## 4. Текущие версии образов + +| Образ | Версия | Что внутри | +|-------|--------|-----------| +| `naeel/sless-operator` | **v0.1.34** | REST API + k8s controllers; GET /source; proxy-готовый PATCH /triggers | +| `naeel/sless-funcs-service` | **v0.2.0** | HTML web-консоль + plain text (backward compat) | +| `naeel/sless-runtime-python3.11` | **v0.1.3** | str return → text/plain | +| `naeel/sless-runtime-nodejs20` | **v0.1.2** | без изменений | +| `naeel/sless-runtime-go1.23` | **v0.1.0** | без изменений | + +--- + +## 5. Структура директорий (актуальная) + +``` +sless/ +├── main.go # точка входа оператора (controller-runtime + HTTP сервер) +├── Dockerfile # сборка оператора +├── go.mod # module: gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless +├── api/v1alpha1/ # CRD типы: Function, FunctionJob, Trigger +│ ├── function_types.go +│ ├── job_types.go +│ └── trigger_types.go +├── controllers/ # k8s reconcilers +│ ├── function_controller.go # Function CRD → kaniko → Deployment/Service +│ ├── functionjob_controller.go # FunctionJob CRD → k8s Job → собирает stdout/stderr +│ └── trigger_controller.go # Trigger CRD → Deployment scale / CronJob +├── internal/ +│ ├── api/ +│ │ ├── router.go # gorilla/mux: все REST маршруты (актуальный) +│ │ ├── middleware/ # Auth (JWT→namespace), Logging +│ │ └── handler/ +│ │ ├── handler.go # Handler struct (K8s, S3, PG, Log) +│ │ ├── functions.go # CRUD Functions +│ │ ├── triggers.go # CRUD Triggers + UpdateTrigger (PATCH enabled) +│ │ ├── upload.go # POST /upload — zip → tar.gz → S3 → Function CRD patch +│ │ ├── source.go # GET /source — tar.gz из S3 → JSON файлы (НОВЫЙ) +│ │ ├── jobs.go # CRUD FunctionJobs +│ │ ├── invocations.go # logs из Postgres +│ │ ├── invoke.go # прокси вызова HTTP функций +│ │ └── namespace.go # EnsureNamespace +│ ├── builder/ +│ │ └── context.go # zip + runtime → tar.gz + Dockerfile для kaniko +│ ├── storage/ +│ │ ├── s3/client.go # minio-go: Upload, UploadContext, Download, Delete +│ │ └── postgres/ # хранение invocation logs +│ └── config/ # env vars конфиг +├── services/ +│ └── funcs/ +│ ├── main.go # web-консоль сервис (v0.2.0) +│ ├── index.html # HTML шаблон (embed) +│ ├── Dockerfile # multi-stage Go → alpine +│ └── funcs-service.yaml # (дубль, не деплоится отсюда) +├── deployments/k8s/ +│ ├── operator.yaml # ConfigMap + Secret + Deployment + Service + Ingress оператора +│ ├── funcs-service.yaml # Deployment + Service + Ingress funcs-service +│ ├── postgres.yaml # PostgreSQL +│ └── rbac.yaml # ClusterRole для оператора +├── terraform/provider/ # terraform-provider-sless +│ ├── main.go +│ └── internal/ +│ ├── client/client.go # HTTP клиент к оператору +│ └── resources/ +│ ├── function_resource.go # sless_function: source_dir→zip, code_hash, ModifyPlan +│ ├── trigger_resource.go # sless_trigger +│ └── job_resource.go # sless_job + ErrJobAlreadyExists handling +├── runtimes/ +│ ├── python3.11/server.py # HTTP wrapper (str → text/plain) +│ ├── nodejs20/ # HTTP wrapper +│ └── go1.23/ # multi-stage builder образ +├── examples/ +│ ├── POSTGRES/ # pg функции: create-table, pg-info, pg-table-reader +│ ├── hello-go/ +│ ├── hello-node/ +│ └── ... +├── migrations/001_initial.sql # PostgreSQL схема +└── doc/ # ← ты здесь + ├── architecture/ + │ └── agent-handoff-2026-03-18.md ← ЭТОТ ФАЙЛ + ├── api/design.md + ├── decisions/log.md + ├── errors/log.md + └── progress.md +``` + +--- + +## 6. REST API оператора — полный список маршрутов + +Все `/v1/` защищены JWT (middleware.Auth проверяет Bearer токен + namespace). +`/fn/` — публичный прокси для HTTP-триггеров (без auth). + +``` +POST /v1/namespaces/{ns}/ensure # создать namespace (идемпотентно) + +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 # загрузить zip → S3 → kaniko +GET /v1/namespaces/{ns}/functions/{name}/source # НОВЫЙ: файлы кода из S3 (JSON) +GET /v1/namespaces/{ns}/functions/{name}/invocations # логи вызовов + +GET /v1/namespaces/{ns}/triggers # список триггеров +POST /v1/namespaces/{ns}/triggers # создать триггер +GET /v1/namespaces/{ns}/triggers/{name} # получить триггер +PATCH /v1/namespaces/{ns}/triggers/{name} # enable/disable: {"enabled": bool} +DELETE /v1/namespaces/{ns}/triggers/{name} # удалить триггер + +POST /v1/namespaces/{ns}/jobs # создать FunctionJob +GET /v1/namespaces/{ns}/jobs/{name} # получить FunctionJob +DELETE /v1/namespaces/{ns}/jobs/{name} # удалить FunctionJob + +GET/POST/... /fn/{namespace}/{name}[/...] # вызов HTTP функции (без auth) +``` + +--- + +## 7. funcs-service — маршруты + +Сервис `sless-funcs-service`, namespace `sless`, порт 8090. +Ingress: `sless.kube5s.ru/funcs` → `sless-funcs-service:8090`. + +``` +GET /health # liveness/readiness probe (без auth) +GET /funcs # usage hint (нет токена → 401 с подсказкой) +GET /funcs?token= # листинг через JWT +GET /funcs/ # листинг по namespace (браузер→HTML, curl→plain text) +GET /funcs//source/ # прокси → оператор GET /source (serviceToken) +PATCH /funcs//triggers/ # прокси → оператор PATCH /triggers (serviceToken) +``` + +Логика переключения HTML/plain text: `strings.Contains(Accept header, "text/html")`. +Браузер всегда шлёт `text/html` в Accept → HTML консоль. +`curl` без `-H "Accept: text/html"` → plain text (совместимость с v0.1.x). + +**Env vars funcs-service:** +``` +SLESS_OPERATOR_URL = http://sless-operator.sless.svc.cluster.local:9090 +SLESS_EXTERNAL_URL = https://sless.kube5s.ru +SLESS_EXCLUDE = (список функций скрытых из листинга, через запятую) +SLESS_SERVICE_TOKEN = (задаётся через kubectl set env, НЕ в git) +PORT = 8090 +``` + +--- + +## 8. Namespace пользователя + +``` +JWT.sub (UUID) → SHA256(sub)[:8] → hex → "sless-" + 16 hex символов +``` + +Пример: sub `019cc268-6c6a-781e-8613-4bed4ec7cd20` → namespace `sless-ffd1f598c169b0ae` + +Эта логика **одинакова** в трёх местах: +- `internal/api/middleware/auth.go` (оператор) +- `services/funcs/main.go` (funcs-service) +- `terraform/provider/internal/client/client.go` (провайдер) + +--- + +## 9. S3 хранение кода — ключи + +При `POST /upload` создаются **два объекта:** + +``` +functions/{ns}/{name}/{timestamp}.zip ← исходный код (zip от пользователя) +contexts/{ns}/{name}/{timestamp}.tar.gz ← build context для kaniko (zip + Dockerfile) +``` + +`Function.Spec.S3Key` хранит путь к `contexts/...`. +`GET /source` читает `Function.Spec.S3Key`, скачивает tar.gz, извлекает файлы без Dockerfile. + +**Важно:** zip исходника (`functions/...`) отдельно не хранится в CRD. +Код источника берётся из tar.gz контекста — там те же файлы пользователя. + +--- + +## 10. Terraform провайдер — sless_function + +```hcl +resource "sless_function" "my_func" { + name = "my-func" + runtime = "python3.11" # python3.11 | nodejs20 | go1.23 + entrypoint = "handler.handle" + + source_dir = "${path.module}/code/my-func" # директория → провайдер делает zip сам + # ИЛИ: + # code_path = "./handler.zip" # готовый zip + # code_hash = filesha256("./handler.zip") # для детекции изменений + + memory_mb = 128 + timeout_sec = 30 + env_vars = { KEY = "value" } + build_timeout_sec = 300 # ожидание kaniko (дефолт 300 сек) +} +``` + +При изменении файлов в `source_dir`: +- `terraform plan` → пересчитывает `code_hash` (ModifyPlan), показывает diff +- `terraform apply` → загружает новый zip, ждёт сборки → `phase = Ready` +- Новый код виден в браузере сразу после apply + +--- + +## 11. Где что запущено (kubectl) + +```bash +# Проверить поды оператора +kubectl get pods -n sless + +# Проверить версию образа оператора +kubectl get deployment sless-operator -n sless -o jsonpath='{.spec.template.spec.containers[0].image}' + +# Посмотреть логи оператора +kubectl logs -n sless -l app=sless-operator --tail=50 + +# Посмотреть логи funcs-service +kubectl logs -n sless -l app=sless-funcs-service --tail=50 + +# Обновить образ оператора +kubectl set image deployment/sless-operator operator=naeel/sless-operator:vX.X.X -n sless +kubectl rollout status deployment/sless-operator -n sless --timeout=90s + +# Обновить образ funcs-service +kubectl set image deployment/sless-funcs-service funcs=naeel/sless-funcs-service:vX.X.X -n sless + +# Посмотреть функции пользователя +kubectl get functions -n sless-ffd1f598c169b0ae + +# Посмотреть триггеры +kubectl get triggers -n sless-ffd1f598c169b0ae + +# Обновить SLESS_SERVICE_TOKEN (не хранится в git!) +kubectl set env deployment/sless-funcs-service -n sless SLESS_SERVICE_TOKEN= +``` + +--- + +## 12. Тестовые данные + +- **Тестовый токен:** `/home/naeel/remote_dev/sless/secrets/test.token` +- **Namespace тест-пользователя:** `sless-ffd1f598c169b0ae` +- **Рабочие функции:** + - `pg-info` (nodejs20) — читает версию PostgreSQL и счётчик строк + - `pg-table-reader` (python3.11) — читает строки из таблицы + - `pg-create-table-runner` (python3.11) — job: создаёт таблицу +- **Быстрый тест:** + ```bash + # plain text список (curl) + curl https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae + + # HTML консоль (браузер или curl с Accept) + curl -H "Accept: text/html" https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae + + # исходный код функции + curl https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae/source/pg-info + + # вызов функции + curl https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae/pg-info + ``` + +--- + +## 13. Деплой нового образа — стандартный workflow + +```bash +SSH="ssh -i /home/naeel/remote_dev/sless/secrets/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no naeel@5.172.178.213" +SCP="scp -i /home/naeel/remote_dev/sless/secrets/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no" + +# 1. Скопировать изменённые файлы оператора на remote +$SCP /home/naeel/remote_dev/sless/path/to/file.go naeel@5.172.178.213:~/terra/sless/path/to/file.go + +# 2. Собрать образ на remote +$SSH "cd ~/terra/sless && docker build -t naeel/sless-operator:vX.X.X . 2>&1 | tail -5" + +# 3. Запушить +$SSH "docker push naeel/sless-operator:vX.X.X 2>&1 | tail -3" + +# 4. Задеплоить +$SSH "kubectl set image deployment/sless-operator operator=naeel/sless-operator:vX.X.X -n sless && kubectl rollout status deployment/sless-operator -n sless --timeout=90s" + +# 5. Коммит (через SSH! не локально) +$SSH "cd ~/terra/sless && git add <файлы> && git commit -m 'msg' && git push origin feat/web-console" +``` + +Для funcs-service: путь `~/terra/sless/services/funcs/`, контейнер называется `funcs`. + +--- + +## 14. Известные проблемы / особенности + +| Проблема | Решение | +|---------|---------| +| `git` зависает локально | Только через SSH на remote machine | +| `SLESS_SERVICE_TOKEN` не в git | Задан через `kubectl set env`, при пересоздании пода — пропадёт! Нужно переставить вручную | +| `job-name=` label удалён в k8s 1.27+ | Используем свой label `functionjob=` на PodTemplate (исправлено в v0.1.34) | +| S3 endpoint с SSL | `useSSL=false` для внутреннего s3, `useSSL=true` для облачного `s3.msk-1.ngcloud.ru` | +| `python3.11`: str return → text/plain | Начиная с `naeel/sless-runtime-python3.11:v0.1.3` | + +--- + +## 15. Следующие возможные задачи (не начаты) + +| Задача | Сложность | Заметки | +|--------|-----------|---------| +| Слияние `feat/web-console` в `main` (или базовую ветку) | низкая | Ветка стабильная, все тесты проходят | +| Кнопка "Обновить код" в HTML консоли (upload из браузера) | средняя | Drag&drop zip или указать source_dir | +| Обратная синхронизация (скачать код из S3 в source_dir) | средняя | terraform data source или отдельная команда | +| History/versioning (несколько версий кода) | высокая | S3 уже хранит по timestamp — нужен UI | +| SLESS_SERVICE_TOKEN из k8s Secret | низкая | Сейчас задаётся через kubectl set env — надо в YAML (sealed secret) | +| Логи функции в HTML консоли | средняя | GET /invocations уже есть в операторе | +| Публикация terraform провайдера | средняя | Terraform Registry или Gitea Releases | diff --git a/doc/architecture/overview.md b/doc/architecture/overview.md index eea641a..8b76c5e 100644 --- a/doc/architecture/overview.md +++ b/doc/architecture/overview.md @@ -1,6 +1,6 @@ # Архитектура системы -Последнее обновление: 2026-03-18 (v0.1.33 + funcs-service v0.1.3) +Последнее обновление: 2026-03-18 (v0.1.34 + funcs-service v0.2.0) ## Общее описание @@ -62,7 +62,7 @@ Terraform source_dir (локально) └─► контроллер → kaniko Job → Docker image ``` -Для web-консоли: `GET /source` читает `functions/{ns}/{name}/{ts}.zip` напрямую. +Для web-консоли: `GET /source` читает `contexts/{ns}/{name}/{ts}.tar.gz` (из `Function.Spec.S3Key`), распаковывает tar.gz, фильтрует Dockerfile, возвращает пользовательские файлы. ## Изоляция пользователей — Namespace per user diff --git a/doc/architecture/project-structure.md b/doc/architecture/project-structure.md index 70d9291..4c975a8 100644 --- a/doc/architecture/project-structure.md +++ b/doc/architecture/project-structure.md @@ -1,49 +1,106 @@ # Структура проекта +Последнее обновление: 2026-03-18 (v0.1.34) + ## Репозиторий `gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless` -## Директории +## Директории (актуальное состояние) ``` sless/ -├── cmd/ -│ └── api/ -│ └── main.go # точка входа API сервера +├── main.go # точка входа оператора (controller-runtime + HTTP сервер) +├── Dockerfile # multi-stage сборка оператора +├── go.mod # module: gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless +├── Makefile +├── api/ +│ └── v1alpha1/ # CRD типы для controller-gen +│ ├── function_types.go # Function CRD: spec, status +│ ├── job_types.go # FunctionJob CRD +│ ├── trigger_types.go # Trigger CRD +│ └── zz_generated.deepcopy.go # автогенерация DeepCopy +├── controllers/ +│ ├── function_controller.go # Function → kaniko Job → Deployment/Service +│ ├── functionjob_controller.go # FunctionJob → k8s Job → stdout/stderr → status +│ └── trigger_controller.go # Trigger → Deployment scale / CronJob ├── internal/ │ ├── api/ -│ │ ├── handler/ # HTTP хендлеры (functions, versions, triggers) -│ │ ├── middleware/ # auth, logging, rate limit -│ │ └── router.go # регистрация маршрутов -│ ├── model/ # доменные модели: Function, Version, Trigger, Invocation +│ │ ├── router.go # gorilla/mux: регистрация всех маршрутов +│ │ ├── middleware/ +│ │ │ ├── auth.go # JWT Bearer → namespace (SHA256) +│ │ │ └── logging.go # access log +│ │ └── handler/ +│ │ ├── handler.go # Handler struct (K8s, S3, PG, Log) +│ │ ├── functions.go # GET/POST/PUT/DELETE функций +│ │ ├── triggers.go # GET/POST/PATCH/DELETE триггеров +│ │ ├── upload.go # POST /upload: zip → tar.gz → S3 → CRD patch +│ │ ├── source.go # GET /source: S3 tar.gz → JSON файлы (НОВЫЙ v0.1.34) +│ │ ├── jobs.go # GET/POST/DELETE FunctionJob +│ │ ├── invocations.go # GET /invocations: логи из Postgres +│ │ ├── invoke.go # прокси HTTP вызовов функций +│ │ └── namespace.go # POST /ensure: создать k8s namespace +│ ├── builder/ +│ │ └── context.go # zip + runtime → tar.gz + Dockerfile для kaniko │ ├── storage/ -│ │ ├── postgres/ # CRUD функций, версий, логов вызовов -│ │ └── s3/ # загрузка/скачивание zip архивов кода -│ ├── builder/ # сборка Docker образа из кода пользователя -│ ├── runner/ # запуск функций в k8s (Jobs / Deployments) -│ └── config/ # конфиг из env переменных -├── migrations/ # SQL миграции (numbered: 001_, 002_, ...) -├── deployments/ -│ └── k8s/ # манифесты: Deployment, Service, Ingress, RBAC -├── api/ -│ └── openapi.yaml # OpenAPI 3.0 спецификация -├── doc/ # документация проекта (эта папка) -└── docker-compose.yml # локальная разработка: postgres, redis, minio +│ │ ├── s3/client.go # minio-go: Upload, UploadContext, Download, Delete +│ │ └── postgres/ # хранение invocation logs +│ └── config/ # конфиг из env переменных +├── services/ +│ └── funcs/ # отдельный HTTP сервис (web-консоль) +│ ├── main.go # v0.2.0: HTML+plain text+proxy к оператору +│ ├── index.html # dark-themed HTML консоль (go:embed) +│ ├── Dockerfile # multi-stage Go → alpine (копирует main.go + index.html) +│ └── go.mod # отдельный Go модуль +├── migrations/ +│ └── 001_initial.sql # схема PostgreSQL (invocation logs) +├── deployments/k8s/ +│ ├── operator.yaml # ConfigMap + Deployment + Service + Ingress оператора +│ ├── funcs-service.yaml # Deployment + Service + Ingress funcs-service +│ ├── postgres.yaml # PostgreSQL +│ └── rbac.yaml # ClusterRole + ClusterRoleBinding для оператора +├── runtimes/ +│ ├── python3.11/server.py # HTTP wrapper (str → text/plain начиная с v0.1.3) +│ ├── nodejs20/server.js # HTTP wrapper +│ └── go1.23/ # multi-stage builder образ +├── terraform/ +│ └── provider/ # terraform-provider-sless (отдельный Go модуль) +│ ├── main.go +│ └── internal/ +│ ├── client/client.go # REST клиент к оператору +│ ├── resources/ +│ │ ├── function_resource.go # sless_function: source_dir, code_hash, ModifyPlan +│ │ ├── trigger_resource.go # sless_trigger +│ │ └── job_resource.go # sless_job + ErrJobAlreadyExists +│ └── provider/provider.go +├── examples/ +│ ├── POSTGRES/ # pg функции: create-table, pg-info, pg-table-reader +│ ├── hello-go/ +│ ├── hello-node/ +│ └── simple-python/ +├── config/ # kustomize конфиги (controller-gen артефакты) +│ ├── crd/ # CRD манифесты (автогенерация) +│ ├── rbac/ +│ └── samples/ +├── hack/ +│ └── boilerplate.go.txt # шаблон заголовков файлов +└── doc/ # документация проекта + ├── architecture/ + │ ├── agent-handoff-2026-03-18.md # полный контекст для нового агента + │ ├── overview.md # архитектура системы + │ └── project-structure.md # этот файл + ├── api/design.md # дизайн REST API + ├── decisions/log.md # журнал архитектурных решений + ├── errors/log.md # известные ошибки и решения + └── progress.md # трекер задач ``` -## Go module +## Go модули -``` -module gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless -``` +Два независимых Go модуля в одном репозитории: -## Порядок разработки - -1. `internal/config` + `internal/model` — базовые структуры данных -2. `migrations/` + `internal/storage/postgres` — схема БД и CRUD -3. `internal/api` — HTTP хендлеры, роутер, middleware -4. `internal/storage/s3` — загрузка кода функций -5. `internal/builder` — сборка Docker образов -6. `internal/runner` — запуск функций в k8s -7. `deployments/k8s` — манифесты для деплоя +| Модуль | Путь | Назначение | +|--------|------|-----------| +| `gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless` | `/` | Оператор + API | +| `gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/services/funcs` | `services/funcs/` | Web-консоль сервис | +| `terra.k8c.ru/naeel/sless` | `terraform/provider/` | Terraform провайдер | diff --git a/doc/decisions/log.md b/doc/decisions/log.md index bb31c6c..deeb862 100644 --- a/doc/decisions/log.md +++ b/doc/decisions/log.md @@ -37,7 +37,7 @@ Terraform уже работает по схеме: `source_dir` → zip → `POS Обратная синхронизация (кластер → локальная папка): скачать zip из S3 → распаковать в `source_dir`. Никаких структурных изменений не потребует. Реализовывать ПОСЛЕ web-консоли. -### Архитектура web-консоли (план, ветка feat/web-console) +### Архитектура web-консоли (реализовано, ветка feat/web-console, оператор v0.1.34 + funcs-service v0.2.0) **Принцип:** минимум изменений в операторе, максимум логики в `sless-funcs-service`. @@ -45,15 +45,16 @@ Terraform уже работает по схеме: `source_dir` → zip → `POS | Метод | Путь | Что делает | |-------|------|-----------| -| GET | `/v1/namespaces/{ns}/functions/{name}/source` | Читает zip из S3 → JSON `[{name, content}]` | +| GET | `/v1/namespaces/{ns}/functions/{name}/source` | Читает tar.gz из S3 (`Function.Spec.S3Key`) → JSON `[{name, content}]`, без Dockerfile | | PATCH | `/v1/namespaces/{ns}/triggers/{name}` | `{"enabled": bool}` → обновляет Trigger CRD | **`sless-funcs-service` — HTML режим:** - Если запрос из браузера (`Accept: text/html`) → отдаёт HTML страницу -- Список функций — аккордеон; при раскрытии `fetch(/source)` подгружает файлы +- Список функций — аккордеон; при раскрытии `fetch(/funcs/{ns}/source/{fn})` подгружает файлы - Подсветка синтаксиса: `highlight.js` с CDN (не требует сборки) -- Кнопки ▶ Старт / ■ Стоп → `PATCH /triggers/{name}` через fetch -- `text/plain` ответ для curl/CLI остаётся без изменений +- Кнопки ▶ Старт / ■ Стоп → `PATCH /funcs/{ns}/triggers/{name}` через fetch +- HTML шаблон `index.html` встроен в бинарник через `//go:embed index.html` +- `text/plain` ответ для curl/CLI остаётся без изменений (браузер шлёт `Accept: text/html`, curl — нет) --- diff --git a/doc/progress.md b/doc/progress.md index 13f60e1..51a880b 100644 --- a/doc/progress.md +++ b/doc/progress.md @@ -1,6 +1,6 @@ # Прогресс разработки -Последнее обновление: 2026-03-18 22:00 (web-консоль v0.2.0 задеплоена, ветка feat/web-console) +Последнее обновление: 2026-03-18 23:30 (документация синхронизирована для передачи контекста новому агенту) ## 2026-03-18 — web-консоль (оператор v0.1.34 + funcs-service v0.2.0)