Files
sless/doc/progress.md
T

41 KiB
Raw Blame History

Прогресс разработки

Последнее обновление: 2026-03-19 14:00

2026-03-19 — event-trigger refactor (ветка feat/event-trigger-refactor)

# Задача Статус Заметки
1 Аудит кластера — удаление мусора Удалены: event-monitor/writer/cleaner, pg-* тестовые функции, failed build jobs, orphan namespaces b1e4df2d/b794a3c4
2 Managed RabbitMQ через Terraform terraform/RABBIT/, ns 1dbfe9da-..., host: rabbitmqk8s.1dbfe9da-...svc.cluster.local
3 Архитектура event-trigger задокументирована doc/decisions/log.md — выбран Вариант A (отдельный event-dispatcher)
4 trigger_types.go: +TriggerTypeEvent, +Queue
5 internal/config: +RabbitMQURL
6 services/event-dispatcher/ Go-сервис: dispatcher.go, watcher.go, main.go
7 controllers/trigger_controller.go: +reconcileEvent Создаёт Service, обновляет status
8 deployments/k8s/event-dispatcher.yaml ServiceAccount + ClusterRole + Deployment
9 Образ naeel/sless-event-dispatcher:v0.1.0 Собран, запушен в DockerHub
10 RABBITMQ_URL в sless-operator-secret managed rabbit (1dbfe9da namespace)
11 event-dispatcher задеплоен в кластер Running в sless namespace

2026-03-19 — pg-table-writer HTML + bugfix invoke.go Content-Length (оператор v0.1.37)

# Задача Статус Заметки
1 Python runtime v0.1.4: text/html поддержка server.py: str начинается с <text/html; charset=utf-8; добавлен _accept в event
2 internal/builder/context.go: python runtime → v0.1.4
3 Образ naeel/sless-runtime-python3.11:v0.1.4 Собран и запушен
4 code/table-rw/table_rw.py Комбинированный reader+writer. list_rows — JSON. add_row: GET → HTML форма, POST form → INSERT + HTML ответ. _render_page шаблон с тёмной темой
5 examples/POSTGRES/resources.tf reader: source_dir=code/table-rw, entrypoint=table_rw.list_rows. Новые: sless_function.postgres_table_writer, sless_trigger.postgres_table_writer_http, output.table_writer_url
6 Удалена папка code/table-reader/
7 internal/builder/builder.go: убран --cache=true kaniko по умолчанию не кэширует слои
8 Оператор v0.1.35 (промежуточный)
9 Оператор v0.1.36 Убран ошибочный --no-cache (kaniko v1.24.0 не поддерживает этот флаг)
10 Оператор v0.1.37 Bugfix invoke.go: добавлен proxyReq.ContentLength = r.ContentLength. Без этого Python-сервер читал тело как 0 байт (бесконечно chunked) — форма не парсилась
11 POST через прокси curl POST title=test-proxy-fix → HTML с «Добавлено: «test-proxy-fix»», строка в таблице

Root cause бага формы

internal/api/handler/invoke.go: при проксировании POST-запроса не устанавливался Content-Length. Go http.Client отправлял запрос без Content-Length → Python BaseHTTPRequestHandler.headers.get("Content-Length", 0) = 0 → тело не читалось → title пустой → JSON ошибка. Фикс: одна строка proxyReq.ContentLength = r.ContentLength.

Образы

Образ Версия Что изменилось
naeel/sless-operator v0.1.37 invoke.go: proxyReq.ContentLength = r.ContentLength
naeel/sless-runtime-python3.11 v0.1.4 text/html support, _accept in event

2026-03-18 — web-консоль (оператор v0.1.34 + funcs-service v0.2.0)

2026-03-18 — web-консоль (оператор v0.1.34 + funcs-service v0.2.0)

