Skip to content
This repository was archived by the owner on Jun 16, 2021. It is now read-only.

Commit 7b7ca41

Browse files
committed
Strict yaml validation
Signed-off-by: Josh Curl <hello@joshcurl.com>
1 parent 613847e commit 7b7ca41

14 files changed

+1273
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
base:
2+
image: busybox
3+
ports: invalid_type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
base:
2+
image: busybox

integration/create_test.go

+88-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"os/exec"
7+
"strings"
78

89
. "gopkg.in/check.v1"
910
"path/filepath"
@@ -149,7 +150,6 @@ func (s *RunSuite) TestFieldTypeConversions(c *C) {
149150
image: tianon/true
150151
mem_limit: $LIMIT
151152
memswap_limit: "40000000"
152-
hostname: 100
153153
`)
154154

155155
name := fmt.Sprintf("%s_%s_1", p, "test")
@@ -160,7 +160,6 @@ func (s *RunSuite) TestFieldTypeConversions(c *C) {
160160
image: tianon/true
161161
mem_limit: 40000000
162162
memswap_limit: 40000000
163-
hostname: "100"
164163
`)
165164

166165
name = fmt.Sprintf("%s_%s_1", p, "reference")
@@ -257,5 +256,92 @@ func (s *RunSuite) TestDefaultMultipleComposeFiles(c *C) {
257256

258257
c.Assert(container, NotNil)
259258
}
259+
}
260+
261+
func (s *RunSuite) TestValidation(c *C) {
262+
template := `
263+
test:
264+
image: busybox
265+
ports: invalid_type
266+
`
267+
_, output := s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
268+
269+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'ports' contains an invalid type, it should be an array."), Equals, true)
270+
271+
template = `
272+
test:
273+
image: busybox
274+
build: .
275+
`
276+
_, output = s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
277+
278+
c.Assert(strings.Contains(output, "Service 'test' has both an image and build path specified. A service can either be built to image or use an existing image, not both."), Equals, true)
279+
280+
template = `
281+
test:
282+
image: busybox
283+
ports: invalid_type
284+
links: invalid_type
285+
devices:
286+
- /dev/foo:/dev/foo
287+
- /dev/foo:/dev/foo
288+
`
289+
_, output = s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
290+
291+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'ports' contains an invalid type, it should be an array."), Equals, true)
292+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'links' contains an invalid type, it should be an array"), Equals, true)
293+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'devices' value [/dev/foo:/dev/foo /dev/foo:/dev/foo] has non-unique elements"), Equals, true)
294+
}
295+
296+
func (s *RunSuite) TestValidationWithExtends(c *C) {
297+
template := `
298+
base:
299+
image: busybox
300+
privilege: "something"
301+
test:
302+
extends:
303+
service: base
304+
`
305+
306+
_, output := s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
307+
308+
c.Assert(strings.Contains(output, "Unsupported config option for base service: 'privilege' (did you mean 'privileged'?)"), Equals, true)
309+
310+
template = `
311+
base:
312+
image: busybox
313+
test:
314+
extends:
315+
service: base
316+
links: invalid_type
317+
`
318+
319+
_, output = s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
320+
321+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'links' contains an invalid type, it should be an array"), Equals, true)
322+
323+
template = `
324+
test:
325+
extends:
326+
file: ./assets/validation/valid/docker-compose.yml
327+
service: base
328+
devices:
329+
- /dev/foo:/dev/foo
330+
- /dev/foo:/dev/foo
331+
`
332+
333+
_, output = s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
334+
335+
c.Assert(strings.Contains(output, "Service 'test' configuration key 'devices' value [/dev/foo:/dev/foo /dev/foo:/dev/foo] has non-unique elements"), Equals, true)
336+
337+
template = `
338+
test:
339+
extends:
340+
file: ./assets/validation/invalid/docker-compose.yml
341+
service: base
342+
`
343+
344+
_, output = s.FromTextCaptureOutput(c, s.RandomProject(), "create", template)
260345

346+
c.Assert(strings.Contains(output, "Service 'base' configuration key 'ports' contains an invalid type, it should be an array."), Equals, true)
261347
}

