github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/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  	// error produced by activation
    74  	activateErr error
    75  	// specifies if the activation sequence is completed (not if it is successful or not)
    76  	activated bool
    77  	// wait for activation to finish
    78  	activateWait *sync.Cond
    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  	if p.activated {
   110  		p.activateWait.L.Unlock()
   111  		return p.activateErr
   112  	}
   113  
   114  	p.activateErr = p.activateWithLock()
   115  	p.activated = true
   116  
   117  	p.activateWait.L.Unlock()
   118  	p.activateWait.Broadcast()
   119  	return p.activateErr
   120  }
   121  
   122  func (p *Plugin) activateWithLock() error {
   123  	c, err := NewClient(p.Addr, p.TLSConfig)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	p.client = c
   128  
   129  	m := new(Manifest)
   130  	if err = p.client.Call("Plugin.Activate", nil, m); err != nil {
   131  		return err
   132  	}
   133  
   134  	p.Manifest = m
   135  
   136  	handlers.RLock()
   137  	for _, iface := range m.Implements {
   138  		hdlrs, handled := handlers.extpointHandlers[iface]
   139  		if !handled {
   140  			continue
   141  		}
   142  		for _, handler := range hdlrs {
   143  			handler(p.name, p.client)
   144  		}
   145  	}
   146  	handlers.RUnlock()
   147  	return nil
   148  }
   149  
   150  func (p *Plugin) waitActive() error {
   151  	p.activateWait.L.Lock()
   152  	for !p.activated {
   153  		p.activateWait.Wait()
   154  	}
   155  	p.activateWait.L.Unlock()
   156  	return p.activateErr
   157  }
   158  
   159  func (p *Plugin) implements(kind string) bool {
   160  	if err := p.waitActive(); err != nil {
   161  		return false
   162  	}
   163  	for _, driver := range p.Manifest.Implements {
   164  		if driver == kind {
   165  			return true
   166  		}
   167  	}
   168  	return false
   169  }
   170  
   171  func load(name string) (*Plugin, error) {
   172  	return loadWithRetry(name, true)
   173  }
   174  
   175  func loadWithRetry(name string, retry bool) (*Plugin, error) {
   176  	registry := newLocalRegistry()
   177  	start := time.Now()
   178  
   179  	var retries int
   180  	for {
   181  		pl, err := registry.Plugin(name)
   182  		if err != nil {
   183  			if !retry {
   184  				return nil, err
   185  			}
   186  
   187  			timeOff := backoff(retries)
   188  			if abort(start, timeOff) {
   189  				return nil, err
   190  			}
   191  			retries++
   192  			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
   193  			time.Sleep(timeOff)
   194  			continue
   195  		}
   196  
   197  		storage.Lock()
   198  		storage.plugins[name] = pl
   199  		storage.Unlock()
   200  
   201  		err = pl.activate()
   202  
   203  		if err != nil {
   204  			storage.Lock()
   205  			delete(storage.plugins, name)
   206  			storage.Unlock()
   207  		}
   208  
   209  		return pl, err
   210  	}
   211  }
   212  
   213  func get(name string) (*Plugin, error) {
   214  	storage.Lock()
   215  	pl, ok := storage.plugins[name]
   216  	storage.Unlock()
   217  	if ok {
   218  		return pl, pl.activate()
   219  	}
   220  	return load(name)
   221  }
   222  
   223  // Get returns the plugin given the specified name and requested implementation.
   224  func Get(name, imp string) (*Plugin, error) {
   225  	pl, err := get(name)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	if pl.implements(imp) {
   230  		logrus.Debugf("%s implements: %s", name, imp)
   231  		return pl, nil
   232  	}
   233  	return nil, ErrNotImplements
   234  }
   235  
   236  // Handle adds the specified function to the extpointHandlers.
   237  func Handle(iface string, fn func(string, *Client)) {
   238  	handlers.Lock()
   239  	hdlrs, ok := handlers.extpointHandlers[iface]
   240  	if !ok {
   241  		hdlrs = []func(string, *Client){}
   242  	}
   243  
   244  	hdlrs = append(hdlrs, fn)
   245  	handlers.extpointHandlers[iface] = hdlrs
   246  	for _, p := range storage.plugins {
   247  		p.activated = false
   248  	}
   249  	handlers.Unlock()
   250  }
   251  
   252  // GetAll returns all the plugins for the specified implementation
   253  func GetAll(imp string) ([]*Plugin, error) {
   254  	pluginNames, err := Scan()
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	type plLoad struct {
   260  		pl  *Plugin
   261  		err error
   262  	}
   263  
   264  	chPl := make(chan *plLoad, len(pluginNames))
   265  	var wg sync.WaitGroup
   266  	for _, name := range pluginNames {
   267  		if pl, ok := storage.plugins[name]; ok {
   268  			chPl <- &plLoad{pl, nil}
   269  			continue
   270  		}
   271  
   272  		wg.Add(1)
   273  		go func(name string) {
   274  			defer wg.Done()
   275  			pl, err := loadWithRetry(name, false)
   276  			chPl <- &plLoad{pl, err}
   277  		}(name)
   278  	}
   279  
   280  	wg.Wait()
   281  	close(chPl)
   282  
   283  	var out []*Plugin
   284  	for pl := range chPl {
   285  		if pl.err != nil {
   286  			logrus.Error(pl.err)
   287  			continue
   288  		}
   289  		if pl.pl.implements(imp) {
   290  			out = append(out, pl.pl)
   291  		}
   292  	}
   293  	return out, nil
   294  }