Skip to content

Commit 983a7bf

Browse files
committed
Add support for devices
Signed-off-by: zikaeroh <[email protected]>
1 parent 542f45a commit 983a7bf

File tree

7 files changed

+649
-161
lines changed

7 files changed

+649
-161
lines changed

agent/exec/dockerapi/container.go

+9
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,15 @@ func (c *containerConfig) resources() enginecontainer.Resources {
459459
}
460460
}
461461

462+
resources.Devices = make([]enginecontainer.DeviceMapping, len(c.spec().Devices))
463+
for i, device := range c.spec().Devices {
464+
resources.Devices[i] = enginecontainer.DeviceMapping{
465+
PathOnHost: device.PathOnHost,
466+
PathInContainer: device.PathInContainer,
467+
CgroupPermissions: device.CgroupPermissions,
468+
}
469+
}
470+
462471
// If no limits are specified let the engine use its defaults.
463472
//
464473
// TODO(aluzzardi): We might want to set some limits anyway otherwise

agent/exec/dockerapi/container_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,35 @@ func TestUlimits(t *testing.T) {
330330
t.Fatalf("expected %v, got %v", expected, actual)
331331
}
332332
}
333+
334+
func TestDevices(t *testing.T) {
335+
c := containerConfig{
336+
task: &api.Task{
337+
Spec: api.TaskSpec{
338+
Runtime: &api.TaskSpec_Container{
339+
Container: &api.ContainerSpec{
340+
Devices: []*api.ContainerSpec_DeviceMapping{
341+
{
342+
PathOnHost: "/dev/dri/card0",
343+
PathInContainer: "/dev/card0",
344+
CgroupPermissions: "ro",
345+
},
346+
},
347+
},
348+
},
349+
},
350+
},
351+
}
352+
353+
expected := []enginecontainer.DeviceMapping{
354+
{
355+
PathOnHost: "/dev/dri/card0",
356+
PathInContainer: "/dev/card0",
357+
CgroupPermissions: "ro",
358+
},
359+
}
360+
actual := c.resources().Devices
361+
if !reflect.DeepEqual(actual, expected) {
362+
t.Fatalf("expected %v, got %v", expected, actual)
363+
}
364+
}

api/api.pb.txt

+32
Original file line numberDiff line numberDiff line change
@@ -5677,6 +5677,14 @@ file {
56775677
type_name: ".docker.swarmkit.v1.ContainerSpec.Ulimit"
56785678
json_name: "ulimits"
56795679
}
5680+
field {
5681+
name: "Devices"
5682+
number: 30
5683+
label: LABEL_REPEATED
5684+
type: TYPE_MESSAGE
5685+
type_name: ".docker.swarmkit.v1.ContainerSpec.DeviceMapping"
5686+
json_name: "Devices"
5687+
}
56805688
nested_type {
56815689
name: "LabelsEntry"
56825690
field {
@@ -5775,6 +5783,30 @@ file {
57755783
json_name: "hard"
57765784
}
57775785
}
5786+
nested_type {
5787+
name: "DeviceMapping"
5788+
field {
5789+
name: "pathOnHost"
5790+
number: 1
5791+
label: LABEL_OPTIONAL
5792+
type: TYPE_STRING
5793+
json_name: "pathOnHost"
5794+
}
5795+
field {
5796+
name: "pathInContainer"
5797+
number: 2
5798+
label: LABEL_OPTIONAL
5799+
type: TYPE_STRING
5800+
json_name: "pathInContainer"
5801+
}
5802+
field {
5803+
name: "cgroupPermissions"
5804+
number: 3
5805+
label: LABEL_OPTIONAL
5806+
type: TYPE_STRING
5807+
json_name: "cgroupPermissions"
5808+
}
5809+
}
57785810
enum_type {
57795811
name: "Isolation"
57805812
value {

api/specs.pb.go

+514-161
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/specs.proto

+8
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,14 @@ message ContainerSpec {
370370
// Ulimits defines the list of ulimits to set in the container. This option
371371
// is equivalent to passing --ulimit to docker run.
372372
repeated Ulimit ulimits = 29;
373+
374+
message DeviceMapping {
375+
string pathOnHost = 1;
376+
string pathInContainer = 2;
377+
string cgroupPermissions = 3;
378+
}
379+
380+
repeated DeviceMapping Devices = 30;
373381
}
374382

375383
// EndpointSpec defines the properties that can be configured to
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package flagparser
2+
3+
import (
4+
"strings"
5+
6+
"github.com/moby/swarmkit/v2/api"
7+
"github.com/pkg/errors"
8+
"github.com/spf13/pflag"
9+
)
10+
11+
func parseDevice(flags *pflag.FlagSet, spec *api.ServiceSpec) error {
12+
if flags.Changed("device") {
13+
container := spec.Task.GetContainer()
14+
if container == nil {
15+
return nil
16+
}
17+
18+
devices, err := flags.GetStringSlice("device")
19+
if err != nil {
20+
return err
21+
}
22+
23+
container.Devices = make([]*api.ContainerSpec_DeviceMapping, len(devices))
24+
25+
for i, device := range devices {
26+
parts := strings.Split(device, ":")
27+
if len(parts) < 1 || len(parts) > 3 {
28+
return errors.Wrap(err, "failed to parse device")
29+
}
30+
31+
mapping := &api.ContainerSpec_DeviceMapping{
32+
PathOnHost: parts[0],
33+
PathInContainer: parts[0],
34+
}
35+
36+
if len(parts) > 1 {
37+
mapping.PathInContainer = parts[1]
38+
}
39+
40+
if len(parts) == 3 {
41+
mapping.CgroupPermissions = parts[2]
42+
}
43+
44+
container.Devices[i] = mapping
45+
}
46+
}
47+
48+
return nil
49+
}

cmd/swarmctl/service/flagparser/flags.go

+5
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func AddServiceFlags(flags *pflag.FlagSet) {
5858
flags.StringSlice("volume", nil, "define a volume mount")
5959
flags.StringSlice("tmpfs", nil, "define a tmpfs mount")
6060
flags.StringSlice("npipe", nil, "define a npipe mount")
61+
flags.StringSlice("device", nil, "device options")
6162

6263
flags.String("log-driver", "", "specify a log driver")
6364
flags.StringSlice("log-opt", nil, "log driver options, as key value pairs")
@@ -150,6 +151,10 @@ func Merge(cmd *cobra.Command, spec *api.ServiceSpec, c api.ControlClient) error
150151
return err
151152
}
152153

154+
if err := parseDevice(flags, spec); err != nil {
155+
return err
156+
}
157+
153158
driver, err := common.ParseLogDriverFlags(flags)
154159
if err != nil {
155160
return err

0 commit comments

Comments
 (0)