github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/volume/drivers/extpoint.go (about)

     1  //go:generate pluginrpc-gen -i $GOFILE -o proxy.go -type volumeDriver -name VolumeDriver
     2  
     3  package drivers // import "github.com/docker/docker/volume/drivers"
     4  
     5  import (
     6  	"fmt"
     7  	"sort"
     8  	"sync"
     9  
    10  	"github.com/docker/docker/errdefs"
    11  	"github.com/docker/docker/pkg/locker"
    12  	getter "github.com/docker/docker/pkg/plugingetter"
    13  	"github.com/docker/docker/pkg/plugins"
    14  	"github.com/docker/docker/volume"
    15  	"github.com/pkg/errors"
    16  	"github.com/sirupsen/logrus"
    17  )
    18  
    19  const extName = "VolumeDriver"
    20  
    21  // volumeDriver defines the available functions that volume plugins must implement.
    22  // This interface is only defined to generate the proxy objects.
    23  // It's not intended to be public or reused.
    24  // nolint: deadcode
    25  type volumeDriver interface {
    26  	// Create a volume with the given name
    27  	Create(name string, opts map[string]string) (err error)
    28  	// Remove the volume with the given name
    29  	Remove(name string) (err error)
    30  	// Get the mountpoint of the given volume
    31  	Path(name string) (mountpoint string, err error)
    32  	// Mount the given volume and return the mountpoint
    33  	Mount(name, id string) (mountpoint string, err error)
    34  	// Unmount the given volume
    35  	Unmount(name, id string) (err error)
    36  	// List lists all the volumes known to the driver
    37  	List() (volumes []*proxyVolume, err error)
    38  	// Get retrieves the volume with the requested name
    39  	Get(name string) (volume *proxyVolume, err error)
    40  	// Capabilities gets the list of capabilities of the driver
    41  	Capabilities() (capabilities volume.Capability, err error)
    42  }
    43  
    44  // Store is an in-memory store for volume drivers
    45  type Store struct {
    46  	extensions   map[string]volume.Driver
    47  	mu           sync.Mutex
    48  	driverLock   *locker.Locker
    49  	pluginGetter getter.PluginGetter
    50  }
    51  
    52  // NewStore creates a new volume driver store
    53  func NewStore(pg getter.PluginGetter) *Store {
    54  	return &Store{
    55  		extensions:   make(map[string]volume.Driver),
    56  		driverLock:   locker.New(),
    57  		pluginGetter: pg,
    58  	}
    59  }
    60  
    61  type driverNotFoundError string
    62  
    63  func (e driverNotFoundError) Error() string {
    64  	return "volume driver not found: " + string(e)
    65  }
    66  
    67  func (driverNotFoundError) NotFound() {}
    68  
    69  // lookup returns the driver associated with the given name. If a
    70  // driver with the given name has not been registered it checks if
    71  // there is a VolumeDriver plugin available with the given name.
    72  func (s *Store) lookup(name string, mode int) (volume.Driver, error) {
    73  	if name == "" {
    74  		return nil, errdefs.InvalidParameter(errors.New("driver name cannot be empty"))
    75  	}
    76  	s.driverLock.Lock(name)
    77  	defer s.driverLock.Unlock(name)
    78  
    79  	s.mu.Lock()
    80  	ext, ok := s.extensions[name]
    81  	s.mu.Unlock()
    82  	if ok {
    83  		return ext, nil
    84  	}
    85  	if s.pluginGetter != nil {
    86  		p, err := s.pluginGetter.Get(name, extName, mode)
    87  		if err != nil {
    88  			return nil, errors.Wrap(err, "error looking up volume plugin "+name)
    89  		}
    90  
    91  		d, err := makePluginAdapter(p)
    92  		if err != nil {
    93  			return nil, errors.Wrap(err, "error making plugin client")
    94  		}
    95  		if err := validateDriver(d); err != nil {
    96  			if mode > 0 {
    97  				// Undo any reference count changes from the initial `Get`
    98  				if _, err := s.pluginGetter.Get(name, extName, mode*-1); err != nil {
    99  					logrus.WithError(err).WithField("action", "validate-driver").WithField("plugin", name).Error("error releasing reference to plugin")
   100  				}
   101  			}
   102  			return nil, err
   103  		}
   104  
   105  		if p.IsV1() {
   106  			s.mu.Lock()
   107  			s.extensions[name] = d
   108  			s.mu.Unlock()
   109  		}
   110  		return d, nil
   111  	}
   112  	return nil, driverNotFoundError(name)
   113  }
   114  
   115  func validateDriver(vd volume.Driver) error {
   116  	scope := vd.Scope()
   117  	if scope != volume.LocalScope && scope != volume.GlobalScope {
   118  		return fmt.Errorf("Driver %q provided an invalid capability scope: %s", vd.Name(), scope)
   119  	}
   120  	return nil
   121  }
   122  
   123  // Register associates the given driver to the given name, checking if
   124  // the name is already associated
   125  func (s *Store) Register(d volume.Driver, name string) bool {
   126  	if name == "" {
   127  		return false
   128  	}
   129  
   130  	s.mu.Lock()
   131  	defer s.mu.Unlock()
   132  
   133  	if _, exists := s.extensions[name]; exists {
   134  		return false
   135  	}
   136  
   137  	if err := validateDriver(d); err != nil {
   138  		return false
   139  	}
   140  
   141  	s.extensions[name] = d
   142  	return true
   143  }
   144  
   145  // GetDriver returns a volume driver by its name.
   146  // If the driver is empty, it looks for the local driver.
   147  func (s *Store) GetDriver(name string) (volume.Driver, error) {
   148  	return s.lookup(name, getter.Lookup)
   149  }
   150  
   151  // CreateDriver returns a volume driver by its name and increments RefCount.
   152  // If the driver is empty, it looks for the local driver.
   153  func (s *Store) CreateDriver(name string) (volume.Driver, error) {
   154  	return s.lookup(name, getter.Acquire)
   155  }
   156  
   157  // ReleaseDriver returns a volume driver by its name and decrements RefCount..
   158  // If the driver is empty, it looks for the local driver.
   159  func (s *Store) ReleaseDriver(name string) (volume.Driver, error) {
   160  	return s.lookup(name, getter.Release)
   161  }
   162  
   163  // GetDriverList returns list of volume drivers registered.
   164  // If no driver is registered, empty string list will be returned.
   165  func (s *Store) GetDriverList() []string {
   166  	var driverList []string
   167  	s.mu.Lock()
   168  	defer s.mu.Unlock()
   169  	for driverName := range s.extensions {
   170  		driverList = append(driverList, driverName)
   171  	}
   172  	sort.Strings(driverList)
   173  	return driverList
   174  }
   175  
   176  // GetAllDrivers lists all the registered drivers
   177  func (s *Store) GetAllDrivers() ([]volume.Driver, error) {
   178  	var plugins []getter.CompatPlugin
   179  	if s.pluginGetter != nil {
   180  		var err error
   181  		plugins, err = s.pluginGetter.GetAllByCap(extName)
   182  		if err != nil {
   183  			return nil, fmt.Errorf("error listing plugins: %v", err)
   184  		}
   185  	}
   186  	var ds []volume.Driver
   187  
   188  	s.mu.Lock()
   189  	defer s.mu.Unlock()
   190  
   191  	for _, d := range s.extensions {
   192  		ds = append(ds, d)
   193  	}
   194  
   195  	for _, p := range plugins {
   196  		name := p.Name()
   197  
   198  		if _, ok := s.extensions[name]; ok {
   199  			continue
   200  		}
   201  
   202  		ext, err := makePluginAdapter(p)
   203  		if err != nil {
   204  			return nil, errors.Wrap(err, "error making plugin client")
   205  		}
   206  		if p.IsV1() {
   207  			s.extensions[name] = ext
   208  		}
   209  		ds = append(ds, ext)
   210  	}
   211  	return ds, nil
   212  }
   213  
   214  func makePluginAdapter(p getter.CompatPlugin) (*volumeDriverAdapter, error) {
   215  	if pc, ok := p.(getter.PluginWithV1Client); ok {
   216  		return &volumeDriverAdapter{name: p.Name(), scopePath: p.ScopedPath, proxy: &volumeDriverProxy{pc.Client()}}, nil
   217  	}
   218  
   219  	pa, ok := p.(getter.PluginAddr)
   220  	if !ok {
   221  		return nil, errdefs.System(errors.Errorf("got unknown plugin instance %T", p))
   222  	}
   223  
   224  	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
   225  		return nil, errors.Errorf("plugin protocol not supported: %s", p)
   226  	}
   227  
   228  	addr := pa.Addr()
   229  	client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
   230  	if err != nil {
   231  		return nil, errors.Wrap(err, "error creating plugin client")
   232  	}
   233  
   234  	return &volumeDriverAdapter{name: p.Name(), scopePath: p.ScopedPath, proxy: &volumeDriverProxy{client}}, nil
   235  }