Skip to content

Commit 18da0ac

Browse files
committed
[aiconformance] Validate networking/ai_inference requirements
1 parent 4607dcc commit 18da0ac

6 files changed

Lines changed: 310 additions & 0 deletions

File tree

tests/e2e/scenarios/ai-conformance/run-test.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ set -o nounset
1919
set -o pipefail
2020

2121
REPO_ROOT=$(git rev-parse --show-toplevel)
22+
23+
BIN_DIR="${REPO_ROOT}/.build/bin"
24+
mkdir -p "${BIN_DIR}"
25+
export PATH="${BIN_DIR}:$PATH"
26+
2227
source "${REPO_ROOT}"/tests/e2e/scenarios/lib/common.sh
2328

2429
# AI Conformance requirements:
@@ -187,6 +192,19 @@ helm upgrade -i kuberay-operator kuberay/kuberay-operator \
187192
echo "Installing Kueue..."
188193
kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.14.8/manifests.yaml
189194

195+
# Gateway API
196+
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.5.0" | kubectl apply -f -
197+
198+
# Gateway API Implenmentation - Istio
199+
helm repo add istio https://istio-release.storage.googleapis.com/charts
200+
helm repo update
201+
202+
wget https://github.com/istio/istio/releases/download/1.29.1/istioctl-1.29.1-linux-amd64.tar.gz -O "${BIN_DIR}/istioctl.tar.gz"
203+
tar -xzf "${BIN_DIR}/istioctl.tar.gz" -C "${BIN_DIR}"
204+
rm "${BIN_DIR}/istioctl.tar.gz"
205+
206+
istioctl install --set profile=minimal -y
207+
190208
echo "----------------------------------------------------------------"
191209
echo "Verifying Cluster and Components"
192210
echo "----------------------------------------------------------------"

