Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding GEP 724: Refresh Route-Gateway Binding #725

Merged
merged 8 commits into from
Aug 6, 2021

Conversation

robscott
Copy link
Member

@robscott robscott commented Jul 17, 2021

What type of PR is this?
/kind gep

What this PR does / why we need it:
This PR adds GEP #724 as a follow up to various proposals and docs around how we can improve Route-Gateway binding.

Does this PR introduce a user-facing change?:

NONE

Deploy Preview: https://deploy-preview-725--kubernetes-sigs-gateway-api.netlify.app/geps/gep-724/

@k8s-ci-robot k8s-ci-robot added kind/gep PRs related to Gateway Enhancement Proposal(GEP) cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jul 17, 2021
@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 17, 2021
@robscott
Copy link
Member Author

/hold for discussion.

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 17, 2021
@robscott
Copy link
Member Author

@k8s-ci-robot
Copy link
Contributor

@robscott: GitHub didn't allow me to request PR reviews from the following users: howardjohn.

Note that only kubernetes-sigs members and repo collaborators can review this PR, and authors cannot review their own PRs.

In response to this:

/cc @bowei @hbagdi @howardjohn @jpeach @youngnick

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@robscott robscott added this to the v1alpha2 milestone Jul 17, 2021
Copy link
Contributor

@howardjohn howardjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this. Istio has a pretty similar model (but this is much clearer, IMO) that hasn't shown any obvious issues

site-src/geps/gep-724.md Outdated Show resolved Hide resolved

// 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I assume for custom attachment points they can choose to make this something else? If so it may be best to clarify that in the comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point, will clarify that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If custom attachment points are supported, maybe SectionName is too specific? Maybe Anchor, TargetName, Fragment, ID?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think SectionName is sufficiently generic. It's meant to refer to a single section within a resource. I can see an extension that just took a jsonpath or something, but I think that may be too complicated for >99% of use cases. Were there any specific use cases/extensions you had in mind?

* May be more difficult to understand which Routes are attached to a Gateway.
* Adding/replacing a Gateway requires changes to Routes.

### Potential Expansion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this impact possible route -> route?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd end up with 3 options here, I'm not sure which is best:

  1. 1:many Route->Routes: Nearly identical to Route->Gateway pattern described here, parent Route describes trusted namespaces per rule, child Routes use attachTo mechanism to attach to a parent.
  2. 1:1 Route->Route: Require direct reference in both directions (include + attachTo would both be direct refs).
  3. 1:1 Route->Route: Direct reference from parent to child, ReferencePolicy to denote trust from child ns.

I think 1 and 3 make the most sense, it really just depends on how we want the feature to be used. If we want Routes to be able to somewhat blindly delegate a path to a namespace, 1 makes more sense. If we want Routes to include a very specific Route in another namespace, 3 probably makes more sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(3) seems to make most sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We designed Contour's HTTPProxy to be more like (3), but we've definitely had requests for not requiring a specific Route name, which I think is more like (1).

I think that (3) requires more out-of-band coordination between parties than (1) does, which could be good or bad. I'm still inclined to err on the side of more coordination and explicit config, because blanked includes scare me a bit. But it's not a strong preference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the more I've thought about this, the more I think 3 is the best approach. That means Route->Route would work nearly identically to Route->Service, which seems reasonable to me.

@bowei
Copy link
Contributor

bowei commented Jul 19, 2021

LGTM -- we've had some feedback from users that indirection of which GW a Route was attached to was not very useful: app owners desire to be in the loop when such Route => GW bindings change.

@robscott robscott force-pushed the route-gateway-kep branch 2 times, most recently from 43e0354 to 91be407 Compare July 28, 2021 00:22
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 28, 2021
@robscott
Copy link
Member Author

Thanks to everyone for the great feedback on this in the community meeting yesterday! As suggested, I've added a rationale section and made the conceptual scope of the change I'm proposing more clear. PTAL

