@@ -17,257 +17,15 @@ limitations under the License.
17
17
package validation
18
18
19
19
import (
20
- "fmt"
21
- "net/http"
22
- "strings"
23
-
24
20
"k8s.io/apimachinery/pkg/util/validation/field"
25
21
26
22
gatewayv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
27
- gatewayv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
28
- )
29
-
30
- var (
31
- // repeatableHTTPRouteFilters are filter types that can are allowed to be
32
- // repeated multiple times in a rule.
33
- repeatableHTTPRouteFilters = []gatewayv1a2.HTTPRouteFilterType {
34
- gatewayv1b1 .HTTPRouteFilterExtensionRef ,
35
- }
36
-
37
- invalidPathSequences = []string {"//" , "/./" , "/../" , "%2f" , "%2F" , "#" }
38
- invalidPathSuffixes = []string {"/.." , "/." }
23
+ gatewayv1b1validation "sigs.k8s.io/gateway-api/apis/v1beta1/validation"
39
24
)
40
25
41
26
// ValidateHTTPRoute validates HTTPRoute according to the Gateway API specification.
42
27
// For additional details of the HTTPRoute spec, refer to:
43
- // https://gateway-api.sigs.k8s.io/v1alpha2 /references/spec/#gateway.networking.k8s.io/v1alpha2 .HTTPRoute
28
+ // https://gateway-api.sigs.k8s.io/v1beta1 /references/spec/#gateway.networking.k8s.io/v1beta1 .HTTPRoute
44
29
func ValidateHTTPRoute (route * gatewayv1a2.HTTPRoute ) field.ErrorList {
45
- return validateHTTPRouteSpec (& route .Spec , field .NewPath ("spec" ))
46
- }
47
-
48
- // validateHTTPRouteSpec validates that required fields of spec are set according to the
49
- // HTTPRoute specification.
50
- func validateHTTPRouteSpec (spec * gatewayv1a2.HTTPRouteSpec , path * field.Path ) field.ErrorList {
51
- var errs field.ErrorList
52
- for i , rule := range spec .Rules {
53
- errs = append (errs , validateHTTPRouteFilters (rule .Filters , rule .Matches , path .Child ("rules" ).Index (i ))... )
54
- for j , backendRef := range rule .BackendRefs {
55
- errs = append (errs , validateHTTPRouteFilters (backendRef .Filters , rule .Matches , path .Child ("rules" ).Index (i ).Child ("backendsrefs" ).Index (j ))... )
56
- }
57
- for j , m := range rule .Matches {
58
- matchPath := path .Child ("rules" ).Index (i ).Child ("matches" ).Index (j )
59
-
60
- if m .Path != nil {
61
- errs = append (errs , validateHTTPPathMatch (m .Path , matchPath .Child ("path" ))... )
62
- }
63
- if len (m .Headers ) > 0 {
64
- errs = append (errs , validateHTTPHeaderMatches (m .Headers , matchPath .Child ("headers" ))... )
65
- }
66
- if len (m .QueryParams ) > 0 {
67
- errs = append (errs , validateHTTPQueryParamMatches (m .QueryParams , matchPath .Child ("queryParams" ))... )
68
- }
69
- }
70
- }
71
- errs = append (errs , validateHTTPRouteBackendServicePorts (spec .Rules , path .Child ("rules" ))... )
72
- errs = append (errs , validateParentRefs (spec .ParentRefs , path .Child ("spec" ))... )
73
- return errs
74
- }
75
-
76
- // validateHTTPRouteBackendServicePorts validates that v1.Service backends always have a port.
77
- func validateHTTPRouteBackendServicePorts (rules []gatewayv1a2.HTTPRouteRule , path * field.Path ) field.ErrorList {
78
- var errs field.ErrorList
79
- path = path .Child ("rules" )
80
-
81
- for i , rule := range rules {
82
- for j , ref := range rule .BackendRefs {
83
- errs = append (errs , validateBackendRefServicePort (& ref .BackendRef , path .Index (i ).Child ("backendRefs" ).Index (j ))... )
84
- }
85
- }
86
-
87
- return errs
88
- }
89
-
90
- // validateHTTPRouteFilters validates that a list of core and extended filters
91
- // is used at most once and that the filter type matches its value
92
- func validateHTTPRouteFilters (filters []gatewayv1a2.HTTPRouteFilter , matches []gatewayv1a2.HTTPRouteMatch , path * field.Path ) field.ErrorList {
93
- var errs field.ErrorList
94
- counts := map [gatewayv1a2.HTTPRouteFilterType ]int {}
95
-
96
- for i , filter := range filters {
97
- counts [filter .Type ]++
98
- if filter .RequestRedirect != nil && filter .RequestRedirect .Path != nil {
99
- errs = append (errs , validateHTTPPathModifier (* filter .RequestRedirect .Path , matches , path .Index (i ).Child ("requestRedirect" , "path" ))... )
100
- }
101
- if filter .URLRewrite != nil && filter .URLRewrite .Path != nil {
102
- errs = append (errs , validateHTTPPathModifier (* filter .URLRewrite .Path , matches , path .Index (i ).Child ("urlRewrite" , "path" ))... )
103
- }
104
- errs = append (errs , validateHTTPRouteFilterTypeMatchesValue (filter , path .Index (i ))... )
105
- }
106
- // custom filters don't have any validation
107
- for _ , key := range repeatableHTTPRouteFilters {
108
- delete (counts , key )
109
- }
110
-
111
- if counts [gatewayv1b1 .HTTPRouteFilterRequestRedirect ] > 0 && counts [gatewayv1b1 .HTTPRouteFilterURLRewrite ] > 0 {
112
- errs = append (errs , field .Invalid (path .Child ("filters" ), gatewayv1b1 .HTTPRouteFilterRequestRedirect , "may specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both" ))
113
- }
114
-
115
- for filterType , count := range counts {
116
- if count > 1 {
117
- errs = append (errs , field .Invalid (path .Child ("filters" ), filterType , "cannot be used multiple times in the same rule" ))
118
- }
119
- }
120
- return errs
121
- }
122
-
123
- // webhook validation of HTTPPathMatch
124
- func validateHTTPPathMatch (path * gatewayv1a2.HTTPPathMatch , fldPath * field.Path ) field.ErrorList {
125
- allErrs := field.ErrorList {}
126
-
127
- if path .Type == nil {
128
- return append (allErrs , field .Required (fldPath .Child ("type" ), "must be specified" ))
129
- }
130
-
131
- if path .Value == nil {
132
- return append (allErrs , field .Required (fldPath .Child ("value" ), "must be specified" ))
133
- }
134
-
135
- switch * path .Type {
136
- case gatewayv1b1 .PathMatchExact , gatewayv1b1 .PathMatchPathPrefix :
137
- if ! strings .HasPrefix (* path .Value , "/" ) {
138
- allErrs = append (allErrs , field .Invalid (fldPath .Child ("value" ), * path .Value , "must be an absolute path" ))
139
- }
140
- if len (* path .Value ) > 0 {
141
- for _ , invalidSeq := range invalidPathSequences {
142
- if strings .Contains (* path .Value , invalidSeq ) {
143
- allErrs = append (allErrs , field .Invalid (fldPath .Child ("value" ), * path .Value , fmt .Sprintf ("must not contain %q" , invalidSeq )))
144
- }
145
- }
146
-
147
- for _ , invalidSuff := range invalidPathSuffixes {
148
- if strings .HasSuffix (* path .Value , invalidSuff ) {
149
- allErrs = append (allErrs , field .Invalid (fldPath .Child ("value" ), * path .Value , fmt .Sprintf ("cannot end with '%s'" , invalidSuff )))
150
- }
151
- }
152
- }
153
- case gatewayv1b1 .PathMatchRegularExpression :
154
- default :
155
- pathTypes := []string {string (gatewayv1b1 .PathMatchExact ), string (gatewayv1b1 .PathMatchPathPrefix ), string (gatewayv1b1 .PathMatchRegularExpression )}
156
- allErrs = append (allErrs , field .NotSupported (fldPath .Child ("type" ), * path .Type , pathTypes ))
157
- }
158
- return allErrs
159
- }
160
-
161
- // validateHTTPHeaderMatches validates that no header name
162
- // is matched more than once (case-insensitive).
163
- func validateHTTPHeaderMatches (matches []gatewayv1a2.HTTPHeaderMatch , path * field.Path ) field.ErrorList {
164
- var errs field.ErrorList
165
- counts := map [string ]int {}
166
-
167
- for _ , match := range matches {
168
- // Header names are case-insensitive.
169
- counts [strings .ToLower (string (match .Name ))]++
170
- }
171
-
172
- for name , count := range counts {
173
- if count > 1 {
174
- errs = append (errs , field .Invalid (path , http .CanonicalHeaderKey (name ), "cannot match the same header multiple times in the same rule" ))
175
- }
176
- }
177
-
178
- return errs
179
- }
180
-
181
- // validateHTTPQueryParamMatches validates that no query param name
182
- // is matched more than once (case-sensitive).
183
- func validateHTTPQueryParamMatches (matches []gatewayv1a2.HTTPQueryParamMatch , path * field.Path ) field.ErrorList {
184
- var errs field.ErrorList
185
- counts := map [string ]int {}
186
-
187
- for _ , match := range matches {
188
- // Query param names are case-sensitive.
189
- counts [string (match .Name )]++
190
- }
191
-
192
- for name , count := range counts {
193
- if count > 1 {
194
- errs = append (errs , field .Invalid (path , name , "cannot match the same query parameter multiple times in the same rule" ))
195
- }
196
- }
197
-
198
- return errs
199
- }
200
-
201
- // validateHTTPRouteFilterTypeMatchesValue validates that only the expected fields are
202
- // set for the specified filter type.
203
- func validateHTTPRouteFilterTypeMatchesValue (filter gatewayv1a2.HTTPRouteFilter , path * field.Path ) field.ErrorList {
204
- var errs field.ErrorList
205
- if filter .ExtensionRef != nil && filter .Type != gatewayv1b1 .HTTPRouteFilterExtensionRef {
206
- errs = append (errs , field .Invalid (path , filter .ExtensionRef , "must be nil if the HTTPRouteFilter.Type is not ExtensionRef" ))
207
- }
208
- if filter .ExtensionRef == nil && filter .Type == gatewayv1b1 .HTTPRouteFilterExtensionRef {
209
- errs = append (errs , field .Required (path , "filter.ExtensionRef must be specified for ExtensionRef HTTPRouteFilter.Type" ))
210
- }
211
- if filter .RequestHeaderModifier != nil && filter .Type != gatewayv1b1 .HTTPRouteFilterRequestHeaderModifier {
212
- errs = append (errs , field .Invalid (path , filter .RequestHeaderModifier , "must be nil if the HTTPRouteFilter.Type is not RequestHeaderModifier" ))
213
- }
214
- if filter .RequestHeaderModifier == nil && filter .Type == gatewayv1b1 .HTTPRouteFilterRequestHeaderModifier {
215
- errs = append (errs , field .Required (path , "filter.RequestHeaderModifier must be specified for RequestHeaderModifier HTTPRouteFilter.Type" ))
216
- }
217
- if filter .RequestMirror != nil && filter .Type != gatewayv1b1 .HTTPRouteFilterRequestMirror {
218
- errs = append (errs , field .Invalid (path , filter .RequestMirror , "must be nil if the HTTPRouteFilter.Type is not RequestMirror" ))
219
- }
220
- if filter .RequestMirror == nil && filter .Type == gatewayv1b1 .HTTPRouteFilterRequestMirror {
221
- errs = append (errs , field .Required (path , "filter.RequestMirror must be specified for RequestMirror HTTPRouteFilter.Type" ))
222
- }
223
- if filter .RequestRedirect != nil && filter .Type != gatewayv1b1 .HTTPRouteFilterRequestRedirect {
224
- errs = append (errs , field .Invalid (path , filter .RequestRedirect , "must be nil if the HTTPRouteFilter.Type is not RequestRedirect" ))
225
- }
226
- if filter .RequestRedirect == nil && filter .Type == gatewayv1b1 .HTTPRouteFilterRequestRedirect {
227
- errs = append (errs , field .Required (path , "filter.RequestRedirect must be specified for RequestRedirect HTTPRouteFilter.Type" ))
228
- }
229
- if filter .URLRewrite != nil && filter .Type != gatewayv1b1 .HTTPRouteFilterURLRewrite {
230
- errs = append (errs , field .Invalid (path , filter .URLRewrite , "must be nil if the HTTPRouteFilter.Type is not URLRewrite" ))
231
- }
232
- if filter .URLRewrite == nil && filter .Type == gatewayv1b1 .HTTPRouteFilterURLRewrite {
233
- errs = append (errs , field .Required (path , "filter.URLRewrite must be specified for URLRewrite HTTPRouteFilter.Type" ))
234
- }
235
- return errs
236
- }
237
-
238
- // validateHTTPPathModifier validates that only the expected fields are set in a
239
- // path modifier.
240
- func validateHTTPPathModifier (modifier gatewayv1a2.HTTPPathModifier , matches []gatewayv1a2.HTTPRouteMatch , path * field.Path ) field.ErrorList {
241
- var errs field.ErrorList
242
- if modifier .ReplaceFullPath != nil && modifier .Type != gatewayv1b1 .FullPathHTTPPathModifier {
243
- errs = append (errs , field .Invalid (path , modifier .ReplaceFullPath , "must be nil if the HTTPRouteFilter.Type is not ReplaceFullPath" ))
244
- }
245
- if modifier .ReplaceFullPath == nil && modifier .Type == gatewayv1b1 .FullPathHTTPPathModifier {
246
- errs = append (errs , field .Invalid (path , modifier .ReplaceFullPath , "must not be nil if the HTTPRouteFilter.Type is ReplaceFullPath" ))
247
- }
248
- if modifier .ReplacePrefixMatch != nil && modifier .Type != gatewayv1b1 .PrefixMatchHTTPPathModifier {
249
- errs = append (errs , field .Invalid (path , modifier .ReplacePrefixMatch , "must be nil if the HTTPRouteFilter.Type is not ReplacePrefixMatch" ))
250
- }
251
- if modifier .ReplacePrefixMatch == nil && modifier .Type == gatewayv1b1 .PrefixMatchHTTPPathModifier {
252
- errs = append (errs , field .Invalid (path , modifier .ReplacePrefixMatch , "must not be nil if the HTTPRouteFilter.Type is ReplacePrefixMatch" ))
253
- }
254
-
255
- if modifier .Type == gatewayv1b1 .PrefixMatchHTTPPathModifier && modifier .ReplacePrefixMatch != nil {
256
- if ! hasExactlyOnePrefixMatch (matches ) {
257
- errs = append (errs , field .Invalid (path , modifier .ReplacePrefixMatch , "exactly one PathPrefix match must be specified to use this path modifier" ))
258
- }
259
- }
260
- return errs
261
- }
262
-
263
- func hasExactlyOnePrefixMatch (matches []gatewayv1a2.HTTPRouteMatch ) bool {
264
- if len (matches ) != 1 || matches [0 ].Path == nil {
265
- return false
266
- }
267
- pathMatchType := matches [0 ].Path .Type
268
- if * pathMatchType != gatewayv1b1 .PathMatchPathPrefix {
269
- return false
270
- }
271
-
272
- return true
30
+ return gatewayv1b1validation .ValidateHTTPRouteSpec (& route .Spec , field .NewPath ("spec" ))
273
31
}
0 commit comments