@@ -7,93 +7,95 @@ import (
7
7
"strings"
8
8
9
9
"github.com/moby/buildkit/frontend/dockerfile/instructions"
10
- "github.com/moby/buildkit/frontend/dockerfile/shell"
10
+ "github.com/moby/buildkit/frontend/dockerfile/linter"
11
+ "github.com/moby/buildkit/frontend/dockerfile/parser"
11
12
"github.com/pkg/errors"
12
13
)
13
14
14
- func dispatchExpose (d * dispatchState , c * instructions.ExposeCommand , shlex * shell. Lex ) error {
15
+ func dispatchExpose (d * dispatchState , c * instructions.ExposeCommand , opt * dispatchOpt ) error {
15
16
ports := []string {}
16
17
env := getEnv (d .state )
17
18
for _ , p := range c .Ports {
18
- ps , err := shlex .ProcessWords (p , env )
19
+ ps , err := opt . shlex .ProcessWords (p , env )
19
20
if err != nil {
20
21
return err
21
22
}
22
23
ports = append (ports , ps ... )
23
24
}
24
25
c .Ports = ports
25
26
26
- ps , err := parsePortSpecs (c .Ports )
27
+ ps := newPortSpecs (
28
+ withLocation (c .Location ()),
29
+ withLint (opt .lint ),
30
+ )
31
+
32
+ psp , err := ps .parsePorts (c .Ports )
27
33
if err != nil {
28
34
return err
29
35
}
30
36
31
37
if d .image .Config .ExposedPorts == nil {
32
38
d .image .Config .ExposedPorts = make (map [string ]struct {})
33
39
}
34
- for _ , p := range ps {
40
+ for _ , p := range psp {
35
41
d .image .Config .ExposedPorts [p ] = struct {}{}
36
42
}
37
43
38
44
return commitToHistory (& d .image , fmt .Sprintf ("EXPOSE %v" , ps ), false , nil , d .epoch )
39
45
}
40
46
41
- // parsePortSpecs receives port specs in the format of [ip:]public:private/proto
42
- // and returns them as a list of "port/proto".
43
- func parsePortSpecs (ports []string ) (exposedPorts []string , _ error ) {
44
- for _ , p := range ports {
45
- portProtos , err := parsePortSpec (p )
46
- if err != nil {
47
- return nil , err
48
- }
49
- exposedPorts = append (exposedPorts , portProtos ... )
50
- }
51
- return exposedPorts , nil
47
+ type portSpecs struct {
48
+ location []parser.Range
49
+ lint * linter.Linter
52
50
}
53
51
54
- // splitProtoPort splits a port(range) and protocol, formatted as "<portnum>/[<proto>]"
55
- // "<startport-endport>/[<proto>]". It returns an error if no port(range) or
56
- // an invalid proto is provided. If no protocol is provided, the default ("tcp")
57
- // protocol is returned.
58
- func splitProtoPort (rawPort string ) (proto string , port string , _ error ) {
59
- port , proto , _ = strings .Cut (rawPort , "/" )
60
- if port == "" {
61
- return "" , "" , errors .New ("no port specified" )
52
+ type portSpecsOption func (ps * portSpecs )
53
+
54
+ func withLocation (location []parser.Range ) portSpecsOption {
55
+ return func (ps * portSpecs ) {
56
+ ps .location = location
62
57
}
63
- proto = strings .ToLower (proto )
64
- switch proto {
65
- case "" :
66
- return "tcp" , port , nil
67
- case "tcp" , "udp" , "sctp" :
68
- return proto , port , nil
69
- default :
70
- return "" , "" , errors .New ("invalid proto: " + proto )
58
+ }
59
+
60
+ func withLint (lint * linter.Linter ) portSpecsOption {
61
+ return func (ps * portSpecs ) {
62
+ ps .lint = lint
71
63
}
72
64
}
73
65
74
- func splitParts (rawport string ) (hostIP , hostPort , containerPort string ) {
75
- parts := strings .Split (rawport , ":" )
66
+ func newPortSpecs (opts ... portSpecsOption ) * portSpecs {
67
+ ps := & portSpecs {}
68
+ for _ , opt := range opts {
69
+ opt (ps )
70
+ }
71
+ return ps
72
+ }
76
73
77
- switch len (parts ) {
78
- case 1 :
79
- return "" , "" , parts [0 ]
80
- case 2 :
81
- return "" , parts [0 ], parts [1 ]
82
- case 3 :
83
- return parts [0 ], parts [1 ], parts [2 ]
84
- default :
85
- n := len (parts )
86
- return strings .Join (parts [:n - 2 ], ":" ), parts [n - 2 ], parts [n - 1 ]
74
+ // parsePorts receives port specs in the format of [ip:]public:private/proto
75
+ // and returns them as a list of "port/proto".
76
+ func (ps * portSpecs ) parsePorts (ports []string ) (exposedPorts []string , _ error ) {
77
+ for _ , p := range ports {
78
+ portProtos , err := ps .parsePort (p )
79
+ if err != nil {
80
+ return nil , err
81
+ }
82
+ exposedPorts = append (exposedPorts , portProtos ... )
87
83
}
84
+ return exposedPorts , nil
88
85
}
89
86
90
- // parsePortSpec parses a port specification string into a slice of "<portnum>/[<proto>]"
91
- func parsePortSpec (rawPort string ) (portProto []string , _ error ) {
92
- ip , hostPort , containerPort := splitParts (rawPort )
93
- proto , containerPort , err := splitProtoPort (containerPort )
87
+ // parsePort parses a port specification string into a slice of "<portnum>/[<proto>]"
88
+ func ( ps * portSpecs ) parsePort (rawPort string ) (portProto []string , _ error ) {
89
+ ip , hostPort , containerPort := ps . splitParts (rawPort )
90
+ proto , containerPort , err := ps . splitProtoPort (containerPort )
94
91
if err != nil {
95
92
return nil , errors .Wrapf (err , "invalid port: %q" , rawPort )
96
93
}
94
+ if lcproto := strings .ToLower (proto ); proto != lcproto && ps .lint != nil {
95
+ msg := linter .RuleExposeProtoCasing .Format (rawPort )
96
+ ps .lint .Run (& linter .RuleExposeProtoCasing , ps .location , msg )
97
+ proto = lcproto
98
+ }
97
99
98
100
// TODO(thaJeztah): mapping IP-addresses should not be allowed for EXPOSE; see https://github.com/moby/buildkit/issues/2173
99
101
if ip != "" && ip [0 ] == '[' {
@@ -108,14 +110,14 @@ func parsePortSpec(rawPort string) (portProto []string, _ error) {
108
110
return nil , errors .New ("invalid IP address: " + ip )
109
111
}
110
112
111
- startPort , endPort , err := parsePortRange (containerPort )
113
+ startPort , endPort , err := ps . parsePortRange (containerPort )
112
114
if err != nil {
113
115
return nil , errors .New ("invalid containerPort: " + containerPort )
114
116
}
115
117
116
118
// TODO(thaJeztah): mapping host-ports should not be allowed for EXPOSE; see https://github.com/moby/buildkit/issues/2173
117
119
if hostPort != "" {
118
- startHostPort , endHostPort , err := parsePortRange (hostPort )
120
+ startHostPort , endHostPort , err := ps . parsePortRange (hostPort )
119
121
if err != nil {
120
122
return nil , errors .New ("invalid hostPort: " + hostPort )
121
123
}
@@ -139,21 +141,21 @@ func parsePortSpec(rawPort string) (portProto []string, _ error) {
139
141
}
140
142
141
143
// parsePortRange parses and validates the specified string as a port range (e.g., "8000-9000").
142
- func parsePortRange (ports string ) (startPort , endPort int , _ error ) {
144
+ func ( ps * portSpecs ) parsePortRange (ports string ) (startPort , endPort int , _ error ) {
143
145
if ports == "" {
144
146
return 0 , 0 , errors .New ("empty string specified for ports" )
145
147
}
146
148
start , end , ok := strings .Cut (ports , "-" )
147
149
148
- startPort , err := parsePortNumber (start )
150
+ startPort , err := ps . parsePortNumber (start )
149
151
if err != nil {
150
152
return 0 , 0 , errors .Wrapf (err , "invalid start port '%s'" , start )
151
153
}
152
154
if ! ok || start == end {
153
155
return startPort , startPort , nil
154
156
}
155
157
156
- endPort , err = parsePortNumber (end )
158
+ endPort , err = ps . parsePortNumber (end )
157
159
if err != nil {
158
160
return 0 , 0 , errors .Wrapf (err , "invalid end port '%s'" , end )
159
161
}
@@ -165,7 +167,7 @@ func parsePortRange(ports string) (startPort, endPort int, _ error) {
165
167
166
168
// parsePortNumber parses rawPort into an int, unwrapping strconv errors
167
169
// and returning a single "out of range" error for any value outside 0–65535.
168
- func parsePortNumber (rawPort string ) (int , error ) {
170
+ func ( ps * portSpecs ) parsePortNumber (rawPort string ) (int , error ) {
169
171
if rawPort == "" {
170
172
return 0 , errors .New ("value is empty" )
171
173
}
@@ -183,3 +185,38 @@ func parsePortNumber(rawPort string) (int, error) {
183
185
184
186
return int (port ), nil
185
187
}
188
+
189
+ // splitProtoPort splits a port(range) and protocol, formatted as "<portnum>/[<proto>]"
190
+ // "<startport-endport>/[<proto>]". It returns an error if no port(range) or
191
+ // an invalid proto is provided. If no protocol is provided, the default ("tcp")
192
+ // protocol is returned.
193
+ func (ps * portSpecs ) splitProtoPort (rawPort string ) (proto string , port string , _ error ) {
194
+ port , proto , _ = strings .Cut (rawPort , "/" )
195
+ if port == "" {
196
+ return "" , "" , errors .New ("no port specified" )
197
+ }
198
+ switch strings .ToLower (proto ) {
199
+ case "" :
200
+ return "tcp" , port , nil
201
+ case "tcp" , "udp" , "sctp" :
202
+ return proto , port , nil
203
+ default :
204
+ return "" , "" , errors .New ("invalid proto: " + proto )
205
+ }
206
+ }
207
+
208
+ func (ps * portSpecs ) splitParts (rawport string ) (hostIP , hostPort , containerPort string ) {
209
+ parts := strings .Split (rawport , ":" )
210
+
211
+ switch len (parts ) {
212
+ case 1 :
213
+ return "" , "" , parts [0 ]
214
+ case 2 :
215
+ return "" , parts [0 ], parts [1 ]
216
+ case 3 :
217
+ return parts [0 ], parts [1 ], parts [2 ]
218
+ default :
219
+ n := len (parts )
220
+ return strings .Join (parts [:n - 2 ], ":" ), parts [n - 2 ], parts [n - 1 ]
221
+ }
222
+ }
0 commit comments