# Задача Статус Заметки
1 Оператор: GET /v1/.../functions/{name}/source internal/api/handler/source.go — скачивает tar.gz контекст из S3; извлекает пользовательские файлы (без Dockerfile); возвращает [{name, content, binary}]
2 Оператор: PATCH /v1/.../triggers/{name} Уже существовал в v0.1.33 — Handler обрабатывает поле enabled
3 internal/api/router.go — маршрут /source GET /namespaces/{ns}/functions/{name}/source → h.GetSource
4 services/funcs/index.html — HTML web-консоль Тёмная тема, аккордеоны, highlight.js, кнопки ▶/■ для enable/disable триггеров
5 services/funcs/main.go — HTML режим + proxy endpoints Accept: text/html → HTML; GET /funcs/{ns}/source/{fn} и PATCH /funcs/{ns}/triggers/{name} — прокси к оператору через SLESS_SERVICE_TOKEN
6 Backward compat: plain text curl без Accept: text/html — старое поведение v0.1.x
7 Оператор v0.1.34 Build+push+deploy в кластер
8 funcs-service v0.2.0 Build+push+deploy в кластер
9 Коммит bf9f073feat: web-console — HTML UI + source viewer + trigger toggle

Текущие URL

https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae          # браузер → HTML консоль
                                                                # curl → plain text (backward compat)
https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae/source/pg-info  # JSON файлы функции
https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae/pg-info       # вызов функции

Образы

Образ Версия Что изменилось
naeel/sless-operator v0.1.34 GET /source endpoint, HTML web-консоль proxy routes
naeel/sless-funcs-service v0.2.0 HTML консоль + source/trigger proxy endpoints

2026-03-18 — FunctionJob bugfix (functionjob label + stderr capture)

# Задача Статус Заметки
1 functionjob_controller.go: label на PodTemplate Добавлен PodTemplate.ObjectMeta.Labels: {managed-by, functionjob, function} — k8s 1.27+ удалил встроенный job-name= лейбл
2 getJobPodOutput по labelSelector Принимает "functionjob=<name>" вместо jobName; используется для успешного и упавшего job
3 Захват stderr при ошибке Job При job.Status.Failed > 0 — берём podOutput; если непустой — пишем в status.Message (truncate до 2000 символов)
4 truncateForStatus() helper Ограничивает длину status.Message для безопасной записи в k8s
5 terraform client: ErrJobAlreadyExists client.go: 409 Conflict → ErrJobAlreadyExists; job_resource.go: при конфликте создания — читаем существующий job вместо ошибки

2026-03-18 — Python runtime str→text/plain

# Задача Статус Заметки
1 runtimes/python3.11/server.py Если функция вернула strContent-Type: text/plain, тело без json.dumps. Dict/list остаются application/json
2 internal/builder/context.go Python runtime базовый образ → v0.1.3
3 Образ naeel/sless-runtime-python3.11:v0.1.3 Собран и задеплоен

2026-03-18 — funcs: глобальный Go сервис + ветка web-console

# Задача Статус Заметки
1 Python runtime str→text/plain runtimes/python3.11/server.py: если функция возвращает strContent-Type: text/plain. Образ naeel/sless-runtime-python3.11:v0.1.3
2 funcs_list.py: plain text вывод Переписан с JSON на plain text с разделителями, фильтр SLESS_EXCLUDE
3 Оператор v0.1.33 context.go: python runtime → v0.1.3; internal/builder/context.go обновлён
4 funcs как глобальный Go сервис services/funcs/main.go — standalone Go HTTP сервер. Деплоится ОДИН РАЗ в namespace sless, работает для ВСЕХ пользователей. Образ naeel/sless-funcs-service:v0.1.3
5 URL без токена https://sless.kube5s.ru/funcs/<namespace> — открывается в браузере без токена. SLESS_SERVICE_TOKEN задан через kubectl set env
6 Удалить funcs из terraform examples/POSTGRES/resources.tf: удалены sless_function.funcs_list, sless_trigger.funcs_list_http, output "funcs_url", local.user_namespace. terraform apply — 2 destroyed
7 Ветка feat/web-console Создана от feat/harbor-integration на remote, запушена
8 Документация архитектуры хранения кода См. doc/decisions/log.md раздел "Хранение кода функций и web-консоль"
9 Коммиты e8cd62e (funcs Go service), 38bb494 (v0.1.3 — /funcs/namespace без токена)

Текущие URL функций

https://sless.kube5s.ru/funcs/sless-ffd1f598c169b0ae     # список функций (без токена)
https://sless.kube5s.ru/funcs?token=<jwt>                 # то же, через токен
https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae/pg-info
https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae/pg-table-reader

