github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/plugin/store.go (about) 1 package plugin 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/Sirupsen/logrus" 8 "github.com/docker/docker/pkg/plugingetter" 9 "github.com/docker/docker/pkg/plugins" 10 "github.com/docker/docker/plugin/v2" 11 "github.com/docker/docker/reference" 12 "github.com/pkg/errors" 13 ) 14 15 /* allowV1PluginsFallback determines daemon's support for V1 plugins. 16 * When the time comes to remove support for V1 plugins, flipping 17 * this bool is all that will be needed. 18 */ 19 const allowV1PluginsFallback bool = true 20 21 /* defaultAPIVersion is the version of the plugin API for volume, network, 22 IPAM and authz. This is a very stable API. When we update this API, then 23 pluginType should include a version. e.g. "networkdriver/2.0". 24 */ 25 const defaultAPIVersion string = "1.0" 26 27 // ErrNotFound indicates that a plugin was not found locally. 28 type ErrNotFound string 29 30 func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) } 31 32 // ErrAmbiguous indicates that a plugin was not found locally. 33 type ErrAmbiguous string 34 35 func (name ErrAmbiguous) Error() string { 36 return fmt.Sprintf("multiple plugins found for %q", string(name)) 37 } 38 39 // GetV2Plugin retrieves a plugin by name, id or partial ID. 40 func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) { 41 ps.RLock() 42 defer ps.RUnlock() 43 44 id, err := ps.resolvePluginID(refOrID) 45 if err != nil { 46 return nil, err 47 } 48 49 p, idOk := ps.plugins[id] 50 if !idOk { 51 return nil, errors.WithStack(ErrNotFound(id)) 52 } 53 54 return p, nil 55 } 56 57 // validateName returns error if name is already reserved. always call with lock and full name 58 func (ps *Store) validateName(name string) error { 59 for _, p := range ps.plugins { 60 if p.Name() == name { 61 return errors.Errorf("plugin %q already exists", name) 62 } 63 } 64 return nil 65 } 66 67 // GetAll retrieves all plugins. 68 func (ps *Store) GetAll() map[string]*v2.Plugin { 69 ps.RLock() 70 defer ps.RUnlock() 71 return ps.plugins 72 } 73 74 // SetAll initialized plugins during daemon restore. 75 func (ps *Store) SetAll(plugins map[string]*v2.Plugin) { 76 ps.Lock() 77 defer ps.Unlock() 78 ps.plugins = plugins 79 } 80 81 func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin { 82 ps.RLock() 83 defer ps.RUnlock() 84 85 result := make([]plugingetter.CompatPlugin, 0, 1) 86 for _, p := range ps.plugins { 87 if p.IsEnabled() { 88 if _, err := p.FilterByCap(capability); err == nil { 89 result = append(result, p) 90 } 91 } 92 } 93 return result 94 } 95 96 // SetState sets the active state of the plugin and updates plugindb. 97 func (ps *Store) SetState(p *v2.Plugin, state bool) { 98 ps.Lock() 99 defer ps.Unlock() 100 101 p.PluginObj.Enabled = state 102 } 103 104 // Add adds a plugin to memory and plugindb. 105 // An error will be returned if there is a collision. 106 func (ps *Store) Add(p *v2.Plugin) error { 107 ps.Lock() 108 defer ps.Unlock() 109 110 if v, exist := ps.plugins[p.GetID()]; exist { 111 return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name()) 112 } 113 ps.plugins[p.GetID()] = p 114 return nil 115 } 116 117 // Remove removes a plugin from memory and plugindb. 118 func (ps *Store) Remove(p *v2.Plugin) { 119 ps.Lock() 120 delete(ps.plugins, p.GetID()) 121 ps.Unlock() 122 } 123 124 // Get returns an enabled plugin matching the given name and capability. 125 func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) { 126 var ( 127 p *v2.Plugin 128 err error 129 ) 130 131 // Lookup using new model. 132 if ps != nil { 133 p, err = ps.GetV2Plugin(name) 134 if err == nil { 135 p.AddRefCount(mode) 136 if p.IsEnabled() { 137 return p.FilterByCap(capability) 138 } 139 // Plugin was found but it is disabled, so we should not fall back to legacy plugins 140 // but we should error out right away 141 return nil, ErrNotFound(name) 142 } 143 if _, ok := errors.Cause(err).(ErrNotFound); !ok { 144 return nil, err 145 } 146 } 147 148 // Lookup using legacy model. 149 if allowV1PluginsFallback { 150 p, err := plugins.Get(name, capability) 151 if err != nil { 152 return nil, fmt.Errorf("legacy plugin: %v", err) 153 } 154 return p, nil 155 } 156 157 return nil, err 158 } 159 160 // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability. 161 func (ps *Store) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin { 162 return ps.getAllByCap(capability) 163 } 164 165 // GetAllByCap returns a list of enabled plugins matching the given capability. 166 func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { 167 result := make([]plugingetter.CompatPlugin, 0, 1) 168 169 /* Daemon start always calls plugin.Init thereby initializing a store. 170 * So store on experimental builds can never be nil, even while 171 * handling legacy plugins. However, there are legacy plugin unit 172 * tests where the volume subsystem directly talks with the plugin, 173 * bypassing the daemon. For such tests, this check is necessary. 174 */ 175 if ps != nil { 176 ps.RLock() 177 result = ps.getAllByCap(capability) 178 ps.RUnlock() 179 } 180 181 // Lookup with legacy model 182 if allowV1PluginsFallback { 183 pl, err := plugins.GetAll(capability) 184 if err != nil { 185 return nil, fmt.Errorf("legacy plugin: %v", err) 186 } 187 for _, p := range pl { 188 result = append(result, p) 189 } 190 } 191 return result, nil 192 } 193 194 // Handle sets a callback for a given capability. It is only used by network 195 // and ipam drivers during plugin registration. The callback registers the 196 // driver with the subsystem (network, ipam). 197 func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) { 198 pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion) 199 200 // Register callback with new plugin model. 201 ps.Lock() 202 handlers, ok := ps.handlers[pluginType] 203 if !ok { 204 handlers = []func(string, *plugins.Client){} 205 } 206 handlers = append(handlers, callback) 207 ps.handlers[pluginType] = handlers 208 ps.Unlock() 209 210 // Register callback with legacy plugin model. 211 if allowV1PluginsFallback { 212 plugins.Handle(capability, callback) 213 } 214 } 215 216 // CallHandler calls the registered callback. It is invoked during plugin enable. 217 func (ps *Store) CallHandler(p *v2.Plugin) { 218 for _, typ := range p.GetTypes() { 219 for _, handler := range ps.handlers[typ.String()] { 220 handler(p.Name(), p.Client()) 221 } 222 } 223 } 224 225 func (ps *Store) resolvePluginID(idOrName string) (string, error) { 226 ps.RLock() // todo: fix 227 defer ps.RUnlock() 228 229 if validFullID.MatchString(idOrName) { 230 return idOrName, nil 231 } 232 233 ref, err := reference.ParseNamed(idOrName) 234 if err != nil { 235 return "", errors.WithStack(ErrNotFound(idOrName)) 236 } 237 if _, ok := ref.(reference.Canonical); ok { 238 logrus.Warnf("canonical references cannot be resolved: %v", ref.String()) 239 return "", errors.WithStack(ErrNotFound(idOrName)) 240 } 241 242 fullRef := reference.WithDefaultTag(ref) 243 244 for _, p := range ps.plugins { 245 if p.PluginObj.Name == fullRef.String() { 246 return p.PluginObj.ID, nil 247 } 248 } 249 250 var found *v2.Plugin 251 for id, p := range ps.plugins { // this can be optimized 252 if strings.HasPrefix(id, idOrName) { 253 if found != nil { 254 return "", errors.WithStack(ErrAmbiguous(idOrName)) 255 } 256 found = p 257 } 258 } 259 if found == nil { 260 return "", errors.WithStack(ErrNotFound(idOrName)) 261 } 262 return found.PluginObj.ID, nil 263 }