doLogin() now calls POST /v1/namespaces/{ns}/ensure before apiListDevices().
Prevents namespace not found error when user logs in for the first time
with a new token (test mode or real JWT).
Image: v0.1.62
- Removed 📋 Скопировать всё button and Подключение физического устройства block
- Added MQTTX Web link (https://mqttx.app/web-client) with brief instructions:
Host, Port 443, Protocol wss, Path /mqtt — copy username/password above
- Warning: check port is 443 not 8084/1883 if connection fails
- Updated help step 1: removed mention of copyAll, added port 443 note
- Removed unused copyAll() function
Image: v0.1.61
authTestMode=true in middleware/auth.go:
- any non-whitespace, non-JWT string is accepted as Bearer token
- string is used as sub for namespace derivation (SHA256)
- JWT validation still runs for actual JWT strings (xxx.yyy.zzz)
- revert to strict mode: authTestMode = false
iot-console.html:
- namespaceFromToken: plain tokens use the string itself as sub
- login form: updated placeholder + hint explaining test mode
Image: v0.1.60
- Форма входа: только токен, без полей API адреса и MQTT broker
- Адреса zardcoded: https://sless.kube5s.ru и wss://iot.kube5s.ru/mqtt
- Страница устройства: блок «Как это работает» — 5 шагов с инструкцией
- operator.yaml: v0.1.55 → v0.1.56
- Поле Namespace удалено из формы входа
- namespace вычисляется из токена: SHA256(sub) → sless-{hex}
- navbar: убран badge с ns
- Телеметрия: убрана техническая подсказка про namespace
- Пользователь не видит и не вводит namespace нигде
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 для совместимости.
- 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)
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
- 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.
- 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
- 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)
- 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
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