Образы

Образ Версия Что изменилось
naeel/sless-operator v0.1.33 created_at, last_built_at в API; python runtime v0.1.3
naeel/sless-runtime-python3.11 v0.1.3 str → text/plain в server.py
naeel/sless-funcs-service v0.1.3 /funcs/ без токена, /health probe

2026-03-18 — Следующие шаги: web-консоль (ветка feat/web-console)

# Задача Статус Заметки
1 Оператор: GET /v1/namespaces/{ns}/functions/{name}/source Реализовано — bf9f073
2 Оператор: PATCH /v1/namespaces/{ns}/triggers/{name} Существовал — проверено, поддерживает enabled
3 sless-funcs-service: HTML страница Accept: text/html → HTML консоль. Аккордеоны + highlight.js + кнопки ▶/■
4 Bump оператора до v0.1.34 Задеплоен
5 Bump funcs-service до v0.2.0 Задеплоен

2026-03-18 12:00 — DNS инцидент устранён, POSTGRES E2E PASS

2026-03-18 — DNS инцидент: sless-api.kube5s.ru → мёртвый кластер

# Задача Статус Заметки
1 Диагностика TLS handshake timeout при terraform apply POSTGRES sless-api.kube5s.ru5.172.178.182 (старый кластер). Порт 443 принимал TCP, но TLS не завершался — Go HTTP клиент висел бесконечно
2 Подтверждение нового кластера kubectl get nodes в контексте tazetdinovn@gmail.com@naeel-test-3 → 3 ноды Ready. Ingress IP 185.247.187.147. DNS sless.kube5s.ru уже указывал на него
3 Обновить Ingress в кластере kubectl patch ingress sless-operator -n sless: host + TLS → sless.kube5s.ru, secretName → sless-operator-tls-sless
4 Обновить ConfigMap оператора EXTERNAL_URL: https://sless.kube5s.ru (было https://sless-api.kube5s.ru)
5 Let's Encrypt сертификат certificate/sless-operator-tls-sless READY=True, order valid — выдан сразу после смены ingress host (ACME HTTP-01 challenge на новом IP прошёл)
6 Заменить sless-api.kube5s.rusless.kube5s.ru во всех examples + deployments POSTGRES/, hello-go/, hello-node/, simple-node/, simple-python/, notes-python/, pg-list-python/, demo-managed-functions/, README.md, deployments/k8s/operator.yaml
7 POSTGRES terraform apply с функцией и job PASS sless_function pg-create-table-runner — создан за 55s. sless_job pg-create-table-job-main-v13 — создан за 20s. Apply complete! Resources: 2 added
8 Коммит и push cca3a8cfix: migrate sless endpoint from sless-api.kube5s.ru to sless.kube5s.ru

2026-03-17 — E2E тесты всех примеров на тест-стенде

# Задача Статус Заметки
1 Исправить nubes_endpoint во всех examples (prod→test) deck-api.ngcloud.rudeck-api-test.ngcloud.ru/api/v1 в hello-node, hello-go, simple-node, notes-python, pg-list-python, demo-managed-functions
2 Исправить sless_endpoint в demo-managed-functions 185.247.187.147.nip.iohttps://sless-api.kube5s.ru
3 Postgres для тестов postgres.sless.svc.cluster.local:5432, user=sless, pass=sless-pg-password, db=sless. Уже запущен в кластере
4 hello-node E2E PASS init → apply (4 ресурса) → modify (memory_mb 128→256, env_var) → apply → destroy
5 simple-python E2E PASS init → apply → modify (memory_mb 64→96) → apply → destroy. job_result = {"time": "..."}
6 simple-node E2E PASS Аналогично simple-python но nodejs20
7 notes-python E2E PASS 7 ресурсов, PostgreSQL init job: {"ok": true, "executed": 1}, destroy complete
8 Коммит на remote a768454 в /home/naeel/terra/sless/examples
9 POSTGRES example 🔄 Следующий шаг — вернуться к POSTGRES после прохождения всех тестов

2026-03-17 — v0.1.31: compile fix + POSTGRES example end-to-end

