Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for devices #1

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions agent/exec/dockerapi/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,15 @@ func (c *containerConfig) resources() enginecontainer.Resources {
}
}

resources.Devices = make([]enginecontainer.DeviceMapping, len(c.spec().Devices))
for i, device := range c.spec().Devices {
resources.Devices[i] = enginecontainer.DeviceMapping{
PathOnHost: device.PathOnHost,
PathInContainer: device.PathInContainer,
CgroupPermissions: device.CgroupPermissions,
}
}

// If no limits are specified let the engine use its defaults.
//
// TODO(aluzzardi): We might want to set some limits anyway otherwise
Expand Down
32 changes: 32 additions & 0 deletions agent/exec/dockerapi/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,35 @@ func TestUlimits(t *testing.T) {
t.Fatalf("expected %v, got %v", expected, actual)
}
}

func TestDevices(t *testing.T) {
c := containerConfig{
task: &api.Task{
Spec: api.TaskSpec{
Runtime: &api.TaskSpec_Container{
Container: &api.ContainerSpec{
Devices: []*api.ContainerSpec_DeviceMapping{
{
PathOnHost: "/dev/dri/card0",
PathInContainer: "/dev/card0",
CgroupPermissions: "ro",
},
},
},
},
},
},
}

expected := []enginecontainer.DeviceMapping{
{
PathOnHost: "/dev/dri/card0",
PathInContainer: "/dev/card0",
CgroupPermissions: "ro",
},
}
actual := c.resources().Devices
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %v, got %v", expected, actual)
}
}
52 changes: 42 additions & 10 deletions api/api.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2735,8 +2735,8 @@ file {
label: LABEL_OPTIONAL
type: TYPE_UINT32
options {
65001: 0
65003: "os.FileMode"
65001: 0
}
json_name: "mode"
}
Expand Down Expand Up @@ -2904,8 +2904,8 @@ file {
type: TYPE_MESSAGE
type_name: ".google.protobuf.Duration"
options {
65001: 0
65011: 1
65001: 0
}
json_name: "delay"
}
Expand Down Expand Up @@ -3348,8 +3348,8 @@ file {
}
}
options {
62001: 0
62023: "PublishMode"
62001: 0
}
}
}
Expand Down Expand Up @@ -4055,8 +4055,8 @@ file {
label: LABEL_OPTIONAL
type: TYPE_UINT32
options {
65001: 0
65003: "os.FileMode"
65001: 0
}
json_name: "mode"
}
Expand Down Expand Up @@ -5052,8 +5052,8 @@ file {
}
}
options {
62001: 0
62023: "NodeRole"
62001: 0
}
}
syntax: "proto3"
Expand Down Expand Up @@ -5677,6 +5677,14 @@ file {
type_name: ".docker.swarmkit.v1.ContainerSpec.Ulimit"
json_name: "ulimits"
}
field {
name: "Devices"
number: 30
label: LABEL_REPEATED
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.ContainerSpec.DeviceMapping"
json_name: "Devices"
}
nested_type {
name: "LabelsEntry"
field {
Expand Down Expand Up @@ -5775,6 +5783,30 @@ file {
json_name: "hard"
}
}
nested_type {
name: "DeviceMapping"
field {
name: "pathOnHost"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "pathOnHost"
}
field {
name: "pathInContainer"
number: 2
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "pathInContainer"
}
field {
name: "cgroupPermissions"
number: 3
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "cgroupPermissions"
}
}
enum_type {
name: "Isolation"
value {
Expand Down Expand Up @@ -9739,8 +9771,8 @@ file {
type: TYPE_MESSAGE
type_name: ".google.protobuf.Duration"
options {
65001: 0
65011: 1
65001: 0
}
json_name: "period"
}
Expand Down Expand Up @@ -10958,14 +10990,14 @@ file {
}
}
options {
63001: 0
63002: 0
63017: 1
63018: 1
63020: 1
63018: 1
63001: 0
63002: 0
63035: 0
63026: 0
63034: 0
63035: 0
}
}
file {
Expand Down
675 changes: 514 additions & 161 deletions api/specs.pb.go

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions api/specs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,14 @@ message ContainerSpec {
// Ulimits defines the list of ulimits to set in the container. This option
// is equivalent to passing --ulimit to docker run.
repeated Ulimit ulimits = 29;

message DeviceMapping {
string pathOnHost = 1;
string pathInContainer = 2;
string cgroupPermissions = 3;
}

repeated DeviceMapping Devices = 30;
}

// EndpointSpec defines the properties that can be configured to
Expand Down
49 changes: 49 additions & 0 deletions cmd/swarmctl/service/flagparser/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package flagparser

import (
"strings"

"github.com/moby/swarmkit/v2/api"
"github.com/pkg/errors"
"github.com/spf13/pflag"
)

func parseDevice(flags *pflag.FlagSet, spec *api.ServiceSpec) error {
if flags.Changed("device") {
container := spec.Task.GetContainer()
if container == nil {
return nil
}

devices, err := flags.GetStringSlice("device")
if err != nil {
return err
}

container.Devices = make([]*api.ContainerSpec_DeviceMapping, len(devices))

for i, device := range devices {
parts := strings.Split(device, ":")
if len(parts) < 1 || len(parts) > 3 {
return errors.Wrap(err, "failed to parse device")
}

mapping := &api.ContainerSpec_DeviceMapping{
PathOnHost: parts[0],
PathInContainer: parts[0],
}

if len(parts) > 1 {
mapping.PathInContainer = parts[1]
}

if len(parts) == 3 {
mapping.CgroupPermissions = parts[2]
}

container.Devices[i] = mapping
}
}

return nil
}
5 changes: 5 additions & 0 deletions cmd/swarmctl/service/flagparser/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func AddServiceFlags(flags *pflag.FlagSet) {
flags.StringSlice("volume", nil, "define a volume mount")
flags.StringSlice("tmpfs", nil, "define a tmpfs mount")
flags.StringSlice("npipe", nil, "define a npipe mount")
flags.StringSlice("device", nil, "device options")

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

if err := parseDevice(flags, spec); err != nil {
return err
}

driver, err := common.ParseLogDriverFlags(flags)
if err != nil {
return err
Expand Down
Loading