Commit Graph
89 Commits
Author SHA1 Message Date
“Naeel” a709b38f6b feat: go1.23 runtime support
- runtimes/go1.23/server.go: HTTP-wrapper + job-runner (SLESS_MODE=job)
- runtimes/go1.23/go.mod: module sless/fn (изолирует от корневого go.mod)
- runtimes/go1.23/Dockerfile: multi-stage build (golang:1.23-alpine → alpine:3.20)
- internal/builder/context.go: go1.23 в runtimeBaseImage + generateDockerfile
- controllers/functionjob_controller.go: go1.23 runner (nil cmd + SLESS_MODE=job env)
- api/v1alpha1/function_types.go: enum go1.21 → go1.23
- config/crd/bases/...: CRD обновлён
- terraform/provider: OneOf обновлён
- examples/hello-go: HTTP + job примеры на go1.23
- deployments/k8s/operator.yaml: v0.1.25
2026-03-11 15:40:47 +04:00
“Naeel” babd8e6109 feat: harbor integration — EnsureProject + per-namespace image path
- internal/harbor/client.go: новый пакет, EnsureProject (GET+POST idempotent)
- config.go: добавлены HarborUser/HarborPass (из HARBOR_USER/HARBOR_PASS env)
- builder.go: Projecter интерфейс, harborClient поле, ImageRef: {host}/{ns}/{func}:{tag}
  EnsureProject вызывается перед каждым Build()
- function_controller.go: HarborClient поле, EnsureProject при создании k8s NS
- main.go: создание harbor.Client если HARBOR_USER+HARBOR_PASS заданы
- operator.yaml: REGISTRY_HOST=pearlharbor..., HARBOR_USER=admin, v0.1.24
- hack/create-registry-secret.sh: переписан для Harbor (HARBOR_USER/HARBOR_PASS)

Смена registry — только через REGISTRY_HOST в ConfigMap, больше нигде.
2026-03-11 14:58:04 +04:00
“Naeel” 6443f21ddc Ignore nested examples .git 2026-03-11 14:45:35 +04:00
“Naeel” 869d728684 chore: staged files from prev session (gitignore, examples, harbor guide) 2026-03-11 14:36:32 +04:00
“Naeel” f033ae0de0 docs: удалённая машина теперь полная — kubectl + kubeconfig скопирован 2026-03-11 12:58:39 +04:00
“Naeel” 729658f9e2 docs: stress test 4/4 PASS (v0.1.23), баги задокументированы 2026-03-11 12:41:04 +04:00
“Naeel” 59563eba76 fix: operator v0.1.23 — BackoffLimit=2 в kaniko, пересоздание Failed функций; script: mod2 96→128 2026-03-11 12:26:06 +04:00
“Naeel” 3dc39ddc20 fix: run_stress_test.sh — tf_output_json для объектных outputs, fix pipeline в assert_step 2026-03-11 11:35:27 +04:00
“Naeel” 09fd6b9f42 chore: gitignore logs dirs 2026-03-11 11:14:28 +04:00
“Naeel” 3026d032b3 test: run_stress_test.sh — полный стресс-тест всех примеров 2026-03-11 11:14:06 +04:00
“Naeel” 6c6040d8f8 test: E2E скрипт run_e2e_tests.sh + fix provider token/version в примерах 2026-03-11 10:49:36 +04:00
“Naeel” b12b72c989 docs: update for v0.1.22 — Builder SoC, immediate fixes, unit tests 2026-03-11 09:42:12 +04:00
“Naeel” 18f25e7a65 refactor: Builder SoC + JWKS stub + unit tests
Builder SoC (builder/context.go):
- Moved generateDockerfile, runtimeBaseImage, zipToTarGz from handler/upload.go
  to internal/builder/context.go.
  Reason: knowledge about runtime images and build context structure is a
  build concern, not an HTTP handler concern.
- Added PrepareContext(zipData []byte, runtime string) (*bytes.Buffer, error) —
  single public entry point. Handler calls one function, gets ready buffer.
- zipToTarGz now accepts *zip.Reader instead of []byte to avoid double parsing.
- upload.go reduced from ~200 LOC to ~60 LOC (build logic gone).

auth.go — JWKS insertion point:
- Added verifySignature() stub with detailed comment explaining what v2
  implementation needs (JWKS endpoint, kid lookup, RS256/ES256 verify).
- Shows exactly where to add the call in validateJWT.

Unit tests (9 total, all pass):
- controllers: TestBuildDeployment_EnvVarsSorted, TestBuildDeployment_EmptyEnv
- handler: TestHopByHopHeaders_* (3 tests)
- builder: TestPrepareContext_PythonWithRequirements, _NodeNoPackageJSON,
           _UnsupportedRuntime, _DockerfileIsFirst
