Browse code

Merge pull request #28601 from tiborvass/plugin-misc-fixes

Plugin miscellaneous fixes

Anusha Ragunathan authored on 2016/11/23 09:12:37
Showing 18 changed files
... ...
@@ -1329,9 +1329,8 @@ definitions:
1329 1329
           - Entrypoint
1330 1330
           - Workdir
1331 1331
           - Network
1332
-          - Capabilities
1332
+          - Linux
1333 1333
           - Mounts
1334
-          - Devices
1335 1334
           - Env
1336 1335
           - Args
1337 1336
         properties:
... ...
@@ -1379,18 +1378,26 @@ definitions:
1379 1379
               Type:
1380 1380
                 x-nullable: false
1381 1381
                 type: "string"
1382
-          Capabilities:
1383
-            type: "array"
1384
-            items:
1385
-              type: "string"
1382
+          Linux:
1383
+            type: "object"
1384
+            x-nullable: false
1385
+            required: [Capabilities, DeviceCreation, Devices]
1386
+            properties:
1387
+              Capabilities:
1388
+                type: "array"
1389
+                items:
1390
+                  type: "string"
1391
+              DeviceCreation:
1392
+                type: "boolean"
1393
+                x-nullable: false
1394
+              Devices:
1395
+                type: "array"
1396
+                items:
1397
+                  $ref: "#/definitions/PluginDevice"
1386 1398
           Mounts:
1387 1399
             type: "array"
1388 1400
             items:
1389 1401
               $ref: "#/definitions/PluginMount"
1390
-          Devices:
1391
-            type: "array"
1392
-            items:
1393
-              $ref: "#/definitions/PluginDevice"
1394 1402
           Env:
1395 1403
             type: "array"
1396 1404
             items:
... ...
@@ -39,18 +39,10 @@ type PluginConfig struct {
39 39
 	// Required: true
40 40
 	Args PluginConfigArgs `json:"Args"`
41 41
 
42
-	// capabilities
43
-	// Required: true
44
-	Capabilities []string `json:"Capabilities"`
45
-
46 42
 	// description
47 43
 	// Required: true
48 44
 	Description string `json:"Description"`
49 45
 
50
-	// devices
51
-	// Required: true
52
-	Devices []PluginDevice `json:"Devices"`
53
-
54 46
 	// documentation
55 47
 	// Required: true
56 48
 	Documentation string `json:"Documentation"`
... ...
@@ -67,6 +59,10 @@ type PluginConfig struct {
67 67
 	// Required: true
68 68
 	Interface PluginConfigInterface `json:"Interface"`
69 69
 
70
+	// linux
71
+	// Required: true
72
+	Linux PluginConfigLinux `json:"Linux"`
73
+
70 74
 	// mounts
71 75
 	// Required: true
72 76
 	Mounts []PluginMount `json:"Mounts"`
... ...
@@ -117,6 +113,23 @@ type PluginConfigInterface struct {
117 117
 	Types []PluginInterfaceType `json:"Types"`
118 118
 }
119 119
 
120
+// PluginConfigLinux plugin config linux
121
+// swagger:model PluginConfigLinux
122
+type PluginConfigLinux struct {
123
+
124
+	// capabilities
125
+	// Required: true
126
+	Capabilities []string `json:"Capabilities"`
127
+
128
+	// device creation
129
+	// Required: true
130
+	DeviceCreation bool `json:"DeviceCreation"`
131
+
132
+	// devices
133
+	// Required: true
134
+	Devices []PluginDevice `json:"Devices"`
135
+}
136
+
120 137
 // PluginConfigNetwork plugin config network
121 138
 // swagger:model PluginConfigNetwork
122 139
 type PluginConfigNetwork struct {
... ...
@@ -8,13 +8,11 @@ import (
8 8
 	"os"
9 9
 	"path/filepath"
10 10
 	"strconv"
11
-	"strings"
12 11
 	"syscall"
13 12
 	"time"
14 13
 
15 14
 	"github.com/Sirupsen/logrus"
16 15
 	"github.com/cloudflare/cfssl/log"
17
-	containertypes "github.com/docker/docker/api/types/container"
18 16
 	"github.com/docker/docker/container"
19 17
 	"github.com/docker/docker/daemon/links"
20 18
 	"github.com/docker/docker/pkg/idtools"
... ...
@@ -22,16 +20,10 @@ import (
22 22
 	"github.com/docker/docker/pkg/stringid"
23 23
 	"github.com/docker/docker/runconfig"
24 24
 	"github.com/docker/libnetwork"
25
-	"github.com/opencontainers/runc/libcontainer/configs"
26
-	"github.com/opencontainers/runc/libcontainer/devices"
27 25
 	"github.com/opencontainers/runc/libcontainer/label"
28
-	"github.com/opencontainers/runtime-spec/specs-go"
29 26
 	"github.com/pkg/errors"
30 27
 )
31 28
 
32
-func u32Ptr(i int64) *uint32     { u := uint32(i); return &u }
33
-func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
34
-
35 29
 func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
36 30
 	var env []string
37 31
 	children := daemon.children(container)
... ...
@@ -247,78 +239,6 @@ func killProcessDirectly(container *container.Container) error {
247 247
 	return nil
248 248
 }
249 249
 
250
-func specDevice(d *configs.Device) specs.Device {
251
-	return specs.Device{
252
-		Type:     string(d.Type),
253
-		Path:     d.Path,
254
-		Major:    d.Major,
255
-		Minor:    d.Minor,
256
-		FileMode: fmPtr(int64(d.FileMode)),
257
-		UID:      u32Ptr(int64(d.Uid)),
258
-		GID:      u32Ptr(int64(d.Gid)),
259
-	}
260
-}
261
-
262
-func specDeviceCgroup(d *configs.Device) specs.DeviceCgroup {
263
-	t := string(d.Type)
264
-	return specs.DeviceCgroup{
265
-		Allow:  true,
266
-		Type:   &t,
267
-		Major:  &d.Major,
268
-		Minor:  &d.Minor,
269
-		Access: &d.Permissions,
270
-	}
271
-}
272
-
273
-func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []specs.Device, devPermissions []specs.DeviceCgroup, err error) {
274
-	resolvedPathOnHost := deviceMapping.PathOnHost
275
-
276
-	// check if it is a symbolic link
277
-	if src, e := os.Lstat(deviceMapping.PathOnHost); e == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
278
-		if linkedPathOnHost, e := filepath.EvalSymlinks(deviceMapping.PathOnHost); e == nil {
279
-			resolvedPathOnHost = linkedPathOnHost
280
-		}
281
-	}
282
-
283
-	device, err := devices.DeviceFromPath(resolvedPathOnHost, deviceMapping.CgroupPermissions)
284
-	// if there was no error, return the device
285
-	if err == nil {
286
-		device.Path = deviceMapping.PathInContainer
287
-		return append(devs, specDevice(device)), append(devPermissions, specDeviceCgroup(device)), nil
288
-	}
289
-
290
-	// if the device is not a device node
291
-	// try to see if it's a directory holding many devices
292
-	if err == devices.ErrNotADevice {
293
-
294
-		// check if it is a directory
295
-		if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() {
296
-
297
-			// mount the internal devices recursively
298
-			filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error {
299
-				childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions)
300
-				if e != nil {
301
-					// ignore the device
302
-					return nil
303
-				}
304
-
305
-				// add the device to userSpecified devices
306
-				childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, deviceMapping.PathInContainer, 1)
307
-				devs = append(devs, specDevice(childDevice))
308
-				devPermissions = append(devPermissions, specDeviceCgroup(childDevice))
309
-
310
-				return nil
311
-			})
312
-		}
313
-	}
314
-
315
-	if len(devs) > 0 {
316
-		return devs, devPermissions, nil
317
-	}
318
-
319
-	return devs, devPermissions, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err)
320
-}
321
-
322 250
 func detachMounted(path string) error {
323 251
 	return syscall.Unmount(path, syscall.MNT_DETACH)
324 252
 }