# Задача Статус Заметки
1 Баг 3: PodLogOptions{Stdout/Stderr} compile error Убраны несуществующие поля. getJobPodOutput в functionjob_controller.go
2 Сборка naeel/sless-operator:v0.1.31 Собрано на naeel@5.172.178.213, запушено в Docker Hub
3 Деплой v0.1.31 в кластер kubectl rollout status → success. Pod sless-operator-545556c59d-n8qv9
4 simple-python — сквозной тест на тест-стенде terraform apply на remote: Apply complete! Resources: 3 added. job_result = {"time": "..."}
5 Найдена главная причина падения POSTGRES nubes_endpoint в provider "sless" указывал на прод deck-api.ngcloud.ru
6 Исправлен nubes_endpoint в POSTGRES/main.tf Теперь deck-api-test.ngcloud.ru/api/v1
7 Исправлен nubes_endpoint в simple-python/main.tf Аналогично
8 End-to-end terraform apply для POSTGRES 🔄 Следующий шаг: синхронизировать на remote и запустить terraform apply

2026-03-17 — POSTGRES example: внешний managed PG, FunctionJob и реальные блокеры

# Задача Статус Заметки
1 Перевод examples/POSTGRES на PGSSLMODE=require Для managed PostgreSQL allow_no_s_s_l = false; disable был некорректен
2 Подтверждение root-cause password authentication failed Из runtime namespace поднят диагностический pod того же image: psycopg2.OperationalError: FATAL: password authentication failed for user "u-user0"
3 Проверка источника пароля nubes_postgres.npg.vault_secrets["users"] не совпадает с фактическим паролем пользователя в кластере managed PostgreSQL; authoritative source сейчас — k8s secret u-user0.postgresqlk8s.credentials.postgresql.acid.zalan.do
4 Попытка читать пароль через hashicorp/external В текущем окружении доступен только кастомный registry; registry.terraform.io/hashicorp/external не устанавливается
5 One-command terraform apply для examples/POSTGRES 🔄 Остаточный баг локализован в связке credential-source + FunctionJob observability. После переключения на актуальный пароль sless_job всё ещё падает, но актуальные pod logs реального job теряются из-за TTL/слабой наблюдаемости
6 Необходимый следующий технический шаг 🔄 Либо добавить в проект доступный data-source/провайдер для чтения k8s secret внутри apply, либо починить источник пароля в nubes-потоке, чтобы vault_secrets и реальный Postgres user password совпадали

2026-03-17 — Fix: examples/POSTGRES / sless_job 409 + диагностика FunctionJob

# Задача Статус Заметки
1 Идемпотентность sless_job при 409 job already exists terraform/provider/internal/client/client.go, terraform/provider/internal/resources/job_resource.go: добавлен typed-error ErrJobAlreadyExists, в Create при 409 читается существующий Job и продолжается ожидание/синхронизация вместо немедленного падения
2 Диагностика падений FunctionJob controllers/functionjob_controller.go: labels добавлены в PodTemplate, fail-message теперь включает output pod-а (обрезка до 2000), fallback-команда логов по job-name
3 Root-cause для examples/POSTGRES Рабочие examples/notes-python используют внутренний DSN (postgres.sless.svc), а examples/POSTGRES работает через managed Nubes Postgres + динамические креды; критичный UX-дефект был в повторном Create после failed apply

Статусы: готово | 🔄 в процессе | не начато


2026-03-10 — Архитектурный разбор и handoff для следующего агента

# Задача Статус Заметки
1 GPT-5.4: Подробный разбор кода и lifecycle issues doc/architecture/agent-handoff-2026-03-10.md — детальный code review, event model problems, invocation history gap, приоритеты
2 Claude Sonnet 4.6: Review + security roadmap doc/architecture/sonnet-review-of-gpt-analysis.md — где GPT-5.4 прав, что пропустил (gVisor, LLM validation, NetworkPolicy), production security roadmap
3 Claude Opus 4.6: Прагматичный обзор для небольшого провайдера doc/architecture/opus-pragmatic-review-2026-03-10.md — анализ с учётом масштаба, конкретный план по этапам, разбор где коллеги перемудрили
4 Claude Opus 4.6: Дизайн LLM-валидации кода doc/decisions/log.md → "2026-03-10 — LLM-валидация кода при upload" — архитектура, интерфейс, prompt, план файлов

