Browse code

Make graphdrivers work with pluginv2.

As part of making graphdrivers support pluginv2, a PluginGetter
interface was necessary for cleaner separation and avoiding import
cycles.

This commit creates a PluginGetter interface and makes pluginStore
implement it. Then the pluginStore object is created in the daemon
(rather than by the plugin manager) and passed to plugin init as
well as to the different subsystems (eg. graphdrivers, volumedrivers).
A side effect of this change was that some code was moved out of
experimental. This is good, since plugin support will be stable soon.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>

Anusha Ragunathan authored on 2016/09/08 09:01:10
Showing 24 changed files
... ...
@@ -1,5 +1,3 @@
1
-// +build experimental
2
-
3 1
 package types
4 2
 
5 3
 import (
6 4
old mode 100755
7 5
new mode 100644
... ...
@@ -47,6 +47,7 @@ import (
47 47
 	"github.com/docker/docker/pkg/sysinfo"
48 48
 	"github.com/docker/docker/pkg/system"
49 49
 	"github.com/docker/docker/pkg/truncindex"
50
+	pluginstore "github.com/docker/docker/plugin/store"
50 51
 	"github.com/docker/docker/reference"
51 52
 	"github.com/docker/docker/registry"
52 53
 	"github.com/docker/docker/runconfig"
... ...
@@ -94,6 +95,7 @@ type Daemon struct {
94 94
 	gidMaps                   []idtools.IDMap
95 95
 	layerStore                layer.Store
96 96
 	imageStore                image.Store
97
+	pluginStore               *pluginstore.Store
97 98
 	nameIndex                 *registrar.Registrar
98 99
 	linkIndex                 *linkIndex
99 100
 	containerd                libcontainerd.Client
... ...
@@ -548,6 +550,9 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
548 548
 	if driverName == "" {
549 549
 		driverName = config.GraphDriver
550 550
 	}
551
+
552
+	d.pluginStore = pluginstore.NewStore(config.Root)
553
+
551 554
 	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
552 555
 		StorePath:                 config.Root,
553 556
 		MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
... ...
@@ -555,6 +560,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
555 555
 		GraphDriverOptions:        config.GraphOptions,
556 556
 		UIDMaps:                   uidMaps,
557 557
 		GIDMaps:                   gidMaps,
558
+		PluginGetter:              d.pluginStore,
558 559
 	})
559 560
 	if err != nil {
560 561
 		return nil, err
... ...
@@ -911,6 +917,8 @@ func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore
911 911
 		return nil, err
912 912
 	}
913 913
 
914
+	volumedrivers.RegisterPluginGetter(daemon.pluginStore)
915
+
914 916
 	if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) {
915 917
 		return nil, fmt.Errorf("local volume driver could not be registered")
916 918
 	}
... ...
@@ -13,7 +13,7 @@ func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.
13 13
 }
14 14
 
15 15
 func pluginInit(d *Daemon, cfg *Config, remote libcontainerd.Remote) error {
16
-	return plugin.Init(cfg.Root, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent)
16
+	return plugin.Init(cfg.Root, d.pluginStore, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent)
17 17
 }
18 18
 
19 19
 func pluginShutdown() {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/docker/docker/pkg/archive"
14 14
 	"github.com/docker/docker/pkg/idtools"
15
+	"github.com/docker/docker/plugin/getter"
15 16
 )
16 17
 
17 18
 // FsMagic unsigned id of the filesystem in use.
... ...
@@ -134,11 +135,11 @@ func Register(name string, initFunc InitFunc) error {
134 134
 }
135 135
 
136 136
 // GetDriver initializes and returns the registered driver
137
-func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
137
+func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
138 138
 	if initFunc, exists := drivers[name]; exists {
139 139
 		return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
140 140
 	}
141
-	if pluginDriver, err := lookupPlugin(name, home, options); err == nil {
141
+	if pluginDriver, err := lookupPlugin(name, home, options, plugingetter); err == nil {
142 142
 		return pluginDriver, nil
143 143
 	}
144 144
 	logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
... ...
@@ -155,10 +156,10 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id
155 155
 }
156 156
 
157 157
 // New creates the driver and initializes it at the specified root.
158
-func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
158
+func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
159 159
 	if name != "" {
160 160
 		logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
161
-		return GetDriver(name, root, options, uidMaps, gidMaps)
161
+		return GetDriver(name, root, options, uidMaps, gidMaps, plugingetter)
162 162
 	}
163 163
 
164 164
 	// Guess for prior driver
... ...
@@ -41,7 +41,7 @@ func newDriver(t testing.TB, name string, options []string) *Driver {
41 41
 		t.Fatal(err)
42 42
 	}
43 43
 
44
-	d, err := graphdriver.GetDriver(name, root, options, nil, nil)
44
+	d, err := graphdriver.GetDriver(name, root, options, nil, nil, nil)
45 45
 	if err != nil {
46 46
 		t.Logf("graphdriver: %v\n", err)
47 47
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"fmt"
7 7
 	"io"
8 8
 
9
-	"github.com/docker/docker/pkg/plugins"
9
+	"github.com/docker/docker/plugin/getter"
10 10
 )
11 11
 
12 12
 type pluginClient interface {
... ...
@@ -18,8 +18,8 @@ type pluginClient interface {
18 18
 	SendFile(string, io.Reader, interface{}) error
19 19
 }
20 20
 
21
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
22
-	pl, err := plugins.Get(name, "GraphDriver")
21
+func lookupPlugin(name, home string, opts []string, pluginGetter getter.PluginGetter) (Driver, error) {
22
+	pl, err := pluginGetter.Get(name, "GraphDriver", getter.LOOKUP)
23 23
 	if err != nil {
24 24
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
25 25
 	}
... ...
@@ -2,6 +2,8 @@
2 2
 
3 3
 package graphdriver
4 4
 
5
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
5
+import "github.com/docker/docker/plugin/getter"
6
+
7
+func lookupPlugin(name, home string, opts []string, plugingetter getter.PluginGetter) (Driver, error) {
6 8
 	return nil, ErrNotSupported
7 9
 }
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"github.com/docker/docker/pkg/archive"
15 15
 	"github.com/docker/docker/pkg/idtools"
16 16
 	"github.com/docker/docker/pkg/stringid"
17
+	"github.com/docker/docker/plugin/getter"
17 18
 	"github.com/vbatts/tar-split/tar/asm"
18 19
 	"github.com/vbatts/tar-split/tar/storage"
19 20
 )
... ...
@@ -44,6 +45,7 @@ type StoreOptions struct {
44 44
 	GraphDriverOptions        []string
45 45
 	UIDMaps                   []idtools.IDMap
46 46
 	GIDMaps                   []idtools.IDMap
47
+	PluginGetter              getter.PluginGetter
47 48
 }
48 49
 
49 50
 // NewStoreFromOptions creates a new Store instance
... ...
@@ -53,7 +55,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
53 53
 		options.GraphDriver,
54 54
 		options.GraphDriverOptions,
55 55
 		options.UIDMaps,
56
-		options.GIDMaps)
56
+		options.GIDMaps,
57
+		options.PluginGetter)
57 58
 	if err != nil {
58 59
 		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
59 60
 	}
