github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/plugin/manager.go (about)

     1  package plugin
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/libcontainerd"
    12  	"github.com/docker/docker/plugin/store"
    13  	"github.com/docker/docker/plugin/v2"
    14  	"github.com/docker/docker/registry"
    15  )
    16  
    17  var (
    18  	manager *Manager
    19  )
    20  
    21  func (pm *Manager) restorePlugin(p *v2.Plugin) error {
    22  	p.RuntimeSourcePath = filepath.Join(pm.runRoot, p.GetID())
    23  	if p.IsEnabled() {
    24  		return pm.restore(p)
    25  	}
    26  	return nil
    27  }
    28  
    29  type eventLogger func(id, name, action string)
    30  
    31  // Manager controls the plugin subsystem.
    32  type Manager struct {
    33  	libRoot           string
    34  	runRoot           string
    35  	pluginStore       *store.Store
    36  	containerdClient  libcontainerd.Client
    37  	registryService   registry.Service
    38  	liveRestore       bool
    39  	pluginEventLogger eventLogger
    40  }
    41  
    42  // GetManager returns the singleton plugin Manager
    43  func GetManager() *Manager {
    44  	return manager
    45  }
    46  
    47  // Init (was NewManager) instantiates the singleton Manager.
    48  // TODO: revert this to NewManager once we get rid of all the singletons.
    49  func Init(root string, ps *store.Store, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
    50  	if manager != nil {
    51  		return nil
    52  	}
    53  
    54  	root = filepath.Join(root, "plugins")
    55  	manager = &Manager{
    56  		libRoot:           root,
    57  		runRoot:           "/run/docker",
    58  		pluginStore:       ps,
    59  		registryService:   rs,
    60  		liveRestore:       liveRestore,
    61  		pluginEventLogger: evL,
    62  	}
    63  	if err := os.MkdirAll(manager.runRoot, 0700); err != nil {
    64  		return err
    65  	}
    66  	manager.containerdClient, err = remote.Client(manager)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	if err := manager.init(); err != nil {
    71  		return err
    72  	}
    73  	return nil
    74  }
    75  
    76  // StateChanged updates plugin internals using libcontainerd events.
    77  func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
    78  	logrus.Debugf("plugin state changed %s %#v", id, e)
    79  
    80  	switch e.State {
    81  	case libcontainerd.StateExit:
    82  		p, err := pm.pluginStore.GetByID(id)
    83  		if err != nil {
    84  			return err
    85  		}
    86  		p.RLock()
    87  		if p.ExitChan != nil {
    88  			close(p.ExitChan)
    89  		}
    90  		restart := p.Restart
    91  		p.RUnlock()
    92  		p.RemoveFromDisk()
    93  		if restart {
    94  			pm.enable(p, true)
    95  		}
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (pm *Manager) init() error {
   102  	dt, err := os.Open(filepath.Join(pm.libRoot, "plugins.json"))
   103  	if err != nil {
   104  		if os.IsNotExist(err) {
   105  			return nil
   106  		}
   107  		return err
   108  	}
   109  	defer dt.Close()
   110  
   111  	plugins := make(map[string]*v2.Plugin)
   112  	if err := json.NewDecoder(dt).Decode(&plugins); err != nil {
   113  		return err
   114  	}
   115  	pm.pluginStore.SetAll(plugins)
   116  
   117  	var group sync.WaitGroup
   118  	group.Add(len(plugins))
   119  	for _, p := range plugins {
   120  		go func(p *v2.Plugin) {
   121  			defer group.Done()
   122  			if err := pm.restorePlugin(p); err != nil {
   123  				logrus.Errorf("failed to restore plugin '%s': %s", p.Name(), err)
   124  				return
   125  			}
   126  
   127  			pm.pluginStore.Add(p)
   128  			requiresManualRestore := !pm.liveRestore && p.IsEnabled()
   129  
   130  			if requiresManualRestore {
   131  				// if liveRestore is not enabled, the plugin will be stopped now so we should enable it
   132  				if err := pm.enable(p, true); err != nil {
   133  					logrus.Errorf("failed to enable plugin '%s': %s", p.Name(), err)
   134  				}
   135  			}
   136  		}(p)
   137  	}
   138  	group.Wait()
   139  	return nil
   140  }
   141  
   142  type logHook struct{ id string }
   143  
   144  func (logHook) Levels() []logrus.Level {
   145  	return logrus.AllLevels
   146  }
   147  
   148  func (l logHook) Fire(entry *logrus.Entry) error {
   149  	entry.Data = logrus.Fields{"plugin": l.id}
   150  	return nil
   151  }
   152  
   153  func attachToLog(id string) func(libcontainerd.IOPipe) error {
   154  	return func(iop libcontainerd.IOPipe) error {
   155  		iop.Stdin.Close()
   156  
   157  		logger := logrus.New()
   158  		logger.Hooks.Add(logHook{id})
   159  		// TODO: cache writer per id
   160  		w := logger.Writer()
   161  		go func() {
   162  			io.Copy(w, iop.Stdout)
   163  		}()
   164  		go func() {
   165  			// TODO: update logrus and use logger.WriterLevel
   166  			io.Copy(w, iop.Stderr)
   167  		}()
   168  		return nil
   169  	}
   170  }