Signed-off-by: Tibor Vass <tibor@docker.com>
| ... | ... |
@@ -1313,8 +1313,6 @@ definitions: |
| 1313 | 1313 |
- Network |
| 1314 | 1314 |
- Linux |
| 1315 | 1315 |
- Mounts |
| 1316 |
- - Devices |
|
| 1317 |
- - DeviceCreation |
|
| 1318 | 1316 |
- Env |
| 1319 | 1317 |
- Args |
| 1320 | 1318 |
properties: |
| ... | ... |
@@ -1365,23 +1363,23 @@ definitions: |
| 1365 | 1365 |
Linux: |
| 1366 | 1366 |
type: "object" |
| 1367 | 1367 |
x-nullable: false |
| 1368 |
- required: [Capabilities] |
|
| 1368 |
+ required: [Capabilities, DeviceCreation, Devices] |
|
| 1369 | 1369 |
properties: |
| 1370 | 1370 |
Capabilities: |
| 1371 | 1371 |
type: "array" |
| 1372 | 1372 |
items: |
| 1373 | 1373 |
type: "string" |
| 1374 |
+ DeviceCreation: |
|
| 1375 |
+ type: "boolean" |
|
| 1376 |
+ x-nullable: false |
|
| 1377 |
+ Devices: |
|
| 1378 |
+ type: "array" |
|
| 1379 |
+ items: |
|
| 1380 |
+ $ref: "#/definitions/PluginDevice" |
|
| 1374 | 1381 |
Mounts: |
| 1375 | 1382 |
type: "array" |
| 1376 | 1383 |
items: |
| 1377 | 1384 |
$ref: "#/definitions/PluginMount" |
| 1378 |
- Devices: |
|
| 1379 |
- type: "array" |
|
| 1380 |
- items: |
|
| 1381 |
- $ref: "#/definitions/PluginDevice" |
|
| 1382 |
- DeviceCreation: |
|
| 1383 |
- type: "boolean" |
|
| 1384 |
- x-nullable: false |
|
| 1385 | 1385 |
Env: |
| 1386 | 1386 |
type: "array" |
| 1387 | 1387 |
items: |
| ... | ... |
@@ -43,14 +43,6 @@ type PluginConfig struct {
|
| 43 | 43 |
// Required: true |
| 44 | 44 |
Description string `json:"Description"` |
| 45 | 45 |
|
| 46 |
- // device creation |
|
| 47 |
- // Required: true |
|
| 48 |
- DeviceCreation bool `json:"DeviceCreation"` |
|
| 49 |
- |
|
| 50 |
- // devices |
|
| 51 |
- // Required: true |
|
| 52 |
- Devices []PluginDevice `json:"Devices"` |
|
| 53 |
- |
|
| 54 | 46 |
// documentation |
| 55 | 47 |
// Required: true |
| 56 | 48 |
Documentation string `json:"Documentation"` |
| ... | ... |
@@ -128,6 +120,14 @@ type PluginConfigLinux struct {
|
| 128 | 128 |
// capabilities |
| 129 | 129 |
// Required: true |
| 130 | 130 |
Capabilities []string `json:"Capabilities"` |
| 131 |
+ |
|
| 132 |
+ // device creation |
|
| 133 |
+ // Required: true |
|
| 134 |
+ DeviceCreation bool `json:"DeviceCreation"` |
|
| 135 |
+ |
|
| 136 |
+ // devices |
|
| 137 |
+ // Required: true |
|
| 138 |
+ Devices []PluginDevice `json:"Devices"` |
|
| 131 | 139 |
} |
| 132 | 140 |
|
| 133 | 141 |
// PluginConfigNetwork plugin config network |
| ... | ... |
@@ -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 |
} |
| 106 | 106 |
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 |
+} |
| ... | ... |
@@ -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" |
| ... | ... |
@@ -103,6 +104,8 @@ func (p *Plugin) InitPlugin() error {
|
| 103 | 103 |
p.PluginObj.Settings.Mounts[i] = mount |
| 104 | 104 |
} |
| 105 | 105 |
p.PluginObj.Settings.Env = make([]string, 0, len(p.PluginObj.Config.Env)) |
| 106 |
+ p.PluginObj.Settings.Devices = make([]types.PluginDevice, 0, len(p.PluginObj.Config.Linux.Devices)) |
|
| 107 |
+ copy(p.PluginObj.Settings.Devices, p.PluginObj.Config.Linux.Devices) |
|
| 106 | 108 |
for _, env := range p.PluginObj.Config.Env {
|
| 107 | 109 |
if env.Value != nil {
|
| 108 | 110 |
p.PluginObj.Settings.Env = append(p.PluginObj.Settings.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
|
| ... | ... |
@@ -175,7 +178,7 @@ next: |
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
// range over all the devices in the config |
| 178 |
- for _, device := range p.PluginObj.Config.Devices {
|
|
| 178 |
+ for _, device := range p.PluginObj.Config.Linux.Devices {
|
|
| 179 | 179 |
// found the device in the config |
| 180 | 180 |
if device.Name == s.name {
|
| 181 | 181 |
// is it settable ? |
| ... | ... |
@@ -233,7 +236,7 @@ func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
|
| 233 | 233 |
}) |
| 234 | 234 |
} |
| 235 | 235 |
} |
| 236 |
- for _, device := range m.Devices {
|
|
| 236 |
+ for _, device := range m.Linux.Devices {
|
|
| 237 | 237 |
if device.Path != nil {
|
| 238 | 238 |
privileges = append(privileges, types.PluginPrivilege{
|
| 239 | 239 |
Name: "device", |
| ... | ... |
@@ -242,7 +245,7 @@ func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
|
| 242 | 242 |
}) |
| 243 | 243 |
} |
| 244 | 244 |
} |
| 245 |
- if m.DeviceCreation {
|
|
| 245 |
+ if m.Linux.DeviceCreation {
|
|
| 246 | 246 |
privileges = append(privileges, types.PluginPrivilege{
|
| 247 | 247 |
Name: "device-creation", |
| 248 | 248 |
Description: "", |
| ... | ... |
@@ -299,12 +302,12 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
|
| 299 | 299 |
Readonly: false, // TODO: all plugins should be readonly? settable in config? |
| 300 | 300 |
} |
| 301 | 301 |
|
| 302 |
- if p.PluginObj.Config.DeviceCreation {
|
|
| 303 |
- rwm := "rwm" |
|
| 304 |
- s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}}
|
|
| 302 |
+ userMounts := make(map[string]struct{}, len(p.PluginObj.Config.Mounts))
|
|
| 303 |
+ for _, m := range p.PluginObj.Config.Mounts {
|
|
| 304 |
+ userMounts[m.Destination] = struct{}{}
|
|
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 |
- mounts := append(p.PluginObj.Settings.Mounts, types.PluginMount{
|
|
| 307 |
+ mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
|
| 308 | 308 |
Source: &p.RuntimeSourcePath, |
| 309 | 309 |
Destination: defaultPluginRuntimeDestination, |
| 310 | 310 |
Type: "bind", |
| ... | ... |
@@ -363,9 +366,25 @@ func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
|
| 363 | 363 |
} |
| 364 | 364 |
|
| 365 | 365 |
for i, m := range s.Mounts {
|
| 366 |
- if strings.HasPrefix(m.Destination, "/dev/") && true { // TODO: && user specified /dev
|
|
| 367 |
- s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...) |
|
| 366 |
+ if strings.HasPrefix(m.Destination, "/dev/") {
|
|
| 367 |
+ if _, ok := userMounts[m.Destination]; ok {
|
|
| 368 |
+ s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...) |
|
| 369 |
+ } |
|
| 370 |
+ } |
|
| 371 |
+ } |
|
| 372 |
+ |
|
| 373 |
+ if p.PluginObj.Config.Linux.DeviceCreation {
|
|
| 374 |
+ rwm := "rwm" |
|
| 375 |
+ s.Linux.Resources.Devices = []specs.DeviceCgroup{{Allow: true, Access: &rwm}}
|
|
| 376 |
+ } |
|
| 377 |
+ for _, dev := range p.PluginObj.Config.Linux.Devices {
|
|
| 378 |
+ path := *dev.Path |
|
| 379 |
+ d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm") |
|
| 380 |
+ if err != nil {
|
|
| 381 |
+ return nil, err |
|
| 368 | 382 |
} |
| 383 |
+ s.Linux.Devices = append(s.Linux.Devices, d...) |
|
| 384 |
+ s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...) |
|
| 369 | 385 |
} |
| 370 | 386 |
|
| 371 | 387 |
envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1) |