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  }