2026-03-11 09:34:34 +04:00
“Naeel” e761439546 fix: immediate fixes from Opus review (v0.1.22)
- trigger: CronJob moved to deployNS (sless-fn-{userNS}), was tr.Namespace
  Reason: with NetworkPolicy default-deny, pod in user-ns can't reach
  Service in sless-fn-ns. Co-locating CronJob with Service guarantees
  connectivity regardless of NetworkPolicy configuration.
  handleTriggerDeletion updated consistently.

- trigger: pin curlimages/curl to 8.5.0 (was :latest)
  Reason: reproducibility, no unexpected behavior changes from image updates.

- function: sort env vars in buildDeployment (was non-deterministic map range)
  Reason: non-deterministic order caused k8s to detect container spec 'change'
  on every reconcile → unnecessary pod restarts. Sorted order is stable.

- function: cleanup kaniko Job in handleDeletion
  Reason: if Function deleted during Building phase, kaniko Job continued
  running, wasting CPU/memory and pushing an unused image.

- invoke: filter hop-by-hop headers in proxy response (RFC 2616 §13.5.1)
  Reason: Transfer-Encoding especially dangerous — forwarding it corrupts
  response body framing for the client.

- config: SLESS_API_TOKEN no longer required
  Reason: dead code — field loaded but never passed to any component.
  Auth uses validateJWT() middleware, not static token.

Namespace lifecycle: user namespaces preserved on destroy (not changed).
E2E: apply 4 resources + destroy clean. Operator v0.1.22 deployed.
2026-03-11 09:20:09 +04:00
“Naeel” bca889d355 docs: обновление документации 2026-03-11
- architecture/overview.md — актуальная архитектура: namespace-per-user,
  схема JWT->SHA256->namespace, структура кода, версии в production
- decisions/log.md — новые решения: два провайдера нельзя объединять,
  namespace-per-user, EnsureNamespace как отдельный endpoint (SoC),
  JWT validation вместо статического токена, валидация через nubes API,
  handler.go SoC рефакторинг
- progress.md — статус 2026-03-11 (all done), технический долг
- architecture/agent-handoff-2026-03-11.md — подробный handoff для Opus:
  полная архитектура, lifecycle контроллеров, примеры кода, ТЗ, вопросы
2026-03-11 08:47:50 +04:00
“Naeel” a1774e178f refactor: SoC — EnsureNamespace в namespace.go, маршрут /ensure, client.EnsureNamespace, fix secrets в .gitignore
- handler.go: убраны бизнес-логика и k8s-типы (corev1/k8serrors/metav1)
  handler.go теперь только инфраструктура: Handler struct + helpers
- namespace.go: новый файл — EnsureNamespace хендлер живёт здесь
  SoC: создание namespace — отдельная ответственность, не смешивается с CRUD
- router.go: добавлен маршрут POST /v1/namespaces/{namespace}/ensure
- client.go: добавлен метод EnsureNamespace(ctx, ns) → POST /ensure
- provider.go: Configure() вызывает c.EnsureNamespace(ctx, namespace) после создания Client
  Namespace создаётся ОДИН РАЗ при инициализации провайдера
  Resource-хендлеры (Function, Trigger, Job) namespace не трогают
- .gitignore: добавлена директория secrets/ (токены, ключи)
- provider v0.1.13, operator v0.1.21

Operator: naeel/sless-operator:v0.1.21
Provider: terra.k8c.ru/naeel/sless v0.1.13
2026-03-11 08:37:33 +04:00
“Naeel” 5ae2ee7f85 feat: JWT auth in operator + hello-node example updated
- operator: auth middleware теперь валидирует JWT (sub+exp), не статический токен
- operator: ensureNamespace идемпотентен при race condition (IsAlreadyExists)
- operator: NewRouter убран параметр apiToken — больше не нужен
- examples/hello-node: prod.token + nubes_endpoint + версия провайдера 0.1.12
- протестировано: namespace sless-cdd874dfa31ba6ca создан автоматически
2026-03-11 07:51:06 +04:00
“Naeel” f41cd39b26 feat: namespace-per-user via JWT sub SHA256 + ensureNamespace in operator
- operator: ensureNamespace() создаёт k8s namespace при первом Create-запросе
- operator: defaultNamespace константа вместо хардкода 'default'
- provider: SubFromJWT декодирует JWT payload, извлекает sub
- provider: NamespaceFromSub вычисляет sless-{sha256[:8]} из sub
- provider: PingNubesAPI валидирует токен запросом к nubes API
- provider: Configure вычисляет namespace и создаёт Client с ним
- provider: новый атрибут nubes_endpoint (опционально, env: NUBES_ENDPOINT)
2026-03-11 07:35:49 +04:00
“Naeel” 6dff628975 docs: clarify separate providers decision; fix invocations 501 stub 2026-03-11 07:24:34 +04:00
“Naeel” 408f58a9e2 fix: stage0 quick fixes (operator v0.1.19)
- TriggerReconciler: RequeueAfter 15s когда Function не Ready
  (ранее зависал без повторного reconcile)