@robscott
Copy link
Member Author

/retest

site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Show resolved Hide resolved
site-src/geps/gep-724.md Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved

// 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If custom attachment points are supported, maybe SectionName is too specific? Maybe Anchor, TargetName, Fragment, ID?


#### Disadvantages

* Even the simplest cross-namespace reference from Gateway -> Route would
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A cross-namespace reference from Route -> Gateway would equally require the Gateway to have a reference policy, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This relies on the existing namespace selector in Gateway listeners to denote where Routes are trusted to bind from. Although it would be nice to reuse ReferencePolicy here, the nested list of listeners makes it significantly more complex + verbose as shown in the alternatives.

site-src/geps/gep-724.md Outdated Show resolved Hide resolved

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main objection to this approach is that ReferencePolicy is a access control object, not a binding object. Best to not mix those concerns.

Copy link
Member Author

@robscott robscott Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that we have to be careful here, but in this case ReferencePolicy would effectively been allowing access/references from Routes to Gateway listeners. To clarify I'm not saying this is a great alternative, just that I think it's still somewhat unique from binding.

@jpeach
Copy link
Contributor

jpeach commented Jul 28, 2021

I really like this. Istio has a pretty similar model (but this is much clearer, IMO) that hasn't shown any obvious issues

@howardjohn could you post a link to the istio mechanism?

@howardjohn
Copy link
Contributor

I really like this. Istio has a pretty similar model (but this is much clearer, IMO) that hasn't shown any obvious issues

@howardjohn could you post a link to the istio mechanism?

https://istio.io/latest/docs/reference/config/networking/gateway/, grep for "some-config-namespace/my-gateway". Basically VirtualService points to a specific Gateway, and the two way handshake is based on hostname alignment

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 3, 2021
@robscott robscott added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Aug 4, 2021
site-src/geps/gep-724.md Show resolved Hide resolved
#### Added
To Route Specs:
```go
// ParentRefs references the parent resources this Route should be attached
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should is lenient and doesn't get the point across.
We want to communicate "anyone can attach to this route ONLY IF they are in this list".
Something along the lines:

Suggested change
// ParentRefs references the parent resources this Route should be attached
// ParentRefs references the resources that can process/attach/use this Route.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, agreed, although I would nit that it should be attach to. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we think "can attach to" is sufficient here? Not exactly sure how to phrase this, but I think the behavior we want is for these referenced resources to be attached as long as they allow it. Exceptions to that would include conflicts or being referenced from a namespace or kind that isn't allowed. I think we need to be clear that this expresses a desire to attach to the given resource, not just permission for a resource to be attached.

site-src/geps/gep-724.md Show resolved Hide resolved
* 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the contention here that this proposal makes the route→route use-case more cumbersome? If so, I share that concern. Seems like that should be called out in the list of disadvantages.

//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Kind string `json:"kind"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When route-to-route is an option, that may become the majority. That said, using gateway as the default does make sense.

site-src/geps/gep-724.md Outdated Show resolved Hide resolved
// resource more than once.
//
// +optional
// +kubebuilder:validation:MaxItems=16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some rationale for limiting this to 16 parents?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just trying to avoid unbounded lists. Hard to think of any use case where this value would be more than 3 or 4. I guess it's possible that a single Route could be used to redirect to HTTPS, and that could get reused frequently. Did you have a use case for >16?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking of route-to-route. Maybe I want to add the same microservice to many different applications. I don't have a concrete use-case though, so I don't feel too strongly about this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's unlikely that we'll use this mechanism for attaching to a Route, but since we don't have a GEP for that yet, we can't know for sure. At least with values like this, we can always increase the number of items allowed, nearly impossible to decrease in the future.

