Skip to content

Commit 75998ba

Browse files
authored
Add status to Inference Pools (#4005)
Add status to Inference Pools Problem: Users want to see the current status of their Inference pools Solution: Add status for inference pools
1 parent aea4ef3 commit 75998ba

File tree

12 files changed

+1219
-71
lines changed

12 files changed

+1219
-71
lines changed

internal/controller/handler.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,17 +361,19 @@ func (h *eventHandlerImpl) updateStatuses(ctx context.Context, gr *graph.Graph,
361361
transitionTime,
362362
h.cfg.gatewayCtlrName,
363363
)
364+
inferencePoolReqs := status.PrepareInferencePoolRequests(gr.ReferencedInferencePools, transitionTime)
364365

365366
reqs := make(
366367
[]status.UpdateRequest,
367368
0,
368-
len(gcReqs)+len(routeReqs)+len(polReqs)+len(ngfPolReqs)+len(snippetsFilterReqs),
369+
len(gcReqs)+len(routeReqs)+len(polReqs)+len(ngfPolReqs)+len(snippetsFilterReqs)+len(inferencePoolReqs),
369370
)
370371
reqs = append(reqs, gcReqs...)
371372
reqs = append(reqs, routeReqs...)
372373
reqs = append(reqs, polReqs...)
373374
reqs = append(reqs, ngfPolReqs...)
374375
reqs = append(reqs, snippetsFilterReqs...)
376+
reqs = append(reqs, inferencePoolReqs...)
375377

376378
h.cfg.statusUpdater.UpdateGroup(ctx, groupAllExceptGateways, reqs...)
377379

internal/controller/state/conditions/conditions.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
inference "sigs.k8s.io/gateway-api-inference-extension/api/v1"
78
v1 "sigs.k8s.io/gateway-api/apis/v1"
89
"sigs.k8s.io/gateway-api/apis/v1alpha2"
910

@@ -1108,3 +1109,55 @@ func NewBackendTLSPolicyNoValidCACertificate(message string) Condition {
11081109
Message: message,
11091110
}
11101111
}
1112+
1113+
// NewInferencePoolAccepted returns a Condition that indicates that the InferencePool is accepted by the Gateway.
1114+
func NewInferencePoolAccepted() Condition {
1115+
return Condition{
1116+
Type: string(inference.InferencePoolConditionAccepted),
1117+
Status: metav1.ConditionTrue,
1118+
Reason: string(inference.InferencePoolConditionAccepted),
1119+
Message: "InferencePool is accepted by the Gateway.",
1120+
}
1121+
}
1122+
1123+
// NewInferencePoolResolvedRefs returns a Condition that
1124+
// indicates that all references in the InferencePool are resolved.
1125+
func NewInferencePoolResolvedRefs() Condition {
1126+
return Condition{
1127+
Type: string(inference.InferencePoolConditionResolvedRefs),
1128+
Status: metav1.ConditionTrue,
1129+
Reason: string(inference.InferencePoolConditionResolvedRefs),
1130+
Message: "Inference pool references a valid ExtensionRef.",
1131+
}
1132+
}
1133+
1134+
// NewDefaultInferenceConditions returns the default Conditions
1135+
// that must be present in the status of an InferencePool.
1136+
func NewDefaultInferenceConditions() []Condition {
1137+
return []Condition{
1138+
NewInferencePoolAccepted(),
1139+
NewInferencePoolResolvedRefs(),
1140+
}
1141+
}
1142+
1143+
// NewInferencePoolInvalidHTTPRouteNotAccepted returns a Condition that indicates that the InferencePool is not
1144+
// accepted because the associated HTTPRoute is not accepted by the Gateway.
1145+
func NewInferencePoolInvalidHTTPRouteNotAccepted(msg string) Condition {
1146+
return Condition{
1147+
Type: string(inference.InferencePoolConditionAccepted),
1148+
Status: metav1.ConditionFalse,
1149+
Reason: string(inference.InferencePoolReasonHTTPRouteNotAccepted),
1150+
Message: msg,
1151+
}
1152+
}
1153+
1154+
// NewInferencePoolInvalidExtensionref returns a Condition that indicates that the InferencePool is not
1155+
// accepted because the ExtensionRef is invalid.
1156+
func NewInferencePoolInvalidExtensionref(msg string) Condition {
1157+
return Condition{
1158+
Type: string(inference.InferencePoolConditionResolvedRefs),
1159+
Status: metav1.ConditionFalse,
1160+
Reason: string(inference.InferencePoolReasonInvalidExtensionRef),
1161+
Message: msg,
1162+
}
1163+
}

