-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathroute_rule_transform.go
More file actions
171 lines (149 loc) · 6.03 KB
/
Copy pathroute_rule_transform.go
File metadata and controls
171 lines (149 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package routeutils
import (
"fmt"
"strings"
elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
)
const (
replaceWholeHostHeaderRegex = ".*"
replaceWholePathMinusQueryParamsRegex = "^([^?]*)"
)
func BuildRoutingRuleTransforms(gwRoute RouteDescriptor, gwRule RulePrecedence) []elbv2model.Transform {
switch gwRoute.GetRouteKind() {
case HTTPRouteKind:
return buildHTTPRuleTransforms(gwRule.CommonRulePrecedence.Rule.GetRawRouteRule().(*gwv1.HTTPRouteRule), gwRule.HTTPMatch)
default:
return []elbv2model.Transform{}
}
}
func buildHTTPRuleTransforms(rule *gwv1.HTTPRouteRule, httpMatch *gwv1.HTTPRouteMatch) []elbv2model.Transform {
var transforms []elbv2model.Transform
if rule != nil {
for _, rf := range rule.Filters {
if rf.URLRewrite != nil {
if rf.URLRewrite.Path != nil {
transforms = append(transforms, generateURLRewritePathTransform(*rf.URLRewrite.Path, httpMatch))
}
if rf.URLRewrite.Hostname != nil {
transforms = append(transforms, generateHostHeaderRewriteTransform(*rf.URLRewrite.Hostname))
}
}
// Handle RequestRedirect with ReplacePrefixMatch as URLRewrite
if rf.RequestRedirect != nil && rf.RequestRedirect.Path != nil && rf.RequestRedirect.Path.ReplacePrefixMatch != nil {
transforms = append(transforms, generateURLRewritePathTransform(*rf.RequestRedirect.Path, httpMatch))
}
}
}
return transforms
}
func generateHostHeaderRewriteTransform(hostname gwv1.PreciseHostname) elbv2model.Transform {
return elbv2model.Transform{
Type: elbv2model.TransformTypeHostHeaderRewrite,
HostHeaderRewriteConfig: &elbv2model.RewriteConfigObject{
Rewrites: []elbv2model.RewriteConfig{
{
Regex: replaceWholeHostHeaderRegex,
Replace: string(hostname),
},
},
},
}
}
func generateURLRewritePathTransform(gwPathModifier gwv1.HTTPPathModifier, httpMatch *gwv1.HTTPRouteMatch) elbv2model.Transform {
var replacementRegex string
var replacement string
switch gwPathModifier.Type {
case gwv1.FullPathHTTPPathModifier:
// Capture just the path, not the query parameters
replacementRegex = replaceWholePathMinusQueryParamsRegex
replacement = *gwPathModifier.ReplaceFullPath
break
case gwv1.PrefixMatchHTTPPathModifier:
replacementRegex, replacement = generatePrefixReplacementRegex(httpMatch, *gwPathModifier.ReplacePrefixMatch)
break
default:
// Need to set route status as failed :blah:
// Probably do this in the routeutils loader step and for validation.
}
return elbv2model.Transform{
Type: elbv2model.TransformTypeUrlRewrite,
UrlRewriteConfig: &elbv2model.RewriteConfigObject{
Rewrites: []elbv2model.RewriteConfig{
{
Regex: replacementRegex,
Replace: replacement,
},
},
},
}
}
func generatePrefixReplacementRegex(httpMatch *gwv1.HTTPRouteMatch, replacement string) (string, string) {
match := *httpMatch.Path.Value
/*
If we're being asked to replace a prefix with "", we still need to keep one '/' to form a valid path.
Consider getting the path '/foo' and having the replacement string being '', we would transform '/foo' => ''
thereby leaving an invalid path of ''. We could (in theory) do this for all replacements, e.g. replace = 'cat'
we could transform this into '/cat' here, but tbh the user can also do this, and I'm not entirely
sure if we could handle all possible cases.
To explain the addition of $2, we set up an optional capture group after the initial prefix match. We only want
to add back the value of the optional capture group when the replacement doesn't already have a '/' suffix.
A couple examples:
Without the capture group, e.g. (^%s)
input path = '/foo/', prefixRegex = '(^/foo)', replacement value = '/cat/' results in '/cat//'
To extend the example, now consider using having the capture group and always adding that to the result.
input path = '/foo/', prefixRegex = '(^/foo(/)?)', replacement value = '/cat/$2' results in (again) '/cat//'
Without the capture group, we would have one '/' too few.
input path = '/foo/bar', prefixRegex = '(^/foo(/)?)', replacement value = '/cat$2' results in '/catbar'
*/
if replacement == "" {
replacement = "/"
} else if !strings.HasSuffix(replacement, "/") {
replacement = fmt.Sprintf("%s$2", replacement)
}
return fmt.Sprintf("(^%s(/)?)", match), replacement
}
// BuildListenerRuleConfigTransforms converts transforms from the ListenerRuleConfiguration CRD
// into internal model transforms. These are merged with any transforms generated from
// Gateway API HTTPRoute filters (e.g., URLRewrite).
func BuildListenerRuleConfigTransforms(ruleWithPrecedence RulePrecedence) []elbv2model.Transform {
rule := ruleWithPrecedence.CommonRulePrecedence.Rule
if rule.GetListenerRuleConfig() == nil {
return nil
}
crdTransforms := rule.GetListenerRuleConfig().Spec.Transforms
if len(crdTransforms) == 0 {
return nil
}
var transforms []elbv2model.Transform
for _, crdTransform := range crdTransforms {
transforms = append(transforms, convertCRDTransform(crdTransform))
}
return transforms
}
func convertCRDTransform(crdTransform elbv2gw.ListenerRuleTransform) elbv2model.Transform {
switch crdTransform.Type {
case elbv2gw.ListenerRuleTransformTypeHostHeaderRewrite:
return convertCRDHostHeaderRewriteTransform(crdTransform)
default:
return elbv2model.Transform{}
}
}
func convertCRDHostHeaderRewriteTransform(crdTransform elbv2gw.ListenerRuleTransform) elbv2model.Transform {
rewrites := make([]elbv2model.RewriteConfig, 0, len(crdTransform.HostHeaderRewriteConfig.Rewrites))
for _, r := range crdTransform.HostHeaderRewriteConfig.Rewrites {
rewrites = append(rewrites, elbv2model.RewriteConfig{
Regex: r.Regex,
Replace: r.Replace,
})
}
rewriteObj := &elbv2model.RewriteConfigObject{
Rewrites: rewrites,
SourceHeader: crdTransform.HostHeaderRewriteConfig.SourceHeader,
}
return elbv2model.Transform{
Type: elbv2model.TransformTypeHostHeaderRewrite,
HostHeaderRewriteConfig: rewriteObj,
}
}