github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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 // GetByName retreives a plugin by name. 34 func (ps *Store) GetByName(name string) (*v2.Plugin, error) { 35 ps.RLock() 36 defer ps.RUnlock() 37 38 id, nameOk := ps.nameToID[name] 39 if !nameOk { 40 return nil, ErrNotFound(name) 41 } 42 43 p, idOk := ps.plugins[id] 44 if !idOk { 45 return nil, ErrNotFound(id) 46 } 47 return p, nil 48 } 49 50 // GetByID retreives a plugin by ID. 51 func (ps *Store) GetByID(id string) (*v2.Plugin, error) { 52 ps.RLock() 53 defer ps.RUnlock() 54 55 p, idOk := ps.plugins[id] 56 if !idOk { 57 return nil, ErrNotFound(id) 58 } 59 return p, nil 60 } 61 62 // GetAll retreives all plugins. 63 func (ps *Store) GetAll() map[string]*v2.Plugin { 64 ps.RLock() 65 defer ps.RUnlock() 66 return ps.plugins 67 } 68 69 // SetAll initialized plugins during daemon restore. 70 func (ps *Store) SetAll(plugins map[string]*v2.Plugin) { 71 ps.Lock() 72 defer ps.Unlock() 73 ps.plugins = plugins 74 } 75 76 func (ps *Store) getByCap(name string, capability string) (*v2.Plugin, error) { 77 ps.RLock() 78 defer ps.RUnlock() 79 80 p, err := ps.GetByName(name) 81 if err != nil { 82 return nil, err 83 } 84 return p.FilterByCap(capability) 85 } 86 87 func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin { 88 ps.RLock() 89 defer ps.RUnlock() 90 91 result := make([]plugingetter.CompatPlugin, 0, 1) 92 for _, p := range ps.plugins { 93 if _, err := p.FilterByCap(capability); err == nil { 94 result = append(result, p) 95 } 96 } 97 return result 98 } 99 100 // SetState sets the active state of the plugin and updates plugindb. 101 func (ps *Store) SetState(p *v2.Plugin, state bool) { 102 ps.Lock() 103 defer ps.Unlock() 104 105 p.PluginObj.Enabled = state 106 ps.updatePluginDB() 107 } 108 109 // Add adds a plugin to memory and plugindb. 110 func (ps *Store) Add(p *v2.Plugin) { 111 ps.Lock() 112 ps.plugins[p.GetID()] = p 113 ps.nameToID[p.Name()] = p.GetID() 114 ps.updatePluginDB() 115 ps.Unlock() 116 } 117 118 // Remove removes a plugin from memory and plugindb. 119 func (ps *Store) Remove(p *v2.Plugin) { 120 ps.Lock() 121 delete(ps.plugins, p.GetID()) 122 delete(ps.nameToID, p.Name()) 123 ps.updatePluginDB() 124 ps.Unlock() 125 } 126 127 // Callers are expected to hold the store lock. 128 func (ps *Store) updatePluginDB() error { 129 jsonData, err := json.Marshal(ps.plugins) 130 if err != nil { 131 logrus.Debugf("Error in json.Marshal: %v", err) 132 return err 133 } 134 ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600) 135 return nil 136 } 137 138 // Get returns a plugin matching the given name and capability. 139 func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) { 140 var ( 141 p *v2.Plugin 142 err error 143 ) 144 145 // Lookup using new model. 146 if ps != nil { 147 fullName := name 148 if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate 149 if reference.IsNameOnly(named) { 150 named = reference.WithDefaultTag(named) 151 } 152 ref, ok := named.(reference.NamedTagged) 153 if !ok { 154 return nil, fmt.Errorf("invalid name: %s", named.String()) 155 } 156 fullName = ref.String() 157 } 158 p, err = ps.GetByName(fullName) 159 if err == nil { 160 p.Lock() 161 p.RefCount += mode 162 p.Unlock() 163 return p.FilterByCap(capability) 164 } 165 if _, ok := err.(ErrNotFound); !ok { 166 return nil, err 167 } 168 } 169 170 // Lookup using legacy model. 171 if allowV1PluginsFallback { 172 p, err := plugins.Get(name, capability) 173 if err != nil { 174 return nil, fmt.Errorf("legacy plugin: %v", err) 175 } 176 return p, nil 177 } 178 179 return nil, err 180 } 181 182 // GetAllByCap returns a list of plugins matching the given capability. 183 func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { 184 result := make([]plugingetter.CompatPlugin, 0, 1) 185 186 /* Daemon start always calls plugin.Init thereby initializing a store. 187 * So store on experimental builds can never be nil, even while 188 * handling legacy plugins. However, there are legacy plugin unit 189 * tests where the volume subsystem directly talks with the plugin, 190 * bypassing the daemon. For such tests, this check is necessary. 191 */ 192 if ps != nil { 193 ps.RLock() 194 result = ps.getAllByCap(capability) 195 ps.RUnlock() 196 } 197 198 // Lookup with legacy model 199 if allowV1PluginsFallback { 200 pl, err := plugins.GetAll(capability) 201 if err != nil { 202 return nil, fmt.Errorf("legacy plugin: %v", err) 203 } 204 for _, p := range pl { 205 result = append(result, p) 206 } 207 } 208 return result, nil 209 } 210 211 // Handle sets a callback for a given capability. It is only used by network 212 // and ipam drivers during plugin registration. The callback registers the 213 // driver with the subsystem (network, ipam). 214 func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) { 215 pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion) 216 217 // Register callback with new plugin model. 218 ps.Lock() 219 handlers, ok := ps.handlers[pluginType] 220 if !ok { 221 handlers = []func(string, *plugins.Client){} 222 } 223 handlers = append(handlers, callback) 224 ps.handlers[pluginType] = handlers 225 ps.Unlock() 226 227 // Register callback with legacy plugin model. 228 if allowV1PluginsFallback { 229 plugins.Handle(capability, callback) 230 } 231 } 232 233 // CallHandler calls the registered callback. It is invoked during plugin enable. 234 func (ps *Store) CallHandler(p *v2.Plugin) { 235 for _, typ := range p.GetTypes() { 236 for _, handler := range ps.handlers[typ.String()] { 237 handler(p.Name(), p.Client()) 238 } 239 } 240 }