|
1 | 1 | package parser |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "strings" |
5 | 4 | "testing" |
6 | 5 |
|
7 | 6 | "github.com/go-logr/zapr" |
@@ -41,6 +40,120 @@ type testCaseIngressRulesFromHTTPRoutes struct { |
41 | 40 | errs []error |
42 | 41 | } |
43 | 42 |
|
| 43 | +func TestValidateHTTPRoute(t *testing.T) { |
| 44 | + testCases := []struct { |
| 45 | + name string |
| 46 | + httpRoute *gatewayapi.HTTPRoute |
| 47 | + expressionRoutes bool |
| 48 | + expectedError error |
| 49 | + }{ |
| 50 | + { |
| 51 | + name: "valid HTTPRoute should pass the validation", |
| 52 | + httpRoute: &gatewayapi.HTTPRoute{ |
| 53 | + ObjectMeta: metav1.ObjectMeta{ |
| 54 | + Name: "basic-httproute", |
| 55 | + Namespace: corev1.NamespaceDefault, |
| 56 | + }, |
| 57 | + Spec: gatewayapi.HTTPRouteSpec{ |
| 58 | + CommonRouteSpec: commonRouteSpecMock("fake-gateway-1"), |
| 59 | + Hostnames: []gatewayapi.Hostname{ |
| 60 | + "konghq.com", |
| 61 | + "www.konghq.com", |
| 62 | + }, |
| 63 | + Rules: []gatewayapi.HTTPRouteRule{{ |
| 64 | + BackendRefs: []gatewayapi.HTTPBackendRef{ |
| 65 | + builder.NewHTTPBackendRef("fake-service").WithPort(80).Build(), |
| 66 | + }, |
| 67 | + }}, |
| 68 | + }, |
| 69 | + }, |
| 70 | + expressionRoutes: false, |
| 71 | + expectedError: nil, |
| 72 | + }, |
| 73 | + { |
| 74 | + name: "HTTPRoute with no rules should not pass the validation", |
| 75 | + httpRoute: &gatewayapi.HTTPRoute{ |
| 76 | + ObjectMeta: metav1.ObjectMeta{ |
| 77 | + Name: "httproute-no-rule", |
| 78 | + Namespace: corev1.NamespaceDefault, |
| 79 | + }, |
| 80 | + Spec: gatewayapi.HTTPRouteSpec{ |
| 81 | + CommonRouteSpec: commonRouteSpecMock("fake-gateway-1"), |
| 82 | + Hostnames: []gatewayapi.Hostname{ |
| 83 | + "konghq.com", |
| 84 | + "www.konghq.com", |
| 85 | + }, |
| 86 | + }, |
| 87 | + }, |
| 88 | + expressionRoutes: false, |
| 89 | + expectedError: translators.ErrRouteValidationNoRules, |
| 90 | + }, |
| 91 | + { |
| 92 | + name: "HTTPRoute with query param match should pass validation with expression routes", |
| 93 | + httpRoute: &gatewayapi.HTTPRoute{ |
| 94 | + ObjectMeta: metav1.ObjectMeta{ |
| 95 | + Name: "httproute-query-param-match", |
| 96 | + Namespace: corev1.NamespaceDefault, |
| 97 | + }, |
| 98 | + Spec: gatewayapi.HTTPRouteSpec{ |
| 99 | + CommonRouteSpec: commonRouteSpecMock("fake-gateway-1"), |
| 100 | + Hostnames: []gatewayapi.Hostname{ |
| 101 | + "konghq.com", |
| 102 | + "www.konghq.com", |
| 103 | + }, |
| 104 | + Rules: []gatewayapi.HTTPRouteRule{{ |
| 105 | + BackendRefs: []gatewayapi.HTTPBackendRef{ |
| 106 | + builder.NewHTTPBackendRef("fake-service").WithPort(80).Build(), |
| 107 | + }, |
| 108 | + Matches: builder.NewHTTPRouteMatch().WithQueryParam("foo", "bar").ToSlice(), |
| 109 | + }}, |
| 110 | + }, |
| 111 | + }, |
| 112 | + expressionRoutes: true, |
| 113 | + expectedError: nil, |
| 114 | + }, |
| 115 | + { |
| 116 | + name: "HTTPRoute with query param match should not pass validation when expression routes disabled", |
| 117 | + httpRoute: &gatewayapi.HTTPRoute{ |
| 118 | + ObjectMeta: metav1.ObjectMeta{ |
| 119 | + Name: "httproute-query-param-match", |
| 120 | + Namespace: corev1.NamespaceDefault, |
| 121 | + }, |
| 122 | + Spec: gatewayapi.HTTPRouteSpec{ |
| 123 | + CommonRouteSpec: commonRouteSpecMock("fake-gateway-1"), |
| 124 | + Hostnames: []gatewayapi.Hostname{ |
| 125 | + "konghq.com", |
| 126 | + "www.konghq.com", |
| 127 | + }, |
| 128 | + Rules: []gatewayapi.HTTPRouteRule{{ |
| 129 | + BackendRefs: []gatewayapi.HTTPBackendRef{ |
| 130 | + builder.NewHTTPBackendRef("fake-service").WithPort(80).Build(), |
| 131 | + }, |
| 132 | + Matches: builder.NewHTTPRouteMatch().WithQueryParam("foo", "bar").ToSlice(), |
| 133 | + }}, |
| 134 | + }, |
| 135 | + }, |
| 136 | + expressionRoutes: false, |
| 137 | + expectedError: translators.ErrRouteValidationQueryParamMatchesUnsupported, |
| 138 | + }, |
| 139 | + } |
| 140 | + |
| 141 | + for _, tc := range testCases { |
| 142 | + tc := tc |
| 143 | + t.Run(tc.name, func(t *testing.T) { |
| 144 | + featureFlags := FeatureFlags{ |
| 145 | + ExpressionRoutes: tc.expressionRoutes, |
| 146 | + } |
| 147 | + err := validateHTTPRoute(tc.httpRoute, featureFlags) |
| 148 | + if tc.expectedError == nil { |
| 149 | + require.NoError(t, err, "should pass the validation") |
| 150 | + } else { |
| 151 | + require.ErrorIs(t, err, tc.expectedError, "should return expected error") |
| 152 | + } |
| 153 | + }) |
| 154 | + } |
| 155 | +} |
| 156 | + |
44 | 157 | func TestIngressRulesFromHTTPRoutes(t *testing.T) { |
45 | 158 | fakestore, err := store.NewFakeStore(store.FakeObjects{}) |
46 | 159 | require.NoError(t, err) |
@@ -257,58 +370,6 @@ func TestIngressRulesFromHTTPRoutes(t *testing.T) { |
257 | 370 | } |
258 | 371 | }, |
259 | 372 | }, |
260 | | - { |
261 | | - msg: "an HTTPRoute with no rules can't be routed", |
262 | | - routes: []*gatewayapi.HTTPRoute{{ |
263 | | - ObjectMeta: metav1.ObjectMeta{ |
264 | | - Name: "basic-httproute", |
265 | | - Namespace: corev1.NamespaceDefault, |
266 | | - }, |
267 | | - Spec: gatewayapi.HTTPRouteSpec{ |
268 | | - CommonRouteSpec: commonRouteSpecMock("fake-gateway"), |
269 | | - }, |
270 | | - }}, |
271 | | - expected: func(routes []*gatewayapi.HTTPRoute) ingressRules { |
272 | | - return ingressRules{ |
273 | | - SecretNameToSNIs: newSecretNameToSNIs(), |
274 | | - ServiceNameToParent: map[string]client.Object{}, |
275 | | - ServiceNameToServices: make(map[string]kongstate.Service), |
276 | | - } |
277 | | - }, |
278 | | - errs: []error{ |
279 | | - translators.ErrRouteValidationNoRules, |
280 | | - }, |
281 | | - }, |
282 | | - { |
283 | | - msg: "an HTTPRoute with queryParam matches is not yet supported", |
284 | | - routes: []*gatewayapi.HTTPRoute{{ |
285 | | - ObjectMeta: metav1.ObjectMeta{ |
286 | | - Name: "basic-httproute", |
287 | | - Namespace: corev1.NamespaceDefault, |
288 | | - }, |
289 | | - Spec: gatewayapi.HTTPRouteSpec{ |
290 | | - CommonRouteSpec: commonRouteSpecMock("fake-gateway"), |
291 | | - Rules: []gatewayapi.HTTPRouteRule{{ |
292 | | - Matches: []gatewayapi.HTTPRouteMatch{ |
293 | | - builder.NewHTTPRouteMatch().WithQueryParam("username", "kong").Build(), |
294 | | - }, |
295 | | - BackendRefs: []gatewayapi.HTTPBackendRef{ |
296 | | - builder.NewHTTPBackendRef("fake-service").WithPort(80).Build(), |
297 | | - }, |
298 | | - }}, |
299 | | - }, |
300 | | - }}, |
301 | | - expected: func(routes []*gatewayapi.HTTPRoute) ingressRules { |
302 | | - return ingressRules{ |
303 | | - SecretNameToSNIs: newSecretNameToSNIs(), |
304 | | - ServiceNameToParent: map[string]client.Object{}, |
305 | | - ServiceNameToServices: make(map[string]kongstate.Service), |
306 | | - } |
307 | | - }, |
308 | | - errs: []error{ |
309 | | - translators.ErrRouteValidationQueryParamMatchesUnsupported, |
310 | | - }, |
311 | | - }, |
312 | 373 | { |
313 | 374 | msg: "an HTTPRoute with regex path matches is supported", |
314 | 375 | routes: []*gatewayapi.HTTPRoute{{ |
@@ -1487,17 +1548,11 @@ func TestIngressRulesFromHTTPRoutesUsingExpressionRoutes(t *testing.T) { |
1487 | 1548 | parser.featureFlags.ExpressionRoutes = true |
1488 | 1549 | httpRouteTypeMeta := metav1.TypeMeta{Kind: "HTTPRoute", APIVersion: gatewayv1beta1.GroupVersion.String()} |
1489 | 1550 |
|
1490 | | - newResourceFailure := func(reason string, objects ...client.Object) failures.ResourceFailure { |
1491 | | - failure, _ := failures.NewResourceFailure(reason, objects...) |
1492 | | - return failure |
1493 | | - } |
1494 | | - |
1495 | 1551 | testCases := []struct { |
1496 | 1552 | name string |
1497 | 1553 | httpRoutes []*gatewayapi.HTTPRoute |
1498 | 1554 | expectedKongServices []kongstate.Service |
1499 | 1555 | expectedKongRoutes map[string][]kongstate.Route |
1500 | | - expectedFailures []failures.ResourceFailure |
1501 | 1556 | }{ |
1502 | 1557 | { |
1503 | 1558 | name: "single HTTPRoute with no hostname and multiple matches", |
@@ -1744,78 +1799,6 @@ func TestIngressRulesFromHTTPRoutesUsingExpressionRoutes(t *testing.T) { |
1744 | 1799 | }, |
1745 | 1800 | }, |
1746 | 1801 | }, |
1747 | | - { |
1748 | | - name: "multiple HTTPRoutes with translation failures", |
1749 | | - httpRoutes: []*gatewayapi.HTTPRoute{ |
1750 | | - { |
1751 | | - TypeMeta: httpRouteTypeMeta, |
1752 | | - ObjectMeta: metav1.ObjectMeta{ |
1753 | | - Namespace: "default", |
1754 | | - Name: "httproute-no-host-no-rule", |
1755 | | - }, |
1756 | | - Spec: gatewayapi.HTTPRouteSpec{ |
1757 | | - Hostnames: []gatewayapi.Hostname{"no-rule.example"}, |
1758 | | - }, |
1759 | | - }, |
1760 | | - { |
1761 | | - TypeMeta: httpRouteTypeMeta, |
1762 | | - ObjectMeta: metav1.ObjectMeta{ |
1763 | | - Namespace: "default", |
1764 | | - Name: "httproute-1", |
1765 | | - }, |
1766 | | - Spec: gatewayapi.HTTPRouteSpec{ |
1767 | | - Hostnames: []gatewayapi.Hostname{ |
1768 | | - "foo.com", |
1769 | | - }, |
1770 | | - Rules: []gatewayapi.HTTPRouteRule{ |
1771 | | - { |
1772 | | - Matches: []gatewayapi.HTTPRouteMatch{ |
1773 | | - builder.NewHTTPRouteMatch().WithPathExact("/v1/foo").Build(), |
1774 | | - }, |
1775 | | - BackendRefs: []gatewayapi.HTTPBackendRef{ |
1776 | | - builder.NewHTTPBackendRef("service1").WithPort(80).Build(), |
1777 | | - }, |
1778 | | - }, |
1779 | | - }, |
1780 | | - }, |
1781 | | - }, |
1782 | | - }, |
1783 | | - expectedKongServices: []kongstate.Service{ |
1784 | | - { |
1785 | | - Service: kong.Service{ |
1786 | | - Name: kong.String("httproute.default.httproute-1.foo.com.0"), |
1787 | | - }, |
1788 | | - Backends: []kongstate.ServiceBackend{ |
1789 | | - { |
1790 | | - Name: "service1", |
1791 | | - PortDef: kongstate.PortDef{Mode: kongstate.PortModeByNumber, Number: int32(80)}, |
1792 | | - }, |
1793 | | - }, |
1794 | | - }, |
1795 | | - }, |
1796 | | - expectedKongRoutes: map[string][]kongstate.Route{ |
1797 | | - "httproute.default.httproute-1.foo.com.0": { |
1798 | | - { |
1799 | | - Route: kong.Route{ |
1800 | | - Name: kong.String("httproute.default.httproute-1.foo.com.0.0"), |
1801 | | - Expression: kong.String(`(http.host == "foo.com") && (http.path == "/v1/foo")`), |
1802 | | - PreserveHost: kong.Bool(true), |
1803 | | - }, |
1804 | | - Plugins: []kong.Plugin{}, |
1805 | | - ExpressionRoutes: true, |
1806 | | - }, |
1807 | | - }, |
1808 | | - }, |
1809 | | - expectedFailures: []failures.ResourceFailure{ |
1810 | | - newResourceFailure(translators.ErrRouteValidationNoRules.Error(), &gatewayapi.HTTPRoute{ |
1811 | | - TypeMeta: httpRouteTypeMeta, |
1812 | | - ObjectMeta: metav1.ObjectMeta{ |
1813 | | - Namespace: "default", |
1814 | | - Name: "httproute-no-host-no-rule", |
1815 | | - }, |
1816 | | - }), |
1817 | | - }, |
1818 | | - }, |
1819 | 1802 | } |
1820 | 1803 |
|
1821 | 1804 | for _, tc := range testCases { |
@@ -1847,15 +1830,6 @@ func TestIngressRulesFromHTTPRoutesUsingExpressionRoutes(t *testing.T) { |
1847 | 1830 | require.Equal(t, *expectedRoute.Expression, *r.Expression) |
1848 | 1831 | } |
1849 | 1832 | } |
1850 | | - // check translation failures |
1851 | | - translationFailures := failureCollector.PopResourceFailures() |
1852 | | - require.Equal(t, len(tc.expectedFailures), len(translationFailures)) |
1853 | | - for _, expectedTranslationFailure := range tc.expectedFailures { |
1854 | | - expectedFailureMessage := expectedTranslationFailure.Message() |
1855 | | - require.True(t, lo.ContainsBy(translationFailures, func(failure failures.ResourceFailure) bool { |
1856 | | - return strings.Contains(failure.Message(), expectedFailureMessage) |
1857 | | - })) |
1858 | | - } |
1859 | 1833 | }) |
1860 | 1834 | } |
1861 | 1835 | } |
|
0 commit comments