This is an automated email from the ASF dual-hosted git repository.

zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git


The following commit(s) were added to refs/heads/master by this push:
     new b3266156 [discovery] fix inject and xds push (#804)
b3266156 is described below

commit b32661561c172e69f831d8e4f780b53cb0fa71c9
Author: Jian Zhong <[email protected]>
AuthorDate: Tue Oct 28 10:08:13 2025 +0800

    [discovery] fix inject and xds push (#804)
---
 go.mod                                             |  45 +---
 go.sum                                             |  85 +-----
 pkg/kube/inject/app_probe.go                       |  14 +
 pkg/kube/inject/inject.go                          |  45 ++++
 pkg/kube/inject/validate.go                        |  23 ++
 pkg/kube/inject/webhook.go                         |  72 ++++-
 pkg/model/proxy.go                                 |   5 +
 pkg/model/xds.go                                   |   1 +
 pkg/util/protomarshal/protomarshal.go              |  46 ++--
 pkg/util/sets/set.go                               |   8 +
 pkg/version/version.go                             |   6 +-
 pkg/xds/server.go                                  |   1 -
 sail/pkg/model/context.go                          | 139 +++++++++-
 sail/pkg/model/push_context.go                     |   5 +
 sail/pkg/model/service.go                          |  15 +-
 sail/pkg/serviceregistry/aggregate/controller.go   |  42 ++-
 .../serviceregistry/kube/controller/controller.go  |  13 +
 sail/pkg/xds/ads.go                                | 144 +++++++++-
 sail/pkg/xds/auth.go                               |   7 +
 sail/pkg/xds/delta.go                              | 299 +++++++++++++++++++--
 sail/pkg/xds/v3/model.go                           |   1 +
 sail/pkg/xds/xdsgen.go                             |  40 ++-
 22 files changed, 852 insertions(+), 204 deletions(-)

diff --git a/go.mod b/go.mod
index f323b7b0..ea16e464 100644
--- a/go.mod
+++ b/go.mod
@@ -33,7 +33,7 @@ require (
        github.com/docker/docker v27.5.1+incompatible
        github.com/docker/docker-credential-helpers v0.9.3
        github.com/docker/go-connections v0.5.0
-       github.com/envoyproxy/go-control-plane/envoy 
v1.32.5-0.20250921184633-9a6a6e1d1741
+       github.com/envoyproxy/go-control-plane/envoy 
v1.32.5-0.20250627145903-197b96a9c7f8
        github.com/evanphx/json-patch/v5 v5.9.11
        github.com/fatih/color v1.18.0
        github.com/fsnotify/fsnotify v1.9.0
@@ -44,6 +44,7 @@ require (
        github.com/golang/protobuf v1.5.4
        github.com/google/go-cmp v0.7.0
        github.com/google/go-containerregistry v0.20.6
+       github.com/google/uuid v1.6.0
        github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
        github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2
        github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
@@ -78,6 +79,7 @@ require (
        k8s.io/klog/v2 v2.130.1
        k8s.io/kubectl v0.33.3
        k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
+       sigs.k8s.io/mcs-api v0.2.0
        sigs.k8s.io/yaml v1.6.0
 )
 
@@ -102,7 +104,6 @@ require (
        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.26.1 // indirect
        github.com/aws/aws-sdk-go-v2/config v1.27.12 // indirect
@@ -133,7 +134,6 @@ require (
        github.com/containerd/typeurl v1.0.2 // 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.11.0 // indirect
@@ -142,7 +142,6 @@ require (
        github.com/dustin/go-humanize v1.0.1 // indirect
        github.com/emicklei/go-restful/v3 v3.13.0 // 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/protoc-gen-validate v1.2.1 // indirect
        github.com/fxamacker/cbor/v2 v2.9.0 // indirect
        github.com/gdamore/encoding v1.0.0 // indirect
@@ -150,25 +149,19 @@ require (
        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.2 // 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/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.26.0 // indirect
        github.com/google/gnostic-models v0.7.0 // indirect
+       github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // 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/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
        github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // 
indirect
        github.com/hashicorp/errwrap v1.1.0 // indirect
-       github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
        github.com/hashicorp/hcl v1.0.0 // indirect
        github.com/huandu/xstrings v1.5.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -180,12 +173,6 @@ require (
        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.9 // indirect
        github.com/mailru/easyjson v0.9.0 // indirect
@@ -193,7 +180,6 @@ require (
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/mattn/go-runewidth v0.0.16 // indirect
        github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
-       github.com/miekg/dns v1.1.68 // indirect
        github.com/mitchellh/copystructure v1.2.0 // indirect
        github.com/mitchellh/go-homedir v1.1.0 // indirect
        github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e // 
indirect
@@ -201,7 +187,6 @@ require (
        github.com/mitchellh/reflectwalk v1.0.2 // indirect
        github.com/moby/buildkit v0.23.2 // 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.5.0 // indirect
@@ -212,8 +197,9 @@ require (
        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.25.3 // indirect
+       github.com/onsi/gomega v1.38.2 // 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
@@ -229,7 +215,6 @@ require (
        github.com/prometheus/client_golang v1.23.2 // indirect
        github.com/prometheus/client_model v0.6.2 // indirect
        github.com/prometheus/common v0.66.1 // indirect
-       github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f 
// indirect
        github.com/prometheus/procfs v0.17.0 // indirect
        github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 // indirect
        github.com/rivo/uniseg v0.4.7 // indirect
@@ -243,7 +228,6 @@ require (
        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.1 // indirect
        github.com/subosito/gotenv v1.6.0 // indirect
        github.com/ulikunitz/xz v0.5.12 // indirect
        github.com/vbatts/tar-split v0.12.1 // indirect
@@ -251,41 +235,26 @@ require (
        github.com/xanzy/ssh-agent v0.3.3 // 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.37.0 // indirect
-       go.opentelemetry.io/otel/exporters/prometheus v0.59.1 // indirect
-       go.opentelemetry.io/otel/metric v1.37.0 // indirect
-       go.opentelemetry.io/otel/sdk v1.37.0 // indirect
-       go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
-       go.opentelemetry.io/otel/trace v1.37.0 // indirect
-       go.opentelemetry.io/proto/otlp v1.7.1 // indirect
+       go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 
// indirect
        go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // 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.4 // indirect
        golang.org/x/mod v0.27.0 // indirect
        golang.org/x/oauth2 v0.30.0 // indirect
        golang.org/x/sync v0.17.0 // indirect
        golang.org/x/text v0.28.0 // indirect
-       golang.org/x/tools v0.36.0 // indirect
        google.golang.org/genproto/googleapis/api 
v0.0.0-20250811230008-5f3141c8851a // indirect
        google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250826171959-ef028d996bc1 // indirect
        gopkg.in/evanphx/json-patch.v4 v4.13.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
-       k8s.io/apiserver v0.34.1 // indirect
        k8s.io/cli-runtime v0.33.3 // indirect
        k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect
-       sigs.k8s.io/gateway-api v1.3.1-0.20250924180216-ab6b5a251c59 // indirect
-       sigs.k8s.io/gateway-api-inference-extension 
v0.0.0-20250917095812-173ad587b675 // indirect
        sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // 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.2.0 // indirect
        sigs.k8s.io/randfill v1.0.0 // indirect
        sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 0070e356..3f4542cc 100644
--- a/go.sum
+++ b/go.sum
@@ -22,8 +22,8 @@ cloud.google.com/go/vertexai v0.12.0 
h1:zTadEo/CtsoyRXNx3uGCncoWAP1H2HakGqwznt+i
 cloud.google.com/go/vertexai v0.12.0/go.mod 
h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8=
 dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
 dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
-github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 
h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
-github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod 
h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 
h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod 
h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
 github.com/AlecAivazis/survey/v2 v2.3.7 
h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
 github.com/AlecAivazis/survey/v2 v2.3.7/go.mod 
h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
 github.com/AssemblyAI/assemblyai-go-sdk v1.3.0 
h1:AtOVgGxUycvK4P4ypP+1ZupecvFgnfH+Jsum0o5ILoU=
@@ -89,8 +89,6 @@ github.com/andybalholm/cascadia v1.3.2 
h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi
 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=
@@ -167,8 +165,6 @@ github.com/cenkalti/backoff v2.2.1+incompatible 
h1:tNowT99t7UNflLxfYYSlKYsBpXdEe
 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/cenkalti/backoff/v5 v5.0.2 
h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
-github.com/cenkalti/backoff/v5 v5.0.2/go.mod 
h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
 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=
@@ -219,8 +215,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 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=
@@ -257,10 +251,8 @@ github.com/emirpasic/gods v1.18.1/go.mod 
h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod 
h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane 
v0.9.1-0.20191026205805-5f8ba28d4473/go.mod 
h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod 
h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-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.20250921184633-9a6a6e1d1741 
h1:cihocN3Y1vol5fNBh5Zxbk0s72PWbDL9WuKIVpCCkTI=
-github.com/envoyproxy/go-control-plane/envoy 
v1.32.5-0.20250921184633-9a6a6e1d1741/go.mod 
h1:2LcmvJoXsDSrsGZIxGM0Gah9ykiwTn/kgjyQdnNH8Jc=
+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=
@@ -309,7 +301,6 @@ github.com/go-kit/log v0.1.0/go.mod 
h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
 github.com/go-logfmt/logfmt v0.4.0/go.mod 
h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod 
h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-logr/logr v0.1.0/go.mod 
h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-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=
@@ -327,8 +318,6 @@ 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/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=
@@ -357,8 +346,6 @@ 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/cel-go v0.26.0 
h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
-github.com/google/cel-go v0.26.0/go.mod 
h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
 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.7.0 
h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
@@ -377,8 +364,6 @@ github.com/google/go-containerregistry v0.20.2/go.mod 
h1:z38EKdKh4h7IP2gSfUUqEva
 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/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-20250607225305-033d6d78b36a 
h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
 github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod 
h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
 github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
@@ -399,10 +384,6 @@ github.com/gorilla/css v1.0.0 
h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
 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/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc 
h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
-github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod 
h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
 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/go-grpc-middleware v1.4.0 
h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
@@ -411,16 +392,11 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 
h1:sGm2vDRFUrQJO/Veii4h4z
 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod 
h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 
h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod 
h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0 
h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 
h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod 
h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
 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=
 github.com/hashicorp/go-multierror v1.1.1 
h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 github.com/hashicorp/go-multierror v1.1.1/go.mod 
h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/golang-lru/v2 v2.0.7 
h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
-github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod 
h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod 
h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0=
@@ -466,23 +442,8 @@ github.com/kr/pty v1.1.1/go.mod 
h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 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=
@@ -506,8 +467,6 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b 
h1:j7+1HpAFS1zy5+Q4qx1f
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod 
h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/microcosm-cc/bluemonday v1.0.26 
h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
 github.com/microcosm-cc/bluemonday v1.0.26/go.mod 
h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
-github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
-github.com/miekg/dns v1.1.68/go.mod 
h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
 github.com/mitchellh/copystructure v1.2.0 
h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod 
h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/go-homedir v1.1.0 
h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -523,8 +482,6 @@ github.com/moby/buildkit v0.10.6 
h1:DJlEuLIgnu34HQKF4n9Eg6q2YqQVC0eOpMb4p2eRS2w=
 github.com/moby/buildkit v0.10.6/go.mod 
h1:tQuuyTWtOb9D+RE425cwOCUkX0/oZ+5iBZ+uWpWQ9bU=
 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=
@@ -549,13 +506,10 @@ github.com/morikuni/aec v1.0.0 
h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 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.25.3 
h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
 github.com/onsi/ginkgo/v2 v2.25.3/go.mod 
h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
 github.com/onsi/gomega v1.5.0/go.mod 
h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -601,8 +555,6 @@ github.com/prometheus/client_model v0.6.2 
h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
 github.com/prometheus/client_model v0.6.2/go.mod 
h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
 github.com/prometheus/common v0.66.1 
h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
 github.com/prometheus/common v0.66.1/go.mod 
h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
-github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f 
h1:QQB6SuvGZjK8kdc2YaLJpYhV8fxauOsjE6jgcL6YJ8Q=
-github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f/go.mod 
h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
 github.com/prometheus/procfs v0.17.0 
h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
 github.com/prometheus/procfs v0.17.0/go.mod 
h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
 github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 
h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc=
@@ -660,8 +612,6 @@ github.com/spf13/pflag v1.0.3/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/spf13/pflag v1.0.6/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
 github.com/spf13/pflag v1.0.7/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stoewer/go-strcase v1.3.1 
h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
-github.com/stoewer/go-strcase v1.3.1/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=
@@ -675,7 +625,6 @@ github.com/stretchr/testify v1.6.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 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.11.1 
h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod 
h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
@@ -728,12 +677,6 @@ 
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6h
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod 
h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
 go.opentelemetry.io/otel v1.37.0 
h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
 go.opentelemetry.io/otel v1.37.0/go.mod 
h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 
h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod 
h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 
h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod 
h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
-go.opentelemetry.io/otel/exporters/prometheus v0.59.1 
h1:HcpSkTkJbggT8bjYP+BjyqPWlD17BH9C5CYNKeDzmcA=
-go.opentelemetry.io/otel/exporters/prometheus v0.59.1/go.mod 
h1:0FJL+gjuUoM07xzik3KPBaN+nz/CoB15kV6WLMiXZag=
 go.opentelemetry.io/otel/metric v1.37.0 
h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
 go.opentelemetry.io/otel/metric v1.37.0/go.mod 
h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
 go.opentelemetry.io/otel/sdk v1.37.0 
h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
@@ -742,8 +685,6 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 
h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
 go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod 
h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
 go.opentelemetry.io/otel/trace v1.37.0 
h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
 go.opentelemetry.io/otel/trace v1.37.0/go.mod 
h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
-go.opentelemetry.io/proto/otlp v1.7.1 
h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
-go.opentelemetry.io/proto/otlp v1.7.1/go.mod 
h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
 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.7.0/go.mod 
h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -755,11 +696,7 @@ go.uber.org/goleak v1.1.10/go.mod 
h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A
 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.6.0/go.mod 
h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-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.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-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.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@@ -927,8 +864,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod 
h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 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=
@@ -953,22 +888,16 @@ istio.io/api v1.27.1-0.20250820125923-f5a5d3a605a9 
h1:gVTxnhYGJ1pY+iqcz/mrbPSpdk
 istio.io/api v1.27.1-0.20250820125923-f5a5d3a605a9/go.mod 
h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg=
 istio.io/client-go v1.27.1 h1:VWEtOzmv9gi4x3OPjN5wMFOBV1i95UIGcbYXoP4VVuA=
 istio.io/client-go v1.27.1/go.mod 
h1:otQns/CCDd4EoyEFWp8w+ksTP0T05baYTIx5FxqS8eM=
-istio.io/istio v0.0.0-20251004003413-450b9f38614f 
h1:Kdl3XP0a/zFHU8//DLlj+LjGFWg/+XHzG1Z7iUMMjwc=
-istio.io/istio v0.0.0-20251004003413-450b9f38614f/go.mod 
h1:rkwXkzAe+prvZU+JxLOJboEX44gr1ycbEPyl9k24CE8=
 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/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
-k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
 k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA=
 k8s.io/cli-runtime v0.33.3/go.mod 
h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo=
 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/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
-k8s.io/component-base v0.34.1/go.mod 
h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
 k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
@@ -981,12 +910,6 @@ k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d 
h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPG
 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/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.1-0.20250924180216-ab6b5a251c59 
h1:oemARIB7UxADVkfGavZazg/PLWSKmzbZhr7l4P0kCho=
-sigs.k8s.io/gateway-api v1.3.1-0.20250924180216-ab6b5a251c59/go.mod 
h1:eEPQfoYoowXAJ+/1RFugqZSanFd705bAPrIjB8Z8LdQ=
-sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250917095812-173ad587b675 
h1:CW+VWxazW54YzQnlHkyCE/WmAvF0YK7HOl+OK8IO3Ug=
-sigs.k8s.io/gateway-api-inference-extension 
v0.0.0-20250917095812-173ad587b675/go.mod 
h1:Tqmt4U654MNQkzC1lFYVa43wbUW7K8r8Lv6Sq2I3HCc=
 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/kustomize/api v0.19.0 
h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
diff --git a/pkg/kube/inject/app_probe.go b/pkg/kube/inject/app_probe.go
new file mode 100644
index 00000000..1a317e55
--- /dev/null
+++ b/pkg/kube/inject/app_probe.go
@@ -0,0 +1,14 @@
+package inject
+
+import corev1 "k8s.io/api/core/v1"
+
+func FindProxy(pod *corev1.Pod) *corev1.Container {
+       return FindContainerFromPod(ProxyContainerName, pod)
+}
+
+func FindContainerFromPod(name string, pod *corev1.Pod) *corev1.Container {
+       if c := FindContainer(name, pod.Spec.Containers); c != nil {
+               return c
+       }
+       return FindContainer(name, pod.Spec.InitContainers)
+}
diff --git a/pkg/kube/inject/inject.go b/pkg/kube/inject/inject.go
index 0989dae8..9a4bac68 100644
--- a/pkg/kube/inject/inject.go
+++ b/pkg/kube/inject/inject.go
@@ -15,10 +15,18 @@ import (
        "k8s.io/apimachinery/pkg/types"
        "k8s.io/klog/v2"
        "sigs.k8s.io/yaml"
+       "sort"
        "strings"
        "text/template"
 )
 
+const (
+       ProxyContainerName      = "dubbo-proxy"
+       ValidationContainerName = "dubbo-validation"
+       InitContainerName       = "dubbo-init"
+       EnableCoreDumpName      = "enable-core-dump"
+)
+
 type (
        Template     *corev1.Pod
        RawTemplates map[string]string
@@ -93,8 +101,14 @@ func potentialPodName(metadata metav1.ObjectMeta) string {
 }
 
 func RunTemplate(params InjectionParameters) (mergedPod *corev1.Pod, 
templatePod *corev1.Pod, err error) {
+       metadata := &params.pod.ObjectMeta
        meshConfig := params.meshConfig
 
+       if err := validateAnnotations(metadata.GetAnnotations()); err != nil {
+               klog.Errorf("Injection failed due to invalid annotations: %v", 
err)
+               return nil, nil, err
+       }
+
        strippedPod, err := reinsertOverrides(stripPod(params))
        if err != nil {
                return nil, nil, err
@@ -139,6 +153,37 @@ func RunTemplate(params InjectionParameters) (mergedPod 
*corev1.Pod, templatePod
        return mergedPod, templatePod, nil
 }
 
+func overwriteClusterInfo(pod *corev1.Pod, params InjectionParameters) {
+       c := FindProxy(pod)
+       if c == nil {
+               return
+       }
+       if len(params.proxyEnvs) > 0 {
+               updateClusterEnvs(c, params.proxyEnvs)
+       }
+}
+
+func updateClusterEnvs(container *corev1.Container, newKVs map[string]string) {
+       envVars := make([]corev1.EnvVar, 0)
+
+       for _, env := range container.Env {
+               if _, found := newKVs[env.Name]; !found {
+                       envVars = append(envVars, env)
+               }
+       }
+
+       keys := make([]string, 0, len(newKVs))
+       for key := range newKVs {
+               keys = append(keys, key)
+       }
+       sort.Strings(keys)
+       for _, key := range keys {
+               val := newKVs[key]
+               envVars = append(envVars, corev1.EnvVar{Name: key, Value: val, 
ValueFrom: nil})
+       }
+       container.Env = envVars
+}
+
 func knownTemplates(t Templates) []string {
        keys := make([]string, 0, len(t))
        for k := range t {
diff --git a/pkg/kube/inject/validate.go b/pkg/kube/inject/validate.go
new file mode 100644
index 00000000..3a2230e2
--- /dev/null
+++ b/pkg/kube/inject/validate.go
@@ -0,0 +1,23 @@
+package inject
+
+import (
+       "fmt"
+       "github.com/hashicorp/go-multierror"
+)
+
+type annotationValidationFunc func(value string) error
+
+var (
+       AnnotationValidation = map[string]annotationValidationFunc{}
+)
+
+func validateAnnotations(annotations map[string]string) (err error) {
+       for name, value := range annotations {
+               if v, ok := AnnotationValidation[name]; ok {
+                       if e := v(value); e != nil {
+                               err = multierror.Append(err, 
fmt.Errorf("invalid value '%s' for annotation '%s': %v", value, name, e))
+                       }
+               }
+       }
+       return
+}
diff --git a/pkg/kube/inject/webhook.go b/pkg/kube/inject/webhook.go
index 6f24befc..f36e9cca 100644
--- a/pkg/kube/inject/webhook.go
+++ b/pkg/kube/inject/webhook.go
@@ -5,10 +5,12 @@ import (
        "errors"
        "fmt"
        opconfig "github.com/apache/dubbo-kubernetes/operator/pkg/apis"
+       "github.com/apache/dubbo-kubernetes/pkg/config/mesh"
        "github.com/apache/dubbo-kubernetes/pkg/kube"
        "github.com/apache/dubbo-kubernetes/pkg/util/protomarshal"
        "github.com/apache/dubbo-kubernetes/sail/pkg/model"
        "gomodules.xyz/jsonpatch/v2"
+       "istio.io/api/annotation"
        meshconfig "istio.io/api/mesh/v1alpha1"
        admissionv1 "k8s.io/api/admission/v1"
        kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1"
@@ -170,7 +172,6 @@ func (wh *Webhook) serveInject(w http.ResponseWriter, r 
*http.Request) {
                return
        }
 
-       // verify the content type is accurate
        contentType := r.Header.Get("Content-Type")
        if contentType != "application/json" {
                http.Error(w, "invalid Content-Type, want `application/json`", 
http.StatusUnsupportedMediaType)
@@ -239,13 +240,11 @@ type InjectionParameters struct {
 }
 
 func injectPod(req InjectionParameters) ([]byte, error) {
-       // The patch will be built relative to the initial pod, capture its 
current state
        originalPodSpec, err := json.Marshal(req.pod)
        if err != nil {
                return nil, err
        }
 
-       // Run the injection template, giving us a partial pod spec
        mergedPod, injectedPodData, err := RunTemplate(req)
        if err != nil {
                return nil, fmt.Errorf("failed to run injection template: %v", 
err)
@@ -256,6 +255,10 @@ func injectPod(req InjectionParameters) ([]byte, error) {
                return nil, fmt.Errorf("failed to re apply container: %v", err)
        }
 
+       if err := postProcessPod(mergedPod, *injectedPodData, req); err != nil {
+               return nil, fmt.Errorf("failed to process pod: %v", err)
+       }
+
        patch, err := createPatch(mergedPod, originalPodSpec)
        if err != nil {
                return nil, fmt.Errorf("failed to create patch: %v", err)
@@ -264,6 +267,69 @@ func injectPod(req InjectionParameters) ([]byte, error) {
        return patch, nil
 }
 
+func postProcessPod(pod *corev1.Pod, injectedPod corev1.Pod, req 
InjectionParameters) error {
+       if pod.Annotations == nil {
+               pod.Annotations = map[string]string{}
+       }
+       if pod.Labels == nil {
+               pod.Labels = map[string]string{}
+       }
+
+       overwriteClusterInfo(pod, req)
+
+       if err := reorderPod(pod, req); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func reorderPod(pod *corev1.Pod, req InjectionParameters) error {
+       var merr error
+       mc := req.meshConfig
+       // Get copy of pod proxyconfig, to determine container ordering
+       if pca, f := 
req.pod.ObjectMeta.GetAnnotations()[annotation.ProxyConfig.Name]; f {
+               mc, merr = mesh.ApplyProxyConfig(pca, req.meshConfig)
+               if merr != nil {
+                       return merr
+               }
+       }
+
+       // nolint: staticcheck
+       holdPod := 
mc.GetDefaultConfig().GetHoldApplicationUntilProxyStarts().GetValue()
+
+       proxyLocation := MoveLast
+       // If HoldApplicationUntilProxyStarts is set, reorder the proxy location
+       if holdPod {
+               proxyLocation = MoveFirst
+       }
+
+       // Proxy container should be last, unless 
HoldApplicationUntilProxyStarts is set
+       // This is to ensure `kubectl exec` and similar commands continue to 
default to the user's container
+       pod.Spec.Containers = modifyContainers(pod.Spec.Containers, 
ProxyContainerName, proxyLocation)
+       if hasContainer(pod.Spec.InitContainers, ProxyContainerName) {
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, EnableCoreDumpName, MoveFirst)
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, ProxyContainerName, MoveFirst)
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, ValidationContainerName, MoveFirst)
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, InitContainerName, MoveFirst)
+       } else {
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, ValidationContainerName, MoveFirst)
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, InitContainerName, MoveLast)
+               pod.Spec.InitContainers = 
modifyContainers(pod.Spec.InitContainers, EnableCoreDumpName, MoveLast)
+       }
+
+       return nil
+}
+
+func hasContainer(cl []corev1.Container, name string) bool {
+       for _, c := range cl {
+               if c.Name == name {
+                       return true
+               }
+       }
+       return false
+}
+
 func reapplyOverwrittenContainers(finalPod *corev1.Pod, originalPod 
*corev1.Pod, templatePod *corev1.Pod,
        proxyConfig *meshconfig.ProxyConfig,
 ) (*corev1.Pod, error) {
diff --git a/pkg/model/proxy.go b/pkg/model/proxy.go
index e9924c6d..ea270a0c 100644
--- a/pkg/model/proxy.go
+++ b/pkg/model/proxy.go
@@ -5,4 +5,9 @@ import "github.com/apache/dubbo-kubernetes/pkg/cluster"
 type NodeMetadata struct {
        Generator string     `json:"GENERATOR,omitempty"`
        ClusterID cluster.ID `json:"CLUSTER_ID,omitempty"`
+       Namespace string     `json:"NAMESPACE,omitempty"`
+}
+
+type BootstrapNodeMetadata struct {
+       NodeMetadata
 }
diff --git a/pkg/model/xds.go b/pkg/model/xds.go
index 72d334d3..130c49a6 100644
--- a/pkg/model/xds.go
+++ b/pkg/model/xds.go
@@ -8,6 +8,7 @@ const (
        RouteType                  = APITypePrefix + 
"envoy.config.route.v3.RouteConfiguration"
        SecretType                 = APITypePrefix + 
"envoy.extensions.transport_sockets.tls.v3.Secret"
        ExtensionConfigurationType = APITypePrefix + 
"envoy.config.core.v3.TypedExtensionConfig"
+       AddressType                = APITypePrefix + "dubbo.workload.Address"
 
        HealthInfoType = APITypePrefix + "dubbo.v1.HealthInformation"
        DebugType      = "dubbo.io/debug"
diff --git a/pkg/util/protomarshal/protomarshal.go 
b/pkg/util/protomarshal/protomarshal.go
index 8dfbd950..564aace8 100644
--- a/pkg/util/protomarshal/protomarshal.go
+++ b/pkg/util/protomarshal/protomarshal.go
@@ -40,16 +40,10 @@ func Unmarshal(b []byte, m proto.Message) error {
        return strictUnmarshaler.Unmarshal(bytes.NewReader(b), 
legacyproto.MessageV1(m))
 }
 
-func UnmarshalAllowUnknown(b []byte, m proto.Message) error {
-       return unmarshaler.Unmarshal(bytes.NewReader(b), 
legacyproto.MessageV1(m))
-}
-
-// ToJSON marshals a proto to canonical JSON
 func ToJSON(msg proto.Message) (string, error) {
        return ToJSONWithIndent(msg, "")
 }
 
-// Marshal marshals a proto to canonical JSON
 func Marshal(msg proto.Message) ([]byte, error) {
        res, err := ToJSONWithIndent(msg, "")
        if err != nil {
@@ -58,7 +52,6 @@ func Marshal(msg proto.Message) ([]byte, error) {
        return []byte(res), err
 }
 
-// MarshalIndent marshals a proto to canonical JSON with indentation
 func MarshalIndent(msg proto.Message, indent string) ([]byte, error) {
        res, err := ToJSONWithIndent(msg, indent)
        if err != nil {
@@ -67,13 +60,10 @@ func MarshalIndent(msg proto.Message, indent string) 
([]byte, error) {
        return []byte(res), err
 }
 
-// ToJSONWithIndent marshals a proto to canonical JSON with pretty printed 
string
 func ToJSONWithIndent(msg proto.Message, indent string) (string, error) {
        return ToJSONWithOptions(msg, indent, false)
 }
 
-// ToJSONWithOptions marshals a proto to canonical JSON with options to indent 
and
-// print enums' int values
 func ToJSONWithOptions(msg proto.Message, indent string, enumsAsInts bool) 
(string, error) {
        if msg == nil {
                return "", errors.New("unexpected nil message")
@@ -84,19 +74,6 @@ func ToJSONWithOptions(msg proto.Message, indent string, 
enumsAsInts bool) (stri
        return m.MarshalToString(legacyproto.MessageV1(msg))
 }
 
-func ToJSONWithAnyResolver(msg proto.Message, indent string, anyResolver 
jsonpb.AnyResolver) (string, error) {
-       if msg == nil {
-               return "", errors.New("unexpected nil message")
-       }
-
-       m := jsonpb.Marshaler{
-               Indent:      indent,
-               AnyResolver: anyResolver,
-       }
-       return m.MarshalToString(legacyproto.MessageV1(msg))
-}
-
-// ToYAML marshals a proto to canonical YAML
 func ToYAML(msg proto.Message) (string, error) {
        js, err := ToJSON(msg)
        if err != nil {
@@ -106,7 +83,6 @@ func ToYAML(msg proto.Message) (string, error) {
        return string(yml), err
 }
 
-// ApplyJSON unmarshals a JSON string into a proto message.
 func ApplyJSON(js string, pb proto.Message) error {
        reader := strings.NewReader(js)
        m := jsonpb.Unmarshaler{}
@@ -119,15 +95,12 @@ func ApplyJSON(js string, pb proto.Message) error {
        return nil
 }
 
-// ApplyJSONStrict unmarshals a JSON string into a proto message.
 func ApplyJSONStrict(js string, pb proto.Message) error {
        reader := strings.NewReader(js)
        m := jsonpb.Unmarshaler{}
        return m.Unmarshal(reader, legacyproto.MessageV1(pb))
 }
 
-// ApplyYAML unmarshals a YAML string into a proto message.
-// Unknown fields are allowed.
 func ApplyYAML(yml string, pb proto.Message) error {
        js, err := yaml.YAMLToJSON([]byte(yml))
        if err != nil {
@@ -141,14 +114,10 @@ type ComparableMessage interface {
        proto.Message
 }
 
-// Clone is a small wrapper that handles the upstream function not returning a 
typed message
 func Clone[T proto.Message](obj T) T {
        return proto.Clone(obj).(T)
 }
 
-// MessageToStructSlow encodes a protobuf Message into a Struct.
-// It roundtrips trough JSON so it is slow.
-// Copied from 
https://github.com/envoyproxy/go-control-plane/blob/d77bd2ea68bdbb72afd65a7ddf6fe8969e556c45/pkg/conversion/struct.go#L29
 func MessageToStructSlow(msg proto.Message) (*structpb.Struct, error) {
        if msg == nil {
                return nil, errors.New("nil message")
@@ -166,3 +135,18 @@ func MessageToStructSlow(msg proto.Message) 
(*structpb.Struct, error) {
 
        return pbs, nil
 }
+
+func MarshalProtoNames(msg proto.Message) ([]byte, error) {
+       if msg == nil {
+               return nil, errors.New("unexpected nil message")
+       }
+
+       // Marshal from proto to json bytes
+       m := jsonpb.Marshaler{OrigName: true}
+       buf := &bytes.Buffer{}
+       err := m.Marshal(buf, legacyproto.MessageV1(msg))
+       if err != nil {
+               return nil, err
+       }
+       return buf.Bytes(), nil
+}
diff --git a/pkg/util/sets/set.go b/pkg/util/sets/set.go
index d9b0ddd5..6638b569 100644
--- a/pkg/util/sets/set.go
+++ b/pkg/util/sets/set.go
@@ -292,3 +292,11 @@ func DeleteCleanupLast[K comparable, T comparable](m 
map[K]Set[T], k K, v T) {
                delete(m, k)
        }
 }
+
+func (s Set[T]) DeleteContains(item T) bool {
+       if !s.Contains(item) {
+               return false
+       }
+       delete(s, item)
+       return true
+}
diff --git a/pkg/version/version.go b/pkg/version/version.go
index 8ae9ffed..1a6d8d17 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -54,7 +54,11 @@ var (
        gitTag       = "unknown"
        gitCommit    = "unknown"
        buildDate    = "unknown"
-       Envoy        = "unknown"
+)
+
+var (
+       // Info exports the build version information.
+       Info BuildInfo
 )
 
 type BuildInfo struct {
diff --git a/pkg/xds/server.go b/pkg/xds/server.go
index 1527bf97..a8230e14 100644
--- a/pkg/xds/server.go
+++ b/pkg/xds/server.go
@@ -64,7 +64,6 @@ type Watcher interface {
        GetWatchedResource(url string) *WatchedResource
        NewWatchedResource(url string, names []string)
        UpdateWatchedResource(string, func(*WatchedResource) *WatchedResource)
-       // GetID identifies an xDS client. This is different from a connection 
ID.
        GetID() string
 }
 
diff --git a/sail/pkg/model/context.go b/sail/pkg/model/context.go
index f86b60a3..99f35526 100644
--- a/sail/pkg/model/context.go
+++ b/sail/pkg/model/context.go
@@ -18,24 +18,49 @@
 package model
 
 import (
+       "encoding/json"
        "fmt"
        "github.com/apache/dubbo-kubernetes/pkg/config/constants"
        "github.com/apache/dubbo-kubernetes/pkg/config/host"
        "github.com/apache/dubbo-kubernetes/pkg/config/mesh"
        "github.com/apache/dubbo-kubernetes/pkg/config/mesh/meshwatcher"
+       "github.com/apache/dubbo-kubernetes/pkg/config/schema/kind"
+       "github.com/apache/dubbo-kubernetes/pkg/maps"
        pm "github.com/apache/dubbo-kubernetes/pkg/model"
+       "github.com/apache/dubbo-kubernetes/pkg/util/protomarshal"
        "github.com/apache/dubbo-kubernetes/pkg/util/sets"
        "github.com/apache/dubbo-kubernetes/pkg/xds"
+       core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
        discovery 
"github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+       "google.golang.org/protobuf/types/known/structpb"
        meshconfig "istio.io/api/mesh/v1alpha1"
        "net"
+       "sort"
        "strconv"
+       "strings"
        "sync"
        "time"
 )
 
+const (
+       serviceNodeSeparator = "~"
+)
+
+type XdsResourceGenerator interface {
+       // Generate generates the Sotw resources for Xds.
+       Generate(proxy *Proxy, w *WatchedResource, req *PushRequest) 
(Resources, XdsLogDetails, error)
+}
+
+// XdsDeltaResourceGenerator generates Sotw and delta resources.
+type XdsDeltaResourceGenerator interface {
+       XdsResourceGenerator
+       // GenerateDeltas returns the changed and removed resources, along with 
whether or not delta was actually used.
+       GenerateDeltas(proxy *Proxy, req *PushRequest, w *WatchedResource) 
(Resources, DeletedResources, XdsLogDetails, bool, error)
+}
+
 type (
-       NodeMetadata = pm.NodeMetadata
+       NodeMetadata          = pm.NodeMetadata
+       BootstrapNodeMetadata = pm.BootstrapNodeMetadata
 )
 type Watcher = meshwatcher.WatcherCollection
 
@@ -145,8 +170,12 @@ type Proxy struct {
        LastPushTime         time.Time
        WatchedResources     map[string]*WatchedResource
        ID                   string
+       DNSDomain            string
        Metadata             *NodeMetadata
        IPAddresses          []string
+       XdsNode              *core.Node
+       ConfigNamespace      string
+       ServiceTargets       []ServiceTarget
 }
 
 func (node *Proxy) GetWatchedResource(typeURL string) *WatchedResource {
@@ -193,6 +222,43 @@ func (node *Proxy) IsProxylessGrpc() bool {
        return node.Metadata != nil && node.Metadata.Generator == "grpc"
 }
 
+func (node *Proxy) ShouldUpdateServiceTargets(updates sets.Set[ConfigKey]) 
bool {
+       // we only care for services which can actually select this proxy
+       for config := range updates {
+               if config.Kind == kind.ServiceEntry || config.Namespace == 
node.Metadata.Namespace {
+                       return true
+               }
+       }
+
+       return false
+}
+
+func (node *Proxy) SetServiceTargets(serviceDiscovery ServiceDiscovery) {
+       instances := serviceDiscovery.GetProxyServiceTargets(node)
+
+       // Keep service instances in order of creation/hostname.
+       sort.SliceStable(instances, func(i, j int) bool {
+               if instances[i].Service != nil && instances[j].Service != nil {
+                       if 
!instances[i].Service.CreationTime.Equal(instances[j].Service.CreationTime) {
+                               return 
instances[i].Service.CreationTime.Before(instances[j].Service.CreationTime)
+                       }
+                       // Additionally, sort by hostname just in case services 
created automatically at the same second.
+                       return instances[i].Service.Hostname < 
instances[j].Service.Hostname
+               }
+               return true
+       })
+
+       node.ServiceTargets = instances
+}
+
+func (node *Proxy) ShallowCloneWatchedResources() map[string]*WatchedResource {
+       node.RLock()
+       defer node.RUnlock()
+       return maps.Clone(node.WatchedResources)
+}
+
+type DeletedResources = []string
+
 type XdsLogDetails struct {
        Incremental    bool
        AdditionalInfo string
@@ -200,7 +266,72 @@ type XdsLogDetails struct {
 
 type Resources = []*discovery.Resource
 
-type XdsResourceGenerator interface {
-       // Generate generates the Sotw resources for Xds.
-       Generate(proxy *Proxy, w *WatchedResource, req *PushRequest) 
(Resources, XdsLogDetails, error)
+func ParseMetadata(metadata *structpb.Struct) (*NodeMetadata, error) {
+       if metadata == nil {
+               return &NodeMetadata{}, nil
+       }
+
+       bootstrapNodeMeta, err := ParseBootstrapNodeMetadata(metadata)
+       if err != nil {
+               return nil, err
+       }
+       return &bootstrapNodeMeta.NodeMetadata, nil
+}
+
+func ParseBootstrapNodeMetadata(metadata *structpb.Struct) 
(*BootstrapNodeMetadata, error) {
+       if metadata == nil {
+               return &BootstrapNodeMetadata{}, nil
+       }
+
+       b, err := protomarshal.MarshalProtoNames(metadata)
+       if err != nil {
+               return nil, fmt.Errorf("failed to read node metadata %v: %v", 
metadata, err)
+       }
+       meta := &BootstrapNodeMetadata{}
+       if err := json.Unmarshal(b, meta); err != nil {
+               return nil, fmt.Errorf("failed to unmarshal node metadata (%v): 
%v", string(b), err)
+       }
+       return meta, nil
+}
+
+func ParseServiceNodeWithMetadata(nodeID string, metadata *NodeMetadata) 
(*Proxy, error) {
+       parts := strings.Split(nodeID, serviceNodeSeparator)
+       out := &Proxy{
+               Metadata: metadata,
+       }
+
+       if len(parts) != 4 {
+               return out, fmt.Errorf("missing parts in the service node %q", 
nodeID)
+       }
+       // TODO ?
+
+       // Does query from ingress or router have to carry valid IP address?
+       if len(out.IPAddresses) == 0 {
+               return out, fmt.Errorf("no valid IP address in the service node 
id or metadata")
+       }
+
+       out.ID = parts[2]
+       out.DNSDomain = parts[3]
+       return out, nil
+}
+
+func GetProxyConfigNamespace(proxy *Proxy) string {
+       if proxy == nil {
+               return ""
+       }
+
+       // First look for ISTIO_META_CONFIG_NAMESPACE
+       // All newer proxies (from Istio 1.1 onwards) are supposed to supply 
this
+       if len(proxy.Metadata.Namespace) > 0 {
+               return proxy.Metadata.Namespace
+       }
+
+       // if not found, for backward compatibility, extract the namespace from
+       // the proxy domain. this is a k8s specific hack and should be enabled
+       parts := strings.Split(proxy.DNSDomain, ".")
+       if len(parts) > 1 { // k8s will have namespace.<domain>
+               return parts[0]
+       }
+
+       return ""
 }
diff --git a/sail/pkg/model/push_context.go b/sail/pkg/model/push_context.go
index eb147798..a51ae06d 100644
--- a/sail/pkg/model/push_context.go
+++ b/sail/pkg/model/push_context.go
@@ -52,6 +52,7 @@ const (
        HeadlessEndpointUpdate TriggerReason = "headlessendpoint"
        EndpointUpdate         TriggerReason = "endpoint"
        ProxyUpdate            TriggerReason = "proxy"
+       DependentResource      TriggerReason = "depdendentresource"
 )
 
 type ProxyPushStatus struct {
@@ -149,6 +150,10 @@ func (pr *PushRequest) CopyMerge(other *PushRequest) 
*PushRequest {
        return merged
 }
 
+func (pr *PushRequest) IsProxyUpdate() bool {
+       return pr.Reason.Has(ProxyUpdate)
+}
+
 type XDSUpdater interface {
        ConfigUpdate(req *PushRequest)
        ServiceUpdate(shard ShardKey, hostname string, namespace string, event 
Event)
diff --git a/sail/pkg/model/service.go b/sail/pkg/model/service.go
index 95985f8b..dd47b061 100644
--- a/sail/pkg/model/service.go
+++ b/sail/pkg/model/service.go
@@ -279,6 +279,19 @@ func (s *Service) Key() string {
        return s.Attributes.Namespace + "/" + string(s.Hostname)
 }
 
+type (
+       ServicePort         = *Port
+       ServiceInstancePort struct {
+               ServicePort
+               TargetPort uint32
+       }
+)
+
+type ServiceTarget struct {
+       Service *Service
+       Port    ServiceInstancePort
+}
+
 type Port struct {
        Name     string            `json:"name,omitempty"`
        Port     int               `json:"port"`
@@ -306,6 +319,7 @@ func (ports PortList) Equals(other PortList) bool {
 type ServiceDiscovery interface {
        Services() []*Service
        GetService(hostname host.Name) *Service
+       GetProxyServiceTargets(*Proxy) []ServiceTarget
 }
 
 func (s *ServiceAttributes) DeepCopy() ServiceAttributes {
@@ -331,7 +345,6 @@ func (s *ServiceAttributes) DeepCopy() ServiceAttributes {
        out.Aliases = slices.Clone(s.Aliases)
        out.PassthroughTargetPorts = maps.Clone(out.PassthroughTargetPorts)
 
-       // AddressMap contains a mutex, which is safe to return a copy in this 
case.
        // nolint: govet
        return out
 }
diff --git a/sail/pkg/serviceregistry/aggregate/controller.go 
b/sail/pkg/serviceregistry/aggregate/controller.go
index 528de8e7..4a6feaf8 100644
--- a/sail/pkg/serviceregistry/aggregate/controller.go
+++ b/sail/pkg/serviceregistry/aggregate/controller.go
@@ -27,7 +27,6 @@ type Controller struct {
 
 type registryEntry struct {
        serviceregistry.Instance
-       // stop if not nil is the per-registry stop chan. If null, the server 
stop chan should be used to Run the registry.
        stop <-chan struct{}
 }
 
@@ -45,11 +44,9 @@ func NewController(opt Options) *Controller {
        }
 }
 
-// Run starts all the controllers
 func (c *Controller) Run(stop <-chan struct{}) {
        c.storeLock.Lock()
        for _, r := range c.registries {
-               // prefer the per-registry stop channel
                registryStop := stop
                if s := r.stop; s != nil {
                        registryStop = s
@@ -186,3 +183,42 @@ func mergeService(dst, src *model.Service, srcRegistry 
serviceregistry.Instance)
                dst.ClusterVIPs.SetAddressesFor(clusterID, newAddresses)
        }
 }
+
+func (c *Controller) GetProxyServiceTargets(node *model.Proxy) 
[]model.ServiceTarget {
+       out := make([]model.ServiceTarget, 0)
+       nodeClusterID := nodeClusterID(node)
+       for _, r := range c.GetRegistries() {
+               if skipSearchingRegistryForProxy(nodeClusterID, r) {
+                       klog.Infof("GetProxyServiceTargets(): not searching 
registry %v: proxy %v CLUSTER_ID is %v",
+                               r.Cluster(), node.ID, nodeClusterID)
+                       continue
+               }
+
+               instances := r.GetProxyServiceTargets(node)
+               if len(instances) > 0 {
+                       out = append(out, instances...)
+               }
+       }
+
+       if len(out) == 0 {
+               klog.Infof("GetProxyServiceTargets(): no service targets found 
for proxy %s with clusterID %s",
+                       node.ID, nodeClusterID.String())
+       }
+
+       return out
+}
+
+func nodeClusterID(node *model.Proxy) cluster.ID {
+       if node.Metadata == nil || node.Metadata.ClusterID == "" {
+               return ""
+       }
+       return node.Metadata.ClusterID
+}
+
+func skipSearchingRegistryForProxy(nodeClusterID cluster.ID, r 
serviceregistry.Instance) bool {
+       if r.Provider() != provider.Kubernetes || nodeClusterID == "" {
+               return false
+       }
+
+       return !r.Cluster().Equals(nodeClusterID)
+}
diff --git a/sail/pkg/serviceregistry/kube/controller/controller.go 
b/sail/pkg/serviceregistry/kube/controller/controller.go
index 558cfcb3..e0ed972d 100644
--- a/sail/pkg/serviceregistry/kube/controller/controller.go
+++ b/sail/pkg/serviceregistry/kube/controller/controller.go
@@ -187,6 +187,19 @@ func (c *Controller) Services() []*model.Service {
        return out
 }
 
+func (c *Controller) isControllerForProxy(proxy *model.Proxy) bool {
+       return proxy.Metadata.ClusterID == "" || proxy.Metadata.ClusterID == 
c.Cluster()
+}
+
+func (c *Controller) GetProxyServiceTargets(proxy *model.Proxy) 
[]model.ServiceTarget {
+       if !c.isControllerForProxy(proxy) {
+               klog.Errorf("proxy is in cluster %v, but controller is for 
cluster %v", proxy.Metadata.ClusterID, c.Cluster())
+               return nil
+       }
+       // TODO
+       return nil
+}
+
 // GetService implements a service catalog operation by hostname specified.
 func (c *Controller) GetService(hostname host.Name) *model.Service {
        c.RLock()
diff --git a/sail/pkg/xds/ads.go b/sail/pkg/xds/ads.go
index f5bbd7d1..e74bba98 100644
--- a/sail/pkg/xds/ads.go
+++ b/sail/pkg/xds/ads.go
@@ -30,10 +30,14 @@ import (
        "google.golang.org/grpc/peer"
        "google.golang.org/grpc/status"
        "k8s.io/klog/v2"
+       "strconv"
        "strings"
+       "sync/atomic"
        "time"
 )
 
+var connectionNumber = int64(0)
+
 type (
        DiscoveryStream      = xds.DiscoveryStream
        DeltaDiscoveryStream = 
discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer
@@ -91,7 +95,6 @@ func (s *DiscoveryServer) AdsPushAll(req *model.PushRequest) {
                totalService := len(req.Push.GetAllServices())
                klog.Infof("XDS: Pushing Services:%d ConnectedEndpoints:%d", 
totalService, s.adsClientCount())
 
-               // Make sure the ConfigsUpdated map exists
                if req.ConfigsUpdated == nil {
                        req.ConfigsUpdated = make(sets.Set[model.ConfigKey])
                }
@@ -105,7 +108,67 @@ func (s *DiscoveryServer) adsClientCount() int {
        return len(s.adsClients)
 }
 
+func (s *DiscoveryServer) computeProxyState(proxy *model.Proxy, request 
*model.PushRequest) {
+       proxy.Lock()
+       defer proxy.Unlock()
+       // 1. If request == nil(initiation phase) or request.ConfigsUpdated == 
nil(global push), set proxy serviceTargets.
+       // 2. otherwise only set when svc update, this is for the case that a 
service may select the proxy
+       if request == nil || request.Forced ||
+               proxy.ShouldUpdateServiceTargets(request.ConfigsUpdated) {
+               proxy.SetServiceTargets(s.Env.ServiceDiscovery)
+       }
+
+       recomputeLabels := request == nil || request.IsProxyUpdate()
+       if recomputeLabels {
+       }
+
+       push := proxy.LastPushContext
+       if request == nil {
+       } else {
+               push = request.Push
+               if request.Forced {
+               }
+               for conf := range request.ConfigsUpdated {
+                       switch conf.Kind {
+                       }
+               }
+       }
+       proxy.LastPushContext = push
+       if request != nil {
+               proxy.LastPushTime = request.Start
+       }
+}
+
+func connectionID(node string) string {
+       id := atomic.AddInt64(&connectionNumber, 1)
+       return node + "-" + strconv.FormatInt(id, 10)
+}
+
 func (s *DiscoveryServer) initConnection(node *core.Node, con *Connection, 
identities []string) error {
+       proxy, err := s.initProxyMetadata(node)
+       if err != nil {
+               return err
+       }
+
+       if alias, exists := s.ClusterAliases[proxy.Metadata.ClusterID]; exists {
+               proxy.Metadata.ClusterID = alias
+       }
+
+       proxy.LastPushContext = s.globalPushContext()
+       con.SetID(connectionID(proxy.ID))
+       con.node = node
+       con.proxy = proxy
+
+       if err := s.authorize(con, identities); err != nil {
+               return err
+       }
+       s.addCon(con.ID(), con)
+
+       defer con.MarkInitialized()
+       if err := s.initializeProxy(con); err != nil {
+               s.closeConnection(con)
+               return err
+       }
 
        return nil
 }
@@ -114,6 +177,51 @@ func (s *DiscoveryServer) closeConnection(con *Connection) 
{
        if con.ID() == "" {
                return
        }
+       s.removeCon(con.ID())
+}
+
+func (s *DiscoveryServer) initializeProxy(con *Connection) error {
+       proxy := con.proxy
+       s.computeProxyState(proxy, nil)
+       proxy.WatchedResources = map[string]*model.WatchedResource{}
+       // Based on node metadata and version, we can associate a different 
generator.
+       if proxy.Metadata.Generator != "" {
+               proxy.XdsResourceGenerator = 
s.Generators[proxy.Metadata.Generator]
+       }
+
+       return nil
+}
+
+func (s *DiscoveryServer) initProxyMetadata(node *core.Node) (*model.Proxy, 
error) {
+       meta, err := model.ParseMetadata(node.Metadata)
+       if err != nil {
+               return nil, status.New(codes.InvalidArgument, err.Error()).Err()
+       }
+       proxy, err := model.ParseServiceNodeWithMetadata(node.Id, meta)
+       if err != nil {
+               return nil, status.New(codes.InvalidArgument, err.Error()).Err()
+       }
+       // Update the config namespace associated with this proxy
+       proxy.ConfigNamespace = model.GetProxyConfigNamespace(proxy)
+       proxy.XdsNode = node
+       return proxy, nil
+}
+
+func (s *DiscoveryServer) addCon(conID string, con *Connection) {
+       s.adsClientsMutex.Lock()
+       defer s.adsClientsMutex.Unlock()
+       s.adsClients[conID] = con
+}
+
+func (s *DiscoveryServer) removeCon(conID string) {
+       s.adsClientsMutex.Lock()
+       defer s.adsClientsMutex.Unlock()
+
+       if _, exist := s.adsClients[conID]; !exist {
+               klog.Errorf("ADS: Removing connection for non-existing 
node:%v.", conID)
+       } else {
+               delete(s.adsClients, conID)
+       }
 }
 
 func (s *DiscoveryServer) Stream(stream DiscoveryStream) error {
@@ -153,11 +261,8 @@ func (s *DiscoveryServer) Stream(stream DiscoveryStream) 
error {
 
 func (s *DiscoveryServer) WaitForRequestLimit(ctx context.Context) error {
        if s.RequestRateLimit.Limit() == 0 {
-               // Allow opt out when rate limiting is set to 0qps
                return nil
        }
-       // Give a bit of time for queue to clear out, but if not fail fast. 
Client will connect to another
-       // instance in best case, or retry with backoff.
        wait, cancel := context.WithTimeout(ctx, time.Second)
        defer cancel()
        return s.RequestRateLimit.Wait(wait)
@@ -169,8 +274,32 @@ func newConnection(peerAddr string, stream 
DiscoveryStream) *Connection {
        }
 }
 
+var PushOrder = []string{
+       v3.ClusterType,
+       v3.EndpointType,
+       v3.ListenerType,
+       v3.RouteType,
+       v3.AddressType,
+}
+
+var KnownOrderedTypeUrls = sets.New(PushOrder...)
+
 func (conn *Connection) watchedResourcesByOrder() []*model.WatchedResource {
-       return nil
+       allWatched := conn.proxy.ShallowCloneWatchedResources()
+       ordered := make([]*model.WatchedResource, 0, len(allWatched))
+       // first add all known types, in order
+       for _, tp := range PushOrder {
+               if allWatched[tp] != nil {
+                       ordered = append(ordered, allWatched[tp])
+               }
+       }
+       // Then add any undeclared types
+       for tp, res := range allWatched {
+               if !KnownOrderedTypeUrls.Contains(tp) {
+                       ordered = append(ordered, res)
+               }
+       }
+       return ordered
 }
 
 func (conn *Connection) Initialize(node *core.Node) error {
@@ -214,7 +343,6 @@ func (s *DiscoveryServer) processRequest(req 
*discovery.DiscoveryRequest, con *C
                return nil
        }
 
-       // For now, don't let xDS piggyback debug requests start watchers.
        if strings.HasPrefix(req.TypeUrl, v3.DebugType) {
                return s.pushXds(con,
                        &model.WatchedResource{TypeUrl: req.TypeUrl, 
ResourceNames: sets.New(req.ResourceNames...)},
@@ -230,10 +358,6 @@ func (s *DiscoveryServer) processRequest(req 
*discovery.DiscoveryRequest, con *C
                Full:   true,
                Push:   con.proxy.LastPushContext,
                Reason: model.NewReasonStats(model.ProxyRequest),
-
-               // The usage of LastPushTime (rather than time.Now()), is 
critical here for correctness; This time
-               // is used by the XDS cache to determine if a entry is stale. 
If we use Now() with an old push context,
-               // we may end up overriding active cache entries with stale 
ones.
                Start:  con.proxy.LastPushTime,
                Delta:  delta,
                Forced: true,
diff --git a/sail/pkg/xds/auth.go b/sail/pkg/xds/auth.go
index 02780d17..1b5b0569 100644
--- a/sail/pkg/xds/auth.go
+++ b/sail/pkg/xds/auth.go
@@ -7,3 +7,10 @@ import (
 func (s *DiscoveryServer) authenticate(ctx context.Context) ([]string, error) {
        return nil, nil
 }
+
+func (s *DiscoveryServer) authorize(con *Connection, identities []string) 
error {
+       if con == nil || con.proxy == nil {
+               return nil
+       }
+       return nil
+}
diff --git a/sail/pkg/xds/delta.go b/sail/pkg/xds/delta.go
index 71b36305..1f8f7184 100644
--- a/sail/pkg/xds/delta.go
+++ b/sail/pkg/xds/delta.go
@@ -2,17 +2,78 @@ package xds
 
 import (
        "errors"
+       "github.com/apache/dubbo-kubernetes/pkg/util/sets"
        "github.com/apache/dubbo-kubernetes/pkg/xds"
        dubbogrpc "github.com/apache/dubbo-kubernetes/sail/pkg/grpc"
        "github.com/apache/dubbo-kubernetes/sail/pkg/model"
        v3 "github.com/apache/dubbo-kubernetes/sail/pkg/xds/v3"
        discovery 
"github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+       "github.com/google/uuid"
        "google.golang.org/grpc/codes"
        "google.golang.org/grpc/peer"
        "google.golang.org/grpc/status"
        "k8s.io/klog/v2"
+       "strings"
+       "time"
 )
 
+func (s *DiscoveryServer) processDeltaRequest(req 
*discovery.DeltaDiscoveryRequest, con *Connection) error {
+       stype := v3.GetShortType(req.TypeUrl)
+       klog.Infof("ADS:%s: REQ %s resources sub:%d unsub:%d nonce:%s", stype,
+               con.ID(), len(req.ResourceNamesSubscribe), 
len(req.ResourceNamesUnsubscribe), req.ResponseNonce)
+
+       if req.TypeUrl == v3.HealthInfoType {
+               return nil
+       }
+       if strings.HasPrefix(req.TypeUrl, v3.DebugType) {
+               return s.pushDeltaXds(con,
+                       &model.WatchedResource{TypeUrl: req.TypeUrl, 
ResourceNames: sets.New(req.ResourceNamesSubscribe...)},
+                       &model.PushRequest{Full: true, Push: 
con.proxy.LastPushContext, Forced: true})
+       }
+
+       shouldRespond := shouldRespondDelta(con, req)
+       if !shouldRespond {
+               return nil
+       }
+
+       subs, _, _ := deltaWatchedResources(nil, req)
+       request := &model.PushRequest{
+               Full:   true,
+               Push:   con.proxy.LastPushContext,
+               Reason: model.NewReasonStats(model.ProxyRequest),
+               Start:  con.proxy.LastPushTime,
+               Delta: model.ResourceDelta{
+                       Subscribed:   subs,
+                       Unsubscribed: 
sets.New(req.ResourceNamesUnsubscribe...).Delete("*"),
+               },
+               Forced: true,
+       }
+
+       err := s.pushDeltaXds(con, con.proxy.GetWatchedResource(req.TypeUrl), 
request)
+       if err != nil {
+               return err
+       }
+       if req.TypeUrl != v3.ClusterType {
+               return nil
+       }
+       return s.forceEDSPush(con)
+}
+
+func (s *DiscoveryServer) forceEDSPush(con *Connection) error {
+       if dwr := con.proxy.GetWatchedResource(v3.EndpointType); dwr != nil {
+               request := &model.PushRequest{
+                       Full:   true,
+                       Push:   con.proxy.LastPushContext,
+                       Reason: model.NewReasonStats(model.DependentResource),
+                       Start:  con.proxy.LastPushTime,
+                       Forced: true,
+               }
+               klog.Infof("ADS:%s: FORCE %s PUSH for warming.", 
v3.GetShortType(v3.EndpointType), con.ID())
+               return s.pushDeltaXds(con, dwr, request)
+       }
+       return nil
+}
+
 func (s *DiscoveryServer) StreamDeltas(stream DeltaDiscoveryStream) error {
        if !s.IsServerReady() {
                return errors.New("server is not ready to serve discovery 
information")
@@ -39,30 +100,14 @@ func (s *DiscoveryServer) StreamDeltas(stream 
DeltaDiscoveryStream) error {
                klog.V(2).Infof("Unauthenticated XDS: %v", peerAddr)
        }
 
-       // InitContext returns immediately if the context was already 
initialized.
        s.globalPushContext().InitContext(s.Env, nil, nil)
        con := newDeltaConnection(peerAddr, stream)
 
-       // Do not call: defer close(con.pushChannel). The push channel will be 
garbage collected
-       // when the connection is no longer used. Closing the channel can cause 
subtle race conditions
-       // with push. According to the spec: "It's only necessary to close a 
channel when it is important
-       // to tell the receiving goroutines that all data have been sent."
-
-       // Block until either a request is received or a push is triggered.
-       // We need 2 go routines because 'read' blocks in Recv().
        go s.receiveDelta(con, ids)
 
-       // Wait for the proxy to be fully initialized before we start serving 
traffic. Because
-       // initialization doesn't have dependencies that will block, there is 
no need to add any timeout
-       // here. Prior to this explicit wait, we were implicitly waiting by 
receive() not sending to
-       // reqChannel and the connection not being enqueued for pushes to 
pushChannel until the
-       // initialization is complete.
        <-con.InitializedCh()
 
        for {
-               // Go select{} statements are not ordered; the same channel can 
be chosen many times.
-               // For requests, these are higher priority (client may be 
blocked on startup until these are done)
-               // and often very cheap to handle (simple ACK), so we check it 
first.
                select {
                case req, ok := <-con.deltaReqChan:
                        if ok {
@@ -77,10 +122,6 @@ func (s *DiscoveryServer) StreamDeltas(stream 
DeltaDiscoveryStream) error {
                        return nil
                default:
                }
-               // If there wasn't already a request, poll for requests and 
pushes. Note: if we have a huge
-               // amount of incoming requests, we may still send some pushes, 
as we do not `continue` above;
-               // however, requests will be handled ~2x as much as pushes. 
This ensures a wave of requests
-               // cannot completely starve pushes. However, this scenario is 
unlikely.
                select {
                case req, ok := <-con.deltaReqChan:
                        if ok {
@@ -88,7 +129,6 @@ func (s *DiscoveryServer) StreamDeltas(stream 
DeltaDiscoveryStream) error {
                                        return err
                                }
                        } else {
-                               // Remote side closed connection or error 
processing the request.
                                return <-con.ErrorCh()
                        }
                case ev := <-con.PushCh():
@@ -108,7 +148,6 @@ func (s *DiscoveryServer) receiveDelta(con *Connection, 
identities []string) {
        defer func() {
                close(con.deltaReqChan)
                close(con.ErrorCh())
-               // Close the initialized channel, if its not already closed, to 
prevent blocking the stream
                select {
                case <-con.InitializedCh():
                default:
@@ -127,7 +166,6 @@ func (s *DiscoveryServer) receiveDelta(con *Connection, 
identities []string) {
                        klog.Errorf("ADS: %q %s terminated with error: %v", 
con.Peer(), con.ID(), err)
                        return
                }
-               // This should be only set for the first request. The node id 
may not be set - for example malicious clients.
                if firstRequest {
                        // probe happens before envoy sends first xDS request
                        if req.TypeUrl == v3.HealthInfoType {
@@ -182,16 +220,190 @@ func (s *DiscoveryServer) pushConnectionDelta(con 
*Connection, pushEv *Event) er
        return nil
 }
 
-func (s *DiscoveryServer) processDeltaRequest(req 
*discovery.DeltaDiscoveryRequest, con *Connection) error {
+func (s *DiscoveryServer) pushDeltaXds(con *Connection, w 
*model.WatchedResource, req *model.PushRequest) error {
+       if w == nil {
+               return nil
+       }
+       gen := s.findGenerator(w.TypeUrl, con)
+       if gen == nil {
+               return nil
+       }
+
+       var logFiltered string
+       var res model.Resources
+       var deletedRes model.DeletedResources
+       var logdata model.XdsLogDetails
+       var usedDelta bool
+       var err error
+       switch g := gen.(type) {
+       case model.XdsDeltaResourceGenerator:
+               res, deletedRes, logdata, usedDelta, err = 
g.GenerateDeltas(con.proxy, req, w)
+       case model.XdsResourceGenerator:
+               res, logdata, err = g.Generate(con.proxy, w, req)
+       }
+       if err != nil || (res == nil && deletedRes == nil) {
+               return err
+       }
+       defer func() {}()
+       resp := &discovery.DeltaDiscoveryResponse{
+               ControlPlane: ControlPlane(w.TypeUrl),
+               TypeUrl:      w.TypeUrl,
+               // TODO: send different version for incremental eds
+               SystemVersionInfo: req.Push.PushVersion,
+               Nonce:             nonce(req.Push.PushVersion),
+               Resources:         res,
+       }
+       if usedDelta {
+               resp.RemovedResources = deletedRes
+       } else if req.Full {
+               // similar to sotw
+               removed := w.ResourceNames.Copy()
+               for _, r := range res {
+                       removed.Delete(r.Name)
+               }
+               resp.RemovedResources = sets.SortedList(removed)
+       }
+       var newResourceNames sets.String
+       if len(resp.RemovedResources) > 0 {
+               klog.Infof("ADS:%v REMOVE for node:%s %v", 
v3.GetShortType(w.TypeUrl), con.ID(), resp.RemovedResources)
+       }
+
+       ptype := "PUSH"
+       info := ""
+       if logdata.Incremental {
+               ptype = "PUSH INC"
+       }
+       if len(logdata.AdditionalInfo) > 0 {
+               info = " " + logdata.AdditionalInfo
+       }
+       if len(logFiltered) > 0 {
+               info += logFiltered
+       }
+
+       if err := con.sendDelta(resp, newResourceNames); err != nil {
+               return err
+       }
+
+       switch {
+       case !req.Full:
+       default:
+               klog.Infof("%s: %s%s for node:%s resources:%d removed:%d 
size:%v%s%s",
+                       v3.GetShortType(w.TypeUrl), ptype, req.PushReason(), 
con.proxy.ID, len(res), len(resp.RemovedResources))
+       }
+
        return nil
 }
 
-func (s *DiscoveryServer) computeProxyState(proxy *model.Proxy, request 
*model.PushRequest) {
-       return
+func deltaWatchedResources(existing sets.String, request 
*discovery.DeltaDiscoveryRequest) (sets.String, bool, bool) {
+       res := existing
+       if res == nil {
+               res = sets.New[string]()
+       }
+       changed := false
+       for _, r := range request.ResourceNamesSubscribe {
+               if !res.InsertContains(r) {
+                       changed = true
+               }
+       }
+       for r := range request.InitialResourceVersions {
+               if !res.InsertContains(r) {
+                       changed = true
+               }
+       }
+       for _, r := range request.ResourceNamesUnsubscribe {
+               if res.DeleteContains(r) {
+                       changed = true
+               }
+       }
+       wildcard := false
+       if res.Contains("*") {
+               wildcard = true
+               res.Delete("*")
+       }
+       if len(request.ResourceNamesSubscribe) == 0 {
+               wildcard = true
+       }
+       return res, wildcard, changed
 }
 
-func (s *DiscoveryServer) pushDeltaXds(con *Connection, w 
*model.WatchedResource, req *model.PushRequest) error {
-       return nil
+func shouldRespondDelta(con *Connection, request 
*discovery.DeltaDiscoveryRequest) bool {
+       stype := v3.GetShortType(request.TypeUrl)
+
+       if request.ErrorDetail != nil {
+               errCode := codes.Code(request.ErrorDetail.Code)
+               klog.Warningf("ADS:%s: ACK ERROR %s %s:%s", stype, con.ID(), 
errCode.String(), request.ErrorDetail.GetMessage())
+               con.proxy.UpdateWatchedResource(request.TypeUrl, func(wr 
*model.WatchedResource) *model.WatchedResource {
+                       wr.LastError = request.ErrorDetail.GetMessage()
+                       return wr
+               })
+               return false
+       }
+
+       klog.Infof("ADS:%s REQUEST %v: sub:%v unsub:%v initial:%v", stype, 
con.ID(),
+               request.ResourceNamesSubscribe, 
request.ResourceNamesUnsubscribe, request.InitialResourceVersions)
+       previousInfo := con.proxy.GetWatchedResource(request.TypeUrl)
+
+       if previousInfo == nil {
+               con.proxy.Lock()
+               defer con.proxy.Unlock()
+
+               if len(request.InitialResourceVersions) > 0 {
+                       klog.Infof("ADS:%s: RECONNECT %s %s resources:%v", 
stype, con.ID(), request.ResponseNonce, len(request.InitialResourceVersions))
+               } else {
+                       klog.Infof("ADS:%s: INIT %s %s", stype, con.ID(), 
request.ResponseNonce)
+               }
+
+               res, wildcard, _ := deltaWatchedResources(nil, request)
+               skip := request.TypeUrl == v3.AddressType && wildcard
+               if skip {
+                       res = nil
+               }
+               con.proxy.WatchedResources[request.TypeUrl] = 
&model.WatchedResource{
+                       TypeUrl:       request.TypeUrl,
+                       ResourceNames: res,
+                       Wildcard:      wildcard,
+               }
+               return true
+       }
+
+       if request.ResponseNonce != "" && request.ResponseNonce != 
previousInfo.NonceSent {
+               klog.Infof("ADS:%s: REQ %s Expired nonce received %s, sent %s", 
stype,
+                       con.ID(), request.ResponseNonce, previousInfo.NonceSent)
+               return false
+       }
+
+       spontaneousReq := request.ResponseNonce == ""
+
+       var alwaysRespond bool
+       var subChanged bool
+
+       con.proxy.UpdateWatchedResource(request.TypeUrl, func(wr 
*model.WatchedResource) *model.WatchedResource {
+               wr.ResourceNames, _, subChanged = 
deltaWatchedResources(wr.ResourceNames, request)
+               if !spontaneousReq {
+                       wr.LastError = ""
+                       wr.NonceAcked = request.ResponseNonce
+               }
+               alwaysRespond = wr.AlwaysRespond
+               wr.AlwaysRespond = false
+               return wr
+       })
+
+       if spontaneousReq && !subChanged || !spontaneousReq && subChanged {
+               klog.Infof("ADS:%s: Subscribed resources check mismatch: %v vs 
%v", stype, spontaneousReq, subChanged)
+       }
+
+       if !subChanged {
+               if alwaysRespond {
+                       klog.Infof("ADS:%s: FORCE RESPONSE %s for warming.", 
stype, con.ID())
+                       return true
+               }
+
+               klog.Infof("ADS:%s: ACK %s %s", stype, con.ID(), 
request.ResponseNonce)
+               return false
+       }
+       klog.Infof("ADS:%s: RESOURCE CHANGE %s %s", stype, con.ID(), 
request.ResponseNonce)
+
+       return true
 }
 
 func newDeltaConnection(peerAddr string, stream DeltaDiscoveryStream) 
*Connection {
@@ -201,3 +413,34 @@ func newDeltaConnection(peerAddr string, stream 
DeltaDiscoveryStream) *Connectio
                deltaReqChan: make(chan *discovery.DeltaDiscoveryRequest, 1),
        }
 }
+
+func nonce(noncePrefix string) string {
+       return noncePrefix + uuid.New().String()
+}
+
+func (conn *Connection) sendDelta(res *discovery.DeltaDiscoveryResponse, 
newResourceNames sets.String) error {
+       sendResonse := func() error {
+               defer func() {}()
+               return conn.deltaStream.Send(res)
+       }
+       err := sendResonse()
+       if err == nil {
+               if !strings.HasPrefix(res.TypeUrl, v3.DebugType) {
+                       conn.proxy.UpdateWatchedResource(res.TypeUrl, func(wr 
*model.WatchedResource) *model.WatchedResource {
+                               if wr == nil {
+                                       wr = &model.WatchedResource{TypeUrl: 
res.TypeUrl}
+                               }
+                               // some resources dynamically update 
ResourceNames. Most don't though
+                               if newResourceNames != nil {
+                                       wr.ResourceNames = newResourceNames
+                               }
+                               wr.NonceSent = res.Nonce
+                               wr.LastSendTime = time.Now()
+                               return wr
+                       })
+               }
+       } else if status.Convert(err).Code() == codes.DeadlineExceeded {
+               klog.Infof("Timeout writing %s: %v", conn.ID(), 
v3.GetShortType(res.TypeUrl))
+       }
+       return err
+}
diff --git a/sail/pkg/xds/v3/model.go b/sail/pkg/xds/v3/model.go
index 38aad5fb..bc589282 100644
--- a/sail/pkg/xds/v3/model.go
+++ b/sail/pkg/xds/v3/model.go
@@ -9,6 +9,7 @@ const (
        RouteType      = model.RouteType
        DebugType      = model.DebugType
        HealthInfoType = model.HealthInfoType
+       AddressType    = model.AddressType
 )
 
 func GetShortType(typeURL string) string {
diff --git a/sail/pkg/xds/xdsgen.go b/sail/pkg/xds/xdsgen.go
index cc2a0598..8de5422b 100644
--- a/sail/pkg/xds/xdsgen.go
+++ b/sail/pkg/xds/xdsgen.go
@@ -1,14 +1,48 @@
 package xds
 
 import (
+       "encoding/json"
+       "github.com/apache/dubbo-kubernetes/pkg/env"
+       "github.com/apache/dubbo-kubernetes/pkg/lazy"
+       dubboversion "github.com/apache/dubbo-kubernetes/pkg/version"
+       "github.com/apache/dubbo-kubernetes/pkg/xds"
        "github.com/apache/dubbo-kubernetes/sail/pkg/model"
+       core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
        discovery 
"github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
-       "strings"
-
-       "github.com/apache/dubbo-kubernetes/pkg/xds"
+       "k8s.io/klog/v2"
        "strconv"
+       "strings"
 )
 
+type IstioControlPlaneInstance struct {
+       // The Istio component type (e.g. "istiod")
+       Component string
+       // The ID of the component instance
+       ID string
+       // The Istio version
+       Info dubboversion.BuildInfo
+}
+
+var controlPlane = lazy.New(func() (*core.ControlPlane, error) {
+       // The Pod Name (instance identity) is in PilotArgs, but not reachable 
globally nor from DiscoveryServer
+       podName := env.Register("POD_NAME", "", "").Get()
+       byVersion, err := json.Marshal(IstioControlPlaneInstance{
+               Component: "dubbod",
+               ID:        podName,
+               Info:      dubboversion.Info,
+       })
+       if err != nil {
+               klog.Warningf("XDS: Could not serialize control plane id: %v", 
err)
+       }
+       return &core.ControlPlane{Identifier: string(byVersion)}, nil
+})
+
+func ControlPlane(typ string) *core.ControlPlane {
+       // Error will never happen because the getter of lazy does not return 
error.
+       cp, _ := controlPlane.Get()
+       return cp
+}
+
 func (s *DiscoveryServer) pushXds(con *Connection, w *model.WatchedResource, 
req *model.PushRequest) error {
        if w == nil {
                return nil

Reply via email to