Skip to content

Commit

Permalink
Gateway API: set route parent statuses correctly (#4604)
Browse files Browse the repository at this point in the history
Rearranges the Gateway API DAG processor to
process each route parent ref atomically,
to enable properly setting status for each
of them.

Closes #4600.

Signed-off-by: Steve Kriss <krisss@vmware.com>
  • Loading branch information
skriss authored Jul 12, 2022
1 parent 920e302 commit 3ab6a48
Show file tree
Hide file tree
Showing 10 changed files with 1,311 additions and 841 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/4604-skriss-small.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Gateway API: sets route parent status correctly when routes attach to specific Listeners.
399 changes: 223 additions & 176 deletions internal/dag/gatewayapi_processor.go

Large diffs are not rendered by default.

287 changes: 238 additions & 49 deletions internal/dag/gatewayapi_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/projectcontour/contour/internal/fixture"
"github.com/projectcontour/contour/internal/gatewayapi"
"github.com/projectcontour/contour/internal/status"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -485,75 +486,254 @@ func TestNamespaceMatches(t *testing.T) {
}
}

func TestRouteSelectsGatewayListener(t *testing.T) {
func TestGetListenersForRouteParentRef(t *testing.T) {
tests := map[string]struct {
routeParentRefs []gatewayapi_v1beta1.ParentReference
routeNamespace string
listener gatewayapi_v1beta1.Listener
want bool
routeParentRef gatewayapi_v1beta1.ParentReference
routeNamespace string
routeKind string
listeners []*listenerInfo
want []int // specify the indexes of the listeners that should be selected
}{
"gateway namespace specified, no listener specified, gateway in same namespace as route": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayParentRef("projectcontour", "contour"),
routeParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"),
routeNamespace: "projectcontour",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
want: true,
want: []int{0, 1},
},
"gateway namespace specified, no listener specified, gateway in different namespace than route": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayParentRef("different-ns-than-gateway", "contour"),
routeParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"),
routeNamespace: "different-namespace-than-gateway",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
want: false,
want: nil,
},
"no gateway namespace specified, no listener specified, gateway in same namespace as route": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayParentRef("", "contour"),
},
routeParentRef: gatewayapi.GatewayParentRef("", "contour"),
routeNamespace: "projectcontour",
want: true,
},
"no gateway namespace specified, no listener specified, gateway in different namespace than route": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayParentRef("", "contour"),
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
routeNamespace: "different-ns-than-gateway",
want: false,
want: []int{0, 1},
},
"parentRef name doesn't match gateway name": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayParentRef("projectcontour", "different-name-than-gateway"),
"no gateway namespace specified, no listener specified, gateway in different namespace than route": {
routeParentRef: gatewayapi.GatewayParentRef("", "contour"),
routeNamespace: "different-namespace-than-gateway",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
want: false,
want: nil,
},

"section name specified, matches listener": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "http-listener"),
"section name specified, matches first listener": {
routeParentRef: gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "http-1"),
routeNamespace: "projectcontour",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
listener: gatewayapi_v1beta1.Listener{Name: "http-listener"},
want: true,
want: []int{0},
},
"section name specified, does not match listener": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "different-listener-name"),
"section name specified, matches second listener": {
routeParentRef: gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "http-2"),
routeNamespace: "projectcontour",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
listener: gatewayapi_v1beta1.Listener{Name: "http-listener"},
want: false,
want: []int{1},
},
"multiple parentRefs with section names specified, first listener": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "listener-1"),
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "listener-2"),
"section name specified, does not match listener": {
routeParentRef: gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "different-listener-name"),
routeNamespace: "projectcontour",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
listener: gatewayapi_v1beta1.Listener{Name: "listener-1"},
want: true,
want: nil,
},
"multiple parentRefs with section names specified, second listener": {
routeParentRefs: []gatewayapi_v1beta1.ParentReference{
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "listener-1"),
gatewayapi.GatewayListenerParentRef("projectcontour", "contour", "listener-2"),
"route kind only allowed by second listener": {
routeParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"),
routeNamespace: "projectcontour",
routeKind: "HTTPRoute",
listeners: []*listenerInfo{
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-1",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"TLSRoute"},
},
{
listener: gatewayapi_v1beta1.Listener{
Name: "http-2",
AllowedRoutes: &gatewayapi_v1beta1.AllowedRoutes{
Namespaces: &gatewayapi_v1beta1.RouteNamespaces{
From: gatewayapi.FromNamespacesPtr(gatewayapi_v1beta1.NamespacesFromSame),
},
},
},
allowedKinds: []gatewayapi_v1beta1.Kind{"HTTPRoute"},
},
},
listener: gatewayapi_v1beta1.Listener{Name: "listener-2"},
want: true,
want: []int{1},
},
}

Expand All @@ -572,8 +752,17 @@ func TestRouteSelectsGatewayListener(t *testing.T) {
},
}

got := routeSelectsGatewayListener(processor.source.gateway, tc.listener, tc.routeParentRefs, tc.routeNamespace)
assert.Equal(t, tc.want, got)
rsu := &status.RouteStatusUpdate{}
rpsu := rsu.StatusUpdateFor(tc.routeParentRef)

got := processor.getListenersForRouteParentRef(tc.routeParentRef, tc.routeNamespace, gatewayapi_v1beta1.Kind(tc.routeKind), tc.listeners, rpsu)

var want []*listenerInfo
for _, i := range tc.want {
want = append(want, tc.listeners[i])
}

assert.Equal(t, want, got)
})
}
}
Loading

0 comments on commit 3ab6a48

Please sign in to comment.