Skip to content

Commit 5abaea3

Browse files
committed
[feat gw-api]remove ReplacePrefixMatch support
1 parent 1c2fbc4 commit 5abaea3

File tree

6 files changed

+108
-71
lines changed

6 files changed

+108
-71
lines changed

docs/guide/gateway/l7gateway.md

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ information see the [Gateway API Conformance Page](https://gateway-api.sigs.k8s.
185185
| HTTPRouteRule - HTTPRouteFilter - RequestHeaderModifier | Core | ❌-- [Limited Support](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/header-modification.html) |
186186
| HTTPRouteRule - HTTPRouteFilter - ResponseHeaderModifier | Core | ❌ |
187187
| HTTPRouteRule - HTTPRouteFilter - RequestMirror | Extended | ❌ |
188-
| HTTPRouteRule - HTTPRouteFilter - RequestRedirect | Core | |
188+
| HTTPRouteRule - HTTPRouteFilter - RequestRedirect | Core | ❌ -- ReplacePrefixMatch not supported |
189189
| HTTPRouteRule - HTTPRouteFilter - UrlRewrite | Extended | ✅ |
190190
| HTTPRouteRule - HTTPRouteFilter - CORS | Extended | ❌ |
191191
| HTTPRouteRule - HTTPRouteFilter - ExternalAuth | Extended | ❌ -- Use [ListenerRuleConfigurations](customization.md#customizing-l7-routing-rules) |
@@ -317,6 +317,87 @@ AND
317317

318318

319319

320+
##### RequestRedirect Path Modification Limitations
321+
322+
**ReplacePrefixMatch is NOT supported** in `RequestRedirect` due to ALB limitations. ALB redirect actions cannot preserve path suffixes during prefix replacement.
323+
324+
❌ **This configuration will be rejected:**
325+
```yaml
326+
apiVersion: gateway.networking.k8s.io/v1
327+
kind: HTTPRoute
328+
metadata:
329+
name: unsupported-redirect
330+
namespace: example-ns
331+
spec:
332+
parentRefs:
333+
- name: my-gateway
334+
rules:
335+
- matches:
336+
- path:
337+
type: PathPrefix
338+
value: /old-prefix
339+
filters:
340+
- type: RequestRedirect
341+
requestRedirect:
342+
path:
343+
type: ReplacePrefixMatch
344+
replacePrefixMatch: /new-prefix # NOT SUPPORTED
345+
```
346+
347+
✅ **Alternative 1: Use URLRewrite for path transformations** (performs internal rewrite before forwarding to backend):
348+
```yaml
349+
apiVersion: gateway.networking.k8s.io/v1
350+
kind: HTTPRoute
351+
metadata:
352+
name: rewrite-example
353+
namespace: example-ns
354+
spec:
355+
parentRefs:
356+
- name: my-gateway
357+
rules:
358+
- matches:
359+
- path:
360+
type: PathPrefix
361+
value: /old-prefix
362+
filters:
363+
- type: URLRewrite
364+
urlRewrite:
365+
path:
366+
type: ReplacePrefixMatch
367+
replacePrefixMatch: /new-prefix # SUPPORTED
368+
backendRefs:
369+
- name: my-service
370+
port: 80
371+
```
372+
373+
✅ **Alternative 2: Use ReplaceFullPath for static redirects**:
374+
```yaml
375+
apiVersion: gateway.networking.k8s.io/v1
376+
kind: HTTPRoute
377+
metadata:
378+
name: redirect-example
379+
namespace: example-ns
380+
spec:
381+
parentRefs:
382+
- name: my-gateway
383+
rules:
384+
- matches:
385+
- path:
386+
type: PathPrefix
387+
value: /old-path
388+
filters:
389+
- type: RequestRedirect
390+
requestRedirect:
391+
path:
392+
type: ReplaceFullPath
393+
replaceFullPath: /new-path # SUPPORTED
394+
statusCode: 301
395+
```
396+
397+
**Key Differences:**
398+
- `RequestRedirect`: Returns HTTP 301/302 redirect response to client
399+
- `URLRewrite`: Transforms the path internally before forwarding to backend (no redirect visible to client)
400+
320401
##### Source IP Condition
321402

322403
```yaml

pkg/gateway/routeutils/route_rule_action.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func buildHttpRuleRedirectActionsBasedOnFilter(filters []gwv1.HTTPRouteFilter, r
216216
case gwv1.HTTPRouteFilterURLRewrite:
217217
continue
218218
default:
219-
return nil, errors.Errorf("Unsupported filter type: %v. Only request redirect is supported. To specify header modification, please configure it through LoadBalancerConfiguration.", filter.Type)
219+
return nil, errors.Errorf("Unsupported filter type: %v. To specify header modification, please configure it through LoadBalancerConfiguration.", filter.Type)
220220
}
221221
}
222222
return nil, nil
@@ -259,13 +259,7 @@ func buildHttpRedirectAction(filter *gwv1.HTTPRequestRedirectFilter, redirectCon
259259
path = filter.Path.ReplaceFullPath
260260
isComponentSpecified = true
261261
} else if filter.Path.ReplacePrefixMatch != nil {
262-
pathValue := *filter.Path.ReplacePrefixMatch
263-
if strings.ContainsAny(pathValue, "*?") {
264-
return nil, errors.Errorf("ReplacePrefixMatch shouldn't contain wildcards: %v", pathValue)
265-
}
266-
processedPath := fmt.Sprintf("%s/*", pathValue)
267-
path = &processedPath
268-
isComponentSpecified = true
262+
return nil, errors.Errorf("ReplacePrefixMatch is not supported in RequestRedirect filters. ALB redirect actions cannot preserve path suffixes during prefix replacement. Use URLRewrite filter with ReplacePrefixMatch for path transformations, or use ReplaceFullPath in RequestRedirect for static path redirects")
269263
}
270264
}
271265

pkg/gateway/routeutils/route_rule_action_test.go

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package routeutils
22

33
import (
44
"context"
5+
"testing"
6+
57
awssdk "github.com/aws/aws-sdk-go-v2/aws"
68
"github.com/stretchr/testify/assert"
79
corev1 "k8s.io/api/core/v1"
@@ -13,7 +15,6 @@ import (
1315
"sigs.k8s.io/controller-runtime/pkg/client"
1416
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1517
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
16-
"testing"
1718
)
1819

1920
func Test_buildHttpRedirectAction(t *testing.T) {
@@ -27,7 +28,6 @@ func Test_buildHttpRedirectAction(t *testing.T) {
2728
query := "test-query"
2829
replaceFullPath := "/new-path"
2930
replacePrefixPath := "/new-prefix-path"
30-
replacePrefixPathAfterProcessing := "/new-prefix-path/*"
3131
invalidPath := "/invalid-path*"
3232

3333
tests := []struct {
@@ -66,20 +66,15 @@ func Test_buildHttpRedirectAction(t *testing.T) {
6666
wantErr: false,
6767
},
6868
{
69-
name: "redirect with prefix match",
69+
name: "redirect with prefix match - not supported",
7070
filter: &gwv1.HTTPRequestRedirectFilter{
7171
Path: &gwv1.HTTPPathModifier{
7272
Type: gwv1.PrefixMatchHTTPPathModifier,
7373
ReplacePrefixMatch: &replacePrefixPath,
7474
},
7575
},
76-
want: &elbv2model.Action{
77-
Type: elbv2model.ActionTypeRedirect,
78-
RedirectConfig: &elbv2model.RedirectActionConfig{
79-
Path: &replacePrefixPathAfterProcessing,
80-
},
81-
},
82-
wantErr: false,
76+
want: nil,
77+
wantErr: true,
8378
},
8479
{
8580
name: "redirect with no component provided",
@@ -106,17 +101,6 @@ func Test_buildHttpRedirectAction(t *testing.T) {
106101
want: nil,
107102
wantErr: true,
108103
},
109-
{
110-
name: "path with wildcards in ReplacePrefixMatch",
111-
filter: &gwv1.HTTPRequestRedirectFilter{
112-
Path: &gwv1.HTTPPathModifier{
113-
Type: gwv1.PrefixMatchHTTPPathModifier,
114-
ReplacePrefixMatch: &invalidPath,
115-
},
116-
},
117-
want: nil,
118-
wantErr: true,
119-
},
120104
}
121105

122106
for _, tt := range tests {
@@ -209,6 +193,23 @@ func Test_BuildHttpRuleRedirectActionsBasedOnFilter(t *testing.T) {
209193
redirectConfig: nil,
210194
wantErr: false,
211195
},
196+
{
197+
name: "ReplacePrefixMatch in RequestRedirect should be rejected",
198+
filters: []gwv1.HTTPRouteFilter{
199+
{
200+
Type: gwv1.HTTPRouteFilterRequestRedirect,
201+
RequestRedirect: &gwv1.HTTPRequestRedirectFilter{
202+
Path: &gwv1.HTTPPathModifier{
203+
Type: gwv1.PrefixMatchHTTPPathModifier,
204+
ReplacePrefixMatch: awssdk.String("/new-prefix"),
205+
},
206+
},
207+
},
208+
},
209+
redirectConfig: nil,
210+
wantErr: true,
211+
errContains: "ReplacePrefixMatch is not supported in RequestRedirect",
212+
},
212213
}
213214

214215
for _, tt := range tests {
@@ -221,9 +222,9 @@ func Test_BuildHttpRuleRedirectActionsBasedOnFilter(t *testing.T) {
221222
assert.Contains(t, err.Error(), tt.errContains)
222223
}
223224
assert.Nil(t, actions)
224-
} else {
225-
assert.NoError(t, err)
225+
return
226226
}
227+
assert.NoError(t, err)
227228
})
228229
}
229230
}

test/e2e/gateway/alb_instance_target_test.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,6 @@ var _ = Describe("test k8s alb gateway using instance targets reconciled by the
469469
Header("Location").Equal("https://example.com:80/new-path")
470470
})
471471

472-
By("testing redirect with ReplacePrefixMatch", func() {
473-
httpExp := httpexpect.New(tf.LoggerReporter, fmt.Sprintf("http://%v", dnsName))
474-
httpExp.GET("/api/v1/users").WithRedirectPolicy(httpexpect.DontFollowRedirects).Expect().
475-
Status(302).
476-
Header("Location").Equal("https://api.example.com:80/v2/*")
477-
})
478-
479472
By("testing redirect with scheme and port change", func() {
480473
httpExp := httpexpect.New(tf.LoggerReporter, fmt.Sprintf("http://%v", dnsName))
481474
httpExp.GET("/secure").WithRedirectPolicy(httpexpect.DontFollowRedirects).Expect().

test/e2e/gateway/alb_ip_target_test.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -455,13 +455,6 @@ var _ = Describe("test k8s alb gateway using ip targets reconciled by the aws lo
455455
Header("Location").Equal("https://example.com:80/new-path")
456456
})
457457

458-
By("testing redirect with ReplacePrefixMatch", func() {
459-
httpExp := httpexpect.New(tf.LoggerReporter, fmt.Sprintf("http://%v", dnsName))
460-
httpExp.GET("/api/v1/users").WithRedirectPolicy(httpexpect.DontFollowRedirects).Expect().
461-
Status(302).
462-
Header("Location").Equal("https://api.example.com:80/v2/*")
463-
})
464-
465458
By("testing redirect with scheme and port change", func() {
466459
httpExp := httpexpect.New(tf.LoggerReporter, fmt.Sprintf("http://%v", dnsName))
467460
httpExp.GET("/secure").WithRedirectPolicy(httpexpect.DontFollowRedirects).Expect().

test/e2e/gateway/consts.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -128,31 +128,6 @@ var httpRouteRuleWithMatchesAndFilters = []gwv1.HTTPRouteRule{
128128
},
129129
},
130130
},
131-
{
132-
Matches: []gwv1.HTTPRouteMatch{
133-
{
134-
Path: &gwv1.HTTPPathMatch{
135-
Type: &[]gwv1.PathMatchType{gwv1.PathMatchPathPrefix}[0],
136-
Value: awssdk.String("/api"),
137-
},
138-
},
139-
},
140-
Filters: []gwv1.HTTPRouteFilter{
141-
{
142-
Type: gwv1.HTTPRouteFilterRequestRedirect,
143-
RequestRedirect: &gwv1.HTTPRequestRedirectFilter{
144-
Scheme: awssdk.String("https"),
145-
Hostname: (*gwv1.PreciseHostname)(awssdk.String("api.example.com")),
146-
Path: &gwv1.HTTPPathModifier{
147-
Type: gwv1.PrefixMatchHTTPPathModifier,
148-
ReplacePrefixMatch: awssdk.String("/v2"),
149-
},
150-
StatusCode: awssdk.Int(302),
151-
Port: &defaultPort,
152-
},
153-
},
154-
},
155-
},
156131
{
157132
Matches: []gwv1.HTTPRouteMatch{
158133
{

0 commit comments

Comments
 (0)