123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- package store
- import (
- "encoding/json"
- "fmt"
- "strings"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/pkg/ioutils"
- "github.com/docker/docker/pkg/plugingetter"
- "github.com/docker/docker/pkg/plugins"
- "github.com/docker/docker/plugin/v2"
- "github.com/docker/docker/reference"
- )
- /* allowV1PluginsFallback determines daemon's support for V1 plugins.
- * When the time comes to remove support for V1 plugins, flipping
- * this bool is all that will be needed.
- */
- const allowV1PluginsFallback bool = true
- /* defaultAPIVersion is the version of the plugin API for volume, network,
- IPAM and authz. This is a very stable API. When we update this API, then
- pluginType should include a version. eg "networkdriver/2.0".
- */
- const defaultAPIVersion string = "1.0"
- // ErrNotFound indicates that a plugin was not found locally.
- type ErrNotFound string
- func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
- // ErrAmbiguous indicates that a plugin was not found locally.
- type ErrAmbiguous string
- func (name ErrAmbiguous) Error() string {
- return fmt.Sprintf("multiple plugins found for %q", string(name))
- }
- // GetByName retreives a plugin by name.
- func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
- ps.RLock()
- defer ps.RUnlock()
- id, nameOk := ps.nameToID[name]
- if !nameOk {
- return nil, ErrNotFound(name)
- }
- p, idOk := ps.plugins[id]
- if !idOk {
- return nil, ErrNotFound(id)
- }
- return p, nil
- }
- // GetByID retreives a plugin by ID.
- func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
- ps.RLock()
- defer ps.RUnlock()
- p, idOk := ps.plugins[id]
- if !idOk {
- return nil, ErrNotFound(id)
- }
- return p, nil
- }
- // GetAll retreives all plugins.
- func (ps *Store) GetAll() map[string]*v2.Plugin {
- ps.RLock()
- defer ps.RUnlock()
- return ps.plugins
- }
- // SetAll initialized plugins during daemon restore.
- func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
- ps.Lock()
- defer ps.Unlock()
- ps.plugins = plugins
- }
- func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
- ps.RLock()
- defer ps.RUnlock()
- result := make([]plugingetter.CompatPlugin, 0, 1)
- for _, p := range ps.plugins {
- if p.IsEnabled() {
- if _, err := p.FilterByCap(capability); err == nil {
- result = append(result, p)
- }
- }
- }
- return result
- }
- // SetState sets the active state of the plugin and updates plugindb.
- func (ps *Store) SetState(p *v2.Plugin, state bool) {
- ps.Lock()
- defer ps.Unlock()
- p.PluginObj.Enabled = state
- ps.updatePluginDB()
- }
- // Add adds a plugin to memory and plugindb.
- // An error will be returned if there is a collision.
- func (ps *Store) Add(p *v2.Plugin) error {
- ps.Lock()
- defer ps.Unlock()
- if v, exist := ps.plugins[p.GetID()]; exist {
- return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
- }
- if _, exist := ps.nameToID[p.Name()]; exist {
- return fmt.Errorf("plugin %q already exists", p.Name())
- }
- ps.plugins[p.GetID()] = p
- ps.nameToID[p.Name()] = p.GetID()
- ps.updatePluginDB()
- return nil
- }
- // Update updates a plugin to memory and plugindb.
- func (ps *Store) Update(p *v2.Plugin) {
- ps.Lock()
- defer ps.Unlock()
- ps.plugins[p.GetID()] = p
- ps.nameToID[p.Name()] = p.GetID()
- ps.updatePluginDB()
- }
- // Remove removes a plugin from memory and plugindb.
- func (ps *Store) Remove(p *v2.Plugin) {
- ps.Lock()
- delete(ps.plugins, p.GetID())
- delete(ps.nameToID, p.Name())
- ps.updatePluginDB()
- ps.Unlock()
- }
- // Callers are expected to hold the store lock.
- func (ps *Store) updatePluginDB() error {
- jsonData, err := json.Marshal(ps.plugins)
- if err != nil {
- logrus.Debugf("Error in json.Marshal: %v", err)
- return err
- }
- ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
- return nil
- }
- // Get returns an enabled plugin matching the given name and capability.
- func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
- var (
- p *v2.Plugin
- err error
- )
- // Lookup using new model.
- if ps != nil {
- fullName := name
- if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
- if reference.IsNameOnly(named) {
- named = reference.WithDefaultTag(named)
- }
- ref, ok := named.(reference.NamedTagged)
- if !ok {
- return nil, fmt.Errorf("invalid name: %s", named.String())
- }
- fullName = ref.String()
- }
- p, err = ps.GetByName(fullName)
- if err == nil {
- p.Lock()
- p.RefCount += mode
- p.Unlock()
- if p.IsEnabled() {
- return p.FilterByCap(capability)
- }
- // Plugin was found but it is disabled, so we should not fall back to legacy plugins
- // but we should error out right away
- return nil, ErrNotFound(fullName)
- }
- if _, ok := err.(ErrNotFound); !ok {
- return nil, err
- }
- }
- // Lookup using legacy model.
- if allowV1PluginsFallback {
- p, err := plugins.Get(name, capability)
- if err != nil {
- return nil, fmt.Errorf("legacy plugin: %v", err)
- }
- return p, nil
- }
- return nil, err
- }
- // GetAllByCap returns a list of enabled plugins matching the given capability.
- func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
- result := make([]plugingetter.CompatPlugin, 0, 1)
- /* Daemon start always calls plugin.Init thereby initializing a store.
- * So store on experimental builds can never be nil, even while
- * handling legacy plugins. However, there are legacy plugin unit
- * tests where the volume subsystem directly talks with the plugin,
- * bypassing the daemon. For such tests, this check is necessary.
- */
- if ps != nil {
- ps.RLock()
- result = ps.getAllByCap(capability)
- ps.RUnlock()
- }
- // Lookup with legacy model
- if allowV1PluginsFallback {
- pl, err := plugins.GetAll(capability)
- if err != nil {
- return nil, fmt.Errorf("legacy plugin: %v", err)
- }
- for _, p := range pl {
- result = append(result, p)
- }
- }
- return result, nil
- }
- // Handle sets a callback for a given capability. It is only used by network
- // and ipam drivers during plugin registration. The callback registers the
- // driver with the subsystem (network, ipam).
- func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
- pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
- // Register callback with new plugin model.
- ps.Lock()
- handlers, ok := ps.handlers[pluginType]
- if !ok {
- handlers = []func(string, *plugins.Client){}
- }
- handlers = append(handlers, callback)
- ps.handlers[pluginType] = handlers
- ps.Unlock()
- // Register callback with legacy plugin model.
- if allowV1PluginsFallback {
- plugins.Handle(capability, callback)
- }
- }
- // CallHandler calls the registered callback. It is invoked during plugin enable.
- func (ps *Store) CallHandler(p *v2.Plugin) {
- for _, typ := range p.GetTypes() {
- for _, handler := range ps.handlers[typ.String()] {
- handler(p.Name(), p.Client())
- }
- }
- }
- // Search retreives a plugin by ID Prefix
- // If no plugin is found, then ErrNotFound is returned
- // If multiple plugins are found, then ErrAmbiguous is returned
- func (ps *Store) Search(partialID string) (*v2.Plugin, error) {
- ps.RLock()
- defer ps.RUnlock()
- var found *v2.Plugin
- for id, p := range ps.plugins {
- if strings.HasPrefix(id, partialID) {
- if found != nil {
- return nil, ErrAmbiguous(partialID)
- }
- found = p
- }
- }
- if found == nil {
- return nil, ErrNotFound(partialID)
- }
- return found, nil
- }
|