github.com/bigcommerce/nomad@v0.9.3-bc/client/pluginmanager/group.go (about) 1 package pluginmanager 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 log "github.com/hashicorp/go-hclog" 9 ) 10 11 // PluginGroup is a utility struct to manage a collectively orchestrate a 12 // set of PluginManagers 13 type PluginGroup struct { 14 // managers is the set of managers managed, access is synced by mLock 15 managers []PluginManager 16 17 // shutdown indicates if shutdown has been called, access is synced by mLock 18 shutdown bool 19 20 // mLock gaurds managers and shutdown 21 mLock sync.Mutex 22 23 logger log.Logger 24 } 25 26 // New returns an initialized PluginGroup 27 func New(logger log.Logger) *PluginGroup { 28 return &PluginGroup{ 29 managers: []PluginManager{}, 30 logger: logger.Named("plugin"), 31 } 32 } 33 34 // RegisterAndRun registers the manager and starts it in a separate goroutine 35 func (m *PluginGroup) RegisterAndRun(manager PluginManager) error { 36 m.mLock.Lock() 37 defer m.mLock.Unlock() 38 if m.shutdown { 39 return fmt.Errorf("plugin group already shutdown") 40 } 41 m.managers = append(m.managers, manager) 42 43 m.logger.Info("starting plugin manager", "plugin-type", manager.PluginType()) 44 manager.Run() 45 return nil 46 } 47 48 // Ready returns a channel which will be closed once all plugin managers are ready. 49 // A timeout for waiting on each manager is given 50 func (m *PluginGroup) WaitForFirstFingerprint(ctx context.Context) (<-chan struct{}, error) { 51 m.mLock.Lock() 52 defer m.mLock.Unlock() 53 if m.shutdown { 54 return nil, fmt.Errorf("plugin group already shutdown") 55 } 56 57 var wg sync.WaitGroup 58 for i := range m.managers { 59 manager, ok := m.managers[i].(FingerprintingPluginManager) 60 if !ok { 61 continue 62 } 63 logger := m.logger.With("plugin-type", manager.PluginType()) 64 wg.Add(1) 65 go func() { 66 defer wg.Done() 67 logger.Debug("waiting on plugin manager initial fingerprint") 68 select { 69 case <-manager.WaitForFirstFingerprint(ctx): 70 select { 71 case <-ctx.Done(): 72 logger.Warn("timeout waiting for plugin manager to be ready") 73 default: 74 logger.Debug("finished plugin manager initial fingerprint") 75 } 76 } 77 }() 78 } 79 80 ret := make(chan struct{}) 81 go func() { 82 wg.Wait() 83 close(ret) 84 }() 85 return ret, nil 86 } 87 88 // Shutdown shutsdown all registered PluginManagers in reverse order of how 89 // they were started. 90 func (m *PluginGroup) Shutdown() { 91 m.mLock.Lock() 92 defer m.mLock.Unlock() 93 for i := len(m.managers) - 1; i >= 0; i-- { 94 m.logger.Info("shutting down plugin manager", "plugin-type", m.managers[i].PluginType()) 95 m.managers[i].Shutdown() 96 m.logger.Info("plugin manager finished", "plugin-type", m.managers[i].PluginType()) 97 } 98 m.shutdown = true 99 }