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