Skip to content

Commit adb94d2

Browse files
Add initial set of CEL validations for HTTPRoute
1 parent 3ae9fa7 commit adb94d2

9 files changed

+2656
-0
lines changed

apis/v1beta1/httproute_types.go

+47
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ type HTTPRouteSpec struct {
125125
// HTTPRouteRule defines semantics for matching an HTTP request based on
126126
// conditions (matches), processing it (filters), and forwarding the request to
127127
// an API object (backendRefs).
128+
//
129+
// +kubebuilder:validation:XValidation:message="RequestRedirect filter must not be used together with backendRefs",rule="(has(self.backendRefs) && size(self.backendRefs) > 0) ? (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): true"
130+
// +kubebuilder:validation:XValidation:message="When using RequestRedirect filter with Path.ReplacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == 'ReplacePrefixMatch' && has(f.requestRedirect.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
131+
// +kubebuilder:validation:XValidation:message="When using URLRewrite filter with Path.ReplacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == 'ReplacePrefixMatch' && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
132+
// +kubebuilder:validation:XValidation:message="Within BackendRefs, when using RequestRedirect filter with Path.ReplacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == 'ReplacePrefixMatch' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
133+
// +kubebuilder:validation:XValidation:message="Within BackendRefs, When using URLRewrite filter with Path.ReplacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == 'ReplacePrefixMatch' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
128134
type HTTPRouteRule struct {
129135
// Matches define conditions used for matching the rule against incoming
130136
// HTTP requests. Each match is independent, i.e. this rule will be matched
@@ -216,6 +222,11 @@ type HTTPRouteRule struct {
216222
//
217223
// +optional
218224
// +kubebuilder:validation:MaxItems=16
225+
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
226+
// +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.exists(f, f.type == 'RequestHeaderModifier') ? self.exists_one(f, f.type == 'RequestHeaderModifier') : true"
227+
// +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.exists(f, f.type == 'ResponseHeaderModifier') ? self.exists_one(f, f.type == 'ResponseHeaderModifier') : true"
228+
// +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.exists(f, f.type == 'RequestRedirect') ? self.exists_one(f, f.type == 'RequestRedirect') : true"
229+
// +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.exists(f, f.type == 'URLRewrite') ? self.exists_one(f, f.type == 'URLRewrite') : true"
219230
Filters []HTTPRouteFilter `json:"filters,omitempty"`
220231

221232
// BackendRefs defines the backend(s) where matching requests should be
@@ -306,6 +317,18 @@ const (
306317
)
307318

308319
// HTTPPathMatch describes how to select a HTTP route by matching the HTTP request path.
320+
//
321+
// +kubebuilder:validation:XValidation:message="value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? self.value.startsWith('/') : true"
322+
// +kubebuilder:validation:XValidation:message="must not contain '//' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('//') : true"
323+
// +kubebuilder:validation:XValidation:message="must not contain '/./' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('/./') : true"
324+
// +kubebuilder:validation:XValidation:message="must not contain '/../' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('/../') : true"
325+
// +kubebuilder:validation:XValidation:message="must not contain '%2f' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('%2f') : true"
326+
// +kubebuilder:validation:XValidation:message="must not contain '%2F' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('%2F') : true"
327+
// +kubebuilder:validation:XValidation:message="must not contain '#' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.contains('#') : true"
328+
// +kubebuilder:validation:XValidation:message="must not end with '/..' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.endsWith('/..') : true"
329+
// +kubebuilder:validation:XValidation:message="must not end with '/.' when type one of ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? !self.value.endsWith('/.') : true"
330+
// +kubebuilder:validation:XValidation:message="type must be one of ['Exact', 'PathPrefix', 'RegularExpression']",rule="self.type == 'Exact' || self.type == 'PathPrefix' || self.type == 'RegularExpression'"
331+
// +kubebuilder:validation:XValidation:message="must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix']",rule="(self.type == 'Exact' || self.type == 'PathPrefix') ? self.value.matches(r\"\"\"^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$\"\"\") : true"
309332
type HTTPPathMatch struct {
310333
// Type specifies how to match against the path Value.
311334
//
@@ -560,6 +583,19 @@ type HTTPRouteMatch struct {
560583
// examples include request or response modification, implementing
561584
// authentication strategies, rate-limiting, and traffic shaping. API
562585
// guarantee/conformance is defined based on the type of the filter.
586+
//
587+
// +kubebuilder:validation:XValidation:message="filter.RequestHeaderModifier must be nil if the HTTPRouteFilter.Type is not RequestHeaderModifier",rule="!(has(self.requestHeaderModifier) && self.type != 'RequestHeaderModifier')"
588+
// +kubebuilder:validation:XValidation:message="filter.RequestHeaderModifier must be specified for RequestHeaderModifier HTTPRouteFilter.Type",rule="!(!has(self.requestHeaderModifier) && self.type == 'RequestHeaderModifier')"
589+
// +kubebuilder:validation:XValidation:message="filter.ResponseHeaderModifier must be nil if the HTTPRouteFilter.Type is not ResponseHeaderModifier",rule="!(has(self.responseHeaderModifier) && self.type != 'ResponseHeaderModifier')"
590+
// +kubebuilder:validation:XValidation:message="filter.ResponseHeaderModifier must be specified for ResponseHeaderModifier HTTPRouteFilter.Type",rule="!(!has(self.responseHeaderModifier) && self.type == 'ResponseHeaderModifier')"
591+
// +kubebuilder:validation:XValidation:message="filter.RequestMirror must be nil if the HTTPRouteFilter.Type is not RequestMirror",rule="!(has(self.requestMirror) && self.type != 'RequestMirror')"
592+
// +kubebuilder:validation:XValidation:message="filter.RequestMirror must be specified for RequestMirror HTTPRouteFilter.Type",rule="!(!has(self.requestMirror) && self.type == 'RequestMirror')"
593+
// +kubebuilder:validation:XValidation:message="filter.RequestRedirect must be nil if the HTTPRouteFilter.Type is not RequestRedirect",rule="!(has(self.requestRedirect) && self.type != 'RequestRedirect')"
594+
// +kubebuilder:validation:XValidation:message="filter.RequestRedirect must be specified for RequestRedirect HTTPRouteFilter.Type",rule="!(!has(self.requestRedirect) && self.type == 'RequestRedirect')"
595+
// +kubebuilder:validation:XValidation:message="filter.URLRewrite must be nil if the HTTPRouteFilter.Type is not URLRewrite",rule="!(has(self.urlRewrite) && self.type != 'URLRewrite')"
596+
// +kubebuilder:validation:XValidation:message="filter.URLRewrite must be specified for URLRewrite HTTPRouteFilter.Type",rule="!(!has(self.urlRewrite) && self.type == 'URLRewrite')"
597+
// +kubebuilder:validation:XValidation:message="filter.ExtensionRef must be nil if the HTTPRouteFilter.Type is not ExtensionRef",rule="!(has(self.extensionRef) && self.type != 'ExtensionRef')"
598+
// +kubebuilder:validation:XValidation:message="filter.ExtensionRef must be specified for ExtensionRef HTTPRouteFilter.Type",rule="!(!has(self.extensionRef) && self.type == 'ExtensionRef')"
563599
type HTTPRouteFilter struct {
564600
// Type identifies the type of filter to apply. As with other API fields,
565601
// types are classified into three conformance levels:
@@ -829,6 +865,11 @@ const (
829865
)
830866

831867
// HTTPPathModifier defines configuration for path modifiers.
868+
//
869+
// +kubebuilder:validation:XValidation:message="replaceFullPath must be set when type is set to 'ReplaceFullPath'",rule="self.type == 'ReplaceFullPath' ? has(self.replaceFullPath) : true"
870+
// +kubebuilder:validation:XValidation:message="type must be 'ReplaceFullPath' when replaceFullPath is set",rule="has(self.replaceFullPath) ? self.type == 'ReplaceFullPath' : true"
871+
// +kubebuilder:validation:XValidation:message="replacePrefixMatch must be set when type is set to 'ReplacePrefixMatch'",rule="self.type == 'ReplacePrefixMatch' ? has(self.replacePrefixMatch) : true"
872+
// +kubebuilder:validation:XValidation:message="type must be 'ReplacePrefixMatch' when replacePrefixMatch is set",rule="has(self.replacePrefixMatch) ? self.type == 'ReplacePrefixMatch' : true"
832873
type HTTPPathModifier struct {
833874
// Type defines the type of path modifier. Additional types may be
834875
// added in a future release of the API.
@@ -1054,6 +1095,12 @@ type HTTPBackendRef struct {
10541095
//
10551096
// +optional
10561097
// +kubebuilder:validation:MaxItems=16
1098+
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
1099+
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
1100+
// +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.exists(f, f.type == 'RequestHeaderModifier') ? self.exists_one(f, f.type == 'RequestHeaderModifier') : true"
1101+
// +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.exists(f, f.type == 'ResponseHeaderModifier') ? self.exists_one(f, f.type == 'ResponseHeaderModifier') : true"
1102+
// +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.exists(f, f.type == 'RequestRedirect') ? self.exists_one(f, f.type == 'RequestRedirect') : true"
1103+
// +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.exists(f, f.type == 'URLRewrite') ? self.exists_one(f, f.type == 'URLRewrite') : true"
10571104
Filters []HTTPRouteFilter `json:"filters,omitempty"`
10581105
}
10591106

apis/v1beta1/object_reference_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ type SecretObjectReference struct {
9191
// References to objects with invalid Group and Kind are not valid, and must
9292
// be rejected by the implementation, with appropriate Conditions set
9393
// on the containing object.
94+
//
95+
// +kubebuilder:validation:XValidation:message="Must have port for Service reference",rule="((!has(self.group) || size(self.group) == 0) && (!has(self.kind) || self.kind == 'Service')) ? has(self.port) : true"
9496
type BackendObjectReference struct {
9597
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
9698
// When unspecified or empty string, core API group is inferred.

config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)