Skip to content

Commit 73d16aa

Browse files
committed
feat: support inferencepool status
Signed-off-by: bitliu <[email protected]>
1 parent cb0496a commit 73d16aa

File tree

3 files changed

+209
-7
lines changed

3 files changed

+209
-7
lines changed

internal/controller/inference_pool_test.go

Lines changed: 202 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,9 @@ func TestBuildAcceptedCondition(t *testing.T) {
365365
// Test other condition types that should result in True status and keep the original type.
366366
condition = buildAcceptedCondition(3, "test-controller", "SomeOtherType", "other message")
367367

368-
require.Equal(t, "SomeOtherType", condition.Type) // Type is preserved for non-"NotAccepted" types
369-
require.Equal(t, metav1.ConditionTrue, condition.Status) // Status is True for non-"NotAccepted" types
370-
require.Equal(t, "Accepted", condition.Reason) // Reason is "Accepted" for non-"NotAccepted" types
368+
require.Equal(t, "SomeOtherType", condition.Type) // Type is preserved for non-"NotAccepted" types.
369+
require.Equal(t, metav1.ConditionTrue, condition.Status) // Status is True for non-"NotAccepted" types.
370+
require.Equal(t, "Accepted", condition.Reason) // Reason is "Accepted" for non-"NotAccepted" types.
371371
require.Contains(t, condition.Message, "InferencePool has been Accepted by controller test-controller: other message")
372372
require.Equal(t, int64(3), condition.ObservedGeneration)
373373
}
@@ -726,7 +726,7 @@ func TestInferencePoolController_EdgeCases(t *testing.T) {
726726
"app": "test-app",
727727
},
728728
TargetPortNumber: 8080,
729-
// No ExtensionRef
729+
// No ExtensionRef.
730730
},
731731
}
732732
require.NoError(t, fakeClient.Create(context.Background(), inferencePoolNoExtRef))
@@ -754,7 +754,7 @@ func TestInferencePoolController_EdgeCases(t *testing.T) {
754754
EndpointPickerConfig: gwaiev1a2.EndpointPickerConfig{
755755
ExtensionRef: &gwaiev1a2.Extension{
756756
ExtensionReference: gwaiev1a2.ExtensionReference{
757-
Name: "", // Empty name
757+
Name: "", // Empty name.
758758
},
759759
},
760760
},
@@ -930,7 +930,7 @@ func TestInferencePoolController_UpdateInferencePoolStatus(t *testing.T) {
930930
ObjectMeta: metav1.ObjectMeta{
931931
Name: "test-inference-pool",
932932
Namespace: "default",
933-
Generation: 5, // Set a specific generation for testing
933+
Generation: 5, // Set a specific generation for testing.
934934
},
935935
Spec: gwaiev1a2.InferencePoolSpec{
936936
Selector: map[gwaiev1a2.LabelKey]gwaiev1a2.LabelValue{
@@ -978,3 +978,199 @@ func TestInferencePoolController_UpdateInferencePoolStatus(t *testing.T) {
978978
require.Equal(t, metav1.ConditionTrue, resolvedRefsCondition.Status)
979979
require.Equal(t, "ResolvedRefs", resolvedRefsCondition.Reason)
980980
}
981+
982+
func TestInferencePoolController_GetReferencedGateways_ErrorHandling(t *testing.T) {
983+
fakeClient := requireNewFakeClientWithIndexesAndInferencePool(t)
984+
c := NewInferencePoolController(fakeClient, kubefake.NewSimpleClientset(), ctrl.Log)
985+
986+
// Create an InferencePool.
987+
inferencePool := &gwaiev1a2.InferencePool{
988+
ObjectMeta: metav1.ObjectMeta{
989+
Name: "test-inference-pool",
990+
Namespace: "default",
991+
},
992+
Spec: gwaiev1a2.InferencePoolSpec{
993+
Selector: map[gwaiev1a2.LabelKey]gwaiev1a2.LabelValue{
994+
"app": "test-app",
995+
},
996+
TargetPortNumber: 8080,
997+
},
998+
}
999+
1000+
// Test getReferencedGateways with various scenarios.
1001+
gateways, err := c.getReferencedGateways(context.Background(), inferencePool)
1002+
require.NoError(t, err)
1003+
require.Empty(t, gateways, "Should return empty list when no routes reference the InferencePool")
1004+
1005+
// Create an AIGatewayRoute that references the InferencePool but no Gateway.
1006+
aiGatewayRoute := &aigv1a1.AIGatewayRoute{
1007+
ObjectMeta: metav1.ObjectMeta{
1008+
Name: "test-route-no-gateway",
1009+
Namespace: "default",
1010+
},
1011+
Spec: aigv1a1.AIGatewayRouteSpec{
1012+
Rules: []aigv1a1.AIGatewayRouteRule{
1013+
{
1014+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
1015+
{
1016+
Name: "test-inference-pool",
1017+
Group: ptr.To("inference.networking.x-k8s.io"),
1018+
Kind: ptr.To("InferencePool"),
1019+
},
1020+
},
1021+
},
1022+
},
1023+
},
1024+
}
1025+
require.NoError(t, fakeClient.Create(context.Background(), aiGatewayRoute))
1026+
1027+
gateways, err = c.getReferencedGateways(context.Background(), inferencePool)
1028+
require.NoError(t, err)
1029+
require.Empty(t, gateways, "Should return empty list when route has no parent refs")
1030+
}
1031+
1032+
func TestInferencePoolController_GatewayReferencesInferencePool_HTTPRoute(t *testing.T) {
1033+
fakeClient := requireNewFakeClientWithIndexesAndInferencePool(t)
1034+
c := NewInferencePoolController(fakeClient, kubefake.NewSimpleClientset(), ctrl.Log)
1035+
1036+
// Create a Gateway.
1037+
gateway := &gwapiv1.Gateway{
1038+
ObjectMeta: metav1.ObjectMeta{
1039+
Name: "test-gateway",
1040+
Namespace: "default",
1041+
},
1042+
Spec: gwapiv1.GatewaySpec{
1043+
GatewayClassName: "test-class",
1044+
},
1045+
}
1046+
require.NoError(t, fakeClient.Create(context.Background(), gateway))
1047+
1048+
// Create an HTTPRoute that references the Gateway and InferencePool.
1049+
httpRoute := &gwapiv1.HTTPRoute{
1050+
ObjectMeta: metav1.ObjectMeta{
1051+
Name: "test-http-route",
1052+
Namespace: "test-namespace",
1053+
},
1054+
Spec: gwapiv1.HTTPRouteSpec{
1055+
CommonRouteSpec: gwapiv1.CommonRouteSpec{
1056+
ParentRefs: []gwapiv1.ParentReference{
1057+
{
1058+
Name: "test-gateway",
1059+
Namespace: ptr.To(gwapiv1.Namespace("default")),
1060+
},
1061+
},
1062+
},
1063+
Rules: []gwapiv1.HTTPRouteRule{
1064+
{
1065+
BackendRefs: []gwapiv1.HTTPBackendRef{
1066+
{
1067+
BackendRef: gwapiv1.BackendRef{
1068+
BackendObjectReference: gwapiv1.BackendObjectReference{
1069+
Group: ptr.To(gwapiv1.Group("inference.networking.x-k8s.io")),
1070+
Kind: ptr.To(gwapiv1.Kind("InferencePool")),
1071+
Name: "test-inference-pool",
1072+
},
1073+
},
1074+
},
1075+
},
1076+
},
1077+
},
1078+
},
1079+
}
1080+
require.NoError(t, fakeClient.Create(context.Background(), httpRoute))
1081+
1082+
// Test positive case - Gateway references InferencePool through HTTPRoute.
1083+
result := c.gatewayReferencesInferencePool(context.Background(), gateway, "test-inference-pool", "test-namespace")
1084+
require.True(t, result, "Should return true when Gateway references InferencePool through HTTPRoute")
1085+
1086+
// Test negative case - different InferencePool name.
1087+
result = c.gatewayReferencesInferencePool(context.Background(), gateway, "different-pool", "test-namespace")
1088+
require.False(t, result, "Should return false when Gateway doesn't reference the specified InferencePool")
1089+
1090+
// Test negative case - different namespace.
1091+
result = c.gatewayReferencesInferencePool(context.Background(), gateway, "test-inference-pool", "different-namespace")
1092+
require.False(t, result, "Should return false when InferencePool is in different namespace")
1093+
}
1094+
1095+
func TestInferencePoolController_ValidateExtensionReference_EdgeCases(t *testing.T) {
1096+
fakeClient := requireNewFakeClientWithIndexesAndInferencePool(t)
1097+
c := NewInferencePoolController(fakeClient, kubefake.NewSimpleClientset(), ctrl.Log)
1098+
1099+
// Test with nil ExtensionRef.
1100+
inferencePoolNilExt := &gwaiev1a2.InferencePool{
1101+
ObjectMeta: metav1.ObjectMeta{
1102+
Name: "test-inference-pool-nil-ext",
1103+
Namespace: "default",
1104+
},
1105+
Spec: gwaiev1a2.InferencePoolSpec{
1106+
Selector: map[gwaiev1a2.LabelKey]gwaiev1a2.LabelValue{
1107+
"app": "test-app",
1108+
},
1109+
TargetPortNumber: 8080,
1110+
// No EndpointPickerConfig.
1111+
},
1112+
}
1113+
1114+
err := c.validateExtensionReference(context.Background(), inferencePoolNilExt)
1115+
require.NoError(t, err, "Should not error when ExtensionRef is nil")
1116+
1117+
// Test with ExtensionRef but nil ExtensionRef field.
1118+
inferencePoolNilExtRef := &gwaiev1a2.InferencePool{
1119+
ObjectMeta: metav1.ObjectMeta{
1120+
Name: "test-inference-pool-nil-extref",
1121+
Namespace: "default",
1122+
},
1123+
Spec: gwaiev1a2.InferencePoolSpec{
1124+
Selector: map[gwaiev1a2.LabelKey]gwaiev1a2.LabelValue{
1125+
"app": "test-app",
1126+
},
1127+
TargetPortNumber: 8080,
1128+
EndpointPickerConfig: gwaiev1a2.EndpointPickerConfig{
1129+
// ExtensionRef is nil.
1130+
},
1131+
},
1132+
}
1133+
1134+
err = c.validateExtensionReference(context.Background(), inferencePoolNilExtRef)
1135+
require.NoError(t, err, "Should not error when ExtensionRef field is nil")
1136+
1137+
// Test with service in different namespace (should fail).
1138+
serviceOtherNS := &corev1.Service{
1139+
ObjectMeta: metav1.ObjectMeta{
1140+
Name: "service-other-ns",
1141+
Namespace: "other-namespace",
1142+
},
1143+
Spec: corev1.ServiceSpec{
1144+
Ports: []corev1.ServicePort{
1145+
{
1146+
Port: 9002,
1147+
},
1148+
},
1149+
},
1150+
}
1151+
require.NoError(t, fakeClient.Create(context.Background(), serviceOtherNS))
1152+
1153+
inferencePoolOtherNS := &gwaiev1a2.InferencePool{
1154+
ObjectMeta: metav1.ObjectMeta{
1155+
Name: "test-inference-pool-other-ns",
1156+
Namespace: "default", // InferencePool in default namespace.
1157+
},
1158+
Spec: gwaiev1a2.InferencePoolSpec{
1159+
Selector: map[gwaiev1a2.LabelKey]gwaiev1a2.LabelValue{
1160+
"app": "test-app",
1161+
},
1162+
TargetPortNumber: 8080,
1163+
EndpointPickerConfig: gwaiev1a2.EndpointPickerConfig{
1164+
ExtensionRef: &gwaiev1a2.Extension{
1165+
ExtensionReference: gwaiev1a2.ExtensionReference{
1166+
Name: "service-other-ns", // Service in other-namespace.
1167+
},
1168+
},
1169+
},
1170+
},
1171+
}
1172+
1173+
err = c.validateExtensionReference(context.Background(), inferencePoolOtherNS)
1174+
require.Error(t, err, "Should error when ExtensionReference service is in different namespace")
1175+
require.Contains(t, err.Error(), "ExtensionReference service service-other-ns not found in namespace default")
1176+
}

tests/e2e/conformance/envoy-ai-gateway-report.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Copyright Envoy AI Gateway Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
# The full text of the Apache license is available in the LICENSE file at
4+
# the root of the repo.
5+
16
GatewayAPIInferenceExtensionVersion: v0.5.1
27
apiVersion: gateway.networking.k8s.io/v1
38
date: "2025-08-01T14:34:49+08:00"

tests/e2e/conformance_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func TestGatewayAPIInferenceExtension(t *testing.T) {
3939
options.TimeoutConfig = defaultTimeoutConfig
4040
options.GatewayClassName = "inference-pool"
4141
options.SkipTests = []string{
42-
"EppUnAvailableFailOpen"}
42+
"EppUnAvailableFailOpen",
43+
}
4344

4445
gie.RunConformanceWithOptions(t, options)
4546
}

0 commit comments

Comments
 (0)