package demos import "sync" // MemStore : Store en mémoire (dev/tests). type MemStore struct { mu sync.RWMutex items map[string]Demo } func NewMemStore() *MemStore { return &MemStore{items: make(map[string]Demo)} } func (m *MemStore) Create(d Demo) (Demo, error) { m.mu.Lock() defer m.mu.Unlock() m.items[d.ID] = d return d, nil } func (m *MemStore) Get(id string) (Demo, bool) { m.mu.RLock() defer m.mu.RUnlock() d, ok := m.items[id] return d, ok } func (m *MemStore) List() ([]Demo, error) { m.mu.RLock() defer m.mu.RUnlock() out := make([]Demo, 0, len(m.items)) for _, d := range m.items { out = append(out, d) } return out, nil } func (m *MemStore) ListByUsername(username string) ([]Demo, error) { m.mu.RLock() defer m.mu.RUnlock() out := make([]Demo, 0) for _, d := range m.items { if d.Username == username { out = append(out, d) } } return out, nil } func (m *MemStore) Update(d Demo) (Demo, error) { m.mu.Lock() defer m.mu.Unlock() m.items[d.ID] = d return d, nil } func (m *MemStore) CountActive() (int, error) { m.mu.RLock() defer m.mu.RUnlock() n := 0 for _, d := range m.items { if d.Status.Active() { n++ } } return n, nil } func (m *MemStore) GetDemoByUsername(username string) (Demo, error) { m.mu.Lock() defer m.mu.Unlock() for _, demo := range m.items { if demo.Username == username { return demo, nil } } return Demo{}, ErrNotFound } // MemPool : Pool en mémoire (dev/tests), PoolSizePerService slots par service. type MemPool struct { mu sync.Mutex res []*ExternalResource next uint } // NewMemPool construit un pool rempli (PoolSizePerService slots libres par service). func NewMemPool() *MemPool { p := &MemPool{} for _, svc := range PooledServices { for i := 1; i <= PoolSizePerService; i++ { p.next++ p.res = append(p.res, &ExternalResource{ ID: p.next, Service: svc, Label: placeholderLabel(svc, i), SecretRef: "secretref://" + svc + "/slot-" + itoa(i), Status: ResourceFree, }) } } return p } func (p *MemPool) Borrow(demoID string) ([]ExternalResource, error) { p.mu.Lock() defer p.mu.Unlock() // Vérifie d'abord la dispo de chaque service (atomicité). pick := make(map[string]*ExternalResource, len(PooledServices)) for _, svc := range PooledServices { var found *ExternalResource for _, r := range p.res { if r.Service == svc && r.Status == ResourceFree { found = r break } } if found == nil { return nil, ErrPoolExhausted } pick[svc] = found } var out []ExternalResource for _, r := range pick { r.Status = ResourceBorrowed r.DemoID = demoID out = append(out, *r) } return out, nil } func (p *MemPool) Return(demoID string) error { p.mu.Lock() defer p.mu.Unlock() for _, r := range p.res { if r.DemoID == demoID { r.Status = ResourceFree r.DemoID = "" } } return nil } func (p *MemPool) FreeCount() (map[string]int, error) { p.mu.Lock() defer p.mu.Unlock() out := map[string]int{} for _, svc := range PooledServices { out[svc] = 0 } for _, r := range p.res { if r.Status == ResourceFree { out[r.Service]++ } } return out, nil }