Skip to content

Commit 87e1464

Browse files
author
Renaud Gaubert
committed
Added support for Generic Resources
Signed-off-by: Renaud Gaubert <[email protected]>
1 parent f7ce35f commit 87e1464

File tree

13 files changed

+196
-33
lines changed

13 files changed

+196
-33
lines changed

api/swagger.yaml

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,41 @@ definitions:
487487
type: "integer"
488488
format: "int64"
489489

490+
ResourceObject:
491+
description: "An object describing the resources which can be advertised by a node and requested by a task"
492+
type: "object"
493+
properties:
494+
NanoCPUs:
495+
type: "integer"
496+
format: "int64"
497+
MemoryBytes:
498+
type: "integer"
499+
format: "int64"
500+
GenericResources:
501+
$ref: "#/definitions/GenericResources"
502+
503+
GenericResources:
504+
description: "User defined Resources, can be either Integer resources (e.g: SSD=3) or String resources (e.g: GPU={UUID1, UUID2})"
505+
type: "array"
506+
items:
507+
type: "object"
508+
properties:
509+
NamedResourceSpec:
510+
type: "object"
511+
properties:
512+
Kind:
513+
type: "string"
514+
Value:
515+
type: "string"
516+
DiscreteResourceSpec:
517+
type: "object"
518+
properties:
519+
Kind:
520+
type: "string"
521+
Value:
522+
type: "integer"
523+
format: "int64"
524+
490525
HealthConfig:
491526
description: "A test to perform to check that the container is healthy."
492527
type: "object"
@@ -1702,14 +1737,7 @@ definitions:
17021737
OS:
17031738
type: "string"
17041739
Resources:
1705-
type: "object"
1706-
properties:
1707-
NanoCPUs:
1708-
type: "integer"
1709-
format: "int64"
1710-
MemoryBytes:
1711-
type: "integer"
1712-
format: "int64"
1740+
$ref: "#/definitions/ResourceObject"
17131741
Engine:
17141742
type: "object"
17151743
properties:
@@ -1750,6 +1778,16 @@ definitions:
17501778
Resources:
17511779
NanoCPUs: 4000000000
17521780
MemoryBytes: 8272408576
1781+
GenericResources:
1782+
- DiscreteResourceSpec:
1783+
Kind: "SSD"
1784+
Value: 3
1785+
- NamedResourceSpec:
1786+
Kind: "GPU"
1787+
Value: "UUID1"
1788+
- NamedResourceSpec:
1789+
Kind: "GPU"
1790+
Value: "UUID2"
17531791
Engine:
17541792
EngineVersion: "17.04.0"
17551793
Labels:
@@ -2132,27 +2170,10 @@ definitions:
21322170
properties:
21332171
Limits:
21342172
description: "Define resources limits."
2135-
type: "object"
2136-
properties:
2137-
NanoCPUs:
2138-
description: "CPU limit in units of 10<sup>-9</sup> CPU shares."
2139-
type: "integer"
2140-
format: "int64"
2141-
MemoryBytes:
2142-
description: "Memory limit in Bytes."
2143-
type: "integer"
2144-
format: "int64"
2173+
$ref: "#/definitions/ResourceObject"
21452174
Reservation:
21462175
description: "Define resources reservation."
2147-
properties:
2148-
NanoCPUs:
2149-
description: "CPU reservation in units of 10<sup>-9</sup> CPU shares."
2150-
type: "integer"
2151-
format: "int64"
2152-
MemoryBytes:
2153-
description: "Memory reservation in Bytes."
2154-
type: "integer"
2155-
format: "int64"
2176+
$ref: "#/definitions/ResourceObject"
21562177
RestartPolicy:
21572178
description: "Specification for the restart policy which applies to containers created as part of this service."
21582179
type: "object"
@@ -2283,6 +2304,8 @@ definitions:
22832304
NodeID:
22842305
description: "The ID of the node that this task is on."
22852306
type: "string"
2307+
AssignedGenericResources:
2308+
$ref: "#/definitions/GenericResources"
22862309
Status:
22872310
type: "object"
22882311
properties:
@@ -2362,6 +2385,16 @@ definitions:
23622385
Gateway: "10.255.0.1"
23632386
Addresses:
23642387
- "10.255.0.10/16"
2388+
AssignedGenericResources:
2389+
- DiscreteResourceSpec:
2390+
Kind: "SSD"
2391+
Value: 3
2392+
- NamedResourceSpec:
2393+
Kind: "GPU"
2394+
Value: "UUID1"
2395+
- NamedResourceSpec:
2396+
Kind: "GPU"
2397+
Value: "UUID2"
23652398
ServiceSpec:
23662399
description: "User modifiable configuration for a service."
23672400
properties:
@@ -5392,6 +5425,8 @@ paths:
53925425
type: "string"
53935426
MemTotal:
53945427
type: "integer"
5428+
GenericResources:
5429+
$ref: "#/definitions/GenericResources"
53955430
MemoryLimit:
53965431
type: "boolean"
53975432
NCPU:

