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