tests/e2e/scenarios/ai-conformance/validators/markdown.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type MarkdownOutput struct {
3232

3333
// WriteText writes the given plain text to the markdown file.
3434
func (o *MarkdownOutput) WriteText(text string) {
35+
o.printf("\n")
3536
o.printf("%s", text)
3637
}
3738

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ai_inference
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
"testing"
23+
24+
"k8s.io/kops/tests/e2e/scenarios/ai-conformance/validators"
25+
)
26+
27+
// TestNetworking_AIInference corresponds to the networking/ai_inference conformance requirement,
28+
// tested using the KubeRay operator as an example of a complex AI operator with a CRD.
29+
func TestNetworking_AIInference(t *testing.T) {
30+
// Description:
31+
// Support the Kubernetes Gateway API with an implementation for advanced traffic management for inference services,
32+
// which enables capabilities like weighted traffic splitting,
33+
// header-based routing (for OpenAI protocol headers),
34+
// and optional integration with service meshes."
35+
36+
h := validators.NewValidatorHarness(t)
37+
38+
h.Logf("# Gateway API support for AI inference")
39+
40+
h.Run("weighted-traffic-splitting", func(h *validators.ValidatorHarness) {
41+
h.Logf("## Verify Weighted Traffic Splitting")
42+
ns := h.TestNamespace()
43+
44+
h.ApplyManifest(ns, "testdata/weighted-traffic-splitting.yaml")
45+
// h.ShellExec(fmt.Sprintf("kubectl wait -n %s --for='jsonpath={.status.jobDeploymentStatus}=Complete' rayjob/rayjob-sample --timeout=300s", ns))
46+
47+
status := h.ShellExec(fmt.Sprintf("kubectl get httproute weighted-traffic-splitting -n %s -oyaml", ns))
48+
if !strings.Contains(status.Stdout(), "Accepted") {
49+
h.Fatalf("Did not find Accepted message in status: %s", status.Stdout())
50+
} else {
51+
h.Success("Found Accepted message in status, indicating the HTTPRoute was accepted successfully.")
52+
}
53+
})
54+
55+
h.Run("header-based-routing", func(h *validators.ValidatorHarness) {
56+
h.Logf("## Verify Header Based Routing")
57+
ns := h.TestNamespace()
58+
59+
h.ApplyManifest(ns, "testdata/header-based-routing.yaml")
60+
// h.ShellExec(fmt.Sprintf("kubectl wait -n %s --for='jsonpath={.status.jobDeploymentStatus}=Complete' rayjob/rayjob-sample --timeout=300s", ns))
61+
62+
status := h.ShellExec(fmt.Sprintf("kubectl get httproute header-based-routing -n %s -oyaml", ns))
63+
if !strings.Contains(status.Stdout(), "Accepted") {
64+
h.Fatalf("Did not find Accepted message in status: %s", status.Stdout())
65+
} else {
66+
h.Success("Found Accepted message in status, indicating the HTTPRoute was accepted successfully.")
67+
}
68+
})
69+
70+
if h.AllPassed() {
71+
h.RecordConformance("networking/ai_inference")
72+
}
73+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: Gateway
3+
metadata:
4+
name: gateway
5+
spec:
6+
gatewayClassName: istio
7+
listeners:
8+
- name: default
9+
hostname: "*.example.com"
10+
port: 80
11+
protocol: HTTP
12+
allowedRoutes:
13+
namespaces:
14+
from: All
15+
16+
---
17+
18+
apiVersion: v1
19+
kind: Service
20+
metadata:
21+
name: app1
22+
spec:
23+
selector:
24+
app: app1
25+
ports:
26+
- protocol: TCP
27+
port: 80
28+
targetPort: 8080
29+
30+
---
31+
32+
apiVersion: apps/v1
33+
kind: Deployment
34+
metadata:
35+
name: app1
36+
spec:
37+
replicas: 1
38+
selector:
39+
matchLabels:
40+
app: app1
41+
template:
42+
metadata:
43+
labels:
44+
app: app1
45+
spec:
46+
containers:
47+
- name: default
48+
image: registry.k8s.io/e2e-test-images/agnhost:2.39
49+
command: ["/agnhost", "netexec", "--http-port", "8080"]
50+
51+
---
52+
53+
apiVersion: v1
54+
kind: Service
55+
metadata:
56+
name: app2
57+
spec:
58+
selector:
59+
app: app2
60+
ports:
61+
- protocol: TCP
62+
port: 80
63+
targetPort: 8080
64+
65+
---
66+
67+
apiVersion: apps/v1
68+
kind: Deployment
69+
metadata:
70+
name: app2
71+
spec:
72+
replicas: 1
73+
selector:
74+
matchLabels:
75+
app: app2
76+
template:
77+
metadata:
78+
labels:
79+
app: app2
80+
spec:
81+
containers:
82+
- name: default
83+
image: registry.k8s.io/e2e-test-images/agnhost:2.39
84+
command: ["/agnhost", "netexec", "--http-port", "8080"]
85+
86+
---
87+
88+
apiVersion: gateway.networking.k8s.io/v1
89+
kind: HTTPRoute
90+
metadata:
91+
name: header-based-routing
92+
spec:
93+
parentRefs:
94+
- name: gateway
95+
hostnames: ["header-based-routing.example.com"]
96+
rules:
97+
- matches:
98+
- headers:
99+
- name: X-Model-Version
100+
value: v1
101+
backendRefs:
102+
- name: app1
103+
port: 80
104+
- matches:
105+
- headers:
106+
- name: X-Model-Version
107+
value: v2
108+
backendRefs:
109+
- name: app2
110+
port: 80
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: Gateway
3+
metadata:
4+
name: gateway
5+
spec:
6+
gatewayClassName: istio
7+
listeners:
8+
- name: default
9+
hostname: "*.example.com"
10+
port: 80
11+
protocol: HTTP
12+
allowedRoutes:
13+
namespaces:
14+
from: All
15+
16+
---
17+
18+
apiVersion: v1
19+
kind: Service
20+
metadata:
21+
name: app1
22+
spec:
23+
selector:
24+
app: app1
25+
ports:
26+
- protocol: TCP
27+
port: 80
28+
targetPort: 8080
29+
30+
---
31+
32+
apiVersion: apps/v1
33+
kind: Deployment
34+
metadata:
35+
name: app1
36+
spec:
37+
replicas: 1
38+
selector:
39+
matchLabels:
40+
app: app1
41+
template:
42+
metadata:
43+
labels:
44+
app: app1
45+
spec:
46+
containers:
47+
- name: default
48+
image: registry.k8s.io/e2e-test-images/agnhost:2.39
49+
command: ["/agnhost", "netexec", "--http-port", "8080"]
50+
51+
---
52+
53+
apiVersion: v1
54+
kind: Service
55+
metadata:
56+
name: app2
57+
spec:
58+
selector:
59+
app: app2
60+
ports:
61+
- protocol: TCP
62+
port: 80
63+
targetPort: 8080
64+
65+
---
66+
67+
apiVersion: apps/v1
68+
kind: Deployment
69+
metadata:
70+
name: app2
71+
spec:
72+
replicas: 1
73+
selector:
74+
matchLabels:
75+
app: app2
76+
template:
77+
metadata:
78+
labels:
79+
app: app2
80+
spec:
81+
containers:
82+
- name: default
83+
image: registry.k8s.io/e2e-test-images/agnhost:2.39
84+
command: ["/agnhost", "netexec", "--http-port", "8080"]
85+
86+
---
87+
88+
apiVersion: gateway.networking.k8s.io/v1
89+
kind: HTTPRoute
90+
metadata:
91+
name: weighted-traffic-splitting
92+
spec:
93+
parentRefs:
94+
- name: gateway
95+
hostnames: ["weighted-traffic-splitting.example.com"]
96+
rules:
97+
- backendRefs:
98+
- name: app1
99+
port: 80
100+
weight: 80
101+
- name: app2
102+
port: 80
103+
weight: 20

tests/e2e/scenarios/ai-conformance/validators/output.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ func (h *ValidatorHarness) Run(name string, testFunc func(h *ValidatorHarness))
8585
})
8686
}
8787

88+
// AllPassed returns true if all sub-tests have passed so far. This can be used to conditionally record conformance only if all checks passed.
89+
func (h *ValidatorHarness) AllPassed() bool {
90+
return !h.t.Failed() && !h.t.Skipped()
91+
}
92+
8893
// Success is like Logf, but indicates a successful check.
8994
func (h *ValidatorHarness) Success(format string, args ...interface{}) {
9095
s := fmt.Sprintf(format, args...)

0 commit comments

Comments
 (0)