... ...
@@ -39,7 +39,7 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
39 39
 		},
40 40
 	}
41 41
 
42
-	return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap)
42
+	return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap, nil)
43 43
 }
44 44
 
45 45
 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
... ...
@@ -83,8 +83,8 @@ func (p *Plugin) Client() *Client {
83 83
 	return p.client
84 84
 }
85 85
 
86
-// IsLegacy returns true for legacy plugins and false otherwise.
87
-func (p *Plugin) IsLegacy() bool {
86
+// IsV1 returns true for V1 plugins and false otherwise.
87
+func (p *Plugin) IsV1() bool {
88 88
 	return true
89 89
 }
90 90
 
... ...
@@ -17,7 +17,6 @@ import (
17 17
 	"github.com/docker/docker/pkg/stringid"
18 18
 	"github.com/docker/docker/plugin/distribution"
19 19
 	"github.com/docker/docker/plugin/v2"
20
-	"github.com/docker/docker/reference"
21 20
 )
22 21
 
23 22
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
... ...
@@ -57,9 +56,9 @@ func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
57 57
 
58 58
 // Pull pulls a plugin and computes the privileges required to install it.
59 59
 func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
60
-	ref, err := reference.ParseNamed(name)
60
+	ref, err := distribution.GetRef(name)
61 61
 	if err != nil {
62
-		logrus.Debugf("error in reference.ParseNamed: %v", err)
62
+		logrus.Debugf("error in distribution.GetRef: %v", err)
63 63
 		return nil, err
64 64
 	}
65 65
 	name = ref.String()
... ...
@@ -76,7 +75,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
76 76
 		return nil, err
77 77
 	}
78 78
 
79
-	pd, err := distribution.Pull(name, pm.registryService, metaHeader, authConfig)
79
+	pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
80 80
 	if err != nil {
81 81
 		logrus.Debugf("error in distribution.Pull(): %v", err)
82 82
 		return nil, err
... ...
@@ -87,10 +86,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
87 87
 		return nil, err
88 88
 	}
89 89
 
90
-	var tag string
91
-	if ref, ok := ref.(reference.NamedTagged); ok {
92
-		tag = ref.Tag()
93
-	}
90
+	tag := distribution.GetTag(ref)
94 91
 	p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag)
