Skip to content

Commit 042347d

Browse files
authored
[backport] make BackendRef.Port Optional for InferencePool Refs (#11450)
Signed-off-by: Daneyon Hansen <[email protected]>
1 parent 9929cea commit 042347d

File tree

2 files changed

+138
-9
lines changed

2 files changed

+138
-9
lines changed

internal/kgateway/krtcollections/policy.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,49 @@ func (i *BackendIndex) getBackendFromRef(kctx krt.HandlerContext, localns string
134134
}
135135

136136
func (i *BackendIndex) GetBackendFromRef(kctx krt.HandlerContext, src ir.ObjectSource, ref gwv1.BackendObjectReference) (*ir.BackendObjectIR, error) {
137-
fromns := src.Namespace
138-
139-
fromgk := schema.GroupKind{
140-
Group: src.Group,
141-
Kind: src.Kind,
137+
// Check if a ReferenceGrant allows the cross-namespace ref
138+
fromNs := src.Namespace
139+
fromGK := schema.GroupKind{Group: src.Group, Kind: src.Kind}
140+
to := toFromBackendRef(fromNs, ref)
141+
if !i.refgrants.ReferenceAllowed(kctx, fromGK, fromNs, to) {
142+
return nil, ErrMissingReferenceGrant
142143
}
143-
to := toFromBackendRef(fromns, ref)
144144

145-
if i.refgrants.ReferenceAllowed(kctx, fromgk, fromns, to) {
145+
// Fetch the InferencePool IR by name/namespace only, ignoring any user-supplied port altogether.
146+
// TODO [danehans]: Add a warning message to HTTPRoute status the required change is made per
147+
// discussion in github.com/kubernetes-sigs/gateway-api-inference-extension/discussions/918
148+
kind := strOr(ref.Kind, string(wellknown.ServiceKind))
149+
if kind == wellknown.InferencePoolKind {
150+
// Find the pool's IR in availableBackends
151+
poolGK := to.GetGroupKind()
152+
nns := types.NamespacedName{Namespace: to.Namespace, Name: to.Name}
153+
var poolIR *ir.BackendObjectIR
154+
155+
if col, exists := i.availableBackends[poolGK]; exists {
156+
matches := krt.Fetch(kctx, col, krt.FilterGeneric(func(obj any) bool {
157+
b, ok := obj.(ir.BackendObjectIR)
158+
return ok &&
159+
b.ObjectSource.Name == nns.Name &&
160+
b.ObjectSource.Namespace == nns.Namespace
161+
}))
162+
if len(matches) > 0 {
163+
poolIR = &matches[0]
164+
}
165+
}
166+
167+
if poolIR == nil {
168+
return nil, &NotFoundError{NotFoundObj: to}
169+
}
170+
171+
// Overwrite ref.Port so lookup uses the correct port
172+
correct := gwv1.PortNumber(poolIR.Port)
173+
ref.Port = &correct
174+
175+
// Delegate to the normal port-aware lookup
146176
return i.getBackendFromRef(kctx, src.Namespace, ref)
147-
} else {
148-
return nil, ErrMissingReferenceGrant
149177
}
178+
179+
return i.getBackendFromRef(kctx, src.Namespace, ref)
150180
}
151181

152182
// MARK: GatewayIndex

internal/kgateway/krtcollections/policy_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import (
1818
gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
1919
gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
2020

21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
2124
extensionsplug "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/plugin"
2225
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir"
2326
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils/krtutil"
@@ -326,6 +329,92 @@ func TestFailInferencePoolWithRefGrantWrongKind(t *testing.T) {
326329
}
327330
}
328331

332+
func TestInferencePoolPortOverride(t *testing.T) {
333+
cases := []struct {
334+
name string
335+
poolNs string
336+
refGrant *gwv1beta1.ReferenceGrant
337+
providedPort gwv1.PortNumber
338+
wantPort int32
339+
expectError bool
340+
}{
341+
{
342+
name: "same‐ns override",
343+
poolNs: "",
344+
refGrant: nil,
345+
providedPort: 9001,
346+
wantPort: 8080,
347+
},
348+
{
349+
name: "cross‐ns override with RefGrant",
350+
poolNs: "foo-ns",
351+
refGrant: refGrantWithNs("foo-ns"),
352+
providedPort: 9999,
353+
wantPort: 8080,
354+
},
355+
}
356+
357+
for _, tc := range cases {
358+
t.Run(tc.name, func(t *testing.T) {
359+
inputs := []any{
360+
infPool(tc.poolNs),
361+
}
362+
if tc.refGrant != nil {
363+
inputs = append(inputs, tc.refGrant)
364+
}
365+
// Build an HTTPRoute whose HTTPBackendRef.Port is “wrong”
366+
ns := ""
367+
if tc.poolNs != "" {
368+
ns = tc.poolNs
369+
}
370+
route := &gwv1.HTTPRoute{
371+
ObjectMeta: metav1.ObjectMeta{
372+
Name: "httproute",
373+
Namespace: "default",
374+
},
375+
Spec: gwv1.HTTPRouteSpec{
376+
Rules: []gwv1.HTTPRouteRule{{
377+
BackendRefs: []gwv1.HTTPBackendRef{{
378+
BackendRef: gwv1.BackendRef{
379+
BackendObjectReference: gwv1.BackendObjectReference{
380+
Group: ptr.To(gwv1.Group(infextv1a2.GroupVersion.Group)),
381+
Kind: ptr.To(gwv1.Kind(wellknown.InferencePoolKind)),
382+
Name: gwv1.ObjectName("foo"),
383+
Namespace: ptrToNamespace(ns),
384+
Port: &tc.providedPort,
385+
},
386+
},
387+
}},
388+
}},
389+
},
390+
}
391+
inputs = append(inputs, route)
392+
393+
ir := translateRoute(t, inputs)
394+
require.NotNil(t, ir)
395+
b := getBackends(ir)[0]
396+
if tc.expectError {
397+
require.Error(t, b.Err)
398+
return
399+
}
400+
require.NoError(t, b.Err)
401+
402+
// Assert the BackendObject IR port number
403+
assert.Equal(t, tc.wantPort, b.BackendObject.Port,
404+
"expected the pool's TargetPortNumber to override the provided port")
405+
})
406+
}
407+
}
408+
409+
// Helper to build a Namespace pointer, or nil if empty
410+
func ptrToNamespace(ns string) *gwv1.Namespace {
411+
if ns == "" {
412+
return nil
413+
}
414+
n := gwv1.Namespace(ns)
415+
return &n
416+
}
417+
329418
func svc(ns string) *corev1.Service {
330419
if ns == "" {
331420
ns = "default"
@@ -404,6 +493,16 @@ func refGrant() *gwv1beta1.ReferenceGrant {
404493
}
405494
}
406495

496+
// Helper that calls refGrant() but with its Namespace set to the given namespace
497+
func refGrantWithNs(ns string) *gwv1beta1.ReferenceGrant {
498+
rg := refGrant()
499+
rg.Namespace = ns
500+
for i := range rg.Spec.From {
501+
rg.Spec.From[i].Namespace = gwv1.Namespace("default")
502+
}
503+
return rg
504+
}
505+
407506
func k8sSvcUpstreams(services krt.Collection[*corev1.Service]) krt.Collection[ir.BackendObjectIR] {
408507
return krt.NewManyCollection(services, func(kctx krt.HandlerContext, svc *corev1.Service) []ir.BackendObjectIR {
409508
uss := []ir.BackendObjectIR{}

0 commit comments

Comments
 (0)