- Определена архитектура managed IoT service
- Согласовано решение: RabbitMQ (MVP), потом Kafka; EMQX + CRD контроллер
- Создан подробный план для Sonnet (doc/iot-mvp-plan.md)
- Добавлено правило в copilot-instructions: лог мышления в doc/thinking/ по датам
- Полный ход рассуждений в doc/thinking/2026-04-04.md
Ключевые решения:
- IoT код в iot/ (легко вынести потом)
- CRD IoTDevice + контроллер (как все остальное в sless)
- EMQX HTTP Auth Backend для динамической аутентификации устройств
- Архитектура broker-agnostic (легко переключить на Kafka)
- Terraform: расширяем текущий provider (sless_iot_device ресурс)
Bug 1: services.go — k8s IsInvalid error (CRD enum validation) маппился в 500.
Теперь errors.IsInvalid() → 400 Bad Request (invalid service spec).
Bug 2: service_controller.go buildServiceDeployment не передавал env SLESS_ENTRYPOINT
в под. Добавлен в envVars из svc.Spec.Entrypoint. Без него server.py использовал
fallback handler.handle и не замечал неверный entrypoint.
operator_failure_test.sh 12B-2: обновлён под новое правильное поведение —
create ruby3.0 → 400 (не 201). Старый 201-путь сохранён как warn для совместимости.
БАГ 1: ensureServiceDeployment UPDATE блок теперь обновляет Containers[0].Resources
→ memory_mb через PUT применяется к k8s Deployment
БАГ 2: ensureServiceDeployment возвращает RequeueAfter: 60s вместо Result{}
→ ручное удаление Deployment пересоздаётся контроллером в течение 60s
lifecycle-тест: 47/47 PASS (ранее 44/44 с 2 known bugs)
Баги:
1. memory_mb через PUT не обновляет k8s Deployment Resources → controllers/service_controller.go:~241
2. нет self-healing при ручном удалении Deployment → нет RequeueAfter / cross-namespace watch
- FunctionJobSpec: убран FunctionRef, добавлены Runtime/Entrypoint/Env/S3Key/MemoryMB/TimeoutSec
- FunctionJobStatus: новый ImageRef, новая фаза Building
- FunctionJobReconciler: Building фаза (kaniko), убрана зависимость от Function CRD
Builder+OperatorNamespace как поля struct; аннотация sless.kube5s.ru/build-job guard
- main.go: Builder+OperatorNamespace переданы в FunctionJobReconciler
- jobs.go handler: jobRequest/jobResponse без FunctionRef; новый UploadJobCode handler
- router.go: /jobs/{name}/upload маршрут
- client.go: JobRequest/JobResponse обновлены; UploadJobCode; uploadCodeToURL общий хелпер
- job_resource.go: полная переработка — источник/среда встроены в JobModel, ModifyPlan,
Create с upload, wait_timeout_sec=900 по умолчанию (kaniko + выполнение)
- examples/POSTGRES/functions.tf: раскомментирован, sless_function удалён,
sless_job самодостаточен (inline source_dir/runtime/entrypoint/env_vars)
- 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.
- 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 контроллеров, примеры кода, ТЗ, вопросы
- 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 из трекинга