Skip to content

Commit

Permalink
reject route if not allowed by listener
Browse files Browse the repository at this point in the history
  • Loading branch information
salonichf5 committed Jul 30, 2024
1 parent 92db570 commit ac6857f
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 24 deletions.
32 changes: 29 additions & 3 deletions internal/mode/static/state/graph/route_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -354,7 +355,11 @@ func tryToAttachRouteToListeners(
rk := CreateRouteKey(route.Source)

bind := func(l *Listener) (allowed, attached bool) {
if !routeAllowedByListener(l, route.Source.GetNamespace(), gw.Source.Namespace, namespaces) {
if !isRouteNamespaceAllowedByListener(l, route.Source.GetNamespace(), gw.Source.Namespace, namespaces) {
return false, false
}

if !isRouteKindAllowedByListener(l, route.Source.GetObjectKind()) {
return false, false
}

Expand Down Expand Up @@ -502,13 +507,14 @@ func GetMoreSpecificHostname(hostname1, hostname2 string) string {
return ""
}

func routeAllowedByListener(
// isRouteNamespaceAllowedByListener checks if the route namespace is allowed by the listener.
func isRouteNamespaceAllowedByListener(
listener *Listener,
routeNS,
gwNS string,
namespaces map[types.NamespacedName]*apiv1.Namespace,
) bool {
if listener.Source.AllowedRoutes != nil {
if listener.Source.AllowedRoutes != nil && listener.Source.AllowedRoutes.Namespaces != nil {
switch *listener.Source.AllowedRoutes.Namespaces.From {
case v1.NamespacesFromAll:
return true
Expand All @@ -529,6 +535,26 @@ func routeAllowedByListener(
return true
}

// isRouteKindAllowedByListener checks if the route kind is allowed by the listener.
// If the listener does not specify allowed kinds, all kinds can attach to it.
// If the listener specifies allowed kinds, the route kind must be in the list.
// If the listener specifies HTTPRoute, a GRPCRoute can be attached to it.
func isRouteKindAllowedByListener(listener *Listener, routeKind schema.ObjectKind) bool {
if listener.Source.AllowedRoutes != nil && listener.Source.AllowedRoutes.Kinds != nil {
for _, kind := range listener.Source.AllowedRoutes.Kinds {
routeKind := v1.Kind(routeKind.GroupVersionKind().Kind)
if kind.Kind == routeKind {
return true

Check warning on line 547 in internal/mode/static/state/graph/route_common.go

View check run for this annotation

Codecov / codecov/patch

internal/mode/static/state/graph/route_common.go#L547

Added line #L547 was not covered by tests
}
if kind.Kind == kinds.HTTPRoute && routeKind == kinds.GRPCRoute {
return true
}
}
return false
}
return true
}

func getHostname(h *v1.Hostname) string {
if h == nil {
return ""
Expand Down
185 changes: 164 additions & 21 deletions internal/mode/static/state/graph/route_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ func TestBindRouteToListeners(t *testing.T) {
Namespace: "test",
Name: "hr",
},
TypeMeta: metav1.TypeMeta{
Kind: "HTTPRoute",
},
Spec: gatewayv1.HTTPRouteSpec{
CommonRouteSpec: gatewayv1.CommonRouteSpec{
ParentRefs: []gatewayv1.ParentReference{
Expand Down Expand Up @@ -291,9 +294,9 @@ func TestBindRouteToListeners(t *testing.T) {
nil,
)

var normalRoute *L7Route
createNormalRoute := func(gateway *gatewayv1.Gateway) *L7Route {
normalRoute = &L7Route{
var normalHTTPRoute *L7Route
createNormalHTTPRoute := func(gateway *gatewayv1.Gateway) *L7Route {
normalHTTPRoute = &L7Route{
RouteType: RouteTypeHTTP,
Source: hr,
Spec: L7RouteSpec{
Expand All @@ -309,10 +312,11 @@ func TestBindRouteToListeners(t *testing.T) {
},
},
}
return normalRoute
return normalHTTPRoute
}
getLastNormalRoute := func() *L7Route {
return normalRoute

getLastNormalHTTPRoute := func() *L7Route {
return normalHTTPRoute
}

invalidAttachableRoute1 := &L7Route{
Expand Down Expand Up @@ -430,6 +434,62 @@ func TestBindRouteToListeners(t *testing.T) {
l.Source.Hostname = helpers.GetPointer[gatewayv1.Hostname]("bar.example.com")
})

createGRPCRouteWithSectionNameAndPort := func(
sectionName *gatewayv1.SectionName,
port *gatewayv1.PortNumber,
) *gatewayv1.GRPCRoute {
return &gatewayv1.GRPCRoute{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test",
Name: "hr",
},
TypeMeta: metav1.TypeMeta{
Kind: "GRPCRoute",
},
Spec: gatewayv1.GRPCRouteSpec{
CommonRouteSpec: gatewayv1.CommonRouteSpec{
ParentRefs: []gatewayv1.ParentReference{
{
Name: gatewayv1.ObjectName(gw.Name),
SectionName: sectionName,
Port: port,
},
},
},
Hostnames: []gatewayv1.Hostname{
"foo.example.com",
},
},
}
}

gr := createGRPCRouteWithSectionNameAndPort(helpers.GetPointer[gatewayv1.SectionName]("listener-80-1"), nil)

var normalGRPCRoute *L7Route
createNormalGRPCRoute := func(gateway *gatewayv1.Gateway) *L7Route {
normalGRPCRoute = &L7Route{
RouteType: RouteTypeGRPC,
Source: gr,
Spec: L7RouteSpec{
Hostnames: gr.Spec.Hostnames,
},
Valid: true,
Attachable: true,
ParentRefs: []ParentRef{
{
Idx: 0,
Gateway: client.ObjectKeyFromObject(gateway),
SectionName: gr.Spec.ParentRefs[0].SectionName,
},
},
}
return normalGRPCRoute
}

getLastNormalGRPCRoute := func() *L7Route {
return normalGRPCRoute
}

tests := []struct {
route *L7Route
gateway *Gateway
Expand All @@ -439,7 +499,7 @@ func TestBindRouteToListeners(t *testing.T) {
expectedConditions []conditions.Condition
}{
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand All @@ -463,7 +523,7 @@ func TestBindRouteToListeners(t *testing.T) {
expectedGatewayListeners: []*Listener{
createModifiedListener("listener-80-1", func(l *Listener) {
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(hr): getLastNormalRoute(),
CreateRouteKey(hr): getLastNormalHTTPRoute(),
}
}),
},
Expand Down Expand Up @@ -620,7 +680,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "listener doesn't exist",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand All @@ -646,7 +706,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "listener isn't valid and attachable",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand Down Expand Up @@ -720,7 +780,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "route isn't valid",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: false,
Expand All @@ -746,7 +806,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "invalid gateway",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand All @@ -773,7 +833,7 @@ func TestBindRouteToListeners(t *testing.T) {
createModifiedListener("listener-80-1", func(l *Listener) {
l.Valid = false
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(hr): getLastNormalRoute(),
CreateRouteKey(hr): getLastNormalHTTPRoute(),
}
}),
},
Expand Down Expand Up @@ -847,7 +907,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "invalid attachable listener with invalid attachable route",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand Down Expand Up @@ -889,7 +949,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "route not allowed via labels",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand Down Expand Up @@ -928,14 +988,14 @@ func TestBindRouteToListeners(t *testing.T) {
},
}
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(hr): getLastNormalRoute(),
CreateRouteKey(hr): getLastNormalHTTPRoute(),
}
}),
},
name: "route allowed via labels",
},
{
route: createNormalRoute(gwDiffNamespace),
route: createNormalHTTPRoute(gwDiffNamespace),
gateway: &Gateway{
Source: gwDiffNamespace,
Valid: true,
Expand Down Expand Up @@ -973,7 +1033,7 @@ func TestBindRouteToListeners(t *testing.T) {
name: "route not allowed via same namespace",
},
{
route: createNormalRoute(gw),
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Expand Down Expand Up @@ -1008,14 +1068,14 @@ func TestBindRouteToListeners(t *testing.T) {
},
}
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(hr): getLastNormalRoute(),
CreateRouteKey(hr): getLastNormalHTTPRoute(),
}
}),
},
name: "route allowed via same namespace",
},
{
route: createNormalRoute(gwDiffNamespace),
route: createNormalHTTPRoute(gwDiffNamespace),
gateway: &Gateway{
Source: gwDiffNamespace,
Valid: true,
Expand Down Expand Up @@ -1050,12 +1110,95 @@ func TestBindRouteToListeners(t *testing.T) {
},
}
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(hr): getLastNormalRoute(),
CreateRouteKey(hr): getLastNormalHTTPRoute(),
}
}),
},
name: "route allowed via all namespaces",
},
{
route: createNormalHTTPRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Listeners: []*Listener{
createModifiedListener("listener-80-1", func(l *Listener) {
l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{
Kinds: []gatewayv1.RouteGroupKind{
{Kind: "GRPCRoute"},
},
}
}),
},
},
expectedSectionNameRefs: []ParentRef{
{
Idx: 0,
Gateway: client.ObjectKeyFromObject(gw),
SectionName: hr.Spec.ParentRefs[0].SectionName,
Attachment: &ParentRefAttachmentStatus{
Attached: false,
FailedCondition: staticConds.NewRouteNotAllowedByListeners(),
AcceptedHostnames: map[string][]string{},
},
},
},
expectedGatewayListeners: []*Listener{
createModifiedListener("listener-80-1", func(l *Listener) {
l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{
Kinds: []gatewayv1.RouteGroupKind{
{Kind: "GRPCRoute"},
},
}
}),
},
name: "http route not allowed when listener allows only grpc routes",
},
{
route: createNormalGRPCRoute(gw),
gateway: &Gateway{
Source: gw,
Valid: true,
Listeners: []*Listener{
createModifiedListener("listener-80-1", func(l *Listener) {
l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{
Kinds: []gatewayv1.RouteGroupKind{
{Kind: "HTTPRoute"},
},
}
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(gr): getLastNormalGRPCRoute(),
}
}),
},
},
expectedSectionNameRefs: []ParentRef{
{
Idx: 0,
Gateway: client.ObjectKeyFromObject(gw),
SectionName: gr.Spec.ParentRefs[0].SectionName,
Attachment: &ParentRefAttachmentStatus{
Attached: true,
AcceptedHostnames: map[string][]string{
"listener-80-1": {"foo.example.com"},
},
},
},
},
expectedGatewayListeners: []*Listener{
createModifiedListener("listener-80-1", func(l *Listener) {
l.Source.AllowedRoutes = &gatewayv1.AllowedRoutes{
Kinds: []gatewayv1.RouteGroupKind{
{Kind: "HTTPRoute"},
},
}
l.Routes = map[RouteKey]*L7Route{
CreateRouteKey(gr): getLastNormalGRPCRoute(),
}
}),
},
name: "grpc route allowed when listener kind is HTTPRoute",
},
}

namespaces := map[types.NamespacedName]*v1.Namespace{
Expand Down

0 comments on commit ac6857f

Please sign in to comment.