internal/controller/state/graph/graph.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ func BuildGraph(
260260
processedSnippetsFilters,
261261
state.InferencePools,
262262
)
263-
referencedInferencePools := buildReferencedInferencePools(routes, gws, state.InferencePools)
263+
264+
referencedInferencePools := buildReferencedInferencePools(routes, gws, state.InferencePools, state.Services)
264265

265266
l4routes := buildL4RoutesForGateways(
266267
state.TLSRoutes,

internal/controller/state/graph/graph_test.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,15 @@ func TestBuildGraph(t *testing.T) {
223223
Namespace: testNs,
224224
Name: controller.CreateInferencePoolServiceName("ipool"),
225225
},
226-
ServicePort: v1.ServicePort{Port: 80},
227-
Valid: true,
228-
Weight: 1,
229-
InvalidForGateways: map[types.NamespacedName]conditions.Condition{},
230-
IsInferencePool: true,
231-
EndpointPickerConfig: &inference.EndpointPickerRef{},
226+
ServicePort: v1.ServicePort{Port: 80},
227+
Valid: true,
228+
Weight: 1,
229+
InvalidForGateways: map[types.NamespacedName]conditions.Condition{},
230+
IsInferencePool: true,
231+
EndpointPickerConfig: &inference.EndpointPickerRef{
232+
Kind: kinds.Service,
233+
Name: inference.ObjectName(controller.CreateInferencePoolServiceName("ipool")),
234+
},
232235
},
233236
}
234237
rbrs := []RouteBackendRef{
@@ -389,6 +392,10 @@ func TestBuildGraph(t *testing.T) {
389392
TargetPorts: []inference.Port{
390393
{Number: 80},
391394
},
395+
EndpointPickerRef: inference.EndpointPickerRef{
396+
Kind: kinds.Service,
397+
Name: inference.ObjectName(controller.CreateInferencePoolServiceName("ipool")),
398+
},
392399
},
393400
}
394401

