Browse code

Reorganize plugin package into sub packages.

Split plugin package into `store` and `v2/plugin`. Now the functionality
is clearly delineated:
- Manager: Manages the global state of the plugin sub-system.
- PluginStore: Manages a collection of plugins (in memory and on-disk)
- Plugin: Manages the single plugin unit.

This also facilitates splitting the global PluginManager lock into:
- PluginManager lock to protect global states.
- PluginStore lock to protect store states.
- Plugin lock to protect individual plugin states.

Importing "github.com/docker/docker/plugin/store" will provide access
to plugins and has lesser dependencies when compared to importing the
original monolithic `plugin package`.

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

Anusha Ragunathan authored on 2016/08/27 02:02:38
Showing 11 changed files
... ...
@@ -15,39 +15,40 @@ import (
15 15
 	"github.com/docker/docker/pkg/archive"
16 16
 	"github.com/docker/docker/pkg/stringid"
17 17
 	"github.com/docker/docker/plugin/distribution"
18
+	"github.com/docker/docker/plugin/v2"
18 19
 	"github.com/docker/docker/reference"
19 20
 	"github.com/docker/engine-api/types"
20 21
 )
21 22
 
22 23
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
23 24
 func (pm *Manager) Disable(name string) error {
24
-	p, err := pm.get(name)
25
+	p, err := pm.pluginStore.GetByName(name)
25 26
 	if err != nil {
26 27
 		return err
27 28
 	}
28 29
 	if err := pm.disable(p); err != nil {
29 30
 		return err
30 31
 	}
31
-	pm.pluginEventLogger(p.PluginObj.ID, name, "disable")
32
+	pm.pluginEventLogger(p.GetID(), name, "disable")
32 33
 	return nil
33 34
 }
34 35
 
35 36
 // Enable activates a plugin, which implies that they are ready to be used by containers.
36 37
 func (pm *Manager) Enable(name string) error {
37
-	p, err := pm.get(name)
38
+	p, err := pm.pluginStore.GetByName(name)
38 39
 	if err != nil {
39 40
 		return err
40 41
 	}
41 42
 	if err := pm.enable(p, false); err != nil {
42 43
 		return err
43 44
 	}
44
-	pm.pluginEventLogger(p.PluginObj.ID, name, "enable")
45
+	pm.pluginEventLogger(p.GetID(), name, "enable")
45 46
 	return nil
46 47
 }
47 48
 
48 49
 // Inspect examines a plugin manifest
49 50
 func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
50
-	p, err := pm.get(name)
51
+	p, err := pm.pluginStore.GetByName(name)
51 52
 	if err != nil {
52 53
 		return tp, err
53 54
 	}
... ...
@@ -63,7 +64,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
63 63
 	}
64 64
 	name = ref.String()
65 65
 
66
-	if p, _ := pm.get(name); p != nil {
66
+	if p, _ := pm.pluginStore.GetByName(name); p != nil {
67 67
 		logrus.Debugf("plugin already exists")
68 68
 		return nil, fmt.Errorf("%s exists", name)
69 69
 	}
... ...
@@ -86,25 +87,25 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
86 86
 		return nil, err
87 87
 	}
88 88
 
89
-	p := pm.newPlugin(ref, pluginID)
90
-	if err := pm.initPlugin(p); err != nil {
89
+	var tag string
90
+	if ref, ok := ref.(reference.NamedTagged); ok {
91
+		tag = ref.Tag()
92
+	}
93
+	p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag)
94
+	if err := p.InitPlugin(pm.libRoot); err != nil {
91 95
 		return nil, err
92 96
 	}
93
-
94
-	pm.Lock()
95
-	pm.plugins[pluginID] = p
96
-	pm.nameToID[name] = pluginID
97
-	pm.save()
98
-	pm.Unlock()
97
+	pm.pluginStore.Add(p)
99 98
 
100 99
 	pm.pluginEventLogger(pluginID, name, "pull")
101
-	return computePrivileges(&p.PluginObj.Manifest), nil
100
+	return p.ComputePrivileges(), nil
102 101
 }
103 102
 
104 103
 // List displays the list of plugins and associated metadata.
