Skip to content

Commit

Permalink
machinary: Namespace linker functions
Browse files Browse the repository at this point in the history
Signed-off-by: KevFan <[email protected]>
  • Loading branch information
KevFan committed Aug 15, 2024
1 parent a4528d4 commit 104d1d1
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 1 deletion.
1 change: 1 addition & 0 deletions machinery/core_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
)

var (
NamespaceGroupKind = schema.GroupKind{Kind: "Namespace"}
ServiceGroupKind = schema.GroupKind{Kind: "Service"}
ServicePortGroupKind = schema.GroupKind{Kind: "ServicePort"}
)
Expand Down
20 changes: 20 additions & 0 deletions machinery/gateway_api_test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ func BuildGatewayClass(f ...func(*gwapiv1.GatewayClass)) *gwapiv1.GatewayClass {
return gc
}

func BuildNamespace(f ...func(*core.Namespace)) *core.Namespace {
ns := &core.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: core.SchemeGroupVersion.String(),
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-namespace",
},
}
for _, fn := range f {
fn(ns)
}
return ns
}

func BuildGateway(f ...func(*gwapiv1.Gateway)) *gwapiv1.Gateway {
g := &gwapiv1.Gateway{
TypeMeta: metav1.TypeMeta{
Expand Down Expand Up @@ -269,6 +285,7 @@ func BuildUDPRoute(f ...func(route *gwapiv1alpha2.UDPRoute)) *gwapiv1alpha2.UDPR

type GatewayAPIResources struct {
GatewayClasses []*gwapiv1.GatewayClass
Namespaces []*core.Namespace
Gateways []*gwapiv1.Gateway
HTTPRoutes []*gwapiv1.HTTPRoute
GRPCRoutes []*gwapiv1.GRPCRoute
Expand Down Expand Up @@ -324,6 +341,9 @@ func BuildComplexGatewayAPITopology(funcs ...func(*GatewayAPIResources)) Gateway
BuildGatewayClass(func(gc *gwapiv1.GatewayClass) { gc.Name = "gatewayclass-1" }),
BuildGatewayClass(func(gc *gwapiv1.GatewayClass) { gc.Name = "gatewayclass-2" }),
},
Namespaces: []*core.Namespace{
BuildNamespace(),
},
Gateways: []*gwapiv1.Gateway{
BuildGateway(func(g *gwapiv1.Gateway) {
g.Name = "gateway-1"
Expand Down
112 changes: 111 additions & 1 deletion machinery/gateway_api_topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

type GatewayAPITopologyOptions struct {
GatewayClasses []*GatewayClass
Namespaces []*Namespace
Gateways []*Gateway
HTTPRoutes []*HTTPRoute
GRPCRoutes []*GRPCRoute
Expand Down Expand Up @@ -43,6 +44,14 @@ func WithGatewayClasses(gatewayClasses ...*gwapiv1.GatewayClass) GatewayAPITopol
}
}

func WithNamespaces(namespaces ...*core.Namespace) GatewayAPITopologyOptionsFunc {
return func(o *GatewayAPITopologyOptions) {
o.Namespaces = append(o.Namespaces, lo.Map(namespaces, func(namespace *core.Namespace, _ int) *Namespace {
return &Namespace{Namespace: namespace}
})...)
}
}

// WithGateways adds gateways to the options to initialize a new Gateway API topology.
func WithGateways(gateways ...*gwapiv1.Gateway) GatewayAPITopologyOptionsFunc {
return func(o *GatewayAPITopologyOptions) {
Expand Down Expand Up @@ -198,6 +207,7 @@ func NewGatewayAPITopology(options ...GatewayAPITopologyOptionsFunc) *Topology {
WithObjects(o.Objects...),
WithPolicies(o.Policies...),
WithTargetables(o.GatewayClasses...),
WithTargetables(o.Namespaces...),
WithTargetables(o.Gateways...),
WithTargetables(o.HTTPRoutes...),
WithTargetables(o.GRPCRoutes...),
Expand All @@ -206,7 +216,15 @@ func NewGatewayAPITopology(options ...GatewayAPITopologyOptionsFunc) *Topology {
WithTargetables(o.UDPRoutes...),
WithTargetables(o.Services...),
WithLinks(o.Links...),
WithLinks(LinkGatewayClassToGatewayFunc(o.GatewayClasses)), // GatewayClass -> Gateway
WithLinks(
LinkGatewayClassToGatewayFunc(o.GatewayClasses), // GatewayClass -> Gateway
LinkNamespaceToGatewayFunc(o.Namespaces), // Namespace -> Gateway
LinkNamespaceToHTTPRouteFunc(o.Namespaces, o.Gateways), // Namespace -> HTTPRoute
LinkNamespaceToGRPCRouteFunc(o.Namespaces, o.Gateways), // Namespace -> GRPCRoute
LinkNamespaceToTCPRouteFunc(o.Namespaces, o.Gateways), // Namespace -> TCPRoute
LinkNamespaceToTLSRouteFunc(o.Namespaces, o.Gateways), // Namespace -> TLSRoute
LinkNamespaceToUDPRouteFunc(o.Namespaces, o.Gateways), // Namespace -> UDPRoute
),
}

if o.ExpandGatewayListeners {
Expand Down Expand Up @@ -461,6 +479,98 @@ func LinkGatewayClassToGatewayFunc(gatewayClasses []*GatewayClass) LinkFunc {
}
}

func LinkNamespaceToGatewayFunc(namepaces []*Namespace) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: GatewayGroupKind,
Func: func(child Object) []Object {
gateway := child.(*Gateway)
namepace, ok := lo.Find(namepaces, func(ns *Namespace) bool {
return ns.Name == gateway.Namespace
})
if ok {
return []Object{namepace}
}
return nil
},
}
}

func LinkNamespaceToHTTPRouteFunc(namespaces []*Namespace, gateways []*Gateway) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: HTTPRouteGroupKind,
Func: func(child Object) []Object {
route := child.(*HTTPRoute)
return findNamespaceForRoute(namespaces, gateways, route.Namespace, route.Spec.ParentRefs)
},
}
}

func LinkNamespaceToGRPCRouteFunc(namespaces []*Namespace, gateways []*Gateway) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: GRPCRouteGroupKind,
Func: func(child Object) []Object {
route := child.(*GRPCRoute)
return findNamespaceForRoute(namespaces, gateways, route.Namespace, route.Spec.ParentRefs)
},
}
}

func LinkNamespaceToTCPRouteFunc(namespaces []*Namespace, gateways []*Gateway) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: TCPRouteGroupKind,
Func: func(child Object) []Object {
route := child.(*TCPRoute)
return findNamespaceForRoute(namespaces, gateways, route.Namespace, route.Spec.ParentRefs)
},
}
}