project/merge.go

+15
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ func mergeProject(p *Project, file string, bytes []byte) (map[string]*ServiceCon
4343
return nil, err
4444
}
4545

46+
if err := validate(datas); err != nil {
47+
return nil, err
48+
}
49+
4650
for name, data := range datas {
4751
data, err := parse(p.context.ResourceLookup, p.context.EnvironmentLookup, file, data, datas)
4852
if err != nil {
@@ -62,6 +66,13 @@ func mergeProject(p *Project, file string, bytes []byte) (map[string]*ServiceCon
6266
datas[name] = data
6367
}
6468

69+
for name, data := range datas {
70+
err := validateServiceConstraints(data, name)
71+
if err != nil {
72+
return nil, err
73+
}
74+
}
75+
6576
if err := utils.Convert(datas, &configs); err != nil {
6677
return nil, err
6778
}
@@ -221,6 +232,10 @@ func parse(resourceLookup ResourceLookup, environmentLookup EnvironmentLookup, i
221232
return nil, err
222233
}
223234

235+
if err := validate(baseRawServices); err != nil {
236+
return nil, err
237+
}
238+
224239
baseService, ok = baseRawServices[service]
225240
if !ok {
226241
return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)

project/merge_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func TestRestartNo(t *testing.T) {
158158

159159
config, err := mergeProject(p, "", []byte(`
160160
test:
161-
restart: no
161+
restart: "no"
162162
image: foo
163163
`))
164164

project/project_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,13 @@ func TestParseWithMultipleComposeFiles(t *testing.T) {
163163
configTwo := []byte(`
164164
multiple:
165165
image: busybox
166-
name: multi
166+
container_name: multi
167167
ports:
168168
- 9000`)
169169

170170
configThree := []byte(`
171171
multiple:
172+
image: busybox
172173
mem_limit: 40000000
173174
ports:
174175
- 10000`)
@@ -182,7 +183,7 @@ func TestParseWithMultipleComposeFiles(t *testing.T) {
182183
assert.Nil(t, err)
183184

184185
assert.Equal(t, "busybox", p.Configs["multiple"].Image)
185-
assert.Equal(t, "multi", p.Configs["multiple"].Name)
186+
assert.Equal(t, "multi", p.Configs["multiple"].ContainerName)
186187
assert.Equal(t, []string{"8000", "9000"}, p.Configs["multiple"].Ports)
187188

188189
p = NewProject(&Context{
@@ -194,7 +195,7 @@ func TestParseWithMultipleComposeFiles(t *testing.T) {
194195
assert.Nil(t, err)
195196

196197
assert.Equal(t, "tianon/true", p.Configs["multiple"].Image)
197-
assert.Equal(t, "multi", p.Configs["multiple"].Name)
198+
assert.Equal(t, "multi", p.Configs["multiple"].ContainerName)
198199
assert.Equal(t, []string{"9000", "8000"}, p.Configs["multiple"].Ports)
199200

200201
p = NewProject(&Context{
@@ -206,7 +207,7 @@ func TestParseWithMultipleComposeFiles(t *testing.T) {
206207
assert.Nil(t, err)
207208

208209
assert.Equal(t, "busybox", p.Configs["multiple"].Image)
209-
assert.Equal(t, "multi", p.Configs["multiple"].Name)
210+
assert.Equal(t, "multi", p.Configs["multiple"].ContainerName)
210211
assert.Equal(t, []string{"8000", "9000", "10000"}, p.Configs["multiple"].Ports)
211212
assert.Equal(t, int64(40000000), p.Configs["multiple"].MemLimit)
212213
}

0 commit comments

Comments
 (0)