v1 — Базовый сервис

Код

# Компонент Статус Заметки
1 Структура проекта, operator-sdk init module: gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless
2 CRD types: Function, Trigger api/v1alpha1/, zz_generated.deepcopy.go
3 internal/config config.go, Load() из env vars, добавлены IngressHost + APIToken
4 Миграции PostgreSQL migrations/001_initial.sql — таблица invocations
5 internal/storage/postgres store.go: SaveInvocation, ListInvocations, RunMigrations
6 internal/storage/s3 client.go: Upload, Download, Delete, EnsureBucket (Ceph S3)
7 internal/builder builder.go: kaniko Jobs для сборки образов функций
8 controllers/function_controller.go Pending→Building(kaniko)→Ready/Failed, Deployment, finalizer; handleDeletion удаляет Service+Ingress
9 controllers/trigger_controller.go HTTP→Service+Ingress (fallback) или прокси через ExternalURL, Cron→CronJob, finalizer; handleTriggerDeletion удаляет Service+Ingress
10 internal/api/router.go gorilla/mux, /v1/namespaces/{ns}/... (auth), /fn/{ns}/{name} (публичный прокси)
11 internal/api/handler/ functions.go, triggers.go, invocations.go, invoke.go (proxy — no such host → 404), handler.go (slog)
12 internal/api/middleware/ auth.go (Bearer token), logging.go (slog)
13 main.go operator manager + HTTP сервер параллельно, wire всех зависимостей

Кластер (namespace: sless)

# Ресурс Статус Заметки
1 namespace sless создан
2 CRD functions.sless.kube5s.ru установлен из config/crd/bases/
3 CRD triggers.sless.kube5s.ru установлен из config/crd/bases/
4 ServiceAccount sless-operator deployments/k8s/rbac.yaml
5 ClusterRole + ClusterRoleBinding deployments/k8s/rbac.yaml
6 PostgreSQL Deployment + Service deployments/k8s/postgres.yaml, Running
7 Secret postgres-secret в namespace sless

Runtime image + Upload endpoint (2026-03-07)

# Компонент Статус Заметки
1 runtimes/python3.11/server.py HTTP wrapper :8080, loads /app/function/handler.py
2 runtimes/python3.11/Dockerfile FROM python:3.11-slim, naeel/sless-runtime-python3.11:v0.1.0
3 Secret sless-registry-auth DockerHub creds для kaniko в namespace sless
4 internal/api/handler/upload.go POST /v1/namespaces/{ns}/functions/{name}/upload
5 Fix: kaniko S3 endpoint https:// + S3_FORCE_PATH_STYLE=true (Ceph)
6 Fix: бесконечный цикл сборки idempotency guard через аннотацию last-built-s3key

E2E тест (2026-03-07)

# Шаг Результат
1 Запуск оператора локально source hack/local.env && go run main.go
2 Создание функции hello + upload handler.zip kaniko собрал naeel/sless-default-hello:latest
3 Function phase=Ready, Deployment Running namespace sless-fn-default
4 GET /health {"status": "ok"}
5 POST / {"name":"Naeel"} {"message": "Hello, Naeel!"}
6 terraform apply (pg-query python3.11) {"invocations": [], "count": 0} через https://sless-api.kube5s.ru
7 hello-node (nodejs20) E2E

Terraform провайдер (2026-03-07)

# Компонент Статус Заметки
1 terraform/provider/ — независимый Go-модуль module: terraform-provider-sless
2 internal/client/client.go HTTP-клиент sless API, готов к переносу в nubes
3 internal/provider/provider.go паттерн идентичен nubes NubesProvider
4 sless_function resource CRUD + upload zip + WaitReady (kaniko)
5 sless_trigger resource CRUD (http/cron); Delete ждёт исчезновения CR (WaitGone, 90с)
6 hack/build-and-publish.sh GPG-подпись + mc upload в S3
7 Публикация в terra.k8c.ru v0.1.3, key CB3A0DF161ECC416
8 terraform init Installed terra.k8c.ru/naeel/sless v0.1.3
9 sless_job resource одноразовые запуски функции через FunctionJob CRD
10 build_timeout_sec / wait_timeout_sec настраиваемые таймауты в sless_function и sless_job

