github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/pkg/plugins/plugins.go (about)

     1  // Package plugins provides structures and helper functions to manage Docker
     2  // plugins.
     3  //
     4  // Docker discovers plugins by looking for them in the plugin directory whenever
     5  // a user or container tries to use one by name. UNIX domain socket files must
     6  // be located under /run/docker/plugins, whereas spec files can be located
     7  // either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled
     8  // by the Registry interface, which lets you list all plugins or get a plugin by
     9  // its name if it exists.
    10  //
    11  // The plugins need to implement an HTTP server and bind this to the UNIX socket
    12  // or the address specified in the spec files.
    13  // A handshake is send at /Plugin.Activate, and plugins are expected to return
    14  // a Manifest with a list of Docker subsystems which this plugin implements.
    15  //
    16  // In order to use a plugins, you can use the ``Get`` with the name of the
    17  // plugin and the subsystem it implements.
    18  //
    19  //	plugin, err := plugins.Get("example", "VolumeDriver")
    20  //	if err != nil {
    21  //		return fmt.Errorf("Error looking up volume plugin example: %v", err)
    22  //	}
    23  package plugins // import "github.com/docker/docker/pkg/plugins"
    24  
    25  import (
    26  	"errors"
    27  	"fmt"
    28  	"sync"
    29  	"time"
    30  
    31  	"github.com/docker/go-connections/tlsconfig"
    32  	"github.com/sirupsen/logrus"
    33  )
    34  
    35  // ProtocolSchemeHTTPV1 is the name of the protocol used for interacting with plugins using this package.
    36  const ProtocolSchemeHTTPV1 = "moby.plugins.http/v1"
    37  
    38  var (
    39  	// ErrNotImplements is returned if the plugin does not implement the requested driver.
    40  	ErrNotImplements = errors.New("Plugin does not implement the requested driver")
    41  )
    42  
    43  type plugins struct {
    44  	sync.Mutex
    45  	plugins map[string]*Plugin
    46  }
    47  
    48  type extpointHandlers struct {
    49  	sync.RWMutex
    50  	extpointHandlers map[string][]func(string, *Client)
    51  }
    52  
    53  var (
    54  	storage  = plugins{plugins: make(map[string]*Plugin)}
    55  	handlers = extpointHandlers{extpointHandlers: make(map[string][]func(string, *Client))}
    56  )
    57  
    58  // Manifest lists what a plugin implements.
    59  type Manifest struct {
    60  	// List of subsystem the plugin implements.
    61  	Implements []string
    62  }
    63  
    64  // Plugin is the definition of a docker plugin.
    65  type Plugin struct {
    66  	// Name of the plugin
    67  	name string
    68  	// Address of the plugin
    69  	Addr string
    70  	// TLS configuration of the plugin
    71  	TLSConfig *tlsconfig.Options
    72  	// Client attached to the plugin
    73  	client *Client
    74  	// Manifest of the plugin (see above)
    75  	Manifest *Manifest `json:"-"`
    76  
    77  	// wait for activation to finish
    78  	activateWait *sync.Cond
    79  	// error produced by activation
    80  	activateErr error
    81  	// keeps track of callback handlers run against this plugin
    82  	handlersRun bool
    83  }
    84  
    85  // Name returns the name of the plugin.
    86  func (p *Plugin) Name() string {
    87  	return p.name
    88  }
    89  
    90  // Client returns a ready-to-use plugin client that can be used to communicate with the plugin.
    91  func (p *Plugin) Client() *Client {
    92  	return p.client
    93  }
    94  
    95  // Protocol returns the protocol name/version used for plugins in this package.
    96  func (p *Plugin) Protocol() string {
    97  	return ProtocolSchemeHTTPV1
    98  }
    99  
   100  // IsV1 returns true for V1 plugins and false otherwise.
   101  func (p *Plugin) IsV1() bool {
   102  	return true
   103  }
   104  
   105  // NewLocalPlugin creates a new local plugin.
   106  func NewLocalPlugin(name, addr string) *Plugin {
   107  	return &Plugin{
   108  		name: name,
   109  		Addr: addr,
   110  		// TODO: change to nil
   111  		TLSConfig:    &tlsconfig.Options{InsecureSkipVerify: true},
   112  		activateWait: sync.NewCond(&sync.Mutex{}),
   113  	}
   114  }
   115  
   116  func (p *Plugin) activate() error {
   117  	p.activateWait.L.Lock()
   118  
   119  	if p.activated() {
   120  		p.runHandlers()
   121  		p.activateWait.L.Unlock()
   122  		return p.activateErr
   123  	}
   124  
   125  	p.activateErr = p.activateWithLock()
   126  
   127  	p.runHandlers()
   128  	p.activateWait.L.Unlock()
   129  	p.activateWait.Broadcast()
   130  	return p.activateErr
   131  }
   132  
   133  // runHandlers runs the registered handlers for the implemented plugin types
   134  // This should only be run after activation, and while the activation lock is held.
   135  func (p *Plugin) runHandlers() {
   136  	if !p.activated() {
   137  		return
   138  	}
   139  
   140  	handlers.RLock()
   141  	if !p.handlersRun {
   142  		for _, iface := range p.Manifest.Implements {
   143  			hdlrs, handled := handlers.extpointHandlers[iface]
   144  			if !handled {
   145  				continue
   146  			}
   147  			for _, handler := range hdlrs {
   148  				handler(p.name, p.client)
   149  			}
   150  		}
   151  		p.handlersRun = true
   152  	}
   153  	handlers.RUnlock()
   154  
   155  }
   156  
   157  // activated returns if the plugin has already been activated.
   158  // This should only be called with the activation lock held
   159  func (p *Plugin) activated() bool {
   160  	return p.Manifest != nil
   161  }
   162  
   163  func (p *Plugin) activateWithLock() error {
   164  	c, err := NewClient(p.Addr, p.TLSConfig)
   165  	if err != nil {
   166  		return err
   167  	}
   168  	p.client = c
   169  
   170  	m := new(Manifest)
   171  	if err = p.client.Call("Plugin.Activate", nil, m); err != nil {
   172  		return err
   173  	}
   174  
   175  	p.Manifest = m
   176  	return nil
   177  }
   178  
   179  func (p *Plugin) waitActive() error {
   180  	p.activateWait.L.Lock()
   181  	for !p.activated() && p.activateErr == nil {
   182  		p.activateWait.Wait()
   183  	}
   184  	p.activateWait.L.Unlock()
   185  	return p.activateErr
   186  }
   187  
   188  func (p *Plugin) implements(kind string) bool {
   189  	if p.Manifest == nil {
   190  		return false
   191  	}
   192  	for _, driver := range p.Manifest.Implements {
   193  		if driver == kind {
   194  			return true
   195  		}
   196  	}
   197  	return false
   198  }
   199  
   200  func load(name string) (*Plugin, error) {
   201  	return loadWithRetry(name, true)
   202  }
   203  
   204  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   205  	registry := newLocalRegistry()
   206  	start := time.Now()
   207  
   208  	var retries int
   209  	for {
   210  		pl, err := registry.Plugin(name)
   211  		if err != nil {
   212  			if !retry {
   213  				return nil, err
   214  			}
   215  
   216  			timeOff := backoff(retries)
   217  			if abort(start, timeOff) {
   218  				return nil, err
   219  			}
   220  			retries++
   221  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   222  			time.Sleep(timeOff)
   223  			continue
   224  		}
   225  
   226  		storage.Lock()
   227  		if pl, exists := storage.plugins[name]; exists {
   228  			storage.Unlock()
   229  			return pl, pl.activate()
   230  		}
   231  		storage.plugins[name] = pl
   232  		storage.Unlock()
   233  
   234  		err = pl.activate()
   235  
   236  		if err != nil {
   237  			storage.Lock()
   238  			delete(storage.plugins, name)
   239  			storage.Unlock()
   240  		}
   241  
   242  		return pl, err
   243  	}
   244  }
   245  
   246  func get(name string) (*Plugin, error) {
   247  	storage.Lock()
   248  	pl, ok := storage.plugins[name]
   249  	storage.Unlock()
   250  	if ok {
   251  		return pl, pl.activate()
   252  	}
   253  	return load(name)
   254  }
   255  
   256  // Get returns the plugin given the specified name and requested implementation.
   257  func Get(name, imp string) (*Plugin, error) {
   258  	if name == "" {
   259  		return nil, errors.New("Unable to find plugin without name")
   260  	}
   261  	pl, err := get(name)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	if err := pl.waitActive(); err == nil && pl.implements(imp) {
   266  		logrus.Debugf("%s implements: %s", name, imp)
   267  		return pl, nil
   268  	}
   269  	return nil, fmt.Errorf("%w: plugin=%q, requested implementation=%q", ErrNotImplements, name, imp)
   270  }
   271  
   272  // Handle adds the specified function to the extpointHandlers.
   273  func Handle(iface string, fn func(string, *Client)) {
   274  	handlers.Lock()
   275  	hdlrs, ok := handlers.extpointHandlers[iface]
   276  	if !ok {
   277  		hdlrs = []func(string, *Client){}
   278  	}
   279  
   280  	hdlrs = append(hdlrs, fn)
   281  	handlers.extpointHandlers[iface] = hdlrs
   282  
   283  	storage.Lock()
   284  	for _, p := range storage.plugins {
   285  		p.activateWait.L.Lock()
   286  		if p.activated() && p.implements(iface) {
   287  			p.handlersRun = false
   288  		}
   289  		p.activateWait.L.Unlock()
   290  	}
   291  	storage.Unlock()
   292  
   293  	handlers.Unlock()
   294  }
   295  
   296  // GetAll returns all the plugins for the specified implementation
   297  func GetAll(imp string) ([]*Plugin, error) {
   298  	pluginNames, err := Scan()
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	type plLoad struct {
   304  		pl  *Plugin
   305  		err error
   306  	}
   307  
   308  	chPl := make(chan *plLoad, len(pluginNames))
   309  	var wg sync.WaitGroup
   310  	for _, name := range pluginNames {
   311  		storage.Lock()
   312  		pl, ok := storage.plugins[name]
   313  		storage.Unlock()
   314  		if ok {
   315  			chPl <- &plLoad{pl, nil}
   316  			continue
   317  		}
   318  
   319  		wg.Add(1)
   320  		go func(name string) {
   321  			defer wg.Done()
   322  			pl, err := loadWithRetry(name, false)
   323  			chPl <- &plLoad{pl, err}
   324  		}(name)
   325  	}
   326  
   327  	wg.Wait()
   328  	close(chPl)
   329  
   330  	var out []*Plugin
   331  	for pl := range chPl {
   332  		if pl.err != nil {
   333  			logrus.Error(pl.err)
   334  			continue
   335  		}
   336  		if err := pl.pl.waitActive(); err == nil && pl.pl.implements(imp) {
   337  			out = append(out, pl.pl)
   338  		}
   339  	}
   340  	return out, nil
   341  }