github.com/yamamoto-febc/docker@v1.9.0/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/docker/pkg/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 logrus.Debugf("%s's manifest: %v", p.Name, m) 100 p.Manifest = m 101 102 for _, iface := range m.Implements { 103 handler, handled := extpointHandlers[iface] 104 if !handled { 105 continue 106 } 107 handler(p.Name, p.Client) 108 } 109 return nil 110 } 111 112 func load(name string) (*Plugin, error) { 113 return loadWithRetry(name, true) 114 } 115 116 func loadWithRetry(name string, retry bool) (*Plugin, error) { 117 registry := newLocalRegistry() 118 start := time.Now() 119 120 var retries int 121 for { 122 pl, err := registry.Plugin(name) 123 if err != nil { 124 if !retry { 125 return nil, err 126 } 127 128 timeOff := backoff(retries) 129 if abort(start, timeOff) { 130 return nil, err 131 } 132 retries++ 133 logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff) 134 time.Sleep(timeOff) 135 continue 136 } 137 138 storage.Lock() 139 storage.plugins[name] = pl 140 storage.Unlock() 141 142 err = pl.activate() 143 144 if err != nil { 145 storage.Lock() 146 delete(storage.plugins, name) 147 storage.Unlock() 148 } 149 150 return pl, err 151 } 152 } 153 154 func get(name string) (*Plugin, error) { 155 storage.Lock() 156 pl, ok := storage.plugins[name] 157 storage.Unlock() 158 if ok { 159 return pl, pl.activate() 160 } 161 return load(name) 162 } 163 164 // Get returns the plugin given the specified name and requested implementation. 165 func Get(name, imp string) (*Plugin, error) { 166 pl, err := get(name) 167 if err != nil { 168 return nil, err 169 } 170 for _, driver := range pl.Manifest.Implements { 171 logrus.Debugf("%s implements: %s", name, driver) 172 if driver == imp { 173 return pl, nil 174 } 175 } 176 return nil, ErrNotImplements 177 } 178 179 // Handle adds the specified function to the extpointHandlers. 180 func Handle(iface string, fn func(string, *Client)) { 181 extpointHandlers[iface] = fn 182 }