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