gopkg.in/docker/docker.v23@v23.0.11/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 // 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, fmt.Errorf("%w: plugin=%q, requested implementation=%q", ErrNotImplements, name, imp) 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 }