Skip to content

Commit 6a78e92

Browse files
authored
Merge pull request #5524 from Stavrospanakakis/compose-ports-validation
cli/compose: implement the ports validation method
2 parents 305985c + 0319795 commit 6a78e92

2 files changed

Lines changed: 119 additions & 4 deletions

File tree

cli/compose/schema/schema.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ package schema
66
import (
77
"embed"
88
"fmt"
9+
"math/big"
910
"strings"
1011
"time"
1112

13+
"github.com/docker/go-connections/nat"
1214
"github.com/pkg/errors"
1315
"github.com/xeipuuv/gojsonschema"
1416
)
@@ -20,9 +22,18 @@ const (
2022

2123
type portsFormatChecker struct{}
2224

23-
func (checker portsFormatChecker) IsFormat(_ any) bool {
24-
// TODO: implement this
25-
return true
25+
func (checker portsFormatChecker) IsFormat(input any) bool {
26+
var portSpec string
27+
28+
switch p := input.(type) {
29+
case string:
30+
portSpec = p
31+
case *big.Rat:
32+
portSpec = strings.Split(p.String(), "/")[0]
33+
}
34+
35+
_, err := nat.ParsePortSpec(portSpec)
36+
return err == nil
2637
}
2738

2839
type durationFormatChecker struct{}
@@ -37,7 +48,6 @@ func (checker durationFormatChecker) IsFormat(input any) bool {
3748
}
3849

3950
func init() {
40-
gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{})
4151
gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{})
4252
gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{})
4353
}

cli/compose/schema/schema_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,111 @@ func TestValidate(t *testing.T) {
2929
assert.ErrorContains(t, Validate(config, "12345"), "unsupported Compose file version: 12345")
3030
}
3131

32+
func TestValidatePorts(t *testing.T) {
33+
testcases := []struct {
34+
ports any
35+
hasError bool
36+
}{
37+
{
38+
ports: []int{8000},
39+
hasError: false,
40+
},
41+
{
42+
ports: []string{"8000:8000"},
43+
hasError: false,
44+
},
45+
{
46+
ports: []string{"8001-8005"},
47+
hasError: false,
48+
},
49+
{
50+
ports: []string{"8001-8005:8001-8005"},
51+
hasError: false,
52+
},
53+
{
54+
ports: []string{"8000"},
55+
hasError: false,
56+
},
57+
{
58+
ports: []string{"8000-9000:80"},
59+
hasError: false,
60+
},
61+
{
62+
ports: []string{"[::1]:8080:8000"},
63+
hasError: false,
64+
},
65+
{
66+
ports: []string{"[::1]:8080-8085:8000"},
67+
hasError: false,
68+
},
69+
{
70+
ports: []string{"127.0.0.1:8000:8000"},
71+
hasError: false,
72+
},
73+
{
74+
ports: []string{"127.0.0.1:8000-8005:8000-8005"},
75+
hasError: false,
76+
},
77+
{
78+
ports: []string{"127.0.0.1:8000:8000/udp"},
79+
hasError: false,
80+
},
81+
{
82+
ports: []string{"8000:8000/udp"},
83+
hasError: false,
84+
},
85+
{
86+
ports: []string{"8000:8000/http"},
87+
hasError: true,
88+
},
89+
{
90+
ports: []string{"-1"},
91+
hasError: true,
92+
},
93+
{
94+
ports: []string{"65536"},
95+
hasError: true,
96+
},
97+
{
98+
ports: []string{"-1:65536/http"},
99+
hasError: true,
100+
},
101+
{
102+
ports: []string{"invalid"},
103+
hasError: true,
104+
},
105+
{
106+
ports: []string{"12345678:8000:8000/tcp"},
107+
hasError: true,
108+
},
109+
{
110+
ports: []string{"8005-8000:8005-8000"},
111+
hasError: true,
112+
},
113+
{
114+
ports: []string{"8006-8000:8005-8000"},
115+
hasError: true,
116+
},
117+
}
118+
119+
for _, tc := range testcases {
120+
config := dict{
121+
"version": "3.0",
122+
"services": dict{
123+
"foo": dict{
124+
"image": "busybox",
125+
"ports": tc.ports,
126+
},
127+
},
128+
}
129+
if tc.hasError {
130+
assert.ErrorContains(t, Validate(config, "3"), "services.foo.ports.0 Does not match format 'ports'")
131+
} else {
132+
assert.NilError(t, Validate(config, "3"))
133+
}
134+
}
135+
}
136+
32137
func TestValidateUndefinedTopLevelOption(t *testing.T) {
33138
config := dict{
34139
"version": "3.0",

0 commit comments

Comments
 (0)