github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/store/store_experimental.go (about)

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