-
Notifications
You must be signed in to change notification settings - Fork 508
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
ListenerSet APIs + Generated Clients #3588
base: main
Are you sure you want to change the base?
Conversation
I'm going to pull
Into a separate PR |
/hold for #3589 |
apisx/v1alpha1/listenerset_types.go
Outdated
// +kubebuilder:validation:XValidation:message="Listener name must be unique within the Gateway",rule="self.all(l1, self.exists_one(l2, l1.name == l2.name))" | ||
// | ||
// # TODO: Figure out how to allow empty port here | ||
// #+kubebuilder:validation:XValidation:message="Combination of port, protocol and hostname must be unique for each listener",rule="self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note this TODO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok this should become
self.all(l1,
!has(l1.port) || // Port is missing no conflict can ever occur
self.exists_one(l2,
has(l2.port) && // If L2 has no port than there can't be a conflict
// Regular check since both listeners have a port
l1.port == l2.port &&
l1.protocol == l2.protocol &&
(has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))
)
)
/hold need to add the Gateway changes |
/hold cancel |
Ok - I figured out there's a marker
So we could have a single clientset that has stable and experimental apis - let me know what people prefer |
@@ -74,7 +74,7 @@ type ListenerNamespaces struct { | |||
// * None: Only listeners defined in the Gateway's spec are allowed | |||
// | |||
// +optional | |||
// +kubebuilder:default=Same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR seems to jump us all the way from provisional
to experimental
without hitting implementable
. If we have consensus then there might not actually be anything wrong with that, but bare minimum we should update the GEP status accordingly here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want me to split out the APIs in to a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No that's OK! I just think we should flag the status change here and then please feel free to consider this comment resolved.
I just realized another opportunity to update the GEP, but I'll create a separate comment.
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dprotaso The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
type AllowedListeners struct { | ||
// Namespaces defines which namespaces ListenerSets can be attached to this Gateway. | ||
// While this feature is experimental, the default value is to allow no ListenerSets. | ||
// | ||
// +optional | ||
// +kubebuilder:default={from: None} | ||
Namespaces *ListenerNamespaces `json:"namespaces,omitempty"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this part of the API, because I think it captures a very important use case for cluster admins: when they want a Gateway
to operate more like a classic ingress-controller, in that there's a single LB for routes across many namespaces.
What occurs to me is that we should update the GEP, as it currently states cross-namespace as a "later goal" but it seems like we're already going for it, and I'm very much in favor of this being an immediate goal. Anything I'm missing? (if not, please just feel free to update the GEP to reflect the goal and then consider my comment resolved at your discretion)
Eventually I personally would like to take this a step further: I would like our documentation to be even more clear about a strong intention for this kind of use case as I anticipate it being a very common ask over time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. I think that cross-namespace is very important for this GEP and I'd love to see it in the first iteration of the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think overall we're heading in a good direction here, but I think we need to reiterate what feels like a more obvious question as I read more: "What is a Gateway after this, really?"
Personally I'll have to think on this quite a bit and come back around to it 🤔
9385dd0
to
547053f
Compare
I pushed a change to combine everything into a single clientset. It requires me to have a single It should be easy to revert the single client changes so let me know what you prefer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @dprotaso!
limitations under the License. | ||
*/ | ||
|
||
package apix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package apix | |
package apisx |
// | ||
// +k8s:openapi-gen=true | ||
// +kubebuilder:object:generate=true | ||
// +groupName=gateway.networking.k8s-x.io |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// +groupName=gateway.networking.k8s-x.io | |
// +groupName=gateway.networking.x-k8s.io |
// | ||
// 1. "parent" Gateway | ||
// 2. ListenerSet ordered by creation time (oldest first) | ||
// 3. ListenerSet ordered alphabetically by “{namespace}/{name}”. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we do if there are conflicting Listeners within a ListenerSet?
|
||
type ListenerEntry struct { | ||
// Name is the name of the Listener. This name MUST be unique within a | ||
// Gateway. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 how is this supposed to work in the wild world of ListenerSets? If I have listener foo
and attach it to Gateway bar
, can a route target the foo
listener in the bar
Gateway and expect to be attached to the listener in this ListenerSet? What about name conflicts?
// Listeners in a `Gateway` and their attached `ListenerSets` are concatenated | ||
// as a list when programming the underlying infrastructure. | ||
// | ||
// Listeners should be merged using the following precedence: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there are conflicts, how are implementations supposed to handle them? IE what if multiple Listeners across different ListenerSets claim the same hostname? Do we reject the entire ListenerSet? Just the Listener? How do we communicate this to users?
AllowedRoutes *AllowedRoutes `json:"allowedRoutes,omitempty"` | ||
} | ||
|
||
type ListenerSetStatus struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure of this status really locks us into sticking within a single ParentRef. I think this is OK, just want to call out that the lack of namespacing by parent here will make it very difficult to every support multiple ParentRefs. With that said, trying to namespace all of these conditions by parent would be hard to scale, so this feels like a strong case in favor of a single parent.
// Controllers may raise this condition with other reasons, | ||
// but should prefer to use the reasons listed above to improve | ||
// interoperability. | ||
ListenerSetConditionProgrammed ListenerSetConditionType = "Programmed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what cases would we expect this status to be different than the parent status? Could a Gateway be "Programmed" but not a ListenerSet? What about the other way around?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that a ListenerSet cannot be programmed in a non-programmed Gateway, while the other way around would be possible.
type AllowedListeners struct { | ||
// Namespaces defines which namespaces ListenerSets can be attached to this Gateway. | ||
// While this feature is experimental, the default value is to allow no ListenerSets. | ||
// | ||
// +optional | ||
// +kubebuilder:default={from: None} | ||
Namespaces *ListenerNamespaces `json:"namespaces,omitempty"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. I think that cross-namespace is very important for this GEP and I'd love to see it in the first iteration of the API.
|
||
// ParentGatewayReference identifies an API object including its namespace, | ||
// defaulting to Gateway. | ||
type ParentGatewayReference struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a generic reference, I would consider naming it differently than ParentGatewayReference
, as the reference can of any group/kind
// ListenerSetSpec defines the desired state of a ListenerSet. | ||
type ListenerSetSpec struct { | ||
// ParentRef references the Gateway that the listeners are attached to. | ||
ParentRef ParentGatewayReference `json:"parentRef,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should consider to make this field Required. Is there any specific reason to accept a ListenerSet
with an empty ParentRef
?
type ListenerSetStatus struct { | ||
// Conditions describe the current conditions of the ListenerSet. | ||
// | ||
// Implementations should prefer to express ListenerSet conditions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should here? I think we may want to have a MUST here, as it'll likely be part of the conditions checked by conformance tests.
// Controllers may raise this condition with other reasons, | ||
// but should prefer to use the reasons listed above to improve | ||
// interoperability. | ||
ListenerSetConditionProgrammed ListenerSetConditionType = "Programmed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that a ListenerSet cannot be programmed in a non-programmed Gateway, while the other way around would be possible.
PR needs rebase. 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-sigs/prow repository. |
Part of #1713
Merge #3589 beforehand (to make this diff easier)
Note: this required two different client sets because if you try to generate a client set containing the stable group (
gateway.networking.k8s.io
) and experimental(gateway.networking.k8s-x.io)
client-gen usesgateway
part from both api groups for naming in the client set and it creates a conflictRelease Note