diff --git a/doc/thinking/2026-04-26-namespace-manager-step50.md b/doc/thinking/2026-04-26-namespace-manager-step50.md index 3e446fef..c40be65b 100644 --- a/doc/thinking/2026-04-26-namespace-manager-step50.md +++ b/doc/thinking/2026-04-26-namespace-manager-step50.md @@ -2,6 +2,21 @@ ## Цель шага +Сделать summary/debug surface полезным в реальном watcher lifecycle, а не только на этапе подготовки manager-а. + +## Что изменено + +1. После успешных add/resync/remove transitions watcher helpers теперь пишут компактный summary manager-а. +2. Добавлен unit test на add-handler path с проверкой structured-log полей. + +## Что это даёт + +- runtime behavior не меняется; +- появляется последовательный debug trail по изменению manager state; +- новый helper `HasActiveNamespaces()` теперь используется и в general logging path, и в watcher transition path.# 2026-04-26 — NamespaceManager rewrite, step 50 + +## Цель шага + Сделать orchestration API для managed namespace watcher-а жёстче и читабельнее. ## Что меняем diff --git a/pkg/utils/namespace_manager.go b/pkg/utils/namespace_manager.go index aaf23724..1d595bbc 100644 --- a/pkg/utils/namespace_manager.go +++ b/pkg/utils/namespace_manager.go @@ -191,7 +191,9 @@ func HandleWatcherNamespaceAdd(ctx context.Context, logger *zap.Logger, componen } if _, _, err := DispatchNamespaceAdd(ctx, manager, namespace, NamespaceSourceWatcher, time.Now().UTC()); err != nil { logger.Error(component+": DispatchAdd failed", zap.String("namespace", namespace.Name), zap.Error(err)) + return } + LogNamespaceManagerSummary(logger, component+": namespace manager summary", manager.Summary()) } func HandleWatcherNamespaceUpdate(ctx context.Context, logger *zap.Logger, component string, manager NamespaceManager, oldNamespace *corev1.Namespace, newNamespace *corev1.Namespace, strategy NamespaceRemovalStrategy) { @@ -207,7 +209,9 @@ func HandleWatcherNamespaceUpdate(ctx context.Context, logger *zap.Logger, compo } if _, _, err := DispatchNamespaceResync(ctx, manager, newNamespace, NamespaceSourceWatcher, time.Now().UTC()); err != nil { logger.Error(component+": DispatchResync failed", zap.String("namespace", newNamespace.Name), zap.Error(err)) + return } + LogNamespaceManagerSummary(logger, component+": namespace manager summary", manager.Summary()) } func HandleWatcherNamespaceDelete(ctx context.Context, logger *zap.Logger, component string, manager NamespaceManager, obj interface{}, strategy NamespaceRemovalStrategy) { @@ -226,10 +230,12 @@ func HandleWatcherNamespaceRemoval(ctx context.Context, logger *zap.Logger, comp } logger.Info(component+": namespace removal dispatched through subscribers", zap.String("namespace", record.Name)) + LogNamespaceManagerSummary(logger, component+": namespace manager summary", manager.Summary()) return } logger.Info(component+": namespace removed from manager state; runtime registrations kept", zap.String("namespace", record.Name)) + LogNamespaceManagerSummary(logger, component+": namespace manager summary", manager.Summary()) } func NewNamespaceWatcherEventHandlers(ctx context.Context, logger *zap.Logger, component string, manager NamespaceManager, strategy NamespaceRemovalStrategy) k8sCache.ResourceEventHandlerFuncs { diff --git a/pkg/utils/namespace_manager_test.go b/pkg/utils/namespace_manager_test.go index f47ce939..90c2c6dc 100644 --- a/pkg/utils/namespace_manager_test.go +++ b/pkg/utils/namespace_manager_test.go @@ -378,6 +378,35 @@ func TestHandleWatcherNamespaceAddUpdateDelete(t *testing.T) { } } +func TestHandleWatcherNamespaceAddLogsSummary(t *testing.T) { + manager := NewNamespaceManager() + router := &testNamespaceSubscriber{name: "router"} + manager.Subscribe(router) + core, logs := observer.New(zap.InfoLevel) + logger := zap.New(core) + namespace := &corev1.Namespace{} + namespace.Name = "tenant-a" + namespace.Labels = map[string]string{ManagedNamespaceLabelKey: ManagedNamespaceLabelValue} + + HandleWatcherNamespaceAdd(context.Background(), logger, "router.NSWatcher", manager, namespace) + + entries := logs.AllUntimed() + if len(entries) != 1 { + t.Fatalf("expected one summary log entry, got %d", len(entries)) + } + if entries[0].Message != "router.NSWatcher: namespace manager summary" { + t.Fatalf("unexpected log message: %s", entries[0].Message) + } + fields := entries[0].ContextMap() + active, ok := fields["has_active_namespaces"].(bool) + if !ok || !active { + t.Fatalf("expected summary log to report active namespaces") + } + if live, ok := fields["live_namespaces"].(int64); !ok || live != 1 { + t.Fatalf("expected summary log to report one live namespace, got %v", fields["live_namespaces"]) + } +} + func TestHandleWatcherNamespaceRemovalDispatch(t *testing.T) { manager := NewNamespaceManager() router := &testNamespaceSubscriber{name: "router"}