github.com/yamamoto-febc/docker@v1.9.0/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/docker/pkg/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 `json:"-"`
    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 `json:"-"`
    65  	// Manifest of the plugin (see above)
    66  	Manifest *Manifest `json:"-"`
    67  
    68  	activatErr   error
    69  	activateOnce sync.Once
    70  }
    71  
    72  func newLocalPlugin(name, addr string) *Plugin {
    73  	return &Plugin{
    74  		Name:      name,
    75  		Addr:      addr,
    76  		TLSConfig: tlsconfig.Options{InsecureSkipVerify: true},
    77  	}
    78  }
    79  
    80  func (p *Plugin) activate() error {
    81  	p.activateOnce.Do(func() {
    82  		p.activatErr = p.activateWithLock()
    83  	})
    84  	return p.activatErr
    85  }
    86  
    87  func (p *Plugin) activateWithLock() error {
    88  	c, err := NewClient(p.Addr, p.TLSConfig)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	p.Client = c
    93  
    94  	m := new(Manifest)
    95  	if err = p.Client.Call("Plugin.Activate", nil, m); err != nil {
    96  		return err
    97  	}
    98  
    99  	logrus.Debugf("%s's manifest: %v", p.Name, m)
   100  	p.Manifest = m
   101  
   102  	for _, iface := range m.Implements {
   103  		handler, handled := extpointHandlers[iface]
   104  		if !handled {
   105  			continue
   106  		}
   107  		handler(p.Name, p.Client)
   108  	}
   109  	return nil
   110  }
   111  
   112  func load(name string) (*Plugin, error) {
   113  	return loadWithRetry(name, true)
   114  }
   115  
   116  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   117  	registry := newLocalRegistry()
   118  	start := time.Now()
   119  
   120  	var retries int
   121  	for {
   122  		pl, err := registry.Plugin(name)
   123  		if err != nil {
   124  			if !retry {
   125  				return nil, err
   126  			}
   127  
   128  			timeOff := backoff(retries)
   129  			if abort(start, timeOff) {
   130  				return nil, err
   131  			}
   132  			retries++
   133  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   134  			time.Sleep(timeOff)
   135  			continue
   136  		}
   137  
   138  		storage.Lock()
   139  		storage.plugins[name] = pl
   140  		storage.Unlock()
   141  
   142  		err = pl.activate()
   143  
   144  		if err != nil {
   145  			storage.Lock()
   146  			delete(storage.plugins, name)
   147  			storage.Unlock()
   148  		}
   149  
   150  		return pl, err
   151  	}
   152  }
   153  
   154  func get(name string) (*Plugin, error) {
   155  	storage.Lock()
   156  	pl, ok := storage.plugins[name]
   157  	storage.Unlock()
   158  	if ok {
   159  		return pl, pl.activate()
   160  	}
   161  	return load(name)
   162  }
   163  
   164  // Get returns the plugin given the specified name and requested implementation.
   165  func Get(name, imp string) (*Plugin, error) {
   166  	pl, err := get(name)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	for _, driver := range pl.Manifest.Implements {
   171  		logrus.Debugf("%s implements: %s", name, driver)
   172  		if driver == imp {
   173  			return pl, nil
   174  		}
   175  	}
   176  	return nil, ErrNotImplements
   177  }
   178  
   179  // Handle adds the specified function to the extpointHandlers.
   180  func Handle(iface string, fn func(string, *Client)) {
   181  	extpointHandlers[iface] = fn
   182  }