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