Comment on lines 404 to 407
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +optional
SectionName string `json:"sectionName,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MinLength=0 or pointer type.

I suggested on a call that we could use ObjectFieldSelector or similar to specify the section, but I neglected to comment on the PR earlier. Would it make sense to use ObjectFieldSelector so the API is explicit about which section this references?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update to a pointer, thanks!

As far as ObjectFieldSelector (ref for anyone curious), I'm not sure it's quite what we're looking for here. As I understand it, it allows us to select a single field within a resource. That would allow for the valid spec.listeners[1], but lots of invalid values as well (someone may select some other part of a Gateway that may not make any sense).

Even the valid example of spec.listeners[1] has issues in that it would be tied to an index and not a name. That would require Gateway admins to maintain a very specific ordering of their listeners. If that order ever switched, lots of things could break. A similar argument could be made for changing a listener name, but that seems both more manageable and expected.

If I'm understanding the relevant API conventions correctly, this field should be interpreted with standard JavaScript syntax, so I don't think we'd even get the flexibility of jsonpath here.

Copy link
Contributor

@Miciah Miciah Aug 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I had in mind was that ParentRef would have a field like SectionFieldRef corev1.ObjectFieldSelector in addition to the SectionName string field, and the user would specify sectionFieldRef: spec.listeners along with sectionName. The API could require that the field identified by sectionFieldRef be a +listType=map, and then sectionName would specify the value to match against the field identified by +listMapKey:

type GatewaySpec struct {
	// ...
	// +listType=map
	// +listMapKey=name
	Listeners []Listener `json:"listeners"`

	// ...
}
type ParentRef struct {
	// ...

	// SectionFieldRef specifies a field containing sections within the
	// referent.  The specified field must be listType=map.  If
	// SectionFieldRef is specified, then SectionName must be specified as
	// well, and the value of SectionName will be used to identify the
	// specific section by matching it against the field specified by
	// listTypeMap.
	//
	// Support: Core
	//
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=253
	// +optional
	SectionFieldRef corev1.ObjectFieldSelector `json:"sectionFieldRef,omitempty"`

	// SectionName is ...
	SectionName string `json:"sectionName,omitempty"`

	// ...
}

Example:

kind: Gateway
metadata:
  namespace: infra
  name: lb
spec:
  listeners:
  - name: foo
    # ...
---
kind: HTTPRoute
# ...
spec:
  attachTo:
  - kind: Gateway
    namespace: infra
    name: lb
    sectionFieldRef:
      fieldPath: spec.listeners
    sectionName: foo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ObjectFieldSelector might not be the best type (maybe it would be better to define a new type that includes the field path and the item name), but the idea is to make explicit what SectionName is identifying.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation @Miciah! That does make a lot more sense. I can see that being a natural extension point if/when we want to support attaching to other arbitrary fields in Gateway or elsewhere. If I'm understanding correctly, it looks like it would be easy to add as a new optional field in the future without any kind of breaking changes.

site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
site-src/geps/gep-724.md Outdated Show resolved Hide resolved
Copy link
Contributor

@youngnick youngnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the direction and overall design, this LGTM, but I think there are few more tidy-up changes needed.

I think we need to ensure that we s/networking.x-k8s.io/gateway.networking.k8s.io everywhere. :)

site-src/geps/gep-724.md Outdated Show resolved Hide resolved
// ...
// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd completely agree that we should avoid this if we were still using selection, but with this new approach, I think the only thing most Gateway admins should have to worry about is the hostname, port, and protocol of a listener + the namespaces it supports. If you need to further restrict Route kinds, that's possible, but that seems like an advanced and usually unnecessary use case.

I think this is the argument that clinches this for me. As long as we mandate what controllers must do for the status of an object that attempts to attach to a listener that can't support it, I think it's okay.

However, I do think that it's important to have some status information that makes it clear to a Gateway owner that Routes have attempted to attach to a Listener that doesn't support them - we need to ensure there's a trail in both Gateway -> Route and Route -> Gateway directions for people to follow when things go wrong, because the objects have different owners.

In terms of having defaults, I think that it's important for the described behavior that this be defaulted to an empty list, but we can specify defaults in the RouteGroupKind struct. I feel like @hbagdi's concern here is about discoverability and status though?

Kinds []RouteGroupKind `json:"kinds,omitempty"`
}

type RouteGroupKind struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that the way these fields are used is that if this stanza is not specified, match anything. If it is, match only this. You must specify at least a Kind, in which case the group defaults to gateway.networking.io. Otherwise, you may specify any Kind.

I like it.

#### Added
To Route Specs:
```go
// ParentRefs references the parent resources this Route should be attached
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, agreed, although I would nit that it should be attach to. :)

