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