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 }