diff --git a/.github/workflows/build-push-release.yml b/.github/workflows/build-push-release.yml index f661522a..7cc64dd6 100644 --- a/.github/workflows/build-push-release.yml +++ b/.github/workflows/build-push-release.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.24.4" + go-version: "1.26" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 diff --git a/.github/workflows/codegen-check.yml b/.github/workflows/codegen-check.yml index 5bf7d228..2aec2ebe 100644 --- a/.github/workflows/codegen-check.yml +++ b/.github/workflows/codegen-check.yml @@ -29,7 +29,7 @@ jobs: if: steps.changes.outputs.codegen == 'true' uses: actions/setup-go@v5 with: - go-version: "1.24.4" + go-version: "1.26" - name: Verify Generated Code if: steps.changes.outputs.codegen == 'true' diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 43c602db..e59967ad 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.26" - name: Create k8s Kind Cluster uses: helm/kind-action@v1 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6e1e643b..6b374ead 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,7 +35,7 @@ jobs: if: steps.changes.outputs.lint == 'true' uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version: "1.26" - name: Run golangci-lint if: steps.changes.outputs.lint == 'true' diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml index 8b2d06c7..3c8f29b1 100644 --- a/.github/workflows/test-coverage.yml +++ b/.github/workflows/test-coverage.yml @@ -42,7 +42,7 @@ jobs: if: steps.changes.outputs.coverage == 'true' uses: actions/setup-go@v5 with: - go-version: "1.24" + go-version: "1.26" - name: Run tests with coverage if: steps.changes.outputs.coverage == 'true' diff --git a/docker/Dockerfile b/docker/Dockerfile index 6656a0cf..20da350c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # Multi-stage build for workloadmanager -FROM golang:1.24.9-alpine AS builder +FROM golang:1.26.1-alpine AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux diff --git a/docker/Dockerfile.picod b/docker/Dockerfile.picod index 918334df..8c076427 100644 --- a/docker/Dockerfile.picod +++ b/docker/Dockerfile.picod @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.24.4 AS builder +FROM golang:1.26.1 AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux diff --git a/docker/Dockerfile.router b/docker/Dockerfile.router index 8e2f22ca..fafd3012 100644 --- a/docker/Dockerfile.router +++ b/docker/Dockerfile.router @@ -1,5 +1,5 @@ # Multi-stage build for agentcube-router -FROM golang:1.24.9-alpine AS builder +FROM golang:1.26.1-alpine AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux diff --git a/go.mod b/go.mod index 3cc399a1..40e1a662 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/volcano-sh/agentcube -go 1.24.4 - -toolchain go1.24.9 +go 1.26.1 require ( github.com/agiledragon/gomonkey/v2 v2.13.0 @@ -13,21 +11,20 @@ require ( github.com/redis/go-redis/v9 v9.17.1 github.com/stretchr/testify v1.11.1 github.com/valkey-io/valkey-go v1.0.69 - golang.org/x/net v0.47.0 - k8s.io/api v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 + golang.org/x/net v0.48.0 + k8s.io/api v0.35.0 + k8s.io/apimachinery v0.35.0 + k8s.io/client-go v0.35.0 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 - sigs.k8s.io/agent-sandbox v0.1.1 - sigs.k8s.io/controller-runtime v0.22.2 + sigs.k8s.io/agent-sandbox v0.3.10 + sigs.k8s.io/controller-runtime v0.23.3 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect @@ -40,7 +37,6 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.2 // indirect @@ -60,11 +56,9 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -83,38 +77,27 @@ require ( github.com/ugorji/go/codec v1.2.12 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect - go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.44.0 // indirect - golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.13.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.34.1 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 01417a0a..928a39bf 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/agiledragon/gomonkey/v2 v2.13.0 h1:B24Jg6wBI1iB8EFR1c+/aoTg7QN/Cum7YffG8KMIyYo= github.com/agiledragon/gomonkey/v2 v2.13.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= @@ -12,8 +14,6 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= @@ -41,11 +41,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= @@ -90,12 +87,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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= @@ -105,18 +98,14 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -141,10 +130,10 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= -github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -189,28 +178,8 @@ github.com/valkey-io/valkey-go v1.0.69 h1:1wxexW0IhBFkRsbjz5Zfbd7EYDv18FP9ugHIak github.com/valkey-io/valkey-go v1.0.69/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY= 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= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -225,62 +194,34 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -293,14 +234,14 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= @@ -309,15 +250,15 @@ k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzk k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/agent-sandbox v0.1.1 h1:UBpfvE/bLRkWnGUsnJ2wIDazcOvs/guBHTyAhlD6BQI= -sigs.k8s.io/agent-sandbox v0.1.1/go.mod h1:xwp+pIX5rG4Bf3ScOwxnj8N8PbIhKpUqhAlrAjIFHVo= -sigs.k8s.io/controller-runtime v0.22.2 h1:cK2l8BGWsSWkXz09tcS4rJh95iOLney5eawcK5A33r4= -sigs.k8s.io/controller-runtime v0.22.2/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/agent-sandbox v0.3.10 h1:a9lWMeOTx+iJ8CeHeU6N474rYHri4HPX/wUb0Hy7PLs= +sigs.k8s.io/agent-sandbox v0.3.10/go.mod h1:Jrc7kae72PhTqo/9e3KdmM5rkg3Du2O0Q+3cGDe6NEw= +sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= +sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/pkg/workloadmanager/handlers.go b/pkg/workloadmanager/handlers.go index f767dfa1..93b25da9 100644 --- a/pkg/workloadmanager/handlers.go +++ b/pkg/workloadmanager/handlers.go @@ -28,7 +28,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/klog/v2" sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" - "sigs.k8s.io/agent-sandbox/controllers" extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" "github.com/volcano-sh/agentcube/pkg/api" @@ -132,11 +131,8 @@ func (s *Server) handleSandboxCreate(c *gin.Context, kind string) { dynamicClient = userDynamicClient } - // CRITICAL: Register watcher BEFORE creating sandbox - // This ensures we don't miss the Running state notification - resultChan := s.sandboxController.WatchSandboxOnce(c.Request.Context(), namespace, sandboxName) - // Ensure cleanup is called when function returns to prevent memory leak - defer s.sandboxController.UnWatchSandbox(namespace, sandboxName) + resultChan, unwatch := s.watchSandboxReady(c.Request.Context(), namespace, sandboxName, sandboxClaim == nil) + defer unwatch() response, err := s.createSandbox(c.Request.Context(), dynamicClient, sandbox, sandboxClaim, sandboxEntry, resultChan) if err != nil { @@ -172,6 +168,19 @@ func (s *Server) handleSandboxCreate(c *gin.Context, kind string) { respondJSON(c, http.StatusOK, response) } +func (s *Server) watchSandboxReady(ctx context.Context, namespace, sandboxName string, enabled bool) (<-chan SandboxStatusUpdate, func()) { + if !enabled { + return nil, func() {} + } + + // CRITICAL: Register watcher BEFORE creating sandbox. + // This ensures we don't miss the Running state notification. + resultChan := s.sandboxController.WatchSandboxOnce(ctx, namespace, sandboxName) + return resultChan, func() { + s.sandboxController.UnWatchSandbox(namespace, sandboxName) + } +} + // createK8sResources creates the K8s sandbox or sandbox claim resource. func (s *Server) createK8sResources(ctx context.Context, dynamicClient dynamic.Interface, sandbox *sandboxv1alpha1.Sandbox, sandboxClaim *extensionsv1alpha1.SandboxClaim) error { if sandboxClaim != nil { @@ -192,6 +201,35 @@ func (s *Server) createK8sResources(ctx context.Context, dynamicClient dynamic.I return nil } +func waitForCreatedSandbox(ctx context.Context, dynamicClient dynamic.Interface, sandbox *sandboxv1alpha1.Sandbox, sandboxClaim *extensionsv1alpha1.SandboxClaim, resultChan <-chan SandboxStatusUpdate, timer *time.Timer) (*sandboxv1alpha1.Sandbox, error) { + if sandboxClaim != nil { + createdSandbox, err := waitForSandboxClaimReady(ctx, dynamicClient, sandboxClaim.Namespace, sandboxClaim.Name, timer) + if err != nil { + if isContextError(err) || errors.Is(err, errSandboxCreationTimeout) { + return nil, err + } + return nil, api.NewInternalError(fmt.Errorf("failed to resolve sandbox claim %s/%s: %w", sandboxClaim.Namespace, sandboxClaim.Name, err)) + } + if createdSandbox == nil { + return nil, api.NewInternalError(fmt.Errorf("sandbox claim %s/%s resolved without a sandbox", sandboxClaim.Namespace, sandboxClaim.Name)) + } + return createdSandbox, nil + } + + select { + case result := <-resultChan: + timer.Stop() + return result.Sandbox, nil + case <-ctx.Done(): + timer.Stop() + klog.Warningf("sandbox %s/%s wait canceled: %v", sandbox.Namespace, sandbox.Name, ctx.Err()) + return nil, ctx.Err() + case <-timer.C: + klog.Warningf("sandbox %s/%s create timed out", sandbox.Namespace, sandbox.Name) + return nil, errSandboxCreationTimeout + } +} + // createSandbox performs sandbox creation and returns the response payload or an error with an HTTP status code. func (s *Server) createSandbox(ctx context.Context, dynamicClient dynamic.Interface, sandbox *sandboxv1alpha1.Sandbox, sandboxClaim *extensionsv1alpha1.SandboxClaim, sandboxEntry *sandboxEntry, resultChan <-chan SandboxStatusUpdate) (*types.CreateSandboxResponse, error) { placeholder := buildSandboxPlaceHolder(sandbox, sandboxEntry) @@ -220,50 +258,45 @@ func (s *Server) createSandbox(ctx context.Context, dynamicClient dynamic.Interf // preventing the runtime from retaining the timer until it fires. timer := time.NewTimer(2 * time.Minute) // consistent with router settings - var createdSandbox *sandboxv1alpha1.Sandbox - select { - case result := <-resultChan: - timer.Stop() - createdSandbox = result.Sandbox - klog.V(2).Infof("sandbox %s/%s reported ready, verifying entrypoints", createdSandbox.Namespace, createdSandbox.Name) - case <-ctx.Done(): - timer.Stop() - klog.Warningf("sandbox %s/%s wait canceled: %v", sandbox.Namespace, sandbox.Name, ctx.Err()) - return nil, ctx.Err() - case <-timer.C: - klog.Warningf("sandbox %s/%s create timed out", sandbox.Namespace, sandbox.Name) - return nil, errSandboxCreationTimeout + createdSandbox, err := waitForCreatedSandbox(ctx, dynamicClient, sandbox, sandboxClaim, resultChan, timer) + if err != nil { + return nil, err } - // agent-sandbox create pod with same name as sandbox if no warmpool is used - // so here we try to get pod IP by sandbox name first - // if warmpool is used, the pod name is stored in sandbox's annotation `agents.x-k8s.io/sandbox-pod-name` - // https://github.com/kubernetes-sigs/agent-sandbox/blob/3ab7fbcd85ad0d75c6e632ecd14bcaeda5e76e1e/controllers/sandbox_controller.go#L465 - sandboxPodName := sandbox.Name - if podName, exists := createdSandbox.Annotations[controllers.SandboxPodNameAnnotation]; exists { - sandboxPodName = podName + klog.V(2).Infof("sandbox %s/%s reported ready, verifying entrypoints", createdSandbox.Namespace, createdSandbox.Name) + + sandboxPodName := createdSandbox.Annotations[sandboxv1alpha1.SandboxPodNameAnnotation] + if sandboxPodName == "" { + return nil, api.NewInternalError(fmt.Errorf("sandbox %s/%s missing pod name annotation %s", createdSandbox.Namespace, createdSandbox.Name, sandboxv1alpha1.SandboxPodNameAnnotation)) } - podIP, err := s.k8sClient.GetSandboxPodIP(ctx, sandbox.Namespace, sandbox.Name, sandboxPodName) + podIP, err := s.k8sClient.GetSandboxPodIP(ctx, createdSandbox.Namespace, createdSandbox.Name, sandboxPodName) if err != nil { if isContextError(err) { return nil, err } - return nil, api.NewInternalError(fmt.Errorf("failed to get sandbox %s/%s pod IP: %w", sandbox.Namespace, sandbox.Name, err)) + return nil, api.NewInternalError(fmt.Errorf("failed to get sandbox %s/%s pod IP: %w", createdSandbox.Namespace, createdSandbox.Name, err)) } if err := s.waitForSandboxEntryPointsReady(ctx, podIP, sandboxEntry); err != nil { if isContextError(err) { return nil, err } - return nil, api.NewInternalError(fmt.Errorf("failed to verify sandbox %s/%s entrypoints: %w", sandbox.Namespace, sandbox.Name, err)) + return nil, api.NewInternalError(fmt.Errorf("failed to verify sandbox %s/%s entrypoints: %w", createdSandbox.Namespace, createdSandbox.Name, err)) } storeCacheInfo := buildSandboxInfo(createdSandbox, podIP, sandboxEntry) + responseSandboxName := createdSandbox.Name + if sandboxClaim != nil { + // Keep the claim name in the session store; it is the stable handle used + // for deletion even when the claim adopts a differently named warm-pool sandbox. + storeCacheInfo.Name = sandboxClaim.Name + responseSandboxName = sandboxClaim.Name + } response := &types.CreateSandboxResponse{ SessionID: sandboxEntry.SessionID, SandboxID: storeCacheInfo.SandboxID, - SandboxName: sandbox.Name, + SandboxName: responseSandboxName, EntryPoints: storeCacheInfo.EntryPoints, } diff --git a/pkg/workloadmanager/handlers_test.go b/pkg/workloadmanager/handlers_test.go index 30336c09..ffbce5d5 100644 --- a/pkg/workloadmanager/handlers_test.go +++ b/pkg/workloadmanager/handlers_test.go @@ -38,7 +38,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" - "sigs.k8s.io/agent-sandbox/controllers" extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" ) @@ -80,7 +79,7 @@ func readySandbox() *sandboxv1alpha1.Sandbox { Name: "sandbox-1", Namespace: "ns-1", UID: "uid-123", - Annotations: map[string]string{controllers.SandboxPodNameAnnotation: "pod-1"}, + Annotations: map[string]string{sandboxv1alpha1.SandboxPodNameAnnotation: "pod-1"}, CreationTimestamp: metav1.Now(), }, Status: sandboxv1alpha1.SandboxStatus{Conditions: []metav1.Condition{{ @@ -107,10 +106,13 @@ func TestServerCreateSandbox(t *testing.T) { storeErr error createSandboxErr error createClaimErr error + claimReadyErr error podIPErr error readyErr error updateErr error sendResult bool + missingPodName bool + resolvedSandbox string expectErr bool expectCreateCalls int expectClaimCalls int @@ -131,6 +133,13 @@ func TestServerCreateSandbox(t *testing.T) { expectClaimCalls: 1, expectUpdateCalls: 1, }, + { + name: "sandbox claim resolves adopted sandbox name", + sandboxClaim: true, + resolvedSandbox: "warm-pool-sandbox-1", + expectClaimCalls: 1, + expectUpdateCalls: 1, + }, { name: "store placeholder fails", storeErr: errors.New("store failed"), @@ -151,6 +160,14 @@ func TestServerCreateSandbox(t *testing.T) { expectClaimCalls: 1, expectDeleteCalls: 1, }, + { + name: "sandbox claim ready wait fails", + sandboxClaim: true, + claimReadyErr: errors.New("claim status failed"), + expectErr: true, + expectClaimCalls: 1, + expectDeleteCalls: 1, + }, { name: "pod ip lookup fails triggers rollback", podIPErr: errors.New("pod ip missing"), @@ -159,6 +176,14 @@ func TestServerCreateSandbox(t *testing.T) { expectCreateCalls: 1, expectDeleteCalls: 1, }, + { + name: "missing pod name annotation triggers rollback", + sendResult: true, + missingPodName: true, + expectErr: true, + expectCreateCalls: 1, + expectDeleteCalls: 1, + }, { name: "entrypoint readiness failure triggers rollback", readyErr: errors.New("connection refused"), @@ -185,6 +210,7 @@ func TestServerCreateSandbox(t *testing.T) { // avoids the repeated patch-reset-repatch cycle entirely. var cur *testCase var createCalls, claimCalls, deleteCalls int + var podIPLookupSandboxName string server := &Server{k8sClient: &K8sClient{}} @@ -202,6 +228,16 @@ func TestServerCreateSandbox(t *testing.T) { claimCalls++ return cur.createClaimErr }) + patches.ApplyFunc(waitForSandboxClaimReady, func(_ context.Context, _ dynamic.Interface, _, _ string, _ *time.Timer) (*sandboxv1alpha1.Sandbox, error) { + if cur.claimReadyErr != nil { + return nil, cur.claimReadyErr + } + sb := readySandbox() + if cur.resolvedSandbox != "" { + sb.Name = cur.resolvedSandbox + } + return sb, nil + }) patches.ApplyFunc(deleteSandbox, func(_ context.Context, _ dynamic.Interface, _, _ string) error { deleteCalls++ return nil @@ -210,7 +246,8 @@ func TestServerCreateSandbox(t *testing.T) { deleteCalls++ return nil }) - patches.ApplyMethod(reflect.TypeOf((*K8sClient)(nil)), "GetSandboxPodIP", func(_ *K8sClient, _ context.Context, _, _, _ string) (string, error) { + patches.ApplyMethod(reflect.TypeOf((*K8sClient)(nil)), "GetSandboxPodIP", func(_ *K8sClient, _ context.Context, _, sandboxName, _ string) (string, error) { + podIPLookupSandboxName = sandboxName if cur.podIPErr != nil { return "", cur.podIPErr } @@ -227,12 +264,16 @@ func TestServerCreateSandbox(t *testing.T) { createCalls = 0 claimCalls = 0 deleteCalls = 0 + podIPLookupSandboxName = "" fakeStoreInst := &fakeStore{storeErr: tt.storeErr, updateErr: tt.updateErr} server.storeClient = fakeStoreInst resultChan := make(chan SandboxStatusUpdate, 1) sb := readySandbox() + if tt.missingPodName { + delete(sb.Annotations, sandboxv1alpha1.SandboxPodNameAnnotation) + } if tt.sendResult { resultChan <- SandboxStatusUpdate{Sandbox: sb.DeepCopy()} } @@ -261,6 +302,9 @@ func TestServerCreateSandbox(t *testing.T) { require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, "sess-1", resp.SessionID) + if tt.resolvedSandbox != "" { + require.Equal(t, tt.resolvedSandbox, podIPLookupSandboxName) + } require.Equal(t, sb.Name, resp.SandboxName) require.Equal(t, string(sb.UID), resp.SandboxID) require.Len(t, resp.EntryPoints, 1) diff --git a/pkg/workloadmanager/informers_test.go b/pkg/workloadmanager/informers_test.go index 993e914f..679fc721 100644 --- a/pkg/workloadmanager/informers_test.go +++ b/pkg/workloadmanager/informers_test.go @@ -32,7 +32,7 @@ type neverSyncedInformer struct { cache.SharedIndexInformer } -func (n *neverSyncedInformer) HasSynced() bool { return false } +func (n *neverSyncedInformer) HasSynced() bool { return false } func (n *neverSyncedInformer) Run(stopCh <-chan struct{}) { <-stopCh } // alwaysSyncedInformer is a cache.SharedIndexInformer whose HasSynced always returns true. @@ -40,7 +40,7 @@ type alwaysSyncedInformer struct { cache.SharedIndexInformer } -func (a *alwaysSyncedInformer) HasSynced() bool { return true } +func (a *alwaysSyncedInformer) HasSynced() bool { return true } func (a *alwaysSyncedInformer) Run(stopCh <-chan struct{}) { <-stopCh } // runCanceled starts RunAndWaitForCacheSync in a goroutine, cancels the context @@ -61,7 +61,7 @@ func runCanceled(t *testing.T, ifm *Informers) error { } func newFactory() informers.SharedInformerFactory { - return informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 0) + return informers.NewSharedInformerFactory(fake.NewClientset(), 0) } func TestRunAndWaitForCacheSync_ContextCancellation(t *testing.T) { @@ -69,10 +69,10 @@ func TestRunAndWaitForCacheSync_ContextCancellation(t *testing.T) { always := func() cache.SharedIndexInformer { return &alwaysSyncedInformer{} } tests := []struct { - name string - agentRuntime cache.SharedIndexInformer - codeInterpreter cache.SharedIndexInformer - pod cache.SharedIndexInformer + name string + agentRuntime cache.SharedIndexInformer + codeInterpreter cache.SharedIndexInformer + pod cache.SharedIndexInformer }{ { name: "AgentRuntimeInformer never syncs", diff --git a/pkg/workloadmanager/k8s_client.go b/pkg/workloadmanager/k8s_client.go index 15cc47bd..9240765a 100644 --- a/pkg/workloadmanager/k8s_client.go +++ b/pkg/workloadmanager/k8s_client.go @@ -21,14 +21,11 @@ import ( "fmt" "time" - "k8s.io/klog/v2" - runtimev1alpha1 "github.com/volcano-sh/agentcube/pkg/apis/runtime/v1alpha1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic/dynamicinformer" @@ -38,6 +35,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" ) @@ -246,6 +244,72 @@ func createSandboxClaim(ctx context.Context, client dynamic.Interface, sandboxCl return nil } +func waitForSandboxClaimReady(ctx context.Context, client dynamic.Interface, namespace, claimName string, timer *time.Timer) (*sandboxv1alpha1.Sandbox, error) { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + sandbox, err := getReadySandboxForClaim(ctx, client, namespace, claimName) + if err != nil && !apierrors.IsNotFound(err) { + timer.Stop() + return nil, err + } + if sandbox != nil { + timer.Stop() + return sandbox, nil + } + + select { + case <-ctx.Done(): + timer.Stop() + klog.Warningf("sandbox claim %s/%s wait canceled: %v", namespace, claimName, ctx.Err()) + return nil, ctx.Err() + case <-timer.C: + klog.Warningf("sandbox claim %s/%s create timed out", namespace, claimName) + return nil, errSandboxCreationTimeout + case <-ticker.C: + } + } +} + +func getReadySandboxForClaim(ctx context.Context, client dynamic.Interface, namespace, claimName string) (*sandboxv1alpha1.Sandbox, error) { + claim, err := client.Resource(SandboxClaimGVR).Namespace(namespace).Get( + ctx, + claimName, + metav1.GetOptions{}, + ) + if err != nil { + return nil, err + } + + sandboxName, found, err := unstructured.NestedString(claim.Object, "status", "sandbox", "name") + if err != nil { + return nil, fmt.Errorf("failed to read sandbox claim %s/%s status: %w", namespace, claimName, err) + } + if !found || sandboxName == "" { + return nil, nil + } + + sandboxObj, err := client.Resource(SandboxGVR).Namespace(namespace).Get( + ctx, + sandboxName, + metav1.GetOptions{}, + ) + if err != nil { + return nil, err + } + + sandbox := &sandboxv1alpha1.Sandbox{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(sandboxObj.Object, sandbox); err != nil { + return nil, fmt.Errorf("failed to convert sandbox %s/%s: %w", namespace, sandboxName, err) + } + if getSandboxStatus(sandbox) != sandboxStatusReady { + return nil, nil + } + + return sandbox, nil +} + // deleteSandbox deletes a Sandbox using the provided dynamic client func deleteSandbox(ctx context.Context, client dynamic.Interface, namespace, sandboxName string) error { err := client.Resource(SandboxGVR).Namespace(namespace).Delete( @@ -296,31 +360,16 @@ func (u *UserK8sClient) DeleteSandboxClaim(ctx context.Context, namespace, sandb // GetSandboxPodIP gets the IP address of the pod corresponding to the Sandbox func (c *K8sClient) GetSandboxPodIP(_ context.Context, namespace, sandboxName, podName string) (string, error) { - // If podName is provided, try to get it directly from cache first - if podName != "" { - pod, err := c.podLister.Pods(namespace).Get(podName) - if err == nil && pod != nil { - return validateAndGetPodIP(pod) - } - klog.Infof("failed to get sandbox pod %s/%s: %v, try get pod by sandbox-name label", namespace, podName, err) + if podName == "" { + return "", fmt.Errorf("sandbox %s has no pod name annotation", sandboxName) } - // Find pod through label selector (sandbox-name label we set) - pods, err := c.podLister.Pods(namespace).List(labels.SelectorFromSet(map[string]string{SandboxNameLabelKey: sandboxName})) + + pod, err := c.podLister.Pods(namespace).Get(podName) if err != nil { - return "", fmt.Errorf("failed to list pods from cache: %w", err) - } - // Find the pod that belongs to this sandbox by checking ownerReferences - for _, pod := range pods { - for _, ownerRef := range pod.OwnerReferences { - if ownerRef.Kind == "Sandbox" && ownerRef.Name == sandboxName { - if ownerRef.Controller == nil || *ownerRef.Controller { - return validateAndGetPodIP(pod) - } - } - } + return "", fmt.Errorf("failed to get sandbox pod %s/%s: %w", namespace, podName, err) } - return "", fmt.Errorf("no pod found for sandbox %s", sandboxName) + return validateAndGetPodIP(pod) } // validateAndGetPodIP validates pod status and returns IP diff --git a/pkg/workloadmanager/k8s_client_test.go b/pkg/workloadmanager/k8s_client_test.go index b14526eb..782aa92e 100644 --- a/pkg/workloadmanager/k8s_client_test.go +++ b/pkg/workloadmanager/k8s_client_test.go @@ -22,28 +22,19 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" listersv1 "k8s.io/client-go/listers/core/v1" ) -// Helper function to create a pod with owner reference -func createPodWithOwner(name, namespace, sandboxName string, phase corev1.PodPhase, podIP string) *corev1.Pod { - controller := true +// Helper function to create a pod. +func createPod(name, namespace string, phase corev1.PodPhase, podIP string) *corev1.Pod { return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, - Labels: map[string]string{ - SandboxNameLabelKey: sandboxName, - }, - OwnerReferences: []metav1.OwnerReference{ - { - Kind: "Sandbox", - Name: sandboxName, - Controller: &controller, - }, - }, }, Status: corev1.PodStatus{ Phase: phase, @@ -52,6 +43,14 @@ func createPodWithOwner(name, namespace, sandboxName string, phase corev1.PodPha } } +func createPodWithLabel(name, namespace, sandboxName string, phase corev1.PodPhase, podIP string) *corev1.Pod { + pod := createPod(name, namespace, phase, podIP) + pod.Labels = map[string]string{ + SandboxNameLabelKey: sandboxName, + } + return pod +} + // mockPodNamespaceLister is a mock implementation of listersv1.PodNamespaceLister type mockPodNamespaceLister struct { pods []*corev1.Pod @@ -76,7 +75,7 @@ func (m *mockPodNamespaceLister) Get(name string) (*corev1.Pod, error) { return pod, nil } } - return nil, nil + return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, name) } // mockPodLister is a mock implementation of listersv1.PodLister @@ -118,7 +117,7 @@ func (m *mockPodLister) addPod(pod *corev1.Pod) { // TestGetSandboxPodIP_Success verifies GetSandboxPodIP returns IP when pod is present and valid func TestGetSandboxPodIP_Success(t *testing.T) { // Setup: Create a mock pod lister with a valid running pod - pod := createPodWithOwner("test-pod", "test-namespace", "test-sandbox", corev1.PodRunning, "10.0.0.1") + pod := createPod("test-pod", "test-namespace", corev1.PodRunning, "10.0.0.1") mockPodLister := newMockPodLister() mockPodLister.addPod(pod) @@ -127,43 +126,55 @@ func TestGetSandboxPodIP_Success(t *testing.T) { } // Execute - ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "") + ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "test-pod") // Verify assert.NoError(t, err, "Expected no error for valid pod") assert.Equal(t, "10.0.0.1", ip, "Expected IP to match pod IP") } -// TestGetSandboxPodIP_PodNotFound verifies GetSandboxPodIP returns error when pod is not found +func TestGetSandboxPodIP_MissingPodName(t *testing.T) { + client := &K8sClient{ + podLister: newMockPodLister(), + } + + ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "") + + assert.Error(t, err, "Expected error when pod name is empty") + assert.Empty(t, ip, "Expected empty IP when error occurs") + assert.Contains(t, err.Error(), "sandbox test-sandbox has no pod name annotation") +} + +// TestGetSandboxPodIP_PodNotFound verifies GetSandboxPodIP returns error when annotated pod is not found. func TestGetSandboxPodIP_PodNotFound(t *testing.T) { - // Setup: Create a mock pod lister with pod that has wrong label mockPodLister := newMockPodLister() - pod := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-pod", - Namespace: "test-namespace", - Labels: map[string]string{ - SandboxNameLabelKey: "other-sandbox", - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - PodIP: "10.0.0.1", - }, - } - mockPodLister.addPod(pod) client := &K8sClient{ podLister: mockPodLister, } // Execute - ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "") + ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "missing-pod") // Verify assert.Error(t, err, "Expected error when pod not found") assert.Empty(t, ip, "Expected empty IP when error occurs") - assert.Contains(t, err.Error(), "no pod found for sandbox test-sandbox", "Error message should indicate pod not found") + assert.Contains(t, err.Error(), "failed to get sandbox pod test-namespace/missing-pod", "Error message should indicate pod not found") +} + +func TestGetSandboxPodIP_DoesNotFallbackToLabelSelector(t *testing.T) { + mockPodLister := newMockPodLister() + mockPodLister.addPod(createPodWithLabel("label-matched-pod", "test-namespace", "test-sandbox", corev1.PodRunning, "10.0.0.1")) + + client := &K8sClient{ + podLister: mockPodLister, + } + + ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "missing-pod") + + assert.Error(t, err, "Expected error when annotated pod is missing") + assert.Empty(t, ip, "Expected empty IP when error occurs") + assert.Contains(t, err.Error(), "missing-pod") } // TestGetSandboxPodIP_InvalidPodStatus verifies GetSandboxPodIP returns error when pod status is invalid @@ -192,7 +203,7 @@ func TestGetSandboxPodIP_InvalidPodStatus(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup: Create a mock pod lister with invalid pod status - pod := createPodWithOwner("test-pod", "test-namespace", "test-sandbox", tc.phase, tc.podIP) + pod := createPod("test-pod", "test-namespace", tc.phase, tc.podIP) mockPodLister := newMockPodLister() mockPodLister.addPod(pod) @@ -201,7 +212,7 @@ func TestGetSandboxPodIP_InvalidPodStatus(t *testing.T) { } // Execute - ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "") + ip, err := client.GetSandboxPodIP(context.Background(), "test-namespace", "test-sandbox", "test-pod") // Verify assert.Error(t, err, "Expected error for invalid pod status") diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index ded58911..34cbfb98 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -1166,34 +1166,41 @@ func (ctx *e2eTestContext) countSandboxClaims(namespace, codeInterpreterName str // countWarmPoolPods counts the number of warmpool pods for a given CodeInterpreter func (ctx *e2eTestContext) countWarmPoolPods(namespace, codeInterpreterName string) (int, error) { - listCtx := context.Background() - - // List all pods in namespace - podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( - listCtx, - metav1.ListOptions{}, - ) + podNames, err := ctx.getWarmPoolPodNames(namespace, codeInterpreterName) if err != nil { - return 0, fmt.Errorf("failed to list pods: %w", err) + return 0, err } - count := 0 - for _, pod := range podList.Items { - for _, owner := range pod.OwnerReferences { + return len(podNames), nil +} + +func (ctx *e2eTestContext) getWarmPoolSandboxNames(namespace, codeInterpreterName string) (map[string]struct{}, error) { + sandboxList := &sandboxv1alpha1.SandboxList{} + if err := ctx.ctrlClient.List(context.Background(), sandboxList, client.InNamespace(namespace)); err != nil { + return nil, fmt.Errorf("failed to list sandboxes: %w", err) + } + + sandboxNames := make(map[string]struct{}) + for _, sandbox := range sandboxList.Items { + for _, owner := range sandbox.OwnerReferences { if owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName { - count++ + sandboxNames[sandbox.Name] = struct{}{} break } } } - return count, nil + return sandboxNames, nil } // getWarmPoolPodNames returns the names of warmpool pods for a given CodeInterpreter func (ctx *e2eTestContext) getWarmPoolPodNames(namespace, codeInterpreterName string) ([]string, error) { - listCtx := context.Background() + warmPoolSandboxes, err := ctx.getWarmPoolSandboxNames(namespace, codeInterpreterName) + if err != nil { + return nil, err + } + listCtx := context.Background() podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( listCtx, metav1.ListOptions{}, @@ -1204,16 +1211,14 @@ func (ctx *e2eTestContext) getWarmPoolPodNames(namespace, codeInterpreterName st podNames := make([]string, 0, len(podList.Items)) for _, pod := range podList.Items { - isWarmPool := false for _, owner := range pod.OwnerReferences { - if owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName { - isWarmPool = true + if owner.Kind == "Sandbox" { + if _, ok := warmPoolSandboxes[owner.Name]; ok { + podNames = append(podNames, pod.Name) + } break } } - if isWarmPool { - podNames = append(podNames, pod.Name) - } } return podNames, nil @@ -1248,6 +1253,11 @@ func (ctx *e2eTestContext) waitForWarmPoolReady(namespace, codeInterpreterName s // arePodsReady checks if warmpool pods are ready func (ctx *e2eTestContext) arePodsReady(namespace, codeInterpreterName string) (bool, error) { + warmPoolSandboxes, err := ctx.getWarmPoolSandboxNames(namespace, codeInterpreterName) + if err != nil { + return false, err + } + listCtx := context.Background() podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( @@ -1263,8 +1273,8 @@ func (ctx *e2eTestContext) arePodsReady(namespace, codeInterpreterName string) ( for _, pod := range podList.Items { isWarmPool := false for _, owner := range pod.OwnerReferences { - if owner.Kind == "SandboxWarmPool" && owner.Name == codeInterpreterName { - isWarmPool = true + if owner.Kind == "Sandbox" { + _, isWarmPool = warmPoolSandboxes[owner.Name] break } } diff --git a/test/e2e/run_e2e.sh b/test/e2e/run_e2e.sh index 415e5e13..ae698f06 100755 --- a/test/e2e/run_e2e.sh +++ b/test/e2e/run_e2e.sh @@ -6,7 +6,7 @@ IFS=$'\n\t' E2E_CLUSTER_NAME=${E2E_CLUSTER_NAME:-agentcube-e2e} E2E_CLEAN_CLUSTER=${E2E_CLEAN_CLUSTER:-true} E2E_SKIP_SETUP=${E2E_SKIP_SETUP:-false} -AGENT_SANDBOX_VERSION=${AGENT_SANDBOX_VERSION:-v0.1.1} +AGENT_SANDBOX_VERSION=${AGENT_SANDBOX_VERSION:-v0.3.10} WORKLOAD_MANAGER_IMAGE=${WORKLOAD_MANAGER_IMAGE:-workloadmanager:latest} ROUTER_IMAGE=${ROUTER_IMAGE:-agentcube-router:latest} PICOD_IMAGE=${PICOD_IMAGE:-picod:latest}