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