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