diff --git a/apis/applyconfiguration/apis/v1/grpcrouterule.go b/apis/applyconfiguration/apis/v1/grpcrouterule.go index 367fe37260..6281e3aaaf 100644 --- a/apis/applyconfiguration/apis/v1/grpcrouterule.go +++ b/apis/applyconfiguration/apis/v1/grpcrouterule.go @@ -18,9 +18,14 @@ limitations under the License. package v1 +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + // GRPCRouteRuleApplyConfiguration represents an declarative configuration of the GRPCRouteRule type for use // with apply. type GRPCRouteRuleApplyConfiguration struct { + Name *v1.SectionName `json:"name,omitempty"` Matches []GRPCRouteMatchApplyConfiguration `json:"matches,omitempty"` Filters []GRPCRouteFilterApplyConfiguration `json:"filters,omitempty"` BackendRefs []GRPCBackendRefApplyConfiguration `json:"backendRefs,omitempty"` @@ -33,6 +38,14 @@ func GRPCRouteRule() *GRPCRouteRuleApplyConfiguration { return &GRPCRouteRuleApplyConfiguration{} } +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *GRPCRouteRuleApplyConfiguration) WithName(value v1.SectionName) *GRPCRouteRuleApplyConfiguration { + b.Name = &value + return b +} + // WithMatches adds the given value to the Matches field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the Matches field. diff --git a/apis/applyconfiguration/apis/v1/httprouterule.go b/apis/applyconfiguration/apis/v1/httprouterule.go index dcef6b12e2..7e9f4d8239 100644 --- a/apis/applyconfiguration/apis/v1/httprouterule.go +++ b/apis/applyconfiguration/apis/v1/httprouterule.go @@ -18,9 +18,14 @@ limitations under the License. package v1 +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + // HTTPRouteRuleApplyConfiguration represents an declarative configuration of the HTTPRouteRule type for use // with apply. type HTTPRouteRuleApplyConfiguration struct { + Name *v1.SectionName `json:"name,omitempty"` Matches []HTTPRouteMatchApplyConfiguration `json:"matches,omitempty"` Filters []HTTPRouteFilterApplyConfiguration `json:"filters,omitempty"` BackendRefs []HTTPBackendRefApplyConfiguration `json:"backendRefs,omitempty"` @@ -34,6 +39,14 @@ func HTTPRouteRule() *HTTPRouteRuleApplyConfiguration { return &HTTPRouteRuleApplyConfiguration{} } +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *HTTPRouteRuleApplyConfiguration) WithName(value v1.SectionName) *HTTPRouteRuleApplyConfiguration { + b.Name = &value + return b +} + // WithMatches adds the given value to the Matches field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the Matches field. diff --git a/apis/applyconfiguration/apis/v1alpha2/tcprouterule.go b/apis/applyconfiguration/apis/v1alpha2/tcprouterule.go index 3c6e418c2f..711556120c 100644 --- a/apis/applyconfiguration/apis/v1alpha2/tcprouterule.go +++ b/apis/applyconfiguration/apis/v1alpha2/tcprouterule.go @@ -19,13 +19,15 @@ limitations under the License. package v1alpha2 import ( - v1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + apisv1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) // TCPRouteRuleApplyConfiguration represents an declarative configuration of the TCPRouteRule type for use // with apply. type TCPRouteRuleApplyConfiguration struct { - BackendRefs []v1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` + Name *v1.SectionName `json:"name,omitempty"` + BackendRefs []apisv1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` } // TCPRouteRuleApplyConfiguration constructs an declarative configuration of the TCPRouteRule type for use with @@ -34,10 +36,18 @@ func TCPRouteRule() *TCPRouteRuleApplyConfiguration { return &TCPRouteRuleApplyConfiguration{} } +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TCPRouteRuleApplyConfiguration) WithName(value v1.SectionName) *TCPRouteRuleApplyConfiguration { + b.Name = &value + return b +} + // WithBackendRefs adds the given value to the BackendRefs field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the BackendRefs field. -func (b *TCPRouteRuleApplyConfiguration) WithBackendRefs(values ...*v1.BackendRefApplyConfiguration) *TCPRouteRuleApplyConfiguration { +func (b *TCPRouteRuleApplyConfiguration) WithBackendRefs(values ...*apisv1.BackendRefApplyConfiguration) *TCPRouteRuleApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithBackendRefs") diff --git a/apis/applyconfiguration/apis/v1alpha2/tlsrouterule.go b/apis/applyconfiguration/apis/v1alpha2/tlsrouterule.go index a2dacdf9b8..d57b6d91f5 100644 --- a/apis/applyconfiguration/apis/v1alpha2/tlsrouterule.go +++ b/apis/applyconfiguration/apis/v1alpha2/tlsrouterule.go @@ -19,13 +19,15 @@ limitations under the License. package v1alpha2 import ( - v1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + apisv1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) // TLSRouteRuleApplyConfiguration represents an declarative configuration of the TLSRouteRule type for use // with apply. type TLSRouteRuleApplyConfiguration struct { - BackendRefs []v1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` + Name *v1.SectionName `json:"name,omitempty"` + BackendRefs []apisv1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` } // TLSRouteRuleApplyConfiguration constructs an declarative configuration of the TLSRouteRule type for use with @@ -34,10 +36,18 @@ func TLSRouteRule() *TLSRouteRuleApplyConfiguration { return &TLSRouteRuleApplyConfiguration{} } +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TLSRouteRuleApplyConfiguration) WithName(value v1.SectionName) *TLSRouteRuleApplyConfiguration { + b.Name = &value + return b +} + // WithBackendRefs adds the given value to the BackendRefs field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the BackendRefs field. -func (b *TLSRouteRuleApplyConfiguration) WithBackendRefs(values ...*v1.BackendRefApplyConfiguration) *TLSRouteRuleApplyConfiguration { +func (b *TLSRouteRuleApplyConfiguration) WithBackendRefs(values ...*apisv1.BackendRefApplyConfiguration) *TLSRouteRuleApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithBackendRefs") diff --git a/apis/applyconfiguration/apis/v1alpha2/udprouterule.go b/apis/applyconfiguration/apis/v1alpha2/udprouterule.go index 8cc077ecc9..0fdf1bf95d 100644 --- a/apis/applyconfiguration/apis/v1alpha2/udprouterule.go +++ b/apis/applyconfiguration/apis/v1alpha2/udprouterule.go @@ -19,13 +19,15 @@ limitations under the License. package v1alpha2 import ( - v1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + apisv1 "sigs.k8s.io/gateway-api/apis/applyconfiguration/apis/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) // UDPRouteRuleApplyConfiguration represents an declarative configuration of the UDPRouteRule type for use // with apply. type UDPRouteRuleApplyConfiguration struct { - BackendRefs []v1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` + Name *v1.SectionName `json:"name,omitempty"` + BackendRefs []apisv1.BackendRefApplyConfiguration `json:"backendRefs,omitempty"` } // UDPRouteRuleApplyConfiguration constructs an declarative configuration of the UDPRouteRule type for use with @@ -34,10 +36,18 @@ func UDPRouteRule() *UDPRouteRuleApplyConfiguration { return &UDPRouteRuleApplyConfiguration{} } +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *UDPRouteRuleApplyConfiguration) WithName(value v1.SectionName) *UDPRouteRuleApplyConfiguration { + b.Name = &value + return b +} + // WithBackendRefs adds the given value to the BackendRefs field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the BackendRefs field. -func (b *UDPRouteRuleApplyConfiguration) WithBackendRefs(values ...*v1.BackendRefApplyConfiguration) *UDPRouteRuleApplyConfiguration { +func (b *UDPRouteRuleApplyConfiguration) WithBackendRefs(values ...*apisv1.BackendRefApplyConfiguration) *UDPRouteRuleApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithBackendRefs") diff --git a/apis/applyconfiguration/internal/internal.go b/apis/applyconfiguration/internal/internal.go index 21866f5425..ca9fbf2912 100644 --- a/apis/applyconfiguration/internal/internal.go +++ b/apis/applyconfiguration/internal/internal.go @@ -420,6 +420,9 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.gateway-api.apis.v1.GRPCRouteMatch elementRelationship: atomic + - name: name + type: + scalar: string - name: sessionPersistence type: namedType: io.k8s.sigs.gateway-api.apis.v1.SessionPersistence @@ -861,6 +864,9 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.gateway-api.apis.v1.HTTPRouteMatch elementRelationship: atomic + - name: name + type: + scalar: string - name: sessionPersistence type: namedType: io.k8s.sigs.gateway-api.apis.v1.SessionPersistence @@ -1290,6 +1296,9 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.gateway-api.apis.v1.BackendRef elementRelationship: atomic + - name: name + type: + scalar: string - name: io.k8s.sigs.gateway-api.apis.v1alpha2.TCPRouteSpec map: fields: @@ -1344,6 +1353,9 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.gateway-api.apis.v1.BackendRef elementRelationship: atomic + - name: name + type: + scalar: string - name: io.k8s.sigs.gateway-api.apis.v1alpha2.TLSRouteSpec map: fields: @@ -1404,6 +1416,9 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.gateway-api.apis.v1.BackendRef elementRelationship: atomic + - name: name + type: + scalar: string - name: io.k8s.sigs.gateway-api.apis.v1alpha2.UDPRouteSpec map: fields: diff --git a/apis/v1/grpcroute_types.go b/apis/v1/grpcroute_types.go index 91a8a3d268..5bfc4a4d91 100644 --- a/apis/v1/grpcroute_types.go +++ b/apis/v1/grpcroute_types.go @@ -143,6 +143,7 @@ type GRPCRouteSpec struct { // // +optional // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:message="Route name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))" Rules []GRPCRouteRule `json:"rules,omitempty"` } @@ -150,6 +151,12 @@ type GRPCRouteSpec struct { // conditions (matches), processing it (filters), and forwarding the request to // an API object (backendRefs). type GRPCRouteRule struct { + // Name is the name of the route rule. This name MUST be unique within a Route if it is set. + // + // Support: Extended + // +optional + Name *SectionName `json:"name,omitempty"` + // Matches define conditions used for matching the rule against incoming // gRPC requests. Each match is independent, i.e. this rule will be matched // if **any** one of the matches is satisfied. diff --git a/apis/v1/httproute_types.go b/apis/v1/httproute_types.go index fda3666e18..cf01deb5b9 100644 --- a/apis/v1/httproute_types.go +++ b/apis/v1/httproute_types.go @@ -117,6 +117,7 @@ type HTTPRouteSpec struct { // Rules are a list of HTTP matchers, filters and actions. // // +optional + // +kubebuilder:validation:XValidation:message="Route name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))" // +kubebuilder:validation:MaxItems=16 // +kubebuilder:default={{matches: {{path: {type: "PathPrefix", value: "/"}}}}} Rules []HTTPRouteRule `json:"rules,omitempty"` @@ -132,6 +133,12 @@ type HTTPRouteSpec struct { // +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" // +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" type HTTPRouteRule struct { + // Name is the name of the route rule. This name MUST be unique within a Route if it is set. + // + // Support: Extended + // +optional + Name *SectionName `json:"name,omitempty"` + // Matches define conditions used for matching the rule against incoming // HTTP requests. Each match is independent, i.e. this rule will be matched // if **any** one of the matches is satisfied. diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go index ddb9bb9d49..782e9a2742 100644 --- a/apis/v1/zz_generated.deepcopy.go +++ b/apis/v1/zz_generated.deepcopy.go @@ -369,6 +369,11 @@ func (in *GRPCRouteMatch) DeepCopy() *GRPCRouteMatch { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GRPCRouteRule) DeepCopyInto(out *GRPCRouteRule) { *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(SectionName) + **out = **in + } if in.Matches != nil { in, out := &in.Matches, &out.Matches *out = make([]GRPCRouteMatch, len(*in)) @@ -1164,6 +1169,11 @@ func (in *HTTPRouteMatch) DeepCopy() *HTTPRouteMatch { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) { *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(SectionName) + **out = **in + } if in.Matches != nil { in, out := &in.Matches, &out.Matches *out = make([]HTTPRouteMatch, len(*in)) diff --git a/apis/v1alpha2/tcproute_types.go b/apis/v1alpha2/tcproute_types.go index fe927ab8d4..3178558623 100644 --- a/apis/v1alpha2/tcproute_types.go +++ b/apis/v1alpha2/tcproute_types.go @@ -49,6 +49,7 @@ type TCPRouteSpec struct { // // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:message="Route name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))" Rules []TCPRouteRule `json:"rules"` } @@ -59,6 +60,12 @@ type TCPRouteStatus struct { // TCPRouteRule is the configuration for a given rule. type TCPRouteRule struct { + // Name is the name of the route rule. This name MUST be unique within a Route if it is set. + // + // Support: Extended + // +optional + Name *SectionName `json:"name,omitempty"` + // BackendRefs defines the backend(s) where matching requests should be // sent. If unspecified or invalid (refers to a non-existent resource or a // Service with no endpoints), the underlying implementation MUST actively diff --git a/apis/v1alpha2/tlsroute_types.go b/apis/v1alpha2/tlsroute_types.go index afe34d82d6..d6d2b25e73 100644 --- a/apis/v1alpha2/tlsroute_types.go +++ b/apis/v1alpha2/tlsroute_types.go @@ -90,6 +90,7 @@ type TLSRouteSpec struct { // // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:message="Route name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))" Rules []TLSRouteRule `json:"rules"` } @@ -100,6 +101,12 @@ type TLSRouteStatus struct { // TLSRouteRule is the configuration for a given rule. type TLSRouteRule struct { + // Name is the name of the route rule. This name MUST be unique within a Route if it is set. + // + // Support: Extended + // +optional + Name *SectionName `json:"name,omitempty"` + // BackendRefs defines the backend(s) where matching requests should be // sent. If unspecified or invalid (refers to a non-existent resource or // a Service with no endpoints), the rule performs no forwarding; if no diff --git a/apis/v1alpha2/udproute_types.go b/apis/v1alpha2/udproute_types.go index 9e3770c293..05ac7a20ac 100644 --- a/apis/v1alpha2/udproute_types.go +++ b/apis/v1alpha2/udproute_types.go @@ -49,6 +49,7 @@ type UDPRouteSpec struct { // // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:message="Route name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))" Rules []UDPRouteRule `json:"rules"` } @@ -59,6 +60,12 @@ type UDPRouteStatus struct { // UDPRouteRule is the configuration for a given rule. type UDPRouteRule struct { + // Name is the name of the route rule. This name MUST be unique within a Route if it is set. + // + // Support: Extended + // +optional + Name *SectionName `json:"name,omitempty"` + // BackendRefs defines the backend(s) where matching requests should be // sent. If unspecified or invalid (refers to a non-existent resource or a // Service with no endpoints), the underlying implementation MUST actively diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index 3bf9f0fbe6..5306ca135d 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -390,6 +390,11 @@ func (in *TCPRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPRouteRule) DeepCopyInto(out *TCPRouteRule) { *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(v1.SectionName) + **out = **in + } if in.BackendRefs != nil { in, out := &in.BackendRefs, &out.BackendRefs *out = make([]v1.BackendRef, len(*in)) @@ -510,6 +515,11 @@ func (in *TLSRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSRouteRule) DeepCopyInto(out *TLSRouteRule) { *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(v1.SectionName) + **out = **in + } if in.BackendRefs != nil { in, out := &in.BackendRefs, &out.BackendRefs *out = make([]v1.BackendRef, len(*in)) @@ -635,6 +645,11 @@ func (in *UDPRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UDPRouteRule) DeepCopyInto(out *UDPRouteRule) { *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(v1.SectionName) + **out = **in + } if in.BackendRefs != nil { in, out := &in.BackendRefs, &out.BackendRefs *out = make([]v1.BackendRef, len(*in)) diff --git a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml index a72329aa2b..6cd6a741fd 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml @@ -1927,6 +1927,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string sessionPersistence: description: |+ SessionPersistence defines and configures session persistence @@ -2030,6 +2040,10 @@ spec: type: object maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of GRPCRoute. @@ -4254,6 +4268,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string sessionPersistence: description: |+ SessionPersistence defines and configures session persistence @@ -4357,6 +4381,10 @@ spec: type: object maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of GRPCRoute. diff --git a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml index 88609d2e72..1dc0acbf7b 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml @@ -2714,6 +2714,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string sessionPersistence: description: |+ SessionPersistence defines and configures session persistence @@ -2928,6 +2938,10 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of HTTPRoute. @@ -5945,6 +5959,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string sessionPersistence: description: |+ SessionPersistence defines and configures session persistence @@ -6159,6 +6183,10 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of HTTPRoute. diff --git a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml index 35917cfbd9..a0db2b6541 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml @@ -486,10 +486,24 @@ spec: maxItems: 16 minItems: 1 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) required: - rules type: object diff --git a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml index 47bbd3a7d6..69266d4858 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml @@ -559,10 +559,24 @@ spec: maxItems: 16 minItems: 1 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) required: - rules type: object diff --git a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml index d399e27c0e..ad23d65661 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml @@ -486,10 +486,24 @@ spec: maxItems: 16 minItems: 1 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) required: - rules type: object diff --git a/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml b/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml index e53e2d15e6..969780ddc3 100644 --- a/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml @@ -1898,9 +1898,23 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of GRPCRoute. @@ -4081,9 +4095,23 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of GRPCRoute. diff --git a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml index a81ffb06e8..4d3e4d9ef0 100644 --- a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml @@ -2685,6 +2685,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with @@ -2728,6 +2738,10 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of HTTPRoute. @@ -5701,6 +5715,16 @@ spec: type: object maxItems: 8 type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with @@ -5744,6 +5768,10 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-validations: + - message: Route name must be unique within the route + rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) + && l1.name == l2.name)) type: object status: description: Status defines the current state of HTTPRoute. diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 9b36da0bb4..91335bf3de 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -3284,6 +3284,13 @@ func schema_sigsk8sio_gateway_api_apis_v1_GRPCRouteRule(ref common.ReferenceCall Description: "GRPCRouteRule defines the semantics for matching a gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs).", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the route rule. This name MUST be unique within a Route if it is set.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, "matches": { SchemaProps: spec.SchemaProps{ Description: "Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied.\n\nFor example, take the following matches configuration:\n\n``` matches: - method:\n service: foo.bar\n headers:\n values:\n version: 2\n- method:\n service: foo.bar.v2\n```\n\nFor a request to match against this rule, it MUST satisfy EITHER of the two conditions:\n\n- service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2\n\nSee the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together.\n\nIf no matches are specified, the implementation MUST match every gRPC request.\n\nProxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of:\n\n* Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches.\n\nIf ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties:\n\n* The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by\n \"{namespace}/{name}\".\n\nIf ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria.", @@ -4657,6 +4664,13 @@ func schema_sigsk8sio_gateway_api_apis_v1_HTTPRouteRule(ref common.ReferenceCall Description: "HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs).", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the route rule. This name MUST be unique within a Route if it is set.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, "matches": { SchemaProps: spec.SchemaProps{ Description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied.\n\nFor example, take the following matches configuration:\n\n``` matches: - path:\n value: \"/foo\"\n headers:\n - name: \"version\"\n value: \"v2\"\n- path:\n value: \"/v2/foo\"\n```\n\nFor a request to match against this rule, a request must satisfy EITHER of the two conditions:\n\n- path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo`\n\nSee the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together.\n\nIf no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request.\n\nProxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match having:\n\n* \"Exact\" path match. * \"Prefix\" path match with largest number of characters. * Method match. * Largest number of header matches. * Largest number of query param matches.\n\nNote: The precedence of RegularExpression path matches are implementation-specific.\n\nIf ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties:\n\n* The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by\n \"{namespace}/{name}\".\n\nIf ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria.\n\nWhen no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned.", @@ -6093,6 +6107,13 @@ func schema_sigsk8sio_gateway_api_apis_v1alpha2_TCPRouteRule(ref common.Referenc Description: "TCPRouteRule is the configuration for a given rule.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the route rule. This name MUST be unique within a Route if it is set.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, "backendRefs": { SchemaProps: spec.SchemaProps{ Description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead.\n\nSupport: Core for Kubernetes Service\n\nSupport: Extended for Kubernetes ServiceImport\n\nSupport: Implementation-specific for any other resource\n\nSupport for weight: Extended", @@ -6295,6 +6316,13 @@ func schema_sigsk8sio_gateway_api_apis_v1alpha2_TLSRouteRule(ref common.Referenc Description: "TLSRouteRule is the configuration for a given rule.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the route rule. This name MUST be unique within a Route if it is set.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, "backendRefs": { SchemaProps: spec.SchemaProps{ Description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead.\n\nSupport: Core for Kubernetes Service\n\nSupport: Extended for Kubernetes ServiceImport\n\nSupport: Implementation-specific for any other resource\n\nSupport for weight: Extended", @@ -6512,6 +6540,13 @@ func schema_sigsk8sio_gateway_api_apis_v1alpha2_UDPRouteRule(ref common.Referenc Description: "UDPRouteRule is the configuration for a given rule.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the route rule. This name MUST be unique within a Route if it is set.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, "backendRefs": { SchemaProps: spec.SchemaProps{ Description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead.\n\nSupport: Core for Kubernetes Service\n\nSupport: Extended for Kubernetes ServiceImport\n\nSupport: Implementation-specific for any other resource\n\nSupport for weight: Extended", diff --git a/pkg/test/cel/httproute_test.go b/pkg/test/cel/httproute_test.go index 9dcc022b03..c8e82d735c 100644 --- a/pkg/test/cel/httproute_test.go +++ b/pkg/test/cel/httproute_test.go @@ -1209,6 +1209,18 @@ func TestHTTPRouteRule(t *testing.T) { }, }, }, + { + name: "invalid because multiple names are repeated", + wantErrors: []string{"Route name must be unique within the route"}, + rules: []gatewayv1.HTTPRouteRule{ + { + Name: ptrTo(gatewayv1.SectionName("name1")), + }, + { + Name: ptrTo(gatewayv1.SectionName("name1")), + }, + }, + }, } for _, tc := range tests {