# 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 не работает".