github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/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  // GetByName retreives a plugin by name.
    34  func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
    35  	ps.RLock()
    36  	defer ps.RUnlock()
    37  
    38  	id, nameOk := ps.nameToID[name]
    39  	if !nameOk {
    40  		return nil, ErrNotFound(name)
    41  	}
    42  
    43  	p, idOk := ps.plugins[id]
    44  	if !idOk {
    45  		return nil, ErrNotFound(id)
    46  	}
    47  	return p, nil
    48  }
    49  
    50  // GetByID retreives a plugin by ID.
    51  func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
    52  	ps.RLock()
    53  	defer ps.RUnlock()
    54  
    55  	p, idOk := ps.plugins[id]
    56  	if !idOk {
    57  		return nil, ErrNotFound(id)
    58  	}
    59  	return p, nil
    60  }
    61  
    62  // GetAll retreives all plugins.
    63  func (ps *Store) GetAll() map[string]*v2.Plugin {
    64  	ps.RLock()
    65  	defer ps.RUnlock()
    66  	return ps.plugins
    67  }
    68  
    69  // SetAll initialized plugins during daemon restore.
    70  func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
    71  	ps.Lock()
    72  	defer ps.Unlock()
    73  	ps.plugins = plugins
    74  }
    75  
    76  func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
    77  	ps.RLock()
    78  	defer ps.RUnlock()
    79  
    80  	result := make([]plugingetter.CompatPlugin, 0, 1)
    81  	for _, p := range ps.plugins {
    82  		if _, err := p.FilterByCap(capability); err == nil {
    83  			result = append(result, p)
    84  		}
    85  	}
    86  	return result
    87  }
    88  
    89  // SetState sets the active state of the plugin and updates plugindb.
    90  func (ps *Store) SetState(p *v2.Plugin, state bool) {
    91  	ps.Lock()
    92  	defer ps.Unlock()
    93  
    94  	p.PluginObj.Enabled = state
    95  	ps.updatePluginDB()
    96  }
    97  
    98  // Add adds a plugin to memory and plugindb.
    99  func (ps *Store) Add(p *v2.Plugin) {
   100  	ps.Lock()
   101  	ps.plugins[p.GetID()] = p
   102  	ps.nameToID[p.Name()] = p.GetID()
   103  	ps.updatePluginDB()
   104  	ps.Unlock()
   105  }
   106  
   107  // Remove removes a plugin from memory and plugindb.
   108  func (ps *Store) Remove(p *v2.Plugin) {
   109  	ps.Lock()
   110  	delete(ps.plugins, p.GetID())
   111  	delete(ps.nameToID, p.Name())
   112  	ps.updatePluginDB()
   113  	ps.Unlock()
   114  }
   115  
   116  // Callers are expected to hold the store lock.
   117  func (ps *Store) updatePluginDB() error {
   118  	jsonData, err := json.Marshal(ps.plugins)
   119  	if err != nil {
   120  		logrus.Debugf("Error in json.Marshal: %v", err)
   121  		return err
   122  	}
   123  	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
   124  	return nil
   125  }
   126  
   127  // Get returns a plugin matching the given name and capability.
   128  func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
   129  	var (
   130  		p   *v2.Plugin
   131  		err error
   132  	)
   133  
   134  	// Lookup using new model.
   135  	if ps != nil {
   136  		fullName := name
   137  		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
   138  			if reference.IsNameOnly(named) {
   139  				named = reference.WithDefaultTag(named)
   140  			}
   141  			ref, ok := named.(reference.NamedTagged)
   142  			if !ok {
   143  				return nil, fmt.Errorf("invalid name: %s", named.String())
   144  			}
   145  			fullName = ref.String()
   146  		}
   147  		p, err = ps.GetByName(fullName)
   148  		if err == nil {
   149  			p.Lock()
   150  			p.RefCount += mode
   151  			p.Unlock()
   152  			return p.FilterByCap(capability)
   153  		}
   154  		if _, ok := err.(ErrNotFound); !ok {
   155  			return nil, err
   156  		}
   157  	}
   158  
   159  	// Lookup using legacy model.
   160  	if allowV1PluginsFallback {
   161  		p, err := plugins.Get(name, capability)
   162  		if err != nil {
   163  			return nil, fmt.Errorf("legacy plugin: %v", err)
   164  		}
   165  		return p, nil
   166  	}
   167  
   168  	return nil, err
   169  }
   170  
   171  // GetAllByCap returns a list of plugins matching the given capability.
   172  func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
   173  	result := make([]plugingetter.CompatPlugin, 0, 1)
   174  
   175  	/* Daemon start always calls plugin.Init thereby initializing a store.
   176  	 * So store on experimental builds can never be nil, even while
   177  	 * handling legacy plugins. However, there are legacy plugin unit
   178  	 * tests where the volume subsystem directly talks with the plugin,
   179  	 * bypassing the daemon. For such tests, this check is necessary.
   180  	 */
   181  	if ps != nil {
   182  		ps.RLock()
   183  		result = ps.getAllByCap(capability)
   184  		ps.RUnlock()
   185  	}
   186  
   187  	// Lookup with legacy model
   188  	if allowV1PluginsFallback {
   189  		pl, err := plugins.GetAll(capability)
   190  		if err != nil {
   191  			return nil, fmt.Errorf("legacy plugin: %v", err)
   192  		}
   193  		for _, p := range pl {
   194  			result = append(result, p)
   195  		}
   196  	}
   197  	return result, nil
   198  }
   199  
   200  // Handle sets a callback for a given capability. It is only used by network
   201  // and ipam drivers during plugin registration. The callback registers the
   202  // driver with the subsystem (network, ipam).
   203  func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
   204  	pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
   205  
   206  	// Register callback with new plugin model.
   207  	ps.Lock()
   208  	handlers, ok := ps.handlers[pluginType]
   209  	if !ok {
   210  		handlers = []func(string, *plugins.Client){}
   211  	}
   212  	handlers = append(handlers, callback)
   213  	ps.handlers[pluginType] = handlers
   214  	ps.Unlock()
   215  
   216  	// Register callback with legacy plugin model.
   217  	if allowV1PluginsFallback {
   218  		plugins.Handle(capability, callback)
   219  	}
   220  }
   221  
   222  // CallHandler calls the registered callback. It is invoked during plugin enable.
   223  func (ps *Store) CallHandler(p *v2.Plugin) {
   224  	for _, typ := range p.GetTypes() {
   225  		for _, handler := range ps.handlers[typ.String()] {
   226  			handler(p.Name(), p.Client())
   227  		}
   228  	}
   229  }