diff --git a/client-go/clientset/versioned/fake/clientset_generated.go b/client-go/clientset/versioned/fake/clientset_generated.go index b740d57ac..f3a7b1ba9 100644 --- a/client-go/clientset/versioned/fake/clientset_generated.go +++ b/client-go/clientset/versioned/fake/clientset_generated.go @@ -34,10 +34,6 @@ import ( // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, // without applying any field management, validations and/or defaults. It shouldn't be considered a replacement // for a real clientset and is mostly useful in simple unit tests. -// -// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves -// server side apply testing. NewClientset is only available when apply configurations are generated (e.g. -// via --with-applyconfig). func NewSimpleClientset(objects ...runtime.Object) *Clientset { o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) for _, obj := range objects { @@ -51,8 +47,8 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { var opts metav1.ListOptions - if watchActcion, ok := action.(testing.WatchActionImpl); ok { - opts = watchActcion.ListOptions + if watchAction, ok := action.(testing.WatchActionImpl); ok { + opts = watchAction.ListOptions } gvr := action.GetResource() ns := action.GetNamespace() @@ -83,6 +79,17 @@ func (c *Clientset) Tracker() testing.ObjectTracker { return c.tracker } +// IsWatchListSemanticsSupported informs the reflector that this client +// doesn't support WatchList semantics. +// +// This is a synthetic method whose sole purpose is to satisfy the optional +// interface check performed by the reflector. +// Returning true signals that WatchList can NOT be used. +// No additional logic is implemented here. +func (c *Clientset) IsWatchListSemanticsUnSupported() bool { + return true +} + var ( _ clientset.Interface = &Clientset{} _ testing.FakeClient = &Clientset{} diff --git a/client-go/informers/externalversions/factory.go b/client-go/informers/externalversions/factory.go index a5dd3e2a3..33e5338a6 100644 --- a/client-go/informers/externalversions/factory.go +++ b/client-go/informers/externalversions/factory.go @@ -97,6 +97,7 @@ func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Dur // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. // Listers obtained via this SharedInformerFactory will be subject to the same filters // as specified here. +// // Deprecated: Please use NewSharedInformerFactoryWithOptions instead func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) @@ -204,7 +205,7 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal // // It is typically used like this: // -// ctx, cancel := context.Background() +// ctx, cancel := context.WithCancel(context.Background()) // defer cancel() // factory := NewSharedInformerFactory(client, resyncPeriod) // defer factory.WaitForStop() // Returns immediately if nothing was started. diff --git a/client-go/informers/externalversions/runtime/v1alpha1/agentruntime.go b/client-go/informers/externalversions/runtime/v1alpha1/agentruntime.go index e8c3d0d1e..1c8743892 100644 --- a/client-go/informers/externalversions/runtime/v1alpha1/agentruntime.go +++ b/client-go/informers/externalversions/runtime/v1alpha1/agentruntime.go @@ -57,7 +57,7 @@ func NewAgentRuntimeInformer(client versioned.Interface, namespace string, resyn // one. This reduces memory footprint and number of connections to the server. func NewFilteredAgentRuntimeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( - &cache.ListWatch{ + cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) @@ -82,7 +82,7 @@ func NewFilteredAgentRuntimeInformer(client versioned.Interface, namespace strin } return client.RuntimeV1alpha1().AgentRuntimes(namespace).Watch(ctx, options) }, - }, + }, client), &apisruntimev1alpha1.AgentRuntime{}, resyncPeriod, indexers, diff --git a/client-go/informers/externalversions/runtime/v1alpha1/codeinterpreter.go b/client-go/informers/externalversions/runtime/v1alpha1/codeinterpreter.go index d44359d9e..41c71b1ac 100644 --- a/client-go/informers/externalversions/runtime/v1alpha1/codeinterpreter.go +++ b/client-go/informers/externalversions/runtime/v1alpha1/codeinterpreter.go @@ -57,7 +57,7 @@ func NewCodeInterpreterInformer(client versioned.Interface, namespace string, re // one. This reduces memory footprint and number of connections to the server. func NewFilteredCodeInterpreterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( - &cache.ListWatch{ + cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) @@ -82,7 +82,7 @@ func NewFilteredCodeInterpreterInformer(client versioned.Interface, namespace st } return client.RuntimeV1alpha1().CodeInterpreters(namespace).Watch(ctx, options) }, - }, + }, client), &apisruntimev1alpha1.CodeInterpreter{}, resyncPeriod, indexers, diff --git a/docker/Dockerfile b/docker/Dockerfile index da25c8eb2..b54eb849d 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.2-alpine AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux diff --git a/docker/Dockerfile.picod b/docker/Dockerfile.picod index 918334df5..3623eb51f 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.2 AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux @@ -25,7 +25,8 @@ RUN --mount=type=cache,target=/go/pkg/mod \ FROM ubuntu:24.04 # Install Python3 to support code execution tasks (Code Interpreter) -RUN apt-get update && apt-get install -y python3 +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3 \ + && rm -rf /var/lib/apt/lists/* # Use /root/ as the working directory # We run as root to allow 'chattr +i' on the public key file (see pkg/picod/auth.go) @@ -34,4 +35,4 @@ WORKDIR /root/ COPY --from=builder /app/picod . -ENTRYPOINT ["./picod"] \ No newline at end of file +ENTRYPOINT ["./picod"] diff --git a/docker/Dockerfile.router b/docker/Dockerfile.router index 8e2f22cac..535047234 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.2-alpine AS builder # Build arguments for multi-architecture support ARG TARGETOS=linux diff --git a/go.mod b/go.mod index c637c9f78..7fc190b7f 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.2 require ( github.com/agiledragon/gomonkey/v2 v2.13.0 @@ -15,21 +13,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 - k8s.io/klog/v2 v2.130.1 + golang.org/x/net v0.52.0 + k8s.io/api v0.35.4 + k8s.io/apimachinery v0.35.4 + k8s.io/client-go v0.35.4 + k8s.io/klog/v2 v2.140.0 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.4.6 + 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 @@ -41,7 +38,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 @@ -61,11 +57,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 @@ -84,38 +78,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.49.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.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 + google.golang.org/protobuf v1.36.11 // 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.4 // 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 5c75dfcdb..2b8cb6a4b 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= @@ -43,11 +43,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= @@ -92,12 +89,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= @@ -107,18 +100,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= @@ -143,10 +132,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= @@ -191,28 +180,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= @@ -227,64 +196,36 @@ 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.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= 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.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= 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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= 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.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= 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= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -295,31 +236,31 @@ 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/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= +k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= +k8s.io/apiextensions-apiserver v0.35.4 h1:HeP+Upp7ItdvnyGmub0yoix+2z5+ev4M5cE5TCgtOUU= +k8s.io/apiextensions-apiserver v0.35.4/go.mod h1:ogQlk+stIE8mnoRthSYCwlOS12fVqgWFiErMwPaXA7c= +k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= +k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= +k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8= +k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= 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.4.6 h1:dUUvQ+rlv4kT3CB9p2MgnPcNSMAQg6eTBrWyCiYgxYs= +sigs.k8s.io/agent-sandbox v0.4.6/go.mod h1:rrBXoA+nKZO2MIs3pTG+qOuKadX5gzMvO/nrlwC9W1w= +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/hack/update-codegen.sh b/hack/update-codegen.sh index f39bb861d..02110e046 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -12,7 +12,7 @@ export GO111MODULE=on # Find code-generator CODEGEN_PKG="" -CODEGEN_VERSION="v0.34.1" +CODEGEN_VERSION="v0.35.4" # Try vendor directory first if [ -d "vendor/k8s.io/code-generator" ]; then @@ -21,10 +21,10 @@ if [ -d "vendor/k8s.io/code-generator" ]; then else # Ensure code-generator is downloaded echo "Ensuring code-generator@${CODEGEN_VERSION} is available..." - go get -d "k8s.io/code-generator@${CODEGEN_VERSION}" || true + CODEGEN_JSON=$(go mod download -json "k8s.io/code-generator@${CODEGEN_VERSION}") # Find code-generator in module cache - CODEGEN_PKG=$(go list -m -f '{{.Dir}}' "k8s.io/code-generator@${CODEGEN_VERSION}" 2>/dev/null || echo "") + CODEGEN_PKG=$(printf '%s\n' "${CODEGEN_JSON}" | sed -n 's/^[[:space:]]*"Dir": "\(.*\)",$/\1/p') if [ -z "${CODEGEN_PKG}" ] || [ ! -d "${CODEGEN_PKG}" ]; then # Try GOPATH/pkg/mod as fallback diff --git a/manifests/charts/base/crds/runtime.agentcube.volcano.sh_agentruntimes.yaml b/manifests/charts/base/crds/runtime.agentcube.volcano.sh_agentruntimes.yaml index a6508b341..de219e65d 100644 --- a/manifests/charts/base/crds/runtime.agentcube.volcano.sh_agentruntimes.yaml +++ b/manifests/charts/base/crds/runtime.agentcube.volcano.sh_agentruntimes.yaml @@ -1885,7 +1885,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -5108,7 +5110,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -5895,8 +5899,8 @@ spec: will be made available to those containers which consume them by name. - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. + This is a stable field but requires that the + DynamicResourceAllocation feature gate is enabled. This field is immutable. items: @@ -6355,9 +6359,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -7151,7 +7156,7 @@ spec: resources: description: |- resources represents the minimum resources the volume should have. - If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + Users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources @@ -8037,6 +8042,24 @@ spec: description: Kubelet's generated CSRs will be addressed to this signer. type: string + userAnnotations: + additionalProperties: + type: string + description: |- + userAnnotations allow pod authors to pass additional information to + the signer implementation. Kubernetes does not restrict or validate this + metadata in any way. + + These values are copied verbatim into the `spec.unverifiedUserAnnotations` field of + the PodCertificateRequest objects that Kubelet creates. + + Entries are subject to the same validation as object metadata annotations, + with the addition that all keys must be domain-prefixed. No restrictions + are placed on values, except an overall size limitation on the entire field. + + Signers should document the keys and values they support. Signers should + deny requests that contain keys they do not recognize. + type: object required: - keyType - signerName @@ -8462,6 +8485,42 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + workloadRef: + description: |- + WorkloadRef provides a reference to the Workload object that this Pod belongs to. + This field is used by the scheduler to identify the PodGroup and apply the + correct group scheduling policies. The Workload object referenced + by this field may not exist at the time the Pod is created. + This field is immutable, but a Workload object with the same name + may be recreated with different policies. Doing this during pod scheduling + may result in the placement not conforming to the expected policies. + properties: + name: + description: |- + Name defines the name of the Workload object this Pod belongs to. + Workload must be in the same namespace as the Pod. + If it doesn't match any existing Workload, the Pod will remain unschedulable + until a Workload object is created and observed by the kube-scheduler. + It must be a DNS subdomain. + type: string + podGroup: + description: |- + PodGroup is the name of the PodGroup within the Workload that this Pod + belongs to. If it doesn't match any existing PodGroup within the Workload, + the Pod will remain unschedulable until the Workload object is recreated + and observed by the kube-scheduler. It must be a DNS label. + type: string + podGroupReplicaKey: + description: |- + PodGroupReplicaKey specifies the replica key of the PodGroup to which this + Pod belongs. It is used to distinguish pods belonging to different replicas + of the same pod group. The pod group policy is applied separately to each replica. + When set, it must be a DNS label. + type: string + required: + - name + - podGroup + type: object required: - containers type: object diff --git a/pkg/workloadmanager/codeinterpreter_controller.go b/pkg/workloadmanager/codeinterpreter_controller.go index da010cbb1..bce73a0d5 100644 --- a/pkg/workloadmanager/codeinterpreter_controller.go +++ b/pkg/workloadmanager/codeinterpreter_controller.go @@ -40,6 +40,8 @@ import ( extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" ) +const codeInterpreterNetworkPolicyManagement = extensionsv1alpha1.NetworkPolicyManagementUnmanaged + // CodeInterpreterReconciler reconciles a CodeInterpreter object type CodeInterpreterReconciler struct { client.Client @@ -155,7 +157,8 @@ func (r *CodeInterpreterReconciler) ensureSandboxTemplate(ctx context.Context, c Namespace: ci.Namespace, }, Spec: extensionsv1alpha1.SandboxTemplateSpec{ - PodTemplate: podTemplate, + PodTemplate: podTemplate, + NetworkPolicyManagement: codeInterpreterNetworkPolicyManagement, }, } @@ -174,9 +177,17 @@ func (r *CodeInterpreterReconciler) ensureSandboxTemplate(ctx context.Context, c return ctrl.Result{}, fmt.Errorf("failed to get SandboxTemplate: %w", err) } - // Update existing SandboxTemplate if needed + // Update existing SandboxTemplate if needed. + needsUpdate := false if !r.podTemplateEqual(sandboxTemplate.Spec.PodTemplate, podTemplate) { sandboxTemplate.Spec.PodTemplate = podTemplate + needsUpdate = true + } + if sandboxTemplate.Spec.NetworkPolicyManagement != codeInterpreterNetworkPolicyManagement { + sandboxTemplate.Spec.NetworkPolicyManagement = codeInterpreterNetworkPolicyManagement + needsUpdate = true + } + if needsUpdate { if err := r.Update(ctx, sandboxTemplate); err != nil { return ctrl.Result{}, fmt.Errorf("failed to update SandboxTemplate: %w", err) } diff --git a/pkg/workloadmanager/codeinterpreter_controller_test.go b/pkg/workloadmanager/codeinterpreter_controller_test.go index 1404c8611..b33c65b2e 100644 --- a/pkg/workloadmanager/codeinterpreter_controller_test.go +++ b/pkg/workloadmanager/codeinterpreter_controller_test.go @@ -17,15 +17,19 @@ limitations under the License. package workloadmanager import ( + "context" "testing" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" runtimev1alpha1 "github.com/volcano-sh/agentcube/pkg/apis/runtime/v1alpha1" sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" + extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1" ) func setupTestReconciler() *CodeInterpreterReconciler { @@ -42,10 +46,92 @@ func setupTestReconciler() *CodeInterpreterReconciler { } } +func newTestReconcilerWithObjects(objects ...runtime.Object) *CodeInterpreterReconciler { + scheme := runtime.NewScheme() + _ = runtimev1alpha1.AddToScheme(scheme) + _ = sandboxv1alpha1.AddToScheme(scheme) + _ = extensionsv1alpha1.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build() + + return &CodeInterpreterReconciler{ + Client: client, + Scheme: scheme, + } +} + func stringPtr(s string) *string { return &s } +func testCodeInterpreterWithWarmPool() *runtimev1alpha1.CodeInterpreter { + warmPoolSize := int32(2) + return &runtimev1alpha1.CodeInterpreter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-code-interpreter", + Namespace: "default", + }, + Spec: runtimev1alpha1.CodeInterpreterSpec{ + AuthMode: runtimev1alpha1.AuthModeNone, + WarmPoolSize: &warmPoolSize, + Template: &runtimev1alpha1.CodeInterpreterSandboxTemplate{ + Image: "picod:latest", + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + } +} + +func TestEnsureSandboxTemplateDisablesAgentSandboxDefaultNetworkPolicy(t *testing.T) { + reconciler := newTestReconcilerWithObjects() + ci := testCodeInterpreterWithWarmPool() + + _, err := reconciler.ensureSandboxTemplate(context.Background(), ci) + assert.NoError(t, err) + + sandboxTemplate := &extensionsv1alpha1.SandboxTemplate{} + err = reconciler.Get(context.Background(), types.NamespacedName{ + Name: ci.Name, + Namespace: ci.Namespace, + }, sandboxTemplate) + assert.NoError(t, err) + assert.Equal(t, extensionsv1alpha1.NetworkPolicyManagementUnmanaged, sandboxTemplate.Spec.NetworkPolicyManagement) +} + +func TestEnsureSandboxTemplateUpdatesManagedNetworkPolicyToUnmanaged(t *testing.T) { + ci := testCodeInterpreterWithWarmPool() + existing := &extensionsv1alpha1.SandboxTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: ci.Name, + Namespace: ci.Namespace, + }, + Spec: extensionsv1alpha1.SandboxTemplateSpec{ + NetworkPolicyManagement: extensionsv1alpha1.NetworkPolicyManagementManaged, + PodTemplate: sandboxv1alpha1.PodTemplate{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "codeinterpreter", + Image: "stale-image", + }}, + }, + }, + }, + } + reconciler := newTestReconcilerWithObjects(existing) + + _, err := reconciler.ensureSandboxTemplate(context.Background(), ci) + assert.NoError(t, err) + + sandboxTemplate := &extensionsv1alpha1.SandboxTemplate{} + err = reconciler.Get(context.Background(), types.NamespacedName{ + Name: ci.Name, + Namespace: ci.Namespace, + }, sandboxTemplate) + assert.NoError(t, err) + assert.Equal(t, extensionsv1alpha1.NetworkPolicyManagementUnmanaged, sandboxTemplate.Spec.NetworkPolicyManagement) +} + func TestConvertToPodTemplate_RuntimeClassName_TableDriven(t *testing.T) { reconciler := setupTestReconciler() diff --git a/pkg/workloadmanager/handlers.go b/pkg/workloadmanager/handlers.go index 7d417c6ec..85b9a23fa 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" @@ -120,7 +119,7 @@ func (s *Server) handleSandboxCreate(c *gin.Context, kind string) { return } - // Calculate sandbox name and namespace before creating + // Calculate sandbox name and namespace before creating. sandboxName := sandbox.Name namespace := sandbox.Namespace @@ -135,11 +134,14 @@ 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) + var resultChan <-chan SandboxStatusUpdate + if sandboxClaim == nil { + // CRITICAL: Register watcher BEFORE creating sandbox. + // This ensures we don't miss the Ready state notification for directly created sandboxes. + 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) + } response, err := s.createSandbox(c.Request.Context(), dynamicClient, sandbox, sandboxClaim, sandboxEntry, resultChan) if err != nil { @@ -195,6 +197,71 @@ func (s *Server) createK8sResources(ctx context.Context, dynamicClient dynamic.I return nil } +func (s *Server) waitForDirectSandboxReady(ctx context.Context, sandbox *sandboxv1alpha1.Sandbox, resultChan <-chan SandboxStatusUpdate) (*sandboxv1alpha1.Sandbox, error) { + // Use NewTimer so we can stop it explicitly when another branch wins, + // preventing the runtime from retaining the timer until it fires. + timer := time.NewTimer(2 * time.Minute) // consistent with router settings + defer timer.Stop() + + select { + case result := <-resultChan: + createdSandbox := result.Sandbox + klog.V(2).Infof("sandbox %s/%s reported ready, verifying entrypoints", createdSandbox.Namespace, createdSandbox.Name) + return createdSandbox, nil + case <-ctx.Done(): + 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 + } +} + +func (s *Server) waitForClaimSandboxReady(ctx context.Context, dynamicClient dynamic.Interface, sandboxClaim *extensionsv1alpha1.SandboxClaim) (*sandboxv1alpha1.Sandbox, error) { + timer := time.NewTimer(2 * time.Minute) // consistent with router settings + defer timer.Stop() + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + claim, err := getSandboxClaim(ctx, dynamicClient, sandboxClaim.Namespace, sandboxClaim.Name) + if err != nil && !isContextError(err) { + klog.V(2).Infof("waiting for sandbox claim %s/%s status: %v", sandboxClaim.Namespace, sandboxClaim.Name, err) + } + if err == nil { + if sandboxName := claim.Status.SandboxStatus.Name; sandboxName != "" { + createdSandbox, err := getSandbox(ctx, dynamicClient, sandboxClaim.Namespace, sandboxName) + if err != nil { + if isContextError(err) { + return nil, err + } + klog.V(2).Infof("waiting for adopted sandbox %s/%s: %v", sandboxClaim.Namespace, sandboxName, err) + } else if getSandboxStatus(createdSandbox) == sandboxStatusReady { + klog.V(2).Infof("sandbox claim %s/%s adopted ready sandbox %s/%s", sandboxClaim.Namespace, sandboxClaim.Name, createdSandbox.Namespace, createdSandbox.Name) + return createdSandbox, nil + } + } + } + + select { + case <-ctx.Done(): + klog.Warningf("sandbox claim %s/%s wait canceled: %v", sandboxClaim.Namespace, sandboxClaim.Name, ctx.Err()) + return nil, ctx.Err() + case <-timer.C: + klog.Warningf("sandbox claim %s/%s create timed out", sandboxClaim.Namespace, sandboxClaim.Name) + return nil, errSandboxCreationTimeout + case <-ticker.C: + } + } +} + +func (s *Server) waitForCreatedSandbox(ctx context.Context, dynamicClient dynamic.Interface, sandbox *sandboxv1alpha1.Sandbox, sandboxClaim *extensionsv1alpha1.SandboxClaim, resultChan <-chan SandboxStatusUpdate) (*sandboxv1alpha1.Sandbox, error) { + if sandboxClaim != nil { + return s.waitForClaimSandboxReady(ctx, dynamicClient, sandboxClaim) + } + return s.waitForDirectSandboxReady(ctx, sandbox, resultChan) +} + // 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) @@ -219,55 +286,47 @@ func (s *Server) createSandbox(ctx context.Context, dynamicClient dynamic.Interf return nil, err } - // Use NewTimer so we can stop it explicitly when another branch wins, - // 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 := s.waitForCreatedSandbox(ctx, dynamicClient, sandbox, sandboxClaim, resultChan) + 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 { + // if warmpool is used, the pod name is stored in sandbox's annotation `agents.x-k8s.io/pod-name` + sandboxNameForPod := createdSandbox.Name + sandboxPodName := createdSandbox.Name + if podName, exists := createdSandbox.Annotations[sandboxv1alpha1.SandboxPodNameAnnotation]; exists { sandboxPodName = podName } - podIP, err := s.k8sClient.GetSandboxPodIP(ctx, sandbox.Namespace, sandbox.Name, sandboxPodName) + podIP, err := s.k8sClient.GetSandboxPodIP(ctx, createdSandbox.Namespace, sandboxNameForPod, 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, sandboxNameForPod, 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, sandboxNameForPod, err)) } storeCacheInfo := buildSandboxInfo(createdSandbox, podIP, sandboxEntry) + if sandboxClaim != nil { + storeCacheInfo.Name = sandboxClaim.Name + storeCacheInfo.SandboxNamespace = sandboxClaim.Namespace + storeCacheInfo.ExpiresAt = placeholder.ExpiresAt + storeCacheInfo.CreatedAt = placeholder.CreatedAt + } response := &types.CreateSandboxResponse{ Kind: storeCacheInfo.Kind, SessionID: sandboxEntry.SessionID, SandboxID: storeCacheInfo.SandboxID, - SandboxName: sandbox.Name, + SandboxName: storeCacheInfo.Name, EntryPoints: storeCacheInfo.EntryPoints, } diff --git a/pkg/workloadmanager/handlers_test.go b/pkg/workloadmanager/handlers_test.go index a71aa124b..a5620dabd 100644 --- a/pkg/workloadmanager/handlers_test.go +++ b/pkg/workloadmanager/handlers_test.go @@ -36,9 +36,12 @@ import ( "github.com/volcano-sh/agentcube/pkg/store" 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/runtime" "k8s.io/client-go/dynamic" + dynamicfake "k8s.io/client-go/dynamic/fake" + k8stesting "k8s.io/client-go/testing" 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 +83,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{{ @@ -100,6 +103,21 @@ func makeEntry() *sandboxEntry { } } +type recordingStore struct { + fakeStore + lastUpdated *types.SandboxInfo +} + +func (f *recordingStore) UpdateSandbox(ctx context.Context, sandbox *types.SandboxInfo) error { + f.fakeStore.UpdateSandbox(ctx, sandbox) + if f.updateErr != nil { + return f.updateErr + } + copied := *sandbox + f.lastUpdated = &copied + return nil +} + func TestServerCreateSandbox(t *testing.T) { type testCase struct { name string @@ -219,6 +237,17 @@ func TestServerCreateSandbox(t *testing.T) { patches.ApplyPrivateMethod(reflect.TypeOf(server), "waitForSandboxEntryPointsReady", func(_ *Server, _ context.Context, _ string, _ *sandboxEntry) error { return cur.readyErr }) + patches.ApplyPrivateMethod(reflect.TypeOf(server), "waitForCreatedSandbox", func(_ *Server, _ context.Context, _ dynamic.Interface, sandbox *sandboxv1alpha1.Sandbox, _ *extensionsv1alpha1.SandboxClaim, resultChan <-chan SandboxStatusUpdate) (*sandboxv1alpha1.Sandbox, error) { + if resultChan == nil { + return readySandbox(), nil + } + select { + case result := <-resultChan: + return result.Sandbox, nil + default: + return sandbox, nil + } + }) for i := range tests { tt := &tests[i] @@ -270,6 +299,115 @@ func TestServerCreateSandbox(t *testing.T) { } } +func TestServerCreateSandboxClaimUsesAdoptedSandboxButStoresClaimName(t *testing.T) { + claim := &extensionsv1alpha1.SandboxClaim{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "extensions.agents.x-k8s.io/v1alpha1", + Kind: types.SandboxClaimsKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "ci-claim", + Namespace: "ns-1", + }, + Status: extensionsv1alpha1.SandboxClaimStatus{ + SandboxStatus: extensionsv1alpha1.SandboxStatus{ + Name: "warm-pool-sandbox-abc", + }, + }, + } + claimObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(claim) + require.NoError(t, err) + claimUnstructured := &unstructured.Unstructured{Object: claimObj} + + adoptedSandboxUnstructured := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "agents.x-k8s.io/v1alpha1", + "kind": types.SandboxKind, + "metadata": map[string]interface{}{ + "name": "warm-pool-sandbox-abc", + "namespace": "ns-1", + "uid": "adopted-uid", + "creationTimestamp": metav1.Now().Format(time.RFC3339), + "annotations": map[string]interface{}{ + sandboxv1alpha1.SandboxPodNameAnnotation: "warm-pool-pod-abc", + }, + }, + "status": map[string]interface{}{ + "conditions": []interface{}{ + map[string]interface{}{ + "type": string(sandboxv1alpha1.SandboxConditionReady), + "status": string(metav1.ConditionTrue), + }, + }, + }, + }, + } + + dynamicClient := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()) + dynamicClient.PrependReactor("get", "*", func(action k8stesting.Action) (bool, runtime.Object, error) { + getAction := action.(k8stesting.GetAction) + switch { + case action.GetResource() == SandboxClaimGVR && getAction.GetName() == "ci-claim": + return true, claimUnstructured.DeepCopy(), nil + case action.GetResource() == SandboxGVR && getAction.GetName() == "warm-pool-sandbox-abc": + return true, adoptedSandboxUnstructured.DeepCopy(), nil + default: + return false, nil, nil + } + }) + + storeInst := &recordingStore{} + server := &Server{k8sClient: &K8sClient{}, storeClient: storeInst} + + var gotNamespace, gotSandboxName, gotPodName string + patches := gomonkey.NewPatches() + defer patches.Reset() + + patches.ApplyFunc(createSandboxClaim, func(_ context.Context, _ dynamic.Interface, _ *extensionsv1alpha1.SandboxClaim) error { + return nil + }) + patches.ApplyFunc(deleteSandboxClaim, func(_ context.Context, _ dynamic.Interface, _, _ string) error { + return nil + }) + patches.ApplyMethod(reflect.TypeOf((*K8sClient)(nil)), "GetSandboxPodIP", func(_ *K8sClient, _ context.Context, namespace, sandboxName, podName string) (string, error) { + gotNamespace = namespace + gotSandboxName = sandboxName + gotPodName = podName + return "10.0.0.10", nil + }) + patches.ApplyPrivateMethod(reflect.TypeOf(server), "waitForSandboxEntryPointsReady", func(_ *Server, _ context.Context, _ string, _ *sandboxEntry) error { + return nil + }) + + templateSandbox := &sandboxv1alpha1.Sandbox{ + ObjectMeta: metav1.ObjectMeta{ + Name: claim.Name, + Namespace: claim.Namespace, + }, + } + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + entry := makeEntry() + entry.Kind = types.SandboxClaimsKind + resp, err := server.createSandbox(ctx, dynamicClient, templateSandbox, claim, entry, nil) + + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, "ci-claim", resp.SandboxName) + require.Equal(t, "adopted-uid", resp.SandboxID) + require.Equal(t, "ns-1", gotNamespace) + require.Equal(t, "warm-pool-sandbox-abc", gotSandboxName) + require.Equal(t, "warm-pool-pod-abc", gotPodName) + + require.NotNil(t, storeInst.lastUpdated) + require.Equal(t, types.SandboxClaimsKind, storeInst.lastUpdated.Kind) + require.Equal(t, "ci-claim", storeInst.lastUpdated.Name) + require.Equal(t, "ns-1", storeInst.lastUpdated.SandboxNamespace) + require.Equal(t, "adopted-uid", storeInst.lastUpdated.SandboxID) + require.Equal(t, "10.0.0.10:8080", storeInst.lastUpdated.EntryPoints[0].Endpoint) +} + func newFakeServer() *Server { return &Server{ config: &Config{SandboxReadyProbeTimeout: 5 * time.Millisecond, SandboxReadyProbeInterval: time.Millisecond}, diff --git a/pkg/workloadmanager/k8s_client.go b/pkg/workloadmanager/k8s_client.go index 1ebf03879..602fb08f5 100644 --- a/pkg/workloadmanager/k8s_client.go +++ b/pkg/workloadmanager/k8s_client.go @@ -238,6 +238,20 @@ func createSandbox(ctx context.Context, client dynamic.Interface, sandbox *sandb }, nil } +// getSandbox gets a Sandbox using the provided dynamic client. +func getSandbox(ctx context.Context, client dynamic.Interface, namespace, sandboxName string) (*sandboxv1alpha1.Sandbox, error) { + obj, err := client.Resource(SandboxGVR).Namespace(namespace).Get(ctx, sandboxName, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get sandbox %s/%s: %w", namespace, sandboxName, err) + } + + sandbox := &sandboxv1alpha1.Sandbox{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, sandbox); err != nil { + return nil, fmt.Errorf("failed to convert sandbox %s/%s: %w", namespace, sandboxName, err) + } + return sandbox, nil +} + // createSandboxClaim creates a SandboxClaim using the provided dynamic client func createSandboxClaim(ctx context.Context, client dynamic.Interface, sandboxClaim *extensionsv1alpha1.SandboxClaim) error { // Convert to unstructured for dynamic client @@ -261,6 +275,20 @@ func createSandboxClaim(ctx context.Context, client dynamic.Interface, sandboxCl return nil } +// getSandboxClaim gets a SandboxClaim using the provided dynamic client. +func getSandboxClaim(ctx context.Context, client dynamic.Interface, namespace, sandboxClaimName string) (*extensionsv1alpha1.SandboxClaim, error) { + obj, err := client.Resource(SandboxClaimGVR).Namespace(namespace).Get(ctx, sandboxClaimName, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get sandbox claim %s/%s: %w", namespace, sandboxClaimName, err) + } + + sandboxClaim := &extensionsv1alpha1.SandboxClaim{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, sandboxClaim); err != nil { + return nil, fmt.Errorf("failed to convert sandbox claim %s/%s: %w", namespace, sandboxClaimName, err) + } + return sandboxClaim, 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( diff --git a/pkg/workloadmanager/sandbox_helper.go b/pkg/workloadmanager/sandbox_helper.go index 322b5d8d6..fb7c96cd5 100644 --- a/pkg/workloadmanager/sandbox_helper.go +++ b/pkg/workloadmanager/sandbox_helper.go @@ -48,6 +48,10 @@ var sandboxEntrypointDial = func(ctx context.Context, endpoint string, timeout t } func buildSandboxPlaceHolder(sandboxCR *sandboxv1alpha1.Sandbox, entry *sandboxEntry) *types.SandboxInfo { + createdAt := sandboxCR.GetCreationTimestamp().Time + if createdAt.IsZero() { + createdAt = time.Now() + } var expiresAt time.Time if sandboxCR.Spec.Lifecycle.ShutdownTime != nil { expiresAt = sandboxCR.Spec.Lifecycle.ShutdownTime.Time @@ -63,6 +67,7 @@ func buildSandboxPlaceHolder(sandboxCR *sandboxv1alpha1.Sandbox, entry *sandboxE SessionID: entry.SessionID, SandboxNamespace: sandboxCR.GetNamespace(), Name: sandboxCR.GetName(), + CreatedAt: createdAt, ExpiresAt: expiresAt, Status: "creating", IdleTimeout: metav1.Duration{Duration: idleTimeout}, diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index daba8c096..9bec44120 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -1181,57 +1181,80 @@ 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() + pods, err := ctx.listWarmPoolPods(namespace, codeInterpreterName) + if err != nil { + return 0, err + } - // List all pods in namespace - podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( - listCtx, - metav1.ListOptions{}, - ) + return len(pods), nil +} + +// getWarmPoolPodNames returns the names of warmpool pods for a given CodeInterpreter +func (ctx *e2eTestContext) getWarmPoolPodNames(namespace, codeInterpreterName string) ([]string, error) { + pods, err := ctx.listWarmPoolPods(namespace, codeInterpreterName) if err != nil { - return 0, fmt.Errorf("failed to list pods: %w", err) + return nil, err } - count := 0 - for _, pod := range podList.Items { - for _, owner := range pod.OwnerReferences { - if owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName { - count++ - break - } - } + podNames := make([]string, 0, len(pods)) + for _, pod := range pods { + podNames = append(podNames, pod.Name) } - return count, nil + return podNames, nil } -// getWarmPoolPodNames returns the names of warmpool pods for a given CodeInterpreter -func (ctx *e2eTestContext) getWarmPoolPodNames(namespace, codeInterpreterName string) ([]string, error) { +// listWarmPoolPods returns pods that still belong to the warm pool. +// +// agent-sandbox v0.3+ models the pool as SandboxWarmPool -> Sandbox -> Pod, +// while older releases used direct Pod ownership in some paths. Keep both +// checks so this e2e helper can validate either controller shape. +func (ctx *e2eTestContext) listWarmPoolPods(namespace, codeInterpreterName string) ([]corev1.Pod, error) { listCtx := context.Background() - podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( - listCtx, - metav1.ListOptions{}, - ) + sandboxList := &sandboxv1alpha1.SandboxList{} + if err := ctx.ctrlClient.List(listCtx, sandboxList, client.InNamespace(namespace)); err != nil { + return nil, fmt.Errorf("failed to list sandboxes: %w", err) + } + + warmPoolSandboxes := make(map[string]struct{}, len(sandboxList.Items)) + for _, sandbox := range sandboxList.Items { + if sandbox.DeletionTimestamp != nil { + continue + } + for _, owner := range sandbox.OwnerReferences { + if owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName { + warmPoolSandboxes[sandbox.Name] = struct{}{} + break + } + } + } + + podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List(listCtx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to list pods: %w", err) } - podNames := make([]string, 0, len(podList.Items)) + pods := make([]corev1.Pod, 0, len(podList.Items)) + seen := make(map[string]struct{}, len(podList.Items)) for _, pod := range podList.Items { - isWarmPool := false + if pod.DeletionTimestamp != nil { + continue + } for _, owner := range pod.OwnerReferences { - if owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName { - isWarmPool = true + _, ownedByWarmPoolSandbox := warmPoolSandboxes[owner.Name] + if (owner.Kind == ownerKindSandboxWarmPool && owner.Name == codeInterpreterName) || + (owner.Kind == "Sandbox" && ownedByWarmPoolSandbox) { + if _, ok := seen[pod.Name]; !ok { + pods = append(pods, pod) + seen[pod.Name] = struct{}{} + } break } } - if isWarmPool { - podNames = append(podNames, pod.Name) - } } - return podNames, nil + return pods, nil } // waitForWarmPoolReady waits for the warmpool to have the expected number of ready pods @@ -1263,36 +1286,21 @@ func (ctx *e2eTestContext) waitForWarmPoolReady(namespace, codeInterpreterName s // arePodsReady checks if warmpool pods are ready func (ctx *e2eTestContext) arePodsReady(namespace, codeInterpreterName string) (bool, error) { - listCtx := context.Background() - - podList, err := ctx.kubeClient.CoreV1().Pods(namespace).List( - listCtx, - metav1.ListOptions{}, - ) + pods, err := ctx.listWarmPoolPods(namespace, codeInterpreterName) if err != nil { - return false, fmt.Errorf("failed to list pods: %w", err) + return false, err } warmPoolPods := 0 readyPods := 0 - for _, pod := range podList.Items { - isWarmPool := false - for _, owner := range pod.OwnerReferences { - if owner.Kind == "SandboxWarmPool" && owner.Name == codeInterpreterName { - isWarmPool = true + for _, pod := range pods { + warmPoolPods++ + for _, condition := range pod.Status.Conditions { + if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { + readyPods++ break } } - - if isWarmPool { - warmPoolPods++ - for _, condition := range pod.Status.Conditions { - if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { - readyPods++ - break - } - } - } } if warmPoolPods == 0 {