1
16
17 package main
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24 "time"
25
26 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
27
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 )
30
31 func TestValidateGateway(t *testing.T) {
32 ctx := context.Background()
33 baseGateway := gatewayv1.Gateway{
34 ObjectMeta: metav1.ObjectMeta{
35 Name: "foo",
36 Namespace: metav1.NamespaceDefault,
37 },
38 Spec: gatewayv1.GatewaySpec{
39 GatewayClassName: "foo",
40 Listeners: []gatewayv1.Listener{
41 {
42 Name: gatewayv1.SectionName("http"),
43 Protocol: gatewayv1.HTTPProtocolType,
44 Port: gatewayv1.PortNumber(80),
45 },
46 },
47 },
48 }
49
50 testCases := []struct {
51 desc string
52 mutate func(gw *gatewayv1.Gateway)
53 mutateStatus func(gw *gatewayv1.Gateway)
54 wantErrors []string
55 }{
56 {
57 desc: "tls config present with http protocol",
58 mutate: func(gw *gatewayv1.Gateway) {
59 gw.Spec.Listeners = []gatewayv1.Listener{
60 {
61 Name: gatewayv1.SectionName("http"),
62 Protocol: gatewayv1.HTTPProtocolType,
63 Port: gatewayv1.PortNumber(8080),
64 TLS: &gatewayv1.GatewayTLSConfig{},
65 },
66 }
67 },
68 wantErrors: []string{"tls must not be specified for protocols ['HTTP', 'TCP', 'UDP']"},
69 },
70 {
71 desc: "tls config present with tcp protocol",
72 mutate: func(gw *gatewayv1.Gateway) {
73 gw.Spec.Listeners = []gatewayv1.Listener{
74 {
75 Name: gatewayv1.SectionName("tcp"),
76 Protocol: gatewayv1.TCPProtocolType,
77 Port: gatewayv1.PortNumber(8080),
78 TLS: &gatewayv1.GatewayTLSConfig{},
79 },
80 }
81 },
82 wantErrors: []string{"tls must not be specified for protocols ['HTTP', 'TCP', 'UDP']"},
83 },
84 {
85 desc: "tls config not set with https protocol",
86 mutate: func(gw *gatewayv1.Gateway) {
87 gw.Spec.Listeners = []gatewayv1.Listener{
88 {
89 Name: gatewayv1.SectionName("https"),
90 Protocol: gatewayv1.HTTPSProtocolType,
91 Port: gatewayv1.PortNumber(8443),
92 },
93 }
94 },
95 wantErrors: []string{"tls must be specified for protocols ['HTTPS', 'TLS']"},
96 },
97 {
98 desc: "tls config not set with tls protocol",
99 mutate: func(gw *gatewayv1.Gateway) {
100 gw.Spec.Listeners = []gatewayv1.Listener{
101 {
102 Name: gatewayv1.SectionName("tls"),
103 Protocol: gatewayv1.TLSProtocolType,
104 Port: gatewayv1.PortNumber(8443),
105 },
106 }
107 },
108 wantErrors: []string{"tls must be specified for protocols ['HTTPS', 'TLS']"},
109 },
110 {
111 desc: "tls config not set with http protocol",
112 mutate: func(gw *gatewayv1.Gateway) {
113 gw.Spec.Listeners = []gatewayv1.Listener{
114 {
115 Name: gatewayv1.SectionName("http"),
116 Protocol: gatewayv1.HTTPProtocolType,
117 Port: gatewayv1.PortNumber(8080),
118 },
119 }
120 },
121 },
122 {
123 desc: "tls config not set with tcp protocol",
124 mutate: func(gw *gatewayv1.Gateway) {
125 gw.Spec.Listeners = []gatewayv1.Listener{
126 {
127 Name: gatewayv1.SectionName("tcp"),
128 Protocol: gatewayv1.TCPProtocolType,
129 Port: gatewayv1.PortNumber(8080),
130 },
131 }
132 },
133 },
134 {
135 desc: "tls config not set with udp protocol",
136 mutate: func(gw *gatewayv1.Gateway) {
137 gw.Spec.Listeners = []gatewayv1.Listener{
138 {
139 Name: gatewayv1.SectionName("udp"),
140 Protocol: gatewayv1.UDPProtocolType,
141 Port: gatewayv1.PortNumber(8080),
142 },
143 }
144 },
145 },
146 {
147 desc: "hostname present with tcp protocol",
148 mutate: func(gw *gatewayv1.Gateway) {
149 hostname := gatewayv1.Hostname("foo")
150 gw.Spec.Listeners = []gatewayv1.Listener{
151 {
152 Name: gatewayv1.SectionName("tcp"),
153 Protocol: gatewayv1.TCPProtocolType,
154 Port: gatewayv1.PortNumber(8080),
155 Hostname: &hostname,
156 },
157 }
158 },
159 wantErrors: []string{"hostname must not be specified for protocols ['TCP', 'UDP']"},
160 },
161 {
162 desc: "hostname present with udp protocol",
163 mutate: func(gw *gatewayv1.Gateway) {
164 hostname := gatewayv1.Hostname("foo")
165 gw.Spec.Listeners = []gatewayv1.Listener{
166 {
167 Name: gatewayv1.SectionName("udp"),
168 Protocol: gatewayv1.UDPProtocolType,
169 Port: gatewayv1.PortNumber(8080),
170 Hostname: &hostname,
171 },
172 }
173 },
174 wantErrors: []string{"hostname must not be specified for protocols ['TCP', 'UDP']"},
175 },
176 {
177 desc: "certificateRefs not set with https protocol and TLS terminate mode",
178 mutate: func(gw *gatewayv1.Gateway) {
179 tlsMode := gatewayv1.TLSModeType("Terminate")
180 gw.Spec.Listeners = []gatewayv1.Listener{
181 {
182 Name: gatewayv1.SectionName("https"),
183 Protocol: gatewayv1.HTTPSProtocolType,
184 Port: gatewayv1.PortNumber(8443),
185 TLS: &gatewayv1.GatewayTLSConfig{
186 Mode: &tlsMode,
187 },
188 },
189 }
190 },
191 wantErrors: []string{"certificateRefs must be specified when TLSModeType is Terminate"},
192 },
193 {
194 desc: "certificateRefs not set with tls protocol and TLS terminate mode",
195 mutate: func(gw *gatewayv1.Gateway) {
196 tlsMode := gatewayv1.TLSModeType("Terminate")
197 gw.Spec.Listeners = []gatewayv1.Listener{
198 {
199 Name: gatewayv1.SectionName("tls"),
200 Protocol: gatewayv1.TLSProtocolType,
201 Port: gatewayv1.PortNumber(8443),
202 TLS: &gatewayv1.GatewayTLSConfig{
203 Mode: &tlsMode,
204 },
205 },
206 }
207 },
208 wantErrors: []string{"certificateRefs must be specified when TLSModeType is Terminate"},
209 },
210 {
211 desc: "certificateRefs set with tls protocol and TLS terminate mode",
212 mutate: func(gw *gatewayv1.Gateway) {
213 tlsMode := gatewayv1.TLSModeType("Terminate")
214 gw.Spec.Listeners = []gatewayv1.Listener{
215 {
216 Name: gatewayv1.SectionName("tls"),
217 Protocol: gatewayv1.TLSProtocolType,
218 Port: gatewayv1.PortNumber(8443),
219 TLS: &gatewayv1.GatewayTLSConfig{
220 Mode: &tlsMode,
221 CertificateRefs: []gatewayv1.SecretObjectReference{
222 {Name: gatewayv1.ObjectName("foo")},
223 },
224 },
225 },
226 }
227 },
228 },
229 {
230 desc: "names are not unique within the Gateway",
231 mutate: func(gw *gatewayv1.Gateway) {
232 gw.Spec.Listeners = []gatewayv1.Listener{
233 {
234 Name: gatewayv1.SectionName("http"),
235 Protocol: gatewayv1.HTTPProtocolType,
236 Port: gatewayv1.PortNumber(80),
237 },
238 {
239 Name: gatewayv1.SectionName("http"),
240 Protocol: gatewayv1.HTTPProtocolType,
241 Port: gatewayv1.PortNumber(8000),
242 },
243 {
244 Name: gatewayv1.SectionName("http"),
245 Protocol: gatewayv1.HTTPProtocolType,
246 Port: gatewayv1.PortNumber(8080),
247 },
248 }
249 },
250 wantErrors: []string{"Listener name must be unique within the Gateway"},
251 },
252 {
253 desc: "names are unique within the Gateway",
254 mutate: func(gw *gatewayv1.Gateway) {
255 gw.Spec.Listeners = []gatewayv1.Listener{
256 {
257 Name: gatewayv1.SectionName("http-1"),
258 Protocol: gatewayv1.HTTPProtocolType,
259 Port: gatewayv1.PortNumber(80),
260 },
261 {
262 Name: gatewayv1.SectionName("http-2"),
263 Protocol: gatewayv1.HTTPProtocolType,
264 Port: gatewayv1.PortNumber(8000),
265 },
266 {
267 Name: gatewayv1.SectionName("http-3"),
268 Protocol: gatewayv1.HTTPProtocolType,
269 Port: gatewayv1.PortNumber(8080),
270 },
271 }
272 },
273 },
274 {
275 desc: "combination of port, protocol, and hostname are not unique for each listener",
276 mutate: func(gw *gatewayv1.Gateway) {
277 hostnameFoo := gatewayv1.Hostname("foo.com")
278 gw.Spec.Listeners = []gatewayv1.Listener{
279 {
280 Name: gatewayv1.SectionName("foo"),
281 Protocol: gatewayv1.HTTPProtocolType,
282 Port: gatewayv1.PortNumber(80),
283 Hostname: &hostnameFoo,
284 },
285 {
286 Name: gatewayv1.SectionName("bar"),
287 Protocol: gatewayv1.HTTPProtocolType,
288 Port: gatewayv1.PortNumber(80),
289 Hostname: &hostnameFoo,
290 },
291 }
292 },
293 wantErrors: []string{"Combination of port, protocol and hostname must be unique for each listener"},
294 },
295 {
296 desc: "combination of port and protocol are not unique for each listener when hostnames not set",
297 mutate: func(gw *gatewayv1.Gateway) {
298 gw.Spec.Listeners = []gatewayv1.Listener{
299 {
300 Name: gatewayv1.SectionName("foo"),
301 Protocol: gatewayv1.HTTPProtocolType,
302 Port: gatewayv1.PortNumber(80),
303 },
304 {
305 Name: gatewayv1.SectionName("bar"),
306 Protocol: gatewayv1.HTTPProtocolType,
307 Port: gatewayv1.PortNumber(80),
308 },
309 }
310 },
311 wantErrors: []string{"Combination of port, protocol and hostname must be unique for each listener"},
312 },
313 {
314 desc: "port is unique when protocol and hostname are the same",
315 mutate: func(gw *gatewayv1.Gateway) {
316 hostnameFoo := gatewayv1.Hostname("foo.com")
317 gw.Spec.Listeners = []gatewayv1.Listener{
318 {
319 Name: gatewayv1.SectionName("foo"),
320 Protocol: gatewayv1.HTTPProtocolType,
321 Port: gatewayv1.PortNumber(80),
322 Hostname: &hostnameFoo,
323 },
324 {
325 Name: gatewayv1.SectionName("bar"),
326 Protocol: gatewayv1.HTTPProtocolType,
327 Port: gatewayv1.PortNumber(8000),
328 Hostname: &hostnameFoo,
329 },
330 }
331 },
332 },
333 {
334 desc: "hostname is unique when protocol and port are the same",
335 mutate: func(gw *gatewayv1.Gateway) {
336 hostnameFoo := gatewayv1.Hostname("foo.com")
337 hostnameBar := gatewayv1.Hostname("bar.com")
338 gw.Spec.Listeners = []gatewayv1.Listener{
339 {
340 Name: gatewayv1.SectionName("foo"),
341 Protocol: gatewayv1.HTTPProtocolType,
342 Port: gatewayv1.PortNumber(80),
343 Hostname: &hostnameFoo,
344 },
345 {
346 Name: gatewayv1.SectionName("bar"),
347 Protocol: gatewayv1.HTTPProtocolType,
348 Port: gatewayv1.PortNumber(80),
349 Hostname: &hostnameBar,
350 },
351 }
352 },
353 },
354 {
355 desc: "one omitted hostname is unique when protocol and port are the same",
356 mutate: func(gw *gatewayv1.Gateway) {
357 hostnameFoo := gatewayv1.Hostname("foo.com")
358 gw.Spec.Listeners = []gatewayv1.Listener{
359 {
360 Name: gatewayv1.SectionName("foo"),
361 Protocol: gatewayv1.HTTPProtocolType,
362 Port: gatewayv1.PortNumber(80),
363 Hostname: &hostnameFoo,
364 },
365 {
366 Name: gatewayv1.SectionName("bar"),
367 Protocol: gatewayv1.HTTPProtocolType,
368 Port: gatewayv1.PortNumber(80),
369 },
370 }
371 },
372 },
373 {
374 desc: "protocol is unique when port and hostname are the same",
375 mutate: func(gw *gatewayv1.Gateway) {
376 hostnameFoo := gatewayv1.Hostname("foo.com")
377 gw.Spec.Listeners = []gatewayv1.Listener{
378 {
379 Name: gatewayv1.SectionName("foo"),
380 Protocol: gatewayv1.HTTPProtocolType,
381 Port: gatewayv1.PortNumber(8000),
382 Hostname: &hostnameFoo,
383 },
384 {
385 Name: gatewayv1.SectionName("bar"),
386 Protocol: gatewayv1.HTTPSProtocolType,
387 Port: gatewayv1.PortNumber(8000),
388 Hostname: &hostnameFoo,
389 TLS: &gatewayv1.GatewayTLSConfig{
390 CertificateRefs: []gatewayv1.SecretObjectReference{
391 {Name: gatewayv1.ObjectName("foo")},
392 },
393 },
394 },
395 }
396 },
397 },
398 {
399 desc: "ip address and hostname in addresses are valid",
400 mutate: func(gw *gatewayv1.Gateway) {
401 gw.Spec.Addresses = []gatewayv1.GatewayAddress{
402 {
403 Type: ptrTo(gatewayv1.IPAddressType),
404 Value: "1.2.3.4",
405 },
406 {
407 Type: ptrTo(gatewayv1.IPAddressType),
408 Value: "1111:2222:3333:4444::",
409 },
410 {
411 Type: ptrTo(gatewayv1.HostnameAddressType),
412 Value: "foo.bar",
413 },
414 }
415 },
416 },
417 {
418 desc: "ip address and hostname in addresses are invalid",
419 mutate: func(gw *gatewayv1.Gateway) {
420 gw.Spec.Addresses = []gatewayv1.GatewayAddress{
421 {
422 Type: ptrTo(gatewayv1.IPAddressType),
423 Value: "1.2.3.4:8080",
424 },
425 {
426 Type: ptrTo(gatewayv1.HostnameAddressType),
427 Value: "*foo/bar",
428 },
429 {
430 Type: ptrTo(gatewayv1.HostnameAddressType),
431 Value: "12:34:56::",
432 },
433 }
434 },
435 wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": spec.addresses[0].value in body must be of type ipv4"},
436 },
437 {
438 desc: "ip address and hostname in status addresses are valid",
439 mutateStatus: func(gw *gatewayv1.Gateway) {
440 gw.Status.Addresses = []gatewayv1.GatewayStatusAddress{
441 {
442 Type: ptrTo(gatewayv1.IPAddressType),
443 Value: "1.2.3.4",
444 },
445 {
446 Type: ptrTo(gatewayv1.IPAddressType),
447 Value: "1111:2222:3333:4444::",
448 },
449 {
450 Type: ptrTo(gatewayv1.HostnameAddressType),
451 Value: "foo.bar",
452 },
453 }
454 },
455 },
456 {
457 desc: "ip address and hostname in status addresses are invalid",
458 mutateStatus: func(gw *gatewayv1.Gateway) {
459 gw.Status.Addresses = []gatewayv1.GatewayStatusAddress{
460 {
461 Type: ptrTo(gatewayv1.IPAddressType),
462 Value: "1.2.3.4:8080",
463 },
464 {
465 Type: ptrTo(gatewayv1.HostnameAddressType),
466 Value: "*foo/bar",
467 },
468 {
469 Type: ptrTo(gatewayv1.HostnameAddressType),
470 Value: "12:34:56::",
471 },
472 }
473 },
474 wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": status.addresses[0].value in body must be of type ipv4"},
475 },
476 {
477 desc: "duplicate ip address or hostname",
478 mutate: func(gw *gatewayv1.Gateway) {
479 gw.Spec.Addresses = []gatewayv1.GatewayAddress{
480 {
481 Type: ptrTo(gatewayv1.IPAddressType),
482 Value: "1.2.3.4",
483 },
484 {
485 Type: ptrTo(gatewayv1.IPAddressType),
486 Value: "1.2.3.4",
487 },
488 {
489 Type: ptrTo(gatewayv1.HostnameAddressType),
490 Value: "foo.bar",
491 },
492 {
493 Type: ptrTo(gatewayv1.HostnameAddressType),
494 Value: "foo.bar",
495 },
496 }
497 },
498 wantErrors: []string{"IPAddress values must be unique", "Hostname values must be unique"},
499 },
500 }
501
502 for _, tc := range testCases {
503 t.Run(tc.desc, func(t *testing.T) {
504 gw := baseGateway.DeepCopy()
505 gw.Name = fmt.Sprintf("foo-%v", time.Now().UnixNano())
506
507 if tc.mutate != nil {
508 tc.mutate(gw)
509 }
510 err := k8sClient.Create(ctx, gw)
511
512 if tc.mutateStatus != nil {
513 tc.mutateStatus(gw)
514 err = k8sClient.Status().Update(ctx, gw)
515 }
516
517 if (len(tc.wantErrors) != 0) != (err != nil) {
518 t.Fatalf("Unexpected response while creating Gateway; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil)
519 }
520
521 var missingErrorStrings []string
522 for _, wantError := range tc.wantErrors {
523 if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) {
524 missingErrorStrings = append(missingErrorStrings, wantError)
525 }
526 }
527 if len(missingErrorStrings) != 0 {
528 t.Errorf("Unexpected response while creating Gateway; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings)
529 }
530 })
531 }
532 }
533
View as plain text