From 49be1db3a00dff59283396cc56f8d3299cc93e0e Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 26 Apr 2026 10:50:28 +0300 Subject: [PATCH] layer1: share namespace watcher event handlers --- .../2026-04-26-namespace-manager-step42.md | 17 ++++++++++++++ pkg/buildermgr/ns_watcher.go | 19 +-------------- pkg/executor/multitenant/ns_watcher.go | 22 +----------------- pkg/router/ns_watcher.go | 19 +-------------- pkg/utils/namespace_manager.go | 22 ++++++++++++++++++ pkg/utils/namespace_manager_test.go | 23 +++++++++++++++++++ 6 files changed, 65 insertions(+), 57 deletions(-) create mode 100644 doc/thinking/2026-04-26-namespace-manager-step42.md diff --git a/doc/thinking/2026-04-26-namespace-manager-step42.md b/doc/thinking/2026-04-26-namespace-manager-step42.md new file mode 100644 index 00000000..a7137b11 --- /dev/null +++ b/doc/thinking/2026-04-26-namespace-manager-step42.md @@ -0,0 +1,17 @@ +# 2026-04-26 — NamespaceManager rewrite, step 42 + +## Цель шага + +Убрать последний крупный слой дублирования в namespace watcher-ах: сами `ResourceEventHandlerFuncs`. + +## Что меняем + +1. В `utils` добавляем `NewNamespaceWatcherEventHandlers()`. +2. Конструктор собирает общий `Add/Update/Delete` flow на базе уже существующих handler helper-ов. +3. `buildermgr`, `router`, `executor/multitenant` используют общий конструктор. + +## Что НЕ меняем + +- не меняем label selector; +- не меняем manager semantics; +- не меняем removal policy по умолчанию. \ No newline at end of file diff --git a/pkg/buildermgr/ns_watcher.go b/pkg/buildermgr/ns_watcher.go index ae2acf0e..84bb0807 100644 --- a/pkg/buildermgr/ns_watcher.go +++ b/pkg/buildermgr/ns_watcher.go @@ -45,24 +45,7 @@ func StartNSWatcher( nsInformer := factory.Core().V1().Namespaces().Informer() - _, _ = nsInformer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - nsObj, ok := obj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceAdd(ctx, logger, "buildermgr.NSWatcher", nsManager, nsObj) - } - }, - UpdateFunc: func(oldObj, newObj interface{}) { - oldNSObj, _ := oldObj.(*corev1.Namespace) - nsObj, ok := newObj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceUpdate(ctx, logger, "buildermgr.NSWatcher", nsManager, oldNSObj, nsObj, utils.NamespaceRemovalStrategyTrackOnly) - } - }, - DeleteFunc: func(obj interface{}) { - utils.HandleWatcherNamespaceDelete(ctx, logger, "buildermgr.NSWatcher", nsManager, obj, utils.NamespaceRemovalStrategyTrackOnly) - }, - }) + _, _ = nsInformer.AddEventHandler(utils.NewNamespaceWatcherEventHandlers(ctx, logger, "buildermgr.NSWatcher", nsManager, utils.NamespaceRemovalStrategyTrackOnly)) mgr.Add(ctx, func(ctx context.Context) { logger.Info("buildermgr.NSWatcher: started", diff --git a/pkg/executor/multitenant/ns_watcher.go b/pkg/executor/multitenant/ns_watcher.go index 34daa384..d55fadca 100644 --- a/pkg/executor/multitenant/ns_watcher.go +++ b/pkg/executor/multitenant/ns_watcher.go @@ -110,27 +110,7 @@ func StartNSWatcher( nsInformer := factory.Core().V1().Namespaces().Informer() - _, _ = nsInformer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{ - // AddFunc fires when a new Namespace with the label appears. - AddFunc: func(obj interface{}) { - nsObj, ok := obj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceAdd(ctx, logger, "multitenant.NSWatcher", nsManager, nsObj) - } - }, - // UpdateFunc fires when an existing Namespace is updated — covers the case - // where the label is added to a pre-existing Namespace. - UpdateFunc: func(oldObj, newObj interface{}) { - oldNSObj, _ := oldObj.(*corev1.Namespace) - nsObj, ok := newObj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceUpdate(ctx, logger, "multitenant.NSWatcher", nsManager, oldNSObj, nsObj, utils.NamespaceRemovalStrategyTrackOnly) - } - }, - DeleteFunc: func(obj interface{}) { - utils.HandleWatcherNamespaceDelete(ctx, logger, "multitenant.NSWatcher", nsManager, obj, utils.NamespaceRemovalStrategyTrackOnly) - }, - }) + _, _ = nsInformer.AddEventHandler(utils.NewNamespaceWatcherEventHandlers(ctx, logger, "multitenant.NSWatcher", nsManager, utils.NamespaceRemovalStrategyTrackOnly)) mgr.Add(ctx, func(ctx context.Context) { logger.Info("multitenant.NSWatcher: started", zap.String("label", utils.ManagedNamespaceLabelSelector())) diff --git a/pkg/router/ns_watcher.go b/pkg/router/ns_watcher.go index f426e566..9bbd3f13 100644 --- a/pkg/router/ns_watcher.go +++ b/pkg/router/ns_watcher.go @@ -45,24 +45,7 @@ func StartNSWatcher( nsInformer := factory.Core().V1().Namespaces().Informer() - _, _ = nsInformer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - nsObj, ok := obj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceAdd(ctx, logger, "router.NSWatcher", nsManager, nsObj) - } - }, - UpdateFunc: func(oldObj, newObj interface{}) { - oldNSObj, _ := oldObj.(*corev1.Namespace) - nsObj, ok := newObj.(*corev1.Namespace) - if ok { - utils.HandleWatcherNamespaceUpdate(ctx, logger, "router.NSWatcher", nsManager, oldNSObj, nsObj, utils.NamespaceRemovalStrategyTrackOnly) - } - }, - DeleteFunc: func(obj interface{}) { - utils.HandleWatcherNamespaceDelete(ctx, logger, "router.NSWatcher", nsManager, obj, utils.NamespaceRemovalStrategyTrackOnly) - }, - }) + _, _ = nsInformer.AddEventHandler(utils.NewNamespaceWatcherEventHandlers(ctx, logger, "router.NSWatcher", nsManager, utils.NamespaceRemovalStrategyTrackOnly)) mgr.Add(ctx, func(ctx context.Context) { logger.Info("router.NSWatcher: started", diff --git a/pkg/utils/namespace_manager.go b/pkg/utils/namespace_manager.go index 67c86334..a97f00bf 100644 --- a/pkg/utils/namespace_manager.go +++ b/pkg/utils/namespace_manager.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" corev1 "k8s.io/api/core/v1" + k8sCache "k8s.io/client-go/tools/cache" ) type NamespaceSubscriber interface { @@ -185,6 +186,27 @@ func HandleWatcherNamespaceRemoval(ctx context.Context, logger *zap.Logger, comp zap.String("namespace", record.Name)) } +func NewNamespaceWatcherEventHandlers(ctx context.Context, logger *zap.Logger, component string, manager NamespaceManager, strategy NamespaceRemovalStrategy) k8sCache.ResourceEventHandlerFuncs { + return k8sCache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + namespace, ok := obj.(*corev1.Namespace) + if ok { + HandleWatcherNamespaceAdd(ctx, logger, component, manager, namespace) + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + oldNamespace, _ := oldObj.(*corev1.Namespace) + newNamespace, ok := newObj.(*corev1.Namespace) + if ok { + HandleWatcherNamespaceUpdate(ctx, logger, component, manager, oldNamespace, newNamespace, strategy) + } + }, + DeleteFunc: func(obj interface{}) { + HandleWatcherNamespaceDelete(ctx, logger, component, manager, obj, strategy) + }, + } +} + 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 0fdf1ca9..d9207f9a 100644 --- a/pkg/utils/namespace_manager_test.go +++ b/pkg/utils/namespace_manager_test.go @@ -406,6 +406,29 @@ func TestHandleWatcherNamespaceUpdateDispatchRemoval(t *testing.T) { } } +func TestNewNamespaceWatcherEventHandlers(t *testing.T) { + manager := NewNamespaceManager() + router := &testNamespaceSubscriber{name: "router"} + manager.Subscribe(router) + logger := zap.NewNop() + handlers := NewNamespaceWatcherEventHandlers(context.Background(), logger, "router.NSWatcher", manager, NamespaceRemovalStrategyTrackOnly) + namespace := &corev1.Namespace{} + namespace.Name = "tenant-a" + namespace.Labels = map[string]string{ManagedNamespaceLabelKey: ManagedNamespaceLabelValue} + + handlers.AddFunc(namespace) + handlers.UpdateFunc(namespace, namespace) + handlers.DeleteFunc(k8sCache.DeletedFinalStateUnknown{Obj: namespace}) + + record, ok := manager.Get("tenant-a") + if !ok || record.Phase != NamespacePhaseRemoved { + t.Fatalf("expected watcher event handlers to drive namespace lifecycle") + } + if router.addCalls != 1 || router.resyncCalls != 1 { + t.Fatalf("expected add and resync calls through event handlers") + } +} + func TestNamespaceManagerDispatchAdd(t *testing.T) { manager := NewNamespaceManager() manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})