Skip to content

Commit cdc8d37

Browse files
authored
e2e: add separate e2e target for infext (#1054)
1 parent 79c5b7a commit cdc8d37

15 files changed

+753
-655
lines changed

.github/codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ignore:
1717
- "**/*.pb.go"
1818
- "**/zz_generated.*.go"
1919
- "tests/internal/envtest.go"
20+
- "tests/internal/e2elib/**/*"
2021
- "tests/internal/testenvironment/**/*"
2122
- "tests/internal/testopeninference/openai_proxy.go"
2223
- "internal/testing/**/*"

.github/workflows/build_and_test.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,38 @@ jobs:
222222
TEST_GEMINI_API_KEY: ${{ secrets.ENVOY_AI_GATEWAY_GEMINI_API_KEY }}
223223
run: make test-e2e
224224

225+
test_e2e_inference_extension:
226+
name: E2E Test for Inference Extensions (Envoy Gateway ${{ matrix.name }})
227+
# TODO: make it possible to run this job on macOS as well, which is a bit tricky due to the nested
228+
# virtualization is not supported on macOS runners.
229+
# E.g. Use https://github.com/douglascamata/setup-docker-macos-action per the comment in
230+
# https://github.com/actions/runner-images/issues/17#issuecomment-1971073406
231+
runs-on: ubuntu-latest
232+
strategy:
233+
fail-fast: false
234+
matrix:
235+
include:
236+
- name: latest
237+
envoy_gateway_version: v0.0.0-latest
238+
- name: v1.5.0
239+
envoy_gateway_version: v1.5.0
240+
steps:
241+
- uses: actions/checkout@v4
242+
- uses: actions/setup-go@v5
243+
with:
244+
cache: false
245+
go-version-file: go.mod
246+
- uses: actions/cache@v4
247+
with:
248+
path: |
249+
~/.cache/go-build
250+
~/.cache/golangci-lint
251+
~/go/pkg/mod
252+
~/go/bin
253+
key: e2e-test-${{ hashFiles('**/go.mod', '**/go.sum', '**/Makefile') }}
254+
- uses: docker/setup-buildx-action@v3
255+
- run: make test-e2e-inference-extension
256+
225257
docker_push:
226258
# Docker builds are verified in test_e2e job, so we only need to push the images when the event is a push event.
227259
if: github.event_name == 'push'

.testcoverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ exclude:
1818
- zz_generated.deepcopy.go
1919
# They are test-only libraries.
2020
- tests/internal/envtest.go
21+
- tests/internal/e2elib/
2122
- tests/internal/testenvironment/
2223
- tests/internal/testopeninference/openai_proxy.go
2324
- internal/testing/

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ test-e2e: build-e2e ## Run the end-to-end tests with a local kind cluster.
172172
@echo "Run E2E tests"
173173
@go test -v ./tests/e2e/... $(GO_TEST_ARGS) $(GO_TEST_E2E_ARGS)
174174

175+
# This runs the end-to-end tests for the controller and extproc with a local kind cluster.
176+
.PHONY: test-e2e-inference-extension
177+
test-e2e-inference-extension: build-e2e ## Run the end-to-end tests with a local kind cluster for Gateway API Inference Extension.
178+
@echo "Run E2E tests for inference extension"
179+
@go test -v ./tests/e2e-inference-extension/... $(GO_TEST_ARGS) $(GO_TEST_E2E_ARGS)
180+
175181
##@ Common
176182

177183
# This builds a binary for the given command under the internal/cmd directory.

tests/e2e/conformance_test.go renamed to tests/e2e-inference-extension/conformance_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import (
1313
gie "sigs.k8s.io/gateway-api-inference-extension/conformance"
1414
v1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
1515
"sigs.k8s.io/gateway-api/conformance/utils/config"
16+
17+
"github.com/envoyproxy/ai-gateway/tests/internal/e2elib"
1618
)
1719

1820
func TestGatewayAPIInferenceExtension(t *testing.T) {
1921
const manifest = "testdata/inference-extension-conformance.yaml"
20-
require.NoError(t, kubectlApplyManifest(t.Context(), manifest))
22+
require.NoError(t, e2elib.KubectlApplyManifest(t.Context(), manifest))
2123

2224
options := gie.DefaultOptions(t)
2325
options.ReportOutputPath = "./inference-extension-conformance-test-report.yaml"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
6+
package e2e
7+
8+
import (
9+
"testing"
10+
11+
"github.com/envoyproxy/ai-gateway/tests/internal/e2elib"
12+
)
13+
14+
func TestMain(m *testing.M) {
15+
e2elib.TestMain(m, nil, true)
16+
}

tests/e2e/inference_pool_test.go renamed to tests/e2e-inference-extension/inference_pool_test.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@ import (
1919
"github.com/stretchr/testify/require"
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121
gwaiev1a2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2"
22+
23+
"github.com/envoyproxy/ai-gateway/tests/internal/e2elib"
2224
)
2325

2426
// TestInferencePoolIntegration tests the InferencePool integration with AI Gateway.
2527
func TestInferencePoolIntegration(t *testing.T) {
2628
// Apply the base test manifest.
2729
const baseManifest = "../../examples/inference-pool/base.yaml"
28-
require.NoError(t, kubectlApplyManifest(t.Context(), baseManifest))
30+
require.NoError(t, e2elib.KubectlApplyManifest(t.Context(), baseManifest))
2931

3032
// Test inferencePool with AIGatewayRoute.
3133
const aiGWRouteManifest = "../../examples/inference-pool/aigwroute.yaml"
32-
require.NoError(t, kubectlApplyManifest(t.Context(), aiGWRouteManifest))
34+
require.NoError(t, e2elib.KubectlApplyManifest(t.Context(), aiGWRouteManifest))
3335

3436
egSelector := "gateway.envoyproxy.io/owning-gateway-name=inference-pool-with-aigwroute"
35-
requireWaitForGatewayPodReady(t, egSelector)
37+
e2elib.RequireWaitForGatewayPodReady(t, egSelector)
3638

3739
// Verify InferencePool status is correctly set for the Gateway.
3840
t.Run("verify_inference_pool_status", func(t *testing.T) {
@@ -93,15 +95,15 @@ func TestInferencePoolIntegration(t *testing.T) {
9395
})
9496

9597
t.Cleanup(func() {
96-
_ = kubectlDeleteManifest(context.Background(), aiGWRouteManifest)
98+
_ = e2elib.KubectlDeleteManifest(context.Background(), aiGWRouteManifest)
9799
})
98100

99101
// Test inferencePool with HTTPRoute.
100102
const httpRouteManifest = "../../examples/inference-pool/httproute.yaml"
101-
require.NoError(t, kubectlApplyManifest(t.Context(), httpRouteManifest))
103+
require.NoError(t, e2elib.KubectlApplyManifest(t.Context(), httpRouteManifest))
102104

103105
egSelector = "gateway.envoyproxy.io/owning-gateway-name=inference-pool-with-httproute"
104-
requireWaitForPodReady(t, egSelector)
106+
e2elib.RequireWaitForPodReady(t, egSelector)
105107

106108
// Verify InferencePool status is correctly set for the HTTPRoute Gateway.
107109
t.Run("verify_inference_pool_status_httproute", func(t *testing.T) {
@@ -116,7 +118,7 @@ func TestInferencePoolIntegration(t *testing.T) {
116118
})
117119

118120
t.Cleanup(func() {
119-
_ = kubectlDeleteManifest(context.Background(), httpRouteManifest)
121+
_ = e2elib.KubectlDeleteManifest(context.Background(), httpRouteManifest)
120122
})
121123
}
122124

@@ -130,16 +132,16 @@ func testInferenceGatewayConnectivityByModel(t *testing.T, egSelector, model str
130132
// testInferenceGatewayConnectivity tests that the InferenceGateway is working as expected and returns a expected status code.
131133
func testInferenceGatewayConnectivity(t *testing.T, egSelector, body string, additionalHeaders map[string]string, expectedStatusCode int) {
132134
require.Eventually(t, func() bool {
133-
fwd := requireNewHTTPPortForwarder(t, egNamespace, egSelector, egDefaultServicePort)
134-
defer fwd.kill()
135+
fwd := e2elib.RequireNewHTTPPortForwarder(t, e2elib.EnvoyGatewayNamespace, egSelector, e2elib.EnvoyGatewayDefaultServicePort)
136+
defer fwd.Kill()
135137

136138
// Set timeout context.
137139
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
138140
defer cancel()
139141
// Create a request to the InferencePool backend with the correct model header.
140142
requestBody := body
141143
t.Logf("Request body: %s", requestBody)
142-
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fwd.address()+"/v1/chat/completions", strings.NewReader(requestBody))
144+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fwd.Address()+"/v1/chat/completions", strings.NewReader(requestBody))
143145
require.NoError(t, err)
144146
// Set required headers for InferencePool routing.
145147
req.Header.Set("Content-Type", "application/json")

tests/e2e/basic_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ import (
1717
"github.com/stretchr/testify/require"
1818

1919
internaltesting "github.com/envoyproxy/ai-gateway/internal/testing"
20+
"github.com/envoyproxy/ai-gateway/tests/internal/e2elib"
2021
)
2122

2223
// TestExamplesBasic tests the basic example in examples/basic directory.
2324
func Test_Examples_Basic(t *testing.T) {
2425
const manifestDir = "../../examples/basic"
2526
const manifest = manifestDir + "/basic.yaml"
26-
require.NoError(t, kubectlApplyManifest(t.Context(), manifest))
27+
require.NoError(t, e2elib.KubectlApplyManifest(t.Context(), manifest))
2728

2829
const egSelector = "gateway.envoyproxy.io/owning-gateway-name=envoy-ai-gateway-basic"
29-
requireWaitForGatewayPodReady(t, egSelector)
30+
e2elib.RequireWaitForGatewayPodReady(t, egSelector)
3031

3132
testUpstreamCase := examplesBasicTestCase{name: "testupsream", modelName: "some-cool-self-hosted-model"}
32-
testUpstreamCase.run(t, egNamespace, egSelector)
33+
testUpstreamCase.run(t, egSelector)
3334

3435
// This requires the following environment variables to be set:
3536
// - TEST_AWS_ACCESS_KEY_ID
@@ -43,20 +44,20 @@ func Test_Examples_Basic(t *testing.T) {
4344
// Replace the placeholders with the actual credentials and apply the manifests.
4445
openAIManifest, err := os.ReadFile(manifestDir + "/openai.yaml")
4546
require.NoError(t, err)
46-
require.NoError(t, kubectlApplyManifestStdin(t.Context(), strings.ReplaceAll(string(openAIManifest), "OPENAI_API_KEY", cc.OpenAIAPIKey)))
47+
require.NoError(t, e2elib.KubectlApplyManifestStdin(t.Context(), strings.ReplaceAll(string(openAIManifest), "OPENAI_API_KEY", cc.OpenAIAPIKey)))
4748
awsManifest, err := os.ReadFile(manifestDir + "/aws.yaml")
4849
require.NoError(t, err)
4950
awsManifestReplaced := strings.ReplaceAll(string(awsManifest), "AWS_ACCESS_KEY_ID", cc.AWSAccessKeyID)
5051
awsManifestReplaced = strings.ReplaceAll(awsManifestReplaced, "AWS_SECRET_ACCESS_KEY", cc.AWSSecretAccessKey)
51-
require.NoError(t, kubectlApplyManifestStdin(t.Context(), awsManifestReplaced))
52+
require.NoError(t, e2elib.KubectlApplyManifestStdin(t.Context(), awsManifestReplaced))
5253

5354
time.Sleep(5 * time.Second) // At least 5 seconds for the updated secret to be propagated.
5455

5556
for _, tc := range []examplesBasicTestCase{
5657
{name: "openai", modelName: "gpt-4o-mini", skip: !cc.OpenAIValid},
5758
{name: "aws", modelName: "us.meta.llama3-2-1b-instruct-v1:0", skip: !cc.AWSValid},
5859
} {
59-
tc.run(t, egNamespace, egSelector)
60+
tc.run(t, egSelector)
6061
}
6162
})
6263
}
@@ -67,19 +68,19 @@ type examplesBasicTestCase struct {
6768
skip bool
6869
}
6970

70-
func (tc examplesBasicTestCase) run(t *testing.T, egNamespace, egSelector string) {
71+
func (tc examplesBasicTestCase) run(t *testing.T, egSelector string) {
7172
t.Run(tc.name, func(t *testing.T) {
7273
if tc.skip {
7374
t.Skip("skipped due to missing credentials")
7475
}
7576
require.Eventually(t, func() bool {
76-
fwd := requireNewHTTPPortForwarder(t, egNamespace, egSelector, egDefaultServicePort)
77-
defer fwd.kill()
77+
fwd := e2elib.RequireNewHTTPPortForwarder(t, e2elib.EnvoyGatewayNamespace, egSelector, e2elib.EnvoyGatewayDefaultServicePort)
78+
defer fwd.Kill()
7879

7980
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
8081
defer cancel()
8182

82-
client := openai.NewClient(option.WithBaseURL(fwd.address() + "/v1/"))
83+
client := openai.NewClient(option.WithBaseURL(fwd.Address() + "/v1/"))
8384

8485
chatCompletion, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
8586
Messages: []openai.ChatCompletionMessageParamUnion{

0 commit comments

Comments
 (0)