From 79c9bda1f9076e3816d1962c2b8c4edef7c26549 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Fri, 16 Jul 2021 18:05:15 -0700 Subject: [PATCH 1/8] Adding GEP 724: Refresh Route-Gateway Binding --- site-src/geps/gep-724.md | 426 ++++++++++++++++++++++++++ site-src/geps/images/724-alt1.png | Bin 0 -> 33208 bytes site-src/geps/images/724-alt2.png | Bin 0 -> 31549 bytes site-src/geps/images/724-proposal.png | Bin 0 -> 26940 bytes 4 files changed, 426 insertions(+) create mode 100644 site-src/geps/gep-724.md create mode 100644 site-src/geps/images/724-alt1.png create mode 100644 site-src/geps/images/724-alt2.png create mode 100644 site-src/geps/images/724-proposal.png diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md new file mode 100644 index 0000000000..6c105e5ce9 --- /dev/null +++ b/site-src/geps/gep-724.md @@ -0,0 +1,426 @@ +# GEP-724: Refresh Route-Gateway Binding + +* Issue URL: [#724](https://github.com/kubernetes-sigs/gateway-api/issues/724) +* Status: Implementable + +## TLDR + +This GEP proposes changes to Route-Gateway binding that will result in Routes +attaching to Gateways with direct references. When supporting Routes in multiple +namespaces, Gateways will need to specify the namespaces they trust Routes in. +These changes will slightly simplify the Route-Gateway relationship and make way +for the future addition of Route inclusion (Routes including other Routes). + +## Goals + +Refactor cross-namespace Route-Gateway binding to: + +* Be more consistent with [cross-namespace references from + Routes](/geps/gep-709.md) +* Provide a clear path to enable Route inclusion (Routes including Routes). +* Simplify user experience based on initial feedback. +* Enable other kinds of Route parents in addition to Gateway, this could include: + * Routes (as part of Route inclusion) + * Custom Gateway resources + * Mesh resources + +## Out of scope + +* Defining how Route inclusion will work. + +## Existing Approach + +The existing API already supports cross-namespace references. Gateways configure +the following: + +* A Route label selector +* Namespaces: Same, Selector, or All + +Routes then have three options as far as which Gateways can bind to them: + +* Same namespace (default) +* From a list of Gateways +* All + +Although this enables a great deal of flexibility, it can lead to confusion. For +example, 2 separate label selectors from Gateway can be challenging to compute. +Additionally, the default behavior of selecting all Routes in the same namespace +makes it easy to accidentally expose applications (see +[#515](https://github.com/kubernetes-sigs/gateway-api/issues/515)). + +## Proposed Changes + +One of the key concepts in the [cross-namespace references from Routes +GEP](/geps/gep-709.md) was that of a handshake for references that cross +namespace boundaries. A key part of that handshake was that one direction +included a direct reference, while the other direction provided a way to denote +trust for a set of Namespaces and kind of resources. + +It seems to make sense to carry that same underlying principle through to the +Route - Gateway relationship. Given that each Gateway is likely to support many +Routes, it would not be practical to support direct references from Gateways to +Routes. Instead, it is simpler if Routes directly reference the Gateways they +want to attach to. Gateways can then specify the namespaces they trust Routes +to attach from. + +![Routes directly reference Gateway](images/724-proposal.png) + +In the following example, the lb Gateway indicates that it trusts Routes from +the foo Namespace, and the HTTPRoute in that namespace attaches directly to the +Gateway. + +```yaml +kind: Gateway +metadata: + name: lb + namespace: infra +spec: + listeners: + - name: foo + hostname: foo.com + port: 80 + routes: + kind: HTTPRoute + namespaces: + from: Selector + selector: + kubernetes.io/metadata.name: foo +--- +kind: HTTPRoute +metadata: + name: foo + namespace: foo +spec: + attachTo: + - kind: Gateway + namespace: infra + name: lb + sectionName: foo + rules: + - name: abc + matches: + - path: /bar +``` + +## API Changes + +The proposed changes here can be summarized as: + +* Remove Route selector from Gateways. +* Replace the 3 options from Route -> Gateway (All, FromList, SameNamespace) + with a reference list that supports arbitrary kinds. + +### Gateway + +On Gateway, the only change involves removing the Route selector field. +Everything else remains the same. + +#### Removed + +```go + // Selector specifies a set of route labels used for selecting + // routes to associate with the Gateway. If this Selector is defined, + // only routes matching the Selector are associated with the Gateway. + // An empty Selector matches all routes. + // + // Support: Core + // + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` +``` + +### Routes + +On Routes, we remove the `RouteGateways` struct and replace it with a list of +parent references to attach to. + +#### Removed +From Route Specs: +```go + // Gateways defines which Gateways can use this Route. + // + // +optional + // +kubebuilder:default={allow: "SameNamespace"} + Gateways *RouteGateways `json:"gateways,omitempty"` +``` + +And the structs that references: +```go +// RouteGateways defines which Gateways will be able to use a route. If this +// field results in preventing the selection of a Route by a Gateway, an +// "Admitted" condition with a status of false must be set for the Gateway on +// that Route. +type RouteGateways struct { + // Allow indicates which Gateways will be allowed to use this route. + // Possible values are: + // * All: Gateways in any namespace can use this route. + // * FromList: Only Gateways specified in GatewayRefs may use this route. + // * SameNamespace: Only Gateways in the same namespace may use this route. + // + // +optional + // +kubebuilder:validation:Enum=All;FromList;SameNamespace + // +kubebuilder:default=SameNamespace + Allow *GatewayAllowType `json:"allow,omitempty"` + + // GatewayRefs must be specified when Allow is set to "FromList". In that + // case, only Gateways referenced in this list will be allowed to use this + // route. This field is ignored for other values of "Allow". + // + // +optional + GatewayRefs []GatewayReference `json:"gatewayRefs,omitempty"` +} +``` + +#### Added +To Route Specs: +```go + // AttachTo defines the parent resources this Route should be attached to. The + // only kind of target resource with "Core" support is Gateway. This API may + // be extended in the future to support additional kinds of parent resources + // such as Routes. + // + // +optional + // +kubebuilder:validation:MaxItems=16 + AttachTo []AttachRef `json:"gateways,omitempty"` +``` + +And the struct that references: +```go +// AttachRef identifies an API object to attach to. The only kind of target +// resource with "Core" support is Gateway. This API may be extended in the +// future to support additional kinds of parent resources such as Routes. +type AttachRef struct { + // Group is the group of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:default=networking.x-k8s.io + Group string `json:"group"` + + // Kind is kind of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` + + // Name is the name of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // SectionName is the name of a section within the target resource. When + // unspecified, this targets the entire resource. In the following + // resources, SectionName is interpreted as the following: + // * Gateway: Listener Name + // * Route: Rule Name + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + SectionName string `json:"sectionName,omitempty"` + + // Namespace is the namespace of the referent. When unspecified, the local + // namespace is inferred unless targeting a cluster-scoped resource in which + // case no namespace is inferred. + // + // Support: Extended + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Namespace string `json:"namespace,omitempty"` +} +``` + +### Advantages + +* Simplifies the API by providing a single way to attach Routes to Gateway. +* Assigns clear responsibilities to Gateway and Route owners. +* Consistent with pattern of direct references from Routes to all associated + resources. +* Enables attaching Routes to arbitrary parents, such as custom Gateways, other + Routes (to be defined), or Meshes. +* Prevents accidental exposure of Routes. +* Easy to understand which Gateways/parents a Route is attached to. +* Further simplifies path to Route inclusion. +* Follows pattern of direct reference in one direction with a broader trust + reference in the other direction. +* Aligns with initial user feedback. + +### Disadvantages + +* Attaching a Route to a named listener with SectionName may be a bit confusing. +* Does not utilize existing ReferencePolicy mechanism. +* May be more difficult to understand which Routes are attached to a Gateway. +* Adding/replacing a Gateway requires changes to Routes. + +### Potential Expansion +In the future, it may be useful to add a selector from Route -> Parent. Although +this would enable greater flexibility, it also significantly increases +complexity. + +## Alternatives + +### 1. ReferencePolicy with Gateways selecting Routes + +![ReferencePolicy with Gateways selecting Routes](images/724-alt1.png) + +A compelling alternative to this proposal would involve retaining the Route +selector in Gateway and replacing the trust concept in Routes with +ReferencePolicy. To represent the same example as above, we'd use a Route +selector on Gateway, a corresponding label on the HTTPRoute, and a +ReferencePolicy that allowed it: + +```yaml +kind: Gateway +metadata: + name: xlb + namespace: infra +spec: + listeners: + - name: foo + hostname: foo.com + port: 80 + routes: + kind: HTTPRoute + selector: + gateway: xlb + namespaces: + from: Selector + selector: + kubernetes.io/metadata.name: foo +--- +kind: HTTPRoute +metadata: + name: foo + namespace: foo + labels: + gateway: xlb +spec: + rules: + - name: abc + matches: + - path: /bar +--- +kind: ReferencePolicy +metadata: + name: infra-gateways + namespace: foo +spec: + from: + - group: networking.gateway.k8s.io + kind: Gateway + namespace: infra + to: + - group: networking.gateway.k8s.io + kind: HTTPRoute +``` + +#### Advantages + +* Consistent use of ReferencePolicy throughout the API. +* Provides a single way of binding Gateways to Routes. + +#### Disadvantages + +* Even the simplest cross-namespace reference from Gateway -> Route would + require a ReferencePolicy in each target namespace. +* Existing demos and examples would become significantly more verbose. +* Does not suppport attaching Routes to arbitrary parents. +* Does not prevent accidental exposure of Routes. +* Route owners have limited control in terms of which Gateways their Route is + attached to. + +### 2. ReferencePolicy with Routes referencing Gateways + +![ReferencePolicy with Routes referencing Gateways](images/724-alt2.png) + +The other way we could use ReferencePolicy would be with Routes referencing +Gateways. Unfortunately the nested structure of Gateways makes this nearly +impossible to do effectively. A core concept for Gateways is that each listener +should be able to attach to an entirely different set of Routes. For example, +a Gateway may want to delegate foo.com to the foo namespace and bar.com to the +bar namespace. Unfortunately thay would be very difficult to recreate with +ReferencePolicy. + +ReferencePolicy is fundamentally about trusting references from resource of kind +Foo in to resources of kind Bar. Names and section names are intentionally +excluded. If we added both of those concepts to ReferencePolicy, this would be +possible, but quite complex and verbose. This is what the example from above +would look like with this approach: + +```yaml +kind: Gateway +metadata: + name: lb + namespace: infra +spec: + listeners: + - name: foo + hostname: foo.com + port: 80 +--- +kind: ReferencePolicy +metadata: + name: foo-lb + namespace: infra +spec: + from: + - group: networking.gateway.k8s.io + kind: Gateway + to: + - group: networking.gateway.k8s.io + kind: Gateway + name: lb + sectionName: foo +--- +kind: HTTPRoute +metadata: + name: foo + namespace: foo +spec: + attachTo: + - kind: Gateway + namespace: infra + name: lb + sectionName: foo + rules: + - name: abc + matches: + - path: /bar +``` + +#### Advantages + +* Consistent use of ReferencePolicy throughout the API. +* Provides a single way of binding Gateways to Routes. +* Supports attaching Routes to arbitrary parents. +* Prevents accidental exposure of Routes. + +#### Disadvantages + +* In most cases, each listener in a Gateway would require a unique + ReferencePolicy resource. +* Even the simplest cross-namespace reference from Route -> Gateway would + require a ReferencePolicy in each target namespace. +* Existing demos and examples would become significantly more verbose. +* ReferencePolicy would become more complex for all other use cases. + +## References + +**GEPs** + +* [GEP 709: ReferencePolicy + Cross Namespace References from Routes](/geps/gep-709.md) + +**Docs:** + +* [Gateway API References Doc](https://docs.google.com/document/d/18MoabVA-fr5XL9cYdf6cxclqRwFpOvHUXV_UYzSiooY/edit) +* [Simplifying Gateway Route Binding](https://docs.google.com/document/d/1YVyB2dizACWrn8Rj31hQFBwCYqgyFKsxKeeGlv-iQCs/edit) + +**Issues:** + +* [Simplify Gateway Route Binding](https://github.com/kubernetes-sigs/gateway-api/issues/594) +* [Route selector improvements on Gateway](https://github.com/kubernetes-sigs/gateway-api/issues/515) \ No newline at end of file diff --git a/site-src/geps/images/724-alt1.png b/site-src/geps/images/724-alt1.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2c44ea7dca6eb5b8b4bf63daa70f3c016dd3e5 GIT binary patch literal 33208 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%0DjTnD`r1%uYwW!x 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 literal 0 HcmV?d00001 diff --git a/site-src/geps/images/724-proposal.png b/site-src/geps/images/724-proposal.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d315154d1f129ff77ab59ef99d08d0801d8b56 GIT binary patch literal 26940 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 literal 0 HcmV?d00001 From d14d504f6600cb97d51f033372fae350afe26c74 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 27 Jul 2021 17:18:22 -0700 Subject: [PATCH 2/8] Adding rationale section, clarifying the conceptual change --- mkdocs.yml | 28 +++++++-------- site-src/geps/gep-724.md | 74 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 6c9317d23a..37f4d00b0a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,22 +23,22 @@ nav: Gateway: api-types/gateway.md HTTPRoute: api-types/httproute.md - Guides: - - Getting started: guides/getting-started.md - - Simple Gateway: guides/simple-gateway.md - - HTTP routing: guides/http-routing.md - - HTTP traffic splitting: guides/traffic-splitting.md - - Multiple namespaces and routes: guides/multiple-ns.md - - TLS: guides/tls.md - - TCP routing: guides/tcp.md + - Getting started: guides/getting-started.md + - Simple Gateway: guides/simple-gateway.md + - HTTP routing: guides/http-routing.md + - HTTP traffic splitting: guides/traffic-splitting.md + - Multiple namespaces and routes: guides/multiple-ns.md + - TLS: guides/tls.md + - TCP routing: guides/tcp.md - References: - - API specification: references/spec.md - - Releases: references/releases.md - - Implementations: references/implementations.md + - API specification: references/spec.md + - Releases: references/releases.md + - Implementations: references/implementations.md - ... | geps/gep-*.md - Contributing: - - Developer guide: contributing/devguide.md - - Gateway Enhancement Proposal: contributing/gep.md - - Enhancement requests: contributing/enhancement-requests.md - - Community: contributing/community.md + - Developer guide: contributing/devguide.md + - Gateway Enhancement Proposal: contributing/gep.md + - Enhancement requests: contributing/enhancement-requests.md + - Community: contributing/community.md - FAQ: faq.md diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 6c105e5ce9..9a259e2fee 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -50,6 +50,11 @@ makes it easy to accidentally expose applications (see ## Proposed Changes +Although the API changes proposed here are relatively small, this represents a +larger conceptual change. Instead of Gateways selecting Routes, Routes would +directly reference the Gateways they wanted to attach to. This pattern was +already possible with the existing API, but not clearly documented. + One of the key concepts in the [cross-namespace references from Routes GEP](/geps/gep-709.md) was that of a handshake for references that cross namespace boundaries. A key part of that handshake was that one direction @@ -102,6 +107,67 @@ spec: - path: /bar ``` +## Rationale + +#### 1. Remove Complexity While Possible +A goal of v1alpha2 is to make any breaking changes we think we'll need to for +the lifetime of the API. After this release, we plan to provide a fully +convertible, and hopefully also fully backwards compatible, API. If we really +do need more advanced functionality in the future, it will be fairly +straightforward to add in a future release. On the other hand, it will be near +impossible to remove this complexity post-v1alpha2 if we were to leave it as is. + +#### 2. Route Selection Added Confusion and Did Not Enhance Security +Although it was easy to look at the selector from Gateway -> Route as providing +Gateway admins some form of control over the Routes attached to their Gateway, +it was nothing but security theater. Route owners still had ultimate control by +deciding how their Routes were labeled. This also made it difficult to document +how Gateway and Route owners should interact. One possible explanation was that +Gateway owners should provide Route owners with a set of labels that they should +add to their Route to bind to a given Gateway. At that point, we were still +ultimately relying on Route owners to attach a Route to a Gateway, just making +it a rather clunky process. + +It should be noted that this proposal does still retain the ability for Gateways +to restrict the namespaces they trust Routes to attach from. This was the only +real control Gateway admins had before this proposed change. + +#### 3. The Existing Defaults Were Too Permissive +One of the common complaints about the existing API was that the defaults made +it far too easy to accidentally expose a Route. By default, a Gateway would be +bound to all Routes in the same namespace. Although that made the getting +started guide simple, it would inevitably lead to some unfortunate mistakes in +the future. As we've already learned with Kubernetes, it's very difficult to +recover from insecure defaults. Instead, it's much safer to start with more +explicit configuration that demonstrates clear intent to connect resources. + +#### 4. We Need to Support non-Gateway Route Parents +With the expansion of this API, it's clear that a Route may have non-Gateway +parents. This may be other Routes, mesh implementations, or custom Gateways. +Although none of these concepts are well specified at this point, making this +change now will give us more flexibility in the future. + +#### 5. Users Want Control over the Gateways Their Routes Are Attached To +Initial feedback we've received has shown that users want to have very clear +control over the Gateways their Routes are attached to. Even in the case of +Gateway replacement, many Route owners would prefer to be involved in the +process. + +As we get more feedback and usage of the API, we may identify more users that +are interested in the more advanced capabilities that some form of selection may +enable, but at this point it's clear that a large portion of users value an +explicit way to attach a Route to a Gateway. + +#### 6. We Need to Maintain a Handshake Between Gateways and Routes +Of course we do still need a handshake that will enable cross-namespace +references between Routes and Gateways. This proposal leaves in the core +capabilities of the v1alpha1 API for this. Gateways can specify the namespaces +they trust Routes to bind from, and Routes directly reference the Gateways they +want to attach to. This is largely similar to the ReferencePolicy model proposed +for Route->Service references, but is embedded within the Route and Gateway +resources. The alternatives below explore what this could look like with +ReferencePolicy. + ## API Changes The proposed changes here can be summarized as: @@ -222,9 +288,11 @@ type AttachRef struct { // +optional SectionName string `json:"sectionName,omitempty"` - // Namespace is the namespace of the referent. When unspecified, the local - // namespace is inferred unless targeting a cluster-scoped resource in which - // case no namespace is inferred. + // Namespace is the namespace of the referent. When unspecified (empty + // string), this will either be: + // + // * local namespace of the target is a namespace scoped resource + // * no namespace (not applicable) if the target is cluster-scoped. // // Support: Extended // From 9136e95ca957ed5a7eda3b3c142ae31f1672c4dd Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 27 Jul 2021 22:03:31 -0700 Subject: [PATCH 3/8] Responding to additional feedback, SectionName clarification --- site-src/geps/gep-724.md | 85 +++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 9a259e2fee..c91de4bc62 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -109,7 +109,7 @@ spec: ## Rationale -#### 1. Remove Complexity While Possible +#### 1. Remove Complexity While it is Still Possible A goal of v1alpha2 is to make any breaking changes we think we'll need to for the lifetime of the API. After this release, we plan to provide a fully convertible, and hopefully also fully backwards compatible, API. If we really @@ -172,7 +172,7 @@ ReferencePolicy. The proposed changes here can be summarized as: -* Remove Route selector from Gateways. +* Remove Route selector from the RouteBindingSelector in Gateway listeners. * Replace the 3 options from Route -> Gateway (All, FromList, SameNamespace) with a reference list that supports arbitrary kinds. @@ -269,17 +269,42 @@ type AttachRef struct { // +kubebuilder:validation:MaxLength=253 Kind string `json:"kind"` + // Namespace is the namespace of the referent. When unspecified (empty + // string), this will either be: + // + // * local namespace of the target is a namespace scoped resource + // * no namespace (not applicable) if the target is cluster-scoped. + // + // Support: Extended + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Namespace string `json:"namespace,omitempty"` + // Name is the name of the referent. // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 Name string `json:"name"` - // SectionName is the name of a section within the target resource. When - // unspecified, this targets the entire resource. In the following - // resources, SectionName is interpreted as the following: + // SectionName is the name of a section within the target resource. In the + // following resources, SectionName is interpreted as the following: + // // * Gateway: Listener Name - // * Route: Rule Name + // + // Implementations MAY choose to support attaching Routes to other resources. + // If that is the case, they MUST clearly document how SectionName is + // interpreted. + // + // When unspecified (empty string), this will reference the entire resource. + // For the purpose of status, an attachment is considered successful if at + // least one section in the parent resource accepts it. For example, Gateway + // listeners can restrict which Routes can bind to them by Route kind, + // namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + // the referencing Route, the Route MUST be considered successfully + // attached. If no Gateway listeners accept attachment from this Route, the + // Route MUST be considered detached from the Gateway. // // Support: Core // @@ -287,19 +312,41 @@ type AttachRef struct { // +kubebuilder:validation:MaxLength=253 // +optional SectionName string `json:"sectionName,omitempty"` +} +``` - // Namespace is the namespace of the referent. When unspecified (empty - // string), this will either be: - // - // * local namespace of the target is a namespace scoped resource - // * no namespace (not applicable) if the target is cluster-scoped. - // - // Support: Extended - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +optional - Namespace string `json:"namespace,omitempty"` +This would also require adding a `Name` field to Gateway listeners and Route +rules to support the SectionName concept: + +```go +type Listener struct { + // Name is the name of the Listener. If more than one Listener is present + // each Listener MUST specify a name. The names of Listeners MUST be unique + // within a Gateway. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Name string `json:"name,omitempty"` + // ... +} +``` + +```go +type RouteRule struct { + // Name is the name of the Route rule. If more than one Route Rule is + // present, each Rule MUST specify a name. The names of Rules MUST be unique + // within a Route. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Name string `json:"name,omitempty"` + // ... } ``` @@ -411,7 +458,7 @@ Gateways. Unfortunately the nested structure of Gateways makes this nearly impossible to do effectively. A core concept for Gateways is that each listener should be able to attach to an entirely different set of Routes. For example, a Gateway may want to delegate foo.com to the foo namespace and bar.com to the -bar namespace. Unfortunately thay would be very difficult to recreate with +bar namespace. Unfortunately that would be very difficult to recreate with ReferencePolicy. ReferencePolicy is fundamentally about trusting references from resource of kind From e6bbc77f5bcb59adfd1f43a5e804ecf6fbaf2ce6 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Wed, 28 Jul 2021 12:01:28 -0700 Subject: [PATCH 4/8] Reworking Route kind and group limitations in Gateway --- site-src/geps/gep-724.md | 77 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index c91de4bc62..74633a9091 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -85,7 +85,6 @@ spec: hostname: foo.com port: 80 routes: - kind: HTTPRoute namespaces: from: Selector selector: @@ -173,6 +172,9 @@ ReferencePolicy. The proposed changes here can be summarized as: * Remove Route selector from the RouteBindingSelector in Gateway listeners. +* Replace Route kind and group with optional list of accepted kinds and groups + in RouteBindingSelector. +* Rename RouteBindingSelector to ListenerRoutes. * Replace the 3 options from Route -> Gateway (All, FromList, SameNamespace) with a reference list that supports arbitrary kinds. @@ -193,6 +195,77 @@ Everything else remains the same. // // +optional Selector *metav1.LabelSelector `json:"selector,omitempty"` + // Group is the group of the route resource to select. Omitting the value or specifying + // the empty string indicates the networking.x-k8s.io API group. + // For example, use the following to select an HTTPRoute: + // + // routes: + // kind: HTTPRoute + // + // Otherwise, if an alternative API group is desired, specify the desired + // group: + // + // routes: + // group: acme.io + // kind: FooRoute + // + // Support: Core + // + // +optional + // +kubebuilder:default=networking.x-k8s.io + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group *string `json:"group,omitempty"` + // Kind is the kind of the route resource to select. + // + // Kind MUST correspond to kinds of routes that are compatible with the + // application protocol specified in the Listener's Protocol field. + // + // If an implementation does not support or recognize this + // resource type, it SHOULD set the "ResolvedRefs" condition to false for + // this listener with the "InvalidRoutesRef" reason. + // + // Support: Core + Kind string `json:"kind"` +``` + +#### Added +```go +type ListenerRoutes struct { + // ... + // Kinds specifies the groups and kinds of Routes that are allowed to bind to + // this Gateway listener. When unspecified or empty, the only limitation on + // the kinds of Routes supported is the Listener protocol. Kind MUST + // correspond to kinds of Routes that are compatible with the application + // protocol specified in the Listener's Protocol field. If an implementation + // does not support or recognize this resource type, it SHOULD set the + // "ResolvedRefs" condition to false for this listener with the + // "InvalidRoutesRef" reason. + // + // Support: Core + // + // +optional + Kinds []RouteGroupKind `json:"kinds,omitempty"` +} + +type RouteGroupKind struct { + // Group is the group of the Route. + // + // Support: Core + // + // +optional + // +kubebuilder:default=networking.gateway.k8s.io + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group *string `json:"group,omitempty"` + // Kind is the kind of the Route. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` +} ``` ### Routes @@ -260,7 +333,7 @@ type AttachRef struct { // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - // +kubebuilder:default=networking.x-k8s.io + // +kubebuilder:default=gateway.networking.k8s.io Group string `json:"group"` // Kind is kind of the referent. From 4693e5e9c9de3dadd8c77265cfe16b8dbbdaf066 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 3 Aug 2021 16:27:21 -0700 Subject: [PATCH 5/8] Additional updates for GEP 724, including rename to ParentRefs --- site-src/geps/gep-724.md | 72 +++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 74633a9091..9d2d5d09ce 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -254,8 +254,7 @@ type RouteGroupKind struct { // Support: Core // // +optional - // +kubebuilder:default=networking.gateway.k8s.io - // +kubebuilder:validation:MinLength=1 + // +kubebuilder:default=gateway.networking.k8s.io // +kubebuilder:validation:MaxLength=253 Group *string `json:"group,omitempty"` // Kind is the kind of the Route. @@ -313,24 +312,28 @@ type RouteGateways struct { #### Added To Route Specs: ```go - // AttachTo defines the parent resources this Route should be attached to. The - // only kind of target resource with "Core" support is Gateway. This API may - // be extended in the future to support additional kinds of parent resources - // such as Routes. + // ParentRefs references the parent resources this Route should be attached + // to. The only kind of parent resource with "Core" support is Gateway. This + // API may be extended in the future to support additional kinds of parent + // resources such as Routes. It is invalid to reference the same parent + // resource more than once. // // +optional // +kubebuilder:validation:MaxItems=16 - AttachTo []AttachRef `json:"gateways,omitempty"` + ParentRefs []ParentRef `json:"parentRefs,omitempty"` ``` And the struct that references: ```go -// AttachRef identifies an API object to attach to. The only kind of target -// resource with "Core" support is Gateway. This API may be extended in the -// future to support additional kinds of parent resources such as Routes. -type AttachRef struct { +// ParentRef identifies an API object that should be considered a parent of this +// resource. The only kind of parent resource with "Core" support is Gateway. +// This API may be extended in the future to support additional kinds of parent +// resources such as Routes. +type ParentRef struct { // Group is the group of the referent. // + // Support: Core + // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 // +kubebuilder:default=gateway.networking.k8s.io @@ -338,8 +341,12 @@ type AttachRef struct { // Kind is kind of the referent. // + // Support: Core (Gateway) + // Support: Extended (Other Resources) + // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:default=Gateway Kind string `json:"kind"` // Namespace is the namespace of the referent. When unspecified (empty @@ -355,8 +362,21 @@ type AttachRef struct { // +optional Namespace string `json:"namespace,omitempty"` + // Scope represents if this refers to a cluster or namespace scoped resource. + // This may be set to "Cluster" or "Namespace". + // + // Support: Core (Namespace) + // Support: Extended (Cluster) + // + // +kubebuilder:validation:Enum=Cluster;Namespace + // +kubebuilder:default=Namespace + // +optional + Scope string `json:"scope,omitempty"` + // Name is the name of the referent. // + // Support: Core + // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 Name string `json:"name"` @@ -407,21 +427,11 @@ type Listener struct { } ``` -```go -type RouteRule struct { - // Name is the name of the Route rule. If more than one Route Rule is - // present, each Rule MUST specify a name. The names of Rules MUST be unique - // within a Route. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +optional - Name string `json:"name,omitempty"` - // ... -} -``` +#### Changed + +To accomodate Routes with arbitrary types of parents, `RouteGatewayStatus` would +be renamed to `RouteParentStatus`. Similarly, the `GatewayRef` inside that +struct would be ### Advantages @@ -499,11 +509,11 @@ metadata: namespace: foo spec: from: - - group: networking.gateway.k8s.io + - group: gateway.networking.k8s.io kind: Gateway namespace: infra to: - - group: networking.gateway.k8s.io + - group: gateway.networking.k8s.io kind: HTTPRoute ``` @@ -557,10 +567,10 @@ metadata: namespace: infra spec: from: - - group: networking.gateway.k8s.io + - group: gateway.networking.k8s.io kind: Gateway to: - - group: networking.gateway.k8s.io + - group: gateway.networking.k8s.io kind: Gateway name: lb sectionName: foo @@ -570,7 +580,7 @@ metadata: name: foo namespace: foo spec: - attachTo: + parentRefs: - kind: Gateway namespace: infra name: lb From c8681622820e790643e6a96a81b9bd01909a67ca Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Thu, 5 Aug 2021 18:46:29 -0700 Subject: [PATCH 6/8] Fixing typos, indentation, and adding clarification --- site-src/geps/gep-724.md | 136 +++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 62 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 9d2d5d09ce..358b5fac4b 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -20,7 +20,7 @@ Refactor cross-namespace Route-Gateway binding to: * Provide a clear path to enable Route inclusion (Routes including Routes). * Simplify user experience based on initial feedback. * Enable other kinds of Route parents in addition to Gateway, this could include: - * Routes (as part of Route inclusion) + * Routes (as part of one potential approach to Route inclusion) * Custom Gateway resources * Mesh resources @@ -43,10 +43,11 @@ Routes then have three options as far as which Gateways can bind to them: * All Although this enables a great deal of flexibility, it can lead to confusion. For -example, 2 separate label selectors from Gateway can be challenging to compute. -Additionally, the default behavior of selecting all Routes in the same namespace -makes it easy to accidentally expose applications (see -[#515](https://github.com/kubernetes-sigs/gateway-api/issues/515)). +example, 2 separate label selectors from Gateway can be challenging for users to +compute. It requires users to do to label selector lookups and then compute the +intersection of that result. Additionally, the default behavior of selecting all +Routes in the same namespace makes it easy to accidentally expose applications +(see [#515](https://github.com/kubernetes-sigs/gateway-api/issues/515)). ## Proposed Changes @@ -195,38 +196,38 @@ Everything else remains the same. // // +optional Selector *metav1.LabelSelector `json:"selector,omitempty"` - // Group is the group of the route resource to select. Omitting the value or specifying - // the empty string indicates the networking.x-k8s.io API group. - // For example, use the following to select an HTTPRoute: - // - // routes: - // kind: HTTPRoute - // - // Otherwise, if an alternative API group is desired, specify the desired - // group: - // - // routes: - // group: acme.io - // kind: FooRoute - // - // Support: Core - // - // +optional - // +kubebuilder:default=networking.x-k8s.io - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Group *string `json:"group,omitempty"` - // Kind is the kind of the route resource to select. - // - // Kind MUST correspond to kinds of routes that are compatible with the - // application protocol specified in the Listener's Protocol field. - // - // If an implementation does not support or recognize this - // resource type, it SHOULD set the "ResolvedRefs" condition to false for - // this listener with the "InvalidRoutesRef" reason. - // - // Support: Core - Kind string `json:"kind"` + // Group is the group of the route resource to select. Omitting the value or specifying + // the empty string indicates the gateway.networking.k8s.io API group. + // For example, use the following to select an HTTPRoute: + // + // routes: + // kind: HTTPRoute + // + // Otherwise, if an alternative API group is desired, specify the desired + // group: + // + // routes: + // group: acme.io + // kind: FooRoute + // + // Support: Core + // + // +optional + // +kubebuilder:default=gateway.networking.k8s.io + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group *string `json:"group,omitempty"` + // Kind is the kind of the route resource to select. + // + // Kind MUST correspond to kinds of routes that are compatible with the + // application protocol specified in the Listener's Protocol field. + // + // If an implementation does not support or recognize this + // resource type, it SHOULD set the "ResolvedRefs" condition to false for + // this listener with the "InvalidRoutesRef" reason. + // + // Support: Core + Kind string `json:"kind"` ``` #### Added @@ -245,25 +246,26 @@ type ListenerRoutes struct { // Support: Core // // +optional + // +kubebuilder:validation:MaxItems=16 Kinds []RouteGroupKind `json:"kinds,omitempty"` } type RouteGroupKind struct { // Group is the group of the Route. - // - // Support: Core - // - // +optional - // +kubebuilder:default=gateway.networking.k8s.io - // +kubebuilder:validation:MaxLength=253 - Group *string `json:"group,omitempty"` - // Kind is the kind of the Route. - // - // Support: Core + // + // Support: Core + // + // +optional + // +kubebuilder:default=gateway.networking.k8s.io + // +kubebuilder:validation:MaxLength=253 + Group *string `json:"group,omitempty"` + // Kind is the kind of the Route. + // + // Support: Core // // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Kind string `json:"kind"` + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` } ``` @@ -312,11 +314,16 @@ type RouteGateways struct { #### Added To Route Specs: ```go - // ParentRefs references the parent resources this Route should be attached - // to. The only kind of parent resource with "Core" support is Gateway. This - // API may be extended in the future to support additional kinds of parent - // resources such as Routes. It is invalid to reference the same parent + // ParentRefs references the resources that can attach to this Route. The only + // kind of parent resource with "Core" support is Gateway. This API may be + // extended in the future to support additional kinds of parent resources such + // as one of the route kinds. It is invalid to reference the same parent // resource more than once. + + // It is possible to separately reference 2 distinct objects that may be + // collapsed by an implementation. For example, some implementations may + // choose to merge compatible listeners together. If that is the case, the + // list of routes attached to those resources should also be merged. // // +optional // +kubebuilder:validation:MaxItems=16 @@ -328,7 +335,7 @@ And the struct that references: // ParentRef identifies an API object that should be considered a parent of this // resource. The only kind of parent resource with "Core" support is Gateway. // This API may be extended in the future to support additional kinds of parent -// resources such as Routes. +// resources, such as HTTPRoute. type ParentRef struct { // Group is the group of the referent. // @@ -347,7 +354,8 @@ type ParentRef struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 // +kubebuilder:default=Gateway - Kind string `json:"kind"` + // +optional + Kind *string `json:"kind,omitempty"` // Namespace is the namespace of the referent. When unspecified (empty // string), this will either be: @@ -360,7 +368,7 @@ type ParentRef struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 // +optional - Namespace string `json:"namespace,omitempty"` + Namespace *string `json:"namespace,omitempty"` // Scope represents if this refers to a cluster or namespace scoped resource. // This may be set to "Cluster" or "Namespace". @@ -368,10 +376,10 @@ type ParentRef struct { // Support: Core (Namespace) // Support: Extended (Cluster) // - // +kubebuilder:validation:Enum=Cluster;Namespace + // +kubebuilder:validation:Enum=Cluster;Namespace // +kubebuilder:default=Namespace // +optional - Scope string `json:"scope,omitempty"` + Scope *string `json:"scope,omitempty"` // Name is the name of the referent. // @@ -404,7 +412,7 @@ type ParentRef struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 // +optional - SectionName string `json:"sectionName,omitempty"` + SectionName *string `json:"sectionName,omitempty"` } ``` @@ -421,6 +429,8 @@ type Listener struct { // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 + // +listType=map + // +listMapKey=name // +optional Name string `json:"name,omitempty"` // ... @@ -431,7 +441,7 @@ type Listener struct { To accomodate Routes with arbitrary types of parents, `RouteGatewayStatus` would be renamed to `RouteParentStatus`. Similarly, the `GatewayRef` inside that -struct would be +struct would be replaced with the `ParentRef` struct included above. ### Advantages @@ -568,7 +578,8 @@ metadata: spec: from: - group: gateway.networking.k8s.io - kind: Gateway + kind: HTTPRoute + namespace: foo to: - group: gateway.networking.k8s.io kind: Gateway @@ -603,7 +614,8 @@ spec: * In most cases, each listener in a Gateway would require a unique ReferencePolicy resource. * Even the simplest cross-namespace reference from Route -> Gateway would - require a ReferencePolicy in each target namespace. + require a ReferencePolicy in each target namespace. This could either rule + out or significantly complicate self-service use-cases. * Existing demos and examples would become significantly more verbose. * ReferencePolicy would become more complex for all other use cases. From 7b17f6c330264465a6fbdfb18aac24f4af25f5bb Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Thu, 5 Aug 2021 22:41:21 -0700 Subject: [PATCH 7/8] Adding Listener status changes --- site-src/geps/gep-724.md | 110 ++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 24 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 358b5fac4b..61072ab305 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -178,13 +178,17 @@ The proposed changes here can be summarized as: * Rename RouteBindingSelector to ListenerRoutes. * Replace the 3 options from Route -> Gateway (All, FromList, SameNamespace) with a reference list that supports arbitrary kinds. +* Add a name to Gateway listeners. +* Restructure listener status to include name, routeRefs, and supportedKinds + fields. -### Gateway +### Gateway Spec -On Gateway, the only change involves removing the Route selector field. +In Gateway spec, the only change involves removing the Route selector field. Everything else remains the same. #### Removed +The following fields would be removed from RouteBindingSelector: ```go // Selector specifies a set of route labels used for selecting @@ -231,6 +235,29 @@ Everything else remains the same. ``` #### Added +Note: The ListMapKey annotation for listeners would also have to change to name +for this. + +```go +type Listener struct { + // Name is the name of the Listener. If more than one Listener is present + // each Listener MUST specify a name. The names of Listeners MUST be unique + // within a Gateway. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Name *string `json:"name,omitempty"` + // ... +} +``` + +The RouteBindingSelector struct would be renamed to ListenerRoutes, and a Kinds +field would be added. Note that the Selector, Group, and Kind field would be +removed from this struct as described above. + ```go type ListenerRoutes struct { // ... @@ -246,7 +273,7 @@ type ListenerRoutes struct { // Support: Core // // +optional - // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:MaxItems=10 Kinds []RouteGroupKind `json:"kinds,omitempty"` } @@ -269,6 +296,62 @@ type RouteGroupKind struct { } ``` +### Gateway Status +The most significant addition to the Gateway resource is in status. It may be +helpful to share a sample of what the YAML would look like: + +```yaml +status: + listeners: + - name: foo + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - ... +``` + +The key changes here all involve Listener status: + +* Replace the `port`, `protocol`, and `hostname` field with `name` to take + advantage of the new Listener name concept. +* Add a new `supportedKinds` field. This will be most useful when the + corresponding field in the spec is left blank or when a user specifies kinds + that a controller does not support. + +Note: The ListMapKey annotation for listener status would also have to change to +name for this. + +```go +// ListenerStatus is the status associated with a Listener. +type ListenerStatus struct { + // Name is the name of the Listener. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Name *string `json:"name,omitempty"` + + // SupportedKinds is the list indicating the Kinds supported by this + // listener. When this is not specified on the Listener, this MUST represent + // the kinds an implementation supports for the specified protocol. When + // there are kinds specified on the Listener, this MUST represent the + // intersection of those kinds and the kinds supported by the implementation + // for the specified protocol. + // + // +kubebuilder:validation:MaxItems=10 + // +optional + SupportedKinds []RouteGroupKind `json:"supportedKinds,omitempty"` + + // AttachedRoutes represents the total number of Routes that have been + // successfully attached to this Listener. + AttachedRoutes int32 `json:"attachedRoutes"` + + // Conditions... +} +``` + ### Routes On Routes, we remove the `RouteGateways` struct and replace it with a list of @@ -416,27 +499,6 @@ type ParentRef struct { } ``` -This would also require adding a `Name` field to Gateway listeners and Route -rules to support the SectionName concept: - -```go -type Listener struct { - // Name is the name of the Listener. If more than one Listener is present - // each Listener MUST specify a name. The names of Listeners MUST be unique - // within a Gateway. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +listType=map - // +listMapKey=name - // +optional - Name string `json:"name,omitempty"` - // ... -} -``` - #### Changed To accomodate Routes with arbitrary types of parents, `RouteGatewayStatus` would From af186bc324fd100dc99a3d8e9d8ab48ffce68276 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Fri, 6 Aug 2021 08:49:32 -0700 Subject: [PATCH 8/8] Clarifying that a Route can be attached to multiple sections --- site-src/geps/gep-724.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/site-src/geps/gep-724.md b/site-src/geps/gep-724.md index 61072ab305..248ce9267a 100644 --- a/site-src/geps/gep-724.md +++ b/site-src/geps/gep-724.md @@ -400,13 +400,14 @@ To Route Specs: // ParentRefs references the resources that can attach to this Route. The only // kind of parent resource with "Core" support is Gateway. This API may be // extended in the future to support additional kinds of parent resources such - // as one of the route kinds. It is invalid to reference the same parent - // resource more than once. - - // It is possible to separately reference 2 distinct objects that may be - // collapsed by an implementation. For example, some implementations may - // choose to merge compatible listeners together. If that is the case, the - // list of routes attached to those resources should also be merged. + // as one of the route kinds. 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 2 Listeners within a Gateway. + // + // 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. // // +optional // +kubebuilder:validation:MaxItems=16