- FunctionJobReconciler: RequeueAfter 15s когда Function не Ready
- UpdateFunction: добавлена валидация runtime/entrypoint/memory_mb
  (ранее мог затереть spec нулями при частичном обновлении)
- CronJob: curlimages/curl:latest → curlimages/curl:8.5.0 (pin version)
- Config: удалён FunctionNamespacePrefix (мёртвое поле, нигде не использовалось)
- Invocations endpoint: возвращает 501 вместо пустого списка
  (SaveInvocation нигде не вызывается — честный ответ клиенту)
- Собран образ naeel/sless-operator:v0.1.19
2026-03-10 17:36:48 +04:00
“Naeel” 7d6f8d6079 docs: добавлен анализ GPT-5.4 и Opus 4.6
- agent-handoff-2026-03-10.md — GPT-5.4 code review (lifecycle issues, invocation history gap)
- opus-pragmatic-review-2026-03-10.md — Opus прагматичный review для небольшого провайдера
- Opus: gVisor/LLM validation — overkill для MVP, фокус на быстрые фиксы + ResourceQuota/NetworkPolicy
- Обновлён progress.md с новыми документами
- .gitignore — добавлен test.token
2026-03-10 08:56:59 +04:00
“Naeel” 9d6db0d223 docs: Claude Sonnet review of GPT-5.4 analysis + security roadmap
- Added sonnet-review-of-gpt-analysis.md
- GPT-5.4 правильно выявил lifecycle issues (event model, invocation history)
- Но пропустил security roadmap (gVisor, NetworkPolicy, LLM validation)
- Дополнен production security roadmap для следующего агента
- Updated progress.md — зафиксированы оба анализа
2026-03-10 08:36:46 +04:00
“Naeel” 80991d2aab docs: update progress.md, errors/log.md; add gitignore for dist/ and tfstate backups
- doc/progress.md: обновлена версия до operator v0.1.18 / provider v0.1.11,
  добавлен блок source_dir + fix destroy cleanup (2026-03-09),
  уточнены комментарии к контроллерам и trigger_resource
- doc/errors/log.md: добавлены две записи — source_dir/hashicorp/archive,
  destroy route cleanup bug (три причины + решения)
