Skip to content
Closed
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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these seemingly random changes came from running DOCKER_SWARMKIT_USE_CONTAINER=1 make generate. I'm not sure what the deal is there, so I split this part out to another commit to hopefully drop.

}
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