Примечание: Код провайдера писался с прицелом на перенос в nubes провайдер. internal/client/internal/core/ nubes, ресурсы → internal/resources_gen/.

source_dir + fix destroy cleanup (2026-03-09)

# Компонент Статус Заметки
1 provider: source_dir атрибут zip в памяти, SHA256 автоматически; hashicorp/archive не нужен
2 fix: trigger_controller handleTriggerDeletion удаляет Service+Ingress из sless-fn-{ns}
3 fix: function_controller handleDeletion удаляет Service+Ingress из sless-fn-{ns}
4 fix: trigger_resource.go Delete WaitGone polling GetTrigger до 404, таймаут 90с
5 fix: invoke.go no such host → 404 после destroy DNS NXDOMAIN → 404 вместо 502
6 E2E тест run_terraform_examples.sh все 4 примера: hello-node, simple-node, simple-python, notes-python

Что ещё не сделано

# Компонент Статус Заметки
1 terraform apply e2e — функция с postgres {"invocations": [], "count": 0} — функция подключилась к postgres.sless.svc, таблица пустая
2 HTTP trigger e2e тест прокси /fn/ через sless-api, без wildcard DNS
3 deployments/k8s/operator.yaml Deployment+Service+Ingress, naeel/sless-operator:v0.1.5, Running
4 Внешний доступ к API оператора https://sless-api.kube5s.ru → HTTP/2 200, TLS cert OK
5 DNS запись sless-api.kube5s.ru A → 5.172.178.182, TTL 3600, zone kube5s.ru
6 nodejs20 runtime runtimes/nodejs20/server.js + Dockerfile, naeel/sless-runtime-nodejs20:v0.1.0
7 upload.go: nodejs20 + package.json runtimeBaseImage switch, hasPackageJSON → npm install
8 go.mod: direct/indirect fix go mod tidy — gorilla/mux, lib/pq, minio-go, k8s.io/api помечены direct
9 Runtime образы: версионированные теги python3.11:v0.1.0, nodejs20:v0.1.0 (убираем :latest)

v2 — Расширения

# Компонент Статус Заметки
1 RabbitMQ event triggers
0 Изоляция пользователей по namespace Каждый токен → свой k8s namespace. API: GET /v1/whoami → {namespace}. Провайдер получает ns при Configure(), юзер не видит. Сейчас: хардкод "default" для демо.
2 Облачный auth (nubes.ru token validation) v1 — статический токен
3 Метрики → Victoria Metrics
4 Managed PostgreSQL от провайдера сейчас postgres в k8s
5 Pre-warm для cron триггеров TriggerSpec.PreWarmSeconds — поле есть, логика не реализована
11 trigger.enabled enabled=false → Deployment replicas=0, in-place update через PATCH
12 job.run_id run_id=0 → skip, run_id>0 → execute. Повторный запуск = увеличить run_id

2026-03-11 — Namespace-per-user + SoC рефакторинг

# Задача Статус Заметки
1 JWT decode в провайдере (SubFromJWT, NamespaceFromSub) terraform/provider/internal/client/client.go
2 PingNubesAPI в provider.Configure() атрибут nubes_endpoint, env NUBES_ENDPOINT
3 JWT auth в операторе (validateJWT vs статический токен) internal/api/middleware/auth.go — sub + exp, без подписи
4 EnsureNamespace как отдельный endpoint POST /v1/namespaces/{ns}/ensure, handler/namespace.go
5 router.go: маршрут /ensure добавлен перед Functions CRUD
6 client.go провайдера: метод EnsureNamespace terraform/provider/internal/client/client.go
7 provider.Configure(): вызов EnsureNamespace namespace создаётся один раз при init
8 handler.go: убраны k8s-типы (SoC) только Handler struct + helpers
9 secrets/ исключены из git (.gitignore) токены не попадают в репу
10 E2E тест: apply + destroy namespace sless-cdd874dfa31ba6ca, все 4 ресурса
11 operator v0.1.21 задеплоен naeel/sless-operator:v0.1.21
12 provider v0.1.13 опубликован terra.k8c.ru/naeel/sless v0.1.13
13 commit + push feat/namespace-per-user a1774e1

