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