Skip to content

Commit

Permalink
Route extension filters are unstructured.Unstructured instances, so
Browse files Browse the repository at this point in the history
caching them should be done with both the name and type as a key.

Signed-off-by: Lior Okman <lior.okman@sap.com>
  • Loading branch information
liorokman committed May 13, 2024
1 parent c30d09f commit b06dee6
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 14 deletions.
21 changes: 21 additions & 0 deletions internal/message/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ package message

import (
"github.com/telepresenceio/watchable"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

Expand All @@ -16,6 +18,25 @@ import (
xdstypes "github.com/envoyproxy/gateway/internal/xds/types"
)

type NamespacedNameAndType struct {
types.NamespacedName
schema.GroupKind
}

// NamespacedNameAndType creates and returns object's NamespacedNameAndType.
func GetNamespacedNameAndType(obj client.Object) NamespacedNameAndType {
return NamespacedNameAndType{
NamespacedName: types.NamespacedName{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
},
GroupKind: schema.GroupKind{
Group: obj.GetObjectKind().GroupVersionKind().GroupKind().Group,
Kind: obj.GetObjectKind().GroupVersionKind().GroupKind().Kind,
},
}
}

// ProviderResources message
type ProviderResources struct {
// GatewayAPIResources is a map from a GatewayClass name to
Expand Down
4 changes: 2 additions & 2 deletions internal/provider/kubernetes/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ type resourceMappings struct {
// extensionRefFilters is a map of filters managed by an extension.
// The key is the namespaced name of the filter and the value is the
// unstructured form of the resource.
extensionRefFilters map[types.NamespacedName]unstructured.Unstructured
extensionRefFilters map[message.NamespacedNameAndType]unstructured.Unstructured
}

func newResourceMapping() *resourceMappings {
return &resourceMappings{
allAssociatedNamespaces: map[string]struct{}{},
allAssociatedBackendRefs: map[gwapiv1.BackendObjectReference]struct{}{},
extensionRefFilters: map[types.NamespacedName]unstructured.Unstructured{},
extensionRefFilters: map[message.NamespacedNameAndType]unstructured.Unstructured{},
}
}

Expand Down
29 changes: 21 additions & 8 deletions internal/provider/kubernetes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

"github.com/envoyproxy/gateway/internal/gatewayapi"
"github.com/envoyproxy/gateway/internal/utils"
"github.com/envoyproxy/gateway/internal/message"
)

// processTLSRoutes finds TLSRoutes corresponding to a gatewayNamespaceName, further checks for
Expand Down Expand Up @@ -172,10 +172,17 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam
if filter.Type == gwapiv1.GRPCRouteFilterExtensionRef {
// NOTE: filters must be in the same namespace as the GRPCRoute
// Check if it's a Kind managed by an extension and add to resourceTree
key := types.NamespacedName{
Namespace: grpcRoute.Namespace,
Name: string(filter.ExtensionRef.Name),
key := message.NamespacedNameAndType{
NamespacedName: types.NamespacedName{
Namespace: grpcRoute.Namespace,
Name: string(filter.ExtensionRef.Name),
},
GroupKind: schema.GroupKind{
Group: string(filter.ExtensionRef.Group),
Kind: string(filter.ExtensionRef.Kind),
},
}

extRefFilter, ok := resourceMap.extensionRefFilters[key]
if !ok {
r.log.Error(
Expand Down Expand Up @@ -215,7 +222,7 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam
}
for i := range extensionRefFilters {
filter := extensionRefFilters[i]
resourceMap.extensionRefFilters[utils.NamespacedName(&filter)] = filter
resourceMap.extensionRefFilters[message.GetNamespacedNameAndType(&filter)] = filter
}

if err := r.client.List(ctx, httpRouteList, &client.ListOptions{
Expand Down Expand Up @@ -347,9 +354,15 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam
} else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef {
// NOTE: filters must be in the same namespace as the HTTPRoute
// Check if it's a Kind managed by an extension and add to resourceTree
key := types.NamespacedName{
Namespace: httpRoute.Namespace,
Name: string(filter.ExtensionRef.Name),
key := message.NamespacedNameAndType{
NamespacedName: types.NamespacedName{
Namespace: httpRoute.Namespace,
Name: string(filter.ExtensionRef.Name),
},
GroupKind: schema.GroupKind{
Group: string(filter.ExtensionRef.Group),
Kind: string(filter.ExtensionRef.Kind),
},
}
extRefFilter, ok := resourceMap.extensionRefFilters[key]
if !ok {
Expand Down
111 changes: 107 additions & 4 deletions internal/provider/kubernetes/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/envoyproxy/gateway/internal/envoygateway"
"github.com/envoyproxy/gateway/internal/gatewayapi"
"github.com/envoyproxy/gateway/internal/logging"
"github.com/envoyproxy/gateway/internal/message"
"github.com/envoyproxy/gateway/internal/utils"
)

Expand Down Expand Up @@ -120,6 +121,102 @@ func TestProcessHTTPRoutes(t *testing.T) {
},
expected: true,
},
{
name: "httproute with extension filter multiple types same name",
routes: []*gwapiv1.HTTPRoute{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: httpRouteNS,
Name: "test",
},
Spec: gwapiv1.HTTPRouteSpec{
CommonRouteSpec: gwapiv1.CommonRouteSpec{
ParentRefs: []gwapiv1.ParentReference{
{
Name: "test",
},
},
},
Rules: []gwapiv1.HTTPRouteRule{
{
Matches: []gwapiv1.HTTPRouteMatch{
{
Path: &gwapiv1.HTTPPathMatch{
Type: ptr.To(gwapiv1.PathMatchPathPrefix),
Value: ptr.To("/"),
},
},
},
Filters: []gwapiv1.HTTPRouteFilter{
{
Type: gwapiv1.HTTPRouteFilterExtensionRef,
ExtensionRef: &gwapiv1.LocalObjectReference{
Group: gwapiv1.Group("gateway.example.io"),
Kind: gwapiv1.Kind("Bar"),
Name: gwapiv1.ObjectName("test"),
},
},
{
Type: gwapiv1.HTTPRouteFilterExtensionRef,
ExtensionRef: &gwapiv1.LocalObjectReference{
Group: gwapiv1.Group("gateway.example.io"),
Kind: gwapiv1.Kind("Foo"),
Name: gwapiv1.ObjectName("test"),
},
},
},
BackendRefs: []gwapiv1.HTTPBackendRef{
{
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Group: gatewayapi.GroupPtr(corev1.GroupName),
Kind: gatewayapi.KindPtr(gatewayapi.KindService),
Name: "test",
},
},
},
},
},
},
},
},
},
extensionFilters: []*unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "gateway.example.io/v1alpha1",
"kind": "Bar",
"metadata": map[string]interface{}{
"name": "test",
"namespace": httpRouteNS,
},
},
},
{
Object: map[string]interface{}{
"apiVersion": "gateway.example.io/v1alpha1",
"kind": "Foo",
"metadata": map[string]interface{}{
"name": "test",
"namespace": httpRouteNS,
},
},
},
},
extensionAPIGroups: []schema.GroupVersionKind{
{
Group: "gateway.example.io",
Version: "v1alpha1",
Kind: "Bar",
},
{
Group: "gateway.example.io",
Version: "v1alpha1",
Kind: "Foo",
},
},
expected: true,
},
{
name: "httproute with one filter_from_extension",
routes: []*gwapiv1.HTTPRoute{
Expand Down Expand Up @@ -294,10 +391,16 @@ func TestProcessHTTPRoutes(t *testing.T) {
// Ensure the resource tree and map are as expected.
require.Equal(t, tc.routes, resourceTree.HTTPRoutes)
if tc.extensionFilters != nil {
for i, filter := range tc.extensionFilters {
key := types.NamespacedName{
Namespace: tc.routes[i].Namespace,
Name: filter.GetName(),
for _, filter := range tc.extensionFilters {
key := message.NamespacedNameAndType{
NamespacedName: types.NamespacedName{
Namespace: tc.routes[0].Namespace,
Name: filter.GetName(),
},
GroupKind: schema.GroupKind{
Group: filter.GroupVersionKind().Group,
Kind: filter.GroupVersionKind().Kind,
},
}
require.Equal(t, *filter, resourceMap.extensionRefFilters[key])
}
Expand Down

0 comments on commit b06dee6

Please sign in to comment.