api/types/swarm/task.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type Task struct {
5151
Status TaskStatus `json:",omitempty"`
5252
DesiredState TaskState `json:",omitempty"`
5353
NetworksAttachments []NetworkAttachment `json:",omitempty"`
54+
GenericResources []GenericResource `json:",omitempty"`
5455
}
5556

5657
// TaskSpec represents the spec of a task.
@@ -79,8 +80,34 @@ type TaskSpec struct {
7980

8081
// Resources represents resources (CPU/Memory).
8182
type Resources struct {
82-
NanoCPUs int64 `json:",omitempty"`
83-
MemoryBytes int64 `json:",omitempty"`
83+
NanoCPUs int64 `json:",omitempty"`
84+
MemoryBytes int64 `json:",omitempty"`
85+
GenericResources []GenericResource `json:",omitempty"`
86+
}
87+
88+
// GenericResource represents a "user defined" resource which can
89+
// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1)
90+
type GenericResource struct {
91+
NamedResourceSpec *NamedGenericResource `json:",omitempty"`
92+
DiscreteResourceSpec *DiscreteGenericResource `json:",omitempty"`
93+
}
94+
95+
// NamedGenericResource represents a "user defined" resource which is defined
96+
// as a string.
97+
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
98+
// Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...)
99+
type NamedGenericResource struct {
100+
Kind string `json:",omitempty"`
101+
Value string `json:",omitempty"`
102+
}
103+
104+
// DiscreteGenericResource represents a "user defined" resource which is defined
105+
// as an integer
106+
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
107+
// Value is used to count the resource (SSD=5, HDD=3, ...)
108+
type DiscreteGenericResource struct {
109+
Kind string `json:",omitempty"`
110+
Value int64 `json:",omitempty"`
84111
}
85112

86113
// ResourceRequirements represents resources requirements.

api/types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ type Info struct {
168168
RegistryConfig *registry.ServiceConfig
169169
NCPU int
170170
MemTotal int64
171+
GenericResources []swarm.GenericResource
171172
DockerRootDir string
172173
HTTPProxy string `json:"HttpProxy"`
173174
HTTPSProxy string `json:"HttpsProxy"`

cmd/dockerd/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
6262

6363
flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
6464

65+
flags.StringVar(&conf.NodeGenericResources, "node-generic-resources", "", "user defined resources (e.g. fpga=2;gpu={UUID1,UUID2,UUID3})")
66+
6567
// "--deprecated-key-path" is to allow configuration of the key used
6668
// for the daemon ID and the deprecated image signing. It was never
6769
// exposed as a command line option but is added here to allow

daemon/cluster/convert/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func NodeFromGRPC(n swarmapi.Node) types.Node {
4242
if n.Description.Resources != nil {
4343
node.Description.Resources.NanoCPUs = n.Description.Resources.NanoCPUs
4444
node.Description.Resources.MemoryBytes = n.Description.Resources.MemoryBytes
45+
node.Description.Resources.GenericResources = GenericResourcesFromGRPC(n.Description.Resources.Generic)
4546
}
4647
if n.Description.Engine != nil {
4748
node.Description.Engine.EngineVersion = n.Description.Engine.EngineVersion

daemon/cluster/convert/service.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/docker/docker/api/types/swarm/runtime"
99
"github.com/docker/docker/pkg/namesgenerator"
1010
swarmapi "github.com/docker/swarmkit/api"
11+
"github.com/docker/swarmkit/api/genericresource"
1112
"github.com/gogo/protobuf/proto"
1213
gogotypes "github.com/gogo/protobuf/types"
1314
"github.com/pkg/errors"
@@ -301,6 +302,31 @@ func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations {
301302
return a
302303
}
303304

305+
// GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource
306+
func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource {
307+
var generic []types.GenericResource
308+
for _, res := range genericRes {
309+
var current types.GenericResource
310+
311+
switch r := res.Resource.(type) {
312+
case *swarmapi.GenericResource_DiscreteResourceSpec:
313+
current.DiscreteResourceSpec = &types.DiscreteGenericResource{
314+
Kind: r.DiscreteResourceSpec.Kind,
315+
Value: r.DiscreteResourceSpec.Value,
316+
}
317+
case *swarmapi.GenericResource_NamedResourceSpec:
318+
current.NamedResourceSpec = &types.NamedGenericResource{
319+
Kind: r.NamedResourceSpec.Kind,
320+
Value: r.NamedResourceSpec.Value,
321+
}
322+
}
323+
324+
generic = append(generic, current)
325+
}
326+
327+
return generic
328+
}
329+
304330
func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequirements {
305331
var resources *types.ResourceRequirements
306332
if res != nil {
@@ -313,15 +339,34 @@ func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequir
313339
}
314340
if res.Reservations != nil {
315341
resources.Reservations = &types.Resources{
316-
NanoCPUs: res.Reservations.NanoCPUs,
317-
MemoryBytes: res.Reservations.MemoryBytes,
342+
NanoCPUs: res.Reservations.NanoCPUs,
343+
MemoryBytes: res.Reservations.MemoryBytes,
344+
GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic),
318345
}
319346
}
320347
}
321348

322349
return resources
323350
}
324351

352+
// GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource
353+
func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource {
354+
var generic []*swarmapi.GenericResource
355+
for _, res := range genericRes {
356+
var r *swarmapi.GenericResource
357+
358+
if res.DiscreteResourceSpec != nil {
359+
r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value)
360+
} else if res.NamedResourceSpec != nil {
361+
r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value)
362+
}
363+
364+
generic = append(generic, r)
365+
}
366+
367+
return generic
368+
}
369+
325370
func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements {
326371
var reqs *swarmapi.ResourceRequirements
327372
if res != nil {
@@ -336,6 +381,7 @@ func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirem
336381
reqs.Reservations = &swarmapi.Resources{
337382
NanoCPUs: res.Reservations.NanoCPUs,
338383
MemoryBytes: res.Reservations.MemoryBytes,
384+
Generic: GenericResourcesToGRPC(res.Reservations.GenericResources),
339385
}
340386

341387
}

daemon/cluster/convert/task.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ func TaskFromGRPC(t swarmapi.Task) (types.Task, error) {
3030
Message: t.Status.Message,
3131
Err: t.Status.Err,
3232
},
33-
DesiredState: types.TaskState(strings.ToLower(t.DesiredState.String())),
33+
DesiredState: types.TaskState(strings.ToLower(t.DesiredState.String())),
34+
GenericResources: GenericResourcesFromGRPC(t.AssignedGenericResources),
3435
}
3536

