diff --git a/doc/thinking/2026-04-26-namespace-manager-step37.md b/doc/thinking/2026-04-26-namespace-manager-step37.md new file mode 100644 index 00000000..482cf0e8 --- /dev/null +++ b/doc/thinking/2026-04-26-namespace-manager-step37.md @@ -0,0 +1,19 @@ +# 2026-04-26 — NamespaceManager rewrite, step 37 + +## Цель шага + +Убрать дублирование startup manager flow в трёх namespace watcher-ах. + +## Что меняем + +1. В `utils` добавляем helper `NewWatcherNamespaceManager()`. +2. Helper: + - создаёт `NamespaceManager` + - подписывает subscriber-ов + - выполняет `BootstrapAndDispatch()` +3. `buildermgr`, `router`, `executor/multitenant` используют новый helper. + +## Что НЕ меняем + +- не меняем semantics dispatch; +- не меняем runtime cleanup policy. \ No newline at end of file diff --git a/pkg/buildermgr/ns_watcher.go b/pkg/buildermgr/ns_watcher.go index d0f22cfd..a55ab8f2 100644 --- a/pkg/buildermgr/ns_watcher.go +++ b/pkg/buildermgr/ns_watcher.go @@ -30,9 +30,8 @@ func StartNSWatcher( pkgw *packageWatcher, mgr manager.Interface, ) { - nsManager := utils.NewNamespaceManager() - nsManager.Subscribe(NewNamespaceSubscriber(envw, pkgw, mgr)) - if _, err := nsManager.BootstrapAndDispatch(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC()); err != nil { + nsManager, err := utils.NewWatcherNamespaceManager(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC(), NewNamespaceSubscriber(envw, pkgw, mgr)) + if err != nil { logger.Error("buildermgr.NSWatcher: BootstrapAndDispatch failed", zap.Error(err)) } diff --git a/pkg/executor/multitenant/ns_watcher.go b/pkg/executor/multitenant/ns_watcher.go index 2a0b4f90..2eccfe1f 100644 --- a/pkg/executor/multitenant/ns_watcher.go +++ b/pkg/executor/multitenant/ns_watcher.go @@ -92,9 +92,8 @@ func StartNSWatcher( executorTypes map[fv1.ExecutorType]executortype.ExecutorType, mgr manager.Interface, ) { - nsManager := utils.NewNamespaceManager() - nsManager.Subscribe(NewNamespaceSubscriber(logger, kubernetesClient, executorTypes, mgr)) - if _, err := nsManager.BootstrapAndDispatch(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC()); err != nil { + nsManager, err := utils.NewWatcherNamespaceManager(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC(), NewNamespaceSubscriber(logger, kubernetesClient, executorTypes, mgr)) + if err != nil { logger.Error("multitenant.NSWatcher: BootstrapAndDispatch failed", zap.Error(err)) } diff --git a/pkg/router/ns_watcher.go b/pkg/router/ns_watcher.go index 94506b15..787357a3 100644 --- a/pkg/router/ns_watcher.go +++ b/pkg/router/ns_watcher.go @@ -30,9 +30,8 @@ func StartNSWatcher( ts *HTTPTriggerSet, mgr manager.Interface, ) { - nsManager := utils.NewNamespaceManager() - nsManager.Subscribe(NewNamespaceSubscriber(ts, mgr)) - if _, err := nsManager.BootstrapAndDispatch(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC()); err != nil { + nsManager, err := utils.NewWatcherNamespaceManager(ctx, utils.DefaultNSResolver().Snapshot(), utils.NamespaceSourceEnv, time.Now().UTC(), NewNamespaceSubscriber(ts, mgr)) + if err != nil { logger.Error("router.NSWatcher: BootstrapAndDispatch failed", zap.Error(err)) } diff --git a/pkg/utils/namespace_manager.go b/pkg/utils/namespace_manager.go index cd960865..4fa70ebb 100644 --- a/pkg/utils/namespace_manager.go +++ b/pkg/utils/namespace_manager.go @@ -88,6 +88,18 @@ func NewBootstrappedNamespaceManager(resolver *NamespaceResolver, source Namespa return manager } +func NewWatcherNamespaceManager(ctx context.Context, namespaces []string, source NamespaceSource, observedAt time.Time, subscribers ...NamespaceSubscriber) (NamespaceManager, error) { + manager := NewNamespaceManager() + for _, subscriber := range subscribers { + if subscriber == nil { + continue + } + manager.Subscribe(subscriber) + } + _, err := manager.BootstrapAndDispatch(ctx, namespaces, source, observedAt) + return manager, err +} + func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) { m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/utils/namespace_manager_test.go b/pkg/utils/namespace_manager_test.go index 0727cc19..975ffec5 100644 --- a/pkg/utils/namespace_manager_test.go +++ b/pkg/utils/namespace_manager_test.go @@ -270,6 +270,21 @@ func TestNewBootstrappedNamespaceManager(t *testing.T) { } } +func TestNewWatcherNamespaceManager(t *testing.T) { + router := &testNamespaceSubscriber{name: "router"} + builder := &testNamespaceSubscriber{name: "buildermgr"} + manager, err := NewWatcherNamespaceManager(context.Background(), []string{"tenant-b", "tenant-a"}, NamespaceSourceEnv, time.Now().UTC(), router, builder) + if err != nil { + t.Fatalf("expected watcher manager bootstrap success: %v", err) + } + if !reflect.DeepEqual([]string{"tenant-a", "tenant-b"}, manager.Snapshot()) { + t.Fatalf("expected watcher manager snapshot to match bootstrapped namespaces") + } + if router.addCalls != 2 || builder.addCalls != 2 { + t.Fatalf("expected all subscribers to receive bootstrap dispatch calls") + } +} + func TestNamespaceManagerDispatchAdd(t *testing.T) { manager := NewNamespaceManager() manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})