... ...
@@ -88,7 +88,7 @@ func setDevices(s *specs.Spec, c *container.Container) error {
88 88
 			return err
89 89
 		}
90 90
 		for _, d := range hostDevices {
91
-			devs = append(devs, specDevice(d))
91
+			devs = append(devs, oci.Device(d))
92 92
 		}
93 93
 		rwm := "rwm"
94 94
 		devPermissions = []specs.DeviceCgroup{
... ...
@@ -99,7 +99,7 @@ func setDevices(s *specs.Spec, c *container.Container) error {
99 99
 		}
100 100
 	} else {
101 101
 		for _, deviceMapping := range c.HostConfig.Devices {
102
-			d, dPermissions, err := getDevicesFromPath(deviceMapping)
102
+			d, dPermissions, err := oci.DevicesFromPath(deviceMapping.PathOnHost, deviceMapping.PathInContainer, deviceMapping.CgroupPermissions)
103 103
 			if err != nil {
104 104
 				return err
105 105
 			}
... ...
@@ -221,18 +221,6 @@ func setCapabilities(s *specs.Spec, c *container.Container) error {
221 221
 	return nil
222 222
 }
223 223
 
224
-func delNamespace(s *specs.Spec, nsType specs.NamespaceType) {
225
-	idx := -1
226
-	for i, n := range s.Linux.Namespaces {
227
-		if n.Type == nsType {
228
-			idx = i
229
-		}
230
-	}
231
-	if idx >= 0 {
232
-		s.Linux.Namespaces = append(s.Linux.Namespaces[:idx], s.Linux.Namespaces[idx+1:]...)
233
-	}
234
-}
235
-
236 224
 func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error {
237 225
 	userNS := false
238 226
 	// user
... ...
@@ -283,7 +271,7 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
283 283
 			setNamespace(s, nsUser)
284 284
 		}
285 285
 	} else if c.HostConfig.IpcMode.IsHost() {
286
-		delNamespace(s, specs.NamespaceType("ipc"))
286
+		oci.RemoveNamespace(s, specs.NamespaceType("ipc"))
287 287
 	} else {
288 288
 		ns := specs.Namespace{Type: "ipc"}
289 289
 		setNamespace(s, ns)
... ...
@@ -304,14 +292,14 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
304 304
 			setNamespace(s, nsUser)
305 305
 		}
306 306
 	} else if c.HostConfig.PidMode.IsHost() {
307
-		delNamespace(s, specs.NamespaceType("pid"))
307
+		oci.RemoveNamespace(s, specs.NamespaceType("pid"))
308 308
 	} else {
309 309
 		ns := specs.Namespace{Type: "pid"}
310 310
 		setNamespace(s, ns)
311 311
 	}
