From e46e958bcc228b67cd33530ac547dd824f424a26 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Wed, 27 Sep 2023 04:40:20 +0000 Subject: [PATCH] Requiring SectionName or Port to be specified for distinct parents Previously parents could be distinct if one specified a port and one specified a listener. Now they either both need to specify a distinct SectionName, both need to specify a distinct Port, or both. Co-authored-by: Gaurav Ghildiyal Co-authored-by: Nick Young --- apis/v1beta1/shared_types.go | 27 +++- .../gateway.networking.k8s.io_grpcroutes.yaml | 68 +++++---- .../gateway.networking.k8s.io_httproutes.yaml | 136 ++++++++++-------- .../gateway.networking.k8s.io_tcproutes.yaml | 68 +++++---- .../gateway.networking.k8s.io_tlsroutes.yaml | 68 +++++---- .../gateway.networking.k8s.io_udproutes.yaml | 68 +++++---- .../gateway.networking.k8s.io_httproutes.yaml | 90 +++++++----- pkg/test/cel/httproute_experimental_test.go | 11 +- 8 files changed, 310 insertions(+), 226 deletions(-) diff --git a/apis/v1beta1/shared_types.go b/apis/v1beta1/shared_types.go index 369328b530..411452ceb1 100644 --- a/apis/v1beta1/shared_types.go +++ b/apis/v1beta1/shared_types.go @@ -177,10 +177,25 @@ type CommonRouteSpec struct { // This API may be extended in the future to support additional kinds of parent // resources. // - // It is invalid to reference an identical parent more than once. It is - // valid to reference multiple distinct sections within the same parent - // resource, such as two separate Listeners on the same Gateway or two separate - // ports on the same Service. + // ParentRefs must be _distinct_. This means either that: + // + // * They select different objects. If this is the case, then parentRef + // entries are distinct. In terms of fields, this means that the + // multi-part key defined by `group`, `kind`, `namespace`, and `name` must + // be unique across all parentRef entries in the Route. + // * They do not select different objects, but for each optional field used, + // each ParentRef that selects the same object must set the same set of + // optional fields to different values. If one ParentRef sets a + // combination of optional fields, all must set the same combination. + // + // Some examples: + // + // * If one ParentRef sets `sectionName`, all ParentRefs referencing the + // same object must also set `sectionName`. + // * If one ParentRef sets `port`, all ParentRefs referencing the same + // object must also set `port`. + // * If one ParentRef sets `sectionName` and `port`, all ParentRefs + // referencing the same object must also set `sectionName` and `port`. // // It is possible to separately reference multiple distinct objects that may // be collapsed by an implementation. For example, some implementations may @@ -208,9 +223,9 @@ type CommonRouteSpec struct { // // +optional // +kubebuilder:validation:MaxItems=32 - // + // // - // + // // ParentRefs []ParentReference `json:"parentRefs,omitempty"` } diff --git a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml index 911c3758b0..b2fc163b8b 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml @@ -137,27 +137,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -284,16 +295,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml index 32b384408e..cba236b624 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml @@ -124,27 +124,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -271,16 +282,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind @@ -2565,27 +2573,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -2712,16 +2731,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml index 7bdce82e94..6c174dd887 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml @@ -59,27 +59,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -206,16 +217,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml index 5d139640f6..8f3cdb7006 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml @@ -105,27 +105,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -252,16 +263,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml index 9cab80369e..a8c2325ab6 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml @@ -59,27 +59,38 @@ spec: support: \n * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, experimental, ClusterIP Services only) This API may be extended in the future to support additional kinds of - parent resources. \n It is invalid to reference an identical parent - more than once. It is valid to reference multiple distinct sections - within the same parent resource, such as two separate Listeners - on the same Gateway or two separate ports on the same Service. \n - It is possible to separately reference multiple distinct objects - that may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n ParentRefs from a Route - to a Service in the same namespace are \"producer\" routes, which - apply default routing rules to inbound connections from any namespace - to the Service. \n ParentRefs from a Route to a Service in a different - namespace are \"consumer\" routes, and these routing rules are only - applied to outbound connections originating from the same namespace - as the Route, for which the intended destination of the connections - are a Service targeted as a ParentRef of the Route. \n " + parent resources. \n ParentRefs must be _distinct_. This means either + that: \n * They select different objects. If this is the case, + then parentRef entries are distinct. In terms of fields, this means + that the multi-part key defined by `group`, `kind`, `namespace`, + and `name` must be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field + used, each ParentRef that selects the same object must set the same + set of optional fields to different values. If one ParentRef sets + a combination of optional fields, all must set the same combination. + \n Some examples: \n * If one ParentRef sets `sectionName`, all + ParentRefs referencing the same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. * If one ParentRef sets `sectionName` + and `port`, all ParentRefs referencing the same object must also + set `sectionName` and `port`. \n It is possible to separately reference + multiple distinct objects that may be collapsed by an implementation. + For example, some implementations may choose to merge compatible + Gateway Listeners together. If that is the case, the list of routes + attached to those resources should also be merged. \n Note that + for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For + example, Gateway has the AllowedRoutes field, and ReferenceGrant + provides a generic way to enable other kinds of cross-namespace + reference. \n ParentRefs from a Route to a Service in the same + namespace are \"producer\" routes, which apply default routing rules + to inbound connections from any namespace to the Service. \n ParentRefs + from a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound connections + originating from the same namespace as the Route, for which the + intended destination of the connections are a Service targeted as + a ParentRef of the Route. \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -206,16 +217,13 @@ spec: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && ( ( (!has(p1.__namespace__) + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ - == '''') ) || ( has(p1.__namespace__) && has(p2.__namespace__) - && p1.__namespace__ == p2.__namespace__ ) ) ? ( ( ( (!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''') && (!has(p1.port) || p1.port == 0) && (!has(p2.port) - || p2.port == 0) ) || ( ( (has(p1.sectionName) && p1.sectionName - != '''') || (has(p1.port) && p1.port != 0) ) && ( (has(p2.sectionName) - && p2.sectionName != '''') || (has(p2.port) && p2.port != 0) ) - ) ) ): true ))' + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) + || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml index a8aa849e4f..2110b294bf 100644 --- a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml @@ -123,20 +123,30 @@ spec: the Route. \n There are two kinds of parent resources with \"Core\" support: \n * Gateway (Gateway conformance profile) This API may be extended in the future to support additional kinds of parent - resources. \n It is invalid to reference an identical parent more - than once. It is valid to reference multiple distinct sections within - the same parent resource, such as two separate Listeners on the - same Gateway or two separate ports on the same Service. \n It is - possible to separately reference multiple distinct objects that - may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n \n " + resources. \n ParentRefs must be _distinct_. This means either that: + \n * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the multi-part + key defined by `group`, `kind`, `namespace`, and `name` must be + unique across all parentRef entries in the Route. * They do not + select different objects, but for each optional field used, each + ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a combination + of optional fields, all must set the same combination. \n Some examples: + \n * If one ParentRef sets `sectionName`, all ParentRefs referencing + the same object must also set `sectionName`. * If one ParentRef + sets `port`, all ParentRefs referencing the same object must also + set `port`. * If one ParentRef sets `sectionName` and `port`, all + ParentRefs referencing the same object must also set `sectionName` + and `port`. \n It is possible to separately reference multiple distinct + objects that may be collapsed by an implementation. For example, + some implementations may choose to merge compatible Gateway Listeners + together. If that is the case, the list of routes attached to those + resources should also be merged. \n Note that for ParentRefs that + cross namespace boundaries, there are specific rules. Cross-namespace + references are only valid if they are explicitly allowed by something + in the namespace they are referring to. For example, Gateway has + the AllowedRoutes field, and ReferenceGrant provides a generic way + to enable other kinds of cross-namespace reference. \n \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -228,10 +238,9 @@ spec: == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && - p1.__namespace__ == p2.__namespace__ )) ? (((!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''')) || (has(p1.sectionName) && p1.sectionName != '''' && - has(p2.sectionName) && p2.sectionName != '''')) : true))' + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind @@ -2430,20 +2439,30 @@ spec: the Route. \n There are two kinds of parent resources with \"Core\" support: \n * Gateway (Gateway conformance profile) This API may be extended in the future to support additional kinds of parent - resources. \n It is invalid to reference an identical parent more - than once. It is valid to reference multiple distinct sections within - the same parent resource, such as two separate Listeners on the - same Gateway or two separate ports on the same Service. \n It is - possible to separately reference multiple distinct objects that - may be collapsed by an implementation. For example, some implementations - may choose to merge compatible Gateway Listeners together. If that - is the case, the list of routes attached to those resources should - also be merged. \n Note that for ParentRefs that cross namespace - boundaries, there are specific rules. Cross-namespace references - are only valid if they are explicitly allowed by something in the - namespace they are referring to. For example, Gateway has the AllowedRoutes - field, and ReferenceGrant provides a generic way to enable other - kinds of cross-namespace reference. \n \n " + resources. \n ParentRefs must be _distinct_. This means either that: + \n * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the multi-part + key defined by `group`, `kind`, `namespace`, and `name` must be + unique across all parentRef entries in the Route. * They do not + select different objects, but for each optional field used, each + ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a combination + of optional fields, all must set the same combination. \n Some examples: + \n * If one ParentRef sets `sectionName`, all ParentRefs referencing + the same object must also set `sectionName`. * If one ParentRef + sets `port`, all ParentRefs referencing the same object must also + set `port`. * If one ParentRef sets `sectionName` and `port`, all + ParentRefs referencing the same object must also set `sectionName` + and `port`. \n It is possible to separately reference multiple distinct + objects that may be collapsed by an implementation. For example, + some implementations may choose to merge compatible Gateway Listeners + together. If that is the case, the list of routes attached to those + resources should also be merged. \n Note that for ParentRefs that + cross namespace boundaries, there are specific rules. Cross-namespace + references are only valid if they are explicitly allowed by something + in the namespace they are referring to. For example, Gateway has + the AllowedRoutes field, and ReferenceGrant provides a generic way + to enable other kinds of cross-namespace reference. \n \n " items: description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually @@ -2535,10 +2554,9 @@ spec: == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && - p1.__namespace__ == p2.__namespace__ )) ? (((!has(p1.sectionName) - || p1.sectionName == '''') && (!has(p2.sectionName) || p2.sectionName - == '''')) || (has(p1.sectionName) && p1.sectionName != '''' && - has(p2.sectionName) && p2.sectionName != '''')) : true))' + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind diff --git a/pkg/test/cel/httproute_experimental_test.go b/pkg/test/cel/httproute_experimental_test.go index 7cbb064c4a..69076e0ada 100644 --- a/pkg/test/cel/httproute_experimental_test.go +++ b/pkg/test/cel/httproute_experimental_test.go @@ -143,8 +143,8 @@ func TestHTTPRouteParentRefExperimental(t *testing.T) { }}, }, { - name: "valid ParentRefs with multiple mixed references to the same parent", - wantErrors: []string{}, + name: "invalid ParentRefs with multiple mixed references to the same parent", + wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, parentRefs: []gatewayv1b1.ParentReference{{ Kind: ptrTo(gatewayv1b1.Kind("Gateway")), Group: ptrTo(gatewayv1b1.Group("gateway.networking.k8s.io")), @@ -171,8 +171,11 @@ func TestHTTPRouteParentRefExperimental(t *testing.T) { }}, }, { - name: "valid because duplicate parent refs with first having sectionName and second having both sectionName and port", - wantErrors: []string{}, + // when referencing the same object, both parentRefs need to specify + // the same optional fields (both parentRefs must specify port, + // sectionName, or both) + name: "invalid because duplicate parent refs with first having sectionName and second having both sectionName and port", + wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"}, parentRefs: []gatewayv1b1.ParentReference{{ Kind: ptrTo(gatewayv1b1.Kind("Gateway")), Group: ptrTo(gatewayv1b1.Group("gateway.networking.k8s.io")),