95 92
 	if err := p.InitPlugin(pm.libRoot); err != nil {
96 93
 		return nil, err
... ...
@@ -62,14 +62,26 @@ func (pd *pullData) Layer() (io.ReadCloser, error) {
62 62
 	return rsc, nil
63 63
 }
64 64
 
65
-// Pull downloads the plugin from Store
66
-func Pull(name string, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
65
+// GetRef returns the distribution reference for a given name.
66
+func GetRef(name string) (reference.Named, error) {
67 67
 	ref, err := reference.ParseNamed(name)
68 68
 	if err != nil {
69
-		logrus.Debugf("pull.go: error in ParseNamed: %v", err)
70 69
 		return nil, err
71 70
 	}
71
+	return ref, nil
72
+}
72 73
 
74
+// GetTag returns the tag associated with the given reference name.
75
+func GetTag(ref reference.Named) string {
76
+	tag := DefaultTag
77
+	if ref, ok := ref.(reference.NamedTagged); ok {
78
+		tag = ref.Tag()
79
+	}
80
+	return tag
81
+}
82
+
83
+// Pull downloads the plugin from Store
84
+func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
73 85
 	repoInfo, err := rs.ResolveRepository(ref)
74 86
 	if err != nil {
75 87
 		logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
76 88
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package getter
1
+
2
+import "github.com/docker/docker/pkg/plugins"
3
+
4
+const (
5
+	// LOOKUP doesn't update RefCount
6
+	LOOKUP = 0
7
+	// CREATE increments RefCount
8
+	CREATE = 1
9
+	// REMOVE decrements RefCount
10
+	REMOVE = -1
11
+)
12
+
13
+// CompatPlugin is a abstraction to handle both v2(new) and v1(legacy) plugins.
14
+type CompatPlugin interface {
15
+	Client() *plugins.Client
16
+	Name() string
17
+	IsV1() bool
18
+}
19
+
20
+// PluginGetter is the interface implemented by Store
21
+type PluginGetter interface {
22
+	Get(name, capability string, mode int) (CompatPlugin, error)
23
+	GetAllByCap(capability string) ([]CompatPlugin, error)
24
+}
... ...
@@ -35,7 +35,7 @@ type Manager struct {
35 35
 	sync.RWMutex
36 36
 	libRoot           string
37 37
 	runRoot           string
38
-	pluginStore       *store.PluginStore
38
+	pluginStore       *store.Store
39 39
 	containerdClient  libcontainerd.Client
40 40
 	registryService   registry.Service
41 41
 	liveRestore       bool
... ...
@@ -50,7 +50,7 @@ func GetManager() *Manager {
50 50
 
51 51
 // Init (was NewManager) instantiates the singleton Manager.
52 52
 // TODO: revert this to NewManager once we get rid of all the singletons.
53
-func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
53
+func Init(root string, ps *store.Store, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
54 54
 	if manager != nil {
55 55
 		return nil
56 56
 	}
... ...
@@ -59,7 +59,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes
59 59
 	manager = &Manager{
60 60
 		libRoot:           root,
61 61
 		runRoot:           "/run/docker",
62
-		pluginStore:       store.NewPluginStore(root),
62
+		pluginStore:       ps,
63 63
 		registryService:   rs,
64 64
 		liveRestore:       liveRestore,
65 65
 		pluginEventLogger: evL,
66 66
new file mode 100644
... ...
@@ -0,0 +1,31 @@
0
+package store
1
+
2
+import (
3
+	"path/filepath"
4
+	"sync"
5
+
6
+	"github.com/docker/docker/pkg/plugins"
7
+	"github.com/docker/docker/plugin/v2"
8
+)
9
+
10
+// Store manages the plugin inventory in memory and on-disk
11
+type Store struct {
12
+	sync.RWMutex
13
+	plugins map[string]*v2.Plugin
14
+	/* handlers are necessary for transition path of legacy plugins
15
+	 * to the new model. Legacy plugins use Handle() for registering an
16
+	 * activation callback.*/
17
+	handlers map[string]func(string, *plugins.Client)
18
+	nameToID map[string]string
19
+	plugindb string
20
+}
21
+
22
+// NewStore creates a Store.
23
+func NewStore(libRoot string) *Store {
24
+	return &Store{
25
+		plugins:  make(map[string]*v2.Plugin),
26
+		handlers: make(map[string]func(string, *plugins.Client)),
27
+		nameToID: make(map[string]string),
28
+		plugindb: filepath.Join(libRoot, "plugins", "plugins.json"),
29
+	}
30
+}
0 31
deleted file mode 100644
... ...
@@ -1,19 +0,0 @@
1
-package store
2
-
3
-import "github.com/docker/docker/pkg/plugins"
4
-
5
-const (
6
-	// LOOKUP doesn't update RefCount
7
-	LOOKUP = 0
8
-	// CREATE increments RefCount
9
-	CREATE = 1
10
-	// REMOVE decrements RefCount
11
-	REMOVE = -1
12
-)
13
-
14
-// CompatPlugin is an abstraction to handle both new and legacy (v1) plugins.
15
-type CompatPlugin interface {
16
-	Client() *plugins.Client
17
-	Name() string
18
-	IsLegacy() bool
19
-}
20 1
deleted file mode 100644
... ...
@@ -1,25 +0,0 @@
1
-// +build !experimental
2
-
3
-package store
4
-
5
-import (
6
-	"github.com/docker/docker/pkg/plugins"
7
-)
8
-
9
-// FindWithCapability returns a list of plugins matching the given capability.
10
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
11
-	pl, err := plugins.GetAll(capability)
12
-	if err != nil {
13
-		return nil, err
14
-	}
15
-	result := make([]CompatPlugin, len(pl))
16
-	for i, p := range pl {
17
-		result[i] = p
18
-	}
19
-	return result, nil
20
-}
21
-
22
-// LookupWithCapability returns a plugin matching the given name and capability.
23
-func LookupWithCapability(name, capability string, _ int) (CompatPlugin, error) {
24
-	return plugins.Get(name, capability)
25
-}
... ...
@@ -1,257 +1,26 @@
1
-// +build experimental
1
+// +build !experimental
2 2
 
3 3
 package store
4 4
 
5 5
 import (
6
-	"encoding/json"
7
-	"fmt"
8
-	"path/filepath"
9
-	"strings"
10
-	"sync"
11
-
12
-	"github.com/Sirupsen/logrus"
13
-	"github.com/docker/docker/pkg/ioutils"
14 6
 	"github.com/docker/docker/pkg/plugins"
15
-	"github.com/docker/docker/plugin/v2"
16
-	"github.com/docker/docker/reference"
17
-)
18
-
19
-var (
20
-	store *PluginStore
21
-	/* allowV1PluginsFallback determines daemon's support for V1 plugins.
22
-	 * When the time comes to remove support for V1 plugins, flipping
23
-	 * this bool is all that will be needed.
24
-	 */
25
-	allowV1PluginsFallback = true
7
+	"github.com/docker/docker/plugin/getter"
26 8
 )
27 9
 
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
-// PluginStore manages the plugin inventory in memory and on-disk
34
-type PluginStore struct {
35
-	sync.RWMutex
36
-	plugins map[string]*v2.Plugin
37
-	/* handlers are necessary for transition path of legacy plugins
38
-	 * to the new model. Legacy plugins use Handle() for registering an
39
-	 * activation callback.*/
40
-	handlers map[string]func(string, *plugins.Client)
41
-	nameToID map[string]string
42
-	plugindb string
43
-}
44
-
45
-// NewPluginStore creates a PluginStore.
46
-func NewPluginStore(libRoot string) *PluginStore {
47
-	store = &PluginStore{
48
-		plugins:  make(map[string]*v2.Plugin),
49
-		handlers: make(map[string]func(string, *plugins.Client)),
50
-		nameToID: make(map[string]string),
51
-		plugindb: filepath.Join(libRoot, "plugins.json"),
52
-	}
53
-	return store
54
-}
55
-
56
-// GetByName retreives a plugin by name.
57
-func (ps *PluginStore) GetByName(name string) (*v2.Plugin, error) {
58
-	ps.RLock()
59
-	defer ps.RUnlock()
60
-
61
-	id, nameOk := ps.nameToID[name]
62
-	if !nameOk {
63
-		return nil, ErrNotFound(name)
64
-	}
65
-
66
-	p, idOk := ps.plugins[id]
67
-	if !idOk {
68
-		return nil, ErrNotFound(id)
69
-	}
70
-	return p, nil
71
-}
72
-
73
-// GetByID retreives a plugin by ID.
74
-func (ps *PluginStore) GetByID(id string) (*v2.Plugin, error) {
75
-	ps.RLock()
76
-	defer ps.RUnlock()
77
-
78
-	p, idOk := ps.plugins[id]
79
-	if !idOk {
80
-		return nil, ErrNotFound(id)
81
-	}
82
-	return p, nil
83
-}
84
-
85
-// GetAll retreives all plugins.
86
-func (ps *PluginStore) GetAll() map[string]*v2.Plugin {
87
-	ps.RLock()
88
-	defer ps.RUnlock()
89
-	return ps.plugins
90
-}
91
-
92
-// SetAll initialized plugins during daemon restore.
93
-func (ps *PluginStore) SetAll(plugins map[string]*v2.Plugin) {
94
-	ps.Lock()
95
-	defer ps.Unlock()
96
-	ps.plugins = plugins
97
-}
98
-
99
-func (ps *PluginStore) getByCap(name string, capability string) (*v2.Plugin, error) {
100
-	ps.RLock()
101
-	defer ps.RUnlock()
102
-
103
-	p, err := ps.GetByName(name)
10
+// GetAllByCap returns a list of plugins matching the given capability.
11
+func (ps Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
12
+	pl, err := plugins.GetAll(capability)
104 13
 	if err != nil {
105 14
 		return nil, err
106 15
 	}
107
-	return p.FilterByCap(capability)
108
-}
109
-
110
-func (ps *PluginStore) getAllByCap(capability string) []CompatPlugin {
111
-	ps.RLock()
112
-	defer ps.RUnlock()
113
-
114
-	result := make([]CompatPlugin, 0, 1)
115
-	for _, p := range ps.plugins {
116
-		if _, err := p.FilterByCap(capability); err == nil {
117
-			result = append(result, p)
118
-		}
119
-	}
120
-	return result
121
-}
122
-
123
-// SetState sets the active state of the plugin and updates plugindb.
124
-func (ps *PluginStore) SetState(p *v2.Plugin, state bool) {
125
-	ps.Lock()
126
-	defer ps.Unlock()
127
-
128
-	p.PluginObj.Enabled = state
129
-	ps.updatePluginDB()
130
-}
131
-
132
-// Add adds a plugin to memory and plugindb.
133
-func (ps *PluginStore) Add(p *v2.Plugin) {
134
-	ps.Lock()
135
-	ps.plugins[p.GetID()] = p
136
-	ps.nameToID[p.Name()] = p.GetID()
137
-	ps.updatePluginDB()
138
-	ps.Unlock()
139
-}
140
-
141
-// Remove removes a plugin from memory, plugindb and disk.
142
-func (ps *PluginStore) Remove(p *v2.Plugin) {
143
-	ps.Lock()
144
-	delete(ps.plugins, p.GetID())
145
-	delete(ps.nameToID, p.Name())
146
-	ps.updatePluginDB()
147
-	p.RemoveFromDisk()
148
-	ps.Unlock()
149
-}
150
-
151
-// Callers are expected to hold the store lock.
152
-func (ps *PluginStore) updatePluginDB() error {
153
-	jsonData, err := json.Marshal(ps.plugins)
154
-	if err != nil {
155
-		logrus.Debugf("Error in json.Marshal: %v", err)
156
-		return err
157
-	}
158
-	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
159
-	return nil
160
-}
161
-
162
-// LookupWithCapability returns a plugin matching the given name and capability.
163
-func LookupWithCapability(name, capability string, mode int) (CompatPlugin, error) {
164
-	var (
165
-		p   *v2.Plugin
166
-		err error
167
-	)
168
-
169
-	// Lookup using new model.
170
-	if store != nil {
171
-		fullName := name
172
-		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
173
-			if reference.IsNameOnly(named) {
174
-				named = reference.WithDefaultTag(named)
175
-			}
176
-			ref, ok := named.(reference.NamedTagged)
177
-			if !ok {
178
-				return nil, fmt.Errorf("invalid name: %s", named.String())
179
-			}
180
-			fullName = ref.String()
181
-		}
182
-		p, err = store.GetByName(fullName)
183
-		if err == nil {
184
-			p.Lock()
185
-			p.RefCount += mode
186
-			p.Unlock()
187
-			return p.FilterByCap(capability)
188
-		}
189
-		if _, ok := err.(ErrNotFound); !ok {
190
-			return nil, err
191
-		}
192
-	}
193
-
194
-	// Lookup using legacy model.
195
-	if allowV1PluginsFallback {
196
-		p, err := plugins.Get(name, capability)
197
-		if err != nil {
198
-			return nil, fmt.Errorf("legacy plugin: %v", err)
199
-		}
200
-		return p, nil
201
-	}
202
-
203
-	return nil, err
204
-}
205
-
206
-// FindWithCapability returns a list of plugins matching the given capability.
207
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
208
-	result := make([]CompatPlugin, 0, 1)
209
-
210
-	/* Daemon start always calls plugin.Init thereby initializing a store.
211
-	 * So store on experimental builds can never be nil, even while
212
-	 * handling legacy plugins. However, there are legacy plugin unit
213
-	 * tests where the volume subsystem directly talks with the plugin,
214
-	 * bypassing the daemon. For such tests, this check is necessary.
215
-	 */
216
-	if store != nil {
217
-		store.RLock()
218
-		result = store.getAllByCap(capability)
219
-		store.RUnlock()
220
-	}
221
-
222
-	// Lookup with legacy model
223
-	if allowV1PluginsFallback {
224
-		pl, err := plugins.GetAll(capability)
225
-		if err != nil {
226
-			return nil, fmt.Errorf("legacy plugin: %v", err)
227
-		}
228
-		for _, p := range pl {
229
-			result = append(result, p)
230
-		}
16
+	result := make([]getter.CompatPlugin, len(pl))
17
+	for i, p := range pl {
18
+		result[i] = p
231 19
 	}
232 20
 	return result, nil
233 21
 }
234 22
 
235
-// Handle sets a callback for a given capability. It is only used by network
236
-// and ipam drivers during plugin registration. The callback registers the
237
-// driver with the subsystem (network, ipam).
238
-func Handle(capability string, callback func(string, *plugins.Client)) {
239
-	pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
240
-
241
-	// Register callback with new plugin model.
242
-	store.handlers[pluginType] = callback
243
-
244
-	// Register callback with legacy plugin model.
245
-	if allowV1PluginsFallback {
246
-		plugins.Handle(capability, callback)
247
-	}
248
-}
249
-
250
-// CallHandler calls the registered callback. It is invoked during plugin enable.
251
-func (ps *PluginStore) CallHandler(p *v2.Plugin) {
252
-	for _, typ := range p.GetTypes() {
253
-		if handler := ps.handlers[typ.String()]; handler != nil {
254
-			handler(p.Name(), p.Client())
255
-		}
256
-	}
23
+// Get returns a plugin matching the given name and capability.
24
+func (ps Store) Get(name, capability string, _ int) (getter.CompatPlugin, error) {
25
+	return plugins.Get(name, capability)
257 26
 }
258 27
new file mode 100644
... ...
@@ -0,0 +1,234 @@
0
+// +build experimental
1
+
2
+package store
3
+
4
+import (
5
+	"encoding/json"
6
+	"fmt"
7
+	"strings"
8
+
9
+	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/docker/pkg/ioutils"
11
+	"github.com/docker/docker/pkg/plugins"
12
+	"github.com/docker/docker/plugin/getter"
13
+	"github.com/docker/docker/plugin/v2"
14
+	"github.com/docker/docker/reference"
15
+)
16
+
17
+/* allowV1PluginsFallback determines daemon's support for V1 plugins.
18
+ * When the time comes to remove support for V1 plugins, flipping
19
+ * this bool is all that will be needed.
20
+ */
21
+var allowV1PluginsFallback = true
22
+
23
+// ErrNotFound indicates that a plugin was not found locally.
24
+type ErrNotFound string
25
+
26
+func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
27
+
28
+// GetByName retreives a plugin by name.
29
+func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
30
+	ps.RLock()
31
+	defer ps.RUnlock()
32
+
33
+	id, nameOk := ps.nameToID[name]
34
+	if !nameOk {
35
+		return nil, ErrNotFound(name)
36
+	}
37
+
38
+	p, idOk := ps.plugins[id]
39
+	if !idOk {
40
+		return nil, ErrNotFound(id)
41
+	}
42
+	return p, nil
43
+}
44
+
45
+// GetByID retreives a plugin by ID.
46
+func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
47
+	ps.RLock()
48
+	defer ps.RUnlock()
49
+
50
+	p, idOk := ps.plugins[id]
51
+	if !idOk {
52
+		return nil, ErrNotFound(id)
53
+	}
54
+	return p, nil
55
+}
56
+
57
+// GetAll retreives all plugins.
58
+func (ps *Store) GetAll() map[string]*v2.Plugin {
59
+	ps.RLock()
60
+	defer ps.RUnlock()
61
+	return ps.plugins
62
+}
63
+
64
+// SetAll initialized plugins during daemon restore.
65
+func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
66
+	ps.Lock()
67
+	defer ps.Unlock()
68
+	ps.plugins = plugins
69
+}
70
+
71
+func (ps *Store) getByCap(name string, capability string) (*v2.Plugin, error) {
72
+	ps.RLock()
73
+	defer ps.RUnlock()
74
+
75
+	p, err := ps.GetByName(name)
76
+	if err != nil {
77
+		return nil, err
78
+	}
79
+	return p.FilterByCap(capability)
80
+}
81
+
82
+func (ps *Store) getAllByCap(capability string) []getter.CompatPlugin {
83
+	ps.RLock()
84
+	defer ps.RUnlock()
85
+
86
+	result := make([]getter.CompatPlugin, 0, 1)
87
+	for _, p := range ps.plugins {
88
+		if _, err := p.FilterByCap(capability); err == nil {
89
+			result = append(result, p)
90
+		}
91
+	}
92
+	return result
93
+}
94
+
95
+// SetState sets the active state of the plugin and updates plugindb.
96
+func (ps *Store) SetState(p *v2.Plugin, state bool) {
97
+	ps.Lock()
98
+	defer ps.Unlock()
99
+
100
+	p.PluginObj.Enabled = state
101
+	ps.updatePluginDB()
102
+}
103
+
104
+// Add adds a plugin to memory and plugindb.
105
+func (ps *Store) Add(p *v2.Plugin) {
106
+	ps.Lock()
107
+	ps.plugins[p.GetID()] = p
108
+	ps.nameToID[p.Name()] = p.GetID()
109
+	ps.updatePluginDB()
110
+	ps.Unlock()
111
+}
112
+
113
+// Remove removes a plugin from memory, plugindb and disk.
114
+func (ps *Store) Remove(p *v2.Plugin) {
115
+	ps.Lock()
116
+	delete(ps.plugins, p.GetID())
117
+	delete(ps.nameToID, p.Name())
118
+	ps.updatePluginDB()
119
+	p.RemoveFromDisk()
120
+	ps.Unlock()
121
+}
122
+
123
+// Callers are expected to hold the store lock.
124
+func (ps *Store) updatePluginDB() error {
125
+	jsonData, err := json.Marshal(ps.plugins)
126
+	if err != nil {
127
+		logrus.Debugf("Error in json.Marshal: %v", err)
128
+		return err
129
+	}
130
+	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
131
+	return nil
132
+}
133
+
134
+// Get returns a plugin matching the given name and capability.
135
+func (ps *Store) Get(name, capability string, mode int) (getter.CompatPlugin, error) {
136
+	var (
137
+		p   *v2.Plugin
138
+		err error
139
+	)
140
+
141
+	// Lookup using new model.
142
+	if ps != nil {
143
+		fullName := name
144
+		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
145
+			if reference.IsNameOnly(named) {
146
+				named = reference.WithDefaultTag(named)
147
+			}
148
+			ref, ok := named.(reference.NamedTagged)
149
+			if !ok {
150
+				return nil, fmt.Errorf("invalid name: %s", named.String())
151
+			}
152
+			fullName = ref.String()
153
+		}
154
+		p, err = ps.GetByName(fullName)
155
+		if err == nil {
156
+			p.Lock()
157
+			p.RefCount += mode
158
+			p.Unlock()
159
+			return p.FilterByCap(capability)
160
+		}
161
+		if _, ok := err.(ErrNotFound); !ok {
162
+			return nil, err
163
+		}
164
+	}
165
+
166
+	// Lookup using legacy model.
167
+	if allowV1PluginsFallback {
168
+		p, err := plugins.Get(name, capability)
169
+		if err != nil {
170
+			return nil, fmt.Errorf("legacy plugin: %v", err)
171
+		}
172
+		return p, nil
173
+	}
174
+
175
+	return nil, err
176
+}
177
+
178
+// GetAllByCap returns a list of plugins matching the given capability.
179
+func (ps *Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
180
+	result := make([]getter.CompatPlugin, 0, 1)
181
+
182
+	/* Daemon start always calls plugin.Init thereby initializing a store.
183
+	 * So store on experimental builds can never be nil, even while
184
+	 * handling legacy plugins. However, there are legacy plugin unit
185
+	 * tests where the volume subsystem directly talks with the plugin,
186
+	 * bypassing the daemon. For such tests, this check is necessary.
187
+	 */
188
+	if ps != nil {
189
+		ps.RLock()
190
+		result = ps.getAllByCap(capability)
191
+		ps.RUnlock()
192
+	}
193
+
194
+	// Lookup with legacy model
195
+	if allowV1PluginsFallback {
196
+		pl, err := plugins.GetAll(capability)
197
+		if err != nil {
198
+			return nil, fmt.Errorf("legacy plugin: %v", err)
199
+		}
200
+		for _, p := range pl {
201
+			result = append(result, p)
202
+		}
203
+	}
204
+	return result, nil
205
+}
206
+
207
+// Handle sets a callback for a given capability. It is only used by network
208
+// and ipam drivers during plugin registration. The callback registers the
209
+// driver with the subsystem (network, ipam).
210
+func (ps Store) Handle(capability string, callback func(string, *plugins.Client)) {
211
+	pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
212
+
213
+	store := &ps
214
+
215
+	// Register callback with new plugin model.
216
+	store.Lock()
217
+	store.handlers[pluginType] = callback
218
+	store.Unlock()
219
+
220
+	// Register callback with legacy plugin model.
221
+	if allowV1PluginsFallback {
222
+		plugins.Handle(capability, callback)
223
+	}
224
+}
225
+
226
+// CallHandler calls the registered callback. It is invoked during plugin enable.
227
+func (ps *Store) CallHandler(p *v2.Plugin) {
228
+	for _, typ := range p.GetTypes() {
229
+		if handler := ps.handlers[typ.String()]; handler != nil {
230
+			handler(p.Name(), p.Client())
231
+		}
232
+	}
233
+}
... ...
@@ -1,32 +1,13 @@
1
-// +build experimental
2
-
3 1
 package v2
4 2
 
5 3
 import (
6
-	"encoding/json"
7
-	"errors"
8
-	"fmt"
9
-	"os"
10
-	"path/filepath"
11
-	"strings"
12 4
 	"sync"
13 5
 
14 6
 	"github.com/docker/docker/api/types"
15 7
 	"github.com/docker/docker/pkg/plugins"
16
-	"github.com/docker/docker/pkg/system"
17 8
 	"github.com/docker/docker/restartmanager"
18
-	"github.com/opencontainers/runtime-spec/specs-go"
19 9
 )
20 10
 
21
-const defaultPluginRuntimeDestination = "/run/docker/plugins"
22
-
23
-// ErrInadequateCapability indicates that the plugin did not have the requested capability.
24
-type ErrInadequateCapability string
25
-
26
-func (cap ErrInadequateCapability) Error() string {
27
-	return fmt.Sprintf("plugin does not provide %q capability", cap)
28
-}
29
-
30 11
 // Plugin represents an individual plugin.
31 12
 type Plugin struct {
32 13
 	sync.RWMutex
... ...
@@ -37,226 +18,3 @@ type Plugin struct {
37 37
 	ExitChan          chan bool                     `json:"-"`
38 38
 	RefCount          int                           `json:"-"`
39 39
 }
40
-
41
-func newPluginObj(name, id, tag string) types.Plugin {
42
-	return types.Plugin{Name: name, ID: id, Tag: tag}
43
-}
44
-
45
-// NewPlugin creates a plugin.
46
-func NewPlugin(name, id, runRoot, tag string) *Plugin {
47
-	return &Plugin{
48
-		PluginObj:         newPluginObj(name, id, tag),
49
-		RuntimeSourcePath: filepath.Join(runRoot, id),
50
-	}
51
-}
52
-
53
-// Client returns the plugin client.
54
-func (p *Plugin) Client() *plugins.Client {
55
-	return p.PClient
56
-}
57
-
58
-// IsLegacy returns true for legacy plugins and false otherwise.
59
-func (p *Plugin) IsLegacy() bool {
60
-	return false
61
-}
62
-
63
-// Name returns the plugin name.
64
-func (p *Plugin) Name() string {
65
-	name := p.PluginObj.Name
66
-	if len(p.PluginObj.Tag) > 0 {
67
-		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
68
-		name += ":" + p.PluginObj.Tag
69
-	}
70
-	return name
71
-}
72
-
73
-// FilterByCap query the plugin for a given capability.
74
-func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
75
-	capability = strings.ToLower(capability)
76
-	for _, typ := range p.PluginObj.Manifest.Interface.Types {
77
-		if typ.Capability == capability && typ.Prefix == "docker" {
78
-			return p, nil
79
-		}
80
-	}
81
-	return nil, ErrInadequateCapability(capability)
82
-}
83
-
84
-// RemoveFromDisk deletes the plugin's runtime files from disk.
85
-func (p *Plugin) RemoveFromDisk() error {
86
-	return os.RemoveAll(p.RuntimeSourcePath)
87
-}
88
-
89
-// InitPlugin populates the plugin object from the plugin manifest file.
90
-func (p *Plugin) InitPlugin(libRoot string) error {
91
-	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
92
-	if err != nil {
93
-		return err
94
-	}
95
-	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
96
-	dt.Close()
97
-	if err != nil {
98
-		return err
99
-	}
100
-
101
-	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
102
-	for i, mount := range p.PluginObj.Manifest.Mounts {
103
-		p.PluginObj.Config.Mounts[i] = mount
104
-	}
105
-	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
106
-	for _, env := range p.PluginObj.Manifest.Env {
107
-		if env.Value != nil {
108
-			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
109
-		}
110
-	}
111
-	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
112
-
113
-	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
114
-	if err != nil {
115
-		return err
116
-	}
117
-	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
118
-	f.Close()
119
-	return err
120
-}
121
-
122
-// Set is used to pass arguments to the plugin.
123
-func (p *Plugin) Set(args []string) error {
124
-	m := make(map[string]string, len(args))
125
-	for _, arg := range args {
126
-		i := strings.Index(arg, "=")
127
-		if i < 0 {
128
-			return fmt.Errorf("No equal sign '=' found in %s", arg)
129
-		}
130
-		m[arg[:i]] = arg[i+1:]
131
-	}
132
-	return errors.New("not implemented")
133
-}
134
-
135
-// ComputePrivileges takes the manifest file and computes the list of access necessary
136
-// for the plugin on the host.
137
-func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
138
-	m := p.PluginObj.Manifest
139
-	var privileges types.PluginPrivileges
140
-	if m.Network.Type != "null" && m.Network.Type != "bridge" {
141
-		privileges = append(privileges, types.PluginPrivilege{
142
-			Name:        "network",
143
-			Description: "",
144
-			Value:       []string{m.Network.Type},
145
-		})
146
-	}
147
-	for _, mount := range m.Mounts {
148
-		if mount.Source != nil {
149
-			privileges = append(privileges, types.PluginPrivilege{
150
-				Name:        "mount",
151
-				Description: "",
152
-				Value:       []string{*mount.Source},
153
-			})
154
-		}
155
-	}
156
-	for _, device := range m.Devices {
157
-		if device.Path != nil {
158
-			privileges = append(privileges, types.PluginPrivilege{
159
-				Name:        "device",
160
-				Description: "",
161
-				Value:       []string{*device.Path},
162
-			})
163
-		}
164
-	}
165
-	if len(m.Capabilities) > 0 {
166
-		privileges = append(privileges, types.PluginPrivilege{
167
-			Name:        "capabilities",
168
-			Description: "",
169
-			Value:       m.Capabilities,
170
-		})
171
-	}
172
-	return privileges
173
-}
174
-
175
-// IsEnabled returns the active state of the plugin.
176
-func (p *Plugin) IsEnabled() bool {
177
-	p.RLock()
178
-	defer p.RUnlock()
179
-
180
-	return p.PluginObj.Enabled
181
-}
182
-
183
-// GetID returns the plugin's ID.
184
-func (p *Plugin) GetID() string {
185
-	p.RLock()
186
-	defer p.RUnlock()
187
-
188
-	return p.PluginObj.ID
189
-}
190
-
191
-// GetSocket returns the plugin socket.
192
-func (p *Plugin) GetSocket() string {
193
-	p.RLock()
194
-	defer p.RUnlock()
195
-
196
-	return p.PluginObj.Manifest.Interface.Socket
197
-}
198
-
199
-// GetTypes returns the interface types of a plugin.
200
-func (p *Plugin) GetTypes() []types.PluginInterfaceType {
201
-	p.RLock()
202
-	defer p.RUnlock()
203
-
204
-	return p.PluginObj.Manifest.Interface.Types
205
-}
206
-
207
-// InitSpec creates an OCI spec from the plugin's config.
208
-func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
209
-	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
210
-	s.Root = specs.Root{
211
-		Path:     rootfs,
212
-		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
213
-	}
214
-
215
-	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
216
-		Source:      &p.RuntimeSourcePath,
217
-		Destination: defaultPluginRuntimeDestination,
218
-		Type:        "bind",
219
-		Options:     []string{"rbind", "rshared"},
220
-	})
221
-	for _, mount := range mounts {
222
-		m := specs.Mount{
223
-			Destination: mount.Destination,
224
-			Type:        mount.Type,
225
-			Options:     mount.Options,
226
-		}
227
-		// TODO: if nil, then it's required and user didn't set it
228
-		if mount.Source != nil {
229
-			m.Source = *mount.Source
230
-		}
231
-		if m.Source != "" && m.Type == "bind" {
232
-			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
233
-			if err != nil {
234
-				return nil, err
235
-			}
236
-			if fi.IsDir() {
237
-				if err := os.MkdirAll(m.Source, 0700); err != nil {
238
-					return nil, err
239
-				}
240
-			}
241
-		}
242
-		s.Mounts = append(s.Mounts, m)
243
-	}
244
-
245
-	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
246
-	envs[0] = "PATH=" + system.DefaultPathEnv
247
-	envs = append(envs, p.PluginObj.Config.Env...)
248
-
249
-	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
250
-	cwd := p.PluginObj.Manifest.Workdir
251
-	if len(cwd) == 0 {
252
-		cwd = "/"
253
-	}
254
-	s.Process = specs.Process{
255
-		Terminal: false,
256
-		Args:     args,
257
-		Cwd:      cwd,
258
-		Env:      envs,
259
-	}
260
-
261
-	return &s, nil
262
-}
263 40
new file mode 100644
... ...
@@ -0,0 +1,249 @@
0
+// +build experimental
1
+
2
+package v2
3
+
4
+import (
5
+	"encoding/json"
6
+	"errors"
7
+	"fmt"
8
+	"os"
9
+	"path/filepath"
10
+	"strings"
11
+
12
+	"github.com/docker/docker/api/types"
13
+	"github.com/docker/docker/pkg/plugins"
14
+	"github.com/docker/docker/pkg/system"
15
+	"github.com/opencontainers/runtime-spec/specs-go"
16
+)
17
+
18
+const defaultPluginRuntimeDestination = "/run/docker/plugins"
19
+
20
+// ErrInadequateCapability indicates that the plugin did not have the requested capability.
21
+type ErrInadequateCapability string
22
+
23
+func (cap ErrInadequateCapability) Error() string {
24
+	return fmt.Sprintf("plugin does not provide %q capability", cap)
25
+}
26
+
27
+func newPluginObj(name, id, tag string) types.Plugin {
28
+	return types.Plugin{Name: name, ID: id, Tag: tag}
29
+}
30
+
31
+// NewPlugin creates a plugin.
32
+func NewPlugin(name, id, runRoot, tag string) *Plugin {
33
+	return &Plugin{
34
+		PluginObj:         newPluginObj(name, id, tag),
35
+		RuntimeSourcePath: filepath.Join(runRoot, id),
36
+	}
37
+}
38
+
39
+// Client returns the plugin client.
40
+func (p *Plugin) Client() *plugins.Client {
41
+	return p.PClient
42
+}
43
+
44
+// IsV1 returns true for V1 plugins and false otherwise.
45
+func (p *Plugin) IsV1() bool {
46
+	return false
47
+}
48
+
49
+// Name returns the plugin name.
50
+func (p *Plugin) Name() string {
51
+	name := p.PluginObj.Name
52
+	if len(p.PluginObj.Tag) > 0 {
53
+		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
54
+		name += ":" + p.PluginObj.Tag
55
+	}
56
+	return name
57
+}
58
+
59
+// FilterByCap query the plugin for a given capability.
60
+func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
61
+	capability = strings.ToLower(capability)
62
+	for _, typ := range p.PluginObj.Manifest.Interface.Types {
63
+		if typ.Capability == capability && typ.Prefix == "docker" {
64
+			return p, nil
65
+		}
66
+	}
67
+	return nil, ErrInadequateCapability(capability)
68
+}
69
+
70
+// RemoveFromDisk deletes the plugin's runtime files from disk.
71
+func (p *Plugin) RemoveFromDisk() error {
72
+	return os.RemoveAll(p.RuntimeSourcePath)
73
+}
74
+
75
+// InitPlugin populates the plugin object from the plugin manifest file.
76
+func (p *Plugin) InitPlugin(libRoot string) error {
77
+	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
78
+	if err != nil {
79
+		return err
80
+	}
81
+	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
82
+	dt.Close()
83
+	if err != nil {
84
+		return err
85
+	}
86
+
87
+	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
88
+	for i, mount := range p.PluginObj.Manifest.Mounts {
89
+		p.PluginObj.Config.Mounts[i] = mount
90
+	}
91
+	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
92
+	for _, env := range p.PluginObj.Manifest.Env {
93
+		if env.Value != nil {
94
+			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
95
+		}
96
+	}
97
+	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
98
+
99
+	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
100
+	if err != nil {
101
+		return err
102
+	}
103
+	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
104
+	f.Close()
105
+	return err
106
+}
107
+
108
+// Set is used to pass arguments to the plugin.
109
+func (p *Plugin) Set(args []string) error {
110
+	m := make(map[string]string, len(args))
111
+	for _, arg := range args {
112
+		i := strings.Index(arg, "=")
113
+		if i < 0 {
114
+			return fmt.Errorf("No equal sign '=' found in %s", arg)
115
+		}
116
+		m[arg[:i]] = arg[i+1:]
117
+	}
118
+	return errors.New("not implemented")
119
+}
120
+
121
+// ComputePrivileges takes the manifest file and computes the list of access necessary
122
+// for the plugin on the host.
123
+func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
124
+	m := p.PluginObj.Manifest
125
+	var privileges types.PluginPrivileges
126
+	if m.Network.Type != "null" && m.Network.Type != "bridge" {
127
+		privileges = append(privileges, types.PluginPrivilege{
128
+			Name:        "network",
129
+			Description: "",
130
+			Value:       []string{m.Network.Type},
131
+		})
132
+	}
133
+	for _, mount := range m.Mounts {
134
+		if mount.Source != nil {
135
+			privileges = append(privileges, types.PluginPrivilege{
136
+				Name:        "mount",
137
+				Description: "",
138
+				Value:       []string{*mount.Source},
139
+			})
140
+		}
141
+	}
142
+	for _, device := range m.Devices {
143
+		if device.Path != nil {
144
+			privileges = append(privileges, types.PluginPrivilege{
145
+				Name:        "device",
146
+				Description: "",
147
+				Value:       []string{*device.Path},
148
+			})
149
+		}
150
+	}
151
+	if len(m.Capabilities) > 0 {
152
+		privileges = append(privileges, types.PluginPrivilege{
153
+			Name:        "capabilities",
154
+			Description: "",
155
+			Value:       m.Capabilities,
156
+		})
157
+	}
158
+	return privileges
159
+}
160
+
161
+// IsEnabled returns the active state of the plugin.
162
+func (p *Plugin) IsEnabled() bool {
163
+	p.RLock()
164
+	defer p.RUnlock()
165
+
166
+	return p.PluginObj.Enabled
167
+}
168
+
169
+// GetID returns the plugin's ID.
170
+func (p *Plugin) GetID() string {
171
+	p.RLock()
172
+	defer p.RUnlock()
173
+
174
+	return p.PluginObj.ID
175
+}
176
+
177
+// GetSocket returns the plugin socket.
178
+func (p *Plugin) GetSocket() string {
179
+	p.RLock()
180
+	defer p.RUnlock()
181
+
182
+	return p.PluginObj.Manifest.Interface.Socket
183
+}
184
+
185
+// GetTypes returns the interface types of a plugin.
186
+func (p *Plugin) GetTypes() []types.PluginInterfaceType {
187
+	p.RLock()
188
+	defer p.RUnlock()
189
+
190
+	return p.PluginObj.Manifest.Interface.Types
191
+}
192
+
193
+// InitSpec creates an OCI spec from the plugin's config.
194
+func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
195
+	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
196
+	s.Root = specs.Root{
197
+		Path:     rootfs,
198
+		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
199
+	}
200
+
201
+	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
202
+		Source:      &p.RuntimeSourcePath,
203
+		Destination: defaultPluginRuntimeDestination,
204
+		Type:        "bind",
205
+		Options:     []string{"rbind", "rshared"},
206
+	})
207
+	for _, mount := range mounts {
208
+		m := specs.Mount{
209
+			Destination: mount.Destination,
210
+			Type:        mount.Type,
211
+			Options:     mount.Options,
212
+		}
213
+		// TODO: if nil, then it's required and user didn't set it
214
+		if mount.Source != nil {
215
+			m.Source = *mount.Source
216
+		}
217
+		if m.Source != "" && m.Type == "bind" {
218
+			fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks
219
+			if err != nil {
220
+				return nil, err
221
+			}
222
+			if fi.IsDir() {
223
+				if err := os.MkdirAll(m.Source, 0700); err != nil {
224
+					return nil, err
225
+				}
226
+			}
227
+		}
228
+		s.Mounts = append(s.Mounts, m)
229
+	}
230
+
231
+	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
232
+	envs[0] = "PATH=" + system.DefaultPathEnv
233
+	envs = append(envs, p.PluginObj.Config.Env...)
234
+
235
+	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
236
+	cwd := p.PluginObj.Manifest.Workdir
237
+	if len(cwd) == 0 {
238
+		cwd = "/"
239
+	}
240
+	s.Process = specs.Process{
241
+		Terminal: false,
242
+		Args:     args,
243
+		Cwd:      cwd,
244
+		Env:      envs,
245
+	}
246
+
247
+	return &s, nil
248
+}
... ...
@@ -7,14 +7,17 @@ import (
7 7
 	"sync"
8 8
 
9 9
 	"github.com/docker/docker/pkg/locker"
10
-	pluginStore "github.com/docker/docker/plugin/store"
10
+	"github.com/docker/docker/plugin/getter"
11 11
 	"github.com/docker/docker/volume"
12 12
 )