- .gitignore: добавлены examples/*/dist/ и terraform.tfstate.*.backup
- examples/: удалены dist/ zip и tfstate.*.backup из трекинга
2026-03-09 20:29:27 +04:00
“Naeel” 26dce8f996 docs: clarify sless_function description 2026-03-09 20:24:59 +04:00
“Naeel” f4c03481e5 docs: remove informal tone from README 2026-03-09 20:24:23 +04:00
“Naeel” 30e34fde8e docs: add project description and resource types to examples README 2026-03-09 20:23:31 +04:00
“Naeel” 72e4137b0a fix: return 404 (not 502) when function Service is gone (DNS no such host)
After trigger/function destroy the Service is deleted, DNS lookup fails with
'no such host'. Previously InvokeFunction returned 502 which kept the test
script retrying for 120s. Now returns 404 — endpoint removed, test passes.

Operator: naeel/sless-operator:v0.1.18
2026-03-09 20:03:08 +04:00
“Naeel” 0b0a50d03a fix: restore token in provider blocks (dev-token-change-me is the real token) 2026-03-09 19:51:27 +04:00
“Naeel” 42645f3967 chore: remove hardcoded token from examples, use SLESS_API_TOKEN env 2026-03-09 19:25:52 +04:00
“Naeel” 1676bf3c5a fix: destroy route cleanup bug — delete Service+Ingress on trigger/function deletion
- controllers/trigger_controller.go: handleTriggerDeletion теперь удаляет Service
  и Ingress из sless-fn-{ns} при удалении HTTP-триггера
- controllers/function_controller.go: handleDeletion теперь удаляет Service и Ingress
  (имена совпадают с fn.Name); добавлен импорт netv1
- terraform/provider trigger_resource.go: Delete ждёт пока GetTrigger вернёт 404
  (до 90с) — провайдер не возвращает успех раньше чем cleanup завершён
- examples: версия провайдера обновлена до ~> 0.1.11

Operator: naeel/sless-operator:v0.1.17
Provider: v0.1.11

Fixes DESTROY_ROUTE_CLEANUP_BUG.md
2026-03-09 19:23:28 +04:00
“Naeel” 9b4ec729b5 chore: удалён пример pg-query 2026-03-09 17:46:52 +04:00
“Naeel” 5f913e9f25 feat: provider v0.1.10 — source_dir (zip внутри провайдера), убран archive провайдер из всех примеров 2026-03-09 17:45:45 +04:00
“Naeel” 6e236e5998 docs: examples/README.md — порядок simple-first, без токена, без pg-query 2026-03-09 15:57:29 +04:00
“Naeel” 5faec45a04 feat: provider v0.1.9 — namespace hardcoded in client, removed from provider schema and all examples 2026-03-09 15:27:55 +04:00
“Naeel” 17d32fcb39 feat: provider v0.1.9 — namespace removed from resources, moved to provider block 2026-03-09 15:12:07 +04:00
“Naeel” 0aaeb47b3b feat: operator v0.1.16 — job stdout -> status.Message (feature B)
- FunctionJobReconciler: added KubeClient field (kubernetes.Interface)
- getJobPodOutput(): reads pod logs via typed client after job succeeds
- main.go: inject kubernetes.NewForConfigOrDie into FunctionJobReconciler
- rbac.yaml: add pods/pods/log get/list/watch permissions
- examples/simple-python/: job->function chain demo (Python)
- examples/simple-node/: job->function chain demo (Node.js)

sless_job.X.message now contains the return value of the function
2026-03-09 14:50:06 +04:00
“Naeel” 53d9fa6e74 feat: outputs — статус init-джобов для пользователя без kubectl 2026-03-09 14:09:55 +04:00
“Naeel” 381ec44852 fix: operator v0.1.15 — обновлять Env при изменении функции
function_controller.go: при update deployment обновлять не только Image,
но и Env целиком. Без этого изменение entrypoint или env_vars не применялось
в уже существующем Deployment — поды продолжали стартовать со старым
SLESS_ENTRYPOINT и падали с FileNotFoundError.
2026-03-09 10:26:54 +04:00
“Naeel” cefef54936 refactor: уникальные имена функций-обработчиков
handle() → run_sql()    (sql_runner.py,  entrypoint: sql_runner.run_sql)
handle() → crud()       (notes_crud.py,  entrypoint: notes_crud.crud)
handle() → list_notes() (notes_list.py,  entrypoint: notes_list.list_notes)
2026-03-09 10:14:36 +04:00
“Naeel” 7dffc61c84 refactor: notes-python — описательные имена файлов/ресурсов + комментарии
Python файлы:
- handler.py → sql_runner.py   (entrypoint: sql_runner.handle)
- handler.py → notes_crud.py   (entrypoint: notes_crud.handle)
- handler.py → notes_list.py   (entrypoint: notes_list.handle)

TF ресурсы переименованы:
- sless_function.notes         → sless_function.notes_crud
- sless_trigger.notes_http     → sless_trigger.notes_crud_http
- sless_job.create_table       → sless_job.notes_table_init
- sless_job.create_index       → sless_job.notes_index_init
- archive_file.notes           → archive_file.notes_crud_zip
- archive_file.sql_runner      → archive_file.sql_runner_zip
- archive_file.notes_list      → archive_file.notes_list_zip

Добавлены подробные комментарии во все .tf файлы
2026-03-09 10:10:43 +04:00
“Naeel” 8602b023fc fix: provider v0.1.8 — wait for trigger URL after create
trigger_resource.go: after CreateTrigger, if URL is empty (HTTP trigger),
retry GetTrigger every 2s up to 30s until controller sets status.URL.
Without this, URL in terraform state was always empty after apply.
2026-03-09 09:58:37 +04:00
“Naeel” 88a0fb9db6 feat: notes-python CRUD example + runtime path/query forwarding
- invoke.go: forward sub-path and query string to function pods
- server.js v0.1.2: add _path, _query, _method to event
- server.py v0.1.1: add _path, _query, _method to event
- upload.go: bump runtime versions (nodejs20:v0.1.2, python3.11:v0.1.1)
- examples/notes-python: CRUD notes via sub-path routing
  - sql-runner: generic SQL executor for DDL jobs
  - notes: CRUD router (/add, /update, /delete)
  - notes-list: SELECT all notes
  - init.tf: create TABLE + INDEX on apply
2026-03-09 09:51:56 +04:00
“Naeel” 0066d9c0f9 refactor: pg-query — ресурсы вынесены в pg-query.tf, main.tf только провайдеры 2026-03-09 09:11:31 +04:00
“Naeel” 237a636097 refactor: pg-query — code/ исходники, dist/ zip, archive_file datasource 2026-03-09 09:07:57 +04:00
“Naeel” e8497644e6 refactor: hello-node — zip-файлы в dist/, исходники остаются в code/ 2026-03-09 09:03:00 +04:00
“Naeel” 53e82ea47b fix: pg-query — filesha256(handler.py) вместо filemd5(handler.zip), версия провайдера 0.1.7 2026-03-09 08:56:19 +04:00
“Naeel” 976fcadc36 fix: API validation + Terraform plan-time validators
API (operator v0.1.13):
- functions.go: добавлена валидация entrypoint (не пустой) и
  memory_mb (1-4096). Фиксирует БАГ-1/2/4 из негативных тестов.
- triggers.go: добавлена валидация type (только 'http'/'cron').
  Фиксирует БАГ-3 (неверное сообщение об ошибке).

Провайдер (v0.1.7):
- Добавлен пакет terraform-plugin-framework-validators v0.19.0
- function_resource: runtime OneOf, memory_mb 1-4096, timeout_sec 1-900
- trigger_resource: type OneOf(http, cron)
- job_resource: run_id AtLeast(0)
- examples/main.tf: обновлена версия до ~> 0.1.7

doc/errors/log.md: задокументированы исправления и результаты повторных тестов
2026-03-09 08:52:13 +04:00
“Naeel” c0fe63b3d3 fix: откат 409-fallback из клиента провайдера (provider v0.1.6 не опубликован с изменениями)
- terraform/provider/internal/client/client.go: откат 409 fallback для
  CreateFunction, CreateTrigger, CreateJob — import не нужен в данном провайдере,
  409 должен быть ошибкой
- examples/hello-node/main.tf: версия провайдера ~> 0.1.6 (опубликована
  без изменений клиента — только исходная версия кода)
- examples/hello-node/job.tf: run_id=9, numbers=[100,200,300]

Тесты пройдены: plan(no-changes), state-rm+apply, code update, run_id++, destroy+apply
2026-03-09 08:17:17 +04:00
“Naeel” f8fe3835a4 fix: уникальный image tag per build вместо :latest (operator v0.1.12)
- internal/builder/builder.go: ImageRef() теперь использует sha256(s3Key)[:12]
  как тег образа вместо :latest
  Пример: naeel/sless-default-hello-http:d431de61e2c9
- controllers/function_controller.go: убран хак с restartedAt аннотацией —
  больше не нужен, новый тег = kubelet всегда пул-ит новый образ
- deployments/k8s/operator.yaml: обновлён до v0.1.12
- doc/progress.md: версия обновлена

Преимущества уникального тега:
- imagePullPolicy: IfNotPresent работает корректно
- Rollback к предыдущей версии возможен (теги не перезаписываются)
- Нет race condition при параллельных сборках
2026-03-08 21:05:35 +04:00
“Naeel” 79719e078d fix: rollout restart после kaniko rebuild (operator v0.1.11)
- controllers/function_controller.go: в ensureDeployment добавить аннотацию
  kubectl.kubernetes.io/restartedAt = fn.Status.LastBuiltAt при обновлении
  существующего Deployment — принудительный rolling restart после каждой сборки
- deployments/k8s/operator.yaml: образ обновлён до v0.1.11
- doc/errors/log.md: задокументирована причина и решение
- doc/decisions/log.md: добавлено правило restartedAt + :latest
- doc/progress.md: версия обновлена до v0.1.11

Причина бага: imagePullPolicy=IfNotPresent + :latest — kubelet кешировал
старый образ, pod не рестартовал после успешного kaniko build.
2026-03-08 20:55:47 +04:00
“Naeel” df540e6b81 docs: обновление doc/ по итогам сессии 2026-03-08 2026-03-08 20:36:16 +04:00
“Naeel” 99ef17d62f fix: убрать UseStateForUnknown с image_ref, provider v0.1.5 2026-03-08 20:34:00 +04:00
“Naeel” b69f795eb1 config: REGISTRY_HOST переключён с Harbor на DockerHub (naeel) 2026-03-08 20:18:59 +04:00
“Naeel” 461ac096f9 fix: FunctionJob зависал в Running — добавлен RequeueAfter polling 2026-03-08 12:13:15 +04:00
“Naeel” e6abc490cd fix: imagePullSecrets, SLESS_ENTRYPOINT, registry secret propagation
- function_controller: добавить RegistrySecret + OperatorNamespace, копировать
  sless-registry-auth в sless-fn-<ns>, выставлять imagePullSecrets в Deployment,
  также обновлять imagePullSecrets при reconcile
- functionjob_controller: fnEnvVars включает SLESS_ENTRYPOINT, runner читает его
- server.js + server.py: читать SLESS_ENTRYPOINT вместо hardcoded handler.js/py
- rbac.yaml: добавить права на secrets
- operator.yaml: v0.1.8
- main.go: передать RegistrySecret + OperatorNamespace в FunctionReconciler
2026-03-08 11:15:48 +04:00
“Naeel” d67b9745a8 feat: trigger.enabled + job.run_id lifecycle control (operator v0.1.6, provider v0.1.4)
- TriggerSpec.Enabled bool (default=true): enabled=false масштабирует Deployment до 0
- FunctionJobSpec.RunID int64 (default=0): run_id=0 = skip, >0 = run
- API: PATCH /v1/namespaces/{ns}/triggers/{name} (UpdateTrigger)
- Provider: enabled attribute (Optional, Computed, in-place update)
- Provider: run_id attribute (Optional, Computed, default=0, RequiresReplace)
- operator image: naeel/sless-operator:v0.1.6
- provider: terra.k8c.ru/naeel/sless v0.1.4
2026-03-08 10:10:32 +04:00
“Naeel” 8fb0ef5ea1 ops: switch registry from DockerHub to Harbor (pearlharbor.registryk8s.services.ngcloud.ru/sless) 2026-03-08 09:28:18 +04:00
“Naeel” 4f0b8fb486 examples/hello-node: two different functions (http greeting, job sum) 2026-03-08 09:22:38 +04:00
“Naeel” 00cd3d181b examples/hello-node: move handler to code/, split http.tf and job.tf 2026-03-08 09:14:34 +04:00
“Naeel” 7f5a881c8a examples: switch hello-node to sless_job (one-shot run) 2026-03-07 23:05:09 +04:00
“Naeel” 2ebff6c05a examples: auto-zip via hashicorp/archive, remove manual zip step 2026-03-07 18:46:23 +04:00
“Naeel” d6212d1cee doc: обновлена документация — FunctionJob, proxy /fn/, operator v0.1.5, provider v0.1.3 2026-03-07 18:40:36 +04:00
“Naeel” 2ee9cae6d2 feat: proxy /fn/{namespace}/{name} — обход wildcard DNS
Проблема: wildcard DNS *.fn.kube5s.ru недоступен.
Решение: прокси через sless-api.kube5s.ru/fn/{ns}/{name}.

- handler/invoke.go: прокси к Service функции внутри кластера
- router.go: /fn/ без auth токена, /v1/ с auth (gorilla Use())
- config.go: поле ExternalURL (EXTERNAL_URL env)
- trigger_controller.go: если ExternalURL задан — URL = ExternalURL/fn/{ns}/{fn}
  иначе fallback: Ingress + поддомен (прежнее поведение)
- operator.yaml: EXTERNAL_URL=https://sless-api.kube5s.ru, image v0.1.5

Оператор v0.1.5 задеплоен.
E2E: curl https://sless-api.kube5s.ru/fn/default/hello-node → {"message":"Hello, Naeel! (nodejs20)"}
2026-03-07 18:36:03 +04:00
“Naeel” a6bc67de05 feat: configurable timeouts for sless_function and sless_job
- sless_function: build_timeout_sec (optional, default 300s = 5 min)
- sless_job: wait_timeout_sec (optional, default 600s = 10 min)
- provider v0.1.3 published to terra.k8c.ru
2026-03-07 17:49:07 +04:00
“Naeel” f405596d42 feat: sless_job terraform resource + jobs REST API
Operator (v0.1.4):
- internal/api/handler/jobs.go: CreateJob/GetJob/DeleteJob handlers
- internal/api/router.go: POST/GET/DELETE /v1/namespaces/{ns}/jobs/{name}

Terraform provider (v0.1.2):
- client.go: JobRequest/JobResponse + CreateJob/GetJob/DeleteJob/WaitJobDone
- resources/job_resource.go: sless_job resource (Create blocks until Succeeded/Failed)
- provider.go: register NewJobResource

Deploy:
- operator:v0.1.4 deployed and running in cluster
- provider v0.1.2 published to terra.k8c.ru
2026-03-07 17:43:26 +04:00
“Naeel” 22685a72a9 feat: add FunctionJob CRD for one-shot function runs
- api/v1alpha1/job_types.go: new CRD FunctionJob (Pending/Running/Succeeded/Failed)
- controllers/functionjob_controller.go: reconciler creates k8s Job from FunctionRef + EventJSON
- zz_generated.deepcopy.go: DeepCopy methods for FunctionJob types
- config/crd/bases: generated CRD YAML, applied to cluster
- main.go: register FunctionJobReconciler
- rbac.yaml: add functionjobs permissions
- operator.yaml: v0.1.2 -> v0.1.3
- operator:v0.1.3 deployed and running in cluster
2026-03-07 17:20:21 +04:00
“Naeel” d6dc676dfd doc: document operator deploy, nodejs20 runtime, Dockerfile fixes, go.mod fix 2026-03-07 17:02:28 +04:00
“Naeel” 22c7e92590 feat: add nodejs20 runtime
- runtimes/nodejs20/server.js: HTTP wrapper, exports.handle(event)
- runtimes/nodejs20/Dockerfile: node:20-alpine base image
- naeel/sless-runtime-nodejs20:v0.1.0 pushed to DockerHub
- upload.go: nodejs20 in runtimeBaseImage(), package.json → npm install
- upload.go: python3.11 now uses v0.1.0 tag (no more latest)
- operator v0.1.2 deployed in cluster
- E2E: hello-node-default.fn.kube5s.ru → {"message":"Hello, Naeel! (nodejs20)"}
2026-03-07 17:00:29 +04:00
“Naeel” 97f38c1f72 chore: switch pg-query example to remote endpoint sless-api.kube5s.ru 2026-03-07 16:50:05 +04:00
“Naeel” b338192cbf chore: go mod tidy — fix direct/indirect annotations 2026-03-07 16:31:54 +04:00
“Naeel” 3fdf342115 feat: deploy operator in-cluster v0.1.1
- Dockerfile: fix golang 1.23→1.25, add internal/ + migrations/ to image
- deployments/k8s/operator.yaml: ConfigMap + Secret + Deployment + Service + Ingress
- Ingress: sless-api.kube5s.ru, TLS via cert-manager letsencrypt-prod
- naeel/sless-operator:v0.1.1 — running in namespace sless
- E2E: https://sless-api.kube5s.ru → HTTP/2 200
2026-03-07 16:30:14 +04:00
“Naeel” 0856fbc454 doc: decisions — scale-to-zero v2, replicas todo, postgres optional 2026-03-07 11:10:37 +04:00
“Naeel” 0268ef0523 fix: multiple fixes + pg-query e2e passed
upload.go:
- Update → Patch (MergeFrom) to avoid resourceVersion conflict when operator
  modifies Function between Get() and Update()

terraform provider v0.1.1:
- trigger_resource.go: trToModel returns StringNull() for empty schedule
  (fixes 'provider produced inconsistent result' for http triggers)
- main.go: bump version to 0.1.1

examples/pg-query:
- handler.py: fix column name started_at → created_at (matches migrations/001)
- main.tf: pin provider to ~> 0.1.1

.gitignore: add terraform state, lock, .terraform/, handler.zip

doc/errors/log.md: documented all 5 errors from this session:
- resourceVersion conflict → use Patch
- terraform inconsistent result for schedule → StringNull
- terraform import not implemented → delete+recreate workaround
- wrong column name → check migrations before writing handlers
- Deployment not restarting after image rebuild → rollout restart / TODO: restartedAt annotation

doc/progress.md: terraform apply e2e 
2026-03-07 10:52:29 +04:00
“Naeel” 4e59d79884 feat: add pg-query example + upload.go supports requirements.txt
- examples/pg-query/handler.py: Python function querying PostgreSQL invocations table
- examples/pg-query/requirements.txt: psycopg2-binary==2.9.9
- examples/pg-query/main.tf: terraform config for sless_function + sless_trigger
- internal/api/handler/upload.go: generateDockerfile() now accepts hasRequirements bool
  - scans zip for requirements.txt at upload time
  - adds RUN pip install --no-cache-dir to Dockerfile when requirements.txt present
- doc/progress.md: updated status for pg-query e2e task
2026-03-07 10:30:50 +04:00
“Naeel” dc7542148e docs: обновить всю документацию по результатам сессии 2026-03-07
- infrastructure/overview.md: исправить S3 URL, Container Registry (DockerHub),
  namespace sless, Terraform Registry terra.k8c.ru
- errors/log.md: добавить 3 ошибки — kaniko S3 scheme, бесконечный цикл сборки,
  docker credStore desktop.exe
- decisions/log.md: добавить решения — DockerHub, terraform провайдер в той же
  репе, WaitReady паттерн, code_hash для версионирования кода
- api/design.md: обновить базовый URL, добавить upload endpoint с описанием
  процесса, исправить runtime список
2026-03-07 10:26:13 +04:00
“Naeel” 19c9e016a8 feat: Terraform провайдер sless v0.1.0 + обновление doc/progress.md
Независимый Go-модуль terraform/provider/ для управления функциями через REST API.

Ресурсы:
- sless_function: CRUD + upload zip + WaitReady (kaniko build 5 мин)
- sless_trigger: CRUD (http/cron)

Компоненты:
- internal/client/client.go   — HTTP-клиент, готов к переносу в nubes
- internal/provider/provider.go — паттерн идентичен nubes NubesProvider
- internal/resources/           — function_resource.go, trigger_resource.go
- hack/build-and-publish.sh    — GPG-подпись + mc upload в S3

Опубликовано: terra.k8c.ru/naeel/sless v0.1.0
Проверено: terraform init успешен
2026-03-07 10:23:52 +04:00
“Naeel” 4115e0f172 fix: бесконечный цикл сборки — idempotency guard через last-built-s3key аннотацию
Проблема: upload handler сбрасывал phase в Pending ПОСЛЕ того как
контроллер уже выставил Building → бесконечный цикл (94 job'а).

Решение:
- controllers/function_controller.go: startBuild сначала ставит аннотацию
  last-built-s3key = spec.S3Key (idempotency guard), потом status.
  Reconcile стартует сборку только если spec.S3Key != last-built-s3key.
- handler/upload.go: убран Status().Update() — контроллер сам управляет фазой.
- builder.go: IsAlreadyExists при создании job не ошибка (parallel reconcile).
2026-03-07 09:50:24 +04:00
“Naeel” 8bae4ca69c fix: kaniko S3 endpoint — добавить https:// схему и S3_FORCE_PATH_STYLE=true
Kaniko AWS SDK требует полный URL с протоколом.
Ceph не поддерживает virtual-hosted style — нужен path style.
2026-03-07 09:39:53 +04:00
“Naeel” 74458f848d feat: upload endpoint POST /v1/namespaces/{ns}/functions/{name}/upload
- s3/client.go: Bucket() accessor + UploadContext() для tar.gz build context
- handler/upload.go: принимает zip, генерирует Dockerfile (FROM naeel/sless-runtime-{runtime}),
  перепаковывает в tar.gz, загружает в S3, обновляет Function CRD → kaniko запускается
- router.go: маршрут POST .../upload зарегистрирован
2026-03-07 09:36:32 +04:00
“Naeel” c61e822308 feat: python3.11 base runtime image + DockerHub registry support
- runtimes/python3.11/: Dockerfile + server.py (HTTP wrapper на :8080)
- builder.go: RegistrySecret field, volume mount docker-creds для kaniko, убран --skip-tls-verify
- config.go: добавлен RegistrySecret (env REGISTRY_SECRET, default sless-registry-auth)
- main.go: передаём RegistrySecret в builder.Config
- hack/create-registry-secret.sh: создаёт k8s Secret из DockerHub токена

Образ наружу: naeel/sless-runtime-python3.11:latest (DockerHub)
K8s secret: sless-registry-auth в namespace sless
2026-03-07 09:32:59 +04:00
“Naeel” 33ee17729f docs: обновить progress.md по фактическому состоянию
- отражён реальный статус всех компонентов кода
- отражено состояние кластера (CRD, RBAC, postgres)
- добавлен список что ещё не сделано
- добавлены deployments/k8s/rbac.yaml, postgres.yaml
2026-03-07 08:57:54 +04:00
“Naeel” 43e7d0ea48 feat: REST API (gorilla/mux + slog) + trigger controller + main.go wiring
- controllers/trigger_controller.go: полная реализация, HTTP->Ingress+Service, Cron->CronJob
- internal/config: добавлены IngressHost, APIToken
- internal/api/router.go: gorilla/mux роутер /v1/namespaces/{ns}/...
- internal/api/handler/: functions, triggers, invocations, base handler с slog
- internal/api/middleware/: auth (Bearer token) + logging (slog)
- main.go: запуск operator + HTTP сервера параллельно
- go.sum: добавлен gorilla/mux v1.8.1
2026-03-07 08:46:10 +04:00
“Naeel” 5f25f61948 chore: ignore .s3cfg* files 2026-03-07 08:28:33 +04:00
“Naeel” a5154c21de chore: remove .s3cfg_registry from repo 2026-03-07 08:28:19 +04:00
“Naeel” d9f5ab56bb feat: define Function and Trigger CRD types
Function:
  - runtime (go1.21/python3.11/nodejs20)
  - entrypoint, s3Bucket, s3Key
  - memoryMB (default 128), timeoutSec (default 30)
  - env map
  - status: phase (Pending/Building/Ready/Failed), imageRef, conditions

Trigger:
  - type (http/cron), functionRef
  - schedule (cron only)
  - preWarmSeconds (default 300) - pre-warm before cron run
  - status: active, url, lastScheduleTime

printcolumns for kubectl get output
2026-03-07 08:24:33 +04:00
“Naeel” 3e10ac1d63 feat: operator-sdk scaffold with Function and Trigger CRDs
- operator-sdk init --domain kube5s.ru
- CRD: Function, Trigger (sless.kube5s.ru)
- controllers: FunctionReconciler, TriggerReconciler (stubs)
- internal/ structure: api, storage/postgres, storage/s3, builder, config
- fix: controller-tools bumped v0.11.1→v0.14.0 (Go 1.23 compat)
2026-03-07 08:16:39 +04:00
“Naeel” 205aecae50 chore: add comments rules to copilot instructions 2026-03-06 09:56:02 +04:00
“Naeel” 3ffc8da373 chore: add copilot instructions 2026-03-06 09:53:10 +04:00
“Naeel” b29b6c3d10 docs: initial project documentation
- architecture overview and stack
- project structure and development order
- API design (endpoints, models, runtimes)
- infrastructure overview (k8s cluster, S3, registry)
- decisions log with rationale
- progress tracker v1/v2
2026-03-06 09:51:01 +04:00