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