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