github.com/kobeld/docker@v1.12.0-rc1/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  // NewLocalPlugin creates a new local plugin.
    87  func NewLocalPlugin(name, addr string) *Plugin {
    88  	return &Plugin{
    89  		name: name,
    90  		Addr: addr,
    91  		// TODO: change to nil
    92  		TLSConfig:    &tlsconfig.Options{InsecureSkipVerify: true},
    93  		activateWait: sync.NewCond(&sync.Mutex{}),
    94  	}
    95  }
    96  
    97  func (p *Plugin) activate() error {
    98  	p.activateWait.L.Lock()
    99  	if p.activated {
   100  		p.activateWait.L.Unlock()
   101  		return p.activateErr
   102  	}
   103  
   104  	p.activateErr = p.activateWithLock()
   105  	p.activated = true
   106  
   107  	p.activateWait.L.Unlock()
   108  	p.activateWait.Broadcast()
   109  	return p.activateErr
   110  }
   111  
   112  func (p *Plugin) activateWithLock() error {
   113  	c, err := NewClient(p.Addr, p.TLSConfig)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	p.client = c
   118  
   119  	m := new(Manifest)
   120  	if err = p.client.Call("Plugin.Activate", nil, m); err != nil {
   121  		return err
   122  	}
   123  
   124  	p.Manifest = m
   125  
   126  	for _, iface := range m.Implements {
   127  		handler, handled := extpointHandlers[iface]
   128  		if !handled {
   129  			continue
   130  		}
   131  		handler(p.name, p.client)
   132  	}
   133  	return nil
   134  }
   135  
   136  func (p *Plugin) waitActive() error {
   137  	p.activateWait.L.Lock()
   138  	for !p.activated {
   139  		p.activateWait.Wait()
   140  	}
   141  	p.activateWait.L.Unlock()
   142  	return p.activateErr
   143  }
   144  
   145  func (p *Plugin) implements(kind string) bool {
   146  	if err := p.waitActive(); err != nil {
   147  		return false
   148  	}
   149  	for _, driver := range p.Manifest.Implements {
   150  		if driver == kind {
   151  			return true
   152  		}
   153  	}
   154  	return false
   155  }
   156  
   157  func load(name string) (*Plugin, error) {
   158  	return loadWithRetry(name, true)
   159  }
   160  
   161  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   162  	registry := newLocalRegistry()
   163  	start := time.Now()
   164  
   165  	var retries int
   166  	for {
   167  		pl, err := registry.Plugin(name)
   168  		if err != nil {
   169  			if !retry {
   170  				return nil, err
   171  			}
   172  
   173  			timeOff := backoff(retries)
   174  			if abort(start, timeOff) {
   175  				return nil, err
   176  			}
   177  			retries++
   178  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   179  			time.Sleep(timeOff)
   180  			continue
   181  		}
   182  
   183  		storage.Lock()
   184  		storage.plugins[name] = pl
   185  		storage.Unlock()
   186  
   187  		err = pl.activate()
   188  
   189  		if err != nil {
   190  			storage.Lock()
   191  			delete(storage.plugins, name)
   192  			storage.Unlock()
   193  		}
   194  
   195  		return pl, err
   196  	}
   197  }
   198  
   199  func get(name string) (*Plugin, error) {
   200  	storage.Lock()
   201  	pl, ok := storage.plugins[name]
   202  	storage.Unlock()
   203  	if ok {
   204  		return pl, pl.activate()
   205  	}
   206  	return load(name)
   207  }
   208  
   209  // Get returns the plugin given the specified name and requested implementation.
   210  func Get(name, imp string) (*Plugin, error) {
   211  	pl, err := get(name)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	if pl.implements(imp) {
   216  		logrus.Debugf("%s implements: %s", name, imp)
   217  		return pl, nil
   218  	}
   219  	return nil, ErrNotImplements
   220  }
   221  
   222  // Handle adds the specified function to the extpointHandlers.
   223  func Handle(iface string, fn func(string, *Client)) {
   224  	extpointHandlers[iface] = fn
   225  }
   226  
   227  // GetAll returns all the plugins for the specified implementation
   228  func GetAll(imp string) ([]*Plugin, error) {
   229  	pluginNames, err := Scan()
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	type plLoad struct {
   235  		pl  *Plugin
   236  		err error
   237  	}
   238  
   239  	chPl := make(chan *plLoad, len(pluginNames))
   240  	var wg sync.WaitGroup
   241  	for _, name := range pluginNames {
   242  		if pl, ok := storage.plugins[name]; ok {
   243  			chPl <- &plLoad{pl, nil}
   244  			continue
   245  		}
   246  
   247  		wg.Add(1)
   248  		go func(name string) {
   249  			defer wg.Done()
   250  			pl, err := loadWithRetry(name, false)
   251  			chPl <- &plLoad{pl, err}
   252  		}(name)
   253  	}
   254  
   255  	wg.Wait()
   256  	close(chPl)
   257  
   258  	var out []*Plugin
   259  	for pl := range chPl {
   260  		if pl.err != nil {
   261  			logrus.Error(pl.err)
   262  			continue
   263  		}
   264  		if pl.pl.implements(imp) {
   265  			out = append(out, pl.pl)
   266  		}
   267  	}
   268  	return out, nil
   269  }