func LinkNamespaceToTLSRouteFunc(namespaces []*Namespace, gateways []*Gateway) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: TLSRouteGroupKind,
Func: func(child Object) []Object {
route := child.(*TLSRoute)
return findNamespaceForRoute(namespaces, gateways, route.Namespace, route.Spec.ParentRefs)
},
}
}

func LinkNamespaceToUDPRouteFunc(namespaces []*Namespace, gateways []*Gateway) LinkFunc {
return LinkFunc{
From: NamespaceGroupKind,
To: UDPRouteGroupKind,
Func: func(child Object) []Object {
route := child.(*UDPRoute)
return findNamespaceForRoute(namespaces, gateways, route.Namespace, route.Spec.ParentRefs)
},
}
}

func findNamespaceForRoute(namespaces []*Namespace, gateways []*Gateway, routeNamespace string, routeParentRefs []gwapiv1.ParentReference) []Object {
namespace, ok := lo.Find(namespaces, func(ns *Namespace) bool {
return ns.Name == routeNamespace
})

if !ok {
return nil
}

linkedGateways := lo.FilterMap(routeParentRefs, findGatewayFromParentRefFunc(gateways, routeNamespace))

// if route is already linked by Gateway through parent ref and the Gateway is in the same namespace,
// there no need to link route with namespace also
if len(linkedGateways) != 0 && namespace.Name == linkedGateways[0].GetNamespace() {
return nil
}

return []Object{namespace}
}

