github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/plugin/store.go (about)

     1  package plugin
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/docker/distribution/reference"
     8  	"github.com/docker/docker/errdefs"
     9  	"github.com/docker/docker/pkg/plugingetter"
    10  	"github.com/docker/docker/pkg/plugins"
    11  	"github.com/docker/docker/plugin/v2"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    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. e.g. "networkdriver/2.0".
    25  */
    26  const defaultAPIVersion string = "1.0"
    27  
    28  // GetV2Plugin retrieves a plugin by name, id or partial ID.
    29  func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) {
    30  	ps.RLock()
    31  	defer ps.RUnlock()
    32  
    33  	id, err := ps.resolvePluginID(refOrID)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	p, idOk := ps.plugins[id]
    39  	if !idOk {
    40  		return nil, errors.WithStack(errNotFound(id))
    41  	}
    42  
    43  	return p, nil
    44  }
    45  
    46  // validateName returns error if name is already reserved. always call with lock and full name
    47  func (ps *Store) validateName(name string) error {
    48  	for _, p := range ps.plugins {
    49  		if p.Name() == name {
    50  			return alreadyExistsError(name)
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  // GetAll retrieves all plugins.
    57  func (ps *Store) GetAll() map[string]*v2.Plugin {
    58  	ps.RLock()
    59  	defer ps.RUnlock()
    60  	return ps.plugins
    61  }
    62  
    63  // SetAll initialized plugins during daemon restore.
    64  func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
    65  	ps.Lock()
    66  	defer ps.Unlock()
    67  	ps.plugins = plugins
    68  }
    69  
    70  func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
    71  	ps.RLock()
    72  	defer ps.RUnlock()
    73  
    74  	result := make([]plugingetter.CompatPlugin, 0, 1)
    75  	for _, p := range ps.plugins {
    76  		if p.IsEnabled() {
    77  			if _, err := p.FilterByCap(capability); err == nil {
    78  				result = append(result, p)
    79  			}
    80  		}
    81  	}
    82  	return result
    83  }
    84  
    85  // SetState sets the active state of the plugin and updates plugindb.
    86  func (ps *Store) SetState(p *v2.Plugin, state bool) {
    87  	ps.Lock()
    88  	defer ps.Unlock()
    89  
    90  	p.PluginObj.Enabled = state
    91  }
    92  
    93  // Add adds a plugin to memory and plugindb.
    94  // An error will be returned if there is a collision.
    95  func (ps *Store) Add(p *v2.Plugin) error {
    96  	ps.Lock()
    97  	defer ps.Unlock()
    98  
    99  	if v, exist := ps.plugins[p.GetID()]; exist {
   100  		return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
   101  	}
   102  	ps.plugins[p.GetID()] = p
   103  	return nil
   104  }
   105  
   106  // Remove removes a plugin from memory and plugindb.
   107  func (ps *Store) Remove(p *v2.Plugin) {
   108  	ps.Lock()
   109  	delete(ps.plugins, p.GetID())
   110  	ps.Unlock()
   111  }
   112  
   113  // Get returns an enabled plugin matching the given name and capability.
   114  func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
   115  	// Lookup using new model.
   116  	if ps != nil {
   117  		p, err := ps.GetV2Plugin(name)
   118  		if err == nil {
   119  			if p.IsEnabled() {
   120  				fp, err := p.FilterByCap(capability)
   121  				if err != nil {
   122  					return nil, err
   123  				}
   124  				p.AddRefCount(mode)
   125  				return fp, nil
   126  			}
   127  
   128  			// Plugin was found but it is disabled, so we should not fall back to legacy plugins
   129  			// but we should error out right away
   130  			return nil, errDisabled(name)
   131  		}
   132  		if _, ok := errors.Cause(err).(errNotFound); !ok {
   133  			return nil, err
   134  		}
   135  	}
   136  
   137  	if !allowV1PluginsFallback {
   138  		return nil, errNotFound(name)
   139  	}
   140  
   141  	p, err := plugins.Get(name, capability)
   142  	if err == nil {
   143  		return p, nil
   144  	}
   145  	if errors.Cause(err) == plugins.ErrNotFound {
   146  		return nil, errNotFound(name)
   147  	}
   148  	return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
   149  }
   150  
   151  // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability.
   152  func (ps *Store) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
   153  	return ps.getAllByCap(capability)
   154  }
   155  
   156  // GetAllByCap returns a list of enabled plugins matching the given capability.
   157  func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
   158  	result := make([]plugingetter.CompatPlugin, 0, 1)
   159  
   160  	/* Daemon start always calls plugin.Init thereby initializing a store.
   161  	 * So store on experimental builds can never be nil, even while
   162  	 * handling legacy plugins. However, there are legacy plugin unit
   163  	 * tests where the volume subsystem directly talks with the plugin,
   164  	 * bypassing the daemon. For such tests, this check is necessary.
   165  	 */
   166  	if ps != nil {
   167  		ps.RLock()
   168  		result = ps.getAllByCap(capability)
   169  		ps.RUnlock()
   170  	}
   171  
   172  	// Lookup with legacy model
   173  	if allowV1PluginsFallback {
   174  		pl, err := plugins.GetAll(capability)
   175  		if err != nil {
   176  			return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
   177  		}
   178  		for _, p := range pl {
   179  			result = append(result, p)
   180  		}
   181  	}
   182  	return result, nil
   183  }
   184  
   185  // Handle sets a callback for a given capability. It is only used by network
   186  // and ipam drivers during plugin registration. The callback registers the
   187  // driver with the subsystem (network, ipam).
   188  func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
   189  	pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
   190  
   191  	// Register callback with new plugin model.
   192  	ps.Lock()
   193  	handlers, ok := ps.handlers[pluginType]
   194  	if !ok {
   195  		handlers = []func(string, *plugins.Client){}
   196  	}
   197  	handlers = append(handlers, callback)
   198  	ps.handlers[pluginType] = handlers
   199  	ps.Unlock()
   200  
   201  	// Register callback with legacy plugin model.
   202  	if allowV1PluginsFallback {
   203  		plugins.Handle(capability, callback)
   204  	}
   205  }
   206  
   207  // CallHandler calls the registered callback. It is invoked during plugin enable.
   208  func (ps *Store) CallHandler(p *v2.Plugin) {
   209  	for _, typ := range p.GetTypes() {
   210  		for _, handler := range ps.handlers[typ.String()] {
   211  			handler(p.Name(), p.Client())
   212  		}
   213  	}
   214  }
   215  
   216  func (ps *Store) resolvePluginID(idOrName string) (string, error) {
   217  	ps.RLock() // todo: fix
   218  	defer ps.RUnlock()
   219  
   220  	if validFullID.MatchString(idOrName) {
   221  		return idOrName, nil
   222  	}
   223  
   224  	ref, err := reference.ParseNormalizedNamed(idOrName)
   225  	if err != nil {
   226  		return "", errors.WithStack(errNotFound(idOrName))
   227  	}
   228  	if _, ok := ref.(reference.Canonical); ok {
   229  		logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
   230  		return "", errors.WithStack(errNotFound(idOrName))
   231  	}
   232  
   233  	ref = reference.TagNameOnly(ref)
   234  
   235  	for _, p := range ps.plugins {
   236  		if p.PluginObj.Name == reference.FamiliarString(ref) {
   237  			return p.PluginObj.ID, nil
   238  		}
   239  	}
   240  
   241  	var found *v2.Plugin
   242  	for id, p := range ps.plugins { // this can be optimized
   243  		if strings.HasPrefix(id, idOrName) {
   244  			if found != nil {
   245  				return "", errors.WithStack(errAmbiguous(idOrName))
   246  			}
   247  			found = p
   248  		}
   249  	}
   250  	if found == nil {
   251  		return "", errors.WithStack(errNotFound(idOrName))
   252  	}
   253  	return found.PluginObj.ID, nil
   254  }