Текущая ветка: feat/namespace-per-user
Последний коммит: a1774e1


2026-03-11 — Opus review: fixes + Builder SoC + unit tests (v0.1.22)

# Задача Статус Заметки
1 CronJob перемещён в deployNS (sless-fn-{userNS}) NetworkPolicy-ready
2 curl:8.5.0 (pin вместо :latest) стабильный образ
3 sort env vars в buildDeployment нет лишних rollout'ов при reconcile
4 cleanup kaniko Job в handleDeletion при удалении Function во время Building
5 hop-by-hop headers отфильтрованы в /fn/ прокси RFC 2616 §13.5.1, 8 заголовков
6 SLESS_API_TOKEN — убран required был dead code (auth через JWT)
7 Builder SoC: internal/builder/context.go PrepareContext публичный API, upload.go 200→60 LOC
8 JWKS insertion point stub в auth.go verifySignature() заготовка для v2
9 Unit tests: 9 тестов, все PASS controllers×2, handler×3, builder×4
10 operator v0.1.22 задеплоен kubectl rollout status — complete
11 Коммиты e761439 + 18f25e7 ветка feat/namespace-per-user

Последний коммит: 18f25e7


2026-03-11 — operator v0.1.23: bug fixes + полный stress test PASS

Исправленные баги

# Компонент Баг Исправление
1 run_stress_test.sh mod2 для simple-node/python: patch_memory time-getter.tf 64→96time-getter.tf начинался с 96, не с 64 → "No changes" → assertion FAIL Изменено на 96→128
2 internal/builder/builder.go BackoffLimit=0 — при транзиентном сбое kaniko (OOM, network blip) сборка сразу считалась провалившейся без retry BackoffLimit=2
3 internal/api/handler/functions.go При BUILD FAIL: Function оставалась в API в статусе Failed, terraform её не добавлял в state → следующий apply → 409 "function already exists" → orphaned resource На 409+phase=Failed: удаляем + пересоздаём

Результат стресс-теста (operator v0.1.23)

Пример Результат Время
hello-node PASS 165s
simple-node PASS 250s
simple-python PASS 222s
notes-python PASS 112s
ИТОГО 4/4 = 100% 749s

Коммит: 59563eb
Схема запуска: агент запускает run_stress_test.sh на удалённом сервере 192.168.1.220 через SSH, получает логи из /tmp/stress_test_run2.log


2026-03-11 — E2E тесты через скрипт run_e2e_tests.sh

Пример init apply modify + apply destroy Итог
hello-node memory 128→256 MB 4 destroyed PASS
simple-node memory 64→96 MB 4 destroyed PASS

Скрипт: run_e2e_tests.sh в корне репы
Возможности: retry 3× при TLS timeout, emergency destroy trap, логи в .e2e-logs/
Провайдер в примерах: токен через var.token + terraform.tfvars (gitignored), version ~> 0.1.13

Наблюдения:

  • Периодические TLS handshake timeout на sless-api.kube5s.ru — сеть нестабильна, retry помогает
  • terra.k8c.ru иногда даёт unexpected EOF при скачивании провайдера — то же, retry
  • Namespace sless-cdd874dfa31ba6ca жив после всех destroy — поведение корректное

Остаток технического долга (не блокирует)

# Что Приоритет
1 ensureRegistrySecret в FunctionReconciler — cross-namespace инфра операция Низкий
2 invocations.go — 501 stub, PG подключён но endpoint не реализован Средний
3 LLM-валидация кода при upload Средний (решение задизайнено в decisions/log.md)
4 JWKS подпись (verifySignature) — stub есть, логика не реализована Средний (ждёт JWKS endpoint от nubes)
5 Scale-to-zero через KEDA HTTP Add-on Низкий (v2)
6 replicas field в FunctionSpec Низкий (v1.1)
7 RabbitMQ event triggers Низкий (v2)
8 Метрики → Victoria Metrics Низкий

2026-03-11 — Harbor integration + Go 1.23 runtime (v0.1.24v0.1.26)

Что сделано