312 312
 	// uts
313 313
 	if c.HostConfig.UTSMode.IsHost() {
314
-		delNamespace(s, specs.NamespaceType("uts"))
314
+		oci.RemoveNamespace(s, specs.NamespaceType("uts"))
315 315
 		s.Hostname = ""
316 316
 	}
317 317
 
... ...
@@ -16,6 +16,7 @@ keywords: "API, Usage, plugins, documentation, developer"
16 16
      will be rejected.
17 17
 -->
18 18
 
19
+
19 20
 # Plugin Config Version 0 of Plugin V2
20 21
 
21 22
 This document outlines the format of the V0 plugin configuration. The plugin
... ...
@@ -85,10 +86,6 @@ Config provides the base accessible fields for working with V0 plugin format
85 85
       	- **host**
86 86
       	- **none**
87 87
 
88
-- **`capabilities`** *array*
89
-
90
-   capabilities of the plugin (*Linux only*), see list [`here`](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md#security)
91
-
92 88
 - **`mounts`** *PluginMount array*
93 89
 
94 90
    mount of the plugin, struct consisting of the following fields, see [`MOUNTS`](https://github.com/opencontainers/runtime-spec/blob/master/config.md#mounts)
... ...
@@ -117,22 +114,6 @@ Config provides the base accessible fields for working with V0 plugin format
117 117
 
118 118
 	  options of the mount.
119 119
 
120
-- **`devices`** *PluginDevice array*
121
-
122
-    device of the plugin, (*Linux only*), struct consisting of the following fields, see [`DEVICES`](https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#devices)
123
-
124
-    - **`name`** *string*
125
-
126
-	  name of the device.
127
-
128
-    - **`description`** *string*
129
-
130
-      description of the device.
131
-
132
-    - **`path`** *string*
133
-
134
-	  path of the device.
135
-
136 120
 - **`env`** *PluginEnv array*
137 121
 
138 122
    env of the plugin, struct consisting of the following fields
... ...
@@ -165,6 +146,27 @@ Config provides the base accessible fields for working with V0 plugin format
165 165
 
166 166
 	  values of the args.
167 167
 
168
+- **`linux`** *PluginLinux*
169
+
170
+    - **`capabilities`** *string array*
171
+
172
+          capabilities of the plugin (*Linux only*), see list [`here`](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md#security)
173
+
174
+    - **`devices`** *PluginDevice array*
175
+
176
+          device of the plugin, (*Linux only*), struct consisting of the following fields, see [`DEVICES`](https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#devices)
177
+
178
+          - **`name`** *string*
179
+
180
+	      name of the device.
181
+
182
+          - **`description`** *string*
183
+
184
+              description of the device.
185
+
186
+          - **`path`** *string*
187
+
188
+              path of the device.
168 189
 
169 190
 ## Example Config
170 191
 
... ...
@@ -13,8 +13,6 @@ import (
13 13
 	"github.com/go-check/check"
14 14
 )
15 15
 
16
-var pluginName = "tiborvass/no-remove"
17
-
18 16
 // TestDaemonRestartWithPluginEnabled tests state restore for an enabled plugin
19 17
 func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
20 18
 	testRequires(c, Network)
... ...
@@ -23,15 +21,15 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
23 23
 		c.Fatalf("Could not start daemon: %v", err)
24 24
 	}
25 25
 
26
-	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
26
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
27 27
 		c.Fatalf("Could not install plugin: %v %s", err, out)
28 28
 	}
29 29
 
30 30
 	defer func() {
31
-		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
31
+		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
32 32
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
33 33
 		}
34
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
34
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
35 35
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
36 36
 		}
37 37
 	}()
... ...
@@ -44,7 +42,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
44 44
 	if err != nil {
45 45
 		c.Fatalf("Could not list plugins: %v %s", err, out)
46 46
 	}
47
-	c.Assert(out, checker.Contains, pluginName)
47
+	c.Assert(out, checker.Contains, pName)
48 48
 	c.Assert(out, checker.Contains, "true")
49 49
 }
50 50
 
... ...
@@ -56,12 +54,12 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
56 56
 		c.Fatalf("Could not start daemon: %v", err)
57 57
 	}
58 58
 
59
-	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName, "--disable"); err != nil {
59
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName, "--disable"); err != nil {
60 60
 		c.Fatalf("Could not install plugin: %v %s", err, out)
61 61
 	}
62 62
 
63 63
 	defer func() {
64
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
64
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
65 65
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
66 66
 		}
67 67
 	}()
... ...
@@ -74,7 +72,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
74 74
 	if err != nil {
75 75
 		c.Fatalf("Could not list plugins: %v %s", err, out)
76 76
 	}
