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