github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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 activatErr error 69 activateOnce sync.Once 70 } 71 72 func newLocalPlugin(name, addr string) *Plugin { 73 return &Plugin{ 74 Name: name, 75 Addr: addr, 76 TLSConfig: tlsconfig.Options{InsecureSkipVerify: true}, 77 } 78 } 79 80 func (p *Plugin) activate() error { 81 p.activateOnce.Do(func() { 82 p.activatErr = p.activateWithLock() 83 }) 84 return p.activatErr 85 } 86 87 func (p *Plugin) activateWithLock() error { 88 c, err := NewClient(p.Addr, p.TLSConfig) 89 if err != nil { 90 return err 91 } 92 p.Client = c 93 94 m := new(Manifest) 95 if err = p.Client.Call("Plugin.Activate", nil, m); err != nil { 96 return err 97 } 98 99 p.Manifest = m 100 101 for _, iface := range m.Implements { 102 handler, handled := extpointHandlers[iface] 103 if !handled { 104 continue 105 } 106 handler(p.Name, p.Client) 107 } 108 return nil 109 } 110 111 func (p *Plugin) implements(kind string) bool { 112 for _, driver := range p.Manifest.Implements { 113 if driver == kind { 114 return true 115 } 116 } 117 return false 118 } 119 120 func load(name string) (*Plugin, error) { 121 return loadWithRetry(name, true) 122 } 123 124 func loadWithRetry(name string, retry bool) (*Plugin, error) { 125 registry := newLocalRegistry() 126 start := time.Now() 127 128 var retries int 129 for { 130 pl, err := registry.Plugin(name) 131 if err != nil { 132 if !retry { 133 return nil, err 134 } 135 136 timeOff := backoff(retries) 137 if abort(start, timeOff) { 138 return nil, err 139 } 140 retries++ 141 logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 142 time.Sleep(timeOff) 143 continue 144 } 145 146 storage.Lock() 147 storage.plugins[name] = pl 148 storage.Unlock() 149 150 err = pl.activate() 151 152 if err != nil { 153 storage.Lock() 154 delete(storage.plugins, name) 155 storage.Unlock() 156 } 157 158 return pl, err 159 } 160 } 161 162 func get(name string) (*Plugin, error) { 163 storage.Lock() 164 pl, ok := storage.plugins[name] 165 storage.Unlock() 166 if ok { 167 return pl, pl.activate() 168 } 169 return load(name) 170 } 171 172 // Get returns the plugin given the specified name and requested implementation. 173 func Get(name, imp string) (*Plugin, error) { 174 pl, err := get(name) 175 if err != nil { 176 return nil, err 177 } 178 if pl.implements(imp) { 179 logrus.Debugf("%s implements: %s", name, imp) 180 return pl, nil 181 } 182 return nil, ErrNotImplements 183 } 184 185 // Handle adds the specified function to the extpointHandlers. 186 func Handle(iface string, fn func(string, *Client)) { 187 extpointHandlers[iface] = fn 188 } 189 190 // GetAll returns all the plugins for the specified implementation 191 func GetAll(imp string) ([]*Plugin, error) { 192 pluginNames, err := Scan() 193 if err != nil { 194 return nil, err 195 } 196 197 type plLoad struct { 198 pl *Plugin 199 err error 200 } 201 202 chPl := make(chan plLoad, len(pluginNames)) 203 for _, name := range pluginNames { 204 go func(name string) { 205 pl, err := loadWithRetry(name, false) 206 chPl <- plLoad{pl, err} 207 }(name) 208 } 209 210 var out []*Plugin 211 for i := 0; i < len(pluginNames); i++ { 212 pl := <-chPl 213 if pl.err != nil { 214 logrus.Error(err) 215 continue 216 } 217 if pl.pl.implements(imp) { 218 out = append(out, pl.pl) 219 } 220 } 221 return out, nil 222 }