From 0e097a982cc0a728e6628e22af8b66ad1ad7eaf4 Mon Sep 17 00:00:00 2001 From: bobzetian Date: Thu, 13 Nov 2025 18:40:39 +0000 Subject: [PATCH 1/2] feat(conformance): add conformance test for endpoint served. --- conformance/resources/base.yaml | 5 +- .../gateway_destination_endpoint_served.go | 109 ++++++++++++++++++ .../gateway_destination_endpoint_served.yaml | 23 ++++ 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 conformance/tests/gateway_destination_endpoint_served.go create mode 100644 conformance/tests/gateway_destination_endpoint_served.yaml diff --git a/conformance/resources/base.yaml b/conformance/resources/base.yaml index 3b4933caf..d3eea5d7c 100644 --- a/conformance/resources/base.yaml +++ b/conformance/resources/base.yaml @@ -200,7 +200,7 @@ spec: terminationGracePeriodSeconds: 130 containers: - name: epp - image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251105-cbb8928 + image: us-central1-docker.pkg.dev/bobzetian-gke-dev/gateway-api-inference-extension/epp:latest imagePullPolicy: Always args: - --pool-name @@ -298,7 +298,7 @@ spec: terminationGracePeriodSeconds: 130 containers: - name: epp - image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251105-cbb8928 + image: us-central1-docker.pkg.dev/bobzetian-gke-dev/gateway-api-inference-extension/epp:latest imagePullPolicy: Always args: - --pool-name @@ -560,6 +560,7 @@ data: kind: EndpointPickerConfig plugins: - type: header-based-testing-filter + - type: destination-endpoint-served-verifier schedulingProfiles: - name: conformance-profile plugins: diff --git a/conformance/tests/gateway_destination_endpoint_served.go b/conformance/tests/gateway_destination_endpoint_served.go new file mode 100644 index 000000000..862abf46d --- /dev/null +++ b/conformance/tests/gateway_destination_endpoint_served.go @@ -0,0 +1,109 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tests + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/types" + gwhttp "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/pkg/features" + + "sigs.k8s.io/gateway-api-inference-extension/conformance/resources" + k8sutils "sigs.k8s.io/gateway-api-inference-extension/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/requestcontrol/plugins/test" + testscheduling "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/framework/plugins/test" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayDestinationEndpointServed) +} + +var GatewayDestinationEndpointServed = suite.ConformanceTest{ + ShortName: "GatewayDestinationEndpointServed", + Description: "A conformance test to verify that the gateway correctly reports the endpoint that served the request.", + Manifests: []string{"tests/gateway_destination_endpoint_served.yaml"}, + Features: []features.FeatureName{ + features.FeatureName("SupportInferencePool"), + features.SupportGateway, + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + const ( + hostname = "primary.example.com" + path = "/destination-endpoint-served-test" + ) + + httpRouteNN := types.NamespacedName{Name: "httproute-for-destination-endpoint-served", Namespace: resources.AppBackendNamespace} + gatewayNN := resources.PrimaryGatewayNN + poolNN := resources.PrimaryInferencePoolNN + backendPodLabels := map[string]string{"app": resources.PrimaryModelServerAppLabel} + + t.Log("Verifying HTTPRoute and InferencePool are accepted and the Gateway has an address.") + k8sutils.HTTPRouteMustBeAcceptedAndResolved(t, s.Client, s.TimeoutConfig, httpRouteNN, gatewayNN) + k8sutils.InferencePoolMustBeAcceptedByParent(t, s.Client, poolNN, gatewayNN) + gwAddr := k8sutils.GetGatewayEndpoint(t, s.Client, s.TimeoutConfig, gatewayNN) + + t.Logf("Fetching backend pods with labels: %v", backendPodLabels) + pods, err := k8sutils.GetPodsWithLabel(t, s.Client, resources.AppBackendNamespace, backendPodLabels, s.TimeoutConfig) + require.NoError(t, err, "Failed to get backend pods") + require.Len(t, pods, resources.ModelServerPodReplicas, "Expected to find %d backend pods, but found %d.", resources.ModelServerPodReplicas, len(pods)) + + podIPs := make([]string, len(pods)) + podNames := make([]string, len(pods)) + for i, pod := range pods { + podIPs[i] = pod.Status.PodIP + podNames[i] = pod.Name + } + + requestBody := `{ + "model": "conformance-fake-model", + "prompt": "Write as if you were a critic: San Francisco" + }` + t.Run("Request is served by the selected backend pod", func(t *testing.T) { + for i := 0; i < len(pods); i++ { + gwhttp.MakeRequestAndExpectEventuallyConsistentResponse( + t, + s.RoundTripper, + s.TimeoutConfig, + gwAddr, + gwhttp.ExpectedResponse{ + Request: gwhttp.Request{ + Host: hostname, + Path: path, + Method: http.MethodPost, + Body: requestBody, + Headers: map[string]string{ + testscheduling.HeaderTestEppEndPointSelectionKey: podIPs[i], + }, + }, + Response: gwhttp.Response{ + StatusCodes: []int{http.StatusOK}, + Headers: map[string]string{ + test.ConformanceTestResultHeader: podIPs[i] + ":3000", // The echo server's port is 3000. + }, + }, + Backend: podNames[i], + Namespace: resources.AppBackendNamespace, + }, + ) + } + }) + }, +} diff --git a/conformance/tests/gateway_destination_endpoint_served.yaml b/conformance/tests/gateway_destination_endpoint_served.yaml new file mode 100644 index 000000000..0b15da1df --- /dev/null +++ b/conformance/tests/gateway_destination_endpoint_served.yaml @@ -0,0 +1,23 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: httproute-for-destination-endpoint-served + namespace: inference-conformance-app-backend +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: conformance-primary + namespace: inference-conformance-infra + sectionName: http + hostnames: + - "primary.example.com" + rules: + - backendRefs: + - group: inference.networking.k8s.io + kind: InferencePool + name: primary-inference-pool + matches: + - path: + type: PathPrefix + value: /destination-endpoint-served-test From 88f205582f4b108712767706e9ac3a640ccbc6fe Mon Sep 17 00:00:00 2001 From: bobzetian Date: Wed, 19 Nov 2025 19:49:23 +0000 Subject: [PATCH 2/2] update to the correct EPP image. --- conformance/resources/base.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conformance/resources/base.yaml b/conformance/resources/base.yaml index d3eea5d7c..87de80adb 100644 --- a/conformance/resources/base.yaml +++ b/conformance/resources/base.yaml @@ -200,7 +200,7 @@ spec: terminationGracePeriodSeconds: 130 containers: - name: epp - image: us-central1-docker.pkg.dev/bobzetian-gke-dev/gateway-api-inference-extension/epp:latest + image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251119-2aaf2a6 imagePullPolicy: Always args: - --pool-name @@ -298,7 +298,7 @@ spec: terminationGracePeriodSeconds: 130 containers: - name: epp - image: us-central1-docker.pkg.dev/bobzetian-gke-dev/gateway-api-inference-extension/epp:latest + image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251119-2aaf2a6 imagePullPolicy: Always args: - --pool-name @@ -507,7 +507,7 @@ spec: terminationGracePeriodSeconds: 130 containers: - name: epp - image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251105-cbb8928 + image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v20251119-2aaf2a6 imagePullPolicy: Always args: - --pool-name