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

GEP 735 L4 Traffic Matching #932

Merged
merged 5 commits into from
Dec 7, 2021

Conversation

shaneutt
Copy link
Member

@shaneutt shaneutt commented Nov 15, 2021

What type of PR is this?
/kind feature
/kind design
/kind gep
/kind api-change
-->

What this PR does / why we need it:

This adds traffic matching to enable more expressive routing for TCPRoute and UDPRoute.

Which issue(s) this PR fixes:

Fixes #735

Does this PR introduce a user-facing change?:

@k8s-ci-robot
Copy link
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/feature Categorizes issue or PR as related to a new feature. kind/design Categorizes issue or PR as related to design. kind/gep PRs related to Gateway Enhancement Proposal(GEP) kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Nov 15, 2021
@shaneutt shaneutt changed the title GEP 735 L4 Traffic Matching (+spec) GEP 735 L4 Traffic Matching Nov 15, 2021
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=45
Address string `json:"address"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add type? Like listener.address.

The reason is for many cases you don't want to go type out a giant IPv6 address, which is either long or dynamic. However, the controller may have higher level constructs. For example, address: my-gcp-address-with-a-name or even address: httpbin.default.svc.cluster.local (ie use the Cluster IP of the service)

If you know what FE80::0202:B3FF:FE1E:8329 is you probably have a name for it somewhere that is more ergonomic to reference.

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 want to make sure I'm clear: are you suggesting:

a) type for IP addresses (v4, v6)
b) type for address (e.g. can include hostnames)
c) both

Copy link
Member

Choose a reason for hiding this comment

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

I think the idea of a "NamedAddress" here makes sense, similar to the address type we have on Gateway. I don't think we'd need to distinguish between v4 and v6 addresses though, but don't feel too strongly about that.

Copy link
Member Author

Choose a reason for hiding this comment

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

1462088 changes this to support both IPAddress and NamedAddress options, let me know your thoughts 🙇

Copy link
Contributor

Choose a reason for hiding this comment

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

NamedAddress feels a bit like ImplementationSpecific in path match (which we got rid off).
@robscott @howardjohn Does NamedAddress by itself (without the additional context of the provider) enough here?

Copy link
Member

Choose a reason for hiding this comment

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

That's a good point, NamedAddress is going to have a different meaning to most implementations. That's easier to solve on Gateways because each will only be implemented by a single controller. This feels similar in nature to RegEx path matching - each implementation will have a different take on it, but it means something conceptually similar.

In most cases you would not want to specify a RegEx match on a Route implemented by multiple controllers unless you knew they supported the same variation of RegEx. I think this would come with the same constraints. Although that's not ideal, I do think it's a sufficiently valuable concept to include. We likely need to document the limitations of these implementation-specific types though.

Copy link
Member Author

Choose a reason for hiding this comment

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

@howardjohn I would like to get your feedback on this: given the contention I'm leaning towards removing the NamedAddress for now such as to get the groundwork completed more quickly and allow for a follow up to suggest NamedAddress or something similar as a separate iteration so that we can focus on it specifically, let me know if you have any thoughts or concerns here.

Copy link
Member

Choose a reason for hiding this comment

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

NamedAddress feels a bit like ImplementationSpecific in path match

@hbagdi I think there's a significant difference here. One of the key problems with "ImplementationSpecific" path matching was that any given implementation may have multiple extended kinds of path matching they'd like to support, and "ImplementationSpecific" could only really support one per implementation. With NamedAddress, I think there will be at most one interpretation per implementation. We already have a "NamedAddress" concept in the API that's fairly well defined, so it's not so much a question of if we should add the concept of a "NamedAddress", but if it makes sense here.

given the contention I'm leaning towards removing the NamedAddress for now

@shaneutt my personal preference is that we leave this in place. I think it's a natural extension point and helps show the purpose of even having an address type here. Although it is something that will have a different meaning per implementation, that matches how NamedAddress already works elsewhere in the API, and seems conceptually similar to RegEx matching.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, @hbagdi does that sound reasonable to you?

Copy link
Member

@robscott robscott left a comment

Choose a reason for hiding this comment

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

Thanks for your work on this @shaneutt!

//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=45
Address string `json:"address"`
Copy link
Member

Choose a reason for hiding this comment

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

I think the idea of a "NamedAddress" here makes sense, similar to the address type we have on Gateway. I don't think we'd need to distinguish between v4 and v6 addresses though, but don't feel too strongly about that.

@shaneutt shaneutt force-pushed the shaneutt/gep-735 branch 8 times, most recently from aac6a8c to 1462088 Compare November 15, 2021 22:37
@shaneutt shaneutt marked this pull request as ready for review November 15, 2021 22:41
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 15, 2021
@shaneutt shaneutt requested a review from robscott November 15, 2021 22:41
@tokers
Copy link
Contributor

tokers commented Nov 16, 2021

Another useful route match type is the TLS matching (according to the SNI), is this also in the plan?

Comment on lines 45 to 79
```go
type AddressMatch struct {
// Type of the address, either IPAddress or NamedAddress.
//
// If NamedAddress is used this is a custom and specific value for each
// implementation to handle (and add validation for) according to their
// own needs.
//
// For IPAddress the implementor may expect either IPv4 or IPv6.
//
// +optional
// +kubebuilder:validation:Enum=IPAddress;NamedAddress
// +kubebuilder:default=IPAddress
Type *AddressType `json:"type,omitempty"`

// Value of the address. The validity of the values will depend
// on the type and support by the controller.
//
// Examples: `1.2.3.4`, `128::1`, `my-named-address`.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Value string `json:"value"`
}
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered making AddressMatch a union type, with Type being the discriminator? Having a generic Value field looks odd in a Kubernetes API, and it forfeits the opportunity to enforce stricter validation.

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 had not as I was very intentionally trying to make this similar to GatewayAddress so that we didn't end up with two different ways of describing an address, do you have any thoughts on that?

