From e2dff8db093db4777ec64d3626a8d3e9703e426d Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 26 Apr 2026 09:06:44 +0300 Subject: [PATCH] doc: add detailed layer1 multi-tenant fix report --- .../2026-04-26-layer1-pass-detailed.md | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 doc/thinking/2026-04-26-layer1-pass-detailed.md diff --git a/doc/thinking/2026-04-26-layer1-pass-detailed.md b/doc/thinking/2026-04-26-layer1-pass-detailed.md new file mode 100644 index 00000000..c10d3abf --- /dev/null +++ b/doc/thinking/2026-04-26-layer1-pass-detailed.md @@ -0,0 +1,248 @@ +# 2026-04-26 - Layer1 multi-tenant NSWatcher: полный разбор до 5/5 PASS + +## Цель + +Довести `test_layer1.sh` до `PASS=5 FAIL=0` для сценария: + +1. создаётся новый namespace +2. namespace получает label `fission.io/managed=true` +3. Fission без рестарта подхватывает namespace +4. в namespace создаются `Environment`, `Function`, `HTTPTrigger` +5. функция успешно вызывается через router + +Ключевое требование: всё должно происходить без rolling restart Fission-компонентов. + +## Исходный симптом + +Первый устойчивый симптом был таким: + +- `test_layer1.sh` стабильно доходил до `4/5` +- шаг вызова функции падал +- в user namespace наблюдалось: + - `FailedCreate` + - `serviceaccount "fission-fetcher" not found` + +Это означало, что poolmgr deployment для environment уже создаётся, но pod не может стартовать без `fission-fetcher` ServiceAccount. + +## Что уже было исправлено до RBAC-этапа + +Кодовая часть hot-registration была уже внедрена ранее: + +- `pkg/utils/serviceaccount.go` + - добавлена `EnsureNamespaceSA(...)` +- `pkg/executor/multitenant/ns_watcher.go` + - при регистрации нового namespace вызывается `EnsureNamespaceSA(...)` +- образ `naeel/fission-bundle:v1.22.0-multi-ns-8` уже был собран и задеплоен + +То есть логика в коде уже существовала; сбой был не в отсутствии вызова, а в невозможности выполнить его успешно в кластере. + +## Диагностика 1: executor не может создать ServiceAccount/Role/RoleBinding + +Была проведена проверка прав service account `fission-executor`. + +Подтверждено: + +- код `EnsureNamespaceSA` вызывается +- `ns_watcher` регистрирует namespace +- executor не имеет достаточных RBAC-прав для provisioning ресурсов в новом namespace + +Первый явный пробел: + +- отсутствовали права на: + - `serviceaccounts` + - `roles` + - `rolebindings` + +После начального RBAC fix было видно, что `ServiceAccount/fission-fetcher` уже создаётся, но этого оказалось недостаточно. + +## Диагностика 2: initial RBAC fix оказался неполным + +После расширения прав на `serviceaccounts/roles/rolebindings` тест перестал падать на отсутствии SA, но при детальной диагностике выяснилось, что `EnsureNamespaceSA` всё ещё не может полностью создать `Role` для fetcher. + +Ключевой лог executor: + +```text +error while creating role for sa fission-fetcher in namespace diag-ns-82702 +... is attempting to grant RBAC permissions not currently held: +{APIGroups:[""], Resources:["events"], Verbs:["create"]} +``` + +И дополнительный лог перед этим: + +```text +localsubjectaccessreviews.authorization.k8s.io is forbidden +``` + +### Что это означает + +Функция `setupSAAndRoleBindings()` делает две важные вещи: + +1. пытается проверить уже существующие права через `LocalSubjectAccessReview` +2. если прав нет, создаёт `Role` с нужными permission-ами + +Следовательно executor должен иметь не только право создавать `Role/RoleBinding`, но и: + +- `authorization.k8s.io/localsubjectaccessreviews:create` +- все permission-ы, которые он пытается делегировать через создаваемую `Role` + +В нашем случае fetcher получает право: + +- `events:create` + +По правилам Kubernetes нельзя создать `Role`, выдающую право, которого нет у самого вызывающего субъекта. Поэтому executor должен был сам иметь `events:create`. + +### Реальный root cause на этом этапе + +`fission-executor` не имел: + +- `events.create` +- `localsubjectaccessreviews.create` + +Из-за этого: + +- `ServiceAccount` создавался +- но `Role` и `RoleBinding` создавались не полностью или не создавались вовсе +- downstream specialization ломалась + +## Исправление 1: полный executor RBAC для dynamic SA provisioning + +В `deploy/multitenant/rbac.yaml` был добавлен и затем расширен `ClusterRole`: + +- `fission-executor-sa-provisioner` + +Итоговый набор прав для него: + +- core: + - `serviceaccounts`: `get`, `list`, `watch`, `create`, `update`, `patch` + - `events`: `create` +- `authorization.k8s.io`: + - `localsubjectaccessreviews`: `create` +- `rbac.authorization.k8s.io`: + - `roles`: `get`, `list`, `watch`, `create`, `update`, `patch` + - `rolebindings`: `get`, `list`, `watch`, `create`, `update`, `patch` + +После применения этого манифеста было подтверждено: + +- `kubectl auth can-i create events --as=system:serviceaccount:fission:fission-executor` -> `yes` +- `kubectl auth can-i create localsubjectaccessreviews.authorization.k8s.io --as=system:serviceaccount:fission:fission-executor` -> `yes` + +И в новом test namespace автоматически появлялись: + +- `ServiceAccount/fission-fetcher` +- `Role/fission-fetcher-role-*` +- `RoleBinding/fission-fetcher-rolebinding-*` + +## Изменение симптома после executor-fix + +После полного executor RBAC fix шаг 5 перестал падать с `500` timeout от executor. + +Новый симптом: + +- постоянный `HTTP 404` +- router не видел route/function в новом namespace + +Это был важный индикатор того, что executor-path уже работает лучше, а оставшаяся проблема находится в router-path. + +## Диагностика 3: router NSWatcher не мог watch/list namespaces + +Лог router показал прямую ошибку: + +```text +failed to list *v1.Namespace: namespaces is forbidden: +User "system:serviceaccount:fission:fission-router" cannot list resource +"namespaces" at the cluster scope +``` + +При этом код router уже содержал dynamic namespace watcher: + +- `pkg/router/ns_watcher.go` + +То есть логика была, но RBAC для `fission-router` отсутствовал. + +### Реальный root cause на этом этапе + +`fission-router` не имел cluster-scope прав: + +- `namespaces:list` +- `namespaces:watch` + +Из-за этого: + +- router не подхватывал новые labeled namespaces +- `HTTPTriggerSet.AddNamespace(...)` не вызывался +- HTTP trigger не попадал в router runtime map +- вызов функции возвращал `404` + +## Исправление 2: router RBAC для NSWatcher + +В тот же `deploy/multitenant/rbac.yaml` добавлены: + +- `ClusterRole/fission-router-ns-watcher` +- `ClusterRoleBinding/fission-router-ns-watcher` + +С правами: + +- core `namespaces`: `list`, `watch` + +После применения подтверждено: + +- `kubectl auth can-i list namespaces --as=system:serviceaccount:fission:fission-router` -> `yes` +- `kubectl auth can-i watch namespaces --as=system:serviceaccount:fission:fission-router` -> `yes` + +## Финальная проверка + +После обоих RBAC fixes повторный запуск `test_layer1.sh` дал: + +```text +ИТОГ: PASS=5 FAIL=0 +``` + +На шаге 5 функция успешно ответила: + +```text +HTTP 200 - hello from layer1 +``` + +## Что именно оказалось правдой по итогу + +Итоговая проблема состояла из двух последовательных RBAC-дырок: + +1. executor не мог полностью provision-ить `fission-fetcher` в динамическом namespace +2. router не мог подхватить новый namespace из-за отсутствия namespace watch/list + +То есть код hot-registration в целом был правильный, но runtime contract в Kubernetes RBAC был реализован не полностью. + +## Итоговые изменения + +### Код и манифесты + +- `deploy/multitenant/rbac.yaml` + - executor namespace watch + - executor SA provisioning RBAC + - router namespace watch RBAC + +### Документация + +- `doc/progress.md` +- `doc/thinking/2026-04-26-rbac-fix.md` +- `doc/thinking/2026-04-26-layer1-pass-detailed.md` + +### Коммиты по ходу исправления + +- `161de70` - `multi-tenant: EnsureNamespaceSA + ns_watcher SA provisioning (v8)` +- `8ccc9fb` - первый RBAC commit +- `f617913` - полный executor RBAC fix для fetcher role provisioning +- `7faaa9d` - router namespace watch RBAC + +## Практический вывод + +Для hot namespace onboarding в Fission недостаточно просто добавить informer-ы в коде. + +Нужно обеспечить весь runtime contract: + +- executor видит namespace +- executor может provision-ить service accounts и RBAC в tenant namespace +- executor может делегировать все требуемые permission-ы +- router видит namespace и подписывается на triggers/functions в нём + +Если хотя бы одно из этих звеньев отсутствует, поведение выглядит как "код вроде есть, но dynamic namespace не работает".