From 910f65b6d4ac4bdf3a59fba72f2a0267877805ab Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 26 Apr 2026 10:01:21 +0300 Subject: [PATCH] layer1: add namespace manager subscribers step 9 --- .../2026-04-26-namespace-manager-step9.md | 20 ++++++++++++++ pkg/utils/namespace_manager.go | 26 +++++++++++++++++++ pkg/utils/namespace_manager_test.go | 20 ++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 doc/thinking/2026-04-26-namespace-manager-step9.md diff --git a/doc/thinking/2026-04-26-namespace-manager-step9.md b/doc/thinking/2026-04-26-namespace-manager-step9.md new file mode 100644 index 00000000..37815d59 --- /dev/null +++ b/doc/thinking/2026-04-26-namespace-manager-step9.md @@ -0,0 +1,20 @@ +# 2026-04-26 — NamespaceManager rewrite, step 9 + +## Цель шага + +Добавить subscriber contract в `NamespaceManager`, не подключая его пока к runtime. + +## Что меняем + +1. Добавляем interface `NamespaceSubscriber`. +2. Добавляем в manager операции: + - `Subscribe()` + - `SnapshotSubscribers()` +3. Добавляем unit tests на регистрацию и snapshot subscriber-ов. + +## Что НЕ меняем + +- не вызываем subscriber-ов из watcher-ов; +- не строим reconcile loop; +- не трогаем runtime components; +- не затрагиваем внешнее изменение в `serviceaccount.go`. \ No newline at end of file diff --git a/pkg/utils/namespace_manager.go b/pkg/utils/namespace_manager.go index 91b800c4..489c948f 100644 --- a/pkg/utils/namespace_manager.go +++ b/pkg/utils/namespace_manager.go @@ -6,10 +6,16 @@ import ( "time" ) +type NamespaceSubscriber interface { + Name() string +} + type NamespaceManager interface { Snapshot() []string SnapshotRecords() []NamespaceRecord Get(name string) (NamespaceRecord, bool) + Subscribe(subscriber NamespaceSubscriber) + SnapshotSubscribers() []string Upsert(event NamespaceEvent) NamespaceRecord MarkPartState(namespace string, part string, state NamespacePartState) (NamespaceRecord, bool) Remove(name string) bool @@ -18,14 +24,34 @@ type NamespaceManager interface { type inMemoryNamespaceManager struct { mu sync.RWMutex records map[string]NamespaceRecord + subs map[string]NamespaceSubscriber } func NewNamespaceManager() NamespaceManager { return &inMemoryNamespaceManager{ records: make(map[string]NamespaceRecord), + subs: make(map[string]NamespaceSubscriber), } } +func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) { + m.mu.Lock() + defer m.mu.Unlock() + m.subs[subscriber.Name()] = subscriber +} + +func (m *inMemoryNamespaceManager) SnapshotSubscribers() []string { + m.mu.RLock() + defer m.mu.RUnlock() + + names := make([]string, 0, len(m.subs)) + for name := range m.subs { + names = append(names, name) + } + sort.Strings(names) + return names +} + func (m *inMemoryNamespaceManager) Snapshot() []string { m.mu.RLock() defer m.mu.RUnlock() diff --git a/pkg/utils/namespace_manager_test.go b/pkg/utils/namespace_manager_test.go index b69e1d41..47db9c54 100644 --- a/pkg/utils/namespace_manager_test.go +++ b/pkg/utils/namespace_manager_test.go @@ -6,6 +6,14 @@ import ( "time" ) +type testNamespaceSubscriber struct { + name string +} + +func (s testNamespaceSubscriber) Name() string { + return s.name +} + func TestNamespaceManagerSnapshotAndGet(t *testing.T) { manager := NewNamespaceManager() now := time.Now().UTC() @@ -93,4 +101,16 @@ func TestNamespaceManagerSnapshotRecordsReturnsCopies(t *testing.T) { if record.RegisteredParts["router"].State != "active" { t.Fatalf("expected part states to be detached copies") } +} + +func TestNamespaceManagerSubscribers(t *testing.T) { + manager := NewNamespaceManager() + manager.Subscribe(testNamespaceSubscriber{name: "router"}) + manager.Subscribe(testNamespaceSubscriber{name: "buildermgr"}) + manager.Subscribe(testNamespaceSubscriber{name: "router"}) + + expected := []string{"buildermgr", "router"} + if !reflect.DeepEqual(expected, manager.SnapshotSubscribers()) { + t.Fatalf("expected subscribers %v, got %v", expected, manager.SnapshotSubscribers()) + } } \ No newline at end of file