13 13
 
14 14
 // currently created by hand. generation tool would generate this like:
15 15
 // $ extpoint-gen Driver > volume/extpoint.go
16 16
 
17
-var drivers = &driverExtpoint{extensions: make(map[string]volume.Driver), driverLock: &locker.Locker{}}
17
+var drivers = &driverExtpoint{
18
+	extensions: make(map[string]volume.Driver),
19
+	driverLock: &locker.Locker{},
20
+}
18 21
 
19 22
 const extName = "VolumeDriver"
20 23
 
... ...
@@ -49,7 +52,13 @@ type volumeDriver interface {
49 49
 type driverExtpoint struct {
50 50
 	extensions map[string]volume.Driver
51 51
 	sync.Mutex
52
-	driverLock *locker.Locker
52
+	driverLock   *locker.Locker
53
+	plugingetter getter.PluginGetter
54
+}
55
+
56
+// RegisterPluginGetter sets the plugingetter
57
+func RegisterPluginGetter(plugingetter getter.PluginGetter) {
58
+	drivers.plugingetter = plugingetter
53 59
 }
54 60
 
55 61
 // Register associates the given driver to the given name, checking if
... ...
@@ -72,6 +81,7 @@ func Register(extension volume.Driver, name string) bool {
72 72
 	}
73 73
 
74 74
 	drivers.extensions[name] = extension
75
+
75 76
 	return true
76 77
 }
