Skip to content

Commit e0e8a29

Browse files
authored
feat: support custom redirects (envoyproxy#6295)
Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
1 parent 28424b0 commit e0e8a29

18 files changed

+369
-45
lines changed

api/v1alpha1/shared_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,10 @@ type CustomRedirect struct {
820820
// Path defines parameters used to modify the path of the incoming request.
821821
// The modified path is then used to construct the `Location` header. When
822822
// empty, the request path is used as-is.
823+
// Only ReplaceFullPath path modifier is supported currently.
823824
//
824825
// +optional
826+
// +kubebuilder:validation:XValidation:message="only ReplaceFullPath is supported for path.type",rule="self.type == 'ReplaceFullPath'"
825827
Path *gwapiv1.HTTPPathModifier `json:"path,omitempty"`
826828

827829
// Port is the port to be used in the value of the `Location`

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,7 @@ spec:
13421342
Path defines parameters used to modify the path of the incoming request.
13431343
The modified path is then used to construct the `Location` header. When
13441344
empty, the request path is used as-is.
1345+
Only ReplaceFullPath path modifier is supported currently.
13451346
properties:
13461347
replaceFullPath:
13471348
description: |-
@@ -1388,6 +1389,8 @@ spec:
13881389
- type
13891390
type: object
13901391
x-kubernetes-validations:
1392+
- message: only ReplaceFullPath is supported for path.type
1393+
rule: self.type == 'ReplaceFullPath'
13911394
- message: replaceFullPath must be specified when type is
13921395
set to 'ReplaceFullPath'
13931396
rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath)

charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,7 @@ spec:
13411341
Path defines parameters used to modify the path of the incoming request.
13421342
The modified path is then used to construct the `Location` header. When
13431343
empty, the request path is used as-is.
1344+
Only ReplaceFullPath path modifier is supported currently.
13441345
properties:
13451346
replaceFullPath:
13461347
description: |-
@@ -1387,6 +1388,8 @@ spec:
13871388
- type
13881389
type: object
13891390
x-kubernetes-validations:
1391+
- message: only ReplaceFullPath is supported for path.type
1392+
rule: self.type == 'ReplaceFullPath'
13901393
- message: replaceFullPath must be specified when type is
13911394
set to 'ReplaceFullPath'
13921395
rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath)

internal/gatewayapi/backendtrafficpolicy.go

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,25 +1051,52 @@ func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, resources *resou
10511051
}
10521052
}
10531053

1054-
response := ir.CustomResponse{
1055-
ContentType: ro.Response.ContentType,
1056-
}
1054+
if ro.Redirect != nil {
1055+
redirect := &ir.Redirect{
1056+
Scheme: ro.Redirect.Scheme,
1057+
}
1058+
if ro.Redirect.Path != nil {
1059+
redirect.Path = &ir.HTTPPathModifier{
1060+
FullReplace: ro.Redirect.Path.ReplaceFullPath,
1061+
PrefixMatchReplace: ro.Redirect.Path.ReplacePrefixMatch,
1062+
}
1063+
}
1064+
if ro.Redirect.Hostname != nil {
1065+
redirect.Hostname = ptr.To(string(*ro.Redirect.Hostname))
1066+
}
1067+
if ro.Redirect.Port != nil {
1068+
redirect.Port = ptr.To(uint32(*ro.Redirect.Port))
1069+
}
1070+
if ro.Redirect.StatusCode != nil {
1071+
redirect.StatusCode = ptr.To(int32(*ro.Redirect.StatusCode))
1072+
}
10571073

1058-
if ro.Response.StatusCode != nil {
1059-
response.StatusCode = ptr.To(uint32(*ro.Response.StatusCode))
1060-
}
1074+
rules = append(rules, ir.ResponseOverrideRule{
1075+
Name: defaultResponseOverrideRuleName(policy, index),
1076+
Match: match,
1077+
Redirect: redirect,
1078+
})
1079+
} else {
1080+
response := &ir.CustomResponse{
1081+
ContentType: ro.Response.ContentType,
1082+
}
10611083

1062-
var err error
1063-
response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace)
1064-
if err != nil {
1065-
return nil, err
1066-
}
1084+
if ro.Response.StatusCode != nil {
1085+
response.StatusCode = ptr.To(uint32(*ro.Response.StatusCode))
1086+
}
10671087

