package utils import ( "context" "sort" "sync" "time" ) type NamespaceSubscriber interface { Name() string OnNamespaceAdd(ctx context.Context, record NamespaceRecord) error OnNamespaceRemove(ctx context.Context, record NamespaceRecord) error OnNamespaceResync(ctx context.Context, record NamespaceRecord) error } type NamespaceManager interface { Snapshot() []string SnapshotRecords() []NamespaceRecord Get(name string) (NamespaceRecord, bool) Bootstrap(namespaces []string, source NamespaceSource, observedAt time.Time) []NamespaceRecord Subscribe(subscriber NamespaceSubscriber) SnapshotSubscribers() []string Upsert(event NamespaceEvent) NamespaceRecord DispatchAdd(ctx context.Context, namespace string) (NamespaceRecord, bool, error) DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error) MarkPartState(namespace string, part string, state NamespacePartState) (NamespaceRecord, bool) MarkPartRegistering(namespace string, part string) (NamespaceRecord, bool) MarkPartActive(namespace string, part string) (NamespaceRecord, bool) MarkPartFailed(namespace string, part string, err error) (NamespaceRecord, bool) Remove(name string) bool } 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 NewBootstrappedNamespaceManager(resolver *NamespaceResolver, source NamespaceSource, observedAt time.Time) NamespaceManager { manager := NewNamespaceManager() if resolver == nil { return manager } manager.Bootstrap(resolver.Snapshot(), source, observedAt) return manager } func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) { m.mu.Lock() defer m.mu.Unlock() 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 { 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) snapshotSubscriberObjects() []NamespaceSubscriber { 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) subscribers := make([]NamespaceSubscriber, 0, len(names)) for _, name := range names { subscribers = append(subscribers, m.subs[name]) } return subscribers } func (m *inMemoryNamespaceManager) Snapshot() []string { m.mu.RLock() defer m.mu.RUnlock() namespaces := make([]string, 0, len(m.records)) for name, record := range m.records { if record.Phase == NamespacePhaseRemoved { continue } namespaces = append(namespaces, name) } sort.Strings(namespaces) return namespaces } func (m *inMemoryNamespaceManager) SnapshotRecords() []NamespaceRecord { m.mu.RLock() defer m.mu.RUnlock() records := make([]NamespaceRecord, 0, len(m.records)) for _, record := range m.records { records = append(records, record.Clone()) } sort.Slice(records, func(i, j int) bool { return records[i].Name < records[j].Name }) return records } func (m *inMemoryNamespaceManager) Get(name string) (NamespaceRecord, bool) { m.mu.RLock() defer m.mu.RUnlock() record, ok := m.records[name] if !ok { return NamespaceRecord{}, false } return record.Clone(), true } func (m *inMemoryNamespaceManager) Upsert(event NamespaceEvent) NamespaceRecord { m.mu.Lock() defer m.mu.Unlock() now := event.ObservedAt if now.IsZero() { now = time.Now().UTC() } record, exists := m.records[event.Name] if !exists { record = NamespaceRecord{ Name: event.Name, RegisteredParts: make(map[string]NamespacePartState), } if event.Type == NamespaceEventRemove { record.Generation = 0 } else { record.Generation = 1 } } else if event.Type != NamespaceEventRemove { record.Generation++ } record.Name = event.Name record.Source = event.Source record.Labels = cloneStringMap(event.Labels) record.UpdatedAt = now switch event.Type { case NamespaceEventAdd, NamespaceEventUpdate, NamespaceEventResync: record.Phase = NamespacePhaseDiscovered record.LastError = "" case NamespaceEventRemove: record.Phase = NamespacePhaseRemoved } if record.RegisteredParts == nil { record.RegisteredParts = make(map[string]NamespacePartState) } m.records[event.Name] = record return record.Clone() } func (m *inMemoryNamespaceManager) DispatchAdd(ctx context.Context, namespace string) (NamespaceRecord, bool, error) { return m.dispatch(ctx, namespace, func(subscriber NamespaceSubscriber, record NamespaceRecord) error { return subscriber.OnNamespaceAdd(ctx, record) }) } func (m *inMemoryNamespaceManager) DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error) { return m.dispatch(ctx, namespace, func(subscriber NamespaceSubscriber, record NamespaceRecord) error { return subscriber.OnNamespaceResync(ctx, record) }) } func (m *inMemoryNamespaceManager) dispatch(ctx context.Context, namespace string, handler func(NamespaceSubscriber, NamespaceRecord) error) (NamespaceRecord, bool, error) { record, ok := m.Get(namespace) if !ok { return NamespaceRecord{}, false, nil } var firstErr error for _, subscriber := range m.snapshotSubscriberObjects() { _, _ = m.MarkPartRegistering(namespace, subscriber.Name()) currentRecord, _ := m.Get(namespace) err := handler(subscriber, currentRecord) if err != nil { _, _ = m.MarkPartFailed(namespace, subscriber.Name(), err) if firstErr == nil { firstErr = err } continue } _, _ = m.MarkPartActive(namespace, subscriber.Name()) } record, _ = m.Get(namespace) return record, true, firstErr } func (m *inMemoryNamespaceManager) MarkPartState(namespace string, part string, state NamespacePartState) (NamespaceRecord, bool) { m.mu.Lock() defer m.mu.Unlock() record, ok := m.records[namespace] if !ok { return NamespaceRecord{}, false } if record.RegisteredParts == nil { record.RegisteredParts = make(map[string]NamespacePartState) } if state.UpdatedAt.IsZero() { state.UpdatedAt = time.Now().UTC() } record.RegisteredParts[part] = state record.Phase = deriveNamespacePhase(record) record.UpdatedAt = state.UpdatedAt m.records[namespace] = record return record.Clone(), true } func (m *inMemoryNamespaceManager) MarkPartRegistering(namespace string, part string) (NamespaceRecord, bool) { return m.MarkPartState(namespace, part, NamespacePartState{State: NamespacePartStateRegistering}) } func (m *inMemoryNamespaceManager) MarkPartActive(namespace string, part string) (NamespaceRecord, bool) { return m.MarkPartState(namespace, part, NamespacePartState{State: NamespacePartStateActive}) } func (m *inMemoryNamespaceManager) MarkPartFailed(namespace string, part string, err error) (NamespaceRecord, bool) { lastError := "" if err != nil { lastError = err.Error() } return m.MarkPartState(namespace, part, NamespacePartState{State: NamespacePartStateFailed, LastError: lastError}) } func (m *inMemoryNamespaceManager) Remove(name string) bool { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.records[name]; !ok { return false } delete(m.records, name) return true } func deriveNamespacePhase(record NamespaceRecord) NamespacePhase { if len(record.RegisteredParts) == 0 { return record.Phase } hasRegistering := false for _, part := range record.RegisteredParts { switch part.State { case NamespacePartStateFailed: return NamespacePhaseFailed case NamespacePartStateRegistering: hasRegistering = true case NamespacePartStateActive: continue default: hasRegistering = true } } if hasRegistering { return NamespacePhaseRegistering } return NamespacePhaseActive }