77
-	c.Assert(out, checker.Contains, pluginName)
77
+	c.Assert(out, checker.Contains, pName)
78 78
 	c.Assert(out, checker.Contains, "false")
79 79
 }
80 80
 
... ...
@@ -86,17 +84,17 @@ func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
86 86
 	if err := s.d.Start("--live-restore"); err != nil {
87 87
 		c.Fatalf("Could not start daemon: %v", err)
88 88
 	}
89
-	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
89
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
90 90
 		c.Fatalf("Could not install plugin: %v %s", err, out)
91 91
 	}
92 92
 	defer func() {
93 93
 		if err := s.d.Restart("--live-restore"); err != nil {
94 94
 			c.Fatalf("Could not restart daemon: %v", err)
95 95
 		}
96
-		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
96
+		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
97 97
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
98 98
 		}
99
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
99
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
100 100
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
101 101
 		}
102 102
 	}()
... ...
@@ -105,7 +103,7 @@ func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
105 105
 		c.Fatalf("Could not kill daemon: %v", err)
106 106
 	}
107 107
 
108
-	cmd := exec.Command("pgrep", "-f", "plugin-no-remove")
108
+	cmd := exec.Command("pgrep", "-f", pluginProcessName)
109 109
 	if out, ec, err := runCommandWithOutput(cmd); ec != 0 {
110 110
 		c.Fatalf("Expected exit code '0', got %d err: %v output: %s ", ec, err, out)
111 111
 	}
... ...
@@ -119,17 +117,17 @@ func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C)
119 119
 	if err := s.d.Start("--live-restore"); err != nil {
120 120
 		c.Fatalf("Could not start daemon: %v", err)
121 121
 	}
122
-	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
122
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
123 123
 		c.Fatalf("Could not install plugin: %v %s", err, out)
124 124
 	}
125 125
 	defer func() {
126 126
 		if err := s.d.Restart("--live-restore"); err != nil {
127 127
 			c.Fatalf("Could not restart daemon: %v", err)
128 128
 		}
129
-		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
129
+		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
130 130
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
131 131
 		}
132
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
132
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
133 133
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
134 134
 		}
135 135
 	}()
... ...
@@ -138,7 +136,7 @@ func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C)
138 138
 		c.Fatalf("Could not kill daemon: %v", err)
139 139
 	}
140 140
 
141
-	cmd := exec.Command("pgrep", "-f", "plugin-no-remove")
141
+	cmd := exec.Command("pgrep", "-f", pluginProcessName)
142 142
 	if out, ec, err := runCommandWithOutput(cmd); ec != 0 {
143 143
 		c.Fatalf("Expected exit code '0', got %d err: %v output: %s ", ec, err, out)
144 144
 	}
... ...
@@ -151,7 +149,7 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
151 151
 	if err := s.d.Start(); err != nil {
152 152
 		c.Fatalf("Could not start daemon: %v", err)
153 153
 	}
154
-	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
154
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
155 155
 		c.Fatalf("Could not install plugin: %v %s", err, out)
156 156
 	}
157 157
 
... ...
@@ -159,10 +157,10 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
159 159
 		if err := s.d.Restart(); err != nil {
160 160
 			c.Fatalf("Could not restart daemon: %v", err)
161 161
 		}
162
-		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
162
+		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
163 163
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
164 164
 		}
165
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
165
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
166 166
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
167 167
 		}
168 168
 	}()
... ...
@@ -177,7 +175,7 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
177 177
 		}
178 178
 	}
179 179
 
180
-	cmd := exec.Command("pgrep", "-f", "plugin-no-remove")
180
+	cmd := exec.Command("pgrep", "-f", pluginProcessName)
181 181
 	if out, ec, err := runCommandWithOutput(cmd); ec != 1 {
182 182
 		c.Fatalf("Expected exit code '1', got %d err: %v output: %s ", ec, err, out)
183 183
 	}
... ...
@@ -195,20 +193,20 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
195 195
 	if err := s.d.Start(); err != nil {
196 196
 		c.Fatalf("Could not start daemon: %v", err)
197 197
 	}
198
-	out, err := s.d.Cmd("plugin", "install", pluginName, "--grant-all-permissions")
198
+	out, err := s.d.Cmd("plugin", "install", pName, "--grant-all-permissions")
199 199
 	if err != nil {
200 200
 		c.Fatalf("Could not install plugin: %v %s", err, out)
201 201
 	}
202 202
 	defer func() {
203
-		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
203
+		if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
204 204
 			c.Fatalf("Could not disable plugin: %v %s", err, out)
205 205
 		}
206
-		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
206
+		if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
207 207
 			c.Fatalf("Could not remove plugin: %v %s", err, out)
208 208
 		}
209 209
 	}()
210 210
 
211
-	out, err = s.d.Cmd("volume", "create", "-d", pluginName, volName)
211
+	out, err = s.d.Cmd("volume", "create", "-d", pName, volName)
212 212
 	if err != nil {
213 213
 		c.Fatalf("Could not create volume: %v %s", err, out)
214 214
 	}
... ...
@@ -223,7 +221,7 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
223 223
 		c.Fatalf("Could not list volume: %v %s", err, out)
