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 736e80982a..cecdf14aeb 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 ba1f2ff4ea..6c2f19d68f 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 354171c40d..46a15b4f8f 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml @@ -2708,6 +2708,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 @@ -2922,6 +2932,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. @@ -5933,6 +5947,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 @@ -6147,6 +6171,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 e4a270e697..873f7fd776 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 ae763ef238..1a6b0110c8 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 81e36ea753..7d2eaca5e4 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 15e4204282..9fc3b96902 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 043b26d7bd..54b7fb7d02 100644 --- a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml @@ -2679,6 +2679,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 @@ -2722,6 +2732,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. @@ -5689,6 +5703,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 @@ -5732,6 +5756,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/conformance/echo-basic/grpcechoserver/grpcecho.pb.go b/conformance/echo-basic/grpcechoserver/grpcecho.pb.go index 9585b7760b..51a413a421 100644 --- a/conformance/echo-basic/grpcechoserver/grpcecho.pb.go +++ b/conformance/echo-basic/grpcechoserver/grpcecho.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.33.0 // protoc v4.22.2 // source: grpcecho.proto diff --git a/conformance/echo-basic/grpcechoserver/grpcecho_grpc.pb.go b/conformance/echo-basic/grpcechoserver/grpcecho_grpc.pb.go index f367cb6651..1ed7a03def 100644 --- a/conformance/echo-basic/grpcechoserver/grpcecho_grpc.pb.go +++ b/conformance/echo-basic/grpcechoserver/grpcecho_grpc.pb.go @@ -1,9 +1,25 @@ +// Copyright 2024 The Kubernetes Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 +// - protoc-gen-go-grpc v1.3.0 // - protoc v4.22.2 // source: grpcecho.proto +// This package name should in general be kept in sync with the directory in which it lives. + package grpcechoserver import ( @@ -18,6 +34,12 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + GrpcEcho_Echo_FullMethodName = "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/Echo" + GrpcEcho_EchoTwo_FullMethodName = "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoTwo" + GrpcEcho_EchoThree_FullMethodName = "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoThree" +) + // GrpcEchoClient is the client API for GrpcEcho service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. @@ -40,7 +62,7 @@ func NewGrpcEchoClient(cc grpc.ClientConnInterface) GrpcEchoClient { func (c *grpcEchoClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { out := new(EchoResponse) - err := c.cc.Invoke(ctx, "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/Echo", in, out, opts...) + err := c.cc.Invoke(ctx, GrpcEcho_Echo_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -49,7 +71,7 @@ func (c *grpcEchoClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc func (c *grpcEchoClient) EchoTwo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { out := new(EchoResponse) - err := c.cc.Invoke(ctx, "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoTwo", in, out, opts...) + err := c.cc.Invoke(ctx, GrpcEcho_EchoTwo_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -58,7 +80,7 @@ func (c *grpcEchoClient) EchoTwo(ctx context.Context, in *EchoRequest, opts ...g func (c *grpcEchoClient) EchoThree(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { out := new(EchoResponse) - err := c.cc.Invoke(ctx, "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoThree", in, out, opts...) + err := c.cc.Invoke(ctx, GrpcEcho_EchoThree_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -114,7 +136,7 @@ func _GrpcEcho_Echo_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/Echo", + FullMethod: GrpcEcho_Echo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(GrpcEchoServer).Echo(ctx, req.(*EchoRequest)) @@ -132,7 +154,7 @@ func _GrpcEcho_EchoTwo_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoTwo", + FullMethod: GrpcEcho_EchoTwo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(GrpcEchoServer).EchoTwo(ctx, req.(*EchoRequest)) @@ -150,7 +172,7 @@ func _GrpcEcho_EchoThree_Handler(srv interface{}, ctx context.Context, dec func( } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/gateway_api_conformance.echo_basic.grpcecho.GrpcEcho/EchoThree", + FullMethod: GrpcEcho_EchoThree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(GrpcEchoServer).EchoThree(ctx, req.(*EchoRequest)) diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 42b6eb6227..66de5ca0cf 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 {