github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/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/demonoid81/moby/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 if name == "" { 258 return nil, errors.New("Unable to find plugin without name") 259 } 260 pl, err := get(name) 261 if err != nil { 262 return nil, err 263 } 264 if err := pl.waitActive(); err == nil && pl.implements(imp) { 265 logrus.Debugf("%s implements: %s", name, imp) 266 return pl, nil 267 } 268 return nil, ErrNotImplements 269 } 270 271 // Handle adds the specified function to the extpointHandlers. 272 func Handle(iface string, fn func(string, *Client)) { 273 handlers.Lock() 274 hdlrs, ok := handlers.extpointHandlers[iface] 275 if !ok { 276 hdlrs = []func(string, *Client){} 277 } 278 279 hdlrs = append(hdlrs, fn) 280 handlers.extpointHandlers[iface] = hdlrs 281 282 storage.Lock() 283 for _, p := range storage.plugins { 284 p.activateWait.L.Lock() 285 if p.activated() && p.implements(iface) { 286 p.handlersRun = false 287 } 288 p.activateWait.L.Unlock() 289 } 290 storage.Unlock() 291 292 handlers.Unlock() 293 } 294 295 // GetAll returns all the plugins for the specified implementation 296 func GetAll(imp string) ([]*Plugin, error) { 297 pluginNames, err := Scan() 298 if err != nil { 299 return nil, err 300 } 301 302 type plLoad struct { 303 pl *Plugin 304 err error 305 } 306 307 chPl := make(chan *plLoad, len(pluginNames)) 308 var wg sync.WaitGroup 309 for _, name := range pluginNames { 310 storage.Lock() 311 pl, ok := storage.plugins[name] 312 storage.Unlock() 313 if ok { 314 chPl <- &plLoad{pl, nil} 315 continue 316 } 317 318 wg.Add(1) 319 go func(name string) { 320 defer wg.Done() 321 pl, err := loadWithRetry(name, false) 322 chPl <- &plLoad{pl, err} 323 }(name) 324 } 325 326 wg.Wait() 327 close(chPl) 328 329 var out []*Plugin 330 for pl := range chPl { 331 if pl.err != nil { 332 logrus.Error(pl.err) 333 continue 334 } 335 if err := pl.pl.waitActive(); err == nil && pl.pl.implements(imp) { 336 out = append(out, pl.pl) 337 } 338 } 339 return out, nil 340 }