1
16
17 package validation
18
19 import (
20 "fmt"
21 "testing"
22
23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24 "k8s.io/apimachinery/pkg/util/validation/field"
25
26 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
27 gatewayv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
28 )
29
30 func TestValidateGateway(t *testing.T) {
31 listeners := []gatewayv1b1.Listener{
32 {
33 Hostname: nil,
34 },
35 }
36 addresses := []gatewayv1b1.GatewayAddress{
37 {
38 Type: nil,
39 },
40 }
41 baseGateway := gatewayv1b1.Gateway{
42 ObjectMeta: metav1.ObjectMeta{
43 Name: "foo",
44 Namespace: metav1.NamespaceDefault,
45 },
46 Spec: gatewayv1b1.GatewaySpec{
47 GatewayClassName: "foo",
48 Listeners: listeners,
49 Addresses: addresses,
50 },
51 }
52 tlsConfig := gatewayv1b1.GatewayTLSConfig{}
53
54 testCases := map[string]struct {
55 mutate func(gw *gatewayv1b1.Gateway)
56 expectErrs []field.Error
57 }{
58 "tls config present with http protocol": {
59 mutate: func(gw *gatewayv1b1.Gateway) {
60 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
61 gw.Spec.Listeners[0].TLS = &tlsConfig
62 },
63 expectErrs: []field.Error{
64 {
65 Type: field.ErrorTypeForbidden,
66 Field: "spec.listeners[0].tls",
67 Detail: "should be empty for protocol HTTP",
68 BadValue: "",
69 },
70 },
71 },
72 "tls config present with tcp protocol": {
73 mutate: func(gw *gatewayv1b1.Gateway) {
74 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType
75 gw.Spec.Listeners[0].TLS = &tlsConfig
76 },
77 expectErrs: []field.Error{
78 {
79 Type: field.ErrorTypeForbidden,
80 Field: "spec.listeners[0].tls",
81 Detail: "should be empty for protocol TCP",
82 BadValue: "",
83 },
84 },
85 },
86 "tls config not set with https protocol": {
87 mutate: func(gw *gatewayv1b1.Gateway) {
88 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType
89 },
90 expectErrs: []field.Error{
91 {
92 Type: field.ErrorTypeForbidden,
93 Field: "spec.listeners[0].tls",
94 Detail: "must be set for protocol HTTPS",
95 BadValue: "",
96 },
97 },
98 },
99 "tls config not set with tls protocol": {
100 mutate: func(gw *gatewayv1b1.Gateway) {
101 gw.Spec.Listeners[0].Protocol = gatewayv1.TLSProtocolType
102 },
103 expectErrs: []field.Error{
104 {
105 Type: field.ErrorTypeForbidden,
106 Field: "spec.listeners[0].tls",
107 Detail: "must be set for protocol TLS",
108 BadValue: "",
109 },
110 },
111 },
112 "tls config not set with http protocol": {
113 mutate: func(gw *gatewayv1b1.Gateway) {
114 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
115 },
116 expectErrs: nil,
117 },
118 "tls config not set with tcp protocol": {
119 mutate: func(gw *gatewayv1b1.Gateway) {
120 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType
121 },
122 expectErrs: nil,
123 },
124 "tls config not set with udp protocol": {
125 mutate: func(gw *gatewayv1b1.Gateway) {
126 gw.Spec.Listeners[0].Protocol = gatewayv1.UDPProtocolType
127 },
128 expectErrs: nil,
129 },
130 "hostname present with tcp protocol": {
131 mutate: func(gw *gatewayv1b1.Gateway) {
132 hostname := gatewayv1b1.Hostname("foo.bar.com")
133 gw.Spec.Listeners[0].Hostname = &hostname
134 gw.Spec.Listeners[0].Protocol = gatewayv1.TCPProtocolType
135 },
136 expectErrs: []field.Error{
137 {
138 Type: field.ErrorTypeForbidden,
139 Field: "spec.listeners[0].hostname",
140 Detail: "should be empty for protocol TCP",
141 BadValue: "",
142 },
143 },
144 },
145 "hostname present with udp protocol": {
146 mutate: func(gw *gatewayv1b1.Gateway) {
147 hostname := gatewayv1b1.Hostname("foo.bar.com")
148 gw.Spec.Listeners[0].Hostname = &hostname
149 gw.Spec.Listeners[0].Protocol = gatewayv1.UDPProtocolType
150 },
151 expectErrs: []field.Error{
152 {
153 Type: field.ErrorTypeForbidden,
154 Field: "spec.listeners[0].hostname",
155 Detail: "should be empty for protocol UDP",
156 BadValue: "",
157 },
158 },
159 },
160 "certificatedRefs not set with https protocol and TLS terminate mode": {
161 mutate: func(gw *gatewayv1b1.Gateway) {
162 hostname := gatewayv1b1.Hostname("foo.bar.com")
163 tlsMode := gatewayv1.TLSModeType("Terminate")
164 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType
165 gw.Spec.Listeners[0].Hostname = &hostname
166 gw.Spec.Listeners[0].TLS = &tlsConfig
167 gw.Spec.Listeners[0].TLS.Mode = &tlsMode
168 },
169 expectErrs: []field.Error{
170 {
171 Type: field.ErrorTypeForbidden,
172 Field: "spec.listeners[0].tls.certificateRefs",
173 Detail: "should be set and not empty when TLSModeType is Terminate",
174 BadValue: "",
175 },
176 },
177 },
178 "certificatedRefs not set with tls protocol and TLS terminate mode": {
179 mutate: func(gw *gatewayv1b1.Gateway) {
180 hostname := gatewayv1b1.Hostname("foo.bar.com")
181 tlsMode := gatewayv1.TLSModeType("Terminate")
182 gw.Spec.Listeners[0].Protocol = gatewayv1.TLSProtocolType
183 gw.Spec.Listeners[0].Hostname = &hostname
184 gw.Spec.Listeners[0].TLS = &tlsConfig
185 gw.Spec.Listeners[0].TLS.Mode = &tlsMode
186 },
187 expectErrs: []field.Error{
188 {
189 Type: field.ErrorTypeForbidden,
190 Field: "spec.listeners[0].tls.certificateRefs",
191 Detail: "should be set and not empty when TLSModeType is Terminate",
192 BadValue: "",
193 },
194 },
195 },
196 "names are not unique within the Gateway": {
197 mutate: func(gw *gatewayv1b1.Gateway) {
198 hostnameFoo := gatewayv1b1.Hostname("foo.com")
199 hostnameBar := gatewayv1b1.Hostname("bar.com")
200 gw.Spec.Listeners[0].Name = "foo"
201 gw.Spec.Listeners[0].Hostname = &hostnameFoo
202 gw.Spec.Listeners = append(gw.Spec.Listeners,
203 gatewayv1b1.Listener{
204 Name: "foo",
205 Hostname: &hostnameBar,
206 },
207 )
208 },
209 expectErrs: []field.Error{
210 {
211 Type: field.ErrorTypeDuplicate,
212 Field: "spec.listeners[1].name",
213 BadValue: "must be unique within the Gateway",
214 },
215 },
216 },
217 "combination of port, protocol, and hostname are not unique for each listener": {
218 mutate: func(gw *gatewayv1b1.Gateway) {
219 hostnameFoo := gatewayv1b1.Hostname("foo.com")
220 gw.Spec.Listeners[0].Name = "foo"
221 gw.Spec.Listeners[0].Hostname = &hostnameFoo
222 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
223 gw.Spec.Listeners[0].Port = 80
224 gw.Spec.Listeners = append(gw.Spec.Listeners,
225 gatewayv1b1.Listener{
226 Name: "bar",
227 Hostname: &hostnameFoo,
228 Protocol: gatewayv1.HTTPProtocolType,
229 Port: 80,
230 },
231 )
232 },
233 expectErrs: []field.Error{
234 {
235 Type: field.ErrorTypeDuplicate,
236 Field: "spec.listeners[1]",
237 BadValue: "combination of port, protocol, and hostname must be unique for each listener",
238 },
239 },
240 },
241 "combination of port and protocol are not unique for each listener when hostnames not set": {
242 mutate: func(gw *gatewayv1b1.Gateway) {
243 gw.Spec.Listeners[0].Name = "foo"
244 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
245 gw.Spec.Listeners[0].Port = 80
246 gw.Spec.Listeners = append(gw.Spec.Listeners,
247 gatewayv1b1.Listener{
248 Name: "bar",
249 Protocol: gatewayv1.HTTPProtocolType,
250 Port: 80,
251 },
252 )
253 },
254 expectErrs: []field.Error{
255 {
256 Type: field.ErrorTypeDuplicate,
257 Field: "spec.listeners[1]",
258 BadValue: "combination of port, protocol, and hostname must be unique for each listener",
259 },
260 },
261 },
262 "port is unique when protocol and hostname are the same": {
263 mutate: func(gw *gatewayv1b1.Gateway) {
264 hostnameFoo := gatewayv1b1.Hostname("foo.com")
265 gw.Spec.Listeners[0].Name = "foo"
266 gw.Spec.Listeners[0].Hostname = &hostnameFoo
267 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
268 gw.Spec.Listeners[0].Port = 80
269 gw.Spec.Listeners = append(gw.Spec.Listeners,
270 gatewayv1b1.Listener{
271 Name: "bar",
272 Hostname: &hostnameFoo,
273 Protocol: gatewayv1.HTTPProtocolType,
274 Port: 8080,
275 },
276 )
277 },
278 expectErrs: nil,
279 },
280 "hostname is unique when protocol and port are the same": {
281 mutate: func(gw *gatewayv1b1.Gateway) {
282 hostnameFoo := gatewayv1b1.Hostname("foo.com")
283 hostnameBar := gatewayv1b1.Hostname("bar.com")
284 gw.Spec.Listeners[0].Name = "foo"
285 gw.Spec.Listeners[0].Hostname = &hostnameFoo
286 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPProtocolType
287 gw.Spec.Listeners[0].Port = 80
288 gw.Spec.Listeners = append(gw.Spec.Listeners,
289 gatewayv1b1.Listener{
290 Name: "bar",
291 Hostname: &hostnameBar,
292 Protocol: gatewayv1.HTTPProtocolType,
293 Port: 80,
294 },
295 )
296 },
297 expectErrs: nil,
298 },
299 "protocol is unique when port and hostname are the same": {
300 mutate: func(gw *gatewayv1b1.Gateway) {
301 hostnameFoo := gatewayv1b1.Hostname("foo.com")
302 tlsConfigFoo := tlsConfig
303 tlsModeFoo := gatewayv1.TLSModeType("Terminate")
304 tlsConfigFoo.Mode = &tlsModeFoo
305 tlsConfigFoo.CertificateRefs = []gatewayv1b1.SecretObjectReference{
306 {
307 Name: "FooCertificateRefs",
308 },
309 }
310 gw.Spec.Listeners[0].Name = "foo"
311 gw.Spec.Listeners[0].Hostname = &hostnameFoo
312 gw.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType
313 gw.Spec.Listeners[0].Port = 8000
314 gw.Spec.Listeners[0].TLS = &tlsConfigFoo
315 gw.Spec.Listeners = append(gw.Spec.Listeners,
316 gatewayv1b1.Listener{
317 Name: "bar",
318 Hostname: &hostnameFoo,
319 Protocol: gatewayv1.TLSProtocolType,
320 Port: 8000,
321 TLS: &tlsConfigFoo,
322 },
323 )
324 },
325 expectErrs: nil,
326 },
327 "ip address and hostname in addresses are valid": {
328 mutate: func(gw *gatewayv1b1.Gateway) {
329 gw.Spec.Addresses = []gatewayv1b1.GatewayAddress{
330 {
331 Type: ptrTo(gatewayv1b1.IPAddressType),
332 Value: "1.2.3.4",
333 },
334 {
335 Type: ptrTo(gatewayv1b1.IPAddressType),
336 Value: "1111:2222:3333:4444::",
337 },
338 {
339 Type: ptrTo(gatewayv1b1.HostnameAddressType),
340 Value: "foo.bar",
341 },
342 }
343 },
344 expectErrs: nil,
345 },
346 "ip address and hostname in addresses are invalid": {
347 mutate: func(gw *gatewayv1b1.Gateway) {
348 gw.Spec.Addresses = []gatewayv1b1.GatewayAddress{
349 {
350 Type: ptrTo(gatewayv1b1.IPAddressType),
351 Value: "1.2.3.4:8080",
352 },
353 {
354 Type: ptrTo(gatewayv1b1.HostnameAddressType),
355 Value: "*foo/bar",
356 },
357 {
358 Type: ptrTo(gatewayv1b1.HostnameAddressType),
359 Value: "12:34:56::",
360 },
361 }
362 },
363 expectErrs: []field.Error{
364 {
365 Type: field.ErrorTypeInvalid,
366 Field: "spec.addresses[0]",
367 Detail: "invalid ip address",
368 BadValue: "1.2.3.4:8080",
369 },
370 {
371 Type: field.ErrorTypeInvalid,
372 Field: "spec.addresses[1]",
373 Detail: fmt.Sprintf("must only contain valid characters (matching %s)", validHostnameAddress),
374 BadValue: "*foo/bar",
375 },
376 {
377 Type: field.ErrorTypeInvalid,
378 Field: "spec.addresses[2]",
379 Detail: fmt.Sprintf("must only contain valid characters (matching %s)", validHostnameAddress),
380 BadValue: "12:34:56::",
381 },
382 },
383 },
384 "duplicate ip address or hostname": {
385 mutate: func(gw *gatewayv1b1.Gateway) {
386 gw.Spec.Addresses = []gatewayv1b1.GatewayAddress{
387 {
388 Type: ptrTo(gatewayv1b1.IPAddressType),
389 Value: "1.2.3.4",
390 },
391 {
392 Type: ptrTo(gatewayv1b1.IPAddressType),
393 Value: "1.2.3.4",
394 },
395 {
396 Type: ptrTo(gatewayv1b1.HostnameAddressType),
397 Value: "foo.bar",
398 },
399 {
400 Type: ptrTo(gatewayv1b1.HostnameAddressType),
401 Value: "foo.bar",
402 },
403 }
404 },
405 expectErrs: []field.Error{
406 {
407 Type: field.ErrorTypeDuplicate,
408 Field: "spec.addresses[1]",
409 BadValue: "1.2.3.4",
410 },
411 {
412 Type: field.ErrorTypeDuplicate,
413 Field: "spec.addresses[3]",
414 BadValue: "foo.bar",
415 },
416 },
417 },
418 }
419
420 for name, tc := range testCases {
421 tc := tc
422 t.Run(name, func(t *testing.T) {
423 gw := baseGateway.DeepCopy()
424 tc.mutate(gw)
425 errs := ValidateGateway(gw)
426 if len(tc.expectErrs) != len(errs) {
427 t.Fatalf("Expected %d errors, got %d errors: %v", len(tc.expectErrs), len(errs), errs)
428 }
429 for i, err := range errs {
430 if err.Type != tc.expectErrs[i].Type {
431 t.Errorf("Expected error on type: %s, got: %s", tc.expectErrs[i].Type, err.Type)
432 }
433 if err.Field != tc.expectErrs[i].Field {
434 t.Errorf("Expected error on field: %s, got: %s", tc.expectErrs[i].Field, err.Field)
435 }
436 if err.Detail != tc.expectErrs[i].Detail {
437 t.Errorf("Expected error on detail: %s, got: %s", tc.expectErrs[i].Detail, err.Detail)
438 }
439 if err.BadValue != tc.expectErrs[i].BadValue {
440 t.Errorf("Expected error on bad value: %s, got: %s", tc.expectErrs[i].BadValue, err.BadValue)
441 }
442 }
443 })
444 }
445 }
446
View as plain text