//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Kind string `json:"kind"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lean towards explicitness on this one as well. +1 to no default.

site-src/geps/gep-724.md Outdated Show resolved Hide resolved
* May be more difficult to understand which Routes are attached to a Gateway.
* Adding/replacing a Gateway requires changes to Routes.

### Potential Expansion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We designed Contour's HTTPProxy to be more like (3), but we've definitely had requests for not requiring a specific Route name, which I think is more like (1).

I think that (3) requires more out-of-band coordination between parties than (1) does, which could be good or bad. I'm still inclined to err on the side of more coordination and explicit config, because blanked includes scare me a bit. But it's not a strong preference.

* 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing that makes Route -> Route inclusion much more complex is the semantics about how you carve up the URL/request metadata space. In HTTPProxy, we explicitly decided that the rules for assigning certain requests to a child HTTPProxy (in the Gateway API, this would be a child HTTPRoute) would become surrounding, uneditable constraints on that child, and that the child did not need to know about those constraints beforehand.

This has tradeoffs, obviously, but the key part here is that you can't design the Route -> Route reference mechanism without also designing how much the included Routes will know about where they are included.

In short, I think that what we have here is broad enough to allow us to experiment in either way, and we shouldn't hold this GEP up on figuring out how Route->Route inclusion works.

@jpeach
Copy link
Contributor

jpeach commented Aug 6, 2021

Overall, this is in good shape. Fantastic work by @robscott to integrate the deluge of feedback :)

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2021
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2021
}
```

### Gateway Status
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpeach @youngnick @hbagdi I've added a new status section to describe suggested additions to status. I think this is relevant to different comments from all of you throughout the review of this GEP, so wanted to get your feedback on this addition if you have time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, love it.

Copy link
Contributor

@youngnick youngnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status changes are excellent, as are the others.

/lgtm

}
```

### Gateway Status
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, love it.

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2021
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2021
@robscott
Copy link
Member Author

robscott commented Aug 6, 2021

Thanks to everyone for the great feedback on this GEP! Now that it's gotten 3 different LGTMs, I'm going to remove the hold and hope for one final LGTM to get it in. My last update clarifies that it is valid to attach a Route to multiple sections/listeners within the same Parent/Gateway. I'm thinking we should be able to cover any additional tweaks to this GEP in the implementation PR if necessary.

/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 6, 2021
@hbagdi
Copy link
Contributor

hbagdi commented Aug 6, 2021

Impressive heavy lifting here.
Thanks @robscott.
/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2021
@k8s-ci-robot k8s-ci-robot merged commit 9aacf14 into kubernetes-sigs:master Aug 6, 2021
@robscott robscott deleted the route-gateway-kep branch January 8, 2022 01:05
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robscott what font did you use for this / is there an original? I ask because ReferencePolicy was renamed but these two pictures weren't updated which left some of the documentation not making much sense.

For this picture, that's:
https://github.com/kubernetes-sigs/gateway-api/blob/9f820af97aaf6e8587712fec7b9266f42da81258/geps/gep-724/index.md#1-referencegrant-with-gateways-selecting-routes

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsoref jsoref mentioned this pull request Oct 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/gep PRs related to Gateway Enhancement Proposal(GEP) lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants