Skip to content

Conversation

@davidjumani
Copy link
Contributor

@davidjumani davidjumani commented May 20, 2025

Description

Adds support for ListenerSets laid out in https://gateway-api.sigs.k8s.io/geps/gep-1713.

This works around the restriction of a maximum of 64 listeners defined on a gateway. Users can now define multiple listenersets and map them to the appropriate parent gateway

Fixes #10662

This PR also adds policy support for listener sets

Added tests for the same

--- PASS: TestListenerSet (61.70s)
    --- PASS: TestListenerSet/ListenerSet (46.42s)
        --- PASS: TestListenerSet/ListenerSet/TestInvalidListenerSetNonExistingGW (3.48s)
        --- PASS: TestListenerSet/ListenerSet/TestInvalidListenerSetNotAllowed (5.46s)
        --- PASS: TestListenerSet/ListenerSet/TestPolicies (11.37s)
        --- PASS: TestListenerSet/ListenerSet/TestValidListenerSet (13.49s)

Change Type

/kind new_feature

Changelog

Allows a Kubernetes gateway to have more than 64 listeners by implementing ListenerSets defined in https://gateway-api.sigs.k8s.io/geps/gep-1713. Listener Sets can define their own listeners and be mapped to a parent gateway via their parentRef. The Kubernetes gateway will have the merged list of all listeners from itself and attached ListenerSets. This experimental feature requires the `xlistenersets.gateway.networking.x-k8s.io` CRD to be present.

@github-actions github-actions bot added do-not-merge/kind-invalid Indicates a PR lacks a `kind/foo` label and requires one. do-not-merge/release-note-invalid Indicates that a PR should not merge because it's missing one of the release note labels. labels May 20, 2025
)

// Trigger an event when the gateway changes. This can even be a change in listener sets attached to the gateway
go func() {
Copy link
Contributor Author

@davidjumani davidjumani May 22, 2025

Choose a reason for hiding this comment

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

Does this need to be in a goroutine ?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, will create a goroutine under the hood

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 we need to call Register only after krt has started. @stevenctl do you know if that issue was fixed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, it's letting me respond here. For posterity: I believe the only issue is if we need the initial state, and luckily in this case we do not.

Signed-off-by: David Jumani <[email protected]>
@github-actions github-actions bot added kind/feature Categorizes issue or PR as related to a new feature. release-note and removed do-not-merge/kind-invalid Indicates a PR lacks a `kind/foo` label and requires one. do-not-merge/release-note-invalid Indicates that a PR should not merge because it's missing one of the release note labels. labels May 23, 2025
@davidjumani davidjumani changed the title [WIP] Add support for GEP-1713: ListenerSets Add support for GEP-1713: ListenerSets May 23, 2025
@yuval-k yuval-k self-assigned this May 23, 2025
Comment on lines +72 to +76
type ListenerSetReporter interface {
Listener(listener *gwv1.Listener) ListenerReporter
ListenerName(listenerName string) ListenerReporter
SetCondition(condition GatewayCondition)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Decided to keep this the same as the GatewayReporter since they are pretty much the same thing. This avoids too many changes in the code, especially around status reporting

Name: gw.GetName(),
Namespace: gw.GetNamespace(),
}
irGW := d.inputs.CommonCollections.GatewayIndex.Gateways.GetKey(gwKey.ResourceName())
Copy link
Contributor

Choose a reason for hiding this comment

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

it might be possible that we miss the krt lookup and only have the api.Gateway here if we hit this path before translation finishes; we're guaranteed to eventually get it though

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 this should work because the controller should start only after krt is warm, IIRC.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the WaitUntilSynced above in an attempt to fix that

return h
}

func allowedListenerSet(gw *gwv1.Gateway, listenerSet *gwxv1a1.XListenerSet, namespaces krt.Collection[NamespaceMetadata]) (func(krt.HandlerContext, string) bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style nit: alias the returned func or give it's args names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, added!

Name: "example-gateway",
},
}),
// TODO: Add this once istio adds support for listener sets
Copy link
Contributor

Choose a reason for hiding this comment

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

we should eventually make our test translator handle getting istio-unknown types into krt clients so we can test for our own CRs too..

Signed-off-by: David Jumani <[email protected]>
})
}

func ResolveNamespace(namespace *apiv1.Namespace) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this exported?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot to remove it in the cleanup. Fixed

Name: gw.GetName(),
Namespace: gw.GetNamespace(),
}
irGW := d.inputs.CommonCollections.GatewayIndex.Gateways.GetKey(gwKey.ResourceName())
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 this should work because the controller should start only after krt is warm, IIRC.

)

// Trigger an event when the gateway changes. This can even be a change in listener sets attached to the gateway
go func() {
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 we need to call Register only after krt has started. @stevenctl do you know if that issue was fixed?

// Trigger an event when the gateway changes. This can even be a change in listener sets attached to the gateway
c.cfg.CommonCollections.GatewayIndex.Gateways.Register(func(o krt.Event[ir.Gateway]) {
gw := o.Latest()
c.reconciler.customEvents <- event.TypedGenericEvent[ir.Gateway]{
Copy link
Contributor

Choose a reason for hiding this comment

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

consider adding select with default clause; so if the channel is full it won't get stuck.
i think the channel would then just need capacity of 1

Copy link
Contributor Author

@davidjumani davidjumani May 28, 2025

Choose a reason for hiding this comment

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

Would that lead to missed events when a gateway is modified ?

Copy link
Contributor

Choose a reason for hiding this comment

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

KRT handles a slow/blocking callback internally; it won't make other Register/dependent collections get stuck.
While we can do something like go func() { events <- ev }() here, I think the current code is fine.

listenerSetPolicies := h.policies.getTargetingPolicies(kctx, extensionsplug.GatewayAttachmentPoint, lsIR.ObjectSource, "", ls.GetLabels())

for _, l := range ls.Spec.Listeners {
listenerSpecificPolicies := h.policies.getTargetingPolicies(kctx, extensionsplug.RouteAttachmentPoint, lsIR.ObjectSource, string(l.Name), ls.GetLabels())
Copy link
Contributor

Choose a reason for hiding this comment

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

why are we getting route attached policies here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is similar to fetching policies for listeners in the gateway
Ref:

AttachedPolicies: toAttachedPolicies(h.policies.getTargetingPolicies(kctx, extensionsplug.RouteAttachmentPoint, out.ObjectSource, string(l.Name), i.GetLabels())),

@stevenctl stevenctl added this pull request to the merge queue May 29, 2025
Merged via the queue into kgateway-dev:main with commit 3913ba2 May 29, 2025
20 checks passed
yuval-k pushed a commit that referenced this pull request May 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature. release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GEP-1713: ListenerSets - Standard Mechanism to Merge Multiple Gateways support

4 participants