github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/manager.go (about)

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