github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/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/Prakhar-Agarwal-byte/moby/pkg/plugins" 24 25 import ( 26 "context" 27 "errors" 28 "fmt" 29 "sync" 30 "time" 31 32 "github.com/containerd/log" 33 "github.com/docker/go-connections/tlsconfig" 34 ) 35 36 // ProtocolSchemeHTTPV1 is the name of the protocol used for interacting with plugins using this package. 37 const ProtocolSchemeHTTPV1 = "moby.plugins.http/v1" 38 39 // ErrNotImplements is returned if the plugin does not implement the requested driver. 40 var ErrNotImplements = errors.New("Plugin does not implement the requested driver") 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 // ScopedPath returns the path scoped to the plugin's rootfs. 105 // For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host. 106 func (p *Plugin) ScopedPath(s string) string { 107 return s 108 } 109 110 // NewLocalPlugin creates a new local plugin. 111 func NewLocalPlugin(name, addr string) *Plugin { 112 return &Plugin{ 113 name: name, 114 Addr: addr, 115 // TODO: change to nil 116 TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true}, 117 activateWait: sync.NewCond(&sync.Mutex{}), 118 } 119 } 120 121 func (p *Plugin) activate() error { 122 p.activateWait.L.Lock() 123 124 if p.activated() { 125 p.runHandlers() 126 p.activateWait.L.Unlock() 127 return p.activateErr 128 } 129 130 p.activateErr = p.activateWithLock() 131 132 p.runHandlers() 133 p.activateWait.L.Unlock() 134 p.activateWait.Broadcast() 135 return p.activateErr 136 } 137 138 // runHandlers runs the registered handlers for the implemented plugin types 139 // This should only be run after activation, and while the activation lock is held. 140 func (p *Plugin) runHandlers() { 141 if !p.activated() { 142 return 143 } 144 145 handlers.RLock() 146 if !p.handlersRun { 147 for _, iface := range p.Manifest.Implements { 148 hdlrs, handled := handlers.extpointHandlers[iface] 149 if !handled { 150 continue 151 } 152 for _, handler := range hdlrs { 153 handler(p.name, p.client) 154 } 155 } 156 p.handlersRun = true 157 } 158 handlers.RUnlock() 159 } 160 161 // activated returns if the plugin has already been activated. 162 // This should only be called with the activation lock held 163 func (p *Plugin) activated() bool { 164 return p.Manifest != nil 165 } 166 167 func (p *Plugin) activateWithLock() error { 168 c, err := NewClient(p.Addr, p.TLSConfig) 169 if err != nil { 170 return err 171 } 172 p.client = c 173 174 m := new(Manifest) 175 if err = p.client.Call("Plugin.Activate", nil, m); err != nil { 176 return err 177 } 178 179 p.Manifest = m 180 return nil 181 } 182 183 func (p *Plugin) waitActive() error { 184 p.activateWait.L.Lock() 185 for !p.activated() && p.activateErr == nil { 186 p.activateWait.Wait() 187 } 188 p.activateWait.L.Unlock() 189 return p.activateErr 190 } 191 192 func (p *Plugin) implements(kind string) bool { 193 if p.Manifest == nil { 194 return false 195 } 196 for _, driver := range p.Manifest.Implements { 197 if driver == kind { 198 return true 199 } 200 } 201 return false 202 } 203 204 func loadWithRetry(name string, retry bool) (*Plugin, error) { 205 registry := NewLocalRegistry() 206 start := time.Now() 207 var testTimeOut int 208 if name == testNonExistingPlugin { 209 // override the timeout in tests 210 testTimeOut = 2 211 } 212 var retries int 213 for { 214 pl, err := registry.Plugin(name) 215 if err != nil { 216 if !retry { 217 return nil, err 218 } 219 220 timeOff := backoff(retries) 221 if abort(start, timeOff, testTimeOut) { 222 return nil, err 223 } 224 retries++ 225 log.G(context.TODO()).Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 226 time.Sleep(timeOff) 227 continue 228 } 229 230 storage.Lock() 231 if pl, exists := storage.plugins[name]; exists { 232 storage.Unlock() 233 return pl, pl.activate() 234 } 235 storage.plugins[name] = pl 236 storage.Unlock() 237 238 err = pl.activate() 239 240 if err != nil { 241 storage.Lock() 242 delete(storage.plugins, name) 243 storage.Unlock() 244 } 245 246 return pl, err 247 } 248 } 249 250 func get(name string) (*Plugin, error) { 251 storage.Lock() 252 pl, ok := storage.plugins[name] 253 storage.Unlock() 254 if ok { 255 return pl, pl.activate() 256 } 257 return loadWithRetry(name, true) 258 } 259 260 // Get returns the plugin given the specified name and requested implementation. 261 func Get(name, imp string) (*Plugin, error) { 262 if name == "" { 263 return nil, errors.New("Unable to find plugin without name") 264 } 265 pl, err := get(name) 266 if err != nil { 267 return nil, err 268 } 269 if err := pl.waitActive(); err == nil && pl.implements(imp) { 270 log.G(context.TODO()).Debugf("%s implements: %s", name, imp) 271 return pl, nil 272 } 273 return nil, fmt.Errorf("%w: plugin=%q, requested implementation=%q", ErrNotImplements, name, imp) 274 } 275 276 // Handle adds the specified function to the extpointHandlers. 277 func Handle(iface string, fn func(string, *Client)) { 278 handlers.Lock() 279 hdlrs, ok := handlers.extpointHandlers[iface] 280 if !ok { 281 hdlrs = []func(string, *Client){} 282 } 283 284 hdlrs = append(hdlrs, fn) 285 handlers.extpointHandlers[iface] = hdlrs 286 287 storage.Lock() 288 for _, p := range storage.plugins { 289 p.activateWait.L.Lock() 290 if p.activated() && p.implements(iface) { 291 p.handlersRun = false 292 } 293 p.activateWait.L.Unlock() 294 } 295 storage.Unlock() 296 297 handlers.Unlock() 298 } 299 300 // GetAll returns all the plugins for the specified implementation 301 func (l *LocalRegistry) GetAll(imp string) ([]*Plugin, error) { 302 pluginNames, err := l.Scan() 303 if err != nil { 304 return nil, err 305 } 306 307 type plLoad struct { 308 pl *Plugin 309 err error 310 } 311 312 chPl := make(chan *plLoad, len(pluginNames)) 313 var wg sync.WaitGroup 314 for _, name := range pluginNames { 315 storage.Lock() 316 pl, ok := storage.plugins[name] 317 storage.Unlock() 318 if ok { 319 chPl <- &plLoad{pl, nil} 320 continue 321 } 322 323 wg.Add(1) 324 go func(name string) { 325 defer wg.Done() 326 pl, err := loadWithRetry(name, false) 327 chPl <- &plLoad{pl, err} 328 }(name) 329 } 330 331 wg.Wait() 332 close(chPl) 333 334 var out []*Plugin 335 for pl := range chPl { 336 if pl.err != nil { 337 log.G(context.TODO()).Error(pl.err) 338 continue 339 } 340 if err := pl.pl.waitActive(); err == nil && pl.pl.implements(imp) { 341 out = append(out, pl.pl) 342 } 343 } 344 return out, nil 345 }