1068-
rules = append(rules, ir.ResponseOverrideRule{
1069-
Name: defaultResponseOverrideRuleName(policy, index),
1070-
Match: match,
1071-
Response: response,
1072-
})
1088+
var err error
1089+
response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace)
1090+
if err != nil {
1091+
return nil, err
1092+
}
1093+
1094+
rules = append(rules, ir.ResponseOverrideRule{
1095+
Name: defaultResponseOverrideRuleName(policy, index),
1096+
Match: match,
1097+
Response: response,
1098+
})
1099+
}
10731100
}
10741101
return &ir.ResponseOverride{
10751102
Name: irConfigName(policy),

internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,25 @@ httpRoutes:
8181
backendRefs:
8282
- name: service-1
8383
port: 8080
84+
- apiVersion: gateway.networking.k8s.io/v1
85+
kind: HTTPRoute
86+
metadata:
87+
namespace: default
88+
name: httproute-3
89+
spec:
90+
hostnames:
91+
- bar.envoyproxy.io
92+
parentRefs:
93+
- namespace: default
94+
name: gateway-2
95+
sectionName: http
96+
rules:
97+
- matches:
98+
- path:
99+
value: "/"
100+
backendRefs:
101+
- name: service-1
102+
port: 8080
84103
configMaps:
85104
- apiVersion: v1
86105
kind: ConfigMap
@@ -178,3 +197,25 @@ backendTrafficPolicies:
178197
- value: 403
179198
response:
180199
statusCode: 401
200+
- apiVersion: gateway.envoyproxy.io/v1alpha1
201+
kind: BackendTrafficPolicy
202+
metadata:
203+
namespace: default
204+
name: policy-for-route-3
205+
spec:
206+
targetRef:
207+
group: gateway.networking.k8s.io
208+
kind: HTTPRoute
209+
name: httproute-3
210+
responseOverride:
211+
- match:
212+
statusCodes:
213+
- value: 200
214+
redirect:
215+
scheme: https
216+
hostname: www.redirect.com
217+
port: 8443
218+
statusCode: 301
219+
path:
220+
type: ReplaceFullPath
221+
replaceFullPath: /redirect/path

internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,45 @@ backendTrafficPolicies:
8484
status: "True"
8585
type: Accepted
8686
controllerName: gateway.envoyproxy.io/gatewayclass-controller
87+
- apiVersion: gateway.envoyproxy.io/v1alpha1
88+
kind: BackendTrafficPolicy
89+
metadata:
90+
creationTimestamp: null
91+
name: policy-for-route-3
92+
namespace: default
93+
spec:
94+
responseOverride:
95+
- match:
96+
statusCodes:
97+
- type: null
98+
value: 200
99+
redirect:
100+
hostname: www.redirect.com
101+
path:
102+
replaceFullPath: /redirect/path
103+
type: ReplaceFullPath
104+
port: 8443
105+
scheme: https
106+
statusCode: 301
107+
targetRef:
108+
group: gateway.networking.k8s.io
109+
kind: HTTPRoute
110+
name: httproute-3
111+
status:
112+
ancestors:
113+
- ancestorRef:
114+
group: gateway.networking.k8s.io
115+
kind: Gateway
116+
name: gateway-2
117+
namespace: default
118+
sectionName: http
119+
conditions:
120+
- lastTransitionTime: null
121+
message: Policy has been accepted.
122+
reason: Accepted
123+
status: "True"
124+
type: Accepted
125+
controllerName: gateway.envoyproxy.io/gatewayclass-controller
87126
- apiVersion: gateway.envoyproxy.io/v1alpha1
88127
kind: BackendTrafficPolicy
89128
metadata:
@@ -193,7 +232,7 @@ gateways:
193232
protocol: HTTP
194233
status:
195234
listeners:
196-
- attachedRoutes: 2
235+
- attachedRoutes: 3
197236
conditions:
198237
- lastTransitionTime: null
199238
message: Sending translated listener configuration to the data plane
@@ -327,6 +366,44 @@ httpRoutes:
327366
name: gateway-2
328367
namespace: default
329368
sectionName: http
369+
- apiVersion: gateway.networking.k8s.io/v1
370+
kind: HTTPRoute
371+
metadata:
372+
creationTimestamp: null
373+
name: httproute-3
374+
namespace: default
375+
spec:
376+
hostnames:
377+
- bar.envoyproxy.io
378+
parentRefs:
379+
- name: gateway-2
380+
namespace: default
381+
sectionName: http
382+
rules:
383+
- backendRefs:
384+
- name: service-1
385+
port: 8080
386+
matches:
387+
- path:
388+
value: /
389+
status:
390+
parents:
391+
- conditions:
392+
- lastTransitionTime: null
393+
message: Route is accepted
394+
reason: Accepted
395+
status: "True"
396+
type: Accepted
397+
- lastTransitionTime: null
398+
message: Resolved all the Object references for the Route
399+
reason: ResolvedRefs
400+
status: "True"
401+
type: ResolvedRefs
402+
controllerName: gateway.envoyproxy.io/gatewayclass-controller
403+
parentRef:
404+
name: gateway-2
405+
namespace: default
406+
sectionName: http
330407
infraIR:
331408
default/gateway-1:
332409
proxy:
@@ -553,6 +630,51 @@ xdsIR:
553630
name: backendtrafficpolicy/default/policy-for-route-2/responseoverride/rule/0
554631
response:
555632
statusCode: 401
633+
- destination:
634+
metadata:
635+
kind: HTTPRoute
636+
name: httproute-3
637+
namespace: default
638+
name: httproute/default/httproute-3/rule/0
639+
settings:
640+
- addressType: IP
641+
endpoints:
642+
- host: 7.7.7.7
643+
port: 8080
644+
metadata:
645+
name: service-1
646+
namespace: default
647+
sectionName: "8080"
648+
name: httproute/default/httproute-3/rule/0/backend/0
649+
protocol: HTTP
650+
weight: 1
651+
hostname: bar.envoyproxy.io
652+
isHTTP2: false
653+
metadata:
654+
kind: HTTPRoute
655+
name: httproute-3
656+
namespace: default
657+
name: httproute/default/httproute-3/rule/0/match/0/bar_envoyproxy_io
658+
pathMatch:
659+
distinct: false
660+
name: ""
661+
prefix: /
662+
traffic:
663+
responseOverride:
664+
name: backendtrafficpolicy/default/policy-for-route-3
665+
rules:
666+
- match:
667+
statusCodes:
668+
- value: 200
669+
name: backendtrafficpolicy/default/policy-for-route-3/responseoverride/rule/0
670+
redirect:
671+
hostname: www.redirect.com
672+
path:
673+
fullReplace: /redirect/path
674+
prefixMatchReplace: null
675+
port: 8443
676+
scheme: https
677+
statusCode: 301
556678
readyListener:
557679
address: 0.0.0.0
558680
ipFamily: IPv4

internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ xdsIR:
137137
name: ""
138138
prefix: /
139139
redirect:
140-
hostname: null
141140
path:
142141
fullReplace: /redirected
143142
prefixMatchReplace: null

internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ xdsIR:
136136
prefix: /
137137
redirect:
138138
hostname: redirected.com
139-
path: null
140139
port: 443
141140
scheme: https
142141
statusCode: 301

internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ xdsIR:
138138
name: ""
139139
prefix: /
140140
redirect:
141-
hostname: null
142141
path:
143142
fullReplace: null
144143
prefixMatchReplace: /redirected

internal/ir/xds.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,9 @@ type ResponseOverrideRule struct {
591591
// Match configuration.
592592
Match CustomResponseMatch `json:"match"`
593593
// Response configuration.
594-
Response CustomResponse `json:"response"`
594+
Response *CustomResponse `json:"response,omitempty"`
595+
// Redirect configuration
596+
Redirect *Redirect `json:"redirect,omitempty"`
595597
}
596598

597599
// CustomResponseMatch defines the configuration for matching a user response to return a custom one.
@@ -1741,15 +1743,15 @@ func (r URLRewrite) Validate() error {
17411743
// +k8s:deepcopy-gen=true
17421744
type Redirect struct {
17431745
// Scheme configures the replacement of the request's scheme.
1744-
Scheme *string `json:"scheme" yaml:"scheme"`
1746+
Scheme *string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
17451747
// Hostname configures the replacement of the request's hostname.
1746-
Hostname *string `json:"hostname" yaml:"hostname"`
1748+
Hostname *string `json:"hostname,omitempty" yaml:"hostname,omitempty"`
17471749
// Path contains config for rewriting the path of the request.
1748-
Path *HTTPPathModifier `json:"path" yaml:"path"`
1750+
Path *HTTPPathModifier `json:"path,omitempty" yaml:"path,omitempty"`
17491751
// Port configures the replacement of the request's port.
1750-
Port *uint32 `json:"port" yaml:"port"`
1752+
Port *uint32 `json:"port,omitempty" yaml:"port,omitempty"`
17511753
// Status code configures the redirection response's status code.
1752-
StatusCode *int32 `json:"statusCode" yaml:"statusCode"`
1754+
StatusCode *int32 `json:"statusCode,omitempty" yaml:"statusCode,omitempty"`
17531755
}
17541756

17551757
// Validate the fields within the Redirect structure

0 commit comments

Comments
 (0)