diff --git a/internal/dataplane/parser/translators/httproute_atc_test.go b/internal/dataplane/parser/translators/httproute_atc_test.go index c8d29d14b8..e513f7208e 100644 --- a/internal/dataplane/parser/translators/httproute_atc_test.go +++ b/internal/dataplane/parser/translators/httproute_atc_test.go @@ -5,6 +5,7 @@ import ( "github.com/kong/go-kong/kong" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/kongstate" @@ -306,3 +307,177 @@ func TestGenerateMatcherFromHTTPRouteMatch(t *testing.T) { }) } } + +func TestCalculateHTTPRoutePriorityTraits(t *testing.T) { + testCases := []struct { + name string + httpRoute *gatewayv1beta1.HTTPRoute + expectedTraits HTTPRoutePriorityTraits + }{ + { + name: "precise hostname and exact path", + httpRoute: &gatewayv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "precise-hostname-exact-path", + }, + Spec: gatewayv1beta1.HTTPRouteSpec{ + Hostnames: []gatewayv1beta1.Hostname{"foo.com"}, + Rules: []gatewayv1beta1.HTTPRouteRule{ + { + Matches: []gatewayv1beta1.HTTPRouteMatch{ + builder.NewHTTPRouteMatch().WithPathExact("/foo").Build(), + }, + }, + }, + }, + }, + expectedTraits: HTTPRoutePriorityTraits{ + PreciseHostname: true, + HostnameLength: len("foo.com"), + PathType: gatewayv1beta1.PathMatchExact, + PathLength: len("/foo"), + }, + }, + { + name: "wildcard hostname and prefix path", + httpRoute: &gatewayv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "wildcard-hostname-prefix-path", + }, + Spec: gatewayv1beta1.HTTPRouteSpec{ + Hostnames: []gatewayv1beta1.Hostname{"*.foo.com"}, + Rules: []gatewayv1beta1.HTTPRouteRule{ + { + Matches: []gatewayv1beta1.HTTPRouteMatch{ + builder.NewHTTPRouteMatch().WithPathPrefix("/foo/").Build(), + }, + }, + }, + }, + }, + expectedTraits: HTTPRoutePriorityTraits{ + PreciseHostname: false, + HostnameLength: len("*.foo.com"), + PathType: gatewayv1beta1.PathMatchPathPrefix, + PathLength: len("/foo/"), + }, + }, + { + name: "no hostname and regex path, with header matches", + httpRoute: &gatewayv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "no-hostname-regex-path", + }, + Spec: gatewayv1beta1.HTTPRouteSpec{ + Rules: []gatewayv1beta1.HTTPRouteRule{ + { + Matches: []gatewayv1beta1.HTTPRouteMatch{ + builder.NewHTTPRouteMatch().WithPathRegex("/[a-z0-9]+"). + WithHeader("foo", "bar").Build(), + }, + }, + }, + }, + }, + expectedTraits: HTTPRoutePriorityTraits{ + PathType: gatewayv1beta1.PathMatchRegularExpression, + PathLength: len("/[a-z0-9]+"), + HeaderCount: 1, + }, + }, + { + name: "precise hostname and method, query param match", + httpRoute: &gatewayv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "precise-hostname-method-query", + }, + Spec: gatewayv1beta1.HTTPRouteSpec{ + Hostnames: []gatewayv1beta1.Hostname{ + "foo.com", + }, + Rules: []gatewayv1beta1.HTTPRouteRule{ + { + Matches: []gatewayv1beta1.HTTPRouteMatch{ + builder.NewHTTPRouteMatch().WithMethod("GET"). + WithQueryParam("foo", "bar").Build(), + }, + }, + }, + }, + }, + expectedTraits: HTTPRoutePriorityTraits{ + PreciseHostname: true, + HostnameLength: len("foo.com"), + HasMethodMatch: true, + QueryParamCount: 1, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + traits := CalculateHTTPRoutePriorityTraits(tc.httpRoute) + require.Equal(t, tc.expectedTraits, traits) + }) + } +} + +func TestEncodeHTTPRoutePriorityFromTraits(t *testing.T) { + testCases := []struct { + name string + traits HTTPRoutePriorityTraits + expectedPriority int + }{ + { + name: "precise hostname and exact path", + traits: HTTPRoutePriorityTraits{ + PreciseHostname: true, + HostnameLength: 7, + PathType: gatewayv1beta1.PathMatchExact, + PathLength: 4, + }, + expectedPriority: (2 << 50) | (1 << 49) | (7 << 41) | (1 << 40) | (3 << 29), + }, + { + name: "wildcard hostname and prefix path", + traits: HTTPRoutePriorityTraits{ + PreciseHostname: false, + HostnameLength: 7, + PathType: gatewayv1beta1.PathMatchPathPrefix, + PathLength: 5, + }, + expectedPriority: (2 << 50) | (7 << 41) | (4 << 29), + }, + { + name: "no hostname and regex path, with header matches", + traits: HTTPRoutePriorityTraits{ + PathType: gatewayv1beta1.PathMatchRegularExpression, + PathLength: 5, + HeaderCount: 2, + }, + expectedPriority: (2 << 50) | (1 << 39) | (4 << 29) | (2 << 24), + }, + { + name: "no hostname and exact path, with method match and query parameter matches", + traits: HTTPRoutePriorityTraits{ + PathType: gatewayv1beta1.PathMatchExact, + PathLength: 5, + HasMethodMatch: true, + QueryParamCount: 1, + }, + expectedPriority: (2 << 50) | (1 << 40) | (4 << 29) | (1 << 23) | (1 << 18), + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expectedPriority, tc.traits.EncodeToPriority()) + }) + } +}