From f42cf119bdcb283cab1ce3645ed2dc2f1730f187 Mon Sep 17 00:00:00 2001 From: Rahul Gurnani Date: Tue, 23 Sep 2025 16:31:06 +0000 Subject: [PATCH 1/2] Update e2e tests to use helm charts --- go.mod | 19 +++++++++++++--- go.sum | 41 ++++++++++++++++++++++++++++++++++ test/e2e/epp/e2e_suite_test.go | 35 +++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 20622250b..6bbe63f07 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,9 @@ require ( github.com/prometheus/common v0.66.1 github.com/prometheus/prometheus v0.306.0 github.com/stretchr/testify v1.11.1 + github.com/prometheus/common v0.65.0 + github.com/prometheus/prometheus v0.305.0 + github.com/stretchr/testify v1.11.1 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/sync v0.17.0 @@ -38,15 +41,19 @@ require ( require ( cel.dev/expr v0.24.0 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect @@ -62,6 +69,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobuffalo/flect v1.0.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-yaml v1.18.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect @@ -71,7 +79,7 @@ require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect - github.com/huandu/xstrings v1.3.3 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -89,8 +97,11 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -125,6 +136,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.34.1 // indirect + helm.sh/helm/v3 v3.19.0 // indirect + k8s.io/apiserver v0.34.0 // indirect k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect diff --git a/go.sum b/go.sum index 6586a872b..837c94677 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIi cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= @@ -14,6 +16,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8U github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -22,6 +26,8 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1 github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= @@ -67,6 +73,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -108,6 +116,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -149,6 +159,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -226,11 +238,21 @@ github.com/prometheus/sigv4 v0.2.0/go.mod h1:D04rqmAaPPEUkjRQxGqjoxdyJuyCh6E0M18 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -244,6 +266,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= +github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -375,6 +400,22 @@ k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc= k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k= +helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc= +k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= +k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= +k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc= +k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0= +k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= +k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.0 h1:Z51fw1iGMqN7uJ1kEaynf2Aec1Y774PqU+FVWCFV3Jg= +k8s.io/apiserver v0.34.0/go.mod h1:52ti5YhxAvewmmpVRqlASvaqxt0gKJxvCeW7ZrwgazQ= +k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo= +k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY= +k8s.io/code-generator v0.34.0 h1:Ze2i1QsvUprIlX3oHiGv09BFQRLCz+StA8qKwwFzees= +k8s.io/code-generator v0.34.0/go.mod h1:Py2+4w2HXItL8CGhks8uI/wS3Y93wPKO/9mBQUYNua0= +k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8= +k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= diff --git a/test/e2e/epp/e2e_suite_test.go b/test/e2e/epp/e2e_suite_test.go index b41f108f8..0533c8e4d 100644 --- a/test/e2e/epp/e2e_suite_test.go +++ b/test/e2e/epp/e2e_suite_test.go @@ -37,6 +37,10 @@ import ( infextv1a2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2" "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/env" testutils "sigs.k8s.io/gateway-api-inference-extension/test/utils" + + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/engine" ) const ( @@ -98,6 +102,34 @@ func TestAPIs(t *testing.T) { ) } +func renderCharts(nsName string) []string { + chartPath := "./charts/inferencepool" // Path to your Helm chart + chart, err := loader.Load(chartPath) + if err != nil { + panic(fmt.Sprintf("Failed to load chart: %v", err)) + } + values, _ := chartutil.ReadValuesFile("charts/inferencepool/values.yaml") + options := chartutil.ReleaseOptions{ + Name: "vllm-llama3-8b-instruct", + Namespace: nsName, + } + renderValues, err := chartutil.ToRenderValues(chart, values, options, nil) + if err != nil { + panic(fmt.Sprintf("Failed to create render values: %v", err)) + } + fmt.Println(values) + rendered, err := engine.Render(chart, renderValues) + if err != nil { + panic(fmt.Sprintf("Failed to render chart: %v", err)) + } + fmt.Println(rendered) + var renderedValues []string + for _, v := range rendered { + renderedValues = append(renderedValues, v) + } + return renderedValues +} + var _ = ginkgo.BeforeSuite(func() { nsName := os.Getenv("E2E_NS") if nsName == "" { @@ -181,6 +213,7 @@ func setupSuite() { gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred()) err = infextv1a2.Install(testConfig.Scheme) + // err = infextv1a2.Install(scheme) gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred()) err = infextv1.Install(testConfig.Scheme) @@ -327,6 +360,8 @@ func createInferExt(testConfig *testutils.TestConfig, filePath string) { for _, manifest := range inManifests { outManifests = append(outManifests, replacer.Replace(manifest)) } +func createInferExt(k8sClient client.Client, filePath string) { + outManifests := renderCharts(nsName) ginkgo.By("Creating inference extension resources from manifest: " + filePath) testutils.CreateObjsFromYaml(testConfig, outManifests) From b7d01492ec73fe0fbb3b0a93e4400d3857adfb46 Mon Sep 17 00:00:00 2001 From: Rahul Gurnani Date: Fri, 3 Oct 2025 23:37:27 +0000 Subject: [PATCH 2/2] Temp --- Makefile | 2 +- .../templates/epp-deployment.yaml | 2 +- .../templates/leader-election-rbac.yaml | 2 +- config/charts/inferencepool/values.yaml | 9 +- test/e2e/epp/e2e_suite_test.go | 118 ++++++++++++++++-- test/e2e/epp/e2e_test.go | 34 ++++- test/utils/utils.go | 2 + 7 files changed, 150 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index c483be79b..f935a41d6 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ E2E_IMAGE ?= $(IMAGE_TAG) # E2E_USE_KIND is a flag used in test-e2e target. when set to true it will load the e2e image into the kind cluster. # it is possible though to run e2e tests against clusters other than kind. in such a case, it is the user's responsibility to load # the image into the cluster. -E2E_USE_KIND ?= true +E2E_USE_KIND ?= false SYNCER_IMAGE_NAME := lora-syncer SYNCER_IMAGE_REPO ?= $(IMAGE_REGISTRY)/$(SYNCER_IMAGE_NAME) diff --git a/config/charts/inferencepool/templates/epp-deployment.yaml b/config/charts/inferencepool/templates/epp-deployment.yaml index f012c2e47..ef4198fef 100644 --- a/config/charts/inferencepool/templates/epp-deployment.yaml +++ b/config/charts/inferencepool/templates/epp-deployment.yaml @@ -118,4 +118,4 @@ spec: {{- if .Values.inferenceExtension.tolerations }} tolerations: {{- toYaml .Values.inferenceExtension.tolerations | nindent 8 }} - {{- end }} + {{- end }} \ No newline at end of file diff --git a/config/charts/inferencepool/templates/leader-election-rbac.yaml b/config/charts/inferencepool/templates/leader-election-rbac.yaml index 11b3dd516..499511f30 100644 --- a/config/charts/inferencepool/templates/leader-election-rbac.yaml +++ b/config/charts/inferencepool/templates/leader-election-rbac.yaml @@ -27,4 +27,4 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: {{ include "gateway-api-inference-extension.name" . }}-leader-election -{{- end }} +{{- end }} \ No newline at end of file diff --git a/config/charts/inferencepool/values.yaml b/config/charts/inferencepool/values.yaml index 91d6a48e6..26c956336 100644 --- a/config/charts/inferencepool/values.yaml +++ b/config/charts/inferencepool/values.yaml @@ -35,7 +35,8 @@ inferenceExtension: # Log verbosity - name: v value: 1 - + - name: ha-enable-leader-election + value: "true" affinity: {} tolerations: [] @@ -59,9 +60,9 @@ inferencePool: - number: 8000 modelServerType: vllm # vllm, triton-tensorrt-llm apiVersion: inference.networking.k8s.io/v1 - # modelServers: # REQUIRED - # matchLabels: - # app: vllm-llama3-8b-instruct + modelServers: # REQUIRED + matchLabels: + app: vllm-llama3-8b-instruct # Should only used if apiVersion is inference.networking.x-k8s.io/v1alpha2, # This will soon be deprecated when upstream GW providers support v1, just doing something simple for now. diff --git a/test/e2e/epp/e2e_suite_test.go b/test/e2e/epp/e2e_suite_test.go index 0533c8e4d..bd4d6fe71 100644 --- a/test/e2e/epp/e2e_suite_test.go +++ b/test/e2e/epp/e2e_suite_test.go @@ -19,6 +19,7 @@ package epp import ( "errors" "fmt" + "log" "os" "strings" "testing" @@ -36,6 +37,7 @@ import ( infextv1 "sigs.k8s.io/gateway-api-inference-extension/api/v1" infextv1a2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2" "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/env" + infextv1a2 "sigs.k8s.io/gateway-api-inference-extension/apix/v1alpha2" testutils "sigs.k8s.io/gateway-api-inference-extension/test/utils" "helm.sh/helm/v3/pkg/chart/loader" @@ -84,6 +86,10 @@ const ( metricsRbacManifest = "../../testdata/metrics-rbac.yaml" // modelServerManifestFilepathEnvVar is the env var that holds absolute path to the manifest for the model server test resource. modelServerManifestFilepathEnvVar = "MANIFEST_PATH" + // replicaCount is the number of replicas of EPP. + replicaCount = 3 + + name = "vllm-llama3-8b-instruct" ) const e2eLeaderElectionEnabledEnvVar = "E2E_LEADER_ELECTION_ENABLED" @@ -102,15 +108,29 @@ func TestAPIs(t *testing.T) { ) } -func renderCharts(nsName string) []string { - chartPath := "./charts/inferencepool" // Path to your Helm chart +func renderChartsToYamls(nsName string) []string { + chartPath := "/usr/local/google/home/rahulgurnani/gateway-api-inference-extension/config/charts/inferencepool" chart, err := loader.Load(chartPath) if err != nil { panic(fmt.Sprintf("Failed to load chart: %v", err)) } - values, _ := chartutil.ReadValuesFile("charts/inferencepool/values.yaml") + values, _ := chartutil.ReadValuesFile("/usr/local/google/home/rahulgurnani/gateway-api-inference-extension/config/charts/inferencepool/values.yaml") + infExt, ok := values["inferenceExtension"].(map[string]interface{}) + if ok { + infExt["replicas"] = replicaCount + fmt.Println(infExt) + flags, ok := infExt["flags"].([]interface{}) + if ok { + flags = append(flags, map[string]string{ + "name": "ha-enable-leader-election", + "value": "true", + }) + infExt["flags"] = flags + } + } + options := chartutil.ReleaseOptions{ - Name: "vllm-llama3-8b-instruct", + Name: name, Namespace: nsName, } renderValues, err := chartutil.ToRenderValues(chart, values, options, nil) @@ -122,11 +142,22 @@ func renderCharts(nsName string) []string { if err != nil { panic(fmt.Sprintf("Failed to render chart: %v", err)) } - fmt.Println(rendered) var renderedValues []string - for _, v := range rendered { - renderedValues = append(renderedValues, v) + for fName, renderedChart := range rendered { + if strings.Contains(fName, "NOTES.txt") { + continue + } + + fmt.Println("----------------rendered----------------") + fmt.Println(fName) + objs := strings.Split(renderedChart, "\n---") + for _, obj := range objs { + fmt.Println("-----------obj-----------") + fmt.Println(obj) + renderedValues = append(renderedValues, obj) + } } + return renderedValues } @@ -144,6 +175,7 @@ var _ = ginkgo.BeforeSuite(func() { leaderElectionEnabled = true ginkgo.By("Leader election test mode enabled via " + e2eLeaderElectionEnabledEnvVar) } + leaderElectionEnabled = true ginkgo.By("Setting up the test suite") setupSuite() @@ -165,8 +197,8 @@ func setupInfra() { } crds := map[string]string{ "inferencepools.inference.networking.x-k8s.io": xInferPoolManifest, - "inferenceobjectives.inference.networking.x-k8s.io": xInferObjectiveManifest, "inferencepools.inference.networking.k8s.io": inferPoolManifest, + "inferenceobjectives.inference.networking.x-k8s.io": xInferObjectiveManifest, } createCRDs(testConfig, crds) @@ -179,6 +211,10 @@ func setupInfra() { createClient(testConfig, clientManifest) createEnvoy(testConfig, envoyManifest) createMetricsRbac(testConfig, metricsRbacManifest) + createInferExt(cli) + createClient(cli, clientManifest) + createEnvoy(cli, envoyManifest) + createMetricsRbac(cli, metricsRbacManifest) // Run this step last, as it requires additional time for the model server to become ready. ginkgo.By("Creating model server resources from manifest: " + modelServerManifestPath) createModelServer(testConfig, modelServerManifestArray) @@ -214,6 +250,8 @@ func setupSuite() { err = infextv1a2.Install(testConfig.Scheme) // err = infextv1a2.Install(scheme) + // TODO: Fix the v1a2 chart + err = infextv1a2.Install(scheme) gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred()) err = infextv1.Install(testConfig.Scheme) @@ -362,9 +400,13 @@ func createInferExt(testConfig *testutils.TestConfig, filePath string) { } func createInferExt(k8sClient client.Client, filePath string) { outManifests := renderCharts(nsName) +func createInferExt(k8sClient client.Client) { + outManifests := renderChartsToYamls(nsName) ginkgo.By("Creating inference extension resources from manifest: " + filePath) testutils.CreateObjsFromYaml(testConfig, outManifests) + ginkgo.By("Creating inference extension resources from outManifests") + createObjsFromYaml(k8sClient, outManifests) // Wait for the deployment to exist. deploy := &appsv1.Deployment{ @@ -378,5 +420,65 @@ func createInferExt(k8sClient client.Client, filePath string) { testutils.DeploymentReadyReplicas(testConfig, deploy, 1) } else { testutils.DeploymentAvailable(testConfig, deploy) + testutils.DeploymentAvailable(ctx, k8sClient, deploy, modelReadyTimeout, interval) + } + + // Wait for the service to exist. + testutils.EventuallyExists(ctx, func() error { + return k8sClient.Get(ctx, types.NamespacedName{Namespace: nsName, Name: inferExtName}, &corev1.Service{}) + }, existsTimeout, interval) +} + +// applyYAMLFile reads a file containing YAML (possibly multiple docs) +// and applies each object to the cluster. +func applyYAMLFile(k8sClient client.Client, filePath string) { + // Create the resources from the manifest file + createObjsFromYaml(k8sClient, readYaml(filePath)) +} + +func readYaml(filePath string) []string { + ginkgo.By("Reading YAML file: " + filePath) + yamlBytes, err := os.ReadFile(filePath) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + // Split multiple docs, if needed + return strings.Split(string(yamlBytes), "\n---") +} + +func createObjsFromYaml(k8sClient client.Client, docs []string) { + // For each doc, decode and create + decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer() + for _, doc := range docs { + trimmed := strings.TrimSpace(doc) + if trimmed == "" { + continue + } + + // Decode into a runtime.Object + obj, gvk, decodeErr := decoder.Decode([]byte(trimmed), nil, nil) + if decodeErr != nil { + log.Printf("Trimmed: %s", trimmed) + continue + } + gomega.Expect(decodeErr).NotTo(gomega.HaveOccurred(), + "Failed to decode YAML document to a Kubernetes object") + + ginkgo.By(fmt.Sprintf("Decoded GVK: %s", gvk)) + + unstrObj, ok := obj.(*unstructured.Unstructured) + if !ok { + // Fallback if it's a typed object + unstrObj = &unstructured.Unstructured{} + // Convert typed to unstructured + err := scheme.Convert(obj, unstrObj, nil) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + } + + unstrObj.SetNamespace(nsName) + + // Create the object + err := k8sClient.Create(ctx, unstrObj) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), + "Failed to create object from YAML") } } diff --git a/test/e2e/epp/e2e_test.go b/test/e2e/epp/e2e_test.go index c3ff49e58..40b6b2335 100644 --- a/test/e2e/epp/e2e_test.go +++ b/test/e2e/epp/e2e_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "strconv" "strings" "time" @@ -85,16 +86,18 @@ var _ = ginkgo.Describe("InferencePool", func() { if !leaderElectionEnabled { ginkgo.Skip("Leader election is not enabled for this test run, skipping.") } - + fmt.Println("Leader election enabled") ginkgo.By("Verifying that exactly one EPP pod is ready") gomega.Eventually(func(g gomega.Gomega) { podList := &corev1.PodList{} + err := cli.List(ctx, podList, client.InNamespace(nsName), client.MatchingLabels{"inferencepool": inferExtName}) + fmt.Println("listed nsName") + fmt.Printf("err %v", err) err := testConfig.K8sClient.List(testConfig.Context, podList, client.InNamespace(testConfig.NsName), client.MatchingLabels{"app": inferExtName}) g.Expect(err).NotTo(gomega.HaveOccurred()) - // The deployment should have 3 replicas for leader election. g.Expect(podList.Items).To(gomega.HaveLen(3)) - + fmt.Println(podList.Items[0]) readyPods := 0 for _, pod := range podList.Items { for _, cond := range pod.Status.Conditions { @@ -103,6 +106,9 @@ var _ = ginkgo.Describe("InferencePool", func() { } } } + fmt.Printf("readpods %d", readyPods) + g.Expect(readyPods).To(gomega.Equal(1), "Expected exactly one pod to be ready") // why do we expect only one pod to be ready??? + }, readyTimeout, interval).Should(gomega.Succeed()) g.Expect(readyPods).To(gomega.Equal(1), "Expected exactly one pod to be ready") }, testConfig.ReadyTimeout, testConfig.Interval).Should(gomega.Succeed()) }) @@ -139,6 +145,8 @@ var _ = ginkgo.Describe("InferencePool", func() { d := &appsv1.Deployment{} err := testConfig.K8sClient.Get(testConfig.Context, types.NamespacedName{Namespace: testConfig.NsName, Name: inferExtName}, d) g.Expect(err).NotTo(gomega.HaveOccurred()) + g.Expect(d.Status.Replicas).To(gomega.Equal(int32(replicaCount)), "Deployment should have 3 replicas") + }, readyTimeout, interval).Should(gomega.Succeed()) g.Expect(d.Status.Replicas).To(gomega.Equal(int32(3)), "Deployment should have 3 replicas") }, testConfig.ReadyTimeout, testConfig.Interval).Should(gomega.Succeed()) @@ -260,6 +268,8 @@ func verifyMetrics() { // Generate traffic by sending requests through the inference extension ginkgo.By("Generating traffic through the inference extension") + curlCmd := getCurlCommand(envoyName, nsName, envoyPort, modelName, curlTimeout, "/completions", "Write as if you were a critic: San Francisco", true) + log.Println("Running curl command in the pod") curlCmd := getCurlCommand(envoyName, testConfig.NsName, envoyPort, modelName, curlTimeout, "/completions", "Write as if you were a critic: San Francisco", true) // Run the curl command multiple times to generate some metrics data @@ -270,6 +280,7 @@ func verifyMetrics() { // modify the curl command to generate some error metrics curlCmd[len(curlCmd)-1] = "invalid input" + log.Println("Running curl with error command in the pod") for i := 0; i < 5; i++ { _, err := testutils.ExecCommandInPod(testConfig, "curl", "curl", curlCmd) gomega.Expect(err).NotTo(gomega.HaveOccurred()) @@ -278,6 +289,7 @@ func verifyMetrics() { // Now scrape metrics from the EPP endpoint via the curl pod ginkgo.By("Scraping metrics from the EPP endpoint") podIP := findReadyPod().Status.PodIP + log.Println("Found ready pod") // Get the authorization token for reading metrics token := "" @@ -286,6 +298,8 @@ func verifyMetrics() { g.Expect(err).NotTo(gomega.HaveOccurred()) g.Expect(t).NotTo(gomega.BeEmpty()) token = t + log.Println("Got the token") + }, existsTimeout, interval).Should(gomega.Succeed()) }, testConfig.ExistsTimeout, testConfig.Interval).Should(gomega.Succeed()) // Construct the metric scraping curl command using Pod IP @@ -294,6 +308,12 @@ func verifyMetrics() { ginkgo.By("Verifying that all expected metrics are present.") gomega.Eventually(func() error { // Execute the metrics scrape command inside the curl pod + log.Println("Execute the metrics scrap command") + resp, err := testutils.ExecCommandInPod(ctx, cfg, scheme, kubeCli, nsName, "curl", "curl", metricScrapeCmd) + log.Println("Response of exec:") + log.Println(resp) + log.Println("Error in exec:") + log.Println(err) resp, err := testutils.ExecCommandInPod(testConfig, "curl", "curl", metricScrapeCmd) if err != nil { return err @@ -327,13 +347,19 @@ func findReadyPod() *corev1.Pod { var readyPod *corev1.Pod gomega.Eventually(func(g gomega.Gomega) { podList := &corev1.PodList{} + log.Printf("Namesapce %s", nsName) + log.Printf("inferExtName %s", inferExtName) + err := cli.List(ctx, podList, client.InNamespace(nsName), client.MatchingLabels{"inferencepool": inferExtName}) err := testConfig.K8sClient.List(testConfig.Context, podList, client.InNamespace(testConfig.NsName), client.MatchingLabels{"app": inferExtName}) g.Expect(err).NotTo(gomega.HaveOccurred()) - + log.Println("listed pods") + log.Println(podList) foundReadyPod := false for i := range podList.Items { pod := &podList.Items[i] for _, cond := range pod.Status.Conditions { + log.Println("Condition:") + log.Println(cond) if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue { g.Expect(pod.Status.PodIP).NotTo(gomega.BeEmpty(), "Ready pod must have an IP") readyPod = pod diff --git a/test/utils/utils.go b/test/utils/utils.go index b65834cb6..6123c3c31 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -275,6 +275,7 @@ func DeploymentReadyReplicas(testConfig *TestConfig, deploy *appsv1.Deployment, var fetchedDeploy appsv1.Deployment err := testConfig.K8sClient.Get(testConfig.Context, types.NamespacedName{Namespace: deploy.Namespace, Name: deploy.Name}, &fetchedDeploy) g.Expect(err).NotTo(gomega.HaveOccurred()) + fmt.Sprintf("%v", fetchedDeploy) g.Expect(fetchedDeploy.Status.ReadyReplicas).To(gomega.BeNumerically(">=", count), fmt.Sprintf("Deployment only has %d ready replicas, want at least %d", fetchedDeploy.Status.ReadyReplicas, count)) }, testConfig.ModelReadyTimeout, testConfig.Interval).Should(gomega.Succeed()) @@ -289,6 +290,7 @@ func checkDeploymentStatus(ctx context.Context, cli client.Client, deploy *appsv found := 0 for _, want := range conditions { for _, c := range fetchedDeploy.Status.Conditions { + fmt.Println(c) if c.Type == want.Type && c.Status == want.Status { found += 1 }