# Компонент Изменение
1 internal/harbor/ Harbor-клиент: EnsureProject создаёт проект перед push образа
2 internal/builder/builder.go Поддержка HarborClient, push в pearlharbor.registryk8s.services.ngcloud.ru
3 runtimes/go1.23/ Новый Go 1.23 runtime: server.go + runner.go + Dockerfile
4 internal/builder/context.go Go 1.23 в switch; COPY . /app/handler/ вместо COPY handler/
5 api/v1alpha1/function_types.go go1.23 в enum поддерживаемых рантаймов
6 terraform/provider/ v0.1.14: go1.23 в stringvalidator.OneOf
7 deployments/k8s/operator.yaml v0.1.26
8 examples/hello-go/ Новый пример: HTTP + job на Go 1.23
9 examples/hello-go/code/greeting.go Переименован из handler.go, функция buildGreeting(guestName)
10 examples/pg-list-python/ Новый пример: Python + PostgreSQL, только HTTP, без job

Docker Hub auth на 192.168.1.220

  • Токен: dckr_pat_aElN35tdXMBrtOAPraV5Fi0o58s (сохранён в secrets/dockerhub.env, chmod 600)
  • docker login -u naeel --password-stdin выполнен на remote machine

Исправленные баги

  • kaniko COPY handler/ /app/handler/ — файлы в tar-контексте без prefix → исправлено на COPY .
  • Decimal из psycopg2 не сериализуется → float(row[2]) в catalog.py
  • Formatter добавил дублирующий package code в greeting.go — удалён вручную
  • CRD в кластере имел только go1.21 в enum → kubectl apply обновлён CRD
  • Provider v0.1.13 имел только go1.21 → пересобран v0.1.14

Git HEAD

d6a2e22  fix: восстановлен requirements.txt после теста
5c6a373  docs: переработан examples/README.md + комментарии function.tf
2ca3137  feat: пример pg-list-python
c4559dd  fix: go1.23 build context
78d11ae  refactor: rename handler.go→greeting.go
a709b38  feat: go1.23 runtime support
babd8e6  feat: harbor integration

2026-03-11 — UX улучшения: build logs + JSON для всех HTTP методов (v0.1.27v0.1.28)

Проблемы до исправления

Проблема Симптом
Ошибка сборки без деталей terraform apply показывал только function build failed: build job failed без причины
HTML 501 при DELETE/HEAD/PUT Python runtime http.server не обрабатывал эти методы → HTML-ответ вместо JSON

Что изменили

controllers/function_controller.go

  • FunctionReconciler получил поле KubeClient kubernetes.Interface
  • Добавлен getBuildPodLogs(ctx, kube, namespace, jobName) — читает логи kaniko-пода (последние 50 строк)
  • При сбое сборки (case "failed") вызывает getBuildPodLogs и включает вывод в Function.Status.Message
  • Провайдер пробрасывает Message в ошибку terraform apply — разработчик видит точную причину

runtimes/python3.11/server.py

  • Выделен _handle_with_body() — общий обработчик для методов с телом
  • do_PUT, do_DELETE, do_PATCH = _handle_with_body — вызывают функцию пользователя
  • do_HEAD — отдаёт заголовки без тела (HTTP-стандарт; тело вычисляется но не отправляется)
  • send_error() переопределён → JSON {"error": "...", "code": N} вместо HTML 501
  • Runtime пересобран: naeel/sless-runtime-python3.11:v0.1.2

main.go

  • KubeClient: kubernetes.NewForConfigOrDie(mgr.GetConfig()) передаётся в FunctionReconciler

internal/builder/context.go

  • python3.11 runtime: v0.1.1v0.1.2

Версии после исправления

  • Оператор: naeel/sless-operator:v0.1.28
  • Python runtime: naeel/sless-runtime-python3.11:v0.1.2
  • Provider: terra.k8c.ru/naeel/sless v0.1.14 (без изменений)

Результат тестирования

Тест До После
DELETE /fn/... HTML 501 JSON 200, функция вызывается
HEAD /fn/... HTML 501 HTTP 200, Content-Type: application/json
PUT /fn/... HTML 501 JSON 200, функция вызывается
Невалидный requirements.txt build job failed Полный вывод pip включая строку с ошибкой

Git коммиты

6de90ac  chore: python runtime v0.1.2 + operator v0.1.28
3372cb1  fix: build logs in status + JSON for all HTTP methods in python runtime