Plugin miscellaneous fixes
| ... | ... |
@@ -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) |