github.com/vvnotw/moby@v1.13.1/pkg/plugins/plugins.go (about) 1 // Package plugins provides structures and helper functions to manage Docker 2 // plugins. 3 // 4 // Docker discovers plugins by looking for them in the plugin directory whenever 5 // a user or container tries to use one by name. UNIX domain socket files must 6 // be located under /run/docker/plugins, whereas spec files can be located 7 // either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled 8 // by the Registry interface, which lets you list all plugins or get a plugin by 9 // its name if it exists. 10 // 11 // The plugins need to implement an HTTP server and bind this to the UNIX socket 12 // or the address specified in the spec files. 13 // A handshake is send at /Plugin.Activate, and plugins are expected to return 14 // a Manifest with a list of of Docker subsystems which this plugin implements. 15 // 16 // In order to use a plugins, you can use the ``Get`` with the name of the 17 // plugin and the subsystem it implements. 18 // 19 // plugin, err := plugins.Get("example", "VolumeDriver") 20 // if err != nil { 21 // return fmt.Errorf("Error looking up volume plugin example: %v", err) 22 // } 23 package plugins 24 25 import ( 26 "errors" 27 "sync" 28 "time" 29 30 "github.com/Sirupsen/logrus" 31 "github.com/docker/go-connections/tlsconfig" 32 ) 33 34 var ( 35 // ErrNotImplements is returned if the plugin does not implement the requested driver. 36 ErrNotImplements = errors.New("Plugin does not implement the requested driver") 37 ) 38 39 type plugins struct { 40 sync.Mutex 41 plugins map[string]*Plugin 42 } 43 44 type extpointHandlers struct { 45 sync.RWMutex 46 extpointHandlers map[string][]func(string, *Client) 47 } 48 49 var ( 50 storage = plugins{plugins: make(map[string]*Plugin)} 51 handlers = extpointHandlers{extpointHandlers: make(map[string][]func(string, *Client))} 52 ) 53 54 // Manifest lists what a plugin implements. 55 type Manifest struct { 56 // List of subsystem the plugin implements. 57 Implements []string 58 } 59 60 // Plugin is the definition of a docker plugin. 61 type Plugin struct { 62 // Name of the plugin 63 name string 64 // Address of the plugin 65 Addr string 66 // TLS configuration of the plugin 67 TLSConfig *tlsconfig.Options 68 // Client attached to the plugin 69 client *Client 70 // Manifest of the plugin (see above) 71 Manifest *Manifest `json:"-"` 72 73 // wait for activation to finish 74 activateWait *sync.Cond 75 // error produced by activation 76 activateErr error 77 // keeps track of callback handlers run against this plugin 78 handlersRun bool 79 } 80 81 // Name returns the name of the plugin. 82 func (p *Plugin) Name() string { 83 return p.name 84 } 85 86 // Client returns a ready-to-use plugin client that can be used to communicate with the plugin. 87 func (p *Plugin) Client() *Client { 88 return p.client 89 } 90 91 // IsV1 returns true for V1 plugins and false otherwise. 92 func (p *Plugin) IsV1() bool { 93 return true 94 } 95 96 // NewLocalPlugin creates a new local plugin. 97 func NewLocalPlugin(name, addr string) *Plugin { 98 return &Plugin{ 99 name: name, 100 Addr: addr, 101 // TODO: change to nil 102 TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true}, 103 activateWait: sync.NewCond(&sync.Mutex{}), 104 } 105 } 106 107 func (p *Plugin) activate() error { 108 p.activateWait.L.Lock() 109 110 if p.activated() { 111 p.runHandlers() 112 p.activateWait.L.Unlock() 113 return p.activateErr 114 } 115 116 p.activateErr = p.activateWithLock() 117 118 p.runHandlers() 119 p.activateWait.L.Unlock() 120 p.activateWait.Broadcast() 121 return p.activateErr 122 } 123 124 // runHandlers runs the registered handlers for the implemented plugin types 125 // This should only be run after activation, and while the activation lock is held. 126 func (p *Plugin) runHandlers() { 127 if !p.activated() { 128 return 129 } 130 131 handlers.RLock() 132 if !p.handlersRun { 133 for _, iface := range p.Manifest.Implements { 134 hdlrs, handled := handlers.extpointHandlers[iface] 135 if !handled { 136 continue 137 } 138 for _, handler := range hdlrs { 139 handler(p.name, p.client) 140 } 141 } 142 p.handlersRun = true 143 } 144 handlers.RUnlock() 145 146 } 147 148 // activated returns if the plugin has already been activated. 149 // This should only be called with the activation lock held 150 func (p *Plugin) activated() bool { 151 return p.Manifest != nil 152 } 153 154 func (p *Plugin) activateWithLock() error { 155 c, err := NewClient(p.Addr, p.TLSConfig) 156 if err != nil { 157 return err 158 } 159 p.client = c 160 161 m := new(Manifest) 162 if err = p.client.Call("Plugin.Activate", nil, m); err != nil { 163 return err 164 } 165 166 p.Manifest = m 167 return nil 168 } 169 170 func (p *Plugin) waitActive() error { 171 p.activateWait.L.Lock() 172 for !p.activated() && p.activateErr == nil { 173 p.activateWait.Wait() 174 } 175 p.activateWait.L.Unlock() 176 return p.activateErr 177 } 178 179 func (p *Plugin) implements(kind string) bool { 180 if p.Manifest == nil { 181 return false 182 } 183 for _, driver := range p.Manifest.Implements { 184 if driver == kind { 185 return true 186 } 187 } 188 return false 189 } 190 191 func load(name string) (*Plugin, error) { 192 return loadWithRetry(name, true) 193 } 194 195 func loadWithRetry(name string, retry bool) (*Plugin, error) { 196 registry := newLocalRegistry() 197 start := time.Now() 198 199 var retries int 200 for { 201 pl, err := registry.Plugin(name) 202 if err != nil { 203 if !retry { 204 return nil, err 205 } 206 207 timeOff := backoff(retries) 208 if abort(start, timeOff) { 209 return nil, err 210 } 211 retries++ 212 logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 213 time.Sleep(timeOff) 214 continue 215 } 216 217 storage.Lock() 218 if pl, exists := storage.plugins[name]; exists { 219 storage.Unlock() 220 return pl, pl.activate() 221 } 222 storage.plugins[name] = pl 223 storage.Unlock() 224 225 err = pl.activate() 226 227 if err != nil { 228 storage.Lock() 229 delete(storage.plugins, name) 230 storage.Unlock() 231 } 232 233 return pl, err 234 } 235 } 236 237 func get(name string) (*Plugin, error) { 238 storage.Lock() 239 pl, ok := storage.plugins[name] 240 storage.Unlock() 241 if ok { 242 return pl, pl.activate() 243 } 244 return load(name) 245 } 246 247 // Get returns the plugin given the specified name and requested implementation. 248 func Get(name, imp string) (*Plugin, error) { 249 pl, err := get(name) 250 if err != nil { 251 return nil, err 252 } 253 if err := pl.waitActive(); err == nil && pl.implements(imp) { 254 logrus.Debugf("%s implements: %s", name, imp) 255 return pl, nil 256 } 257 return nil, ErrNotImplements 258 } 259 260 // Handle adds the specified function to the extpointHandlers. 261 func Handle(iface string, fn func(string, *Client)) { 262 handlers.Lock() 263 hdlrs, ok := handlers.extpointHandlers[iface] 264 if !ok { 265 hdlrs = []func(string, *Client){} 266 } 267 268 hdlrs = append(hdlrs, fn) 269 handlers.extpointHandlers[iface] = hdlrs 270 271 storage.Lock() 272 for _, p := range storage.plugins { 273 p.activateWait.L.Lock() 274 if p.activated() && p.implements(iface) { 275 p.handlersRun = false 276 } 277 p.activateWait.L.Unlock() 278 } 279 storage.Unlock() 280 281 handlers.Unlock() 282 } 283 284 // GetAll returns all the plugins for the specified implementation 285 func GetAll(imp string) ([]*Plugin, error) { 286 pluginNames, err := Scan() 287 if err != nil { 288 return nil, err 289 } 290 291 type plLoad struct { 292 pl *Plugin 293 err error 294 } 295 296 chPl := make(chan *plLoad, len(pluginNames)) 297 var wg sync.WaitGroup 298 for _, name := range pluginNames { 299 storage.Lock() 300 pl, ok := storage.plugins[name] 301 storage.Unlock() 302 if ok { 303 chPl <- &plLoad{pl, nil} 304 continue 305 } 306 307 wg.Add(1) 308 go func(name string) { 309 defer wg.Done() 310 pl, err := loadWithRetry(name, false) 311 chPl <- &plLoad{pl, err} 312 }(name) 313 } 314 315 wg.Wait() 316 close(chPl) 317 318 var out []*Plugin 319 for pl := range chPl { 320 if pl.err != nil { 321 logrus.Error(pl.err) 322 continue 323 } 324 if err := pl.pl.waitActive(); err == nil && pl.pl.implements(imp) { 325 out = append(out, pl.pl) 326 } 327 } 328 return out, nil 329 }