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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 func TestHTTPPathMatch(t *testing.T) {
47 tests := []struct {
48 name string
49 wantErrors []string
50 path *gatewayv1.HTTPPathMatch
51 }{
52 {
53 name: "invalid because path does not start with '/'",
54 wantErrors: []string{"value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix']"},
55 path: &gatewayv1.HTTPPathMatch{
56 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
57 Value: ptrTo("foo"),
58 },
59 },
60 {
61 name: "invalid httpRoute prefix (/.)",
62 wantErrors: []string{"must not end with '/.' when type one of ['Exact', 'PathPrefix']"},
63 path: &gatewayv1.HTTPPathMatch{
64 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
65 Value: ptrTo("/."),
66 },
67 },
68 {
69 name: "invalid exact (/./)",
70 wantErrors: []string{"must not contain '/./' when type one of ['Exact', 'PathPrefix']"},
71 path: &gatewayv1.HTTPPathMatch{
72 Type: ptrTo(gatewayv1.PathMatchType("Exact")),
73 Value: ptrTo("/foo/./bar"),
74 },
75 },
76 {
77 name: "invalid type",
78 wantErrors: []string{"type must be one of ['Exact', 'PathPrefix', 'RegularExpression']"},
79 path: &gatewayv1.HTTPPathMatch{
80 Type: ptrTo(gatewayv1.PathMatchType("FooBar")),
81 Value: ptrTo("/path"),
82 },
83 },
84 {
85 name: "valid because type is RegularExpression but would not be valid for Exact",
86 path: &gatewayv1.HTTPPathMatch{
87 Type: ptrTo(gatewayv1.PathMatchType("RegularExpression")),
88 Value: ptrTo("/foo/./bar"),
89 },
90 },
91 {
92 name: "valid httpRoute prefix",
93 path: &gatewayv1.HTTPPathMatch{
94 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
95 Value: ptrTo("/path"),
96 },
97 },
98 {
99 name: "valid path with some special characters",
100 path: &gatewayv1.HTTPPathMatch{
101 Type: ptrTo(gatewayv1.PathMatchType("Exact")),
102 Value: ptrTo("/abc/123'/a-b-c/d@gmail/%0A"),
103 },
104 },
105 {
106 name: "invalid prefix path (/[])",
107 path: &gatewayv1.HTTPPathMatch{
108 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
109 Value: ptrTo("/[]"),
110 },
111 wantErrors: []string{"must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix']"},
112 },
113 {
114 name: "invalid exact path (/^)",
115 path: &gatewayv1.HTTPPathMatch{
116 Type: ptrTo(gatewayv1.PathMatchType("Exact")),
117 Value: ptrTo("/^"),
118 },
119 wantErrors: []string{"must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix']"},
120 },
121 }
122
123 for _, tc := range tests {
124 t.Run(tc.name, func(t *testing.T) {
125 route := &gatewayv1.HTTPRoute{
126 ObjectMeta: metav1.ObjectMeta{
127 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
128 Namespace: metav1.NamespaceDefault,
129 },
130 Spec: gatewayv1.HTTPRouteSpec{
131 Rules: []gatewayv1.HTTPRouteRule{{
132 Matches: []gatewayv1.HTTPRouteMatch{{
133 Path: tc.path,
134 }},
135 BackendRefs: []gatewayv1.HTTPBackendRef{{
136 BackendRef: gatewayv1.BackendRef{
137 BackendObjectReference: gatewayv1.BackendObjectReference{
138 Name: gatewayv1.ObjectName("test"),
139 Port: ptrTo(gatewayv1.PortNumber(8080)),
140 },
141 },
142 }},
143 }},
144 },
145 }
146 validateHTTPRoute(t, route, tc.wantErrors)
147 })
148 }
149 }
150
151 func TestBackendObjectReference(t *testing.T) {
152 portPtr := func(n int) *gatewayv1.PortNumber {
153 p := gatewayv1.PortNumber(n)
154 return &p
155 }
156
157 groupPtr := func(g string) *gatewayv1.Group {
158 p := gatewayv1.Group(g)
159 return &p
160 }
161
162 kindPtr := func(k string) *gatewayv1.Kind {
163 p := gatewayv1.Kind(k)
164 return &p
165 }
166
167 tests := []struct {
168 name string
169 wantErrors []string
170 rules []gatewayv1.HTTPRouteRule
171 backendRef gatewayv1.BackendObjectReference
172 }{
173 {
174 name: "default groupkind with port",
175 backendRef: gatewayv1.BackendObjectReference{
176 Name: "backend",
177 Port: portPtr(99),
178 },
179 },
180 {
181 name: "default groupkind with no port",
182 wantErrors: []string{"Must have port for Service reference"},
183 backendRef: gatewayv1.BackendObjectReference{
184 Name: "backend",
185 },
186 },
187 {
188 name: "explicit service with port",
189 backendRef: gatewayv1.BackendObjectReference{
190 Group: groupPtr(""),
191 Kind: kindPtr("Service"),
192 Name: "backend",
193 Port: portPtr(99),
194 },
195 },
196 {
197 name: "explicit service with no port",
198 wantErrors: []string{"Must have port for Service reference"},
199 backendRef: gatewayv1.BackendObjectReference{
200 Group: groupPtr(""),
201 Kind: kindPtr("Service"),
202 Name: "backend",
203 },
204 },
205 {
206 name: "explicit ref with no port",
207 backendRef: gatewayv1.BackendObjectReference{
208 Group: groupPtr("foo.example.com"),
209 Kind: kindPtr("Foo"),
210 Name: "backend",
211 },
212 },
213 }
214
215 for _, tc := range tests {
216 t.Run(tc.name, func(t *testing.T) {
217 route := &gatewayv1.HTTPRoute{
218 ObjectMeta: metav1.ObjectMeta{
219 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
220 Namespace: metav1.NamespaceDefault,
221 },
222 Spec: gatewayv1.HTTPRouteSpec{
223 Rules: []gatewayv1.HTTPRouteRule{{
224 BackendRefs: []gatewayv1.HTTPBackendRef{{
225 BackendRef: gatewayv1.BackendRef{
226 BackendObjectReference: tc.backendRef,
227 },
228 }},
229 }},
230 },
231 }
232 validateHTTPRoute(t, route, tc.wantErrors)
233 })
234 }
235 }
236
237 func TestHTTPRouteFilter(t *testing.T) {
238 tests := []struct {
239 name string
240 wantErrors []string
241 routeFilter gatewayv1.HTTPRouteFilter
242 }{
243 {
244 name: "valid HTTPRouteFilterRequestHeaderModifier route filter",
245 routeFilter: gatewayv1.HTTPRouteFilter{
246 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
247 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
248 Set: []gatewayv1.HTTPHeader{{Name: "name", Value: "foo"}},
249 Add: []gatewayv1.HTTPHeader{{Name: "add", Value: "foo"}},
250 Remove: []string{"remove"},
251 },
252 },
253 },
254 {
255 name: "invalid HTTPRouteFilterRequestHeaderModifier type filter with non-matching field",
256 routeFilter: gatewayv1.HTTPRouteFilter{
257 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
258 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{},
259 },
260 wantErrors: []string{"filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type", "filter.requestMirror must be nil if the filter.type is not RequestMirror"},
261 },
262 {
263 name: "invalid HTTPRouteFilterRequestHeaderModifier type filter with empty value field",
264 routeFilter: gatewayv1.HTTPRouteFilter{
265 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
266 },
267 wantErrors: []string{"filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type"},
268 },
269 {
270 name: "valid HTTPRouteFilterRequestMirror route filter",
271 routeFilter: gatewayv1.HTTPRouteFilter{
272 Type: gatewayv1.HTTPRouteFilterRequestMirror,
273 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{BackendRef: gatewayv1.BackendObjectReference{
274 Group: ptrTo(gatewayv1.Group("group")),
275 Kind: ptrTo(gatewayv1.Kind("kind")),
276 Name: "name",
277 Namespace: ptrTo(gatewayv1.Namespace("ns")),
278 Port: ptrTo(gatewayv1.PortNumber(22)),
279 }},
280 },
281 },
282 {
283 name: "invalid HTTPRouteFilterRequestMirror type filter with non-matching field",
284 routeFilter: gatewayv1.HTTPRouteFilter{
285 Type: gatewayv1.HTTPRouteFilterRequestMirror,
286 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{},
287 },
288 wantErrors: []string{"filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier", "filter.requestMirror must be specified for RequestMirror filter.type"},
289 },
290 {
291 name: "invalid HTTPRouteFilterRequestMirror type filter with empty value field",
292 routeFilter: gatewayv1.HTTPRouteFilter{
293 Type: gatewayv1.HTTPRouteFilterRequestMirror,
294 },
295 wantErrors: []string{"filter.requestMirror must be specified for RequestMirror filter.type"},
296 },
297 {
298 name: "valid HTTPRouteFilterRequestRedirect route filter",
299 routeFilter: gatewayv1.HTTPRouteFilter{
300 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
301 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
302 Scheme: ptrTo("http"),
303 Hostname: ptrTo(gatewayv1.PreciseHostname("hostname")),
304 Path: &gatewayv1.HTTPPathModifier{
305 Type: gatewayv1.FullPathHTTPPathModifier,
306 ReplaceFullPath: ptrTo("path"),
307 },
308 Port: ptrTo(gatewayv1.PortNumber(8080)),
309 StatusCode: ptrTo(302),
310 },
311 },
312 },
313 {
314 name: "invalid HTTPRouteFilterRequestRedirect type filter with non-matching field",
315 routeFilter: gatewayv1.HTTPRouteFilter{
316 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
317 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{},
318 },
319 wantErrors: []string{"filter.requestMirror must be nil if the filter.type is not RequestMirror", "filter.requestRedirect must be specified for RequestRedirect filter.type"},
320 },
321 {
322 name: "invalid HTTPRouteFilterRequestRedirect type filter with empty value field",
323 routeFilter: gatewayv1.HTTPRouteFilter{
324 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
325 },
326 wantErrors: []string{"filter.requestRedirect must be specified for RequestRedirect filter.type"},
327 },
328 {
329 name: "valid HTTPRouteFilterExtensionRef filter",
330 routeFilter: gatewayv1.HTTPRouteFilter{
331 Type: gatewayv1.HTTPRouteFilterExtensionRef,
332 ExtensionRef: &gatewayv1.LocalObjectReference{
333 Group: "group",
334 Kind: "kind",
335 Name: "name",
336 },
337 },
338 },
339 {
340 name: "invalid HTTPRouteFilterExtensionRef type filter with non-matching field",
341 routeFilter: gatewayv1.HTTPRouteFilter{
342 Type: gatewayv1.HTTPRouteFilterExtensionRef,
343 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{},
344 },
345 wantErrors: []string{"filter.requestMirror must be nil if the filter.type is not RequestMirror", "filter.extensionRef must be specified for ExtensionRef filter.type"},
346 },
347 {
348 name: "invalid HTTPRouteFilterExtensionRef type filter with empty value field",
349 routeFilter: gatewayv1.HTTPRouteFilter{
350 Type: gatewayv1.HTTPRouteFilterExtensionRef,
351 },
352 wantErrors: []string{"filter.extensionRef must be specified for ExtensionRef filter.type"},
353 },
354 {
355 name: "valid HTTPRouteFilterURLRewrite route filter",
356 routeFilter: gatewayv1.HTTPRouteFilter{
357 Type: gatewayv1.HTTPRouteFilterURLRewrite,
358 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
359 Hostname: ptrTo(gatewayv1.PreciseHostname("hostname")),
360 Path: &gatewayv1.HTTPPathModifier{
361 Type: gatewayv1.FullPathHTTPPathModifier,
362 ReplaceFullPath: ptrTo("path"),
363 },
364 },
365 },
366 },
367 {
368 name: "invalid HTTPRouteFilterURLRewrite type filter with non-matching field",
369 routeFilter: gatewayv1.HTTPRouteFilter{
370 Type: gatewayv1.HTTPRouteFilterURLRewrite,
371 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{},
372 },
373 wantErrors: []string{"filter.requestMirror must be nil if the filter.type is not RequestMirror", "filter.urlRewrite must be specified for URLRewrite filter.type"},
374 },
375 {
376 name: "invalid HTTPRouteFilterURLRewrite type filter with empty value field",
377 routeFilter: gatewayv1.HTTPRouteFilter{
378 Type: gatewayv1.HTTPRouteFilterURLRewrite,
379 },
380 wantErrors: []string{"filter.urlRewrite must be specified for URLRewrite filter.type"},
381 },
382 }
383
384 for _, tc := range tests {
385 t.Run(tc.name, func(t *testing.T) {
386 route := &gatewayv1.HTTPRoute{
387 ObjectMeta: metav1.ObjectMeta{
388 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
389 Namespace: metav1.NamespaceDefault,
390 },
391 Spec: gatewayv1.HTTPRouteSpec{
392 Rules: []gatewayv1.HTTPRouteRule{{
393 Filters: []gatewayv1.HTTPRouteFilter{tc.routeFilter},
394 }},
395 },
396 }
397 validateHTTPRoute(t, route, tc.wantErrors)
398 })
399 }
400 }
401
402 func TestHTTPRouteRule(t *testing.T) {
403 testService := gatewayv1.ObjectName("test-service")
404 tests := []struct {
405 name string
406 wantErrors []string
407 rules []gatewayv1.HTTPRouteRule
408 }{
409 {
410 name: "valid httpRoute with no filters",
411 rules: []gatewayv1.HTTPRouteRule{
412 {
413 Matches: []gatewayv1.HTTPRouteMatch{
414 {
415 Path: &gatewayv1.HTTPPathMatch{
416 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
417 Value: ptrTo("/"),
418 },
419 },
420 },
421 BackendRefs: []gatewayv1.HTTPBackendRef{
422 {
423 BackendRef: gatewayv1.BackendRef{
424 BackendObjectReference: gatewayv1.BackendObjectReference{
425 Name: testService,
426 Port: ptrTo(gatewayv1.PortNumber(8080)),
427 },
428 Weight: ptrTo(int32(100)),
429 },
430 },
431 },
432 },
433 },
434 },
435 {
436 name: "valid httpRoute with 1 filter",
437 rules: []gatewayv1.HTTPRouteRule{
438 {
439 Matches: []gatewayv1.HTTPRouteMatch{
440 {
441 Path: &gatewayv1.HTTPPathMatch{
442 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
443 Value: ptrTo("/"),
444 },
445 },
446 },
447 Filters: []gatewayv1.HTTPRouteFilter{
448 {
449 Type: gatewayv1.HTTPRouteFilterRequestMirror,
450 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{
451 BackendRef: gatewayv1.BackendObjectReference{
452 Name: testService,
453 Port: ptrTo(gatewayv1.PortNumber(8081)),
454 },
455 },
456 },
457 },
458 },
459 },
460 },
461 {
462 name: "valid httpRoute with duplicate ExtensionRef filters",
463 rules: []gatewayv1.HTTPRouteRule{
464 {
465 Matches: []gatewayv1.HTTPRouteMatch{
466 {
467 Path: &gatewayv1.HTTPPathMatch{
468 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
469 Value: ptrTo("/"),
470 },
471 },
472 },
473 Filters: []gatewayv1.HTTPRouteFilter{
474 {
475 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
476 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
477 Set: []gatewayv1.HTTPHeader{
478 {
479 Name: "special-header",
480 Value: "foo",
481 },
482 },
483 },
484 },
485 {
486 Type: gatewayv1.HTTPRouteFilterRequestMirror,
487 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{
488 BackendRef: gatewayv1.BackendObjectReference{
489 Name: testService,
490 Port: ptrTo(gatewayv1.PortNumber(8080)),
491 },
492 },
493 },
494 {
495 Type: "ExtensionRef",
496 ExtensionRef: &gatewayv1.LocalObjectReference{
497 Kind: "Service",
498 Name: "test",
499 },
500 },
501 {
502 Type: "ExtensionRef",
503 ExtensionRef: &gatewayv1.LocalObjectReference{
504 Kind: "Service",
505 Name: "test",
506 },
507 },
508 {
509 Type: "ExtensionRef",
510 ExtensionRef: &gatewayv1.LocalObjectReference{
511 Kind: "Service",
512 Name: "test",
513 },
514 },
515 },
516 },
517 },
518 },
519 {
520 name: "valid redirect path modifier",
521 rules: []gatewayv1.HTTPRouteRule{
522 {
523 Filters: []gatewayv1.HTTPRouteFilter{
524 {
525 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
526 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
527 Path: &gatewayv1.HTTPPathModifier{
528 Type: gatewayv1.FullPathHTTPPathModifier,
529 ReplaceFullPath: ptrTo("foo"),
530 },
531 },
532 },
533 },
534 },
535 },
536 },
537 {
538 name: "valid rewrite path modifier",
539 rules: []gatewayv1.HTTPRouteRule{{
540 Matches: []gatewayv1.HTTPRouteMatch{{
541 Path: &gatewayv1.HTTPPathMatch{
542 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
543 Value: ptrTo("/bar"),
544 },
545 }},
546 Filters: []gatewayv1.HTTPRouteFilter{{
547 Type: gatewayv1.HTTPRouteFilterURLRewrite,
548 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
549 Path: &gatewayv1.HTTPPathModifier{
550 Type: gatewayv1.PrefixMatchHTTPPathModifier,
551 ReplacePrefixMatch: ptrTo("foo"),
552 },
553 },
554 }},
555 }},
556 },
557 {
558 name: "multiple actions for different request headers",
559 rules: []gatewayv1.HTTPRouteRule{{
560 Filters: []gatewayv1.HTTPRouteFilter{{
561 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
562 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
563 Add: []gatewayv1.HTTPHeader{
564 {
565 Name: gatewayv1.HTTPHeaderName("x-vegetable"),
566 Value: "carrot",
567 },
568 {
569 Name: gatewayv1.HTTPHeaderName("x-grain"),
570 Value: "rye",
571 },
572 },
573 Set: []gatewayv1.HTTPHeader{
574 {
575 Name: gatewayv1.HTTPHeaderName("x-fruit"),
576 Value: "watermelon",
577 },
578 {
579 Name: gatewayv1.HTTPHeaderName("x-spice"),
580 Value: "coriander",
581 },
582 },
583 },
584 }},
585 }},
586 },
587 {
588 name: "multiple actions for different response headers",
589 rules: []gatewayv1.HTTPRouteRule{{
590 Filters: []gatewayv1.HTTPRouteFilter{{
591 Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier,
592 ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{
593 Add: []gatewayv1.HTTPHeader{{
594 Name: gatewayv1.HTTPHeaderName("x-example"),
595 Value: "blueberry",
596 }},
597 Set: []gatewayv1.HTTPHeader{{
598 Name: gatewayv1.HTTPHeaderName("x-different"),
599 Value: "turnip",
600 }},
601 },
602 }},
603 }},
604 },
605 {
606 name: "backendref with request redirect httpRoute filter",
607 wantErrors: []string{"RequestRedirect filter must not be used together with backendRefs"},
608 rules: []gatewayv1.HTTPRouteRule{
609 {
610 Filters: []gatewayv1.HTTPRouteFilter{
611 {
612 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
613 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
614 Scheme: ptrTo("https"),
615 StatusCode: ptrTo(301),
616 },
617 },
618 },
619 BackendRefs: []gatewayv1.HTTPBackendRef{
620 {
621 BackendRef: gatewayv1.BackendRef{
622 BackendObjectReference: gatewayv1.BackendObjectReference{
623 Name: testService,
624 Port: ptrTo(gatewayv1.PortNumber(80)),
625 },
626 },
627 },
628 },
629 },
630 },
631 },
632 {
633 name: "request redirect without backendref in httpRoute filter",
634 rules: []gatewayv1.HTTPRouteRule{
635 {
636 Filters: []gatewayv1.HTTPRouteFilter{
637 {
638 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
639 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
640 Scheme: ptrTo("https"),
641 StatusCode: ptrTo(301),
642 },
643 },
644 },
645 },
646 },
647 },
648 {
649 name: "backendref without request redirect filter",
650 rules: []gatewayv1.HTTPRouteRule{
651 {
652 Filters: []gatewayv1.HTTPRouteFilter{
653 {
654 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
655 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
656 Set: []gatewayv1.HTTPHeader{{Name: "name", Value: "foo"}},
657 },
658 },
659 },
660 BackendRefs: []gatewayv1.HTTPBackendRef{
661 {
662 BackendRef: gatewayv1.BackendRef{
663 BackendObjectReference: gatewayv1.BackendObjectReference{
664 Name: testService,
665 Port: ptrTo(gatewayv1.PortNumber(80)),
666 },
667 },
668 },
669 },
670 },
671 },
672 },
673 {
674 name: "backendref without any filter",
675 rules: []gatewayv1.HTTPRouteRule{
676 {
677 BackendRefs: []gatewayv1.HTTPBackendRef{
678 {
679 BackendRef: gatewayv1.BackendRef{
680 BackendObjectReference: gatewayv1.BackendObjectReference{
681 Name: testService,
682 Port: ptrTo(gatewayv1.PortNumber(80)),
683 },
684 },
685 },
686 },
687 },
688 },
689 },
690 {
691 name: "valid use of URLRewrite filter",
692 rules: []gatewayv1.HTTPRouteRule{{
693 Matches: []gatewayv1.HTTPRouteMatch{
694 {
695 Path: &gatewayv1.HTTPPathMatch{
696 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
697 Value: ptrTo("/foo"),
698 },
699 },
700 },
701 Filters: []gatewayv1.HTTPRouteFilter{{
702 Type: gatewayv1.HTTPRouteFilterURLRewrite,
703 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
704 Path: &gatewayv1.HTTPPathModifier{
705 Type: gatewayv1.PrefixMatchHTTPPathModifier,
706 ReplacePrefixMatch: ptrTo("foo"),
707 },
708 },
709 }},
710 }},
711 },
712 {
713 name: "invalid URLRewrite filter because too many path matches",
714 wantErrors: []string{"When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
715 rules: []gatewayv1.HTTPRouteRule{{
716 Matches: []gatewayv1.HTTPRouteMatch{
717 {
718 Path: &gatewayv1.HTTPPathMatch{
719 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
720 Value: ptrTo("/foo"),
721 },
722 },
723 {
724 Path: &gatewayv1.HTTPPathMatch{
725 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
726 Value: ptrTo("/bar"),
727 },
728 },
729 },
730 Filters: []gatewayv1.HTTPRouteFilter{{
731 Type: gatewayv1.HTTPRouteFilterURLRewrite,
732 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
733 Path: &gatewayv1.HTTPPathModifier{
734 Type: gatewayv1.PrefixMatchHTTPPathModifier,
735 ReplacePrefixMatch: ptrTo("foo"),
736 },
737 },
738 }},
739 }},
740 },
741 {
742 name: "invalid URLRewrite filter because too many path matches",
743 wantErrors: []string{"When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
744 rules: []gatewayv1.HTTPRouteRule{{
745 Matches: []gatewayv1.HTTPRouteMatch{
746 {
747 Path: &gatewayv1.HTTPPathMatch{
748 Type: ptrTo(gatewayv1.PathMatchType(gatewayv1.FullPathHTTPPathModifier)),
749 Value: ptrTo("/foo"),
750 },
751 },
752 },
753 Filters: []gatewayv1.HTTPRouteFilter{{
754 Type: gatewayv1.HTTPRouteFilterURLRewrite,
755 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
756 Path: &gatewayv1.HTTPPathModifier{
757 Type: gatewayv1.PrefixMatchHTTPPathModifier,
758 ReplacePrefixMatch: ptrTo("foo"),
759 },
760 },
761 }},
762 }},
763 },
764 {
765 name: "valid use of RequestRedirect filter",
766 rules: []gatewayv1.HTTPRouteRule{{
767 Matches: []gatewayv1.HTTPRouteMatch{
768 {
769 Path: &gatewayv1.HTTPPathMatch{
770 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
771 Value: ptrTo("/foo"),
772 },
773 },
774 },
775 Filters: []gatewayv1.HTTPRouteFilter{{
776 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
777 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
778 Path: &gatewayv1.HTTPPathModifier{
779 Type: gatewayv1.PrefixMatchHTTPPathModifier,
780 ReplacePrefixMatch: ptrTo("foo"),
781 },
782 },
783 }},
784 }},
785 },
786 {
787 name: "invalid RequestRedirect filter because too many path matches",
788 wantErrors: []string{"When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
789 rules: []gatewayv1.HTTPRouteRule{{
790 Matches: []gatewayv1.HTTPRouteMatch{
791 {
792 Path: &gatewayv1.HTTPPathMatch{
793 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
794 Value: ptrTo("/foo"),
795 },
796 },
797 {
798 Path: &gatewayv1.HTTPPathMatch{
799 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
800 Value: ptrTo("/bar"),
801 },
802 },
803 },
804 Filters: []gatewayv1.HTTPRouteFilter{{
805 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
806 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
807 Path: &gatewayv1.HTTPPathModifier{
808 Type: gatewayv1.PrefixMatchHTTPPathModifier,
809 ReplacePrefixMatch: ptrTo("foo"),
810 },
811 },
812 }},
813 }},
814 },
815 {
816 name: "invalid RequestRedirect filter because path match has type ReplaceFullPath",
817 wantErrors: []string{"When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
818 rules: []gatewayv1.HTTPRouteRule{{
819 Matches: []gatewayv1.HTTPRouteMatch{
820 {
821 Path: &gatewayv1.HTTPPathMatch{
822 Type: ptrTo(gatewayv1.PathMatchType(gatewayv1.FullPathHTTPPathModifier)),
823 Value: ptrTo("/foo"),
824 },
825 },
826 },
827 Filters: []gatewayv1.HTTPRouteFilter{{
828 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
829 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
830 Path: &gatewayv1.HTTPPathModifier{
831 Type: gatewayv1.PrefixMatchHTTPPathModifier,
832 ReplacePrefixMatch: ptrTo("foo"),
833 },
834 },
835 }},
836 }},
837 },
838 {
839 name: "valid use of URLRewrite filter (within backendRefs)",
840 rules: []gatewayv1.HTTPRouteRule{{
841 Matches: []gatewayv1.HTTPRouteMatch{
842 {
843 Path: &gatewayv1.HTTPPathMatch{
844 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
845 Value: ptrTo("/foo"),
846 },
847 },
848 },
849 BackendRefs: []gatewayv1.HTTPBackendRef{
850 {
851 BackendRef: gatewayv1.BackendRef{
852 BackendObjectReference: gatewayv1.BackendObjectReference{
853 Name: testService,
854 Port: ptrTo(gatewayv1.PortNumber(80)),
855 },
856 },
857 Filters: []gatewayv1.HTTPRouteFilter{{
858 Type: gatewayv1.HTTPRouteFilterURLRewrite,
859 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
860 Path: &gatewayv1.HTTPPathModifier{
861 Type: gatewayv1.PrefixMatchHTTPPathModifier,
862 ReplacePrefixMatch: ptrTo("foo"),
863 },
864 },
865 }},
866 },
867 },
868 }},
869 },
870 {
871 name: "invalid URLRewrite filter (within backendRefs) because too many path matches",
872 wantErrors: []string{"Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
873 rules: []gatewayv1.HTTPRouteRule{{
874 Matches: []gatewayv1.HTTPRouteMatch{
875 {
876 Path: &gatewayv1.HTTPPathMatch{
877 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
878 Value: ptrTo("/foo"),
879 },
880 },
881 {
882 Path: &gatewayv1.HTTPPathMatch{
883 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
884 Value: ptrTo("/bar"),
885 },
886 },
887 },
888 BackendRefs: []gatewayv1.HTTPBackendRef{
889 {
890 BackendRef: gatewayv1.BackendRef{
891 BackendObjectReference: gatewayv1.BackendObjectReference{
892 Name: testService,
893 Port: ptrTo(gatewayv1.PortNumber(80)),
894 },
895 },
896 Filters: []gatewayv1.HTTPRouteFilter{{
897 Type: gatewayv1.HTTPRouteFilterURLRewrite,
898 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
899 Path: &gatewayv1.HTTPPathModifier{
900 Type: gatewayv1.PrefixMatchHTTPPathModifier,
901 ReplacePrefixMatch: ptrTo("foo"),
902 },
903 },
904 }},
905 },
906 },
907 }},
908 },
909 {
910 name: "invalid URLRewrite filter (within backendRefs) because path match has type ReplaceFullPath",
911 wantErrors: []string{"Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
912 rules: []gatewayv1.HTTPRouteRule{{
913 Matches: []gatewayv1.HTTPRouteMatch{
914 {
915 Path: &gatewayv1.HTTPPathMatch{
916 Type: ptrTo(gatewayv1.PathMatchType(gatewayv1.FullPathHTTPPathModifier)),
917 Value: ptrTo("/foo"),
918 },
919 },
920 },
921 BackendRefs: []gatewayv1.HTTPBackendRef{
922 {
923 BackendRef: gatewayv1.BackendRef{
924 BackendObjectReference: gatewayv1.BackendObjectReference{
925 Name: testService,
926 Port: ptrTo(gatewayv1.PortNumber(80)),
927 },
928 },
929 Filters: []gatewayv1.HTTPRouteFilter{{
930 Type: gatewayv1.HTTPRouteFilterURLRewrite,
931 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
932 Path: &gatewayv1.HTTPPathModifier{
933 Type: gatewayv1.PrefixMatchHTTPPathModifier,
934 ReplacePrefixMatch: ptrTo("foo"),
935 },
936 },
937 }},
938 },
939 },
940 }},
941 },
942 {
943 name: "valid use of RequestRedirect filter (within backendRefs)",
944 rules: []gatewayv1.HTTPRouteRule{{
945 Matches: []gatewayv1.HTTPRouteMatch{
946 {
947 Path: &gatewayv1.HTTPPathMatch{
948 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
949 Value: ptrTo("/foo"),
950 },
951 },
952 },
953 BackendRefs: []gatewayv1.HTTPBackendRef{
954 {
955 BackendRef: gatewayv1.BackendRef{
956 BackendObjectReference: gatewayv1.BackendObjectReference{
957 Name: testService,
958 Port: ptrTo(gatewayv1.PortNumber(80)),
959 },
960 },
961 Filters: []gatewayv1.HTTPRouteFilter{{
962 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
963 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
964 Path: &gatewayv1.HTTPPathModifier{
965 Type: gatewayv1.PrefixMatchHTTPPathModifier,
966 ReplacePrefixMatch: ptrTo("foo"),
967 },
968 },
969 }},
970 },
971 },
972 }},
973 },
974 {
975 name: "invalid RequestRedirect filter (within backendRefs) because too many path matches",
976 wantErrors: []string{"Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
977 rules: []gatewayv1.HTTPRouteRule{{
978 Matches: []gatewayv1.HTTPRouteMatch{
979 {
980 Path: &gatewayv1.HTTPPathMatch{
981 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
982 Value: ptrTo("/foo"),
983 },
984 },
985 {
986 Path: &gatewayv1.HTTPPathMatch{
987 Type: ptrTo(gatewayv1.PathMatchPathPrefix),
988 Value: ptrTo("/bar"),
989 },
990 },
991 },
992 BackendRefs: []gatewayv1.HTTPBackendRef{
993 {
994 BackendRef: gatewayv1.BackendRef{
995 BackendObjectReference: gatewayv1.BackendObjectReference{
996 Name: testService,
997 Port: ptrTo(gatewayv1.PortNumber(80)),
998 },
999 },
1000 Filters: []gatewayv1.HTTPRouteFilter{{
1001 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1002 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1003 Path: &gatewayv1.HTTPPathModifier{
1004 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1005 ReplacePrefixMatch: ptrTo("foo"),
1006 },
1007 },
1008 }},
1009 },
1010 },
1011 }},
1012 },
1013 {
1014 name: "invalid RequestRedirect filter (within backendRefs) because path match has type ReplaceFullPath",
1015 wantErrors: []string{"Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified"},
1016 rules: []gatewayv1.HTTPRouteRule{{
1017 Matches: []gatewayv1.HTTPRouteMatch{
1018 {
1019 Path: &gatewayv1.HTTPPathMatch{
1020 Type: ptrTo(gatewayv1.PathMatchType(gatewayv1.FullPathHTTPPathModifier)),
1021 Value: ptrTo("/foo"),
1022 },
1023 },
1024 },
1025 BackendRefs: []gatewayv1.HTTPBackendRef{
1026 {
1027 BackendRef: gatewayv1.BackendRef{
1028 BackendObjectReference: gatewayv1.BackendObjectReference{
1029 Name: testService,
1030 Port: ptrTo(gatewayv1.PortNumber(80)),
1031 },
1032 },
1033 Filters: []gatewayv1.HTTPRouteFilter{{
1034 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1035 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1036 Path: &gatewayv1.HTTPPathModifier{
1037 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1038 ReplacePrefixMatch: ptrTo("foo"),
1039 },
1040 },
1041 }},
1042 },
1043 },
1044 }},
1045 },
1046 {
1047 name: "rewrite and redirect filters combined (invalid)",
1048 wantErrors: []string{"May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both"},
1049 rules: []gatewayv1.HTTPRouteRule{{
1050 Filters: []gatewayv1.HTTPRouteFilter{{
1051 Type: gatewayv1.HTTPRouteFilterURLRewrite,
1052 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
1053 Path: &gatewayv1.HTTPPathModifier{
1054 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1055 ReplacePrefixMatch: ptrTo("foo"),
1056 },
1057 },
1058 }, {
1059 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1060 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1061 Path: &gatewayv1.HTTPPathModifier{
1062 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1063 ReplacePrefixMatch: ptrTo("foo"),
1064 },
1065 },
1066 }},
1067 }},
1068 },
1069 {
1070 name: "invalid because repeated URLRewrite filter",
1071 wantErrors: []string{"URLRewrite filter cannot be repeated"},
1072 rules: []gatewayv1.HTTPRouteRule{
1073 {
1074 Matches: []gatewayv1.HTTPRouteMatch{
1075 {
1076 Path: &gatewayv1.HTTPPathMatch{
1077 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
1078 Value: ptrTo("/"),
1079 },
1080 },
1081 },
1082 Filters: []gatewayv1.HTTPRouteFilter{
1083 {
1084 Type: gatewayv1.HTTPRouteFilterURLRewrite,
1085 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
1086 Path: &gatewayv1.HTTPPathModifier{
1087 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1088 ReplacePrefixMatch: ptrTo("foo"),
1089 },
1090 },
1091 },
1092 {
1093 Type: gatewayv1.HTTPRouteFilterURLRewrite,
1094 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
1095 Path: &gatewayv1.HTTPPathModifier{
1096 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1097 ReplacePrefixMatch: ptrTo("bar"),
1098 },
1099 },
1100 },
1101 },
1102 },
1103 },
1104 },
1105 {
1106 name: "invalid because repeated RequestHeaderModifier filter among mix of filters",
1107 wantErrors: []string{"RequestHeaderModifier filter cannot be repeated"},
1108 rules: []gatewayv1.HTTPRouteRule{
1109 {
1110 Matches: []gatewayv1.HTTPRouteMatch{
1111 {
1112 Path: &gatewayv1.HTTPPathMatch{
1113 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
1114 Value: ptrTo("/"),
1115 },
1116 },
1117 },
1118 Filters: []gatewayv1.HTTPRouteFilter{
1119 {
1120 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
1121 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
1122 Set: []gatewayv1.HTTPHeader{
1123 {
1124 Name: "special-header",
1125 Value: "foo",
1126 },
1127 },
1128 },
1129 },
1130 {
1131 Type: gatewayv1.HTTPRouteFilterRequestMirror,
1132 RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{
1133 BackendRef: gatewayv1.BackendObjectReference{
1134 Name: testService,
1135 Port: ptrTo(gatewayv1.PortNumber(8080)),
1136 },
1137 },
1138 },
1139 {
1140 Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
1141 RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
1142 Add: []gatewayv1.HTTPHeader{
1143 {
1144 Name: "my-header",
1145 Value: "bar",
1146 },
1147 },
1148 },
1149 },
1150 },
1151 },
1152 },
1153 },
1154 {
1155 name: "invalid because multiple filters are repeated",
1156 wantErrors: []string{"ResponseHeaderModifier filter cannot be repeated", "RequestRedirect filter cannot be repeated"},
1157 rules: []gatewayv1.HTTPRouteRule{
1158 {
1159 Matches: []gatewayv1.HTTPRouteMatch{
1160 {
1161 Path: &gatewayv1.HTTPPathMatch{
1162 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
1163 Value: ptrTo("/"),
1164 },
1165 },
1166 },
1167 Filters: []gatewayv1.HTTPRouteFilter{
1168 {
1169 Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier,
1170 ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{
1171 Set: []gatewayv1.HTTPHeader{
1172 {
1173 Name: "special-header",
1174 Value: "foo",
1175 },
1176 },
1177 },
1178 },
1179 {
1180 Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier,
1181 ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{
1182 Add: []gatewayv1.HTTPHeader{
1183 {
1184 Name: "my-header",
1185 Value: "bar",
1186 },
1187 },
1188 },
1189 },
1190 {
1191 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1192 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1193 Path: &gatewayv1.HTTPPathModifier{
1194 Type: gatewayv1.FullPathHTTPPathModifier,
1195 ReplaceFullPath: ptrTo("foo"),
1196 },
1197 },
1198 },
1199 {
1200 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1201 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1202 Path: &gatewayv1.HTTPPathModifier{
1203 Type: gatewayv1.FullPathHTTPPathModifier,
1204 ReplaceFullPath: ptrTo("bar"),
1205 },
1206 },
1207 },
1208 },
1209 },
1210 },
1211 },
1212 }
1213
1214 for _, tc := range tests {
1215 t.Run(tc.name, func(t *testing.T) {
1216 route := &gatewayv1.HTTPRoute{
1217 ObjectMeta: metav1.ObjectMeta{
1218 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
1219 Namespace: metav1.NamespaceDefault,
1220 },
1221 Spec: gatewayv1.HTTPRouteSpec{Rules: tc.rules},
1222 }
1223 validateHTTPRoute(t, route, tc.wantErrors)
1224 })
1225 }
1226 }
1227
1228 func TestHTTPBackendRef(t *testing.T) {
1229 testService := gatewayv1.ObjectName("test-service")
1230 tests := []struct {
1231 name string
1232 wantErrors []string
1233 rules []gatewayv1.HTTPRouteRule
1234 }{
1235 {
1236 name: "invalid because repeated URLRewrite filter within backendRefs",
1237 wantErrors: []string{"URLRewrite filter cannot be repeated"},
1238 rules: []gatewayv1.HTTPRouteRule{
1239 {
1240 Matches: []gatewayv1.HTTPRouteMatch{
1241 {
1242 Path: &gatewayv1.HTTPPathMatch{
1243 Type: ptrTo(gatewayv1.PathMatchType("PathPrefix")),
1244 Value: ptrTo("/"),
1245 },
1246 },
1247 },
1248 BackendRefs: []gatewayv1.HTTPBackendRef{
1249 {
1250 BackendRef: gatewayv1.BackendRef{
1251 BackendObjectReference: gatewayv1.BackendObjectReference{
1252 Name: testService,
1253 Port: ptrTo(gatewayv1.PortNumber(80)),
1254 },
1255 },
1256 Filters: []gatewayv1.HTTPRouteFilter{
1257 {
1258 Type: gatewayv1.HTTPRouteFilterURLRewrite,
1259 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
1260 Path: &gatewayv1.HTTPPathModifier{
1261 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1262 ReplacePrefixMatch: ptrTo("foo"),
1263 },
1264 },
1265 },
1266 {
1267 Type: gatewayv1.HTTPRouteFilterURLRewrite,
1268 URLRewrite: &gatewayv1.HTTPURLRewriteFilter{
1269 Path: &gatewayv1.HTTPPathModifier{
1270 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1271 ReplacePrefixMatch: ptrTo("bar"),
1272 },
1273 },
1274 },
1275 },
1276 },
1277 },
1278 },
1279 },
1280 },
1281 }
1282
1283 for _, tc := range tests {
1284 t.Run(tc.name, func(t *testing.T) {
1285 route := &gatewayv1.HTTPRoute{
1286 ObjectMeta: metav1.ObjectMeta{
1287 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
1288 Namespace: metav1.NamespaceDefault,
1289 },
1290 Spec: gatewayv1.HTTPRouteSpec{Rules: tc.rules},
1291 }
1292 validateHTTPRoute(t, route, tc.wantErrors)
1293 })
1294 }
1295 }
1296
1297 func TestHTTPPathModifier(t *testing.T) {
1298 tests := []struct {
1299 name string
1300 wantErrors []string
1301 pathModifier gatewayv1.HTTPPathModifier
1302 }{
1303 {
1304 name: "valid ReplaceFullPath",
1305 pathModifier: gatewayv1.HTTPPathModifier{
1306 Type: gatewayv1.FullPathHTTPPathModifier,
1307 ReplaceFullPath: ptrTo("foo"),
1308 },
1309 },
1310 {
1311 name: "replaceFullPath must be specified when type is set to 'ReplaceFullPath'",
1312 wantErrors: []string{"replaceFullPath must be specified when type is set to 'ReplaceFullPath'"},
1313 pathModifier: gatewayv1.HTTPPathModifier{
1314 Type: gatewayv1.FullPathHTTPPathModifier,
1315 },
1316 },
1317 {
1318 name: "type must be 'ReplaceFullPath' when replaceFullPath is set",
1319 wantErrors: []string{"type must be 'ReplaceFullPath' when replaceFullPath is set"},
1320 pathModifier: gatewayv1.HTTPPathModifier{
1321 ReplaceFullPath: ptrTo("foo"),
1322 },
1323 },
1324 {
1325 name: "valid ReplacePrefixMatch",
1326 pathModifier: gatewayv1.HTTPPathModifier{
1327 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1328 ReplacePrefixMatch: ptrTo("/foo"),
1329 },
1330 },
1331 {
1332 name: "replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch'",
1333 wantErrors: []string{"replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch'"},
1334 pathModifier: gatewayv1.HTTPPathModifier{
1335 Type: gatewayv1.PrefixMatchHTTPPathModifier,
1336 },
1337 },
1338 {
1339 name: "type must be 'ReplacePrefixMatch' when replacePrefixMatch is set",
1340 wantErrors: []string{"type must be 'ReplacePrefixMatch' when replacePrefixMatch is set"},
1341 pathModifier: gatewayv1.HTTPPathModifier{
1342 ReplacePrefixMatch: ptrTo("/foo"),
1343 },
1344 },
1345 }
1346
1347 for _, tc := range tests {
1348 t.Run(tc.name, func(t *testing.T) {
1349 route := &gatewayv1.HTTPRoute{
1350 ObjectMeta: metav1.ObjectMeta{
1351 Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
1352 Namespace: metav1.NamespaceDefault,
1353 },
1354 Spec: gatewayv1.HTTPRouteSpec{
1355 Rules: []gatewayv1.HTTPRouteRule{
1356 {
1357 Filters: []gatewayv1.HTTPRouteFilter{
1358 {
1359 Type: gatewayv1.HTTPRouteFilterRequestRedirect,
1360 RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
1361 Path: &tc.pathModifier,
1362 },
1363 },
1364 },
1365 },
1366 },
1367 },
1368 }
1369 validateHTTPRoute(t, route, tc.wantErrors)
1370 })
1371 }
1372 }
1373
1374 func validateHTTPRoute(t *testing.T, route *gatewayv1.HTTPRoute, wantErrors []string) {
1375 t.Helper()
1376
1377 ctx := context.Background()
1378 err := k8sClient.Create(ctx, route)
1379
1380 if (len(wantErrors) != 0) != (err != nil) {
1381 t.Fatalf("Unexpected response while creating HTTPRoute %q; got err=\n%v\n;want error=%v", fmt.Sprintf("%v/%v", route.Namespace, route.Name), err, wantErrors)
1382 }
1383
1384 var missingErrorStrings []string
1385 for _, wantError := range wantErrors {
1386 if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) {
1387 missingErrorStrings = append(missingErrorStrings, wantError)
1388 }
1389 }
1390 if len(missingErrorStrings) != 0 {
1391 t.Errorf("Unexpected response while creating HTTPRoute %q; got err=\n%v\n;missing strings within error=%q", fmt.Sprintf("%v/%v", route.Namespace, route.Name), err, missingErrorStrings)
1392 }
1393 }
1394
View as plain text