224 224
 	}
225 225
 	c.Assert(out, checker.Contains, volName)
226
-	c.Assert(out, checker.Contains, pluginName)
226
+	c.Assert(out, checker.Contains, pName)
227 227
 
228 228
 	mountPoint, err := s.d.Cmd("volume", "inspect", volName, "--format", "{{.Mountpoint}}")
229 229
 	if err != nil {
... ...
@@ -278,12 +278,11 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
278 278
 func (s *DockerSuite) TestEventsPluginOps(c *check.C) {
279 279
 	testRequires(c, DaemonIsLinux)
280 280
 
281
-	pluginName := "tiborvass/no-remove:latest"
282 281
 	since := daemonUnixTime(c)
283 282
 
284
-	dockerCmd(c, "plugin", "install", pluginName, "--grant-all-permissions")
285
-	dockerCmd(c, "plugin", "disable", pluginName)
286
-	dockerCmd(c, "plugin", "remove", pluginName)
283
+	dockerCmd(c, "plugin", "install", pNameWithTag, "--grant-all-permissions")
284
+	dockerCmd(c, "plugin", "disable", pNameWithTag)
285
+	dockerCmd(c, "plugin", "remove", pNameWithTag)
287 286
 
288 287
 	out, _ := dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c))
289 288
 	events := strings.Split(out, "\n")
