github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/plugin/store/store.go (about)

     1  package store
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/docker/docker/pkg/ioutils"
    10  	"github.com/docker/docker/pkg/plugingetter"
    11  	"github.com/docker/docker/pkg/plugins"
    12  	"github.com/docker/docker/plugin/v2"
    13  	"github.com/docker/docker/reference"
    14  )
    15  
    16  /* allowV1PluginsFallback determines daemon's support for V1 plugins.
    17   * When the time comes to remove support for V1 plugins, flipping
    18   * this bool is all that will be needed.
    19   */
    20  const allowV1PluginsFallback bool = true
    21  
    22  /* defaultAPIVersion is the version of the plugin API for volume, network,
    23     IPAM and authz. This is a very stable API. When we update this API, then
    24     pluginType should include a version. eg "networkdriver/2.0".
    25  */
    26  const defaultAPIVersion string = "1.0"
    27  
    28  // ErrNotFound indicates that a plugin was not found locally.
    29  type ErrNotFound string
    30  
    31  func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
    32  
    33  // ErrAmbiguous indicates that a plugin was not found locally.
    34  type ErrAmbiguous string
    35  
    36  func (name ErrAmbiguous) Error() string {
    37  	return fmt.Sprintf("multiple plugins found for %q", string(name))
    38  }
    39  
    40  // GetByName retreives a plugin by name.
    41  func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
    42  	ps.RLock()
    43  	defer ps.RUnlock()
    44  
    45  	id, nameOk := ps.nameToID[name]
    46  	if !nameOk {
    47  		return nil, ErrNotFound(name)
    48  	}
    49  
    50  	p, idOk := ps.plugins[id]
    51  	if !idOk {
    52  		return nil, ErrNotFound(id)
    53  	}
    54  	return p, nil
    55  }
    56  
    57  // GetByID retreives a plugin by ID.
    58  func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
    59  	ps.RLock()
    60  	defer ps.RUnlock()
    61  
    62  	p, idOk := ps.plugins[id]
    63  	if !idOk {
    64  		return nil, ErrNotFound(id)
    65  	}
    66  	return p, nil
    67  }
    68  
    69  // GetAll retreives all plugins.
    70  func (ps *Store) GetAll() map[string]*v2.Plugin {
    71  	ps.RLock()
    72  	defer ps.RUnlock()
    73  	return ps.plugins
    74  }
    75  
    76  // SetAll initialized plugins during daemon restore.
    77  func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
    78  	ps.Lock()
    79  	defer ps.Unlock()
    80  	ps.plugins = plugins
    81  }
    82  
    83  func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
    84  	ps.RLock()
    85  	defer ps.RUnlock()
    86  
    87  	result := make([]plugingetter.CompatPlugin, 0, 1)
    88  	for _, p := range ps.plugins {
    89  		if p.IsEnabled() {
    90  			if _, err := p.FilterByCap(capability); err == nil {
    91  				result = append(result, p)
    92  			}
    93  		}
    94  	}
    95  	return result
    96  }
    97  
    98  // SetState sets the active state of the plugin and updates plugindb.
    99  func (ps *Store) SetState(p *v2.Plugin, state bool) {
   100  	ps.Lock()
   101  	defer ps.Unlock()
   102  
   103  	p.PluginObj.Enabled = state
   104  	ps.updatePluginDB()
   105  }
   106  
   107  // Add adds a plugin to memory and plugindb.
   108  // An error will be returned if there is a collision.
   109  func (ps *Store) Add(p *v2.Plugin) error {
   110  	ps.Lock()
   111  	defer ps.Unlock()
   112  
   113  	if v, exist := ps.plugins[p.GetID()]; exist {
   114  		return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
   115  	}
   116  	if _, exist := ps.nameToID[p.Name()]; exist {
   117  		return fmt.Errorf("plugin %q already exists", p.Name())
   118  	}
   119  	ps.plugins[p.GetID()] = p
   120  	ps.nameToID[p.Name()] = p.GetID()
   121  	ps.updatePluginDB()
   122  	return nil
   123  }
   124  
   125  // Update updates a plugin to memory and plugindb.
   126  func (ps *Store) Update(p *v2.Plugin) {
   127  	ps.Lock()
   128  	defer ps.Unlock()
   129  
   130  	ps.plugins[p.GetID()] = p
   131  	ps.nameToID[p.Name()] = p.GetID()
   132  	ps.updatePluginDB()
   133  }
   134  
   135  // Remove removes a plugin from memory and plugindb.
   136  func (ps *Store) Remove(p *v2.Plugin) {
   137  	ps.Lock()
   138  	delete(ps.plugins, p.GetID())
   139  	delete(ps.nameToID, p.Name())
   140  	ps.updatePluginDB()
   141  	ps.Unlock()
   142  }
   143  
   144  // Callers are expected to hold the store lock.
   145  func (ps *Store) updatePluginDB() error {
   146  	jsonData, err := json.Marshal(ps.plugins)
   147  	if err != nil {
   148  		logrus.Debugf("Error in json.Marshal: %v", err)
   149  		return err
   150  	}
   151  	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
   152  	return nil
   153  }
   154  
   155  // Get returns an enabled plugin matching the given name and capability.
   156  func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
   157  	var (
   158  		p   *v2.Plugin
   159  		err error
   160  	)
   161  
   162  	// Lookup using new model.
   163  	if ps != nil {
   164  		fullName := name
   165  		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
   166  			if reference.IsNameOnly(named) {
   167  				named = reference.WithDefaultTag(named)
   168  			}
   169  			ref, ok := named.(reference.NamedTagged)
   170  			if !ok {
   171  				return nil, fmt.Errorf("invalid name: %s", named.String())
   172  			}
   173  			fullName = ref.String()
   174  		}
   175  		p, err = ps.GetByName(fullName)
   176  		if err == nil {
   177  			p.Lock()
   178  			p.RefCount += mode
   179  			p.Unlock()
   180  			if p.IsEnabled() {
   181  				return p.FilterByCap(capability)
   182  			}
   183  			// Plugin was found but it is disabled, so we should not fall back to legacy plugins
   184  			// but we should error out right away
   185  			return nil, ErrNotFound(fullName)
   186  		}
   187  		if _, ok := err.(ErrNotFound); !ok {
   188  			return nil, err
   189  		}
   190  	}
   191  
   192  	// Lookup using legacy model.
   193  	if allowV1PluginsFallback {
   194  		p, err := plugins.Get(name, capability)
   195  		if err != nil {
   196  			return nil, fmt.Errorf("legacy plugin: %v", err)
   197  		}
   198  		return p, nil
   199  	}
   200  
   201  	return nil, err
   202  }
   203  
   204  // GetAllByCap returns a list of enabled plugins matching the given capability.
   205  func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
   206  	result := make([]plugingetter.CompatPlugin, 0, 1)
   207  
   208  	/* Daemon start always calls plugin.Init thereby initializing a store.
   209  	 * So store on experimental builds can never be nil, even while
   210  	 * handling legacy plugins. However, there are legacy plugin unit
   211  	 * tests where the volume subsystem directly talks with the plugin,
   212  	 * bypassing the daemon. For such tests, this check is necessary.
   213  	 */
   214  	if ps != nil {
   215  		ps.RLock()
   216  		result = ps.getAllByCap(capability)
   217  		ps.RUnlock()
   218  	}
   219  
   220  	// Lookup with legacy model
   221  	if allowV1PluginsFallback {
   222  		pl, err := plugins.GetAll(capability)
   223  		if err != nil {
   224  			return nil, fmt.Errorf("legacy plugin: %v", err)
   225  		}
   226  		for _, p := range pl {
   227  			result = append(result, p)
   228  		}
   229  	}
   230  	return result, nil
   231  }
   232  
   233  // Handle sets a callback for a given capability. It is only used by network
   234  // and ipam drivers during plugin registration. The callback registers the
   235  // driver with the subsystem (network, ipam).
   236  func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
   237  	pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
   238  
   239  	// Register callback with new plugin model.
   240  	ps.Lock()
   241  	handlers, ok := ps.handlers[pluginType]
   242  	if !ok {
   243  		handlers = []func(string, *plugins.Client){}
   244  	}
   245  	handlers = append(handlers, callback)
   246  	ps.handlers[pluginType] = handlers
   247  	ps.Unlock()
   248  
   249  	// Register callback with legacy plugin model.
   250  	if allowV1PluginsFallback {
   251  		plugins.Handle(capability, callback)
   252  	}
   253  }
   254  
   255  // CallHandler calls the registered callback. It is invoked during plugin enable.
   256  func (ps *Store) CallHandler(p *v2.Plugin) {
   257  	for _, typ := range p.GetTypes() {
   258  		for _, handler := range ps.handlers[typ.String()] {
   259  			handler(p.Name(), p.Client())
   260  		}
   261  	}
   262  }
   263  
   264  // Search retreives a plugin by ID Prefix
   265  // If no plugin is found, then ErrNotFound is returned
   266  // If multiple plugins are found, then ErrAmbiguous is returned
   267  func (ps *Store) Search(partialID string) (*v2.Plugin, error) {
   268  	ps.RLock()
   269  	defer ps.RUnlock()
   270  
   271  	var found *v2.Plugin
   272  	for id, p := range ps.plugins {
   273  		if strings.HasPrefix(id, partialID) {
   274  			if found != nil {
   275  				return nil, ErrAmbiguous(partialID)
   276  			}
   277  			found = p
   278  		}
   279  	}
   280  	if found == nil {
   281  		return nil, ErrNotFound(partialID)
   282  	}
   283  	return found, nil
   284  }