Copy link
Contributor

@Miciah Miciah Nov 16, 2021

Choose a reason for hiding this comment

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

Ah, I can see how making AddressMatch similar to GatewayAddress could reduce confusion if the user wanted to specify the same IP address or named address on both the listener and an address match. On the other hand, AddressMatch is missing Hostname, the semantics of NamedAddress across the two types gets a little funny, and I'm wary of making things appear alike superficially when they are fundamentally different.

Elaborating on what I mean by funny semantics, do the names that the user can specify for a NamedAddress in the listener refer to the same set of things as the names that the user can specify for a NamedAddress in a source match? What about for destination matches? Is this relationship (or lack thereof) entirely up to the implementation to define?

(I now kind of wish we had used a union type for GatewayAddress, but it's probably too late to change that now without an extremely compelling need.)

Copy link
Member

Choose a reason for hiding this comment

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

For better or worse, we've used the Type and Value pair heavily throughout the API, including path, header, and query param matching. I believe HTTPRoute filters are the only place we've used a proper union type. Although union types are common in upstream Kubernetes, I think the pattern proposed here is also quite common.

Since we're dealing with CRDs and there is not proper union support yet, we'd be stuck with adding additional webhook validation if we transitioned to a union type. My personal preference would be to continue with the proposed approach to more closely match the pattern we've used elsewhere in Gateway API.

Copy link
Contributor

Choose a reason for hiding this comment

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

Using union types probably would have made sense in those other contexts (and in hindsight, it would have simplified defining support levels). It makes even more sense for AddressMatch because we can specify OpenAPI validation to enforce that any value that the user puts in, say, the "address" field must be either an IPv4 or an IPv6 address (the following is incomplete and not tested, but this example should illustrate what I mean):

sourceAddresses:
  items:
    oneOf:
    - properties:
        address:
          format: ipv4
    - properties:
        address:
          format: ipv6
    properties:
      address:
        type: string
    type: object
  type: array

Granted, CRD validation may have gaps (if I recall correctly, it admits an object definition that specifies a field that doesn't correspond to the discriminator value). However, I figure that some validation is better than none.

I think we'd need webhook validation anyway to validate that the user provided a valid regexp for the path, header, and query param matching, but regexp support is "Custom" and underspecified anyway, so it's fair to leave that up to implementors.

Using a union type is just a suggestion for enabling slightly improved validation and clarity, so I won't press it if the goal is to make AddressMatch match GatewayAddress.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the problem with both using a union type and having more interesting OpenAPI validation is that neither fits well through the CRD-validation-shaped hole we have available for now.

I also think that having some more complex validation in either the webhook or an exported library (as we've discussed briefly a few times) would be beneficial.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Miciah I like your suggestion to use a union type, but I'm also aware of some of the other implications that were brought up by @robscott and @youngnick. I'm inclined to think that the use of union types are generally preferable but given the existing precedents perhaps doing that needs to be a holistic effort and not this specific PR starting some of the APIs in a different direction than the rest and starting an implementation dichotomy. I think a reasonable action and resolution for this comment may be to use what we have now for this PR and create a follow up issue that effectively states "convert everything to union types and significantly improve our CRD validation" what are your thoughts on that?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Miciah just wanted to double check this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, please disregard the suggestion to use a union type as far as this PR is concerned.

@hbagdi
Copy link
Contributor

hbagdi commented Nov 16, 2021

Another useful route match type is the TLS matching (according to the SNI), is this also in the plan?

Gateway API already supports that. Please take a look at TLSRoute.

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.

I really like the shape of this, but have a couple of small things about the exact field semantics. In general, I think we should add enum fields very carefully, as they are tricky to manage, because adding values to an enum is a breaking API change (by the API guidelines).

Comment on lines 45 to 79
```go
type AddressMatch struct {
// Type of the address, either IPAddress or NamedAddress.
//
// If NamedAddress is used this is a custom and specific value for each
// implementation to handle (and add validation for) according to their
// own needs.
//
// For IPAddress the implementor may expect either IPv4 or IPv6.
//
// +optional
// +kubebuilder:validation:Enum=IPAddress;NamedAddress
// +kubebuilder:default=IPAddress
Type *AddressType `json:"type,omitempty"`

// Value of the address. The validity of the values will depend
// on the type and support by the controller.
//
// Examples: `1.2.3.4`, `128::1`, `my-named-address`.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Value string `json:"value"`
}
```
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the problem with both using a union type and having more interesting OpenAPI validation is that neither fits well through the CRD-validation-shaped hole we have available for now.

I also think that having some more complex validation in either the webhook or an exported library (as we've discussed briefly a few times) would be beneficial.

@hbagdi
Copy link
Contributor

hbagdi commented Nov 30, 2021

LGTM once there is clarity on the proxy protocol.

@hbagdi
Copy link
Contributor

hbagdi commented Dec 6, 2021

/lgtm

/hold for other reviews

@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 Dec 6, 2021
@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 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 Dec 6, 2021
@shaneutt shaneutt requested review from hbagdi and youngnick December 6, 2021 23:43
@robscott
Copy link
Member

robscott commented Dec 7, 2021

Thanks! Will leave final LGTM for someone else (cc @bowei @hbagdi @jpeach @youngnick).

/approve
/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 Dec 7, 2021
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: robscott, shaneutt

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 7, 2021
@youngnick
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 7, 2021
@k8s-ci-robot k8s-ci-robot merged commit d734fa1 into kubernetes-sigs:master Dec 7, 2021
@shaneutt shaneutt deleted the shaneutt/gep-735 branch December 7, 2021 13:49
@candita
Copy link
Contributor

candita commented Jul 28, 2022

@shaneutt what happens once the GEP is accepted? Who completes the work?

Update: I found the PR in v1alpha2, and the revert of the GEP to provisional. Bummer.

@shaneutt
Copy link
Member Author

@shaneutt what happens once the GEP is accepted? Who completes the work?

Update: I found the PR in v1alpha2, and the revert of the GEP to provisional. Bummer.

Indeed, given the current state this GEP's actual status is closest to being considered declined. If you are feeling strongly about it, you could move it forward or even create a new one that follows up on it with a different implemention. Extra context: I created this GEP entirely because there was an open ask to do this, I did not have and still do not have a strong need for this functionality in my own implementations currently so I'm inclined to call it declined and wait for someone else who has a need to come along later.

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/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API kind/design Categorizes issue or PR as related to design. kind/feature Categorizes issue or PR as related to a new feature. kind/gep PRs related to Gateway Enhancement Proposal(GEP) lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Address Matching to TCPRoute and UDPRoute
9 participants