... ...
@@ -292,7 +291,7 @@ func (s *DockerSuite) TestEventsPluginOps(c *check.C) {
292 292
 	nEvents := len(events)
293 293
 	c.Assert(nEvents, checker.GreaterOrEqualThan, 4)
294 294
 
295
-	pluginEvents := eventActionsByIDAndType(c, events, pluginName, "plugin")
295
+	pluginEvents := eventActionsByIDAndType(c, events, pNameWithTag, "plugin")
296 296
 	c.Assert(pluginEvents, checker.HasLen, 4, check.Commentf("events: %v", events))
297 297
 
298 298
 	c.Assert(pluginEvents[0], checker.Equals, "pull", check.Commentf(out))
... ...
@@ -774,7 +774,7 @@ func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *check.C) {
774 774
 	testRequires(c, DaemonIsLinux, Network, IsAmd64)
775 775
 
776 776
 	var (
777
-		npName        = "mavenugo/test-docker-netplugin"
777
+		npName        = "tiborvass/test-docker-netplugin"
778 778
 		npTag         = "latest"
779 779
 		npNameWithTag = npName + ":" + npTag
780 780
 	)
... ...
@@ -10,9 +10,10 @@ import (
10 10
 )
11 11
 
12 12
 var (
13
-	pName        = "tiborvass/no-remove"
14
-	pTag         = "latest"
15
-	pNameWithTag = pName + ":" + pTag
13
+	pluginProcessName = "no-remove"
14
+	pName             = "tiborvass/no-remove"
15
+	pTag              = "latest"
16
+	pNameWithTag      = pName + ":" + pTag
16 17
 )
17 18
 
18 19
 func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
19 20
new file mode 100644
... ...
@@ -0,0 +1,86 @@
0
+package oci
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"path/filepath"
6
+	"strings"
7
+
8
+	"github.com/opencontainers/runc/libcontainer/configs"
9
+	"github.com/opencontainers/runc/libcontainer/devices"
10
+	specs "github.com/opencontainers/runtime-spec/specs-go"
11
+)
12
+
13
+// Device transforms a libcontainer configs.Device to a specs.Device object.
14
+func Device(d *configs.Device) specs.Device {
15
+	return specs.Device{
16
+		Type:     string(d.Type),
17
+		Path:     d.Path,
18
+		Major:    d.Major,
19
+		Minor:    d.Minor,
20
+		FileMode: fmPtr(int64(d.FileMode)),
21
+		UID:      u32Ptr(int64(d.Uid)),
22
+		GID:      u32Ptr(int64(d.Gid)),
23
+	}
24
+}
25
+
26
+func deviceCgroup(d *configs.Device) specs.DeviceCgroup {
27
+	t := string(d.Type)
28
+	return specs.DeviceCgroup{
29
+		Allow:  true,
30
+		Type:   &t,
31
+		Major:  &d.Major,
32
+		Minor:  &d.Minor,
33
+		Access: &d.Permissions,
34
+	}
35
+}
36
+
37
+// DevicesFromPath computes a list of devices and device permissions from paths (pathOnHost and pathInContainer) and cgroup permissions.
38
+func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (devs []specs.Device, devPermissions []specs.DeviceCgroup, err error) {
39
+	resolvedPathOnHost := pathOnHost
40
+
41
+	// check if it is a symbolic link
42
+	if src, e := os.Lstat(pathOnHost); e == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
43
+		if linkedPathOnHost, e := filepath.EvalSymlinks(pathOnHost); e == nil {
44
+			resolvedPathOnHost = linkedPathOnHost
45
+		}
46
+	}
47
+
48
+	device, err := devices.DeviceFromPath(resolvedPathOnHost, cgroupPermissions)
49
+	// if there was no error, return the device
50
+	if err == nil {
51
+		device.Path = pathInContainer
52
+		return append(devs, Device(device)), append(devPermissions, deviceCgroup(device)), nil
53
+	}
54
+
55
+	// if the device is not a device node
56
+	// try to see if it's a directory holding many devices
57
+	if err == devices.ErrNotADevice {
58
+
59
+		// check if it is a directory
60
+		if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() {
61
+
62
+			// mount the internal devices recursively
63
+			filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error {
64
+				childDevice, e := devices.DeviceFromPath(dpath, cgroupPermissions)
65
+				if e != nil {
66
+					// ignore the device
67
+					return nil
68
+				}
69
+
70
+				// add the device to userSpecified devices
71
+				childDevice.Path = strings.Replace(dpath, resolvedPathOnHost, pathInContainer, 1)
72
+				devs = append(devs, Device(childDevice))
73
+				devPermissions = append(devPermissions, deviceCgroup(childDevice))
74
+
75
+				return nil
76
+			})
77
+		}
78
+	}
79
+
80
+	if len(devs) > 0 {
81
+		return devs, devPermissions, nil
82
+	}
83
+
84
+	return devs, devPermissions, fmt.Errorf("error gathering device information while adding custom device %q: %s", pathOnHost, err)
85
+}
0 86
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+// +build !linux
1
+
2
+package oci
3
+
4
+import (
5
+	"errors"
6
+
7
+	"github.com/opencontainers/runc/libcontainer/configs"
8
+	specs "github.com/opencontainers/runtime-spec/specs-go"
9
+)
10
+
11
+// Device transforms a libcontainer configs.Device to a specs.Device object.
12
+// Not implemented
13
+func Device(d *configs.Device) specs.Device { return specs.Device{} }
14
+
15
+// DevicesFromPath computes a list of devices and device permissions from paths (pathOnHost and pathInContainer) and cgroup permissions.
16
+// Not implemented
17
+func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (devs []specs.Device, devPermissions []specs.DeviceCgroup, err error) {
18
+	return nil, nil, errors.New("oci/devices: unsupported platform")
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+package oci
1
+
2
+import specs "github.com/opencontainers/runtime-spec/specs-go"
3
+
4
+// RemoveNamespace removes the `nsType` namespace from OCI spec `s`
5
+func RemoveNamespace(s *specs.Spec, nsType specs.NamespaceType) {
6
+	idx := -1
7
+	for i, n := range s.Linux.Namespaces {
8
+		if n.Type == nsType {
9
+			idx = i
10
+		}
11
+	}
12
+	if idx >= 0 {
13
+		s.Linux.Namespaces = append(s.Linux.Namespaces[:idx], s.Linux.Namespaces[idx+1:]...)
14
+	}
15
+}
... ...
@@ -126,7 +126,7 @@ func (ps *Store) updatePluginDB() error {
126 126
 	return nil
127 127
 }
128 128
 
129
-// Get returns a plugin matching the given name and capability.
129
+// Get returns an enabled plugin matching the given name and capability.
130 130
 func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
131 131
 	var (
132 132
 		p   *v2.Plugin
... ...
@@ -151,7 +151,12 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug
151 151
 			p.Lock()
152 152
 			p.RefCount += mode
153 153
 			p.Unlock()
154
-			return p.FilterByCap(capability)
154
+			if p.IsEnabled() {
155
+				return p.FilterByCap(capability)
156
+			}
157
+			// Plugin was found but it is disabled, so we should not fall back to legacy plugins
158
+			// but we should error out right away
159
+			return nil, ErrNotFound(fullName)
155 160
 		}
156 161
 		if _, ok := err.(ErrNotFound); !ok {
157 162
 			return nil, err
... ...
@@ -170,7 +175,7 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug
170 170
 	return nil, err
171 171
 }
172 172
 
173
-// GetAllByCap returns a list of plugins matching the given capability.
173
+// GetAllByCap returns a list of enabled plugins matching the given capability.
174 174
 func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
175 175
 	result := make([]plugingetter.CompatPlugin, 0, 1)
176 176
 
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"sync"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/oci"
12 13
 	"github.com/docker/docker/pkg/plugins"
13 14
 	"github.com/docker/docker/pkg/system"
14 15
 	specs "github.com/opencontainers/runtime-spec/specs-go"
... ...
@@ -104,6 +105,8 @@ func (p *Plugin) InitPlugin() error {
104 104
 		p.PluginObj.Settings.Mounts[i] = mount
105 105
 	}
106 106
 	p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env))
