[navi] add discovery init mesh config v1 (#768)
diff --git a/go.mod b/go.mod
index 0778740..79b2cd0 100644
--- a/go.mod
+++ b/go.mod
@@ -30,6 +30,7 @@
github.com/docker/docker-credential-helpers v0.9.3
github.com/docker/go-connections v0.5.0
github.com/fatih/color v1.18.0
+ github.com/fsnotify/fsnotify v1.9.0
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.13.1
github.com/golang/protobuf v1.5.4
@@ -43,6 +44,7 @@
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/tmc/langchaingo v0.1.13
+ go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.40.0
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
golang.org/x/net v0.42.0
@@ -61,7 +63,6 @@
)
require (
- cel.dev/expr v0.24.0 // indirect
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
@@ -82,7 +83,6 @@
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
- github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/apex/log v1.9.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
@@ -100,21 +100,17 @@
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230522190001-adf1bafd791a // indirect
- github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/buildpacks/imgutil v0.0.0-20230626185301-726f02e4225c // indirect
github.com/buildpacks/lifecycle v0.17.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
- github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
- github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dgraph-io/ristretto v0.0.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
@@ -123,33 +119,25 @@
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
- github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8 // indirect
- github.com/envoyproxy/go-control-plane/envoy v1.32.5-0.20250627145903-197b96a9c7f8 // indirect
- github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
- github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.6.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.4.3 // indirect
- github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
- github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.3 // indirect
- github.com/google/cel-go v0.25.0 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/goph/emperror v0.17.2 // indirect
- github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -163,12 +151,6 @@
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
- github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
- github.com/lestrrat-go/blackmagic v1.0.3 // indirect
- github.com/lestrrat-go/httpcc v1.0.1 // indirect
- github.com/lestrrat-go/iter v1.0.2 // indirect
- github.com/lestrrat-go/jwx v1.2.31 // indirect
- github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
@@ -184,7 +166,6 @@
github.com/moby/buildkit v0.21.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
- github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
@@ -195,8 +176,9 @@
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nikolalohinski/gonja v1.5.3 // indirect
+ github.com/onsi/ginkgo/v2 v2.23.4 // indirect
+ github.com/onsi/gomega v1.37.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runc v1.1.7 // indirect
@@ -208,7 +190,6 @@
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkoukk/tiktoken-go v0.1.6 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca // indirect
- github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
@@ -223,7 +204,6 @@
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.8.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
- github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
@@ -235,18 +215,7 @@
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yargevad/filepathx v1.0.0 // indirect
- go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/otel v1.36.0 // indirect
- go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect
- go.opentelemetry.io/otel/metric v1.36.0 // indirect
- go.opentelemetry.io/otel/sdk v1.36.0 // indirect
- go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
- go.opentelemetry.io/otel/trace v1.36.0 // indirect
- go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
- go.uber.org/atomic v1.11.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.2 // indirect
go.yaml.in/yaml/v3 v3.0.3 // indirect
golang.org/x/mod v0.25.0 // indirect
@@ -261,21 +230,15 @@
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
- gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
- istio.io/client-go v1.26.0-alpha.0.0.20250731213105-2f06e3d976fe // indirect
- k8s.io/apiserver v0.33.2 // indirect
k8s.io/cli-runtime v0.33.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
- sigs.k8s.io/gateway-api v1.3.0 // indirect
- sigs.k8s.io/gateway-api-inference-extension v0.5.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
- sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
)
diff --git a/go.sum b/go.sum
index 82d0239..70985de 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,3 @@
-cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
-cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY=
cloud.google.com/go v0.114.0/go.mod h1:ZV9La5YYxctro1HTPug5lXH/GefROyW8PPD4T8n9J8E=
@@ -89,8 +87,6 @@
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
-github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
-github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
@@ -164,8 +160,6 @@
github.com/buildpacks/pack v0.30.0/go.mod h1:ZtkyUJKcTdWgEDFi0KOmtHQAOkeQeOeJ2wre1+0ipnA=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -188,8 +182,6 @@
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
-github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
-github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
@@ -213,8 +205,6 @@
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs=
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
@@ -249,13 +239,7 @@
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8 h1:KXgXPtBofHkRHr+8dO058dGZnLHapW7m0yJEgSYdAFA=
-github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8/go.mod h1:Nx/YcyEeIcgjT13QwKHdcPmS060urxZ835MeO8cLOrg=
-github.com/envoyproxy/go-control-plane/envoy v1.32.5-0.20250627145903-197b96a9c7f8 h1:/F9jLyfDeNr4iZxyibtKlZxCDqCFEhoYiLdc9VOZT2E=
-github.com/envoyproxy/go-control-plane/envoy v1.32.5-0.20250627145903-197b96a9c7f8/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
-github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
@@ -292,7 +276,6 @@
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-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=
@@ -305,13 +288,10 @@
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
-github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -339,8 +319,6 @@
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/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
-github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
github.com/google/generative-ai-go v0.15.1 h1:n8aQUpvhPOlGVuM2DRkJ2jvx04zpp42B778AROJa+pQ=
github.com/google/generative-ai-go v0.15.1/go.mod h1:AAucpWZjXsDKhQYWvCYuP6d0yB1kX998pJlOW1rAesw=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
@@ -360,8 +338,8 @@
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
-github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
+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/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@@ -380,13 +358,8 @@
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
-github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -437,23 +410,8 @@
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
-github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
-github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
-github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
-github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
-github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
-github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
-github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
-github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
-github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
-github.com/lestrrat-go/jwx v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54=
-github.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c=
-github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
-github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
-github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -494,8 +452,6 @@
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
-github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
-github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
@@ -519,13 +475,10 @@
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c=
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -623,8 +576,6 @@
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
-github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -638,7 +589,6 @@
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -694,16 +644,10 @@
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
-go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
-go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
@@ -712,8 +656,6 @@
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
-go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
-go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg=
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -722,10 +664,6 @@
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
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=
-go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
@@ -881,8 +819,6 @@
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -904,24 +840,16 @@
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
istio.io/api v1.26.3 h1:/TiA7bJi24yBQSgpLy5vHhFkobf4DWS1L+CuUxNk4os=
istio.io/api v1.26.3/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg=
-istio.io/client-go v1.26.0-alpha.0.0.20250731213105-2f06e3d976fe h1:A3m5j1PRh1J/Y1YkDHP7wqaaIoxdK0NuFFNewwEqNHQ=
-istio.io/client-go v1.26.0-alpha.0.0.20250731213105-2f06e3d976fe/go.mod h1:QE8FEo9JtZNOrXjzYTsKq8kzb9f3xt/Mkp0XXUvg5Hc=
-istio.io/istio v0.0.0-20250801115405-9aa2567b5daf h1:nAxrB4KVuux87MkeVn2wYtXDdgTNQdmRsg7qlSk/lvE=
-istio.io/istio v0.0.0-20250801115405-9aa2567b5daf/go.mod h1:n2Rl5PCQ3uQOAFYZqeXTA4z/oVvc1Fj1VfkBs+qRS2Q=
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8=
k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8=
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/apiserver v0.33.2 h1:KGTRbxn2wJagJowo29kKBp4TchpO1DRO3g+dB/KOJN4=
-k8s.io/apiserver v0.33.2/go.mod h1:9qday04wEAMLPWWo9AwqCZSiIn3OYSZacDyu/AcoM/M=
k8s.io/cli-runtime v0.33.2 h1:koNYQKSDdq5AExa/RDudXMhhtFasEg48KLS2KSAU74Y=
k8s.io/cli-runtime v0.33.2/go.mod h1:gnhsAWpovqf1Zj5YRRBBU7PFsRc6NkEkwYNQE+mXL88=
k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
-k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
-k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
@@ -932,20 +860,12 @@
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.1 h1:Cf+ed5N8038zbsaXFO7mKQDi/+VcSRafb0jM84KX5so=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.1/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
-sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M=
-sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk=
-sigs.k8s.io/gateway-api-inference-extension v0.5.0 h1:bYtXffUF1WUUFT2gYXaQBXIEXxXq/ZZLP9gqQweTrBI=
-sigs.k8s.io/gateway-api-inference-extension v0.5.0/go.mod h1:lki0jx1qysZSZT4Ai2BxuAcpx6G8g5oBgOGuuJzjy/k=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
-sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c h1:F7hIEutAxtXDOQX9NXFdvhWmWETu2zmUPHuPPcAez7g=
-sigs.k8s.io/mcs-api v0.1.1-0.20240624222831-d7001fe1d21c/go.mod h1:DPFniRsBzCeLB4ANjlPEvQQt9QGIX489d1faK+GPvI4=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
diff --git a/navigator/pkg/bootstrap/mesh.go b/navigator/pkg/bootstrap/mesh.go
index 224f79e..ffe293c 100644
--- a/navigator/pkg/bootstrap/mesh.go
+++ b/navigator/pkg/bootstrap/mesh.go
@@ -61,9 +61,9 @@
if s.kubeClient == nil {
return nil
}
- // configMapName := getMeshConfigMapName("")
- // primary := kubemesh.NewConfigMapSource(s.kubeClient, args.Namespace, configMapName, cmKey, opts)
- return nil
+ configMapName := getMeshConfigMapName("")
+ primary := kubemesh.NewConfigMapSource(s.kubeClient, args.Namespace, configMapName, cmKey, opts)
+ return toSources(primary, userMeshConfig)
}
func toSources(base meshwatcher.MeshConfigSource, user *meshwatcher.MeshConfigSource) []meshwatcher.MeshConfigSource {
diff --git a/navigator/pkg/features/navigator.go b/navigator/pkg/features/navigator.go
index 7b2d29a..6db8bf7 100644
--- a/navigator/pkg/features/navigator.go
+++ b/navigator/pkg/features/navigator.go
@@ -31,6 +31,9 @@
"If enabled, addition runtime asserts will be performed. "+
"These checks are both expensive and panic on failure. As a result, this should be used only for testing.",
).Get()
+ InformerWatchNamespace = env.Register("DUBBO_WATCH_NAMESPACE", "",
+ "If set, limit Kubernetes watches to a single namespace. "+
+ "Warning: only a single namespace can be set.").Get()
ClusterName = env.Register("CLUSTER_ID", constants.DefaultClusterName,
"Defines the cluster and service registry that this Dubbod instance belongs to").Get()
)
diff --git a/pkg/config/mesh/kubemesh/watcher.go b/pkg/config/mesh/kubemesh/watcher.go
index f671366..18ef0db 100644
--- a/pkg/config/mesh/kubemesh/watcher.go
+++ b/pkg/config/mesh/kubemesh/watcher.go
@@ -18,10 +18,16 @@
package kubemesh
import (
+ "fmt"
"github.com/apache/dubbo-kubernetes/pkg/kube"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/kclient"
"github.com/apache/dubbo-kubernetes/pkg/kube/krt"
"github.com/apache/dubbo-kubernetes/pkg/mesh/meshwatcher"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/types"
)
const (
@@ -29,8 +35,19 @@
MeshNetworksKey = "meshNetworks"
)
-func NewConfigMapSource(client kube.Client, namespace, name, key string, opts krt.OptionsBuilder) meshwatcher.MeshConfigResource {
- return meshwatcher.MeshConfigResource{}
+// NewConfigMapSource builds a MeshConfigSource reading from ConfigMap "name" with key "key".
+func NewConfigMapSource(client kube.Client, namespace, name, key string, opts krt.OptionsBuilder) meshwatcher.MeshConfigSource {
+ clt := kclient.NewFiltered[*v1.ConfigMap](client, kclient.Filter{
+ Namespace: namespace,
+ FieldSelector: fields.OneTermEqualSelector(metav1.ObjectNameField, name).String(),
+ })
+ cms := krt.WrapClient(clt, opts.WithName("ConfigMap_"+name)...)
+ clt.Start(opts.Stop())
+ cmKey := types.NamespacedName{Namespace: namespace, Name: name}.String()
+ return krt.NewSingleton(func(ctx krt.HandlerContext) *string {
+ cm := ptr.Flatten(krt.FetchOne(ctx, cms, krt.FilterKey(cmKey)))
+ return meshConfigMapData(cm, key)
+ }, opts.WithName(fmt.Sprintf("ConfigMap_%s_%s", name, key))...)
}
func meshConfigMapData(cm *v1.ConfigMap, key string) *string {
diff --git a/pkg/config/schema/gvr/resource.gen.go b/pkg/config/schema/gvr/resource.gen.go
index f5f1bba..beb02e9 100644
--- a/pkg/config/schema/gvr/resource.gen.go
+++ b/pkg/config/schema/gvr/resource.gen.go
@@ -18,3 +18,9 @@
Service = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}
ServiceAccount = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}
)
+
+func IsClusterScoped(g schema.GroupVersionResource) bool {
+ switch g {
+ }
+ return false
+}
diff --git a/pkg/config/schema/kubeclient/common.go b/pkg/config/schema/kubeclient/common.go
new file mode 100644
index 0000000..5fbd1b4
--- /dev/null
+++ b/pkg/config/schema/kubeclient/common.go
@@ -0,0 +1,187 @@
+package kubeclient
+
+import (
+ "context"
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/config/schema/kubetypes"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/informerfactory"
+ ktypes "github.com/apache/dubbo-kubernetes/pkg/kube/kubetypes"
+ "github.com/apache/dubbo-kubernetes/pkg/typemap"
+ kubeext "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/dynamic"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/metadata"
+ "k8s.io/client-go/tools/cache"
+)
+
+type ClientGetter interface {
+ // Ext returns the API extensions client.
+ Ext() kubeext.Interface
+
+ // Kube returns the core kube client
+ Kube() kubernetes.Interface
+
+ // Dynamic client.
+ Dynamic() dynamic.Interface
+
+ // Metadata returns the Metadata kube client.
+ Metadata() metadata.Interface
+
+ // Informers returns an informer factory.
+ Informers() informerfactory.InformerFactory
+}
+
+type TypeRegistration[T runtime.Object] interface {
+ kubetypes.RegisterType[T]
+
+ // ListWatchFunc provides the necessary methods for list and
+ // watch for the informer
+ ListWatch(c ClientGetter, opts ktypes.InformerOptions) cache.ListerWatcher
+}
+
+var registerTypes = typemap.NewTypeMap()
+
+func GetInformerFiltered[T runtime.Object](
+ c ClientGetter,
+ opts ktypes.InformerOptions,
+ gvr schema.GroupVersionResource,
+) informerfactory.StartableInformer {
+ reg := typemap.Get[TypeRegistration[T]](registerTypes)
+ if reg != nil {
+ // This is registered type
+ tr := *reg
+ return c.Informers().InformerFor(tr.GetGVR(), opts, func() cache.SharedIndexInformer {
+ inf := cache.NewSharedIndexInformer(
+ tr.ListWatch(c, opts),
+ tr.Object(),
+ 0,
+ cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
+ )
+ setupInformer(opts, inf)
+ return inf
+ })
+ }
+ return GetInformerFilteredFromGVR(c, opts, gvr)
+}
+
+func setupInformer(opts ktypes.InformerOptions, inf cache.SharedIndexInformer) {
+ if opts.ObjectTransform != nil {
+ _ = inf.SetTransform(opts.ObjectTransform)
+ } else {
+ _ = inf.SetTransform(stripUnusedFields)
+ }
+}
+
+func GetInformerFilteredFromGVR(c ClientGetter, opts ktypes.InformerOptions, g schema.GroupVersionResource) informerfactory.StartableInformer {
+ switch opts.InformerType {
+ case ktypes.DynamicInformer:
+ return getInformerFilteredDynamic(c, opts, g)
+ case ktypes.MetadataInformer:
+ return getInformerFilteredMetadata(c, opts, g)
+ default:
+ return getInformerFiltered(c, opts, g)
+ }
+}
+
+func getInformerFilteredDynamic(c ClientGetter, opts ktypes.InformerOptions, g schema.GroupVersionResource) informerfactory.StartableInformer {
+ return c.Informers().InformerFor(g, opts, func() cache.SharedIndexInformer {
+ inf := cache.NewSharedIndexInformerWithOptions(
+ &cache.ListWatch{
+ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return c.Dynamic().Resource(g).Namespace(opts.Namespace).List(context.Background(), options)
+ },
+ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return c.Dynamic().Resource(g).Namespace(opts.Namespace).Watch(context.Background(), options)
+ },
+ },
+ &unstructured.Unstructured{},
+ cache.SharedIndexInformerOptions{
+ ResyncPeriod: 0,
+ Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
+ ObjectDescription: g.String(),
+ },
+ )
+ setupInformer(opts, inf)
+ return inf
+ })
+}
+
+func getInformerFilteredMetadata(c ClientGetter, opts ktypes.InformerOptions, g schema.GroupVersionResource) informerfactory.StartableInformer {
+ return c.Informers().InformerFor(g, opts, func() cache.SharedIndexInformer {
+ inf := cache.NewSharedIndexInformerWithOptions(
+ &cache.ListWatch{
+ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return c.Metadata().Resource(g).Namespace(opts.Namespace).List(context.Background(), options)
+ },
+ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return c.Metadata().Resource(g).Namespace(opts.Namespace).Watch(context.Background(), options)
+ },
+ },
+ &metav1.PartialObjectMetadata{},
+ cache.SharedIndexInformerOptions{
+ ResyncPeriod: 0,
+ Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
+ ObjectDescription: g.String(),
+ },
+ )
+ setupInformer(opts, inf)
+ return inf
+ })
+}
+
+func stripUnusedFields(obj any) (any, error) {
+ t, ok := obj.(metav1.ObjectMetaAccessor)
+ if !ok {
+ // shouldn't happen
+ return obj, nil
+ }
+ // ManagedFields is large and we never use it
+ t.GetObjectMeta().SetManagedFields(nil)
+ return obj, nil
+}
+
+func getInformerFiltered(c ClientGetter, opts ktypes.InformerOptions, g schema.GroupVersionResource) informerfactory.StartableInformer {
+ var l func(options metav1.ListOptions) (runtime.Object, error)
+ var w func(options metav1.ListOptions) (watch.Interface, error)
+ return c.Informers().InformerFor(g, opts, func() cache.SharedIndexInformer {
+ inf := cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return l(options)
+ },
+ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+ options.FieldSelector = opts.FieldSelector
+ options.LabelSelector = opts.LabelSelector
+ return w(options)
+ },
+ },
+ gvrToObject(g),
+ 0,
+ cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
+ )
+ setupInformer(opts, inf)
+ return inf
+ })
+}
+
+func gvrToObject(g schema.GroupVersionResource) runtime.Object {
+ switch g {
+ default:
+ panic(fmt.Sprintf("Unknown type %v", g))
+ }
+}
diff --git a/pkg/config/schema/kubeclient/resource.gen.go b/pkg/config/schema/kubeclient/resource.gen.go
new file mode 100644
index 0000000..b44b608
--- /dev/null
+++ b/pkg/config/schema/kubeclient/resource.gen.go
@@ -0,0 +1,15 @@
+package kubeclient
+
+import (
+ "fmt"
+ ktypes "github.com/apache/dubbo-kubernetes/pkg/kube/kubetypes"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+func GetWriteClient[T runtime.Object](c ClientGetter, namespace string) ktypes.WriteAPI[T] {
+ switch any(ptr.Empty[T]()).(type) {
+ default:
+ panic(fmt.Sprintf("Unknown type %T", ptr.Empty[T]()))
+ }
+}
diff --git a/pkg/config/schema/kubetypes/common.go b/pkg/config/schema/kubetypes/common.go
new file mode 100644
index 0000000..5864cc4
--- /dev/null
+++ b/pkg/config/schema/kubetypes/common.go
@@ -0,0 +1,38 @@
+package kubetypes
+
+import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/config"
+ "github.com/apache/dubbo-kubernetes/pkg/config/schema/gvk"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "github.com/apache/dubbo-kubernetes/pkg/typemap"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+func MustGVKFromType[T runtime.Object]() (cfg config.GroupVersionKind) {
+ if gvk, ok := getGvk(ptr.Empty[T]()); ok {
+ return gvk
+ }
+ if rp := typemap.Get[RegisterType[T]](registeredTypes); rp != nil {
+ return (*rp).GetGVK()
+ }
+ panic("unknown kind: " + cfg.String())
+}
+
+func MustToGVR[T runtime.Object](cfg config.GroupVersionKind) schema.GroupVersionResource {
+ if r, ok := gvk.ToGVR(cfg); ok {
+ return r
+ }
+ if rp := typemap.Get[RegisterType[T]](registeredTypes); rp != nil {
+ return (*rp).GetGVR()
+ }
+ panic("unknown kind: " + cfg.String())
+}
+
+var registeredTypes = typemap.NewTypeMap()
+
+type RegisterType[T runtime.Object] interface {
+ GetGVK() config.GroupVersionKind
+ GetGVR() schema.GroupVersionResource
+ Object() T
+}
diff --git a/pkg/config/schema/kubetypes/resources.gen.go b/pkg/config/schema/kubetypes/resources.gen.go
new file mode 100644
index 0000000..7458225
--- /dev/null
+++ b/pkg/config/schema/kubetypes/resources.gen.go
@@ -0,0 +1,12 @@
+package kubetypes
+
+import (
+ "github.com/apache/dubbo-kubernetes/operator/pkg/config"
+)
+
+func getGvk(obj any) (config.GroupVersionKind, bool) {
+ switch obj.(type) {
+ default:
+ return config.GroupVersionKind{}, false
+ }
+}
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index b49a8c9..82babef 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -20,14 +20,19 @@
import (
"fmt"
"github.com/apache/dubbo-kubernetes/operator/pkg/config"
+ "github.com/apache/dubbo-kubernetes/pkg/cluster"
"github.com/apache/dubbo-kubernetes/pkg/kube/collections"
"github.com/apache/dubbo-kubernetes/pkg/kube/informerfactory"
"github.com/apache/dubbo-kubernetes/pkg/lazy"
+ "github.com/apache/dubbo-kubernetes/pkg/sleep"
kubeExtClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
kubeVersion "k8s.io/apimachinery/pkg/version"
+ "k8s.io/client-go/metadata"
+ "k8s.io/client-go/tools/cache"
+
// "k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -47,15 +52,23 @@
dynamic dynamic.Interface
kube kubernetes.Interface
mapper meta.ResettableRESTMapper
+ metadata metadata.Interface
http *http.Client
+ clusterID cluster.ID
}
type Client interface {
- // Ext returns the API extensions client.
Ext() kubeExtClient.Interface
- // Kube returns the core kube client
Kube() kubernetes.Interface
+
+ ClusterID() cluster.ID
+
+ Dynamic() dynamic.Interface
+
+ Metadata() metadata.Interface
+
+ Informers() informerfactory.InformerFactory
}
type CLIClient interface {
@@ -135,6 +148,22 @@
return c.kube
}
+func (c *client) ClusterID() cluster.ID {
+ return c.clusterID
+}
+
+func (c *client) Dynamic() dynamic.Interface {
+ return c.dynamic
+}
+
+func (c *client) Metadata() metadata.Interface {
+ return c.metadata
+}
+
+func (c *client) Informers() informerfactory.InformerFactory {
+ return c.informerFactory
+}
+
func (c *client) DynamicClientFor(gvk schema.GroupVersionKind, obj *unstructured.Unstructured, namespace string) (dynamic.ResourceInterface, error) {
gvr, namespaced := c.bestEffortToGVR(gvk, obj, namespace)
var dr dynamic.ResourceInterface
@@ -179,3 +208,52 @@
return client
}
}
+
+func WithCluster(id cluster.ID) ClientOption {
+ return func(c CLIClient) CLIClient {
+ client := c.(*client)
+ client.clusterID = id
+ return client
+ }
+}
+
+func WaitForCacheSync(name string, stop <-chan struct{}, cacheSyncs ...cache.InformerSynced) (r bool) {
+ _ = time.Now()
+ maximum := time.Millisecond * 100
+ delay := time.Millisecond
+ f := func() bool {
+ for _, syncFunc := range cacheSyncs {
+ if !syncFunc() {
+ return false
+ }
+ }
+ return true
+ }
+ attempt := 0
+ defer func() {
+ if r {
+ } else {
+ }
+ }()
+ for {
+ select {
+ case <-stop:
+ return false
+ default:
+ }
+ attempt++
+ res := f()
+ if res {
+ return true
+ }
+ delay *= 2
+ if delay > maximum {
+ delay = maximum
+ }
+ if attempt%50 == 0 {
+ }
+ if !sleep.Until(stop, delay) {
+ return false
+ }
+ }
+}
diff --git a/pkg/kube/controllers/common.go b/pkg/kube/controllers/common.go
index dc460a4..c9f2302 100644
--- a/pkg/kube/controllers/common.go
+++ b/pkg/kube/controllers/common.go
@@ -1,5 +1,10 @@
package controllers
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
type EventType int
const (
@@ -27,3 +32,13 @@
}
return out
}
+
+type ComparableObject interface {
+ comparable
+ Object
+}
+
+type Object interface {
+ metav1.Object
+ runtime.Object
+}
diff --git a/pkg/kube/kclient/client.go b/pkg/kube/kclient/client.go
new file mode 100644
index 0000000..6442920
--- /dev/null
+++ b/pkg/kube/kclient/client.go
@@ -0,0 +1,209 @@
+package kclient
+
+import (
+ "context"
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/navigator/pkg/features"
+ dubbogvr "github.com/apache/dubbo-kubernetes/pkg/config/schema/gvr"
+ "github.com/apache/dubbo-kubernetes/pkg/config/schema/kubeclient"
+ types "github.com/apache/dubbo-kubernetes/pkg/config/schema/kubetypes"
+ "github.com/apache/dubbo-kubernetes/pkg/kube"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/informerfactory"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/kubetypes"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "github.com/apache/dubbo-kubernetes/pkg/util/sets"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ klabels "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ apitypes "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/tools/cache"
+ "sync"
+)
+
+type fullClient[T controllers.Object] struct {
+ writeClient[T]
+ Informer[T]
+}
+
+type writeClient[T controllers.Object] struct {
+ client kube.Client
+}
+
+func NewFiltered[T controllers.ComparableObject](c kube.Client, filter Filter) Client[T] {
+ gvr := types.MustToGVR[T](types.MustGVKFromType[T]())
+ inf := kubeclient.GetInformerFiltered[T](c, ToOpts(c, gvr, filter), gvr)
+ return &fullClient[T]{
+ writeClient: writeClient[T]{client: c},
+ Informer: newInformerClient[T](gvr, inf, filter),
+ }
+}
+
+type Filter = kubetypes.Filter
+
+func ToOpts(c kube.Client, gvr schema.GroupVersionResource, filter Filter) kubetypes.InformerOptions {
+ ns := filter.Namespace
+ if !dubbogvr.IsClusterScoped(gvr) && ns == "" {
+ ns = features.InformerWatchNamespace
+ }
+ return kubetypes.InformerOptions{
+ LabelSelector: filter.LabelSelector,
+ FieldSelector: filter.FieldSelector,
+ Namespace: ns,
+ ObjectTransform: filter.ObjectTransform,
+ Cluster: c.ClusterID(),
+ }
+}
+
+type handlerRegistration struct {
+ registration cache.ResourceEventHandlerRegistration
+ // handler is the actual handler. Note this does NOT have the filtering applied.
+ handler cache.ResourceEventHandler
+}
+
+type informerClient[T controllers.Object] struct {
+ informer cache.SharedIndexInformer
+ startInformer func(stopCh <-chan struct{})
+ filter func(t any) bool
+
+ handlerMu sync.RWMutex
+ registeredHandlers []handlerRegistration
+}
+
+func newInformerClient[T controllers.ComparableObject](
+ gvr schema.GroupVersionResource,
+ inf informerfactory.StartableInformer,
+ filter Filter,
+) Informer[T] {
+ ic := &informerClient[T]{
+ informer: inf.Informer,
+ startInformer: inf.Start,
+ }
+ if filter.ObjectFilter != nil {
+ applyDynamicFilter(filter, gvr, ic)
+ }
+ return ic
+}
+
+func applyDynamicFilter[T controllers.ComparableObject](filter Filter, gvr schema.GroupVersionResource, ic *informerClient[T]) {
+ if filter.ObjectFilter != nil {
+ ic.filter = filter.ObjectFilter.Filter
+ filter.ObjectFilter.AddHandler(func(added, removed sets.String) {
+ ic.handlerMu.RLock()
+ defer ic.handlerMu.RUnlock()
+ if gvr == dubbogvr.Namespace {
+ for _, item := range ic.ListUnfiltered(metav1.NamespaceAll, klabels.Everything()) {
+ if !added.Contains(item.GetName()) {
+ continue
+ }
+ for _, c := range ic.registeredHandlers {
+ c.handler.OnAdd(item, false)
+ }
+ }
+ } else {
+ for ns := range added {
+ for _, item := range ic.ListUnfiltered(ns, klabels.Everything()) {
+ for _, c := range ic.registeredHandlers {
+ c.handler.OnAdd(item, false)
+ }
+ }
+ }
+ for ns := range removed {
+ for _, item := range ic.ListUnfiltered(ns, klabels.Everything()) {
+ for _, c := range ic.registeredHandlers {
+ c.handler.OnDelete(item)
+ }
+ }
+ }
+ }
+ })
+ }
+}
+
+func (n *informerClient[T]) ListUnfiltered(namespace string, selector klabels.Selector) []T {
+ var res []T
+ err := cache.ListAllByNamespace(n.informer.GetIndexer(), namespace, selector, func(i any) {
+ cast := i.(T)
+ res = append(res, cast)
+ })
+
+ // Should never happen
+ if err != nil && features.EnableUnsafeAssertions {
+ fmt.Printf("lister returned err for %v: %v", namespace, err)
+ }
+ return res
+}
+
+func (n *informerClient[T]) Start(stopCh <-chan struct{}) {
+ n.startInformer(stopCh)
+}
+
+func (n *informerClient[T]) Get(name, namespace string) T {
+ obj, exists, err := n.informer.GetIndexer().GetByKey(keyFunc(name, namespace))
+ if err != nil {
+ return ptr.Empty[T]()
+ }
+ if !exists {
+ return ptr.Empty[T]()
+ }
+ cast := obj.(T)
+ if !n.applyFilter(cast) {
+ return ptr.Empty[T]()
+ }
+ return cast
+}
+
+func keyFunc(name, namespace string) string {
+ if len(namespace) == 0 {
+ return name
+ }
+ return namespace + "/" + name
+}
+
+func (n *informerClient[T]) applyFilter(t T) bool {
+ if n.filter == nil {
+ return true
+ }
+ return n.filter(t)
+}
+
+func (n *writeClient[T]) Create(object T) (T, error) {
+ api := kubeclient.GetWriteClient[T](n.client, object.GetNamespace())
+ return api.Create(context.Background(), object, metav1.CreateOptions{})
+}
+
+func (n *writeClient[T]) Update(object T) (T, error) {
+ api := kubeclient.GetWriteClient[T](n.client, object.GetNamespace())
+ return api.Update(context.Background(), object, metav1.UpdateOptions{})
+}
+
+func (n *writeClient[T]) Patch(name, namespace string, pt apitypes.PatchType, data []byte) (T, error) {
+ api := kubeclient.GetWriteClient[T](n.client, namespace)
+ return api.Patch(context.Background(), name, pt, data, metav1.PatchOptions{})
+}
+
+func (n *writeClient[T]) PatchStatus(name, namespace string, pt apitypes.PatchType, data []byte) (T, error) {
+ api := kubeclient.GetWriteClient[T](n.client, namespace)
+ return api.Patch(context.Background(), name, pt, data, metav1.PatchOptions{}, "status")
+}
+
+func (n *writeClient[T]) ApplyStatus(name, namespace string, pt apitypes.PatchType, data []byte, fieldManager string) (T, error) {
+ api := kubeclient.GetWriteClient[T](n.client, namespace)
+ return api.Patch(context.Background(), name, pt, data, metav1.PatchOptions{
+ Force: ptr.Of(true),
+ FieldManager: fieldManager,
+ }, "status")
+}
+
+func (n *writeClient[T]) UpdateStatus(object T) (T, error) {
+ api, ok := kubeclient.GetWriteClient[T](n.client, object.GetNamespace()).(kubetypes.WriteStatusAPI[T])
+ if !ok {
+ return ptr.Empty[T](), fmt.Errorf("%T does not support UpdateStatus", object)
+ }
+ return api.UpdateStatus(context.Background(), object, metav1.UpdateOptions{})
+}
+
+func (n *writeClient[T]) Delete(name, namespace string) error {
+ api := kubeclient.GetWriteClient[T](n.client, namespace)
+ return api.Delete(context.Background(), name, metav1.DeleteOptions{})
+}
diff --git a/pkg/kube/kclient/interfaces.go b/pkg/kube/kclient/interfaces.go
new file mode 100644
index 0000000..819f002
--- /dev/null
+++ b/pkg/kube/kclient/interfaces.go
@@ -0,0 +1,43 @@
+package kclient
+
+import (
+ "github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
+ klabels "k8s.io/apimachinery/pkg/labels"
+ apitypes "k8s.io/apimachinery/pkg/types"
+)
+
+type Reader[T controllers.Object] interface {
+ // Get looks up an object by name and namespace. If it does not exist, nil is returned
+ Get(name, namespace string) T
+ // List looks up an object by namespace and labels.
+ // Use metav1.NamespaceAll and klabels.Everything() to select everything.
+ List(namespace string, selector klabels.Selector) []T
+}
+
+type Writer[T controllers.Object] interface {
+ // Create creates a resource, returning the newly applied resource.
+ Create(object T) (T, error)
+ // Update updates a resource, returning the newly applied resource.
+ Update(object T) (T, error)
+ // UpdateStatus updates a resource's status, returning the newly applied resource.
+ UpdateStatus(object T) (T, error)
+ // Patch patches the resource, returning the newly applied resource.
+ Patch(name, namespace string, pt apitypes.PatchType, data []byte) (T, error)
+ // PatchStatus patches the resource's status, returning the newly applied resource.
+ PatchStatus(name, namespace string, pt apitypes.PatchType, data []byte) (T, error)
+ // ApplyStatus does a server-side Apply of the the resource's status, returning the newly applied resource.
+ // fieldManager is a required field; see https://kubernetes.io/docs/reference/using-api/server-side-apply/#managers.
+ ApplyStatus(name, namespace string, pt apitypes.PatchType, data []byte, fieldManager string) (T, error)
+ // Delete removes a resource.
+ Delete(name, namespace string) error
+}
+
+type Informer[T controllers.Object] interface {
+ Start(stop <-chan struct{})
+}
+
+type Client[T controllers.Object] interface {
+ Reader[T]
+ Writer[T]
+ Informer[T]
+}
diff --git a/pkg/kube/krt/collection.go b/pkg/kube/krt/collection.go
new file mode 100644
index 0000000..0aaccc3
--- /dev/null
+++ b/pkg/kube/krt/collection.go
@@ -0,0 +1,178 @@
+package krt
+
+import (
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "github.com/apache/dubbo-kubernetes/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "sync"
+)
+
+type indexedDependencyType uint8
+
+type Key[O any] string
+
+const (
+ unknownIndexType indexedDependencyType = iota
+ indexType indexedDependencyType = iota
+ getKeyType indexedDependencyType = iota
+)
+
+type indexedDependency struct {
+ id collectionUID
+ key string
+ typ indexedDependencyType
+}
+
+type extractorKey struct {
+ uid collectionUID
+ filterUID collectionUID
+ typ indexedDependencyType
+}
+
+type objectKeyExtractor = func(o any) []string
+
+type multiIndex[I, O any] struct {
+ outputs map[Key[O]]O
+ inputs map[Key[I]]I
+ mappings map[Key[I]]sets.Set[Key[O]]
+}
+
+type collectionIndex[I, O any] struct {
+ extract func(o O) []string
+ index map[string]sets.Set[Key[O]]
+ parent *manyCollection[I, O]
+}
+
+type dependencyState[I any] struct {
+ collectionDependencies sets.Set[collectionUID]
+ objectDependencies map[Key[I]][]*dependency
+ indexedDependencies map[indexedDependency]sets.Set[Key[I]]
+ indexedDependenciesExtractor map[extractorKey]objectKeyExtractor
+}
+
+type handlerSet[O any] struct {
+ mu sync.RWMutex
+ wg wait.Group
+}
+
+func newHandlerSet[O any]() *handlerSet[O] {
+ return &handlerSet[O]{}
+}
+
+func NewCollection[I, O any](c Collection[I], hf TransformationSingle[I, O], opts ...CollectionOption) Collection[O] {
+ hm := func(ctx HandlerContext, i I) []O {
+ res := hf(ctx, i)
+ if res == nil {
+ return nil
+ }
+ return []O{*res}
+ }
+ o := buildCollectionOptions(opts...)
+ if o.name == "" {
+ o.name = fmt.Sprintf("Collection[%v,%v]", ptr.TypeName[I](), ptr.TypeName[O]())
+ }
+ return newManyCollection(c, hm, o, nil)
+}
+
+func newManyCollection[I, O any](
+ cc Collection[I],
+ hf TransformationMulti[I, O],
+ opts collectionOptions,
+ onPrimaryInputEventHandler func([]Event[I]),
+) Collection[O] {
+ c := cc.(internalCollection[I])
+
+ h := &manyCollection[I, O]{
+ transformation: hf,
+ collectionName: opts.name,
+ id: nextUID(),
+ parent: c,
+ dependencyState: dependencyState[I]{
+ collectionDependencies: sets.New[collectionUID](),
+ objectDependencies: map[Key[I]][]*dependency{},
+ indexedDependencies: map[indexedDependency]sets.Set[Key[I]]{},
+ indexedDependenciesExtractor: map[extractorKey]func(o any) []string{},
+ },
+ collectionState: multiIndex[I, O]{
+ inputs: map[Key[I]]I{},
+ outputs: map[Key[O]]O{},
+ mappings: map[Key[I]]sets.Set[Key[O]]{},
+ },
+ indexes: make(map[string]collectionIndex[I, O]),
+ eventHandlers: newHandlerSet[O](),
+ augmentation: opts.augmentation,
+ synced: make(chan struct{}),
+ stop: opts.stop,
+ onPrimaryInputEventHandler: onPrimaryInputEventHandler,
+ }
+
+ if opts.metadata != nil {
+ h.metadata = opts.metadata
+ }
+
+ return nil
+}
+
+type internalCollection[T any] interface {
+ Collection[T]
+
+ // Name is a human facing name for this collection.
+ // Note this may not be universally unique
+ name() string
+ // Uid is an internal unique ID for this collection. MUST be globally unique
+ uid() collectionUID
+
+ dump() CollectionDump
+
+ // Augment mutates an object for use in various function calls. See WithObjectAugmentation
+ augment(any) any
+
+ // Create a new index into the collection
+ index(name string, extract func(o T) []string) indexer[T]
+}
+
+type CollectionDump struct {
+ // Map of output key -> output
+ Outputs map[string]any `json:"outputs,omitempty"`
+ // Name of the input collection
+ InputCollection string `json:"inputCollection,omitempty"`
+ // Map of input key -> info
+ Inputs map[string]InputDump `json:"inputs,omitempty"`
+ // Synced returns whether the collection is synced or not
+ Synced bool `json:"synced"`
+}
+
+type InputDump struct {
+ Outputs []string `json:"outputs,omitempty"`
+ Dependencies []string `json:"dependencies,omitempty"`
+}
+
+type manyCollection[I, O any] struct {
+ // collectionName provides the collectionName for this collection.
+ collectionName string
+ id collectionUID
+ // parent is the input collection we are building off of.
+ parent Collection[I]
+ mu sync.RWMutex
+ collectionState multiIndex[I, O]
+ dependencyState dependencyState[I]
+ // internal indexes
+ indexes map[string]collectionIndex[I, O]
+
+ // eventHandlers is a list of event handlers registered for the collection. On any changes, each will be notified.
+ eventHandlers *handlerSet[O]
+
+ transformation TransformationMulti[I, O]
+
+ // augmentation allows transforming an object into another for usage throughout the library. See WithObjectAugmentation.
+ augmentation func(a any) any
+ synced chan struct{}
+ stop <-chan struct{}
+ metadata Metadata
+
+ // onPrimaryInputEventHandler is a specialized internal handler that runs synchronously when a primary input changes
+ onPrimaryInputEventHandler func(o []Event[I])
+
+ syncer Syncer
+}
diff --git a/pkg/kube/krt/core.go b/pkg/kube/krt/core.go
index fa267fc..49635f1 100644
--- a/pkg/kube/krt/core.go
+++ b/pkg/kube/krt/core.go
@@ -5,34 +5,28 @@
type FetchOption func(*dependency)
type HandlerContext interface {
- // DiscardResult triggers the result of this invocation to be skipped
- // This allows a collection to mark that the current state is *invalid* and should use the last-known state.
- //
- // Note: this differs from returning `nil`, which would otherwise wipe out the last known state.
- //
- // Note: if the current resource has never been computed, the result will not be discarded if it is non-nil. This allows
- // setting a default. For example, you may always return a static default config if the initial results are invalid,
- // but not revert to this config if later results are invalid. Results can unconditionally be discarded by returning nil.
DiscardResult()
- // _internalHandler is an interface that can only be implemented by this package.
_internalHandler()
}
type (
- TransformationEmpty[T any] func(ctx HandlerContext) *T
+ TransformationEmpty[T any] func(ctx HandlerContext) *T
+ TransformationMulti[I, O any] func(ctx HandlerContext, i I) []O
+ TransformationSingle[I, O any] func(ctx HandlerContext, i I) *O
)
type Metadata map[string]any
type Event[T any] struct {
- // Old object, set on Update or Delete.
- Old *T
- // New object, set on Add or Update
- New *T
- // Event is the change type
+ Old *T
+ New *T
Event controllers.EventType
}
+type indexer[T any] interface {
+ Lookup(key string) []T
+}
+
type EventStream[T any] interface {
Syncer
Register(f func(o Event[T])) HandlerRegistration
@@ -57,27 +51,14 @@
}
type Collection[T any] interface {
- // GetKey returns an object by its key, if present. Otherwise, nil is returned.
GetKey(k string) *T
-
- // List returns all objects in the collection.
- // Order of the list is undefined.
List() []T
-
- // EventStream provides event handling capabilities for the collection, allowing clients to subscribe to changes
- // and receive notifications when objects are added, modified, or removed.
EventStream[T]
-
- // Metadata returns the metadata associated with this collection.
- // This can be used to store and retrieve arbitrary key-value pairs
- // that provide additional context or configuration for the collection.
Metadata() Metadata
}
type Singleton[T any] interface {
- // Get returns the object, or nil if there is none.
Get() *T
- // Register adds an event watcher to the object. Any time it changes, the handler will be called
Register(f func(o Event[T])) HandlerRegistration
AsCollection() Collection[T]
Metadata() Metadata
diff --git a/pkg/kube/krt/files/files.go b/pkg/kube/krt/files/files.go
index d00aaf1..8ab08ce 100644
--- a/pkg/kube/krt/files/files.go
+++ b/pkg/kube/krt/files/files.go
@@ -3,6 +3,8 @@
import (
"github.com/apache/dubbo-kubernetes/pkg/filewatcher"
"github.com/apache/dubbo-kubernetes/pkg/kube/krt"
+ "go.uber.org/atomic"
+ "time"
)
type FileSingleton[T any] struct {
@@ -14,6 +16,49 @@
filename string,
readFile func(filename string) (T, error),
opts ...krt.CollectionOption,
-) {
- return
+) (FileSingleton[T], error) {
+ cfg, err := readFile(filename)
+ if err != nil {
+ return FileSingleton[T]{}, err
+ }
+
+ stop := krt.GetStop(opts...)
+
+ cur := atomic.NewPointer(&cfg)
+ trigger := krt.NewRecomputeTrigger(true, opts...)
+ sc := krt.NewSingleton[T](func(ctx krt.HandlerContext) *T {
+ trigger.MarkDependant(ctx)
+ return cur.Load()
+ }, opts...)
+ sc.AsCollection().WaitUntilSynced(stop)
+ watchFile(fileWatcher, filename, stop, func() {
+ cfg, err := readFile(filename)
+ if err != nil {
+ log.Warnf("failed to update: %v", err)
+ return
+ }
+ cur.Store(&cfg)
+ trigger.TriggerRecomputation()
+ })
+ return FileSingleton[T]{sc}, nil
+}
+
+func watchFile(fileWatcher filewatcher.FileWatcher, file string, stop <-chan struct{}, callback func()) {
+ _ = fileWatcher.Add(file)
+ go func() {
+ var timerC <-chan time.Time
+ for {
+ select {
+ case <-stop:
+ return
+ case <-timerC:
+ timerC = nil
+ callback()
+ case <-fileWatcher.Events(file):
+ if timerC == nil {
+ timerC = time.After(100 * time.Millisecond)
+ }
+ }
+ }
+ }()
}
diff --git a/pkg/kube/krt/filter.go b/pkg/kube/krt/filter.go
index 4545782..2bb04cd 100644
--- a/pkg/kube/krt/filter.go
+++ b/pkg/kube/krt/filter.go
@@ -1,3 +1,13 @@
package krt
-type filter struct{}
+import "github.com/apache/dubbo-kubernetes/pkg/util/smallset"
+
+type filter struct {
+ keys smallset.Set[string]
+}
+
+func FilterKey(k string) FetchOption {
+ return func(h *dependency) {
+ h.filter.keys = smallset.New(k)
+ }
+}
diff --git a/pkg/kube/krt/helpers.go b/pkg/kube/krt/helpers.go
new file mode 100644
index 0000000..9b3f14c
--- /dev/null
+++ b/pkg/kube/krt/helpers.go
@@ -0,0 +1,29 @@
+package krt
+
+import (
+ "time"
+)
+
+func waitForCacheSync(name string, stop <-chan struct{}, collections ...<-chan struct{}) (r bool) {
+ t := time.NewTicker(time.Second * 5)
+ defer t.Stop()
+ _ = time.Now()
+ defer func() {
+ if r {
+ } else {
+ }
+ }()
+ for _, col := range collections {
+ for {
+ select {
+ case <-t.C:
+ continue
+ case <-stop:
+ return false
+ case <-col:
+ }
+ break
+ }
+ }
+ return true
+}
diff --git a/pkg/kube/krt/informer.go b/pkg/kube/krt/informer.go
new file mode 100644
index 0000000..3a64e3b
--- /dev/null
+++ b/pkg/kube/krt/informer.go
@@ -0,0 +1,50 @@
+package krt
+
+import (
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/kclient"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+)
+
+type informer[I controllers.ComparableObject] struct {
+ inf kclient.Informer[I]
+ collectionName string
+ id collectionUID
+ augmentation func(a any) any
+ synced chan struct{}
+ baseSyncer Syncer
+ metadata Metadata
+}
+
+type channelSyncer struct {
+ name string
+ synced <-chan struct{}
+}
+
+func WrapClient[I controllers.ComparableObject](c kclient.Informer[I], opts ...CollectionOption) Collection[I] {
+ o := buildCollectionOptions(opts...)
+ if o.name == "" {
+ o.name = fmt.Sprintf("Informer[%v]", ptr.TypeName[I]())
+ }
+ h := &informer[I]{
+ inf: c,
+ collectionName: o.name,
+ id: nextUID(),
+ augmentation: o.augmentation,
+ synced: make(chan struct{}),
+ }
+ h.baseSyncer = channelSyncer{
+ name: h.collectionName,
+ synced: h.synced,
+ }
+
+ if o.metadata != nil {
+ h.metadata = o.metadata
+ }
+ return nil
+}
+
+func (c channelSyncer) WaitUntilSynced(stop <-chan struct{}) bool {
+ return waitForCacheSync(c.name, stop, c.synced)
+}
diff --git a/pkg/kube/krt/internal.go b/pkg/kube/krt/internal.go
index 58f83b1..291c4f3 100644
--- a/pkg/kube/krt/internal.go
+++ b/pkg/kube/krt/internal.go
@@ -1,10 +1,11 @@
package krt
+import "go.uber.org/atomic"
+
type dependency struct {
id collectionUID
collectionName string
- // Filter over the collection
- filter *filter
+ filter *filter
}
type collectionUID uint64
@@ -24,3 +25,9 @@
}
return *c
}
+
+var globalUIDCounter = atomic.NewUint64(1)
+
+func nextUID() collectionUID {
+ return collectionUID(globalUIDCounter.Inc())
+}
diff --git a/pkg/kube/krt/options.go b/pkg/kube/krt/options.go
index c7446c8..6f076e6 100644
--- a/pkg/kube/krt/options.go
+++ b/pkg/kube/krt/options.go
@@ -14,6 +14,10 @@
}
}
+func (k OptionsBuilder) Stop() <-chan struct{} {
+ return k.stop
+}
+
func (k OptionsBuilder) WithName(n string) []CollectionOption {
name := n
if k.namePrefix != "" {
diff --git a/pkg/kube/krt/recomputetrigger.go b/pkg/kube/krt/recomputetrigger.go
new file mode 100644
index 0000000..9d41eed
--- /dev/null
+++ b/pkg/kube/krt/recomputetrigger.go
@@ -0,0 +1,86 @@
+package krt
+
+import (
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "go.uber.org/atomic"
+)
+
+type RecomputeProtected[T any] struct {
+ trigger *RecomputeTrigger
+ data T
+}
+
+// Get marks us as dependent on the value and fetches it.
+func (c RecomputeProtected[T]) Get(ctx HandlerContext) T {
+ c.trigger.MarkDependant(ctx)
+ return c.data
+}
+
+// TriggerRecomputation tells all dependants to recompute
+func (c RecomputeProtected[T]) TriggerRecomputation() {
+ c.trigger.TriggerRecomputation()
+}
+
+// MarkSynced marks this trigger as ready. Before this is called, dependant collections will be blocked.
+// This ensures initial state is populated.
+func (c RecomputeProtected[T]) MarkSynced() {
+ c.trigger.MarkSynced()
+}
+
+// Modify modifies the object and triggers a recompution.
+func (c RecomputeProtected[T]) Modify(fn func(*T)) {
+ fn(&c.data)
+ c.TriggerRecomputation()
+}
+
+// AccessUnprotected returns the data without marking as dependant. This must be used with caution; any use within a collection
+// is likely broken
+func (c RecomputeProtected[T]) AccessUnprotected() T {
+ return c.data
+}
+
+// NewRecomputeProtected builds a RecomputeProtected which wraps some data, ensuring it is always MarkDependant when accessed
+func NewRecomputeProtected[T any](initialData T, startSynced bool, opts ...CollectionOption) RecomputeProtected[T] {
+ trigger := NewRecomputeTrigger(startSynced, opts...)
+ return RecomputeProtected[T]{
+ trigger: trigger,
+ data: initialData,
+ }
+}
+
+// RecomputeTrigger trigger provides an escape hatch to allow krt transformations to depend on external state and recompute
+// correctly when those change.
+// Typically, all state is registered and fetched through krt.Fetch. Through this mechanism, any changes are automatically
+// propagated through the system to dependencies.
+// In some cases, it may not be feasible to get all state into krt; hopefully, this is a temporary state.
+// RecomputeTrigger works around this by allowing an explicit call to recompute a collection; the caller must be sure to call Trigger()
+// any time the state changes.
+type RecomputeTrigger struct {
+ inner StaticSingleton[int32]
+ // krt will suppress events for unchanged resources. To workaround this, we constantly change and int each time TriggerRecomputation
+ // is called to ensure our event is not suppressed.
+ i *atomic.Int32
+}
+
+func NewRecomputeTrigger(startSynced bool, opts ...CollectionOption) *RecomputeTrigger {
+ inner := NewStatic[int32](ptr.Of(int32(0)), startSynced, opts...)
+ return &RecomputeTrigger{inner: inner, i: atomic.NewInt32(0)}
+}
+
+// TriggerRecomputation tells all dependants to recompute
+func (r *RecomputeTrigger) TriggerRecomputation() {
+ v := r.i.Inc()
+ r.inner.Set(ptr.Of(v))
+}
+
+// MarkDependant marks the given context as depending on this trigger. This registers it to be recomputed when TriggerRecomputation
+// is called.
+func (r *RecomputeTrigger) MarkDependant(ctx HandlerContext) {
+ _ = Fetch(ctx, r.inner.AsCollection())
+}
+
+// MarkSynced marks this trigger as ready. Before this is called, dependant collections will be blocked.
+// This ensures initial state is populated.
+func (r *RecomputeTrigger) MarkSynced() {
+ r.inner.MarkSynced()
+}
diff --git a/pkg/kube/krt/singleton.go b/pkg/kube/krt/singleton.go
index 414641f..b9e56d6 100644
--- a/pkg/kube/krt/singleton.go
+++ b/pkg/kube/krt/singleton.go
@@ -1,17 +1,200 @@
package krt
-func NewSingleton[O any](hf TransformationEmpty[O], opts ...CollectionOption) Singleton[O] {
- return nil
+import (
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "github.com/apache/dubbo-kubernetes/pkg/util/slices"
+
+ "sync/atomic"
+)
+
+type dummyValue struct{}
+
+func (d dummyValue) ResourceName() string {
+ return ""
}
-var (
- _ Singleton[any] = &collectionAdapter[any]{}
-)
+type StaticSingleton[T any] interface {
+ Singleton[T]
+ Set(*T)
+ MarkSynced()
+}
+
+func NewStatic[T any](initial *T, startSynced bool, opts ...CollectionOption) StaticSingleton[T] {
+ val := new(atomic.Pointer[T])
+ val.Store(initial)
+ x := &static[T]{
+ val: val,
+ synced: &atomic.Bool{},
+ id: nextUID(),
+ eventHandlers: &handlers[T]{},
+ }
+ x.synced.Store(startSynced)
+ o := buildCollectionOptions(opts...)
+ if o.name == "" {
+ o.name = fmt.Sprintf("Static[%v]", ptr.TypeName[T]())
+ }
+ x.collectionName = o.name
+ x.syncer = pollSyncer{
+ name: x.collectionName,
+ f: func() bool {
+ return x.synced.Load()
+ },
+ }
+ if o.metadata != nil {
+ x.metadata = o.metadata
+ }
+ maybeRegisterCollectionForDebugging(x, o.debugger)
+ return collectionAdapter[T]{x}
+}
+
+type static[T any] struct {
+ val *atomic.Pointer[T]
+ synced *atomic.Bool
+ id collectionUID
+ eventHandlers *handlers[T]
+ collectionName string
+ syncer Syncer
+ metadata Metadata
+}
+
+func (d *static[T]) GetKey(k string) *T {
+ return d.val.Load()
+}
+
+func (d *static[T]) List() []T {
+ v := d.val.Load()
+ if v == nil {
+ return nil
+ }
+ return []T{*v}
+}
+
+func (d *static[T]) Metadata() Metadata {
+ return d.metadata
+}
+
+func (d *static[T]) Register(f func(o Event[T])) HandlerRegistration {
+ return registerHandlerAsBatched[T](d, f)
+}
+
+func (d *static[T]) RegisterBatch(f func(o []Event[T]), runExistingState bool) HandlerRegistration {
+ reg := d.eventHandlers.Insert(f)
+ if runExistingState {
+ v := d.val.Load()
+ if v != nil {
+ f([]Event[T]{{
+ New: v,
+ Event: controllers.EventAdd,
+ }})
+ }
+ }
+
+ return staticHandler{Syncer: d.syncer, remove: func() {
+ d.eventHandlers.Delete(reg)
+ }}
+}
+
+type staticHandler struct {
+ Syncer
+ remove func()
+}
+
+func (s staticHandler) UnregisterHandler() {
+ s.remove()
+}
+
+func (d *static[T]) Synced() Syncer {
+ return pollSyncer{
+ name: d.collectionName,
+ f: func() bool {
+ return d.synced.Load()
+ },
+ }
+}
+
+func (d *static[T]) HasSynced() bool {
+ return d.syncer.HasSynced()
+}
+
+func (d *static[T]) WaitUntilSynced(stop <-chan struct{}) bool {
+ return d.syncer.WaitUntilSynced(stop)
+}
+
+func (d *static[T]) Set(now *T) {
+ old := d.val.Swap(now)
+ if old == now {
+ return
+ }
+ for _, h := range d.eventHandlers.Get() {
+ h([]Event[T]{toEvent[T](old, now)})
+ }
+}
+
+// nolint: unused // (not true, its to implement an interface)
+func (d *static[T]) dump() CollectionDump {
+ return CollectionDump{
+ Outputs: map[string]any{
+ "static": d.val.Load(),
+ },
+ Synced: d.HasSynced(),
+ }
+}
+
+// nolint: unused // (not true, its to implement an interface)
+func (d *static[T]) augment(a any) any {
+ // not supported in this collection type
+ return a
+}
+
+// nolint: unused // (not true, its to implement an interface)
+func (d *static[T]) name() string {
+ return d.collectionName
+}
+
+// nolint: unused // (not true, its to implement an interface)
+func (d *static[T]) uid() collectionUID {
+ return d.id
+}
+
+func (d *static[T]) index(name string, extract func(o T) []string) indexer[T] {
+ panic("TODO")
+}
+
+func toEvent[T any](old, now *T) Event[T] {
+ if old == nil {
+ return Event[T]{
+ New: now,
+ Event: controllers.EventAdd,
+ }
+ } else if now == nil {
+ return Event[T]{
+ Old: old,
+ Event: controllers.EventDelete,
+ }
+ }
+ return Event[T]{
+ New: now,
+ Old: old,
+ Event: controllers.EventUpdate,
+ }
+}
+
+var _ Collection[dummyValue] = &static[dummyValue]{}
type collectionAdapter[T any] struct {
c Collection[T]
}
+func (c collectionAdapter[T]) MarkSynced() {
+ c.c.(*static[T]).synced.Store(true)
+}
+
+func (c collectionAdapter[T]) Set(t *T) {
+ c.c.(*static[T]).Set(t)
+}
+
func (c collectionAdapter[T]) Get() *T {
// Guaranteed to be 0 or 1 len
res := c.c.List()
@@ -33,3 +216,16 @@
func (c collectionAdapter[T]) AsCollection() Collection[T] {
return c.c
}
+
+var (
+ _ Singleton[any] = &collectionAdapter[any]{}
+)
+
+func NewSingleton[O any](hf TransformationEmpty[O], opts ...CollectionOption) Singleton[O] {
+ staticOpts := append(slices.Clone(opts), nil)
+ dummyCollection := NewStatic[dummyValue](&dummyValue{}, true, staticOpts...).AsCollection()
+ col := NewCollection[dummyValue, O](dummyCollection, func(ctx HandlerContext, _ dummyValue) *O {
+ return hf(ctx)
+ }, opts...)
+ return collectionAdapter[O]{col}
+}
diff --git a/pkg/kube/kubetypes/types.go b/pkg/kube/kubetypes/types.go
index eecccd3..989de8c 100644
--- a/pkg/kube/kubetypes/types.go
+++ b/pkg/kube/kubetypes/types.go
@@ -17,14 +17,54 @@
package kubetypes
+import (
+ "context"
+ "github.com/apache/dubbo-kubernetes/pkg/cluster"
+ "github.com/apache/dubbo-kubernetes/pkg/util/sets"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+)
+
+type InformerType int
+
+const (
+ StandardInformer InformerType = iota
+ DynamicInformer
+ MetadataInformer
+)
+
+type WriteAPI[T runtime.Object] interface {
+ Create(ctx context.Context, object T, opts metav1.CreateOptions) (T, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result T, err error)
+ Update(ctx context.Context, object T, opts metav1.UpdateOptions) (T, error)
+ Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
+}
+
+type WriteStatusAPI[T runtime.Object] interface {
+ UpdateStatus(ctx context.Context, object T, opts metav1.UpdateOptions) (T, error)
+}
+
type InformerOptions struct {
- // A selector to restrict the list of returned objects by their labels.
- LabelSelector string
- // A selector to restrict the list of returned objects by their fields.
- FieldSelector string
- // Namespace to watch.
- Namespace string
- // ObjectTransform allows arbitrarily modifying objects stored in the underlying cache.
- // If unset, a default transform is provided to remove ManagedFields (high cost, low value)
+ LabelSelector string
+ FieldSelector string
+ Namespace string
ObjectTransform func(obj any) (any, error)
+ Cluster cluster.ID
+ InformerType InformerType
+}
+
+type Filter struct {
+ LabelSelector string
+ FieldSelector string
+ Namespace string
+ ObjectFilter DynamicObjectFilter
+ ObjectTransform func(obj any) (any, error)
+}
+
+type DynamicObjectFilter interface {
+ // Filter returns true if the input object or namespace string resides in a namespace selected for discovery
+ Filter(obj any) bool
+ // AddHandler registers a handler on namespace, which will be triggered when namespace selected or deselected.
+ AddHandler(func(selected, deselected sets.String))
}
diff --git a/pkg/mesh/meshwatcher/collection.go b/pkg/mesh/meshwatcher/collection.go
index 04090d4..d1e93be 100644
--- a/pkg/mesh/meshwatcher/collection.go
+++ b/pkg/mesh/meshwatcher/collection.go
@@ -6,7 +6,6 @@
"github.com/apache/dubbo-kubernetes/pkg/kube/krt"
krtfiles "github.com/apache/dubbo-kubernetes/pkg/kube/krt/files"
"github.com/apache/dubbo-kubernetes/pkg/mesh"
-
"os"
"path"
)
diff --git a/pkg/ptr/pointer.go b/pkg/ptr/pointer.go
index 5388152..6f40c10 100644
--- a/pkg/ptr/pointer.go
+++ b/pkg/ptr/pointer.go
@@ -1,5 +1,24 @@
package ptr
+import "fmt"
+
func Of[T any](t T) *T {
return &t
}
+
+func Empty[T any]() T {
+ var empty T
+ return empty
+}
+
+func Flatten[T any](t **T) *T {
+ if t == nil {
+ return nil
+ }
+ return *t
+}
+
+func TypeName[T any]() string {
+ var empty T
+ return fmt.Sprintf("%T", empty)
+}
diff --git a/pkg/sleep/sleep.go b/pkg/sleep/sleep.go
new file mode 100644
index 0000000..f959876
--- /dev/null
+++ b/pkg/sleep/sleep.go
@@ -0,0 +1,23 @@
+package sleep
+
+import (
+ "context"
+ "time"
+)
+
+func UntilContext(ctx context.Context, d time.Duration) bool {
+ return Until(ctx.Done(), d)
+}
+
+func Until(ch <-chan struct{}, d time.Duration) bool {
+ timer := time.NewTimer(d)
+ select {
+ case <-ch:
+ if !timer.Stop() {
+ <-timer.C
+ }
+ return false
+ case <-timer.C:
+ return true
+ }
+}
diff --git a/pkg/typemap/map.go b/pkg/typemap/map.go
new file mode 100644
index 0000000..037cd27
--- /dev/null
+++ b/pkg/typemap/map.go
@@ -0,0 +1,27 @@
+package typemap
+
+import (
+ "github.com/apache/dubbo-kubernetes/pkg/ptr"
+ "reflect"
+)
+
+type TypeMap struct {
+ inner map[reflect.Type]any
+}
+
+func NewTypeMap() TypeMap {
+ return TypeMap{make(map[reflect.Type]any)}
+}
+
+func Set[T any](t TypeMap, v T) {
+ interfaceType := reflect.TypeOf((*T)(nil)).Elem()
+ t.inner[interfaceType] = v
+}
+
+func Get[T any](t TypeMap) *T {
+ v, f := t.inner[reflect.TypeFor[T]()]
+ if f {
+ return ptr.Of(v.(T))
+ }
+ return nil
+}
diff --git a/pkg/util/smallset/smallset.go b/pkg/util/smallset/smallset.go
new file mode 100644
index 0000000..5adbfa7
--- /dev/null
+++ b/pkg/util/smallset/smallset.go
@@ -0,0 +1,108 @@
+package smallset
+
+import (
+ "cmp"
+ "fmt"
+ "github.com/apache/dubbo-kubernetes/pkg/util/slices"
+)
+
+// Set is an immutable set optimized for *small number of items*. For general purposes, Sets is likely better
+//
+// *Set construction*: sets is roughly 1kb allocations per 250 items. smallsets is 0.
+// *Contains* sets is O(1). smallsets is O(logn). smallsets is typically faster up to about 5 elements.
+//
+// At 1000 items, it is roughly 5x slower (30ns vs 5ns).
+type Set[T cmp.Ordered] struct {
+ items []T
+}
+
+// NewPresorted creates a new Set with the given items.
+// If items is not sorted or contains duplicates, this gives undefined behavior; use New instead.
+func NewPresorted[T cmp.Ordered](items ...T) Set[T] {
+ return Set[T]{items: items}
+}
+
+// New creates a new Set with the given items.
+// Duplicates are removed
+func New[T cmp.Ordered](items ...T) Set[T] {
+ if len(items) == 1 {
+ return Set[T]{items: items}
+ }
+ slices.Sort(items)
+ items = slices.FilterDuplicatesPresorted(items)
+ return Set[T]{items: items}
+}
+
+// CopyAndInsert builds a *new* with all the current items plus new items
+func (s Set[T]) CopyAndInsert(items ...T) Set[T] {
+ slices.Sort(items)
+ // This is basically the 'merge' part of merge sort.
+ a := s.items
+ b := items
+ nl := make([]T, 0, len(a)+len(b))
+ i, j := 0, 0
+ appendIfUnique := func(t T) []T {
+ l := len(nl)
+ if l == 0 {
+ nl = append(nl, t)
+ } else {
+ last := nl[l-1]
+ if last != t {
+ nl = append(nl, t)
+ }
+ }
+ return nl
+ }
+ for i < len(a) && j < len(b) {
+ if a[i] < b[j] {
+ nl = appendIfUnique(a[i])
+ i++
+ } else {
+ nl = appendIfUnique(b[j])
+ j++
+ }
+ }
+ for ; i < len(a); i++ {
+ nl = appendIfUnique(a[i])
+ }
+ for ; j < len(b); j++ {
+ nl = appendIfUnique(b[j])
+ }
+ // we already know they are sorted+unique
+ return Set[T]{items: nl}
+}
+
+// List returns the underlying slice. Must not be modified
+func (s Set[T]) List() []T {
+ return s.items
+}
+
+// Contains returns whether the given item is in the set.
+func (s Set[T]) Contains(item T) bool {
+ _, f := slices.BinarySearch(s.items, item)
+ return f
+}
+
+// Len returns the number of elements in this Set.
+func (s Set[T]) Len() int {
+ return len(s.items)
+}
+
+// IsEmpty indicates whether the set is the empty set.
+func (s Set[T]) IsEmpty() bool {
+ return len(s.items) == 0
+}
+
+// IsNil indicates whether the set is nil. This is different from an empty set.
+// 'var smallset.Set': nil
+// smallset.New(): nil
+// smallset.New(emptyList...): not nil
+func (s Set[T]) IsNil() bool {
+ return s.items == nil
+}
+
+// String returns a string representation of the set.
+// Use it only for debugging and logging.
+func (s Set[T]) String() string {
+ return fmt.Sprintf("%v", s.items)
+}