105 104
 func (pm *Manager) List() ([]types.Plugin, error) {
106
-	out := make([]types.Plugin, 0, len(pm.plugins))
107
-	for _, p := range pm.plugins {
105
+	plugins := pm.pluginStore.GetAll()
106
+	out := make([]types.Plugin, 0, len(plugins))
107
+	for _, p := range plugins {
108 108
 		out = append(out, p.PluginObj)
109 109
 	}
110 110
 	return out, nil
... ...
@@ -112,11 +113,11 @@ func (pm *Manager) List() ([]types.Plugin, error) {
112 112
 
113 113
 // Push pushes a plugin to the store.
114 114
 func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.AuthConfig) error {
115
-	p, err := pm.get(name)
115
+	p, err := pm.pluginStore.GetByName(name)
116 116
 	if err != nil {
117 117
 		return err
118 118
 	}
119
-	dest := filepath.Join(pm.libRoot, p.PluginObj.ID)
119
+	dest := filepath.Join(pm.libRoot, p.GetID())
120 120
 	config, err := ioutil.ReadFile(filepath.Join(dest, "manifest.json"))
121 121
 	if err != nil {
122 122
 		return err
... ...
@@ -142,22 +143,28 @@ func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.A
142 142
 
143 143
 // Remove deletes plugin's root directory.
144 144
 func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
145
-	p, err := pm.get(name)
145
+	p, err := pm.pluginStore.GetByName(name)
146 146
 	if err != nil {
147 147
 		return err
148 148
 	}
149
-	if err := pm.remove(p, config.ForceRemove); err != nil {
150
-		return err
149
+	if p.IsEnabled() {
150
+		if !config.ForceRemove {
151
+			return fmt.Errorf("plugin %s is enabled", p.Name())
152
+		}
153
+		if err := pm.disable(p); err != nil {
154
+			logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
155
+		}
151 156
 	}
152
-	pm.pluginEventLogger(p.PluginObj.ID, name, "remove")
157
+	pm.pluginStore.Remove(p)
158
+	pm.pluginEventLogger(p.GetID(), name, "remove")
153 159
 	return nil
154 160
 }
155 161
 
156 162
 // Set sets plugin args
157 163
 func (pm *Manager) Set(name string, args []string) error {
158
-	p, err := pm.get(name)
164
+	p, err := pm.pluginStore.GetByName(name)
159 165
 	if err != nil {
160 166
 		return err
161 167
 	}
162
-	return pm.set(p, args)
168
+	return p.Set(args)
163 169
 }
164 170
deleted file mode 100644
... ...
@@ -1,10 +0,0 @@
1
-package plugin
2
-
3
-import "github.com/docker/docker/pkg/plugins"
4
-
5
-// Plugin represents a plugin. It is used to abstract from an older plugin architecture (in pkg/plugins).
6
-type Plugin interface {
7
-	Client() *plugins.Client
8
-	Name() string
9
-	IsLegacy() bool
10
-}
11 1
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-// +build !experimental
2
-
3
-package plugin
4
-
5
-import "github.com/docker/docker/pkg/plugins"
6
-
7
-// FindWithCapability returns a list of plugins matching the given capability.
8
-func FindWithCapability(capability string) ([]Plugin, error) {
9
-	pl, err := plugins.GetAll(capability)
10
-	if err != nil {
11
-		return nil, err
12
-	}
13
-	result := make([]Plugin, len(pl))
14
-	for i, p := range pl {
15
-		result[i] = p
16
-	}
17
-	return result, nil
18
-}
19
-
20
-// LookupWithCapability returns a plugin matching the given name and capability.
21
-func LookupWithCapability(name, capability string) (Plugin, error) {
22
-	return plugins.Get(name, capability)
23
-}
... ...
@@ -4,7 +4,6 @@ package plugin
4 4
 
5 5
 import (
6 6
 	"encoding/json"
7
-	"errors"
8 7
 	"fmt"
9 8
 	"io"
10 9
 	"os"
... ...
@@ -14,16 +13,12 @@ import (
14 14
 
15 15
 	"github.com/Sirupsen/logrus"
16 16
 	"github.com/docker/docker/libcontainerd"
17
-	"github.com/docker/docker/pkg/ioutils"
18 17
 	"github.com/docker/docker/pkg/plugins"
19
-	"github.com/docker/docker/reference"
18
+	"github.com/docker/docker/plugin/store"
19
+	"github.com/docker/docker/plugin/v2"
20 20
 	"github.com/docker/docker/registry"
21
-	"github.com/docker/docker/restartmanager"
22
-	"github.com/docker/engine-api/types"
23 21
 )
24 22
 
25
-const defaultPluginRuntimeDestination = "/run/docker/plugins"
26
-
27 23
 var (
28 24
 	manager *Manager
29 25
 
... ...
@@ -34,71 +29,14 @@ var (
34 34
 	allowV1PluginsFallback = true
35 35
 )
36 36
 
37
-// ErrNotFound indicates that a plugin was not found locally.
38
-type ErrNotFound string
39
-
40
-func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
41
-
42
-// ErrInadequateCapability indicates that a plugin was found but did not have the requested capability.
43
-type ErrInadequateCapability struct {
44
-	name       string
45
-	capability string
46
-}
47
-
48
-func (e ErrInadequateCapability) Error() string {
49
-	return fmt.Sprintf("plugin %q found, but not with %q capability", e.name, e.capability)
50
-}
51
-
52
-type plugin struct {
53
-	//sync.RWMutex TODO
54
-	PluginObj         types.Plugin `json:"plugin"`
55
-	client            *plugins.Client
56
-	restartManager    restartmanager.RestartManager
57
-	runtimeSourcePath string
58
-	exitChan          chan bool
59
-}
60
-
61
-func (p *plugin) Client() *plugins.Client {
62
-	return p.client
63
-}
64
-
65
-// IsLegacy returns true for legacy plugins and false otherwise.
66
-func (p *plugin) IsLegacy() bool {
67
-	return false
68
-}
69
-
70
-func (p *plugin) Name() string {
71
-	name := p.PluginObj.Name
72
-	if len(p.PluginObj.Tag) > 0 {
73
-		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
74
-		name += ":" + p.PluginObj.Tag
75
-	}
76
-	return name
77
-}
78
-
79
-func (pm *Manager) newPlugin(ref reference.Named, id string) *plugin {
80
-	p := &plugin{
81
-		PluginObj: types.Plugin{
82
-			Name: ref.Name(),
83
-			ID:   id,
84
-		},
85
-		runtimeSourcePath: filepath.Join(pm.runRoot, id),
86
-	}
87
-	if ref, ok := ref.(reference.NamedTagged); ok {
88
-		p.PluginObj.Tag = ref.Tag()
89
-	}
90
-	return p
91
-}
92
-
93
-func (pm *Manager) restorePlugin(p *plugin) error {
94
-	p.runtimeSourcePath = filepath.Join(pm.runRoot, p.PluginObj.ID)
95
-	if p.PluginObj.Enabled {
37
+func (pm *Manager) restorePlugin(p *v2.Plugin) error {
38
+	p.RuntimeSourcePath = filepath.Join(pm.runRoot, p.GetID())
39
+	if p.IsEnabled() {
96 40
 		return pm.restore(p)
97 41
 	}
98 42
 	return nil
99 43
 }
100 44
 
101
-type pluginMap map[string]*plugin
102 45
 type eventLogger func(id, name, action string)
103 46
 
104 47
 // Manager controls the plugin subsystem.
... ...
@@ -106,8 +44,7 @@ type Manager struct {
106 106
 	sync.RWMutex
107 107
 	libRoot           string
108 108
 	runRoot           string
109
-	plugins           pluginMap // TODO: figure out why save() doesn't json encode *plugin object
110
-	nameToID          map[string]string
109
+	pluginStore       *store.PluginStore
111 110
 	handlers          map[string]func(string, *plugins.Client)
112 111
 	containerdClient  libcontainerd.Client
113 112
 	registryService   registry.Service
... ...
@@ -132,8 +69,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes
132 132
 	manager = &Manager{
133 133
 		libRoot:           root,
134 134
 		runRoot:           "/run/docker",
135
-		plugins:           make(map[string]*plugin),
136
-		nameToID:          make(map[string]string),
135
+		pluginStore:       store.NewPluginStore(root),
137 136
 		handlers:          make(map[string]func(string, *plugins.Client)),
138 137
 		registryService:   rs,
139 138
 		liveRestore:       liveRestore,
... ...
@@ -162,105 +98,6 @@ func Handle(capability string, callback func(string, *plugins.Client)) {
162 162
 	}
163 163
 }
164 164
 
165
-func (pm *Manager) get(name string) (*plugin, error) {
166
-	pm.RLock()
167
-	defer pm.RUnlock()
168
-
169
-	id, nameOk := pm.nameToID[name]
170
-	if !nameOk {
171
-		return nil, ErrNotFound(name)
172
-	}
173
-
174
-	p, idOk := pm.plugins[id]
175
-	if !idOk {
176
-		return nil, ErrNotFound(name)
177
-	}
178
-
179
-	return p, nil
180
-}
181
-
182
-// FindWithCapability returns a list of plugins matching the given capability.
183
-func FindWithCapability(capability string) ([]Plugin, error) {
184
-	result := make([]Plugin, 0, 1)
185
-
186
-	/* Daemon start always calls plugin.Init thereby initializing a manager.
187
-	 * So manager on experimental builds can never be nil, even while
188
-	 * handling legacy plugins. However, there are legacy plugin unit
189
-	 * tests where volume subsystem directly talks with the plugin,
190
-	 * bypassing the daemon. For such tests, this check is necessary.*/
191
-	if manager != nil {
192
-		manager.RLock()
193
-		for _, p := range manager.plugins {
194
-			for _, typ := range p.PluginObj.Manifest.Interface.Types {
195
-				if strings.EqualFold(typ.Capability, capability) && typ.Prefix == "docker" {
196
-					result = append(result, p)
197
-					break
198
-				}
199
-			}
200
-		}
201
-		manager.RUnlock()
202
-	}
203
-
204
-	// Lookup with legacy model.
205
-	if allowV1PluginsFallback {
206
-		pl, err := plugins.GetAll(capability)
207
-		if err != nil {
208
-			return nil, fmt.Errorf("legacy plugin: %v", err)
209
-		}
210
-		for _, p := range pl {
211
-			result = append(result, p)
212
-		}
213
-	}
214
-	return result, nil
215
-}
216
-
217
-// LookupWithCapability returns a plugin matching the given name and capability.
218
-func LookupWithCapability(name, capability string) (Plugin, error) {
219
-	var (
220
-		p   *plugin
221
-		err error
222
-	)
223
-
224
-	// Lookup using new model.
225
-	if manager != nil {
226
-		fullName := name
227
-		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
228
-			if reference.IsNameOnly(named) {
229
-				named = reference.WithDefaultTag(named)
230
-			}
231
-			ref, ok := named.(reference.NamedTagged)
232
-			if !ok {
233
-				return nil, fmt.Errorf("invalid name: %s", named.String())
234
-			}
235
-			fullName = ref.String()
236
-		}
237
-		p, err = manager.get(fullName)
238
-		if err == nil {
239
-			capability = strings.ToLower(capability)
240
-			for _, typ := range p.PluginObj.Manifest.Interface.Types {
241
-				if typ.Capability == capability && typ.Prefix == "docker" {
242
-					return p, nil
243
-				}
244
-			}
245
-			return nil, ErrInadequateCapability{name, capability}
246
-		}
247
-		if _, ok := err.(ErrNotFound); !ok {
248
-			return nil, err
249
-		}
250
-	}
251
-
252
-	// Lookup using legacy model
253
-	if allowV1PluginsFallback {
254
-		p, err := plugins.Get(name, capability)
255
-		if err != nil {
256
-			return nil, fmt.Errorf("legacy plugin: %v", err)
257
-		}
258
-		return p, nil
259
-	}
260
-
261
-	return nil, err
262
-}
263
-
264 165
 // StateChanged updates plugin internals using libcontainerd events.
265 166
 func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
266 167
 	logrus.Debugf("plugin state changed %s %#v", id, e)
... ...
@@ -272,13 +109,11 @@ func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
272 272
 		shutdown = pm.shutdown
273 273
 		pm.RUnlock()
274 274
 		if shutdown {
275
-			pm.RLock()
276
-			p, idOk := pm.plugins[id]
277
-			pm.RUnlock()
278
-			if !idOk {
279
-				return ErrNotFound(id)
275
+			p, err := pm.pluginStore.GetByID(id)
276
+			if err != nil {
277
+				return err
280 278
 			}
281
-			close(p.exitChan)
279
+			close(p.ExitChan)
282 280
 		}
283 281
 	}
284 282
 
... ...
@@ -313,24 +148,24 @@ func (pm *Manager) init() error {
313 313
 	}
314 314
 	defer dt.Close()
315 315
 
316
-	if err := json.NewDecoder(dt).Decode(&pm.plugins); err != nil {
316
+	plugins := make(map[string]*v2.Plugin)
317
+	if err := json.NewDecoder(dt).Decode(&plugins); err != nil {
317 318
 		return err
318 319
 	}
320
+	pm.pluginStore.SetAll(plugins)
319 321
 
320 322
 	var group sync.WaitGroup
321
-	group.Add(len(pm.plugins))
322
-	for _, p := range pm.plugins {
323
-		go func(p *plugin) {
323
+	group.Add(len(plugins))
324
+	for _, p := range plugins {
325
+		go func(p *v2.Plugin) {
324 326
 			defer group.Done()
325 327
 			if err := pm.restorePlugin(p); err != nil {
326 328
 				logrus.Errorf("failed to restore plugin '%s': %s", p.Name(), err)
327 329
 				return
328 330
 			}
329 331
 
330
-			pm.Lock()
331
-			pm.nameToID[p.Name()] = p.PluginObj.ID
332
-			requiresManualRestore := !pm.liveRestore && p.PluginObj.Enabled
333
-			pm.Unlock()
332
+			pm.pluginStore.Add(p)
333
+			requiresManualRestore := !pm.liveRestore && p.IsEnabled()
334 334
 
335 335
 			if requiresManualRestore {
336 336
 				// if liveRestore is not enabled, the plugin will be stopped now so we should enable it
... ...
@@ -341,80 +176,6 @@ func (pm *Manager) init() error {
341 341
 		}(p)
342 342
 	}
343 343
 	group.Wait()
344
-	return pm.save()
345
-}
346
-
347
-func (pm *Manager) initPlugin(p *plugin) error {
348
-	dt, err := os.Open(filepath.Join(pm.libRoot, p.PluginObj.ID, "manifest.json"))
349
-	if err != nil {
350
-		return err
351
-	}
352
-	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
353
-	dt.Close()
354
-	if err != nil {
355
-		return err
356
-	}
357
-
358
-	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
359
-	for i, mount := range p.PluginObj.Manifest.Mounts {
360
-		p.PluginObj.Config.Mounts[i] = mount
361
-	}
362
-	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
363
-	for _, env := range p.PluginObj.Manifest.Env {
364
-		if env.Value != nil {
365
-			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
366
-		}
367
-	}
368
-	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
369
-
370
-	f, err := os.Create(filepath.Join(pm.libRoot, p.PluginObj.ID, "plugin-config.json"))
371
-	if err != nil {
372
-		return err
373
-	}
374
-	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
375
-	f.Close()
376
-	return err
377
-}
378
-
379
-func (pm *Manager) remove(p *plugin, force bool) error {
380
-	if p.PluginObj.Enabled {
381
-		if !force {
382
-			return fmt.Errorf("plugin %s is enabled", p.Name())
383
-		}
384
-		if err := pm.disable(p); err != nil {
385
-			logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
386
-		}
387
-	}
388
-	pm.Lock() // fixme: lock single record
389
-	defer pm.Unlock()
390
-	delete(pm.plugins, p.PluginObj.ID)
391
-	delete(pm.nameToID, p.Name())
392
-	pm.save()
393
-	return os.RemoveAll(filepath.Join(pm.libRoot, p.PluginObj.ID))
394
-}
395
-
396
-func (pm *Manager) set(p *plugin, args []string) error {
397
-	m := make(map[string]string, len(args))
398
-	for _, arg := range args {
399
-		i := strings.Index(arg, "=")
400
-		if i < 0 {
401
-			return fmt.Errorf("no equal sign '=' found in %s", arg)
402
-		}
403
-		m[arg[:i]] = arg[i+1:]
404
-	}
405
-	return errors.New("not implemented")
406
-}
407
-
408
-// fixme: not safe
409
-func (pm *Manager) save() error {
410
-	filePath := filepath.Join(pm.libRoot, "plugins.json")
411
-
412
-	jsonData, err := json.Marshal(pm.plugins)
413
-	if err != nil {
414
-		logrus.Debugf("failure in json.Marshal: %v", err)
415
-		return err
416
-	}
417
-	ioutils.AtomicWriteFile(filePath, jsonData, 0600)
418 344
 	return nil
419 345
 }
420 346
 
... ...
@@ -428,40 +189,3 @@ func (l logHook) Fire(entry *logrus.Entry) error {
428 428
 	entry.Data = logrus.Fields{"plugin": l.id}
429 429
 	return nil
430 430
 }
431
-
432
-func computePrivileges(m *types.PluginManifest) types.PluginPrivileges {
433
-	var privileges types.PluginPrivileges
434
-	if m.Network.Type != "null" && m.Network.Type != "bridge" {
435
-		privileges = append(privileges, types.PluginPrivilege{
436
-			Name:        "network",
437
-			Description: "",
438
-			Value:       []string{m.Network.Type},
439
-		})
440
-	}
441
-	for _, mount := range m.Mounts {
442
-		if mount.Source != nil {
443
-			privileges = append(privileges, types.PluginPrivilege{
444
-				Name:        "mount",
445
-				Description: "",
446
-				Value:       []string{*mount.Source},
447
-			})
448
-		}
449
-	}
450
-	for _, device := range m.Devices {
451
-		if device.Path != nil {
452
-			privileges = append(privileges, types.PluginPrivilege{
453
-				Name:        "device",
454
-				Description: "",
455
-				Value:       []string{*device.Path},
456
-			})
457
-		}
458
-	}
459
-	if len(m.Capabilities) > 0 {
460
-		privileges = append(privileges, types.PluginPrivilege{
461
-			Name:        "capabilities",
462
-			Description: "",
463
-			Value:       m.Capabilities,
464
-		})
465
-	}
466
-	return privileges
467
-}
... ...
@@ -4,7 +4,6 @@ package plugin
4 4
 
5 5
 import (
6 6
 	"fmt"
7
-	"os"
8 7
 	"path/filepath"
9 8
 	"syscall"
10 9
 	"time"
... ...
@@ -13,45 +12,38 @@ import (
13 13
 	"github.com/docker/docker/libcontainerd"
14 14
 	"github.com/docker/docker/oci"
15 15
 	"github.com/docker/docker/pkg/plugins"
16
-	"github.com/docker/docker/pkg/system"
16
+	"github.com/docker/docker/plugin/v2"
17 17
 	"github.com/docker/docker/restartmanager"
18
-	"github.com/docker/engine-api/types"
19 18
 	"github.com/docker/engine-api/types/container"
20
-	"github.com/opencontainers/runtime-spec/specs-go"
21 19
 )
22 20
 
23
-func (pm *Manager) enable(p *plugin, force bool) error {
24
-	if p.PluginObj.Enabled && !force {
21
+func (pm *Manager) enable(p *v2.Plugin, force bool) error {
22
+	if p.IsEnabled() && !force {
25 23
 		return fmt.Errorf("plugin %s is already enabled", p.Name())
26 24
 	}
27
-	spec, err := pm.initSpec(p)
25
+	spec, err := p.InitSpec(oci.DefaultSpec(), pm.libRoot)
28 26
 	if err != nil {
29 27
 		return err
30 28
 	}
31 29
 
32
-	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
33
-	if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
34
-		if err := p.restartManager.Cancel(); err != nil {
30
+	p.RestartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
31
+	if err := pm.containerdClient.Create(p.GetID(), libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.RestartManager)); err != nil {
32
+		if err := p.RestartManager.Cancel(); err != nil {
35 33
 			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
36 34
 		}
37 35
 		return err
38 36
 	}
39 37
 
40
-	socket := p.PluginObj.Manifest.Interface.Socket
41
-	p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
38
+	p.PClient, err = plugins.NewClient("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil)
42 39
 	if err != nil {
43
-		if err := p.restartManager.Cancel(); err != nil {
40
+		if err := p.RestartManager.Cancel(); err != nil {
44 41
 			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
45 42
 		}
46 43
 		return err
47 44
 	}
48 45
 
49
-	pm.Lock() // fixme: lock single record
50
-	p.PluginObj.Enabled = true
51
-	pm.save()
52
-	pm.Unlock()
53
-
54
-	for _, typ := range p.PluginObj.Manifest.Interface.Types {
46
+	pm.pluginStore.SetState(p, true)
47
+	for _, typ := range p.GetTypes() {
55 48
 		if handler := pm.handlers[typ.String()]; handler != nil {
56 49
 			handler(p.Name(), p.Client())
57 50
 		}
... ...
@@ -60,90 +52,25 @@ func (pm *Manager) enable(p *plugin, force bool) error {
60 60
 	return nil
61 61
 }
62 62
 
63
-func (pm *Manager) restore(p *plugin) error {
64
-	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
65
-	return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager))
63
+func (pm *Manager) restore(p *v2.Plugin) error {
64
+	p.RestartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
65
+	return pm.containerdClient.Restore(p.GetID(), libcontainerd.WithRestartManager(p.RestartManager))
66 66
 }
67 67
 
68
-func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
69
-	s := oci.DefaultSpec()
70
-
71
-	rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
72
-	s.Root = specs.Root{
73
-		Path:     rootfs,
74
-		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
75
-	}
76
-
77
-	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
78
-		Source:      &p.runtimeSourcePath,
79
-		Destination: defaultPluginRuntimeDestination,
80
-		Type:        "bind",
81
-		Options:     []string{"rbind", "rshared"},
82
-	})
83
-	for _, mount := range mounts {
84
-		m := specs.Mount{
85
-			Destination: mount.Destination,
86
-			Type:        mount.Type,
87
-			Options:     mount.Options,
88
-		}
89
-		// TODO: if nil, then it's required and user didn't set it
90
-		if mount.Source != nil {
91
-			m.Source = *mount.Source
92
-		}
93
-
94
-		if m.Source != "" && m.Type == "bind" {
95
-			/* Debugging issue #25511: Volumes and other content created under the
96
-			bind mount should be recursively propagated. rshared, not shared.
97
-			This could be the reason for EBUSY during removal. Override options
98
-			with rbind, rshared and see if CI errors are fixed. */
99
-			m.Options = []string{"rbind", "rshared"}
100
-			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
101
-			if err != nil {
102
-				return nil, err
103
-			}
104
-			if fi.IsDir() {
105
-				if err := os.MkdirAll(m.Source, 0700); err != nil {
106
-					return nil, err
107
-				}
108
-			}
109
-		}
110
-		s.Mounts = append(s.Mounts, m)
111
-	}
112
-
113
-	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
114
-	envs[0] = "PATH=" + system.DefaultPathEnv
115
-	envs = append(envs, p.PluginObj.Config.Env...)
116
-
117
-	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
118
-	cwd := p.PluginObj.Manifest.Workdir
119
-	if len(cwd) == 0 {
120
-		cwd = "/"
121
-	}
122
-	s.Process = specs.Process{
123
-		Terminal: false,
124
-		Args:     args,
125
-		Cwd:      cwd,
126
-		Env:      envs,
127
-	}
128
-
129
-	return &s, nil
130
-}
131
-
132
-func (pm *Manager) disable(p *plugin) error {
133
-	if !p.PluginObj.Enabled {
68
+func (pm *Manager) disable(p *v2.Plugin) error {
69
+	if !p.IsEnabled() {
134 70
 		return fmt.Errorf("plugin %s is already disabled", p.Name())
135 71
 	}
136
-	if err := p.restartManager.Cancel(); err != nil {
72
+	if err := p.RestartManager.Cancel(); err != nil {
73
+		logrus.Error(err)
74
+	}
75
+	if err := pm.containerdClient.Signal(p.GetID(), int(syscall.SIGKILL)); err != nil {
137 76
 		logrus.Error(err)
138 77
 	}
139
-	if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
78
+	if err := p.RemoveFromDisk(); err != nil {
140 79
 		logrus.Error(err)
141 80
 	}
142
-	os.RemoveAll(p.runtimeSourcePath)
143
-	pm.Lock() // fixme: lock single record
144
-	defer pm.Unlock()
145
-	p.PluginObj.Enabled = false
146
-	pm.save()
81
+	pm.pluginStore.SetState(p, false)
147 82
 	return nil
148 83
 }
149 84
 
... ...
@@ -155,34 +82,36 @@ func (pm *Manager) Shutdown() {
155 155
 
156 156
 	pm.RLock()
157 157
 	defer pm.RUnlock()
158
-	for _, p := range pm.plugins {
159
-		if pm.liveRestore && p.PluginObj.Enabled {
160
-			logrus.Debug("Plugin enabled when liveRestore is set, skipping shutdown")
158
+	plugins := pm.pluginStore.GetAll()
159
+	for _, p := range plugins {
160
+		if pm.liveRestore && p.IsEnabled() {
161
+			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
161 162
 			continue
162 163
 		}
163
-		if p.restartManager != nil {
164
-			if err := p.restartManager.Cancel(); err != nil {
164
+		if p.RestartManager != nil {
165
+			if err := p.RestartManager.Cancel(); err != nil {
165 166
 				logrus.Error(err)
166 167
 			}
167 168
 		}
168
-		if pm.containerdClient != nil && p.PluginObj.Enabled {
169
-			p.exitChan = make(chan bool)
169
+		if pm.containerdClient != nil && p.IsEnabled() {
170
+			pluginID := p.GetID()
171
+			p.ExitChan = make(chan bool)
170 172
 			err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
171 173
 			if err != nil {
172 174
 				logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
173 175
 			} else {
174 176
 				select {
175
-				case <-p.exitChan:
177
+				case <-p.ExitChan:
176 178
 					logrus.Debug("Clean shutdown of plugin")
177 179
 				case <-time.After(time.Second * 10):
178 180
 					logrus.Debug("Force shutdown plugin")
179
-					if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
181
+					if err := pm.containerdClient.Signal(pluginID, int(syscall.SIGKILL)); err != nil {
180 182
 						logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
181 183
 					}
182 184
 				}
183 185
 			}
184 186
 		}
185
-		if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
187
+		if err := p.RemoveFromDisk(); err != nil {
186 188
 			logrus.Errorf("Remove plugin runtime failed with error: %v", err)
187 189
 		}
188 190
 	}
... ...
@@ -5,22 +5,23 @@ package plugin
5 5
 import (
6 6
 	"fmt"
7 7
 
8
+	"github.com/docker/docker/plugin/v2"
8 9
 	"github.com/opencontainers/runtime-spec/specs-go"
9 10
 )
10 11
 
11
-func (pm *Manager) enable(p *plugin, force bool) error {
12
+func (pm *Manager) enable(p *v2.Plugin, force bool) error {
12 13
 	return fmt.Errorf("Not implemented")
13 14
 }
14 15
 
15
-func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
16
+func (pm *Manager) initSpec(p *v2.Plugin) (*specs.Spec, error) {
16 17
 	return nil, fmt.Errorf("Not implemented")
17 18
 }
18 19
 
19
-func (pm *Manager) disable(p *plugin) error {
20
+func (pm *Manager) disable(p *v2.Plugin) error {
20 21
 	return fmt.Errorf("Not implemented")
21 22
 }
22 23
 
23
-func (pm *Manager) restore(p *plugin) error {
24
+func (pm *Manager) restore(p *v2.Plugin) error {
24 25
 	return fmt.Errorf("Not implemented")
25 26
 }
26 27
 
27 28
new file mode 100644
... ...
@@ -0,0 +1,10 @@
0
+package store
1
+
2
+import "github.com/docker/docker/pkg/plugins"
3
+
4
+// CompatPlugin is a abstraction to handle both new and legacy (v1) plugins.
5
+type CompatPlugin interface {
6
+	Client() *plugins.Client
7
+	Name() string
8
+	IsLegacy() bool
9
+}
0 10
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+// +build !experimental
1
+
2
+package store
3
+
4
+import (
5
+	"github.com/docker/docker/pkg/plugins"
6
+)
7
+
8
+// FindWithCapability returns a list of plugins matching the given capability.
9
+func FindWithCapability(capability string) ([]CompatPlugin, error) {
10
+	pl, err := plugins.GetAll(capability)
11
+	if err != nil {
12
+		return nil, err
13
+	}
14
+	result := make([]CompatPlugin, len(pl))
15
+	for i, p := range pl {
16
+		result[i] = p
17
+	}
18
+	return result, nil
19
+}
20
+
21
+// LookupWithCapability returns a plugin matching the given name and capability.
22
+func LookupWithCapability(name, capability string) (CompatPlugin, error) {
23
+	return plugins.Get(name, capability)
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,224 @@
0
+// +build experimental
1
+
2
+package store
3
+
4
+import (
5
+	"encoding/json"
6
+	"fmt"
7
+	"path/filepath"
8
+	"sync"
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/v2"
14
+	"github.com/docker/docker/reference"
15
+)
16
+
17
+var (
18
+	store *PluginStore
19
+	/* allowV1PluginsFallback determines daemon's support for V1 plugins.
20
+	 * When the time comes to remove support for V1 plugins, flipping
21
+	 * this bool is all that will be needed.
22
+	 */
23
+	allowV1PluginsFallback = true
24
+)
25
+
26
+// ErrNotFound indicates that a plugin was not found locally.
27
+type ErrNotFound string
28
+
29
+func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
30
+
31
+// PluginStore manages the plugin inventory in memory and on-disk
32
+type PluginStore struct {
33
+	sync.RWMutex
34
+	plugins  map[string]*v2.Plugin
35
+	nameToID map[string]string
36
+	plugindb string
37
+}
38
+
39
+// NewPluginStore creates a PluginStore.
40
+func NewPluginStore(libRoot string) *PluginStore {
41
+	store = &PluginStore{
42
+		plugins:  make(map[string]*v2.Plugin),
43
+		nameToID: make(map[string]string),
44
+		plugindb: filepath.Join(libRoot, "plugins.json"),
45
+	}
46
+	return store
47
+}
48
+
49
+// GetByName retreives a plugin by name.
50
+func (ps *PluginStore) GetByName(name string) (*v2.Plugin, error) {
51
+	ps.RLock()
52
+	defer ps.RUnlock()
53
+
54
+	id, nameOk := ps.nameToID[name]
55
+	if !nameOk {
56
+		return nil, ErrNotFound(name)
57
+	}
58
+
59
+	p, idOk := ps.plugins[id]
60
+	if !idOk {
61
+		return nil, ErrNotFound(id)
62
+	}
63
+	return p, nil
64
+}
65
+
66
+// GetByID retreives a plugin by ID.
67
+func (ps *PluginStore) GetByID(id string) (*v2.Plugin, error) {
68
+	ps.RLock()
69
+	defer ps.RUnlock()
70
+
71
+	p, idOk := ps.plugins[id]
72
+	if !idOk {
73
+		return nil, ErrNotFound(id)
74
+	}
75
+	return p, nil
76
+}
77
+
78
+// GetAll retreives all plugins.
79
+func (ps *PluginStore) GetAll() map[string]*v2.Plugin {
80
+	ps.RLock()
81
+	defer ps.RUnlock()
82
+	return ps.plugins
83
+}
84
+
85
+// SetAll initialized plugins during daemon restore.
86
+func (ps *PluginStore) SetAll(plugins map[string]*v2.Plugin) {
87
+	ps.Lock()
88
+	defer ps.Unlock()
89
+	ps.plugins = plugins
90
+}
91
+
92
+func (ps *PluginStore) getByCap(name string, capability string) (*v2.Plugin, error) {
93
+	ps.RLock()
94
+	defer ps.RUnlock()
95
+
96
+	p, err := ps.GetByName(name)
97
+	if err != nil {
98
+		return nil, err
99
+	}
100
+	return p.FilterByCap(capability)
101
+}
102
+
103
+func (ps *PluginStore) getAllByCap(capability string) []CompatPlugin {
104
+	ps.RLock()
105
+	defer ps.RUnlock()
106
+
107
+	result := make([]CompatPlugin, 0, 1)
108
+	for _, p := range ps.plugins {
109
+		if _, err := p.FilterByCap(capability); err == nil {
110
+			result = append(result, p)
111
+		}
112
+	}
113
+	return result
114
+}
115
+
116
+// SetState sets the active state of the plugin and updates plugindb.
117
+func (ps *PluginStore) SetState(p *v2.Plugin, state bool) {
118
+	ps.Lock()
119
+	defer ps.Unlock()
120
+
121
+	p.PluginObj.Enabled = state
122
+	ps.updatePluginDB()
123
+}
124
+
125
+// Add adds a plugin to memory and plugindb.
126
+func (ps *PluginStore) Add(p *v2.Plugin) {
127
+	ps.Lock()
128
+	ps.plugins[p.GetID()] = p
129
+	ps.nameToID[p.Name()] = p.GetID()
130
+	ps.updatePluginDB()
131
+	ps.Unlock()
132
+}
133
+
134
+// Remove removes a plugin from memory, plugindb and disk.
135
+func (ps *PluginStore) Remove(p *v2.Plugin) {
136
+	ps.Lock()
137
+	delete(ps.plugins, p.GetID())
138
+	delete(ps.nameToID, p.Name())
139
+	ps.updatePluginDB()
140
+	p.RemoveFromDisk()
141
+	ps.Unlock()
142
+}
143
+
144
+// Callers are expected to hold the store lock.
145
+func (ps *PluginStore) 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
+// LookupWithCapability returns a plugin matching the given name and capability.
156
+func LookupWithCapability(name, capability string) (CompatPlugin, error) {
157
+	var (
158
+		p   *v2.Plugin
159
+		err error
160
+	)
161
+
162
+	// Lookup using new model.
163
+	if store != 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 = store.GetByName(fullName)
176
+		if err == nil {
177
+			return p.FilterByCap(capability)
178
+		}
179
+		if _, ok := err.(ErrNotFound); !ok {
180
+			return nil, err
181
+		}
182
+	}
183
+
184
+	// Lookup using legacy model.
185
+	if allowV1PluginsFallback {
186
+		p, err := plugins.Get(name, capability)
187
+		if err != nil {
188
+			return nil, fmt.Errorf("legacy plugin: %v", err)
189
+		}
190
+		return p, nil
191
+	}
192
+
193
+	return nil, err
194
+}
195
+
196
+// FindWithCapability returns a list of plugins matching the given capability.
197
+func FindWithCapability(capability string) ([]CompatPlugin, error) {
198
+	result := make([]CompatPlugin, 0, 1)
199
+
200
+	/* Daemon start always calls plugin.Init thereby initializing a store.
201
+	 * So store on experimental builds can never be nil, even while
202
+	 * handling legacy plugins. However, there are legacy plugin unit
203
+	 * tests where the volume subsystem directly talks with the plugin,
204
+	 * bypassing the daemon. For such tests, this check is necessary.
205
+	 */
206
+	if store != nil {
207
+		store.RLock()
208
+		result = store.getAllByCap(capability)
209
+		store.RUnlock()
210
+	}
211
+
212
+	// Lookup with legacy model
213
+	if allowV1PluginsFallback {
214
+		pl, err := plugins.GetAll(capability)
215
+		if err != nil {
216
+			return nil, fmt.Errorf("legacy plugin: %v", err)
217
+		}
218
+		for _, p := range pl {
219
+			result = append(result, p)
220
+		}
221
+	}
222
+	return result, nil
223
+}
0 224
new file mode 100644
... ...
@@ -0,0 +1,261 @@
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
+	"sync"
12
+
13
+	"github.com/docker/docker/pkg/plugins"
14
+	"github.com/docker/docker/pkg/system"
15
+	"github.com/docker/docker/restartmanager"
16
+	"github.com/docker/engine-api/types"
17
+	"github.com/opencontainers/runtime-spec/specs-go"
18
+)
19
+
20
+const defaultPluginRuntimeDestination = "/run/docker/plugins"
21
+
22
+// ErrInadequateCapability indicates that the plugin did not have the requested capability.
23
+type ErrInadequateCapability string
24
+
25
+func (cap ErrInadequateCapability) Error() string {
26
+	return fmt.Sprintf("plugin does not provide %q capability", cap)
27
+}
28
+
29
+// Plugin represents an individual plugin.
30
+type Plugin struct {
31
+	sync.RWMutex
32
+	PluginObj         types.Plugin                  `json:"plugin"`
33
+	PClient           *plugins.Client               `json:"-"`
34
+	RestartManager    restartmanager.RestartManager `json:"-"`
35
+	RuntimeSourcePath string                        `json:"-"`
36
+	ExitChan          chan bool                     `json:"-"`
37
+}
38
+
39
+func newPluginObj(name, id, tag string) types.Plugin {
40
+	return types.Plugin{Name: name, ID: id, Tag: tag}
41
+}
42
+
43
+// NewPlugin creates a plugin.
44
+func NewPlugin(name, id, runRoot, tag string) *Plugin {
45
+	return &Plugin{
46
+		PluginObj:         newPluginObj(name, id, tag),
47
+		RuntimeSourcePath: filepath.Join(runRoot, id),
48
+	}
49
+}
50
+
51
+// Client returns the plugin client.
52
+func (p *Plugin) Client() *plugins.Client {
53
+	return p.PClient
54
+}
55
+
56
+// IsLegacy returns true for legacy plugins and false otherwise.
57
+func (p *Plugin) IsLegacy() bool {
58
+	return false
59
+}
60
+
61
+// Name returns the plugin name.
62
+func (p *Plugin) Name() string {
63
+	name := p.PluginObj.Name
64
+	if len(p.PluginObj.Tag) > 0 {
65
+		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
66
+		name += ":" + p.PluginObj.Tag
67
+	}
68
+	return name
69
+}
70
+
71
+// FilterByCap query the plugin for a given capability.
72
+func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
73
+	capability = strings.ToLower(capability)
74
+	for _, typ := range p.PluginObj.Manifest.Interface.Types {
75
+		if typ.Capability == capability && typ.Prefix == "docker" {
76
+			return p, nil
77
+		}
78
+	}
79
+	return nil, ErrInadequateCapability(capability)
80
+}
81
+
82
+// RemoveFromDisk deletes the plugin's runtime files from disk.
83
+func (p *Plugin) RemoveFromDisk() error {
84
+	return os.RemoveAll(p.RuntimeSourcePath)
85
+}
86
+
87
+// InitPlugin populates the plugin object from the plugin manifest file.
88
+func (p *Plugin) InitPlugin(libRoot string) error {
89
+	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
90
+	if err != nil {
91
+		return err
92
+	}
93
+	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
94
+	dt.Close()
95
+	if err != nil {
96
+		return err
97
+	}
98
+
99
+	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
100
+	for i, mount := range p.PluginObj.Manifest.Mounts {
101
+		p.PluginObj.Config.Mounts[i] = mount
102
+	}
103
+	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
104
+	for _, env := range p.PluginObj.Manifest.Env {
105
+		if env.Value != nil {
106
+			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
107
+		}
108
+	}
109
+	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
110
+
111
+	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
112
+	if err != nil {
113
+		return err
114
+	}
115
+	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
116
+	f.Close()
117
+	return err
118
+}
119
+
120
+// Set is used to pass arguments to the plugin.
121
+func (p *Plugin) Set(args []string) error {
122
+	m := make(map[string]string, len(args))
123
+	for _, arg := range args {
124
+		i := strings.Index(arg, "=")
125
+		if i < 0 {
126
+			return fmt.Errorf("No equal sign '=' found in %s", arg)
127
+		}
128
+		m[arg[:i]] = arg[i+1:]
129
+	}
130
+	return errors.New("not implemented")
131
+}
132
+
133
+// ComputePrivileges takes the manifest file and computes the list of access necessary
134
+// for the plugin on the host.
135
+func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
136
+	m := p.PluginObj.Manifest
137
+	var privileges types.PluginPrivileges
138
+	if m.Network.Type != "null" && m.Network.Type != "bridge" {
139
+		privileges = append(privileges, types.PluginPrivilege{
140
+			Name:        "network",
141
+			Description: "",
142
+			Value:       []string{m.Network.Type},
143
+		})
144
+	}
145
+	for _, mount := range m.Mounts {
146
+		if mount.Source != nil {
147
+			privileges = append(privileges, types.PluginPrivilege{
148
+				Name:        "mount",
149
+				Description: "",
150
+				Value:       []string{*mount.Source},
151
+			})
152
+		}
153
+	}
154
+	for _, device := range m.Devices {
155
+		if device.Path != nil {
156
+			privileges = append(privileges, types.PluginPrivilege{
157
+				Name:        "device",
158
+				Description: "",
159
+				Value:       []string{*device.Path},
160
+			})
161
+		}
162
+	}
163
+	if len(m.Capabilities) > 0 {
164
+		privileges = append(privileges, types.PluginPrivilege{
165
+			Name:        "capabilities",
166
+			Description: "",
167
+			Value:       m.Capabilities,
168
+		})
169
+	}
170
+	return privileges
171
+}
172
+
173
+// IsEnabled returns the active state of the plugin.
174
+func (p *Plugin) IsEnabled() bool {
175
+	p.RLock()
176
+	defer p.RUnlock()
177
+
178
+	return p.PluginObj.Enabled
179
+}
180
+
181
+// GetID returns the plugin's ID.
182
+func (p *Plugin) GetID() string {
183
+	p.RLock()
184
+	defer p.RUnlock()
185
+
186
+	return p.PluginObj.ID
187
+}
188
+
189
+// GetSocket returns the plugin socket.
190
+func (p *Plugin) GetSocket() string {
191
+	p.RLock()
192
+	defer p.RUnlock()
193
+
194
+	return p.PluginObj.Manifest.Interface.Socket
195
+}
196
+
197
+// GetTypes returns the interface types of a plugin.
198
+func (p *Plugin) GetTypes() []types.PluginInterfaceType {
199
+	p.RLock()
200
+	defer p.RUnlock()
201
+
202
+	return p.PluginObj.Manifest.Interface.Types
203
+}
204
+
205
+// InitSpec creates an OCI spec from the plugin's config.
206
+func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
207
+	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
208
+	s.Root = specs.Root{
209
+		Path:     rootfs,
210
+		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
211
+	}
212
+
213
+	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
214
+		Source:      &p.RuntimeSourcePath,
215
+		Destination: defaultPluginRuntimeDestination,
216
+		Type:        "bind",
217
+		Options:     []string{"rbind", "rshared"},
218
+	})
219
+	for _, mount := range mounts {
220
+		m := specs.Mount{
221
+			Destination: mount.Destination,
222
+			Type:        mount.Type,
223
+			Options:     mount.Options,
224
+		}
225
+		// TODO: if nil, then it's required and user didn't set it
226
+		if mount.Source != nil {
227
+			m.Source = *mount.Source
228
+		}
229
+		if m.Source != "" && m.Type == "bind" {
230
+			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
231
+			if err != nil {
232
+				return nil, err
233
+			}
234
+			if fi.IsDir() {
235
+				if err := os.MkdirAll(m.Source, 0700); err != nil {
236
+					return nil, err
237
+				}
238
+			}
239
+		}
240
+		s.Mounts = append(s.Mounts, m)
241
+	}
242
+
243
+	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
244
+	envs[0] = "PATH=" + system.DefaultPathEnv
245
+	envs = append(envs, p.PluginObj.Config.Env...)
246
+
247
+	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
248
+	cwd := p.PluginObj.Manifest.Workdir
249
+	if len(cwd) == 0 {
250
+		cwd = "/"
251
+	}
252
+	s.Process = specs.Process{
253
+		Terminal: false,
254
+		Args:     args,
255
+		Cwd:      cwd,
256
+		Env:      envs,
257
+	}
258
+
259
+	return &s, nil
260
+}
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"sync"
8 8
 
9 9
 	"github.com/docker/docker/pkg/locker"
10
-	"github.com/docker/docker/plugin"
10
+	pluginStore "github.com/docker/docker/plugin/store"
11 11
 	"github.com/docker/docker/volume"
12 12
 )
13 13
 
... ...
@@ -102,7 +102,7 @@ func lookup(name string) (volume.Driver, error) {
102 102
 		return ext, nil
103 103
 	}
104 104
 
105
-	p, err := plugin.LookupWithCapability(name, extName)
105
+	p, err := pluginStore.LookupWithCapability(name, extName)
106 106
 	if err != nil {
107 107
 		return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
108 108
 	}
... ...
@@ -151,7 +151,7 @@ func GetDriverList() []string {
151 151
 
152 152
 // GetAllDrivers lists all the registered drivers
153 153
 func GetAllDrivers() ([]volume.Driver, error) {
154
-	plugins, err := plugin.FindWithCapability(extName)
154
+	plugins, err := pluginStore.FindWithCapability(extName)
155 155
 	if err != nil {
156 156
 		return nil, fmt.Errorf("error listing plugins: %v", err)
157 157
 	}