github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/plugin/store/store.go (about) 1 package store 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/Sirupsen/logrus" 9 "github.com/docker/docker/pkg/ioutils" 10 "github.com/docker/docker/pkg/plugingetter" 11 "github.com/docker/docker/pkg/plugins" 12 "github.com/docker/docker/plugin/v2" 13 "github.com/docker/docker/reference" 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. eg "networkdriver/2.0". 25 */ 26 const defaultAPIVersion string = "1.0" 27 28 // ErrNotFound indicates that a plugin was not found locally. 29 type ErrNotFound string 30 31 func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) } 32 33 // ErrAmbiguous indicates that a plugin was not found locally. 34 type ErrAmbiguous string 35 36 func (name ErrAmbiguous) Error() string { 37 return fmt.Sprintf("multiple plugins found for %q", string(name)) 38 } 39 40 // GetByName retreives a plugin by name. 41 func (ps *Store) GetByName(name string) (*v2.Plugin, error) { 42 ps.RLock() 43 defer ps.RUnlock() 44 45 id, nameOk := ps.nameToID[name] 46 if !nameOk { 47 return nil, ErrNotFound(name) 48 } 49 50 p, idOk := ps.plugins[id] 51 if !idOk { 52 return nil, ErrNotFound(id) 53 } 54 return p, nil 55 } 56 57 // GetByID retreives a plugin by ID. 58 func (ps *Store) GetByID(id string) (*v2.Plugin, error) { 59 ps.RLock() 60 defer ps.RUnlock() 61 62 p, idOk := ps.plugins[id] 63 if !idOk { 64 return nil, ErrNotFound(id) 65 } 66 return p, nil 67 } 68 69 // GetAll retreives all plugins. 70 func (ps *Store) GetAll() map[string]*v2.Plugin { 71 ps.RLock() 72 defer ps.RUnlock() 73 return ps.plugins 74 } 75 76 // SetAll initialized plugins during daemon restore. 77 func (ps *Store) SetAll(plugins map[string]*v2.Plugin) { 78 ps.Lock() 79 defer ps.Unlock() 80 ps.plugins = plugins 81 } 82 83 func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin { 84 ps.RLock() 85 defer ps.RUnlock() 86 87 result := make([]plugingetter.CompatPlugin, 0, 1) 88 for _, p := range ps.plugins { 89 if p.IsEnabled() { 90 if _, err := p.FilterByCap(capability); err == nil { 91 result = append(result, p) 92 } 93 } 94 } 95 return result 96 } 97 98 // SetState sets the active state of the plugin and updates plugindb. 99 func (ps *Store) SetState(p *v2.Plugin, state bool) { 100 ps.Lock() 101 defer ps.Unlock() 102 103 p.PluginObj.Enabled = state 104 ps.updatePluginDB() 105 } 106 107 // Add adds a plugin to memory and plugindb. 108 // An error will be returned if there is a collision. 109 func (ps *Store) Add(p *v2.Plugin) error { 110 ps.Lock() 111 defer ps.Unlock() 112 113 if v, exist := ps.plugins[p.GetID()]; exist { 114 return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name()) 115 } 116 if _, exist := ps.nameToID[p.Name()]; exist { 117 return fmt.Errorf("plugin %q already exists", p.Name()) 118 } 119 ps.plugins[p.GetID()] = p 120 ps.nameToID[p.Name()] = p.GetID() 121 ps.updatePluginDB() 122 return nil 123 } 124 125 // Update updates a plugin to memory and plugindb. 126 func (ps *Store) Update(p *v2.Plugin) { 127 ps.Lock() 128 defer ps.Unlock() 129 130 ps.plugins[p.GetID()] = p 131 ps.nameToID[p.Name()] = p.GetID() 132 ps.updatePluginDB() 133 } 134 135 // Remove removes a plugin from memory and plugindb. 136 func (ps *Store) Remove(p *v2.Plugin) { 137 ps.Lock() 138 delete(ps.plugins, p.GetID()) 139 delete(ps.nameToID, p.Name()) 140 ps.updatePluginDB() 141 ps.Unlock() 142 } 143 144 // Callers are expected to hold the store lock. 145 func (ps *Store) updatePluginDB() error { 146 jsonData, err := json.Marshal(ps.plugins) 147 if err != nil { 148 logrus.Debugf("Error in json.Marshal: %v", err) 149 return err 150 } 151 ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600) 152 return nil 153 } 154 155 // Get returns an enabled plugin matching the given name and capability. 156 func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) { 157 var ( 158 p *v2.Plugin 159 err error 160 ) 161 162 // Lookup using new model. 163 if ps != nil { 164 fullName := name 165 if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate 166 if reference.IsNameOnly(named) { 167 named = reference.WithDefaultTag(named) 168 } 169 ref, ok := named.(reference.NamedTagged) 170 if !ok { 171 return nil, fmt.Errorf("invalid name: %s", named.String()) 172 } 173 fullName = ref.String() 174 } 175 p, err = ps.GetByName(fullName) 176 if err == nil { 177 p.Lock() 178 p.RefCount += mode 179 p.Unlock() 180 if p.IsEnabled() { 181 return p.FilterByCap(capability) 182 } 183 // Plugin was found but it is disabled, so we should not fall back to legacy plugins 184 // but we should error out right away 185 return nil, ErrNotFound(fullName) 186 } 187 if _, ok := err.(ErrNotFound); !ok { 188 return nil, err 189 } 190 } 191 192 // Lookup using legacy model. 193 if allowV1PluginsFallback { 194 p, err := plugins.Get(name, capability) 195 if err != nil { 196 return nil, fmt.Errorf("legacy plugin: %v", err) 197 } 198 return p, nil 199 } 200 201 return nil, err 202 } 203 204 // GetAllByCap returns a list of enabled plugins matching the given capability. 205 func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { 206 result := make([]plugingetter.CompatPlugin, 0, 1) 207 208 /* Daemon start always calls plugin.Init thereby initializing a store. 209 * So store on experimental builds can never be nil, even while 210 * handling legacy plugins. However, there are legacy plugin unit 211 * tests where the volume subsystem directly talks with the plugin, 212 * bypassing the daemon. For such tests, this check is necessary. 213 */ 214 if ps != nil { 215 ps.RLock() 216 result = ps.getAllByCap(capability) 217 ps.RUnlock() 218 } 219 220 // Lookup with legacy model 221 if allowV1PluginsFallback { 222 pl, err := plugins.GetAll(capability) 223 if err != nil { 224 return nil, fmt.Errorf("legacy plugin: %v", err) 225 } 226 for _, p := range pl { 227 result = append(result, p) 228 } 229 } 230 return result, nil 231 } 232 233 // Handle sets a callback for a given capability. It is only used by network 234 // and ipam drivers during plugin registration. The callback registers the 235 // driver with the subsystem (network, ipam). 236 func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) { 237 pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion) 238 239 // Register callback with new plugin model. 240 ps.Lock() 241 handlers, ok := ps.handlers[pluginType] 242 if !ok { 243 handlers = []func(string, *plugins.Client){} 244 } 245 handlers = append(handlers, callback) 246 ps.handlers[pluginType] = handlers 247 ps.Unlock() 248 249 // Register callback with legacy plugin model. 250 if allowV1PluginsFallback { 251 plugins.Handle(capability, callback) 252 } 253 } 254 255 // CallHandler calls the registered callback. It is invoked during plugin enable. 256 func (ps *Store) CallHandler(p *v2.Plugin) { 257 for _, typ := range p.GetTypes() { 258 for _, handler := range ps.handlers[typ.String()] { 259 handler(p.Name(), p.Client()) 260 } 261 } 262 } 263 264 // Search retreives a plugin by ID Prefix 265 // If no plugin is found, then ErrNotFound is returned 266 // If multiple plugins are found, then ErrAmbiguous is returned 267 func (ps *Store) Search(partialID string) (*v2.Plugin, error) { 268 ps.RLock() 269 defer ps.RUnlock() 270 271 var found *v2.Plugin 272 for id, p := range ps.plugins { 273 if strings.HasPrefix(id, partialID) { 274 if found != nil { 275 return nil, ErrAmbiguous(partialID) 276 } 277 found = p 278 } 279 } 280 if found == nil { 281 return nil, ErrNotFound(partialID) 282 } 283 return found, nil 284 }