|
1 |
| -#!/bin/bash |
| 1 | +#!/usr/bin/env bash |
2 | 2 |
|
3 | 3 | # Copyright 2025 The Kubernetes Authors.
|
4 | 4 | #
|
|
14 | 14 | # See the License for the specific language governing permissions and
|
15 | 15 | # limitations under the License.
|
16 | 16 |
|
17 |
| -# This script verifies end-to-end connectivity for an example inference extension test environment based on |
18 |
| -# resources from the quickstart guide or e2e test framework. It can optionally launch a "curl" client pod to |
19 |
| -# run these tests within the cluster. |
20 |
| -# |
21 |
| -# USAGE: ./hack/e2e-test.sh |
22 |
| -# |
23 |
| -# OPTIONAL ENVIRONMENT VARIABLES: |
24 |
| -# - TIME: The duration (in seconds) for which the test will run. Defaults to 1 second. |
25 |
| -# - CURL_POD: If set to "true", the script will use a Kubernetes pod named "curl" for making requests. |
26 |
| -# - IP: Override the detected IP address. If not provided, the script attempts to use a Gateway based on |
27 |
| -# the quickstart guide or an Envoy service IP based on the e2e test framework. |
28 |
| -# - PORT: Override the detected port. If not provided, the script attempts to use a Gateway based on the |
29 |
| -# quickstart guide or an Envoy service IP based on the e2e test framework. |
30 |
| -# |
31 |
| -# WHAT THE SCRIPT DOES: |
32 |
| -# 1. Determines if there is a Gateway named "inference-gateway" in the "default" namespace. If found, it extracts the IP |
33 |
| -# address and port from the Gateway's "llm-gw" listener. Otherwise, it falls back to the Envoy service in the "default" namespace. |
34 |
| -# 2. Optionally checks for (or creates) a "curl" pod, ensuring it is ready to execute requests. |
35 |
| -# 3. Loops for $TIME seconds, sending requests every 5 seconds to the /v1/completions endpoint to confirm successful connectivity. |
36 |
| - |
37 |
| -set -euo pipefail |
38 |
| - |
39 |
| -# Determine the directory of this script and build an absolute path to client.yaml. |
40 |
| -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
41 |
| -CLIENT_YAML="$SCRIPT_DIR/../test/testdata/client.yaml" |
42 |
| - |
43 |
| -# TIME is the amount of time, in seconds, to run the test. |
44 |
| -TIME=${TIME:-1} |
45 |
| -# Optionally use a client curl pod for executing the curl command. |
46 |
| -CURL_POD=${CURL_POD:-false} |
47 |
| - |
48 |
| -check_resource_exists() { |
49 |
| - local type=$1 |
50 |
| - local name=$2 |
51 |
| - local namespace=$3 |
52 |
| - |
53 |
| - if kubectl get "$type" "$name" -n "$namespace" &>/dev/null; then |
54 |
| - return 0 |
55 |
| - else |
56 |
| - return 1 |
57 |
| - fi |
| 17 | +set -euox pipefail |
| 18 | + |
| 19 | +install_kind() { |
| 20 | + if ! command -v kind &>/dev/null; then |
| 21 | + echo "kind not found, installing..." |
| 22 | + [ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-amd64 |
| 23 | + # For ARM64 |
| 24 | + [ $(uname -m) = aarch64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-arm64 |
| 25 | + chmod +x ./kind |
| 26 | + mv ./kind /usr/local/bin/kind |
| 27 | + else |
| 28 | + echo "kind is already installed." |
| 29 | + fi |
58 | 30 | }
|
59 | 31 |
|
60 |
| -check_pod_ready() { |
61 |
| - local pod_name=$1 |
62 |
| - local namespace=$2 |
63 |
| - # Check the Ready condition using jsonpath. Default to False if not found. |
64 |
| - local ready_status |
65 |
| - ready_status=$(kubectl get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False") |
66 |
| - if [[ "$ready_status" == "True" ]]; then |
67 |
| - return 0 |
68 |
| - else |
69 |
| - return 1 |
70 |
| - fi |
71 |
| -} |
72 |
| - |
73 |
| -# Try to get the Gateway's IP and the port from the listener named "llm-gw" if it exists. |
74 |
| -if check_resource_exists "gateway" "inference-gateway" "default"; then |
75 |
| - GATEWAY_IP=$(kubectl get gateway inference-gateway -n default -o jsonpath='{.status.addresses[0].value}') |
76 |
| - # Use JSONPath to select the port from the listener with name "http" |
77 |
| - GATEWAY_PORT=$(kubectl get gateway inference-gateway -n default -o jsonpath='{.spec.listeners[?(@.name=="http")].port}') |
78 |
| -else |
79 |
| - GATEWAY_IP="" |
80 |
| - GATEWAY_PORT="" |
| 32 | +if [ "$USE_KIND" = "true" ]; then |
| 33 | + install_kind # make sure kind cli is installed |
| 34 | + if ! kubectl config current-context >/dev/null 2>&1; then # if no active kind cluster found |
| 35 | + echo "No active kubecontext found. creating a kind cluster for running the tests..." |
| 36 | + kind create cluster --name inference-e2e |
| 37 | + KIND_CLUSTER=inference-e2e IMAGE_TAG=${E2E_IMAGE} make image-kind |
| 38 | + else |
| 39 | + current_context=$(kubectl config current-context) |
| 40 | + current_kind_cluster="${current_context#kind-}" |
| 41 | + echo "Found an active kind cluster ${current_kind_cluster} for running the tests..." |
| 42 | + KIND_CLUSTER=${current_kind_cluster} IMAGE_TAG=${E2E_IMAGE} make image-kind |
| 43 | + fi |
| 44 | +else |
| 45 | + # don't use kind. it's the caller responsibility to load the image into the cluster, we just run the tests. |
| 46 | + # this section is useful when one wants to run an official release or latest main against a cluster other than kind. |
| 47 | + if ! kubectl config current-context >/dev/null 2>&1; then # if no active cluster found |
| 48 | + echo "No active kubecontext found. exiting..." |
| 49 | + exit |
| 50 | + fi |
81 | 51 | fi
|
82 | 52 |
|
83 |
| -if [[ -n "$GATEWAY_IP" && -n "$GATEWAY_PORT" ]]; then |
84 |
| - echo "Using Gateway inference-gateway IP and port from listener 'llm-gw'." |
85 |
| - IP=${IP:-$GATEWAY_IP} |
86 |
| - PORT=${PORT:-$GATEWAY_PORT} |
87 |
| -else |
88 |
| - echo "Gateway inference-gateway not found or missing IP/port. Falling back to Envoy service." |
89 |
| - # Ensure the Envoy service exists. |
90 |
| - if ! check_resource_exists "svc" "envoy" "default"; then |
91 |
| - echo "Error: Envoy service not found in namespace 'default'." |
92 |
| - exit 1 |
93 |
| - fi |
94 |
| - IP=${IP:-$(kubectl get svc envoy -n default -o jsonpath='{.spec.clusterIP}')} |
95 |
| - PORT=${PORT:-$(kubectl get svc envoy -n default -o jsonpath='{.spec.ports[0].port}')} |
96 |
| -fi |
97 |
| - |
98 |
| -# Optionally verify that the curl pod exists and is ready. |
99 |
| -if [[ "$CURL_POD" == "true" ]]; then |
100 |
| - if ! check_resource_exists "pod" "curl" "default"; then |
101 |
| - echo "Pod 'curl' not found in namespace 'default'. Applying client.yaml from $CLIENT_YAML..." |
102 |
| - kubectl apply -f "$CLIENT_YAML" |
103 |
| - fi |
104 |
| - echo "Waiting for pod 'curl' to be ready..." |
105 |
| - # Retry every 5 seconds for up to 30 seconds (6 attempts) |
106 |
| - for i in {1..6}; do |
107 |
| - if check_pod_ready "curl" "default"; then |
108 |
| - echo "Pod 'curl' is now ready." |
109 |
| - break |
110 |
| - fi |
111 |
| - echo "Retry attempt $i: Pod 'curl' not ready; waiting 5 seconds..." |
112 |
| - sleep 5 |
113 |
| - done |
114 |
| - |
115 |
| - if ! check_pod_ready "curl" "default"; then |
116 |
| - echo "Error: Pod 'curl' is still not ready in namespace 'default' after 30 seconds." |
117 |
| - exit 1 |
118 |
| - fi |
119 |
| -fi |
120 |
| - |
121 |
| -# Validate that we have a non-empty IP and PORT. |
122 |
| -if [[ -z "$IP" ]]; then |
123 |
| - echo "Error: Unable to determine a valid IP from either Gateway or Envoy service." |
124 |
| - exit 1 |
125 |
| -fi |
126 |
| - |
127 |
| -if [[ -z "$PORT" ]]; then |
128 |
| - echo "Error: Unable to determine a valid port from either Gateway or Envoy service." |
129 |
| - exit 1 |
130 |
| -fi |
131 |
| - |
132 |
| -echo "Using IP: $IP" |
133 |
| -echo "Using PORT: $PORT" |
134 |
| - |
135 |
| -# Run the test for the specified duration. |
136 |
| -end=$((SECONDS + TIME)) |
137 |
| -if [[ "$CURL_POD" == "true" ]]; then |
138 |
| - while [ $SECONDS -lt $end ]; do |
139 |
| - kubectl exec po/curl -- curl -i "$IP:$PORT/v1/completions" \ |
140 |
| - -H 'Content-Type: application/json' \ |
141 |
| - -d '{"model": "food-review","prompt": "Write as if you were a critic: San Francisco","max_tokens": 100,"temperature": 0}' |
142 |
| - sleep 5 |
143 |
| - done |
144 |
| -else |
145 |
| - while [ $SECONDS -lt $end ]; do |
146 |
| - curl -i "$IP:$PORT/v1/completions" \ |
147 |
| - -H 'Content-Type: application/json' \ |
148 |
| - -d '{"model": "food-review","prompt": "Write as if you were a critic: San Francisco","max_tokens": 100,"temperature": 0}' |
149 |
| - sleep 5 |
150 |
| - done |
151 |
| -fi |
| 53 | +echo "Found an active cluster. Running Go e2e tests in ./epp..." |
| 54 | +go test ./test/e2e/epp/ -v -ginkgo.v |
0 commit comments