github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/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 `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  	p.Manifest = m
   100  
   101  	for _, iface := range m.Implements {
   102  		handler, handled := extpointHandlers[iface]
   103  		if !handled {
   104  			continue
   105  		}
   106  		handler(p.Name, p.Client)
   107  	}
   108  	return nil
   109  }
   110  
   111  func (p *Plugin) implements(kind string) bool {
   112  	for _, driver := range p.Manifest.Implements {
   113  		if driver == kind {
   114  			return true
   115  		}
   116  	}
   117  	return false
   118  }
   119  
   120  func load(name string) (*Plugin, error) {
   121  	return loadWithRetry(name, true)
   122  }
   123  
   124  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   125  	registry := newLocalRegistry()
   126  	start := time.Now()
   127  
   128  	var retries int
   129  	for {
   130  		pl, err := registry.Plugin(name)
   131  		if err != nil {
   132  			if !retry {
   133  				return nil, err
   134  			}
   135  
   136  			timeOff := backoff(retries)
   137  			if abort(start, timeOff) {
   138  				return nil, err
   139  			}
   140  			retries++
   141  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   142  			time.Sleep(timeOff)
   143  			continue
   144  		}
   145  
   146  		storage.Lock()
   147  		storage.plugins[name] = pl
   148  		storage.Unlock()
   149  
   150  		err = pl.activate()
   151  
   152  		if err != nil {
   153  			storage.Lock()
   154  			delete(storage.plugins, name)
   155  			storage.Unlock()
   156  		}
   157  
   158  		return pl, err
   159  	}
   160  }
   161  
   162  func get(name string) (*Plugin, error) {
   163  	storage.Lock()
   164  	pl, ok := storage.plugins[name]
   165  	storage.Unlock()
   166  	if ok {
   167  		return pl, pl.activate()
   168  	}
   169  	return load(name)
   170  }
   171  
   172  // Get returns the plugin given the specified name and requested implementation.
   173  func Get(name, imp string) (*Plugin, error) {
   174  	pl, err := get(name)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	if pl.implements(imp) {
   179  		logrus.Debugf("%s implements: %s", name, imp)
   180  		return pl, nil
   181  	}
   182  	return nil, ErrNotImplements
   183  }
   184  
   185  // Handle adds the specified function to the extpointHandlers.
   186  func Handle(iface string, fn func(string, *Client)) {
   187  	extpointHandlers[iface] = fn
   188  }
   189  
   190  // GetAll returns all the plugins for the specified implementation
   191  func GetAll(imp string) ([]*Plugin, error) {
   192  	pluginNames, err := Scan()
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	type plLoad struct {
   198  		pl  *Plugin
   199  		err error
   200  	}
   201  
   202  	chPl := make(chan plLoad, len(pluginNames))
   203  	for _, name := range pluginNames {
   204  		go func(name string) {
   205  			pl, err := loadWithRetry(name, false)
   206  			chPl <- plLoad{pl, err}
   207  		}(name)
   208  	}
   209  
   210  	var out []*Plugin
   211  	for i := 0; i < len(pluginNames); i++ {
   212  		pl := <-chPl
   213  		if pl.err != nil {
   214  			logrus.Error(err)
   215  			continue
   216  		}
   217  		if pl.pl.implements(imp) {
   218  			out = append(out, pl.pl)
   219  		}
   220  	}
   221  	return out, nil
   222  }