github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/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 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 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 // Name returns the name of the plugin. 77 func (p *Plugin) Name() string { 78 return p.name 79 } 80 81 // Client returns a ready-to-use plugin client that can be used to communicate with the plugin. 82 func (p *Plugin) Client() *Client { 83 return p.client 84 } 85 86 // IsV1 returns true for V1 plugins and false otherwise. 87 func (p *Plugin) IsV1() bool { 88 return true 89 } 90 91 // NewLocalPlugin creates a new local plugin. 92 func NewLocalPlugin(name, addr string) *Plugin { 93 return &Plugin{ 94 name: name, 95 Addr: addr, 96 // TODO: change to nil 97 TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true}, 98 activateWait: sync.NewCond(&sync.Mutex{}), 99 } 100 } 101 102 func (p *Plugin) activate() error { 103 p.activateWait.L.Lock() 104 if p.activated { 105 p.activateWait.L.Unlock() 106 return p.activateErr 107 } 108 109 p.activateErr = p.activateWithLock() 110 p.activated = true 111 112 p.activateWait.L.Unlock() 113 p.activateWait.Broadcast() 114 return p.activateErr 115 } 116 117 func (p *Plugin) activateWithLock() error { 118 c, err := NewClient(p.Addr, p.TLSConfig) 119 if err != nil { 120 return err 121 } 122 p.client = c 123 124 m := new(Manifest) 125 if err = p.client.Call("Plugin.Activate", nil, m); err != nil { 126 return err 127 } 128 129 p.Manifest = m 130 131 for _, iface := range m.Implements { 132 handler, handled := extpointHandlers[iface] 133 if !handled { 134 continue 135 } 136 handler(p.name, p.client) 137 } 138 return nil 139 } 140 141 func (p *Plugin) waitActive() error { 142 p.activateWait.L.Lock() 143 for !p.activated { 144 p.activateWait.Wait() 145 } 146 p.activateWait.L.Unlock() 147 return p.activateErr 148 } 149 150 func (p *Plugin) implements(kind string) bool { 151 if err := p.waitActive(); err != nil { 152 return false 153 } 154 for _, driver := range p.Manifest.Implements { 155 if driver == kind { 156 return true 157 } 158 } 159 return false 160 } 161 162 func load(name string) (*Plugin, error) { 163 return loadWithRetry(name, true) 164 } 165 166 func loadWithRetry(name string, retry bool) (*Plugin, error) { 167 registry := newLocalRegistry() 168 start := time.Now() 169 170 var retries int 171 for { 172 pl, err := registry.Plugin(name) 173 if err != nil { 174 if !retry { 175 return nil, err 176 } 177 178 timeOff := backoff(retries) 179 if abort(start, timeOff) { 180 return nil, err 181 } 182 retries++ 183 logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 184 time.Sleep(timeOff) 185 continue 186 } 187 188 storage.Lock() 189 storage.plugins[name] = pl 190 storage.Unlock() 191 192 err = pl.activate() 193 194 if err != nil { 195 storage.Lock() 196 delete(storage.plugins, name) 197 storage.Unlock() 198 } 199 200 return pl, err 201 } 202 } 203 204 func get(name string) (*Plugin, error) { 205 storage.Lock() 206 pl, ok := storage.plugins[name] 207 storage.Unlock() 208 if ok { 209 return pl, pl.activate() 210 } 211 return load(name) 212 } 213 214 // Get returns the plugin given the specified name and requested implementation. 215 func Get(name, imp string) (*Plugin, error) { 216 pl, err := get(name) 217 if err != nil { 218 return nil, err 219 } 220 if pl.implements(imp) { 221 logrus.Debugf("%s implements: %s", name, imp) 222 return pl, nil 223 } 224 return nil, ErrNotImplements 225 } 226 227 // Handle adds the specified function to the extpointHandlers. 228 func Handle(iface string, fn func(string, *Client)) { 229 extpointHandlers[iface] = fn 230 } 231 232 // GetAll returns all the plugins for the specified implementation 233 func GetAll(imp string) ([]*Plugin, error) { 234 pluginNames, err := Scan() 235 if err != nil { 236 return nil, err 237 } 238 239 type plLoad struct { 240 pl *Plugin 241 err error 242 } 243 244 chPl := make(chan *plLoad, len(pluginNames)) 245 var wg sync.WaitGroup 246 for _, name := range pluginNames { 247 if pl, ok := storage.plugins[name]; ok { 248 chPl <- &plLoad{pl, nil} 249 continue 250 } 251 252 wg.Add(1) 253 go func(name string) { 254 defer wg.Done() 255 pl, err := loadWithRetry(name, false) 256 chPl <- &plLoad{pl, err} 257 }(name) 258 } 259 260 wg.Wait() 261 close(chPl) 262 263 var out []*Plugin 264 for pl := range chPl { 265 if pl.err != nil { 266 logrus.Error(pl.err) 267 continue 268 } 269 if pl.pl.implements(imp) { 270 out = append(out, pl.pl) 271 } 272 } 273 return out, nil 274 }