Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit a823cd6

Browse files
committed
Add support for missing composefile keys
- cpu_quota - cgroup_parent - mac_address - ulimits Signed-off-by: Vincent Demeester <[email protected]>
1 parent 0e83b2f commit a823cd6

File tree

4 files changed

+249
-11
lines changed

4 files changed

+249
-11
lines changed

docker/convert.go

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,34 @@ func Convert(c *project.ServiceConfig, ctx *Context) (*dockerclient.Config, *doc
129129
WorkingDir: c.WorkingDir,
130130
VolumeDriver: c.VolumeDriver,
131131
Volumes: volumes(c, ctx),
132+
MacAddress: c.MacAddress,
132133
}
134+
135+
ulimits := []dockerclient.ULimit{}
136+
if c.Ulimits.Elements != nil {
137+
for _, ulimit := range c.Ulimits.Elements {
138+
ulimits = append(ulimits, dockerclient.ULimit{
139+
Name: ulimit.Name,
140+
Soft: ulimit.Soft,
141+
Hard: ulimit.Hard,
142+
})
143+
}
144+
}
145+
133146
hostConfig := &dockerclient.HostConfig{
134-
VolumesFrom: utils.CopySlice(c.VolumesFrom),
135-
CapAdd: utils.CopySlice(c.CapAdd),
136-
CapDrop: utils.CopySlice(c.CapDrop),
137-
CPUShares: c.CPUShares,
138-
CPUSetCPUs: c.CPUSet,
139-
ExtraHosts: utils.CopySlice(c.ExtraHosts),
140-
Privileged: c.Privileged,
141-
Binds: Filter(c.Volumes, isBind),
142-
Devices: deviceMappings,
143-
DNS: utils.CopySlice(c.DNS.Slice()),
144-
DNSSearch: utils.CopySlice(c.DNSSearch.Slice()),
147+
VolumesFrom: utils.CopySlice(c.VolumesFrom),
148+
CapAdd: utils.CopySlice(c.CapAdd),
149+
CapDrop: utils.CopySlice(c.CapDrop),
150+
CgroupParent: c.CgroupParent,
151+
CPUQuota: c.CPUQuota,
152+
CPUShares: c.CPUShares,
153+
CPUSetCPUs: c.CPUSet,
154+
ExtraHosts: utils.CopySlice(c.ExtraHosts),
155+
Privileged: c.Privileged,
156+
Binds: Filter(c.Volumes, isBind),
157+
Devices: deviceMappings,
158+
DNS: utils.CopySlice(c.DNS.Slice()),
159+
DNSSearch: utils.CopySlice(c.DNSSearch.Slice()),
145160
LogConfig: dockerclient.LogConfig{
146161
Type: c.LogDriver,
147162
Config: utils.CopyMap(c.LogOpt),
@@ -156,6 +171,7 @@ func Convert(c *project.ServiceConfig, ctx *Context) (*dockerclient.Config, *doc
156171
PortBindings: portBindings,
157172
RestartPolicy: *restartPolicy,
158173
SecurityOpt: utils.CopySlice(c.SecurityOpt),
174+
Ulimits: ulimits,
159175
}
160176

161177
return config, hostConfig, nil

project/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ type ServiceConfig struct {
171171
Build string `yaml:"build,omitempty"`
172172
CapAdd []string `yaml:"cap_add,omitempty"`
173173
CapDrop []string `yaml:"cap_drop,omitempty"`
174+
CgroupParent string `yaml:"cgroup_parent,omitempty"`
175+
CPUQuota int64 `yaml:"cpu_quota,omitempty"`
174176
CPUSet string `yaml:"cpuset,omitempty"`
175177
CPUShares int64 `yaml:"cpu_shares,omitempty"`
176178
Command Command `yaml:"command,flow,omitempty"`
@@ -188,6 +190,7 @@ type ServiceConfig struct {
188190
Labels SliceorMap `yaml:"labels,omitempty"`
189191
Links MaporColonSlice `yaml:"links,omitempty"`
190192
LogDriver string `yaml:"log_driver,omitempty"`
193+
MacAddress string `yaml:"mac_address,omitempty"`
191194
MemLimit int64 `yaml:"mem_limit,omitempty"`
192195
MemSwapLimit int64 `yaml:"memswap_limit,omitempty"`
193196
Name string `yaml:"name,omitempty"`
@@ -211,6 +214,7 @@ type ServiceConfig struct {
211214
ExternalLinks []string `yaml:"external_links,omitempty"`
212215
LogOpt map[string]string `yaml:"log_opt,omitempty"`
213216
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
217+
Ulimits Ulimits `yaml:"ulimits,omitemty"`
214218
}
215219

216220
// EnvironmentLookup defines methods to provides environment variable loading.

project/types_yaml.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package project
22

33
import (
44
"fmt"
5+
"reflect"
6+
"sort"
57
"strings"
68

79
"github.com/flynn/go-shlex"
@@ -71,6 +73,93 @@ func NewStringorslice(parts ...string) Stringorslice {
7173
return Stringorslice{parts}
7274
}
7375

76+
// Ulimits represent a list of Ulimit.
77+
// It is, however, represented in yaml as keys (and thus map in Go)
78+
type Ulimits struct {
79+
Elements []Ulimit
80+
}
81+
82+
// MarshalYAML implements the Marshaller interface.
83+
func (u Ulimits) MarshalYAML() (tag string, value interface{}, err error) {
84+
ulimitMap := make(map[string]Ulimit)
85+
for _, ulimit := range u.Elements {
86+
ulimitMap[ulimit.Name] = ulimit
87+
}
88+
return "", ulimitMap, nil
89+
}
90+
91+
// UnmarshalYAML implements the Unmarshaller interface.
92+
func (u *Ulimits) UnmarshalYAML(tag string, value interface{}) error {
93+
ulimits := make(map[string]Ulimit)
94+
yamlUlimits := reflect.ValueOf(value)
95+
switch yamlUlimits.Kind() {
96+
case reflect.Map:
97+
for _, key := range yamlUlimits.MapKeys() {
98+
var name string
99+
var soft, hard int64
100+
mapValue := yamlUlimits.MapIndex(key).Elem()
101+
name = key.Elem().String()
102+
switch mapValue.Kind() {
103+
case reflect.Int64:
104+
soft = mapValue.Int()
105+
hard = mapValue.Int()
106+
case reflect.Map:
107+
if len(mapValue.MapKeys()) != 2 {
108+
return fmt.Errorf("Failed to unmarshal Ulimit: %#v", mapValue)
109+
}
110+
for _, subKey := range mapValue.MapKeys() {
111+
subValue := mapValue.MapIndex(subKey).Elem()
112+
switch subKey.Elem().String() {
113+
case "soft":
114+
soft = subValue.Int()
115+
case "hard":
116+
hard = subValue.Int()
117+
}
118+
}
119+
default:
120+
return fmt.Errorf("Failed to unmarshal Ulimit: %#v, %v", mapValue, mapValue.Kind())
121+
}
122+
ulimits[name] = Ulimit{
123+
Name: name,
124+
ulimitValues: ulimitValues{
125+
Soft: soft,
126+
Hard: hard,
127+
},
128+
}
129+
}
130+
keys := make([]string, 0, len(ulimits))
131+
for key := range ulimits {
132+
keys = append(keys, key)
133+
}
134+
sort.Strings(keys)
135+
for _, key := range keys {
136+
u.Elements = append(u.Elements, ulimits[key])
137+
}
138+
default:
139+
return fmt.Errorf("Failed to unmarshal Ulimit: %#v", value)
140+
}
141+
return nil
142+
}
143+
144+
// Ulimit represent ulimit inforation.
145+
type Ulimit struct {
146+
ulimitValues
147+
Name string
148+
}
149+
150+
type ulimitValues struct {
151+
Soft int64 `yaml:"soft"`
152+
Hard int64 `yaml:"hard"`
153+
}
154+
155+
// MarshalYAML implements the Marshaller interface.
156+
func (u Ulimit) MarshalYAML() (tag string, value interface{}, err error) {
157+
if u.Soft == u.Hard {
158+
return "", u.Soft, nil
159+
}
160+
return "", u.ulimitValues, err
161+
}
162+
74163
// Command represents a docker command, can be a string or an array of strings.
75164
// FIXME why not use Stringorslice (type Command struct { Stringorslice }
76165
type Command struct {

project/types_yaml_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ func newTestConfig() TestConfig {
3737
VolumesFrom: []string{
3838
"system-volumes",
3939
},
40+
Ulimits: Ulimits{
41+
Elements: []Ulimit{
42+
{
43+
Name: "nproc",
44+
ulimitValues: ulimitValues{
45+
Soft: 65557,
46+
Hard: 65557,
47+
},
48+
},
49+
},
50+
},
4051
},
4152
"system-volumes": {
4253
Image: "state",
@@ -233,3 +244,121 @@ func TestUnmarshalEmptyCommand(t *testing.T) {
233244
assert.Nil(t, err)
234245
assert.Nil(t, s2.Command.Slice())
235246
}
247+
248+
func TestMarshalUlimit(t *testing.T) {
249+
ulimits := []struct {
250+
ulimits *Ulimits
251+
expected string
252+
}{
253+
{
254+
ulimits: &Ulimits{
255+
Elements: []Ulimit{
256+
{
257+
ulimitValues: ulimitValues{
258+
Soft: 65535,
259+
Hard: 65535,
260+
},
261+
Name: "nproc",
262+
},
263+
},
264+
},
265+
expected: `nproc: 65535
266+
`,
267+
},
268+
{
269+
ulimits: &Ulimits{
270+
Elements: []Ulimit{
271+
{
272+
Name: "nofile",
273+
ulimitValues: ulimitValues{
274+
Soft: 20000,
275+
Hard: 40000,
276+
},
277+
},
278+
},
279+
},
280+
expected: `nofile:
281+
soft: 20000
282+
hard: 40000
283+
`,
284+
},
285+
}
286+
287+
for _, ulimit := range ulimits {
288+
289+
bytes, err := yaml.Marshal(ulimit.ulimits)
290+
291+
assert.Nil(t, err)
292+
assert.Equal(t, ulimit.expected, string(bytes), "should be equal")
293+
}
294+
}
295+
296+
func TestUnmarshalUlimits(t *testing.T) {
297+
ulimits := []struct {
298+
yaml string
299+
expected *Ulimits
300+
}{
301+
{
302+
yaml: "nproc: 65535",
303+
expected: &Ulimits{
304+
Elements: []Ulimit{
305+
{
306+
Name: "nproc",
307+
ulimitValues: ulimitValues{
308+
Soft: 65535,
309+
Hard: 65535,
310+
},
311+
},
312+
},
313+
},
314+
},
315+
{
316+
yaml: `nofile:
317+
soft: 20000
318+
hard: 40000`,
319+
expected: &Ulimits{
320+
Elements: []Ulimit{
321+
{
322+
Name: "nofile",
323+
ulimitValues: ulimitValues{
324+
Soft: 20000,
325+
Hard: 40000,
326+
},
327+
},
328+
},
329+
},
330+
},
331+
{
332+
yaml: `nproc: 65535
333+
nofile:
334+
soft: 20000
335+
hard: 40000`,
336+
expected: &Ulimits{
337+
Elements: []Ulimit{
338+
{
339+
Name: "nofile",
340+
ulimitValues: ulimitValues{
341+
Soft: 20000,
342+
Hard: 40000,
343+
},
344+
},
345+
{
346+
Name: "nproc",
347+
ulimitValues: ulimitValues{
348+
Soft: 65535,
349+
Hard: 65535,
350+
},
351+
},
352+
},
353+
},
354+
},
355+
}
356+
357+
for _, ulimit := range ulimits {
358+
actual := &Ulimits{}
359+
err := yaml.Unmarshal([]byte(ulimit.yaml), actual)
360+
361+
assert.Nil(t, err)
362+
assert.Equal(t, ulimit.expected, actual, "should be equal")
363+
}
364+
}

0 commit comments

Comments
 (0)