diff --git a/internal/dataplane/kongstate/route_test.go b/internal/dataplane/kongstate/route_test.go index 15faa322f6..efa732a1ea 100644 --- a/internal/dataplane/kongstate/route_test.go +++ b/internal/dataplane/kongstate/route_test.go @@ -2,6 +2,7 @@ package kongstate import ( "reflect" + "strconv" "testing" "github.com/kong/go-kong/kong" @@ -225,6 +226,95 @@ func TestOverrideRoute(t *testing.T) { }) } +func TestOverrideExpressionRoute(t *testing.T) { + testCases := []struct { + name string + inRoute Route + outRoute Route + }{ + { + name: "protoocls should be overriden, but hosts, method, headers, snis should not", + inRoute: Route{ + Route: kong.Route{ + Name: kong.String("expression-route-1"), + Expression: kong.String(`(http.host == "foo.com") && (http.path ^= "/v1/api")`), + }, + Ingress: util.K8sObjectInfo{ + Annotations: map[string]string{ + "konghq.com/protocols": "https", + "konghq.com/method": "GET", + "konghq.com/host-aliases": "bar.com", + "konghq.com/headers.foo": "bar", + "kohghq.com/snis": "foo.com,bar.com", + }, + }, + ExpressionRoutes: true, + }, + outRoute: Route{ + Route: kong.Route{ + Name: kong.String("expression-route-1"), + Expression: kong.String(`(http.host == "foo.com") && (http.path ^= "/v1/api")`), + Protocols: kong.StringSlice("https"), + }, + Ingress: util.K8sObjectInfo{ + Annotations: map[string]string{ + "konghq.com/protocols": "https", + "konghq.com/method": "GET", + "konghq.com/host-aliases": "bar.com", + "konghq.com/headers.foo": "bar", + "kohghq.com/snis": "foo.com,bar.com", + }, + }, + ExpressionRoutes: true, + }, + }, + { + name: "request_buffering, response_buffering should be overriden, but regex_priority, path_handling should not", + inRoute: Route{ + Route: kong.Route{ + Name: kong.String("expression-route-2"), + Expression: kong.String(`(http.host == "foo.com") && (http.path ^= "/v1/api")`), + }, + Ingress: util.K8sObjectInfo{ + Annotations: map[string]string{ + "konghq.com/request-buffering": "true", + "konghq.com/response-buffering": "true", + "konghq.com/regex-priority": "100", + "konghq.com/path-handling": "v1", + }, + }, + ExpressionRoutes: true, + }, + outRoute: Route{ + Route: kong.Route{ + Name: kong.String("expression-route-2"), + Expression: kong.String(`(http.host == "foo.com") && (http.path ^= "/v1/api")`), + RequestBuffering: kong.Bool(true), + ResponseBuffering: kong.Bool(true), + }, + Ingress: util.K8sObjectInfo{ + Annotations: map[string]string{ + "konghq.com/request-buffering": "true", + "konghq.com/response-buffering": "true", + "konghq.com/regex-priority": "100", + "konghq.com/path-handling": "v1", + }, + }, + ExpressionRoutes: true, + }, + }, + } + + for i, tc := range testCases { + indexStr := strconv.Itoa(i) + tc := tc + t.Run(indexStr+"-"+tc.name, func(t *testing.T) { + tc.inRoute.override(logrus.New(), nil) + assert.Equal(t, tc.outRoute, tc.inRoute, "should be the same as expected after overriding") + }) + } +} + func TestOverrideRoutePriority(t *testing.T) { assert := assert.New(t) diff --git a/internal/dataplane/parser/translate_httproute_test.go b/internal/dataplane/parser/translate_httproute_test.go index 25aa54898b..dc457490db 100644 --- a/internal/dataplane/parser/translate_httproute_test.go +++ b/internal/dataplane/parser/translate_httproute_test.go @@ -1680,6 +1680,63 @@ func TestIngressRulesFromHTTPRoutesUsingExpressionRoutes(t *testing.T) { }, }, }, + { + name: "single HTTPRoute with protocol and SNI annotations", + httpRoutes: []*gatewayv1beta1.HTTPRoute{ + { + TypeMeta: httpRouteTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "httproute-1", + Annotations: map[string]string{ + "konghq.com/protocols": "https", + "konghq.com/snis": "foo.com", + }, + }, + Spec: gatewayv1beta1.HTTPRouteSpec{ + Hostnames: []gatewayv1beta1.Hostname{ + "foo.com", + }, + Rules: []gatewayv1beta1.HTTPRouteRule{ + { + Matches: []gatewayv1beta1.HTTPRouteMatch{ + builder.NewHTTPRouteMatch().WithPathExact("/v1/foo").Build(), + }, + BackendRefs: []gatewayv1beta1.HTTPBackendRef{ + builder.NewHTTPBackendRef("service1").WithPort(80).Build(), + }, + }, + }, + }, + }, + }, + expectedKongServices: []kongstate.Service{ + { + Service: kong.Service{ + Name: kong.String("httproute.default.httproute-1.foo.com.0"), + }, + Backends: []kongstate.ServiceBackend{ + { + Name: "service1", + PortDef: kongstate.PortDef{Mode: kongstate.PortModeByNumber, Number: int32(80)}, + }, + }, + }, + }, + expectedKongRoutes: map[string][]kongstate.Route{ + "httproute.default.httproute-1.foo.com.0": { + { + Route: kong.Route{ + Name: kong.String("httproute.default.httproute-1.foo.com.0.0"), + Expression: kong.String(`(http.host == "foo.com") && (tls.sni == "foo.com") && (http.path == "/v1/foo")`), + PreserveHost: kong.Bool(true), + }, + Plugins: []kong.Plugin{}, + ExpressionRoutes: true, + }, + }, + }, + }, { name: "multiple HTTPRoutes with translation failures", httpRoutes: []*gatewayv1beta1.HTTPRoute{ @@ -1781,7 +1838,7 @@ func TestIngressRulesFromHTTPRoutesUsingExpressionRoutes(t *testing.T) { routeName := expectedRoute.Name r, ok := kongRouteNameToRoute[*routeName] require.Truef(t, ok, "should find route %s", *routeName) - require.Equal(t, expectedRoute.Expression, r.Expression) + require.Equal(t, *expectedRoute.Expression, *r.Expression) } } // check translation failures diff --git a/internal/dataplane/parser/translators/httproute_atc.go b/internal/dataplane/parser/translators/httproute_atc.go index 14536e820f..65ca6cdf8d 100644 --- a/internal/dataplane/parser/translators/httproute_atc.go +++ b/internal/dataplane/parser/translators/httproute_atc.go @@ -59,7 +59,7 @@ func GenerateKongExpressionRoutesFromHTTPRouteMatches( // if we do not need to generate a kong route for each match, we OR matchers from all matches together. routeMatcher := atc.And(atc.Or(generateMatchersFromHTTPRouteMatches(translation.Matches)...)) - // add matcher from parent httproute (hostnames, SNIs) to be ANDed with the matcher from match. + // Add matcher from parent httproute (hostnames, SNIs) to be ANDed with the matcher from match. matchersFromParent := matchersFromParentHTTPRoute(hostnames, ingressObjectInfo.Annotations) for _, matcher := range matchersFromParent { routeMatcher.And(matcher) diff --git a/internal/dataplane/parser/translators/ingress_atc_test.go b/internal/dataplane/parser/translators/ingress_atc_test.go index 93760d7950..783fb80ada 100644 --- a/internal/dataplane/parser/translators/ingress_atc_test.go +++ b/internal/dataplane/parser/translators/ingress_atc_test.go @@ -177,6 +177,105 @@ func TestTranslateIngressATC(t *testing.T) { }, }, }, + { + name: "an ingress with method, protocol, and header annotations", + ingress: &netv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-annotations", + Namespace: corev1.NamespaceDefault, + Annotations: map[string]string{ + "konghq.com/methods": "GET", + "konghq.com/protocols": "http", + "konghq.com/headers.foo": "bar", + }, + }, + Spec: netv1.IngressSpec{ + Rules: []netv1.IngressRule{{ + Host: "konghq.com", + IngressRuleValue: netv1.IngressRuleValue{ + HTTP: &netv1.HTTPIngressRuleValue{ + Paths: []netv1.HTTPIngressPath{{ + Path: "/api/", + Backend: netv1.IngressBackend{ + Service: &netv1.IngressServiceBackend{ + Name: "test-service", + Port: netv1.ServiceBackendPort{ + Name: "http", + Number: 80, + }, + }, + }, + }}, + }, + }, + }}, + }, + }, + expectedServices: map[string]kongstate.Service{ + "default.test-ingress-annotations.test-service.80": { + Namespace: corev1.NamespaceDefault, + Service: kong.Service{ + Name: kong.String("default.test-ingress-annotations.test-service.80"), + Host: kong.String("test-service.default.80.svc"), + ConnectTimeout: kong.Int(int(defaultServiceTimeout.Milliseconds())), + Path: kong.String("/"), + Port: kong.Int(80), + Protocol: kong.String("http"), + Retries: kong.Int(defaultRetries), + ReadTimeout: kong.Int(int(defaultServiceTimeout.Milliseconds())), + WriteTimeout: kong.Int(int(defaultServiceTimeout.Milliseconds())), + }, + Routes: []kongstate.Route{{ + Ingress: util.K8sObjectInfo{ + Name: "test-ingress-annotations", + Namespace: corev1.NamespaceDefault, + Annotations: map[string]string{ + "konghq.com/methods": "GET", + "konghq.com/protocols": "http", + "konghq.com/headers.foo": "bar", + }, + }, + Route: kong.Route{ + Name: kong.String("default.test-ingress-annotations.test-service.konghq.com.80"), + Expression: kong.String(`(http.host == "konghq.com") && (http.path ^= "/api/") && (http.headers.foo == "bar") && (http.method == "GET")`), + Priority: kong.Int(IngressRoutePriorityTraits{ + MatchFields: 4, + PlainHostOnly: true, + MaxPathLength: 5, + HasRegexPath: false, + HeaderCount: 1, + }.EncodeToPriority()), + PreserveHost: kong.Bool(true), + StripPath: kong.Bool(false), + ResponseBuffering: kong.Bool(true), + RequestBuffering: kong.Bool(true), + Tags: kong.StringSlice("k8s-name:test-ingress-annotations", "k8s-namespace:default"), + }, + ExpressionRoutes: true, + }}, + Backends: []kongstate.ServiceBackend{{ + Name: "test-service", + Namespace: corev1.NamespaceDefault, + PortDef: kongstate.PortDef{ + Mode: kongstate.PortModeByNumber, + Number: 80, + }, + }}, + Parent: &netv1.Ingress{ + TypeMeta: metav1.TypeMeta{Kind: "Ingress", APIVersion: netv1.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-annotations", + Namespace: corev1.NamespaceDefault, + Annotations: map[string]string{ + "konghq.com/methods": "GET", + "konghq.com/protocols": "http", + "konghq.com/headers.foo": "bar", + }, + }, + }, + }, + }, + }, } for _, tc := range testCases {