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