From eeb99c35363f3b6b0c835a9244d15a95f7f197db Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Mon, 21 Oct 2024 09:54:04 +0200 Subject: [PATCH] Preserve HTTPRoute filters order --- .../httproute/filter_extension_ref.yml | 7 +++- pkg/provider/kubernetes/gateway/httproute.go | 39 +++++++++++++------ .../kubernetes/gateway/kubernetes_test.go | 19 ++++++--- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml index c377fd139..0d2ffc918 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml @@ -54,4 +54,9 @@ spec: extensionRef: group: traefik.io kind: Middleware - name: my-middleware + name: my-first-middleware + - type: ExtensionRef + extensionRef: + group: traefik.io + kind: Middleware + name: my-second-middleware diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go index fc6c9cf1b..df9905aed 100644 --- a/pkg/provider/kubernetes/gateway/httproute.go +++ b/pkg/provider/kubernetes/gateway/httproute.go @@ -301,36 +301,52 @@ func (p *Provider) loadHTTPBackendRef(namespace string, backendRef gatev1.HTTPBa } func (p *Provider) loadMiddlewares(conf *dynamic.Configuration, namespace, routerName string, filters []gatev1.HTTPRouteFilter, pathMatch *gatev1.HTTPPathMatch) ([]string, error) { + type namedMiddleware struct { + Name string + Config *dynamic.Middleware + } + pm := ptr.Deref(pathMatch, gatev1.HTTPPathMatch{ Type: ptr.To(gatev1.PathMatchPathPrefix), Value: ptr.To("/"), }) - middlewares := make(map[string]*dynamic.Middleware) + var middlewares []namedMiddleware for i, filter := range filters { name := fmt.Sprintf("%s-%s-%d", routerName, strings.ToLower(string(filter.Type)), i) + switch filter.Type { case gatev1.HTTPRouteFilterRequestRedirect: - middlewares[name] = createRequestRedirect(filter.RequestRedirect, pm) + middlewares = append(middlewares, namedMiddleware{ + name, + createRequestRedirect(filter.RequestRedirect, pm), + }) case gatev1.HTTPRouteFilterRequestHeaderModifier: - middlewares[name] = createRequestHeaderModifier(filter.RequestHeaderModifier) + middlewares = append(middlewares, namedMiddleware{ + name, + createRequestHeaderModifier(filter.RequestHeaderModifier), + }) case gatev1.HTTPRouteFilterExtensionRef: name, middleware, err := p.loadHTTPRouteFilterExtensionRef(namespace, filter.ExtensionRef) if err != nil { return nil, fmt.Errorf("loading ExtensionRef filter %s: %w", filter.Type, err) } - - middlewares[name] = middleware + middlewares = append(middlewares, namedMiddleware{ + name, + middleware, + }) case gatev1.HTTPRouteFilterURLRewrite: - var err error middleware, err := createURLRewrite(filter.URLRewrite, pm) if err != nil { return nil, fmt.Errorf("invalid filter %s: %w", filter.Type, err) } - middlewares[name] = middleware + middlewares = append(middlewares, namedMiddleware{ + name, + middleware, + }) default: // As per the spec: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional @@ -342,12 +358,11 @@ func (p *Provider) loadMiddlewares(conf *dynamic.Configuration, namespace, route } var middlewareNames []string - for name, middleware := range middlewares { - if middleware != nil { - conf.HTTP.Middlewares[name] = middleware + for _, m := range middlewares { + if m.Config != nil { + conf.HTTP.Middlewares[m.Name] = m.Config } - - middlewareNames = append(middlewareNames, name) + middlewareNames = append(middlewareNames, m.Name) } return middlewareNames, nil diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 28d1c9591..c3b287ddd 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -2431,7 +2431,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { entryPoints map[string]Entrypoint }{ { - desc: "HTTPRoute with ExtensionRef filter", + desc: "ExtensionRef filter", groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{ traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) { return namespace + "-" + name, nil, nil @@ -2459,7 +2459,10 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", - Middlewares: []string{"default-my-middleware"}, + Middlewares: []string{ + "default-my-first-middleware", + "default-my-second-middleware", + }, }, }, Middlewares: map[string]*dynamic.Middleware{}, @@ -2497,7 +2500,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { }, }, { - desc: "HTTPRoute with ExtensionRef filter and create middleware", + desc: "ExtensionRef filter with middleware creation", groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{ traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) { return namespace + "-" + name, &dynamic.Middleware{Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, nil @@ -2525,11 +2528,15 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { Rule: "Host(`foo.com`) && Path(`/bar`)", Priority: 100008, RuleSyntax: "v3", - Middlewares: []string{"default-my-middleware"}, + Middlewares: []string{ + "default-my-first-middleware", + "default-my-second-middleware", + }, }, }, Middlewares: map[string]*dynamic.Middleware{ - "default-my-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, + "default-my-first-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, + "default-my-second-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, }, Services: map[string]*dynamic.Service{ "default-http-app-1-my-gateway-web-0-wrr": { @@ -2565,7 +2572,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { }, }, { - desc: "ExtensionRef filter: Unknown", + desc: "Unknown ExtensionRef filter", entryPoints: map[string]Entrypoint{"web": { Address: ":80", }},