// LinkGatewayToHTTPRouteFunc returns a link function that teaches a topology how to link HTTPRoutes from known
// Gateways, based on the HTTPRoute's `parentRefs` field.
func LinkGatewayToHTTPRouteFunc(gateways []*Gateway) LinkFunc {
Expand Down
94 changes: 94 additions & 0 deletions machinery/gateway_api_topology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func TestGatewayAPITopology(t *testing.T) {
name: "one of each kind",
targetables: GatewayAPIResources{
GatewayClasses: []*gwapiv1.GatewayClass{BuildGatewayClass()},
Namespaces: []*core.Namespace{BuildNamespace()},
Gateways: []*gwapiv1.Gateway{BuildGateway()},
HTTPRoutes: []*gwapiv1.HTTPRoute{BuildHTTPRoute()},
GRPCRoutes: []*gwapiv1.GRPCRoute{BuildGRPCRoute()},
Expand All @@ -55,6 +56,7 @@ func TestGatewayAPITopology(t *testing.T) {
policies: []Policy{buildPolicy()},
expectedLinks: map[string][]string{
"my-gateway-class": {"my-gateway"},
"my-namespace": {"my-gateway"},
"my-gateway": {"my-http-route", "my-grpc-route", "my-tcp-route", "my-tls-route", "my-udp-route"},
"my-grpc-route": {"my-service"},
"my-http-route": {"my-service"},
Expand All @@ -63,12 +65,92 @@ func TestGatewayAPITopology(t *testing.T) {
"my-udp-route": {"my-service"},
},
},
{
name: "multiple namespaces - linking routes across namespaces",
targetables: GatewayAPIResources{
GatewayClasses: []*gwapiv1.GatewayClass{
BuildGatewayClass(),
},
Namespaces: []*core.Namespace{
BuildNamespace(func(namespace *core.Namespace) {
namespace.Name = "namespace-1"
}),
BuildNamespace(func(namespace *core.Namespace) {
namespace.Name = "namespace-2"
}),
BuildNamespace(func(namespace *core.Namespace) {
namespace.Name = "namespace-3"
}),
},
Gateways: []*gwapiv1.Gateway{
BuildGateway(func(gateway *gwapiv1.Gateway) {
gateway.Name = "gateway-1"
gateway.Namespace = "namespace-1"
}),
BuildGateway(func(gateway *gwapiv1.Gateway) {
gateway.Name = "gateway-2"
gateway.Namespace = "namespace-2"
}),
},
HTTPRoutes: []*gwapiv1.HTTPRoute{
BuildHTTPRoute(func(httpRoute *gwapiv1.HTTPRoute) {
httpRoute.Namespace = "namespace-1"
httpRoute.Name = "http-route-1"
httpRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
BuildHTTPRoute(func(httpRoute *gwapiv1.HTTPRoute) {
httpRoute.Namespace = "namespace-3"
httpRoute.Name = "http-route-2"
httpRoute.Spec.ParentRefs[0].Namespace = ptr.To[gwapiv1.Namespace]("namespace-1")
httpRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
},
GRPCRoutes: []*gwapiv1.GRPCRoute{
BuildGRPCRoute(func(grpcRoute *gwapiv1.GRPCRoute) {
grpcRoute.Namespace = "namespace-3"
grpcRoute.Name = "grpc-route-1"
grpcRoute.Spec.ParentRefs[0].Namespace = ptr.To[gwapiv1.Namespace]("namespace-1")
grpcRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
},
TCPRoutes: []*gwapiv1alpha2.TCPRoute{
BuildTCPRoute(func(tcpRoute *gwapiv1alpha2.TCPRoute) {
tcpRoute.Namespace = "namespace-1"
tcpRoute.Name = "tcp-route-1"
tcpRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
},
TLSRoutes: []*gwapiv1alpha2.TLSRoute{
BuildTLSRoute(func(tlsRoute *gwapiv1alpha2.TLSRoute) {
tlsRoute.Namespace = "namespace-3"
tlsRoute.Name = "tls-route-1"
tlsRoute.Spec.ParentRefs[0].Namespace = ptr.To[gwapiv1.Namespace]("namespace-1")
tlsRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
},
UDPRoutes: []*gwapiv1alpha2.UDPRoute{
BuildUDPRoute(func(udpRoute *gwapiv1alpha2.UDPRoute) {
udpRoute.Namespace = "namespace-1"
udpRoute.Name = "udp-route-1"
udpRoute.Spec.ParentRefs[0].Name = "gateway-1"
}),
},
},
expectedLinks: map[string][]string{
"my-gateway-class": {"gateway-1", "gateway-2"},
"namespace-1": {"gateway-1"},
"namespace-2": {"gateway-2"},
"namespace-3": {"http-route-2", "grpc-route-1", "tls-route-1"},
"gateway-1": {"http-route-1", "http-route-2", "grpc-route-1", "tcp-route-1", "tls-route-1", "udp-route-1"},
},
},
{
name: "complex topology",
targetables: BuildComplexGatewayAPITopology(),
expectedLinks: map[string][]string{
"gatewayclass-1": {"gateway-1", "gateway-2", "gateway-3"},
"gatewayclass-2": {"gateway-4", "gateway-5"},
"my-namespace": {"gateway-1", "gateway-2", "gateway-3", "gateway-4", "gateway-5"},
"gateway-1": {"http-route-1", "http-route-2"},
"gateway-2": {"http-route-2", "http-route-3"},
"gateway-3": {"udp-route-1", "tls-route-1"},
Expand All @@ -89,6 +171,7 @@ func TestGatewayAPITopology(t *testing.T) {
gatewayClasses := lo.Map(tc.targetables.GatewayClasses, func(gatewayClass *gwapiv1.GatewayClass, _ int) *GatewayClass {
return &GatewayClass{GatewayClass: gatewayClass}
})
namespaces := lo.Map(tc.targetables.Namespaces, func(namespace *core.Namespace, _ int) *Namespace { return &Namespace{Namespace: namespace} })
gateways := lo.Map(tc.targetables.Gateways, func(gateway *gwapiv1.Gateway, _ int) *Gateway { return &Gateway{Gateway: gateway} })
httpRoutes := lo.Map(tc.targetables.HTTPRoutes, func(httpRoute *gwapiv1.HTTPRoute, _ int) *HTTPRoute { return &HTTPRoute{HTTPRoute: httpRoute} })
grpcRoutes := lo.Map(tc.targetables.GRPCRoutes, func(grpcRoute *gwapiv1.GRPCRoute, _ int) *GRPCRoute { return &GRPCRoute{GRPCRoute: grpcRoute} })
Expand All @@ -99,6 +182,7 @@ func TestGatewayAPITopology(t *testing.T) {

topology := NewTopology(
WithTargetables(gatewayClasses...),
WithTargetables(namespaces...),
WithTargetables(gateways...),
WithTargetables(httpRoutes...),
WithTargetables(services...),
Expand All @@ -108,6 +192,12 @@ func TestGatewayAPITopology(t *testing.T) {
WithTargetables(udpRoutes...),
WithLinks(
LinkGatewayClassToGatewayFunc(gatewayClasses),
LinkNamespaceToGatewayFunc(namespaces),
LinkNamespaceToHTTPRouteFunc(namespaces, gateways),
LinkNamespaceToGRPCRouteFunc(namespaces, gateways),
LinkNamespaceToTCPRouteFunc(namespaces, gateways),
LinkNamespaceToTLSRouteFunc(namespaces, gateways),
LinkNamespaceToUDPRouteFunc(namespaces, gateways),
LinkGatewayToHTTPRouteFunc(gateways),
LinkGatewayToGRPCRouteFunc(gateways),
LinkGatewayToTCPRouteFunc(gateways),
Expand Down Expand Up @@ -158,6 +248,7 @@ func TestGatewayAPITopologyWithSectionNames(t *testing.T) {
name: "one of each kind",
targetables: GatewayAPIResources{
GatewayClasses: []*gwapiv1.GatewayClass{BuildGatewayClass()},
Namespaces: []*core.Namespace{BuildNamespace()},
Gateways: []*gwapiv1.Gateway{BuildGateway()},
HTTPRoutes: []*gwapiv1.HTTPRoute{BuildHTTPRoute()},
GRPCRoutes: []*gwapiv1.GRPCRoute{BuildGRPCRoute()},
Expand All @@ -169,6 +260,7 @@ func TestGatewayAPITopologyWithSectionNames(t *testing.T) {
policies: []Policy{buildPolicy()},
expectedLinks: map[string][]string{
"my-gateway-class": {"my-gateway"},
"my-namespace": {"my-gateway"},
"my-gateway": {"my-gateway#my-listener"},
"my-gateway#my-listener": {"my-http-route", "my-grpc-route", "my-tcp-route", "my-tls-route", "my-udp-route"},
"my-grpc-route": {"my-grpc-route#rule-1"},
Expand Down Expand Up @@ -268,13 +360,15 @@ func TestGatewayAPITopologyWithSectionNames(t *testing.T) {
"service-5": {"service-5#port-1"},
"service-6": {"service-6#port-1"},
"service-7": {"service-7#port-1"},
"my-namespace": {"gateway-1", "gateway-2", "gateway-3", "gateway-4", "gateway-5"},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
topology := NewGatewayAPITopology(
WithGatewayClasses(tc.targetables.GatewayClasses...),
WithNamespaces(tc.targetables.Namespaces...),
WithGateways(tc.targetables.Gateways...),
ExpandGatewayListeners(),
WithHTTPRoutes(tc.targetables.HTTPRoutes...),
Expand Down

0 comments on commit 104d1d1

Please sign in to comment.