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