package registry import ( "context" "fmt" "sync" "github.com/grafana/grafana/pkg/plugins" ) // InMemory is a registry that only allows a single version of a plugin to be registered at a time. type InMemory struct { store map[string]*plugins.Plugin alias map[string]*plugins.Plugin mu sync.RWMutex } func ProvideService() *InMemory { return NewInMemory() } func NewInMemory() *InMemory { return &InMemory{ store: make(map[string]*plugins.Plugin), alias: make(map[string]*plugins.Plugin), } } func (i *InMemory) Plugin(_ context.Context, pluginID, _ string) (*plugins.Plugin, bool) { return i.plugin(pluginID) } func (i *InMemory) Plugins(_ context.Context) []*plugins.Plugin { i.mu.RLock() defer i.mu.RUnlock() res := make([]*plugins.Plugin, 0, len(i.store)) for _, p := range i.store { res = append(res, p) } return res } func (i *InMemory) Add(_ context.Context, p *plugins.Plugin) error { if i.isRegistered(p.ID) { return fmt.Errorf("plugin %s is already registered", p.ID) } i.mu.Lock() i.store[p.ID] = p for _, a := range p.AliasIDs { i.alias[a] = p } i.mu.Unlock() return nil } func (i *InMemory) Remove(_ context.Context, pluginID, _ string) error { p, ok := i.plugin(pluginID) if !ok { return fmt.Errorf("plugin %s is not registered", pluginID) } i.mu.Lock() delete(i.store, pluginID) if p != nil { for _, a := range p.AliasIDs { delete(i.alias, a) } } i.mu.Unlock() return nil } func (i *InMemory) plugin(pluginID string) (*plugins.Plugin, bool) { i.mu.RLock() defer i.mu.RUnlock() p, exists := i.store[pluginID] if !exists { p, exists = i.alias[pluginID] if !exists { return nil, false } } return p, true } func (i *InMemory) isRegistered(pluginID string) bool { p, exists := i.plugin(pluginID) // This may have matched based on an alias return exists && p.ID == pluginID }