layer1: add namespace bootstrap step 11
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# 2026-04-26 — NamespaceManager rewrite, step 11
|
||||||
|
|
||||||
|
## Цель шага
|
||||||
|
|
||||||
|
Добавить bootstrap helper для массовой загрузки initial namespace set в manager.
|
||||||
|
|
||||||
|
## Что меняем
|
||||||
|
|
||||||
|
1. Добавляем `Bootstrap()` в manager interface и реализацию.
|
||||||
|
2. Метод принимает список namespace-ов и `NamespaceSource`.
|
||||||
|
3. Метод прогоняет namespaces через `Upsert()` как initial discovered set.
|
||||||
|
4. Добавляем unit tests на bootstrap.
|
||||||
|
|
||||||
|
## Что НЕ меняем
|
||||||
|
|
||||||
|
- не подключаем bootstrap к runtime startup path;
|
||||||
|
- не меняем watcher-ы;
|
||||||
|
- не трогаем resolver/SA/runtime.
|
||||||
@@ -14,6 +14,7 @@ type NamespaceManager interface {
|
|||||||
Snapshot() []string
|
Snapshot() []string
|
||||||
SnapshotRecords() []NamespaceRecord
|
SnapshotRecords() []NamespaceRecord
|
||||||
Get(name string) (NamespaceRecord, bool)
|
Get(name string) (NamespaceRecord, bool)
|
||||||
|
Bootstrap(namespaces []string, source NamespaceSource, observedAt time.Time) []NamespaceRecord
|
||||||
Subscribe(subscriber NamespaceSubscriber)
|
Subscribe(subscriber NamespaceSubscriber)
|
||||||
SnapshotSubscribers() []string
|
SnapshotSubscribers() []string
|
||||||
Upsert(event NamespaceEvent) NamespaceRecord
|
Upsert(event NamespaceEvent) NamespaceRecord
|
||||||
@@ -40,6 +41,22 @@ func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) {
|
|||||||
m.subs[subscriber.Name()] = subscriber
|
m.subs[subscriber.Name()] = subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *inMemoryNamespaceManager) Bootstrap(namespaces []string, source NamespaceSource, observedAt time.Time) []NamespaceRecord {
|
||||||
|
records := make([]NamespaceRecord, 0, len(namespaces))
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
records = append(records, m.Upsert(NamespaceEvent{
|
||||||
|
Type: NamespaceEventAdd,
|
||||||
|
Name: namespace,
|
||||||
|
Source: source,
|
||||||
|
ObservedAt: observedAt,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
sort.Slice(records, func(i, j int) bool {
|
||||||
|
return records[i].Name < records[j].Name
|
||||||
|
})
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
func (m *inMemoryNamespaceManager) SnapshotSubscribers() []string {
|
func (m *inMemoryNamespaceManager) SnapshotSubscribers() []string {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
defer m.mu.RUnlock()
|
defer m.mu.RUnlock()
|
||||||
|
|||||||
@@ -138,3 +138,23 @@ func TestNamespaceManagerMarkPartStateDerivesPhase(t *testing.T) {
|
|||||||
t.Fatalf("expected phase failed, got %s", record.Phase)
|
t.Fatalf("expected phase failed, got %s", record.Phase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNamespaceManagerBootstrap(t *testing.T) {
|
||||||
|
manager := NewNamespaceManager()
|
||||||
|
now := time.Now().UTC()
|
||||||
|
records := manager.Bootstrap([]string{"tenant-b", "tenant-a"}, NamespaceSourceBackfill, now)
|
||||||
|
|
||||||
|
if len(records) != 2 {
|
||||||
|
t.Fatalf("expected 2 bootstrap records, got %d", len(records))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual([]string{"tenant-a", "tenant-b"}, manager.Snapshot()) {
|
||||||
|
t.Fatalf("expected manager snapshot to contain bootstrapped namespaces")
|
||||||
|
}
|
||||||
|
record, ok := manager.Get("tenant-a")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected tenant-a after bootstrap")
|
||||||
|
}
|
||||||
|
if record.Source != NamespaceSourceBackfill {
|
||||||
|
t.Fatalf("expected bootstrap source backfill, got %s", record.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user