github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/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 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 24 25 import ( 26 "errors" 27 "sync" 28 "time" 29 30 "github.com/Sirupsen/logrus" 31 "github.com/docker/go-connections/tlsconfig" 32 ) 33 34 var ( 35 // ErrNotImplements is returned if the plugin does not implement the requested driver. 36 ErrNotImplements = errors.New("Plugin does not implement the requested driver") 37 ) 38 39 type plugins struct { 40 sync.Mutex 41 plugins map[string]*Plugin 42 } 43 44 var ( 45 storage = plugins{plugins: make(map[string]*Plugin)} 46 extpointHandlers = make(map[string]func(string, *Client)) 47 ) 48 49 // Manifest lists what a plugin implements. 50 type Manifest struct { 51 // List of subsystem the plugin implements. 52 Implements []string 53 } 54 55 // Plugin is the definition of a docker plugin. 56 type Plugin struct { 57 // Name of the plugin 58 Name string `json:"-"` 59 // Address of the plugin 60 Addr string 61 // TLS configuration of the plugin 62 TLSConfig tlsconfig.Options 63 // Client attached to the plugin 64 Client *Client `json:"-"` 65 // Manifest of the plugin (see above) 66 Manifest *Manifest `json:"-"` 67 68 // error produced by activation 69 activateErr error 70 // specifies if the activation sequence is completed (not if it is successful or not) 71 activated bool 72 // wait for activation to finish 73 activateWait *sync.Cond 74 } 75 76 func newLocalPlugin(name, addr string) *Plugin { 77 return &Plugin{ 78 Name: name, 79 Addr: addr, 80 TLSConfig: tlsconfig.Options{InsecureSkipVerify: true}, 81 activateWait: sync.NewCond(&sync.Mutex{}), 82 } 83 } 84 85 func (p *Plugin) activate() error { 86 p.activateWait.L.Lock() 87 if p.activated { 88 p.activateWait.L.Unlock() 89 return p.activateErr 90 } 91 92 p.activateErr = p.activateWithLock() 93 p.activated = true 94 95 p.activateWait.L.Unlock() 96 p.activateWait.Broadcast() 97 return p.activateErr 98 } 99 100 func (p *Plugin) activateWithLock() error { 101 c, err := NewClient(p.Addr, p.TLSConfig) 102 if err != nil { 103 return err 104 } 105 p.Client = c 106 107 m := new(Manifest) 108 if err = p.Client.Call("Plugin.Activate", nil, m); err != nil { 109 return err 110 } 111 112 p.Manifest = m 113 114 for _, iface := range m.Implements { 115 handler, handled := extpointHandlers[iface] 116 if !handled { 117 continue 118 } 119 handler(p.Name, p.Client) 120 } 121 return nil 122 } 123 124 func (p *Plugin) waitActive() error { 125 p.activateWait.L.Lock() 126 for !p.activated { 127 p.activateWait.Wait() 128 } 129 p.activateWait.L.Unlock() 130 return p.activateErr 131 } 132 133 func (p *Plugin) implements(kind string) bool { 134 if err := p.waitActive(); err != nil { 135 return false 136 } 137 for _, driver := range p.Manifest.Implements { 138 if driver == kind { 139 return true 140 } 141 } 142 return false 143 } 144 145 func load(name string) (*Plugin, error) { 146 return loadWithRetry(name, true) 147 } 148 149 func loadWithRetry(name string, retry bool) (*Plugin, error) { 150 registry := newLocalRegistry() 151 start := time.Now() 152 153 var retries int 154 for { 155 pl, err := registry.Plugin(name) 156 if err != nil { 157 if !retry { 158 return nil, err 159 } 160 161 timeOff := backoff(retries) 162 if abort(start, timeOff) { 163 return nil, err 164 } 165 retries++ 166 logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 167 time.Sleep(timeOff) 168 continue 169 } 170 171 storage.Lock() 172 storage.plugins[name] = pl 173 storage.Unlock() 174 175 err = pl.activate() 176 177 if err != nil { 178 storage.Lock() 179 delete(storage.plugins, name) 180 storage.Unlock() 181 } 182 183 return pl, err 184 } 185 } 186 187 func get(name string) (*Plugin, error) { 188 storage.Lock() 189 pl, ok := storage.plugins[name] 190 storage.Unlock() 191 if ok { 192 return pl, pl.activate() 193 } 194 return load(name) 195 } 196 197 // Get returns the plugin given the specified name and requested implementation. 198 func Get(name, imp string) (*Plugin, error) { 199 pl, err := get(name) 200 if err != nil { 201 return nil, err 202 } 203 if pl.implements(imp) { 204 logrus.Debugf("%s implements: %s", name, imp) 205 return pl, nil 206 } 207 return nil, ErrNotImplements 208 } 209 210 // Handle adds the specified function to the extpointHandlers. 211 func Handle(iface string, fn func(string, *Client)) { 212 extpointHandlers[iface] = fn 213 } 214 215 // GetAll returns all the plugins for the specified implementation 216 func GetAll(imp string) ([]*Plugin, error) { 217 pluginNames, err := Scan() 218 if err != nil { 219 return nil, err 220 } 221 222 type plLoad struct { 223 pl *Plugin 224 err error 225 } 226 227 chPl := make(chan *plLoad, len(pluginNames)) 228 var wg sync.WaitGroup 229 for _, name := range pluginNames { 230 if pl, ok := storage.plugins[name]; ok { 231 chPl <- &plLoad{pl, nil} 232 continue 233 } 234 235 wg.Add(1) 236 go func(name string) { 237 defer wg.Done() 238 pl, err := loadWithRetry(name, false) 239 chPl <- &plLoad{pl, err} 240 }(name) 241 } 242 243 wg.Wait() 244 close(chPl) 245 246 var out []*Plugin 247 for pl := range chPl { 248 if pl.err != nil { 249 logrus.Error(pl.err) 250 continue 251 } 252 if pl.pl.implements(imp) { 253 out = append(out, pl.pl) 254 } 255 } 256 return out, nil 257 }