@@ -1325,6 +1332,13 @@ func TestBuildGraph(t *testing.T) {
13251332
ReferencedInferencePools: map[types.NamespacedName]*ReferencedInferencePool{
13261333
client.ObjectKeyFromObject(inferencePool): {
13271334
Source: inferencePool,
1335+
Gateways: []*gatewayv1.Gateway{
1336+
gw1.Source,
1337+
},
1338+
HTTPRoutes: []*L7Route{
1339+
inferenceRoute,
1340+
},
1341+
Conditions: []conditions.Condition{},
13281342
},
13291343
},
13301344
ReferencedCaCertConfigMaps: map[types.NamespacedName]*CaCertConfigMap{

internal/controller/state/graph/inferencepools.go

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package graph
22

33
import (
4+
"fmt"
5+
6+
v1 "k8s.io/api/core/v1"
47
"k8s.io/apimachinery/pkg/types"
58
"sigs.k8s.io/controller-runtime/pkg/client"
69
inference "sigs.k8s.io/gateway-api-inference-extension/api/v1"
10+
apiv1 "sigs.k8s.io/gateway-api/apis/v1"
711

12+
"github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions"
813
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/controller"
914
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds"
1015
)
@@ -14,6 +19,12 @@ import (
1419
type ReferencedInferencePool struct {
1520
// Source is the original InferencePool that this ReferencedInferencePool is based on.
1621
Source *inference.InferencePool
22+
// Gateways are the Gateways that this ReferencedInferencePool is attached to.
23+
Gateways []*apiv1.Gateway
24+
// HTTPRoutes are the HTTPRoutes that reference this InferencePool.
25+
HTTPRoutes []*L7Route
26+
// Conditions contains the conditions that should be applied to the InferencePool.
27+
Conditions []conditions.Condition
1728
}
1829

1930
// buildReferencedInferencePools builds a map of InferencePools that are referenced by HTTPRoutes
@@ -22,8 +33,9 @@ func buildReferencedInferencePools(
2233
routes map[RouteKey]*L7Route,
2334
gws map[types.NamespacedName]*Gateway,
2435
inferencePools map[types.NamespacedName]*inference.InferencePool,
36+
services map[types.NamespacedName]*v1.Service,
2537
) map[types.NamespacedName]*ReferencedInferencePool {
26-
referencedInferencePools := make(map[types.NamespacedName]*ReferencedInferencePool)
38+
referencedInferencePools := make(map[types.NamespacedName]*ReferencedInferencePool, len(inferencePools))
2739

2840
for _, gw := range gws {
2941
if gw == nil {
@@ -37,6 +49,17 @@ func buildReferencedInferencePools(
3749
return nil
3850
}
3951

52+
// validate each referenced InferencePool and add conditions.
53+
for _, refPool := range referencedInferencePools {
54+
if routeCond := validateInferencePoolRoutesAcceptance(refPool.Source, refPool.HTTPRoutes); routeCond != nil {
55+
refPool.Conditions = append(refPool.Conditions, *routeCond)
56+
}
57+
58+
if extensionRefCond := validateInferencePoolExtensionRef(refPool.Source, services); extensionRefCond != nil {
59+
refPool.Conditions = append(refPool.Conditions, *extensionRefCond)
60+
}
61+
}
62+
4063
return referencedInferencePools
4164
}
4265

@@ -48,8 +71,9 @@ func processInferencePoolsForGateway(
4871
inferencePools map[types.NamespacedName]*inference.InferencePool,
4972
) {
5073
gwKey := client.ObjectKeyFromObject(gw.Source)
74+
5175
for _, route := range routes {
52-
if !route.Valid || !routeBelongsToGateway(route.ParentRefs, gwKey) {
76+
if !routeBelongsToGateway(route.ParentRefs, gwKey) {
5377
continue
5478
}
5579

@@ -70,13 +94,83 @@ func processInferencePoolsForGateway(
7094
}
7195

7296
if _, referenced := referencedInferencePools[poolName]; !referenced {
73-
referencedInferencePools[poolName] = &ReferencedInferencePool{}
97+
referencedInferencePools[poolName] = &ReferencedInferencePool{
98+
Conditions: make([]conditions.Condition, 0, 2),
99+
Gateways: make([]*apiv1.Gateway, 0),
100+
HTTPRoutes: make([]*L7Route, 0),
101+
}
74102
}
75103

76104
if pool, exists := inferencePools[poolName]; exists {
77105
referencedInferencePools[poolName].Source = pool
106+
referencedInferencePools[poolName].Gateways = append(
107+
referencedInferencePools[poolName].Gateways,
108+
gw.Source,
109+
)
110+
referencedInferencePools[poolName].HTTPRoutes = append(
111+
referencedInferencePools[poolName].HTTPRoutes,
112+
route,
113+
)
78114
}
79115
}
80116
}
81117
}
82118
}
119+
120+
// validateInferencePoolExtensionRef validates the ExtensionRef of the InferencePool.
121+
func validateInferencePoolExtensionRef(
122+
ip *inference.InferencePool,
123+
svc map[types.NamespacedName]*v1.Service,
124+
) *conditions.Condition {
125+
var failingCond conditions.Condition
126+
if ip == nil {
127+
return nil
128+
}
129+
130+
// if kind is empty, it defaults to Service
131+
kind := string(ip.Spec.EndpointPickerRef.Kind)
132+
if kind == "" {
133+
kind = kinds.Service
134+
}
135+
136+
if kind != kinds.Service {
137+
failingCond = conditions.NewInferencePoolInvalidExtensionref("Invalid ExtensionRef kind: " + kind)
138+
return &failingCond
139+
}
140+
141+
eppNsName := types.NamespacedName{
142+
Name: string(ip.Spec.EndpointPickerRef.Name),
143+
Namespace: ip.GetNamespace(),
144+
}
145+
146+
if _, ok := svc[eppNsName]; !ok {
147+
failingCond = conditions.NewInferencePoolInvalidExtensionref("ExtensionRef Service not found: " + eppNsName.String())
148+
return &failingCond
149+
}
150+
151+
return nil
152+
}
153+
154+
// validateInferencePoolRoutesAcceptance checks if the routes that reference the InferencePool
155+
// are accepted by the Gateway.
156+
func validateInferencePoolRoutesAcceptance(ip *inference.InferencePool, routes []*L7Route) *conditions.Condition {
157+
if ip == nil || len(routes) == 0 {
158+
return nil
159+
}
160+
161+
// we do not need to validate that the route belongs to the gateway or not
162+
// we only process routes that belong to the gateway in the first place
163+
for _, route := range routes {
164+
if !route.Valid {
165+
cond := conditions.NewInferencePoolInvalidHTTPRouteNotAccepted(
166+
fmt.Sprintf("Referenced HTTPRoute %s/%s is not accepted by the Gateway",
167+
route.Source.GetNamespace(),
168+
route.Source.GetName(),
169+
),
170+
)
171+
return &cond
172+
}
173+
}
174+
175+
return nil
176+
}

0 commit comments

Comments
 (0)