107
+	p.PluginObj.Settings.Devices = make([]types.PluginDevice, 0, len(p.PluginObj.Config.Linux.Devices))
108
+	copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices)
107 109
 	for _, env := range p.PluginObj.Config.Env {
108 110
 		if env.Value != nil {
109 111
 			p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
... ...
@@ -176,7 +179,7 @@ next:
176 176
 		}
177 177
 
178 178
 		// range over all the devices in the config
179
-		for _, device := range p.PluginObj.Config.Devices {
179
+		for _, device := range p.PluginObj.Config.Linux.Devices {
180 180
 			// found the device in the config
181 181
 			if device.Name == s.name {
182 182
 				// is it settable ?
... ...
@@ -216,38 +219,45 @@ next:
216 216
 // ComputePrivileges takes the config file and computes the list of access necessary
217 217
 // for the plugin on the host.
218 218
 func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
219
-	m := p.PluginObj.Config
219
+	c := p.PluginObj.Config
220 220
 	var privileges types.PluginPrivileges
221
-	if m.Network.Type != "null" && m.Network.Type != "bridge" {
221
+	if c.Network.Type != "null" && c.Network.Type != "bridge" {
222 222
 		privileges = append(privileges, types.PluginPrivilege{
223 223
 			Name:        "network",
224
-			Description: "",
225
-			Value:       []string{m.Network.Type},
224
+			Description: "permissions to access a network",
225
+			Value:       []string{c.Network.Type},
226 226
 		})
227 227
 	}
228
-	for _, mount := range m.Mounts {
228
+	for _, mount := range c.Mounts {
229 229
 		if mount.Source != nil {
230 230
 			privileges = append(privileges, types.PluginPrivilege{
231 231
 				Name:        "mount",
232
-				Description: "",
232
+				Description: "host path to mount",
233 233
 				Value:       []string{*mount.Source},
234 234
 			})
235 235
 		}
236 236
 	}
237
-	for _, device := range m.Devices {
237
+	for _, device := range c.Linux.Devices {
238 238
 		if device.Path != nil {
239 239
 			privileges = append(privileges, types.PluginPrivilege{
240 240
 				Name:        "device",
241
-				Description: "",
241
+				Description: "host device to access",
242 242
 				Value:       []string{*device.Path},
243 243
 			})
244 244
 		}
245 245
 	}
246
-	if len(m.Capabilities) > 0 {
246
+	if c.Linux.DeviceCreation {
247
+		privileges = append(privileges, types.PluginPrivilege{
248
+			Name:        "device-creation",
249
+			Description: "allow creating devices inside plugin",
250
+			Value:       []string{"true"},
251
+		})
252
+	}
253
+	if len(c.Linux.Capabilities) > 0 {
247 254
 		privileges = append(privileges, types.PluginPrivilege{
248 255
 			Name:        "capabilities",
249
-			Description: "",
250
-			Value:       m.Capabilities,
256
+			Description: "list of additional capabilities required",
257
+			Value:       c.Linux.Capabilities,
251 258
 		})
252 259
 	}
253 260
 	return privileges
... ...
@@ -293,12 +303,40 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
293 293
 		Readonly: false, // TODO: all plugins should be readonly? settable in config?
294 294
 	}
295 295
 
296
-	mounts := append(p.PluginObj.Settings.Mounts, types.PluginMount{
296
+	userMounts := make(map[string]struct{}, len(p.PluginObj.Config.Mounts))
297
+	for _, m := range p.PluginObj.Config.Mounts {
298
+		userMounts[m.Destination] = struct{}{}
299
+	}
300
+
301
+	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
297 302
 		Source:      &p.RuntimeSourcePath,
298 303
 		Destination: defaultPluginRuntimeDestination,
299 304
 		Type:        "bind",
300 305
 		Options:     []string{"rbind", "rshared"},
301 306
 	})
307
+
308
+	if p.PluginObj.Config.Network.Type != "" {
309
+		// TODO: if net == bridge, use libnetwork controller to create a new plugin-specific bridge, bind mount /etc/hosts and /etc/resolv.conf look at the docker code (allocateNetwork, initialize)
310
+		if p.PluginObj.Config.Network.Type == "host" {
311
+			oci.RemoveNamespace(&s, specs.NamespaceType("network"))
312
+		}
313
+		etcHosts := "/etc/hosts"
314
+		resolvConf := "/etc/resolv.conf"
315
+		mounts = append(mounts,
316
+			types.PluginMount{
317
+				Source:      &etcHosts,
318
+				Destination: etcHosts,
319
+				Type:        "bind",
320
+				Options:     []string{"rbind", "ro"},
321
+			},
322
+			types.PluginMount{
323
+				Source:      &resolvConf,
324
+				Destination: resolvConf,
325
+				Type:        "bind",
326
+				Options:     []string{"rbind", "ro"},
327
+			})
328
+	}
329
+
302 330
 	for _, mount := range mounts {
303 331
 		m := specs.Mount{
304 332
 			Destination: mount.Destination,
... ...
@@ -323,6 +361,28 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
323 323
 		s.Mounts = append(s.Mounts, m)
324 324
 	}
325 325
 
326
+	for i, m := range s.Mounts {
327
+		if strings.HasPrefix(m.Destination, "/dev/") {
328
+			if _, ok := userMounts[m.Destination]; ok {
329
+				s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...)
330
+			}
331
+		}
332
+	}
333
+
334
+	if p.PluginObj.Config.Linux.DeviceCreation {
335
+		rwm := "rwm"
336
+		s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}}
337
+	}
338
+	for _, dev := range p.PluginObj.Config.Linux.Devices {
339
+		path := *dev.Path
340
+		d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm")
341
+		if err != nil {
342
+			return nil, err
343
+		}
344
+		s.Linux.Devices = append(s.Linux.Devices, d...)
345
+		s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...)
346
+	}
347
+
326 348
 	envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1)
327 349
 	envs[0] = "PATH=" + system.DefaultPathEnv
328 350
 	envs = append(envs, p.PluginObj.Settings.Env...)
... ...
@@ -332,12 +392,12 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
332 332
 	if len(cwd) == 0 {
333 333
 		cwd = "/"
334 334
 	}
335
-	s.Process = specs.Process{
336
-		Terminal: false,
337
-		Args:     args,
338
-		Cwd:      cwd,
339
-		Env:      envs,
340
-	}
335
+	s.Process.Terminal = false
336
+	s.Process.Args = args
337
+	s.Process.Cwd = cwd
338
+	s.Process.Env = envs
339
+
340
+	s.Process.Capabilities = append(s.Process.Capabilities, p.PluginObj.Config.Linux.Capabilities...)
341 341
 
342 342
 	return &s, nil
343 343
 }
... ...
@@ -17,7 +17,7 @@ github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
17 17
 golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://github.com/tonistiigi/net.git
18 18
 golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
19 19
 github.com/docker/go-units 8a7beacffa3009a9ac66bad506b18ffdd110cf97
20
-github.com/docker/go-connections f512407a188ecb16f31a33dbc9c4e4814afc1b03
20
+github.com/docker/go-connections 4ccf312bf1d35e5dbda654e57a9be4c3f3cd0366
21 21
 
22 22
 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
23 23
 github.com/imdario/mergo 0.2.1
... ...
@@ -2,6 +2,7 @@
2 2
 package sockets
3 3
 
4 4
 import (
5
+	"errors"
5 6
 	"net"
6 7
 	"net/http"
7 8
 	"time"
... ...
@@ -10,6 +11,9 @@ import (
10 10
 // Why 32? See https://github.com/docker/docker/pull/8035.
11 11
 const defaultTimeout = 32 * time.Second
12 12
 
13
+// ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system.
14
+var ErrProtocolNotAvailable = errors.New("protocol not available")
15
+
13 16
 // ConfigureTransport configures the specified Transport according to the
14 17
 // specified proto and addr.
15 18
 // If the proto is unix (using a unix socket to communicate) or npipe the
... ...
@@ -17,17 +21,9 @@ const defaultTimeout = 32 * time.Second
17 17
 func ConfigureTransport(tr *http.Transport, proto, addr string) error {
18 18
 	switch proto {
19 19
 	case "unix":
20
-		// No need for compression in local communications.
21
-		tr.DisableCompression = true
22
-		tr.Dial = func(_, _ string) (net.Conn, error) {
23
-			return net.DialTimeout(proto, addr, defaultTimeout)
24
-		}
20
+		return configureUnixTransport(tr, proto, addr)
25 21
 	case "npipe":
26
-		// No need for compression in local communications.
27
-		tr.DisableCompression = true
28
-		tr.Dial = func(_, _ string) (net.Conn, error) {
29
-			return DialPipe(addr, defaultTimeout)
30
-		}
22
+		return configureNpipeTransport(tr, proto, addr)
31 23
 	default:
32 24
 		tr.Proxy = http.ProxyFromEnvironment
33 25
 		dialer, err := DialerFromEnvironment(&net.Dialer{
... ...
@@ -3,11 +3,31 @@
3 3
 package sockets
4 4
 
5 5
 import (
6
+	"fmt"
6 7
 	"net"
8
+	"net/http"
7 9
 	"syscall"
8 10
 	"time"
9 11
 )
10 12
 
13
+const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
14
+
15
+func configureUnixTransport(tr *http.Transport, proto, addr string) error {
16
+	if len(addr) > maxUnixSocketPathSize {
17
+		return fmt.Errorf("Unix socket path %q is too long", addr)
18
+	}
19
+	// No need for compression in local communications.
20
+	tr.DisableCompression = true
21
+	tr.Dial = func(_, _ string) (net.Conn, error) {
22
+		return net.DialTimeout(proto, addr, defaultTimeout)
23
+	}
24
+	return nil
25
+}
26
+
27
+func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
28
+	return ErrProtocolNotAvailable
29
+}
30
+
11 31
 // DialPipe connects to a Windows named pipe.
12 32
 // This is not supported on other OSes.
13 33
 func DialPipe(_ string, _ time.Duration) (net.Conn, error) {
... ...
@@ -2,11 +2,25 @@ package sockets
2 2
 
3 3
 import (
4 4
 	"net"
5
+	"net/http"
5 6
 	"time"
6 7
 
7 8
 	"github.com/Microsoft/go-winio"
8 9
 )
9 10
 
11
+func configureUnixTransport(tr *http.Transport, proto, addr string) error {
12
+	return ErrProtocolNotAvailable
13
+}
14
+
15
+func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
16
+	// No need for compression in local communications.
17
+	tr.DisableCompression = true
18
+	tr.Dial = func(_, _ string) (net.Conn, error) {
19
+		return DialPipe(addr, defaultTimeout)
20
+	}
21
+	return nil
22
+}
23
+
10 24
 // DialPipe connects to a Windows named pipe.
11 25
 func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
12 26
 	return winio.DialPipe(addr, &timeout)