77 78
 
... ...
@@ -102,7 +112,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
102 102
 		return ext, nil
103 103
 	}
104 104
 
105
-	p, err := pluginStore.LookupWithCapability(name, extName, mode)
105
+	p, err := drivers.plugingetter.Get(name, extName, mode)
106 106
 	if err != nil {
107 107
 		return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
108 108
 	}
... ...
@@ -112,7 +122,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
112 112
 		return nil, err
113 113
 	}
114 114
 
115
-	if p.IsLegacy() {
115
+	if p.IsV1() {
116 116
 		drivers.Lock()
117 117
 		drivers.extensions[name] = d
118 118
 		drivers.Unlock()
... ...
@@ -134,7 +144,7 @@ func GetDriver(name string) (volume.Driver, error) {
134 134
 	if name == "" {
135 135
 		name = volume.DefaultDriverName
136 136
 	}
137
-	return lookup(name, pluginStore.LOOKUP)
137
+	return lookup(name, getter.LOOKUP)
138 138
 }
139 139
 
140 140
 // CreateDriver returns a volume driver by its name and increments RefCount.
... ...
@@ -143,7 +153,7 @@ func CreateDriver(name string) (volume.Driver, error) {
143 143
 	if name == "" {
144 144
 		name = volume.DefaultDriverName
145 145
 	}
146
-	return lookup(name, pluginStore.CREATE)
146
+	return lookup(name, getter.CREATE)
147 147
 }
148 148
 
149 149
 // RemoveDriver returns a volume driver by its name and decrements RefCount..
... ...
@@ -152,7 +162,7 @@ func RemoveDriver(name string) (volume.Driver, error) {
152 152
 	if name == "" {
153 153
 		name = volume.DefaultDriverName
154 154
 	}
155
-	return lookup(name, pluginStore.REMOVE)
155
+	return lookup(name, getter.REMOVE)
156 156
 }
157 157
 
158 158
 // GetDriverList returns list of volume drivers registered.
... ...
@@ -169,7 +179,7 @@ func GetDriverList() []string {
169 169
 
170 170
 // GetAllDrivers lists all the registered drivers
171 171
 func GetAllDrivers() ([]volume.Driver, error) {
172
-	plugins, err := pluginStore.FindWithCapability(extName)
172
+	plugins, err := drivers.plugingetter.GetAllByCap(extName)
173 173
 	if err != nil {
174 174
 		return nil, fmt.Errorf("error listing plugins: %v", err)
175 175
 	}
... ...
@@ -190,7 +200,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
190 190
 		}
191 191
 
192 192
 		ext = NewVolumeDriver(name, p.Client())
193
-		if p.IsLegacy() {
193
+		if p.IsV1() {
194 194
 			drivers.extensions[name] = ext
195 195
 		}
196 196
 		ds = append(ds, ext)
... ...
@@ -3,16 +3,20 @@ package volumedrivers
3 3
 import (
4 4
 	"testing"
5 5
 
6
+	pluginstore "github.com/docker/docker/plugin/store"
6 7
 	volumetestutils "github.com/docker/docker/volume/testutils"
7 8
 )
8 9
 
9 10
 func TestGetDriver(t *testing.T) {
11
+	pluginStore := pluginstore.NewStore("/var/lib/docker")
12
+	RegisterPluginGetter(pluginStore)
13
+
10 14
 	_, err := GetDriver("missing")
11 15
 	if err == nil {
12 16
 		t.Fatal("Expected error, was nil")
13 17
 	}
14
-
15 18
 	Register(volumetestutils.NewFakeDriver("fake"), "fake")
19
+
16 20
 	d, err := GetDriver("fake")
17 21
 	if err != nil {
18 22
 		t.Fatal(err)
... ...
@@ -5,11 +5,15 @@ import (
5 5
 	"strings"
6 6
 	"testing"
7 7
 
8
+	pluginstore "github.com/docker/docker/plugin/store"
8 9
 	"github.com/docker/docker/volume/drivers"
9 10
 	volumetestutils "github.com/docker/docker/volume/testutils"
10 11
 )
11 12
 
12 13
 func TestCreate(t *testing.T) {
14
+	pluginStore := pluginstore.NewStore("/var/lib/docker")
15
+	volumedrivers.RegisterPluginGetter(pluginStore)
16
+
13 17
 	volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
14 18
 	defer volumedrivers.Unregister("fake")
15 19
 	s, err := New("")