3637
// Meta

daemon/cluster/executor/container/container.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
netconst "github.com/docker/libnetwork/datastore"
2626
"github.com/docker/swarmkit/agent/exec"
2727
"github.com/docker/swarmkit/api"
28+
"github.com/docker/swarmkit/api/genericresource"
2829
"github.com/docker/swarmkit/template"
2930
gogotypes "github.com/gogo/protobuf/types"
3031
)
@@ -186,13 +187,16 @@ func (c *containerConfig) exposedPorts() map[nat.Port]struct{} {
186187
}
187188

188189
func (c *containerConfig) config() *enginecontainer.Config {
190+
genericEnvs := genericresource.EnvFormat(c.task.AssignedGenericResources, "DOCKER_RESOURCE")
191+
env := append(c.spec().Env, genericEnvs...)
192+
189193
config := &enginecontainer.Config{
190194
Labels: c.labels(),
191195
StopSignal: c.spec().StopSignal,
192196
Tty: c.spec().TTY,
193197
OpenStdin: c.spec().OpenStdin,
194198
User: c.spec().User,
195-
Env: c.spec().Env,
199+
Env: env,
196200
Hostname: c.spec().Hostname,
197201
WorkingDir: c.spec().Dir,
198202
Image: c.image(),

daemon/cluster/executor/container/executor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/docker/docker/api/types/network"
1212
swarmtypes "github.com/docker/docker/api/types/swarm"
1313
"github.com/docker/docker/daemon/cluster/controllers/plugin"
14+
"github.com/docker/docker/daemon/cluster/convert"
1415
executorpkg "github.com/docker/docker/daemon/cluster/executor"
1516
clustertypes "github.com/docker/docker/daemon/cluster/provider"
1617
networktypes "github.com/docker/libnetwork/types"
@@ -119,6 +120,7 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
119120
Resources: &api.Resources{
120121
NanoCPUs: int64(info.NCPU) * 1e9,
121122
MemoryBytes: info.MemTotal,
123+
Generic: convert.GenericResourcesToGRPC(info.GenericResources),
122124
},
123125
}
124126

daemon/config/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ type CommonConfig struct {
168168
ValuesSet map[string]interface{}
169169

170170
Experimental bool `json:"experimental"` // Experimental indicates whether experimental features should be exposed or not
171+
172+
// Exposed node Generic Resources
173+
NodeGenericResources string `json:"node-generic-resources,omitempty"`
171174
}
172175

173176
// IsValueSet returns true if a configuration value
@@ -497,6 +500,10 @@ func Validate(config *Config) error {
497500
}
498501
}
499502

503+
if _, err := opts.ParseGenericResources(config.NodeGenericResources); err != nil {
504+
return err
505+
}
506+
500507
if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != StockRuntimeName {
501508
runtimes := config.GetAllRuntimes()
502509
if _, ok := runtimes[defaultRuntime]; !ok {

0 commit comments

Comments
 (0)