github.com/portworx/docker@v1.12.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  var (
    45  	storage          = plugins{plugins: make(map[string]*Plugin)}
    46  	extpointHandlers = make(map[string]func(string, *Client))
    47  )
    48  
    49  // Manifest lists what a plugin implements.
    50  type Manifest struct {
    51  	// List of subsystem the plugin implements.
    52  	Implements []string
    53  }
    54  
    55  // Plugin is the definition of a docker plugin.
    56  type Plugin struct {
    57  	// Name of the plugin
    58  	name string
    59  	// Address of the plugin
    60  	Addr string
    61  	// TLS configuration of the plugin
    62  	TLSConfig *tlsconfig.Options
    63  	// Client attached to the plugin
    64  	client *Client
    65  	// Manifest of the plugin (see above)
    66  	Manifest *Manifest `json:"-"`
    67  
    68  	// error produced by activation
    69  	activateErr error
    70  	// specifies if the activation sequence is completed (not if it is successful or not)
    71  	activated bool
    72  	// wait for activation to finish
    73  	activateWait *sync.Cond
    74  }
    75  
    76  // Name returns the name of the plugin.
    77  func (p *Plugin) Name() string {
    78  	return p.name
    79  }
    80  
    81  // Client returns a ready-to-use plugin client that can be used to communicate with the plugin.
    82  func (p *Plugin) Client() *Client {
    83  	return p.client
    84  }
    85  
    86  // IsLegacy returns true for legacy plugins and false otherwise.
    87  func (p *Plugin) IsLegacy() bool {
    88  	return true
    89  }
    90  
    91  // NewLocalPlugin creates a new local plugin.
    92  func NewLocalPlugin(name, addr string) *Plugin {
    93  	return &Plugin{
    94  		name: name,
    95  		Addr: addr,
    96  		// TODO: change to nil
    97  		TLSConfig:    &tlsconfig.Options{InsecureSkipVerify: true},
    98  		activateWait: sync.NewCond(&sync.Mutex{}),
    99  	}
   100  }
   101  
   102  func (p *Plugin) activate() error {
   103  	p.activateWait.L.Lock()
   104  	if p.activated {
   105  		p.activateWait.L.Unlock()
   106  		return p.activateErr
   107  	}
   108  
   109  	p.activateErr = p.activateWithLock()
   110  	p.activated = true
   111  
   112  	p.activateWait.L.Unlock()
   113  	p.activateWait.Broadcast()
   114  	return p.activateErr
   115  }
   116  
   117  func (p *Plugin) activateWithLock() error {
   118  	c, err := NewClient(p.Addr, p.TLSConfig)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	p.client = c
   123  
   124  	m := new(Manifest)
   125  	if err = p.client.Call("Plugin.Activate", nil, m); err != nil {
   126  		return err
   127  	}
   128  
   129  	p.Manifest = m
   130  
   131  	for _, iface := range m.Implements {
   132  		handler, handled := extpointHandlers[iface]
   133  		if !handled {
   134  			continue
   135  		}
   136  		handler(p.name, p.client)
   137  	}
   138  	return nil
   139  }
   140  
   141  func (p *Plugin) waitActive() error {
   142  	p.activateWait.L.Lock()
   143  	for !p.activated {
   144  		p.activateWait.Wait()
   145  	}
   146  	p.activateWait.L.Unlock()
   147  	return p.activateErr
   148  }
   149  
   150  func (p *Plugin) implements(kind string) bool {
   151  	if err := p.waitActive(); err != nil {
   152  		return false
   153  	}
   154  	for _, driver := range p.Manifest.Implements {
   155  		if driver == kind {
   156  			return true
   157  		}
   158  	}
   159  	return false
   160  }
   161  
   162  func load(name string) (*Plugin, error) {
   163  	return loadWithRetry(name, true)
   164  }
   165  
   166  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   167  	registry := newLocalRegistry()
   168  	start := time.Now()
   169  
   170  	var retries int
   171  	for {
   172  		pl, err := registry.Plugin(name)
   173  		if err != nil {
   174  			if !retry {
   175  				return nil, err
   176  			}
   177  
   178  			timeOff := backoff(retries)
   179  			if abort(start, timeOff) {
   180  				return nil, err
   181  			}
   182  			retries++
   183  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   184  			time.Sleep(timeOff)
   185  			continue
   186  		}
   187  
   188  		storage.Lock()
   189  		storage.plugins[name] = pl
   190  		storage.Unlock()
   191  
   192  		err = pl.activate()
   193  
   194  		if err != nil {
   195  			storage.Lock()
   196  			delete(storage.plugins, name)
   197  			storage.Unlock()
   198  		}
   199  
   200  		return pl, err
   201  	}
   202  }
   203  
   204  func get(name string) (*Plugin, error) {
   205  	storage.Lock()
   206  	pl, ok := storage.plugins[name]
   207  	storage.Unlock()
   208  	if ok {
   209  		return pl, pl.activate()
   210  	}
   211  	return load(name)
   212  }
   213  
   214  // Get returns the plugin given the specified name and requested implementation.
   215  func Get(name, imp string) (*Plugin, error) {
   216  	pl, err := get(name)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	if pl.implements(imp) {
   221  		logrus.Debugf("%s implements: %s", name, imp)
   222  		return pl, nil
   223  	}
   224  	return nil, ErrNotImplements
   225  }
   226  
   227  // Handle adds the specified function to the extpointHandlers.
   228  func Handle(iface string, fn func(string, *Client)) {
   229  	extpointHandlers[iface] = fn
   230  }
   231  
   232  // GetAll returns all the plugins for the specified implementation
   233  func GetAll(imp string) ([]*Plugin, error) {
   234  	pluginNames, err := Scan()
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	type plLoad struct {
   240  		pl  *Plugin
   241  		err error
   242  	}
   243  
   244  	chPl := make(chan *plLoad, len(pluginNames))
   245  	var wg sync.WaitGroup
   246  	for _, name := range pluginNames {
   247  		if pl, ok := storage.plugins[name]; ok {
   248  			chPl <- &plLoad{pl, nil}
   249  			continue
   250  		}
   251  
   252  		wg.Add(1)
   253  		go func(name string) {
   254  			defer wg.Done()
   255  			pl, err := loadWithRetry(name, false)
   256  			chPl <- &plLoad{pl, err}
   257  		}(name)
   258  	}
   259  
   260  	wg.Wait()
   261  	close(chPl)
   262  
   263  	var out []*Plugin
   264  	for pl := range chPl {
   265  		if pl.err != nil {
   266  			logrus.Error(pl.err)
   267  			continue
   268  		}
   269  		if pl.pl.implements(imp) {
   270  			out = append(out, pl.pl)
   271  		}
   272  	}
   273  	return out, nil
   274  }