Repository: kumahq/kuma
Pull request #2551 (2021-08-11 15:44:18)

chore(kuma-cp) migrate DiscoveryRequest/Response in KDS to V3 (backport #2541)
This is an automatic backport of pull request #2541 done by [Mergify](https://mergify.io).
Cherry-pick of 5752bd67ce86a35e2ed373fa2cb8632e622eddbe has failed:
```
On branch mergify/bp/release-1.2/pr-2541
Your branch is up to date with 'origin/release-1.2'.

You are currently cherry-picking commit 5752bd67.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   api/mesh/v1alpha1/kds.pb.go
	modified:   api/mesh/v1alpha1/kds.proto
	modified:   api/mesh/v1alpha1/mux.pb.go
	modified:   api/mesh/v1alpha1/mux.proto
	modified:   pkg/kds/client/sink_test.go
	modified:   pkg/kds/client/stream.go
	modified:   pkg/kds/mux/client.go
	modified:   pkg/kds/mux/clientstream.go
	modified:   pkg/kds/mux/serverstream.go
	modified:   pkg/kds/mux/session.go
	modified:   pkg/kds/mux/session_test.go
	new file:   pkg/kds/mux/version.go
	modified:   pkg/kds/reconcile/interfaces.go
	modified:   pkg/kds/reconcile/reconciler.go
	modified:   pkg/kds/reconcile/snapshot_generator.go
	modified:   pkg/kds/server/components.go
	modified:   pkg/kds/server/kds.go
	modified:   pkg/kds/server/server_test.go
	modified:   pkg/kds/server/status_tracker.go
	modified:   pkg/kds/util/resource.go
	modified:   pkg/test/grpc/clientstream.go
	modified:   pkg/test/grpc/serverstream.go
	modified:   pkg/test/kds/verifier/context.go
	modified:   pkg/test/kds/verifier/executables.go
	modified:   test/e2e/compatibility/compatibility_suite_test.go
	new file:   test/e2e/compatibility/cp_compatibility_kubernetes_multizone.go
	new file:   test/e2e/compatibility/cp_compatibility_kubernetes_multizone_test.go
	modified:   test/framework/setup.go

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   pkg/kds/cache/snapshot.go
	both modified:   pkg/kds/server/type_adjust_callbacks.go

```


To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.io/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.io/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.io/
</details>


codecov-commenter: # [Codecov](https://codecov.io/gh/kumahq/kuma/pull/2551?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) Report
> :exclamation: No coverage uploaded for pull request base (`release-1.2@82dd4ec`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#section-missing-base-commit).
> The diff coverage is `n/a`.

> :exclamation: Current head 5752bd6 differs from pull request most recent head 6d46374. Consider uploading reports for the commit 6d46374 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/kumahq/kuma/pull/2551/graphs/tree.svg?width=650&height=150&src=pr&token=D7Yvqs6rwV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)](https://codecov.io/gh/kumahq/kuma/pull/2551?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)

```diff
@@              Coverage Diff               @@
##             release-1.2    #2551   +/-   ##
==============================================
  Coverage               ?   52.36%           
==============================================
  Files                  ?      871           
  Lines                  ?    48437           
  Branches               ?        0           
==============================================
  Hits                   ?    25366           
  Misses                 ?    21020           
  Partials               ?     2051           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/kumahq/kuma/pull/2551?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kumahq/kuma/pull/2551?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Last update [82dd4ec...6d46374](https://codecov.io/gh/kumahq/kuma/pull/2551?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).

jakubdyszkiewicz: @Mergifyio refresh
mergify[bot]: **Command `refresh`: success**
> **Pull request refreshed**
> 


Patch:
diff --git a/api/mesh/v1alpha1/kds.pb.go b/api/mesh/v1alpha1/kds.pb.go
index 88f3b97e9..f0e2a96bf 100644
--- a/api/mesh/v1alpha1/kds.pb.go
+++ b/api/mesh/v1alpha1/kds.pb.go
@@ -8,7 +8,7 @@ package v1alpha1
 
 import (
 	context "context"
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	grpc "google.golang.org/grpc"
 	codes "google.golang.org/grpc/codes"
 	status "google.golang.org/grpc/status"
@@ -168,54 +168,45 @@ var file_mesh_v1alpha1_kds_proto_rawDesc = []byte{
 	0x6b, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x6b, 0x75, 0x6d, 0x61, 0x2e,
 	0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1f, 0x67,
 	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
-	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c,
-	0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x69, 0x73,
-	0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
-	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc8, 0x02, 0x0a, 0x0c, 0x4b, 0x75, 0x6d, 0x61,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65,
-	0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x75, 0x6d, 0x61,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d,
-	0x65, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x1a, 0xd2, 0x01,
-	0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65,
-	0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x12, 0x3f,
-	0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-	0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12,
-	0x47, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
-	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61,
-	0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
-	0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
-	0x6f, 0x6e, 0x32, 0xae, 0x02, 0x0a, 0x14, 0x4b, 0x75, 0x6d, 0x61, 0x44, 0x69, 0x73, 0x63, 0x6f,
-	0x76, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x44,
-	0x65, 0x6c, 0x74, 0x61, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x73, 0x12, 0x23, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32,
-	0x2e, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x61,
-	0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x44, 0x69, 0x73, 0x63, 0x6f,
-	0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01,
-	0x12, 0x5a, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e,
-	0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e,
-	0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x55, 0x0a, 0x12,
-	0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x73, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
-	0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
-	0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70,
-	0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a,
+	0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x64, 0x69,
+	0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x76, 0x33, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f,
+	0x76, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc8, 0x02, 0x0a, 0x0c, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74,
+	0x61, 0x12, 0x28, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x1a, 0xd2, 0x01, 0x0a, 0x04,
+	0x4d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x73, 0x68,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x12, 0x3f, 0x0a, 0x0d,
+	0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
+	0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x47, 0x0a,
+	0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69,
+	0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x32, 0x8e, 0x01, 0x0a, 0x14, 0x4b, 0x75, 0x6d, 0x61, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65,
+	0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x76, 0x0a, 0x13, 0x53, 0x74, 0x72,
+	0x65, 0x61, 0x6d, 0x4b, 0x75, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+	0x12, 0x2c, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x69,
+	0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d,
+	0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x64,
+	0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x69, 0x73, 0x63,
+	0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30,
+	0x01, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+	0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f,
+	0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -232,28 +223,22 @@ func file_mesh_v1alpha1_kds_proto_rawDescGZIP() []byte {
 
 var file_mesh_v1alpha1_kds_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_mesh_v1alpha1_kds_proto_goTypes = []interface{}{
-	(*KumaResource)(nil),              // 0: kuma.mesh.v1alpha1.KumaResource
-	(*KumaResource_Meta)(nil),         // 1: kuma.mesh.v1alpha1.KumaResource.Meta
-	(*anypb.Any)(nil),                 // 2: google.protobuf.Any
-	(*timestamppb.Timestamp)(nil),     // 3: google.protobuf.Timestamp
-	(*v2.DeltaDiscoveryRequest)(nil),  // 4: envoy.api.v2.DeltaDiscoveryRequest
-	(*v2.DiscoveryRequest)(nil),       // 5: envoy.api.v2.DiscoveryRequest
-	(*v2.DeltaDiscoveryResponse)(nil), // 6: envoy.api.v2.DeltaDiscoveryResponse
-	(*v2.DiscoveryResponse)(nil),      // 7: envoy.api.v2.DiscoveryResponse
+	(*KumaResource)(nil),          // 0: kuma.mesh.v1alpha1.KumaResource
+	(*KumaResource_Meta)(nil),     // 1: kuma.mesh.v1alpha1.KumaResource.Meta
+	(*anypb.Any)(nil),             // 2: google.protobuf.Any
+	(*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp
+	(*v3.DiscoveryRequest)(nil),   // 4: envoy.service.discovery.v3.DiscoveryRequest
+	(*v3.DiscoveryResponse)(nil),  // 5: envoy.service.discovery.v3.DiscoveryResponse
 }
 var file_mesh_v1alpha1_kds_proto_depIdxs = []int32{
 	1, // 0: kuma.mesh.v1alpha1.KumaResource.meta:type_name -> kuma.mesh.v1alpha1.KumaResource.Meta
 	2, // 1: kuma.mesh.v1alpha1.KumaResource.spec:type_name -> google.protobuf.Any
 	3, // 2: kuma.mesh.v1alpha1.KumaResource.Meta.creation_time:type_name -> google.protobuf.Timestamp
 	3, // 3: kuma.mesh.v1alpha1.KumaResource.Meta.modification_time:type_name -> google.protobuf.Timestamp
-	4, // 4: kuma.mesh.v1alpha1.KumaDiscoveryService.DeltaKumaResources:input_type -> envoy.api.v2.DeltaDiscoveryRequest
-	5, // 5: kuma.mesh.v1alpha1.KumaDiscoveryService.StreamKumaResources:input_type -> envoy.api.v2.DiscoveryRequest
-	5, // 6: kuma.mesh.v1alpha1.KumaDiscoveryService.FetchKumaResources:input_type -> envoy.api.v2.DiscoveryRequest
-	6, // 7: kuma.mesh.v1alpha1.KumaDiscoveryService.DeltaKumaResources:output_type -> envoy.api.v2.DeltaDiscoveryResponse
-	7, // 8: kuma.mesh.v1alpha1.KumaDiscoveryService.StreamKumaResources:output_type -> envoy.api.v2.DiscoveryResponse
-	7, // 9: kuma.mesh.v1alpha1.KumaDiscoveryService.FetchKumaResources:output_type -> envoy.api.v2.DiscoveryResponse
-	7, // [7:10] is the sub-list for method output_type
-	4, // [4:7] is the sub-list for method input_type
+	4, // 4: kuma.mesh.v1alpha1.KumaDiscoveryService.StreamKumaResources:input_type -> envoy.service.discovery.v3.DiscoveryRequest
+	5, // 5: kuma.mesh.v1alpha1.KumaDiscoveryService.StreamKumaResources:output_type -> envoy.service.discovery.v3.DiscoveryResponse
+	5, // [5:6] is the sub-list for method output_type
+	4, // [4:5] is the sub-list for method input_type
 	4, // [4:4] is the sub-list for extension type_name
 	4, // [4:4] is the sub-list for extension extendee
 	0, // [0:4] is the sub-list for field type_name
@@ -322,9 +307,7 @@ const _ = grpc.SupportPackageIsVersion6
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
 type KumaDiscoveryServiceClient interface {
-	DeltaKumaResources(ctx context.Context, opts ...grpc.CallOption) (KumaDiscoveryService_DeltaKumaResourcesClient, error)
 	StreamKumaResources(ctx context.Context, opts ...grpc.CallOption) (KumaDiscoveryService_StreamKumaResourcesClient, error)
-	FetchKumaResources(ctx context.Context, in *v2.DiscoveryRequest, opts ...grpc.CallOption) (*v2.DiscoveryResponse, error)
 }
 
 type kumaDiscoveryServiceClient struct {
@@ -335,39 +318,8 @@ func NewKumaDiscoveryServiceClient(cc grpc.ClientConnInterface) KumaDiscoverySer
 	return &kumaDiscoveryServiceClient{cc}
 }
 
-func (c *kumaDiscoveryServiceClient) DeltaKumaResources(ctx context.Context, opts ...grpc.CallOption) (KumaDiscoveryService_DeltaKumaResourcesClient, error) {
-	stream, err := c.cc.NewStream(ctx, &_KumaDiscoveryService_serviceDesc.Streams[0], "/kuma.mesh.v1alpha1.KumaDiscoveryService/DeltaKumaResources", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &kumaDiscoveryServiceDeltaKumaResourcesClient{stream}
-	return x, nil
-}
-
-type KumaDiscoveryService_DeltaKumaResourcesClient interface {
-	Send(*v2.DeltaDiscoveryRequest) error
-	Recv() (*v2.DeltaDiscoveryResponse, error)
-	grpc.ClientStream
-}
-
-type kumaDiscoveryServiceDeltaKumaResourcesClient struct {
-	grpc.ClientStream
-}
-
-func (x *kumaDiscoveryServiceDeltaKumaResourcesClient) Send(m *v2.DeltaDiscoveryRequest) error {
-	return x.ClientStream.SendMsg(m)
-}
-
-func (x *kumaDiscoveryServiceDeltaKumaResourcesClient) Recv() (*v2.DeltaDiscoveryResponse, error) {
-	m := new(v2.DeltaDiscoveryResponse)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
 func (c *kumaDiscoveryServiceClient) StreamKumaResources(ctx context.Context, opts ...grpc.CallOption) (KumaDiscoveryService_StreamKumaResourcesClient, error) {
-	stream, err := c.cc.NewStream(ctx, &_KumaDiscoveryService_serviceDesc.Streams[1], "/kuma.mesh.v1alpha1.KumaDiscoveryService/StreamKumaResources", opts...)
+	stream, err := c.cc.NewStream(ctx, &_KumaDiscoveryService_serviceDesc.Streams[0], "/kuma.mesh.v1alpha1.KumaDiscoveryService/StreamKumaResources", opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -376,8 +328,8 @@ func (c *kumaDiscoveryServiceClient) StreamKumaResources(ctx context.Context, op
 }
 
 type KumaDiscoveryService_StreamKumaResourcesClient interface {
-	Send(*v2.DiscoveryRequest) error
-	Recv() (*v2.DiscoveryResponse, error)
+	Send(*v3.DiscoveryRequest) error
+	Recv() (*v3.DiscoveryResponse, error)
 	grpc.ClientStream
 }
 
@@ -385,85 +337,42 @@ type kumaDiscoveryServiceStreamKumaResourcesClient struct {
 	grpc.ClientStream
 }
 
-func (x *kumaDiscoveryServiceStreamKumaResourcesClient) Send(m *v2.DiscoveryRequest) error {
+func (x *kumaDiscoveryServiceStreamKumaResourcesClient) Send(m *v3.DiscoveryRequest) error {
 	return x.ClientStream.SendMsg(m)
 }
 
-func (x *kumaDiscoveryServiceStreamKumaResourcesClient) Recv() (*v2.DiscoveryResponse, error) {
-	m := new(v2.DiscoveryResponse)
+func (x *kumaDiscoveryServiceStreamKumaResourcesClient) Recv() (*v3.DiscoveryResponse, error) {
+	m := new(v3.DiscoveryResponse)
 	if err := x.ClientStream.RecvMsg(m); err != nil {
 		return nil, err
 	}
 	return m, nil
 }
 
-func (c *kumaDiscoveryServiceClient) FetchKumaResources(ctx context.Context, in *v2.DiscoveryRequest, opts ...grpc.CallOption) (*v2.DiscoveryResponse, error) {
-	out := new(v2.DiscoveryResponse)
-	err := c.cc.Invoke(ctx, "/kuma.mesh.v1alpha1.KumaDiscoveryService/FetchKumaResources", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
 // KumaDiscoveryServiceServer is the server API for KumaDiscoveryService service.
 type KumaDiscoveryServiceServer interface {
-	DeltaKumaResources(KumaDiscoveryService_DeltaKumaResourcesServer) error
 	StreamKumaResources(KumaDiscoveryService_StreamKumaResourcesServer) error
-	FetchKumaResources(context.Context, *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error)
 }
 
 // UnimplementedKumaDiscoveryServiceServer can be embedded to have forward compatible implementations.
 type UnimplementedKumaDiscoveryServiceServer struct {
 }
 
-func (*UnimplementedKumaDiscoveryServiceServer) DeltaKumaResources(KumaDiscoveryService_DeltaKumaResourcesServer) error {
-	return status.Errorf(codes.Unimplemented, "method DeltaKumaResources not implemented")
-}
 func (*UnimplementedKumaDiscoveryServiceServer) StreamKumaResources(KumaDiscoveryService_StreamKumaResourcesServer) error {
 	return status.Errorf(codes.Unimplemented, "method StreamKumaResources not implemented")
 }
-func (*UnimplementedKumaDiscoveryServiceServer) FetchKumaResources(context.Context, *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method FetchKumaResources not implemented")
-}
 
 func RegisterKumaDiscoveryServiceServer(s *grpc.Server, srv KumaDiscoveryServiceServer) {
 	s.RegisterService(&_KumaDiscoveryService_serviceDesc, srv)
 }
 
-func _KumaDiscoveryService_DeltaKumaResources_Handler(srv interface{}, stream grpc.ServerStream) error {
-	return srv.(KumaDiscoveryServiceServer).DeltaKumaResources(&kumaDiscoveryServiceDeltaKumaResourcesServer{stream})
-}
-
-type KumaDiscoveryService_DeltaKumaResourcesServer interface {
-	Send(*v2.DeltaDiscoveryResponse) error
-	Recv() (*v2.DeltaDiscoveryRequest, error)
-	grpc.ServerStream
-}
-
-type kumaDiscoveryServiceDeltaKumaResourcesServer struct {
-	grpc.ServerStream
-}
-
-func (x *kumaDiscoveryServiceDeltaKumaResourcesServer) Send(m *v2.DeltaDiscoveryResponse) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func (x *kumaDiscoveryServiceDeltaKumaResourcesServer) Recv() (*v2.DeltaDiscoveryRequest, error) {
-	m := new(v2.DeltaDiscoveryRequest)
-	if err := x.ServerStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
 func _KumaDiscoveryService_StreamKumaResources_Handler(srv interface{}, stream grpc.ServerStream) error {
 	return srv.(KumaDiscoveryServiceServer).StreamKumaResources(&kumaDiscoveryServiceStreamKumaResourcesServer{stream})
 }
 
 type KumaDiscoveryService_StreamKumaResourcesServer interface {
-	Send(*v2.DiscoveryResponse) error
-	Recv() (*v2.DiscoveryRequest, error)
+	Send(*v3.DiscoveryResponse) error
+	Recv() (*v3.DiscoveryRequest, error)
 	grpc.ServerStream
 }
 
@@ -471,52 +380,23 @@ type kumaDiscoveryServiceStreamKumaResourcesServer struct {
 	grpc.ServerStream
 }
 
-func (x *kumaDiscoveryServiceStreamKumaResourcesServer) Send(m *v2.DiscoveryResponse) error {
+func (x *kumaDiscoveryServiceStreamKumaResourcesServer) Send(m *v3.DiscoveryResponse) error {
 	return x.ServerStream.SendMsg(m)
 }
 
-func (x *kumaDiscoveryServiceStreamKumaResourcesServer) Recv() (*v2.DiscoveryRequest, error) {
-	m := new(v2.DiscoveryRequest)
+func (x *kumaDiscoveryServiceStreamKumaResourcesServer) Recv() (*v3.DiscoveryRequest, error) {
+	m := new(v3.DiscoveryRequest)
 	if err := x.ServerStream.RecvMsg(m); err != nil {
 		return nil, err
 	}
 	return m, nil
 }
 
-func _KumaDiscoveryService_FetchKumaResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(v2.DiscoveryRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(KumaDiscoveryServiceServer).FetchKumaResources(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/kuma.mesh.v1alpha1.KumaDiscoveryService/FetchKumaResources",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(KumaDiscoveryServiceServer).FetchKumaResources(ctx, req.(*v2.DiscoveryRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
 var _KumaDiscoveryService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "kuma.mesh.v1alpha1.KumaDiscoveryService",
 	HandlerType: (*KumaDiscoveryServiceServer)(nil),
-	Methods: []grpc.MethodDesc{
-		{
-			MethodName: "FetchKumaResources",
-			Handler:    _KumaDiscoveryService_FetchKumaResources_Handler,
-		},
-	},
+	Methods:     []grpc.MethodDesc{},
 	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "DeltaKumaResources",
-			Handler:       _KumaDiscoveryService_DeltaKumaResources_Handler,
-			ServerStreams: true,
-			ClientStreams: true,
-		},
 		{
 			StreamName:    "StreamKumaResources",
 			Handler:       _KumaDiscoveryService_StreamKumaResources_Handler,
diff --git a/api/mesh/v1alpha1/kds.proto b/api/mesh/v1alpha1/kds.proto
index f03f0e6db..01349284f 100644
--- a/api/mesh/v1alpha1/kds.proto
+++ b/api/mesh/v1alpha1/kds.proto
@@ -5,19 +5,12 @@ package kuma.mesh.v1alpha1;
 option go_package = "github.com/kumahq/kuma/api/mesh/v1alpha1";
 
 import "google/protobuf/timestamp.proto";
-import "envoy/api/v2/discovery.proto";
+import "envoy/service/discovery/v3/discovery.proto";
 import "google/protobuf/any.proto";
 
 service KumaDiscoveryService {
-
-  rpc DeltaKumaResources(stream envoy.api.v2.DeltaDiscoveryRequest)
-      returns (stream envoy.api.v2.DeltaDiscoveryResponse);
-
-  rpc StreamKumaResources(stream envoy.api.v2.DiscoveryRequest)
-      returns (stream envoy.api.v2.DiscoveryResponse);
-
-  rpc FetchKumaResources(envoy.api.v2.DiscoveryRequest)
-      returns (envoy.api.v2.DiscoveryResponse);
+  rpc StreamKumaResources(stream envoy.service.discovery.v3.DiscoveryRequest)
+      returns (stream envoy.service.discovery.v3.DiscoveryResponse);
 }
 
 message KumaResource {
diff --git a/api/mesh/v1alpha1/mux.pb.go b/api/mesh/v1alpha1/mux.pb.go
index 16dc848e1..bc1a971d5 100644
--- a/api/mesh/v1alpha1/mux.pb.go
+++ b/api/mesh/v1alpha1/mux.pb.go
@@ -9,6 +9,7 @@ package v1alpha1
 import (
 	context "context"
 	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	grpc "google.golang.org/grpc"
 	codes "google.golang.org/grpc/codes"
 	status "google.golang.org/grpc/status"
@@ -31,6 +32,8 @@ type Message struct {
 	unknownFields protoimpl.UnknownFields
 
 	// Types that are assignable to Value:
+	//	*Message_LegacyRequest
+	//	*Message_LegacyResponse
 	//	*Message_Request
 	//	*Message_Response
 	Value isMessage_Value `protobuf_oneof:"value"`
@@ -75,14 +78,28 @@ func (m *Message) GetValue() isMessage_Value {
 	return nil
 }
 
-func (x *Message) GetRequest() *v2.DiscoveryRequest {
+func (x *Message) GetLegacyRequest() *v2.DiscoveryRequest {
+	if x, ok := x.GetValue().(*Message_LegacyRequest); ok {
+		return x.LegacyRequest
+	}
+	return nil
+}
+
+func (x *Message) GetLegacyResponse() *v2.DiscoveryResponse {
+	if x, ok := x.GetValue().(*Message_LegacyResponse); ok {
+		return x.LegacyResponse
+	}
+	return nil
+}
+
+func (x *Message) GetRequest() *v3.DiscoveryRequest {
 	if x, ok := x.GetValue().(*Message_Request); ok {
 		return x.Request
 	}
 	return nil
 }
 
-func (x *Message) GetResponse() *v2.DiscoveryResponse {
+func (x *Message) GetResponse() *v3.DiscoveryResponse {
 	if x, ok := x.GetValue().(*Message_Response); ok {
 		return x.Response
 	}
@@ -93,14 +110,26 @@ type isMessage_Value interface {
 	isMessage_Value()
 }
 
+type Message_LegacyRequest struct {
+	LegacyRequest *v2.DiscoveryRequest `protobuf:"bytes,1,opt,name=legacy_request,json=legacyRequest,proto3,oneof"`
+}
+
+type Message_LegacyResponse struct {
+	LegacyResponse *v2.DiscoveryResponse `protobuf:"bytes,2,opt,name=legacy_response,json=legacyResponse,proto3,oneof"`
+}
+
 type Message_Request struct {
-	Request *v2.DiscoveryRequest `protobuf:"bytes,1,opt,name=request,proto3,oneof"`
+	Request *v3.DiscoveryRequest `protobuf:"bytes,3,opt,name=request,proto3,oneof"`
 }
 
 type Message_Response struct {
-	Response *v2.DiscoveryResponse `protobuf:"bytes,2,opt,name=response,proto3,oneof"`
+	Response *v3.DiscoveryResponse `protobuf:"bytes,4,opt,name=response,proto3,oneof"`
 }
 
+func (*Message_LegacyRequest) isMessage_Value() {}
+
+func (*Message_LegacyResponse) isMessage_Value() {}
+
 func (*Message_Request) isMessage_Value() {}
 
 func (*Message_Response) isMessage_Value() {}
@@ -112,26 +141,39 @@ var file_mesh_v1alpha1_mux_proto_rawDesc = []byte{
 	0x6d, 0x75, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x6b, 0x75, 0x6d, 0x61, 0x2e,
 	0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1c, 0x65,
 	0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x69, 0x73, 0x63,
-	0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x07,
-	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79,
-	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
-	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
+	0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x65, 0x6e, 0x76,
+	0x6f, 0x79, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f,
+	0x76, 0x65, 0x72, 0x79, 0x2f, 0x76, 0x33, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
+	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x02, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73,
+	0x61, 0x67, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x72, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e,
+	0x76, 0x6f, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
+	0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x6c,
+	0x65, 0x67, 0x61, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0f,
+	0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
 	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x61, 0x70,
 	0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x61, 0x0a, 0x10, 0x4d,
-	0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
-	0x4d, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-	0x12, 0x1b, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1b, 0x2e,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x76, 0x6f,
+	0x79, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
+	0x65, 0x72, 0x79, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x4b, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x76,
+	0x33, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
+	0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x61, 0x0a, 0x10, 0x4d, 0x75, 0x6c, 0x74,
+	0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x0d,
+	0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e,
 	0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2a,
-	0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d,
-	0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73,
-	0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x61, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1b, 0x2e, 0x6b, 0x75, 0x6d,
+	0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2a, 0x5a, 0x28, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71,
+	0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -151,17 +193,21 @@ var file_mesh_v1alpha1_mux_proto_goTypes = []interface{}{
 	(*Message)(nil),              // 0: kuma.mesh.v1alpha1.Message
 	(*v2.DiscoveryRequest)(nil),  // 1: envoy.api.v2.DiscoveryRequest
 	(*v2.DiscoveryResponse)(nil), // 2: envoy.api.v2.DiscoveryResponse
+	(*v3.DiscoveryRequest)(nil),  // 3: envoy.service.discovery.v3.DiscoveryRequest
+	(*v3.DiscoveryResponse)(nil), // 4: envoy.service.discovery.v3.DiscoveryResponse
 }
 var file_mesh_v1alpha1_mux_proto_depIdxs = []int32{
-	1, // 0: kuma.mesh.v1alpha1.Message.request:type_name -> envoy.api.v2.DiscoveryRequest
-	2, // 1: kuma.mesh.v1alpha1.Message.response:type_name -> envoy.api.v2.DiscoveryResponse
-	0, // 2: kuma.mesh.v1alpha1.MultiplexService.StreamMessage:input_type -> kuma.mesh.v1alpha1.Message
-	0, // 3: kuma.mesh.v1alpha1.MultiplexService.StreamMessage:output_type -> kuma.mesh.v1alpha1.Message
-	3, // [3:4] is the sub-list for method output_type
-	2, // [2:3] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	1, // 0: kuma.mesh.v1alpha1.Message.legacy_request:type_name -> envoy.api.v2.DiscoveryRequest
+	2, // 1: kuma.mesh.v1alpha1.Message.legacy_response:type_name -> envoy.api.v2.DiscoveryResponse
+	3, // 2: kuma.mesh.v1alpha1.Message.request:type_name -> envoy.service.discovery.v3.DiscoveryRequest
+	4, // 3: kuma.mesh.v1alpha1.Message.response:type_name -> envoy.service.discovery.v3.DiscoveryResponse
+	0, // 4: kuma.mesh.v1alpha1.MultiplexService.StreamMessage:input_type -> kuma.mesh.v1alpha1.Message
+	0, // 5: kuma.mesh.v1alpha1.MultiplexService.StreamMessage:output_type -> kuma.mesh.v1alpha1.Message
+	5, // [5:6] is the sub-list for method output_type
+	4, // [4:5] is the sub-list for method input_type
+	4, // [4:4] is the sub-list for extension type_name
+	4, // [4:4] is the sub-list for extension extendee
+	0, // [0:4] is the sub-list for field type_name
 }
 
 func init() { file_mesh_v1alpha1_mux_proto_init() }
@@ -184,6 +230,8 @@ func file_mesh_v1alpha1_mux_proto_init() {
 		}
 	}
 	file_mesh_v1alpha1_mux_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*Message_LegacyRequest)(nil),
+		(*Message_LegacyResponse)(nil),
 		(*Message_Request)(nil),
 		(*Message_Response)(nil),
 	}
diff --git a/api/mesh/v1alpha1/mux.proto b/api/mesh/v1alpha1/mux.proto
index 887d536e4..6b3d48108 100644
--- a/api/mesh/v1alpha1/mux.proto
+++ b/api/mesh/v1alpha1/mux.proto
@@ -5,6 +5,7 @@ package kuma.mesh.v1alpha1;
 option go_package = "github.com/kumahq/kuma/api/mesh/v1alpha1";
 
 import "envoy/api/v2/discovery.proto";
+import "envoy/service/discovery/v3/discovery.proto";
 
 service MultiplexService {
   rpc StreamMessage(stream Message) returns (stream Message);
@@ -12,7 +13,9 @@ service MultiplexService {
 
 message Message {
   oneof value {
-    envoy.api.v2.DiscoveryRequest request = 1;
-    envoy.api.v2.DiscoveryResponse response = 2;
+    envoy.api.v2.DiscoveryRequest legacy_request = 1;
+    envoy.api.v2.DiscoveryResponse legacy_response = 2;
+    envoy.service.discovery.v3.DiscoveryRequest request = 3;
+    envoy.service.discovery.v3.DiscoveryResponse response = 4;
   }
 }
diff --git a/pkg/kds/cache/snapshot.go b/pkg/kds/cache/snapshot.go
index 09b04ace5..e0a6d4d51 100644
--- a/pkg/kds/cache/snapshot.go
+++ b/pkg/kds/cache/snapshot.go
@@ -4,13 +4,13 @@ import (
 	"fmt"
 
 	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
+	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3"
 	"github.com/pkg/errors"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/registry"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 type ResourceBuilder interface {
@@ -18,7 +18,7 @@ type ResourceBuilder interface {
 
 type SnapshotBuilder interface {
 	With(typ string, resources []envoy_types.Resource) SnapshotBuilder
-	Build(version string) util_xds.Snapshot
+	Build(version string) util_xds_v3.Snapshot
 }
 
 type builder struct {
@@ -37,7 +37,7 @@ func (b *builder) With(typ string, resources []envoy_types.Resource) SnapshotBui
 	return b
 }
 
-func (b *builder) Build(version string) util_xds.Snapshot {
+func (b *builder) Build(version string) util_xds_v3.Snapshot {
 	snapshot := &Snapshot{Resources: map[string]envoy_cache.Resources{}}
 	for _, typ := range snapshot.GetSupportedTypes() {
 		snapshot.Resources[typ] = envoy_cache.NewResources(version, nil)
@@ -57,7 +57,7 @@ type Snapshot struct {
 	Resources map[string]envoy_cache.Resources
 }
 
-var _ util_xds.Snapshot = &Snapshot{}
+var _ util_xds_v3.Snapshot = &Snapshot{}
 
 func (s *Snapshot) GetSupportedTypes() (types []string) {
 	for _, def := range registry.Global().ObjectTypes(model.HasKdsEnabled()) {
@@ -110,7 +110,7 @@ func (s *Snapshot) GetVersion(typ string) string {
 	return ""
 }
 
-func (s *Snapshot) WithVersion(typ string, version string) util_xds.Snapshot {
+func (s *Snapshot) WithVersion(typ string, version string) util_xds_v3.Snapshot {
 	if s == nil {
 		return nil
 	}
diff --git a/pkg/kds/client/stream.go b/pkg/kds/client/stream.go
index 81f34acb0..b6b4623cc 100644
--- a/pkg/kds/client/stream.go
+++ b/pkg/kds/client/stream.go
@@ -3,8 +3,8 @@ package client
 import (
 	"fmt"
 
-	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"google.golang.org/genproto/googleapis/rpc/status"
 	"google.golang.org/protobuf/types/known/structpb"
 
@@ -28,16 +28,16 @@ var _ KDSStream = &stream{}
 
 type stream struct {
 	streamClient   mesh_proto.KumaDiscoveryService_StreamKumaResourcesClient
-	latestACKed    map[string]*envoy.DiscoveryResponse
-	latestReceived map[string]*envoy.DiscoveryResponse
+	latestACKed    map[string]*envoy_sd.DiscoveryResponse
+	latestReceived map[string]*envoy_sd.DiscoveryResponse
 	clientId       string
 }
 
 func NewKDSStream(s mesh_proto.KumaDiscoveryService_StreamKumaResourcesClient, clientId string) KDSStream {
 	return &stream{
 		streamClient:   s,
-		latestACKed:    make(map[string]*envoy.DiscoveryResponse),
-		latestReceived: make(map[string]*envoy.DiscoveryResponse),
+		latestACKed:    make(map[string]*envoy_sd.DiscoveryResponse),
+		latestReceived: make(map[string]*envoy_sd.DiscoveryResponse),
 		clientId:       clientId,
 	}
 }
@@ -54,7 +54,7 @@ func (s *stream) DiscoveryRequest(resourceType model.ResourceType) error {
 	if err != nil {
 		return err
 	}
-	return s.streamClient.Send(&envoy.DiscoveryRequest{
+	return s.streamClient.Send(&envoy_sd.DiscoveryRequest{
 		VersionInfo:   "",
 		ResponseNonce: "",
 		Node: &envoy_core.Node{
@@ -88,7 +88,7 @@ func (s *stream) ACK(typ string) error {
 	if latestReceived == nil {
 		return nil
 	}
-	err := s.streamClient.Send(&envoy.DiscoveryRequest{
+	err := s.streamClient.Send(&envoy_sd.DiscoveryRequest{
 		VersionInfo:   latestReceived.VersionInfo,
 		ResponseNonce: latestReceived.Nonce,
 		ResourceNames: []string{},
@@ -109,7 +109,7 @@ func (s *stream) NACK(typ string, err error) error {
 		return nil
 	}
 	latestACKed := s.latestACKed[typ]
-	return s.streamClient.Send(&envoy.DiscoveryRequest{
+	return s.streamClient.Send(&envoy_sd.DiscoveryRequest{
 		VersionInfo:   latestACKed.GetVersionInfo(),
 		ResponseNonce: latestReceived.Nonce,
 		ResourceNames: []string{},
diff --git a/pkg/kds/mux/client.go b/pkg/kds/mux/client.go
index 0c63d6397..83ec458c7 100644
--- a/pkg/kds/mux/client.go
+++ b/pkg/kds/mux/client.go
@@ -71,8 +71,11 @@ func (c *client) Start(stop <-chan struct{}) (errs error) {
 	}()
 	muxClient := mesh_proto.NewMultiplexServiceClient(conn)
 
-	withClientIDCtx := metadata.AppendToOutgoingContext(c.ctx, "client-id", c.clientID)
-	stream, err := muxClient.StreamMessage(withClientIDCtx)
+	withKDSCtx := metadata.AppendToOutgoingContext(c.ctx,
+		"client-id", c.clientID,
+		KDSVersionHeaderKey, KDSVersionV3,
+	)
+	stream, err := muxClient.StreamMessage(withKDSCtx)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/kds/mux/clientstream.go b/pkg/kds/mux/clientstream.go
index 97bd6182d..19aa5c50b 100644
--- a/pkg/kds/mux/clientstream.go
+++ b/pkg/kds/mux/clientstream.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"io"
 
-	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"google.golang.org/grpc/metadata"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
@@ -12,18 +12,30 @@ import (
 
 type kdsClientStream struct {
 	MultiplexStream
-	responses chan *envoy_api_v2.DiscoveryResponse
+	responses chan *envoy_sd.DiscoveryResponse
 }
 
-func (k *kdsClientStream) put(response *envoy_api_v2.DiscoveryResponse) {
+func (k *kdsClientStream) put(response *envoy_sd.DiscoveryResponse) {
 	k.responses <- response
 }
 
-func (k *kdsClientStream) Send(request *envoy_api_v2.DiscoveryRequest) error {
-	return k.MultiplexStream.Send(&mesh_proto.Message{Value: &mesh_proto.Message_Request{Request: request}})
+func (k *kdsClientStream) Send(request *envoy_sd.DiscoveryRequest) error {
+	var msg *mesh_proto.Message
+
+	kdsVersion := KDSVersion(k.Context())
+	switch kdsVersion {
+	case KDSVersionV2:
+		msg = &mesh_proto.Message{Value: &mesh_proto.Message_LegacyRequest{LegacyRequest: DiscoveryRequestV2(request)}}
+	case KDSVersionV3:
+		msg = &mesh_proto.Message{Value: &mesh_proto.Message_Request{Request: request}}
+	default:
+		return UnsupportedKDSVersion(kdsVersion)
+	}
+
+	return k.MultiplexStream.Send(msg)
 }
 
-func (k *kdsClientStream) Recv() (*envoy_api_v2.DiscoveryResponse, error) {
+func (k *kdsClientStream) Recv() (*envoy_sd.DiscoveryResponse, error) {
 	if r, ok := <-k.responses; ok {
 		return r, nil
 	}
diff --git a/pkg/kds/mux/serverstream.go b/pkg/kds/mux/serverstream.go
index 2a996bc13..f935b6308 100644
--- a/pkg/kds/mux/serverstream.go
+++ b/pkg/kds/mux/serverstream.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"io"
 
-	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"google.golang.org/grpc/metadata"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
@@ -18,18 +18,30 @@ type MultiplexStream interface {
 
 type kdsServerStream struct {
 	MultiplexStream
-	requests chan *envoy_api_v2.DiscoveryRequest
+	requests chan *envoy_sd.DiscoveryRequest
 }
 
-func (k *kdsServerStream) put(request *envoy_api_v2.DiscoveryRequest) {
+func (k *kdsServerStream) put(request *envoy_sd.DiscoveryRequest) {
 	k.requests <- request
 }
 
-func (k *kdsServerStream) Send(response *envoy_api_v2.DiscoveryResponse) error {
-	return k.MultiplexStream.Send(&mesh_proto.Message{Value: &mesh_proto.Message_Response{Response: response}})
+func (k *kdsServerStream) Send(response *envoy_sd.DiscoveryResponse) error {
+	var msg *mesh_proto.Message
+
+	kdsVersion := KDSVersion(k.Context())
+	switch kdsVersion {
+	case KDSVersionV2:
+		msg = &mesh_proto.Message{Value: &mesh_proto.Message_LegacyResponse{LegacyResponse: DiscoveryResponseV2(response)}}
+	case KDSVersionV3:
+		msg = &mesh_proto.Message{Value: &mesh_proto.Message_Response{Response: response}}
+	default:
+		return UnsupportedKDSVersion(kdsVersion)
+	}
+
+	return k.MultiplexStream.Send(msg)
 }
 
-func (k *kdsServerStream) Recv() (*envoy_api_v2.DiscoveryRequest, error) {
+func (k *kdsServerStream) Recv() (*envoy_sd.DiscoveryRequest, error) {
 	if r, ok := <-k.requests; ok {
 		return r, nil
 	}
diff --git a/pkg/kds/mux/session.go b/pkg/kds/mux/session.go
index 3df4ba2be..86cfc753f 100644
--- a/pkg/kds/mux/session.go
+++ b/pkg/kds/mux/session.go
@@ -3,7 +3,7 @@ package mux
 import (
 	"sync/atomic"
 
-	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 )
@@ -31,11 +31,11 @@ func NewSession(peerID string, stream MultiplexStream, stop <-chan struct{}) Ses
 		done:   make(chan struct{}, 1),
 		err:    make(chan error, 1),
 		serverStream: &kdsServerStream{
-			requests:        make(chan *envoy_api_v2.DiscoveryRequest, 1),
+			requests:        make(chan *envoy_sd.DiscoveryRequest, 1),
 			MultiplexStream: stream,
 		},
 		clientStream: &kdsClientStream{
-			responses:       make(chan *envoy_api_v2.DiscoveryResponse, 1),
+			responses:       make(chan *envoy_sd.DiscoveryResponse, 1),
 			MultiplexStream: stream,
 		},
 		closed: int32(0),
@@ -65,8 +65,12 @@ func (s *session) handle(stream MultiplexStream, stop <-chan struct{}) error {
 			return err
 		}
 		switch v := msg.Value.(type) {
+		case *mesh_proto.Message_LegacyRequest:
+			s.serverStream.put(DiscoveryRequestV3(v.LegacyRequest))
 		case *mesh_proto.Message_Request:
 			s.serverStream.put(v.Request)
+		case *mesh_proto.Message_LegacyResponse:
+			s.clientStream.put(DiscoveryResponseV3(v.LegacyResponse))
 		case *mesh_proto.Message_Response:
 			s.clientStream.put(v.Response)
 		}
diff --git a/pkg/kds/mux/version.go b/pkg/kds/mux/version.go
new file mode 100644
index 000000000..e82c7a0fb
--- /dev/null
+++ b/pkg/kds/mux/version.go
@@ -0,0 +1,92 @@
+package mux
+
+import (
+	"context"
+
+	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_core_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_sd_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+	"github.com/pkg/errors"
+	"google.golang.org/grpc/metadata"
+)
+
+var KDSVersionHeaderKey = "kds-version"
+var KDSVersionV2 = "v2"
+var KDSVersionV3 = "v3"
+
+func KDSVersion(ctx context.Context) string {
+	md, found := metadata.FromIncomingContext(ctx)
+	if found {
+		version := md.Get(KDSVersionHeaderKey)
+		if len(version) == 1 {
+			return version[0]
+		}
+	}
+
+	md, found = metadata.FromOutgoingContext(ctx)
+	if found {
+		version := md.Get(KDSVersionHeaderKey)
+		if len(version) == 1 {
+			return version[0]
+		}
+	}
+
+	return KDSVersionV2
+}
+
+func UnsupportedKDSVersion(version string) error {
+	return errors.Errorf("invalid KDS version %s. Supported versions %s %s", version, KDSVersionV2, KDSVersionV3)
+}
+
+func DiscoveryRequestV2(request *envoy_sd_v3.DiscoveryRequest) *envoy_api_v2.DiscoveryRequest {
+	return &envoy_api_v2.DiscoveryRequest{
+		VersionInfo: request.VersionInfo,
+		Node: &envoy_core_v2.Node{
+			Id:       request.Node.Id,
+			Metadata: request.Node.Metadata,
+		},
+		ResourceNames: request.ResourceNames,
+		TypeUrl:       request.TypeUrl,
+		ResponseNonce: request.ResponseNonce,
+		ErrorDetail:   request.ErrorDetail,
+	}
+}
+
+func DiscoveryRequestV3(request *envoy_api_v2.DiscoveryRequest) *envoy_sd_v3.DiscoveryRequest {
+	return &envoy_sd_v3.DiscoveryRequest{
+		VersionInfo: request.VersionInfo,
+		Node: &envoy_core_v3.Node{
+			Id:       request.Node.Id,
+			Metadata: request.Node.Metadata,
+		},
+		ResourceNames: request.ResourceNames,
+		TypeUrl:       request.TypeUrl,
+		ResponseNonce: request.ResponseNonce,
+		ErrorDetail:   request.ErrorDetail,
+	}
+}
+
+func DiscoveryResponseV2(response *envoy_sd_v3.DiscoveryResponse) *envoy_api_v2.DiscoveryResponse {
+	return &envoy_api_v2.DiscoveryResponse{
+		VersionInfo: response.VersionInfo,
+		Resources:   response.Resources,
+		TypeUrl:     response.TypeUrl,
+		Nonce:       response.Nonce,
+		ControlPlane: &envoy_core_v2.ControlPlane{
+			Identifier: response.ControlPlane.Identifier,
+		},
+	}
+}
+
+func DiscoveryResponseV3(response *envoy_api_v2.DiscoveryResponse) *envoy_sd_v3.DiscoveryResponse {
+	return &envoy_sd_v3.DiscoveryResponse{
+		VersionInfo: response.VersionInfo,
+		Resources:   response.Resources,
+		TypeUrl:     response.TypeUrl,
+		Nonce:       response.Nonce,
+		ControlPlane: &envoy_core_v3.ControlPlane{
+			Identifier: response.ControlPlane.Identifier,
+		},
+	}
+}
diff --git a/pkg/kds/reconcile/interfaces.go b/pkg/kds/reconcile/interfaces.go
index 786964344..159bdc9e9 100644
--- a/pkg/kds/reconcile/interfaces.go
+++ b/pkg/kds/reconcile/interfaces.go
@@ -3,9 +3,9 @@ package reconcile
 import (
 	"context"
 
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 // Reconciler re-computes configuration for a given node.
@@ -15,5 +15,5 @@ type Reconciler interface {
 
 // Generates a snapshot of xDS resources for a given node.
 type SnapshotGenerator interface {
-	GenerateSnapshot(context.Context, *envoy_core.Node) (util_xds.Snapshot, error)
+	GenerateSnapshot(context.Context, *envoy_core.Node) (util_xds_v3.Snapshot, error)
 }
diff --git a/pkg/kds/reconcile/reconciler.go b/pkg/kds/reconcile/reconciler.go
index de27c88de..b4e1911d3 100644
--- a/pkg/kds/reconcile/reconciler.go
+++ b/pkg/kds/reconcile/reconciler.go
@@ -3,17 +3,17 @@ package reconcile
 import (
 	"context"
 
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3"
 
 	config_core "github.com/kumahq/kuma/pkg/config/core"
 	"github.com/kumahq/kuma/pkg/core"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 var log = core.Log.WithName("kds").WithName("reconcile")
 
-func NewReconciler(hasher envoy_cache.NodeHash, cache util_xds.SnapshotCache, generator SnapshotGenerator, versioner util_xds.SnapshotVersioner, mode config_core.CpMode) Reconciler {
+func NewReconciler(hasher envoy_cache.NodeHash, cache util_xds_v3.SnapshotCache, generator SnapshotGenerator, versioner util_xds_v3.SnapshotVersioner, mode config_core.CpMode) Reconciler {
 	return &reconciler{
 		hasher:    hasher,
 		cache:     cache,
@@ -25,9 +25,9 @@ func NewReconciler(hasher envoy_cache.NodeHash, cache util_xds.SnapshotCache, ge
 
 type reconciler struct {
 	hasher    envoy_cache.NodeHash
-	cache     util_xds.SnapshotCache
+	cache     util_xds_v3.SnapshotCache
 	generator SnapshotGenerator
-	versioner util_xds.SnapshotVersioner
+	versioner util_xds_v3.SnapshotVersioner
 	mode      config_core.CpMode
 }
 
@@ -46,7 +46,7 @@ func (r *reconciler) Reconcile(ctx context.Context, node *envoy_core.Node) error
 	return r.cache.SetSnapshot(id, new)
 }
 
-func (r *reconciler) logChanges(new util_xds.Snapshot, old util_xds.Snapshot, node *envoy_core.Node) {
+func (r *reconciler) logChanges(new util_xds_v3.Snapshot, old util_xds_v3.Snapshot, node *envoy_core.Node) {
 	for _, typ := range new.GetSupportedTypes() {
 		if old != nil && old.GetVersion(typ) != new.GetVersion(typ) {
 			client := node.Id
diff --git a/pkg/kds/reconcile/snapshot_generator.go b/pkg/kds/reconcile/snapshot_generator.go
index fadc3e87b..f9aafa7f5 100644
--- a/pkg/kds/reconcile/snapshot_generator.go
+++ b/pkg/kds/reconcile/snapshot_generator.go
@@ -3,7 +3,7 @@ package reconcile
 import (
 	"context"
 
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
 
 	core_manager "github.com/kumahq/kuma/pkg/core/resources/manager"
@@ -11,7 +11,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/kds/cache"
 	"github.com/kumahq/kuma/pkg/kds/util"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 type ResourceFilter func(clusterID string, r model.Resource) bool
@@ -34,7 +34,7 @@ type snapshotGenerator struct {
 	resourceFilter  ResourceFilter
 }
 
-func (s *snapshotGenerator) GenerateSnapshot(ctx context.Context, node *envoy_core.Node) (util_xds.Snapshot, error) {
+func (s *snapshotGenerator) GenerateSnapshot(ctx context.Context, node *envoy_core.Node) (util_xds_v3.Snapshot, error) {
 	builder := cache.NewSnapshotBuilder()
 	for _, typ := range s.resourceTypes {
 		resources, err := s.getResources(ctx, typ, node)
diff --git a/pkg/kds/server/components.go b/pkg/kds/server/components.go
index 4550c2512..35d705043 100644
--- a/pkg/kds/server/components.go
+++ b/pkg/kds/server/components.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"time"
 
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3"
+	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v3"
 	"github.com/go-logr/logr"
 	"github.com/prometheus/client_golang/prometheus"
 
@@ -17,13 +17,13 @@ import (
 	core_metrics "github.com/kumahq/kuma/pkg/metrics"
 	util_watchdog "github.com/kumahq/kuma/pkg/util/watchdog"
 	util_xds "github.com/kumahq/kuma/pkg/util/xds"
-	util_xds_v2 "github.com/kumahq/kuma/pkg/util/xds/v2"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 func New(log logr.Logger, rt core_runtime.Runtime, providedTypes []model.ResourceType, serverID string, refresh time.Duration, filter reconcile.ResourceFilter, insight bool) (Server, error) {
 	hasher, cache := newKDSContext(log)
 	generator := reconcile.NewSnapshotGenerator(rt.ReadOnlyResourceManager(), providedTypes, filter)
-	versioner := util_xds.SnapshotAutoVersioner{UUID: core.NewUUID}
+	versioner := util_xds_v3.SnapshotAutoVersioner{UUID: core.NewUUID}
 	reconciler := reconcile.NewReconciler(hasher, cache, generator, versioner, rt.Config().Mode)
 	syncTracker, err := newSyncTracker(log, reconciler, refresh, rt.Metrics())
 	if err != nil {
@@ -33,11 +33,11 @@ func New(log logr.Logger, rt core_runtime.Runtime, providedTypes []model.Resourc
 	if err != nil {
 		return nil, err
 	}
-	callbacks := util_xds_v2.CallbacksChain{
+	callbacks := util_xds_v3.CallbacksChain{
 		&typeAdjustCallbacks{},
-		util_xds_v2.NewControlPlaneIdCallbacks(serverID),
-		util_xds_v2.AdaptCallbacks(util_xds.LoggingCallbacks{Log: log}),
-		util_xds_v2.AdaptCallbacks(statsCallbacks),
+		util_xds_v3.NewControlPlaneIdCallbacks(serverID),
+		util_xds_v3.AdaptCallbacks(util_xds.LoggingCallbacks{Log: log}),
+		util_xds_v3.AdaptCallbacks(statsCallbacks),
 		syncTracker,
 	}
 	if insight {
@@ -75,7 +75,7 @@ func newSyncTracker(log logr.Logger, reconciler reconcile.Reconciler, refresh ti
 	if err := metrics.Register(kdsGenerationsErrors); err != nil {
 		return nil, err
 	}
-	return util_xds_v2.NewWatchdogCallbacks(func(ctx context.Context, node *envoy_core.Node, streamID int64) (util_watchdog.Watchdog, error) {
+	return util_xds_v3.NewWatchdogCallbacks(func(ctx context.Context, node *envoy_core.Node, streamID int64) (util_watchdog.Watchdog, error) {
 		log := log.WithValues("streamID", streamID, "node", node)
 		return &util_watchdog.SimpleWatchdog{
 			NewTicker: func() *time.Ticker {
@@ -97,10 +97,10 @@ func newSyncTracker(log logr.Logger, reconciler reconcile.Reconciler, refresh ti
 	}), nil
 }
 
-func newKDSContext(log logr.Logger) (envoy_cache.NodeHash, util_xds.SnapshotCache) {
+func newKDSContext(log logr.Logger) (envoy_cache.NodeHash, util_xds_v3.SnapshotCache) {
 	hasher := hasher{}
 	logger := util_xds.NewLogger(log)
-	return hasher, util_xds.NewSnapshotCache(false, hasher, logger)
+	return hasher, util_xds_v3.NewSnapshotCache(false, hasher, logger)
 }
 
 type hasher struct {
diff --git a/pkg/kds/server/kds.go b/pkg/kds/server/kds.go
index 6b9a480f0..aeda5ad48 100644
--- a/pkg/kds/server/kds.go
+++ b/pkg/kds/server/kds.go
@@ -3,10 +3,9 @@ package server
 import (
 	"context"
 
-	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	"github.com/envoyproxy/go-control-plane/pkg/server/sotw/v2"
-	envoy_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3"
+	"github.com/envoyproxy/go-control-plane/pkg/server/sotw/v3"
+	envoy_server "github.com/envoyproxy/go-control-plane/pkg/server/v3"
 	"github.com/go-logr/logr"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
@@ -27,14 +26,6 @@ type server struct {
 	sotw.Server
 }
 
-func (s *server) DeltaKumaResources(resourcesServer mesh_proto.KumaDiscoveryService_DeltaKumaResourcesServer) error {
-	panic("not supported") // we don't use Delta XDS in KDS
-}
-
 func (s *server) StreamKumaResources(stream mesh_proto.KumaDiscoveryService_StreamKumaResourcesServer) error {
 	return s.StreamHandler(stream, "")
 }
-
-func (s *server) FetchKumaResources(ctx context.Context, request *envoy.DiscoveryRequest) (*envoy.DiscoveryResponse, error) {
-	panic("not supported") // we don't need to support Fetch
-}
diff --git a/pkg/kds/server/status_tracker.go b/pkg/kds/server/status_tracker.go
index 0e04a578f..53ae1d7fe 100644
--- a/pkg/kds/server/status_tracker.go
+++ b/pkg/kds/server/status_tracker.go
@@ -4,8 +4,8 @@ import (
 	"context"
 	"sync"
 
-	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v3"
 	"github.com/go-logr/logr"
 	"github.com/golang/protobuf/proto"
 	"google.golang.org/protobuf/types/known/structpb"
@@ -16,7 +16,7 @@ import (
 	core_runtime "github.com/kumahq/kuma/pkg/core/runtime"
 	"github.com/kumahq/kuma/pkg/kds/util"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
-	util_xds_v2 "github.com/kumahq/kuma/pkg/util/xds/v2"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 type StatusTracker interface {
@@ -43,7 +43,7 @@ func NewStatusTracker(runtimeInfo core_runtime.RuntimeInfo,
 var _ StatusTracker = &statusTracker{}
 
 type statusTracker struct {
-	util_xds_v2.NoopCallbacks
+	util_xds_v3.NoopCallbacks
 	runtimeInfo      core_runtime.RuntimeInfo
 	createStatusSink ZoneInsightSinkFactoryFunc
 	mu               sync.RWMutex // protects access to the fields below
@@ -107,7 +107,7 @@ func (c *statusTracker) OnStreamClosed(streamID int64) {
 
 // OnStreamRequest is called once a request is received on a stream.
 // Returning an error will end processing and close the stream. OnStreamClosed will still be called.
-func (c *statusTracker) OnStreamRequest(streamID int64, req *envoy.DiscoveryRequest) error {
+func (c *statusTracker) OnStreamRequest(streamID int64, req *envoy_sd.DiscoveryRequest) error {
 	c.mu.RLock() // read access to the map of all ADS streams
 	defer c.mu.RUnlock()
 
@@ -143,7 +143,7 @@ func (c *statusTracker) OnStreamRequest(streamID int64, req *envoy.DiscoveryRequ
 }
 
 // OnStreamResponse is called immediately prior to sending a response on a stream.
-func (c *statusTracker) OnStreamResponse(streamID int64, req *envoy.DiscoveryRequest, resp *envoy.DiscoveryResponse) {
+func (c *statusTracker) OnStreamResponse(streamID int64, req *envoy_sd.DiscoveryRequest, resp *envoy_sd.DiscoveryResponse) {
 	c.mu.RLock() // read access to the map of all ADS streams
 	defer c.mu.RUnlock()
 
diff --git a/pkg/kds/server/type_adjust_callbacks.go b/pkg/kds/server/type_adjust_callbacks.go
index 7e371729b..a09f8e046 100644
--- a/pkg/kds/server/type_adjust_callbacks.go
+++ b/pkg/kds/server/type_adjust_callbacks.go
@@ -1,10 +1,10 @@
 package server
 
 import (
-	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 
 	"github.com/kumahq/kuma/pkg/kds"
-	util_xds_v2 "github.com/kumahq/kuma/pkg/util/xds/v2"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
 )
 
 // We are using go-control-plane's server and cache for KDS exchange.
@@ -16,10 +16,10 @@ import (
 // Ignores the TypeURL from marshalling operation and overrides it with TypeURL of the request.
 // If we pass wrong TypeURL in envoy_api.DiscoveryResponse#Resources we won't be able to unmarshall it, therefore we need to adjust the type.
 type typeAdjustCallbacks struct {
-	util_xds_v2.NoopCallbacks
+	util_xds_v3.NoopCallbacks
 }
 
-func (c *typeAdjustCallbacks) OnStreamResponse(streamID int64, req *envoy_api.DiscoveryRequest, resp *envoy_api.DiscoveryResponse) {
+func (c *typeAdjustCallbacks) OnStreamResponse(streamID int64, req *envoy_sd.DiscoveryRequest, resp *envoy_sd.DiscoveryResponse) {
 	for _, res := range resp.Resources {
 		res.TypeUrl = kds.KumaResource
 	}
diff --git a/pkg/kds/util/resource.go b/pkg/kds/util/resource.go
index 0d1c42bc1..9795493f0 100644
--- a/pkg/kds/util/resource.go
+++ b/pkg/kds/util/resource.go
@@ -3,7 +3,7 @@ package util
 import (
 	"fmt"
 
-	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
@@ -13,7 +13,7 @@ import (
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
 )
 
-func ToCoreResourceList(response *envoy.DiscoveryResponse) (model.ResourceList, error) {
+func ToCoreResourceList(response *envoy_sd.DiscoveryResponse) (model.ResourceList, error) {
 	krs := []*mesh_proto.KumaResource{}
 	for _, r := range response.Resources {
 		kr := &mesh_proto.KumaResource{}


Test patch:
diff --git a/pkg/kds/client/sink_test.go b/pkg/kds/client/sink_test.go
index bbdd5053d..611b5119b 100644
--- a/pkg/kds/client/sink_test.go
+++ b/pkg/kds/client/sink_test.go
@@ -3,7 +3,7 @@ package client_test
 import (
 	"time"
 
-	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 
@@ -35,12 +35,12 @@ var _ = Describe("KDS Sink", func() {
 		}
 	})
 
-	It("", func() {
+	It("should verify KDS exchange", func() {
 		vrf := kds_verifier.New().
-			Exec(kds_verifier.WaitRequest(defaultTimeout, func(req *envoy_api_v2.DiscoveryRequest) {
+			Exec(kds_verifier.WaitRequest(defaultTimeout, func(req *envoy_sd.DiscoveryRequest) {
 				Expect(req.TypeUrl).To(Equal(string(mesh.MeshType)))
 			})).
-			Exec(kds_verifier.WaitRequest(defaultTimeout, func(req *envoy_api_v2.DiscoveryRequest) {
+			Exec(kds_verifier.WaitRequest(defaultTimeout, func(req *envoy_sd.DiscoveryRequest) {
 				Expect(req.TypeUrl).To(Equal(string(mesh.DataplaneType)))
 			})).
 			Exec(kds_verifier.DiscoveryResponse(
@@ -48,7 +48,7 @@ var _ = Describe("KDS Sink", func() {
 					{Meta: &test_model.ResourceMeta{Name: "mesh1"}, Spec: samples.Mesh1},
 					{Meta: &test_model.ResourceMeta{Name: "mesh2"}, Spec: samples.Mesh2},
 				}}, "1", "1")).
-			Exec(kds_verifier.WaitRequest(defaultTimeout, func(rs *envoy_api_v2.DiscoveryRequest) {
+			Exec(kds_verifier.WaitRequest(defaultTimeout, func(rs *envoy_sd.DiscoveryRequest) {
 				Expect(rs.VersionInfo).To(Equal("1"))
 				Expect(rs.ResponseNonce).To(Equal("1"))
 				Expect(rs.TypeUrl).To(Equal(string(mesh.MeshType)))
diff --git a/pkg/kds/mux/session_test.go b/pkg/kds/mux/session_test.go
index e88c4dec8..497c71bac 100644
--- a/pkg/kds/mux/session_test.go
+++ b/pkg/kds/mux/session_test.go
@@ -5,9 +5,10 @@ import (
 	"sync"
 	"time"
 
-	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	"google.golang.org/grpc/metadata"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/pkg/kds/mux"
@@ -29,7 +30,7 @@ func (t *testMultiplexStream) Recv() (*mesh_proto.Message, error) {
 }
 
 func (t *testMultiplexStream) Context() context.Context {
-	panic("implement me")
+	return metadata.AppendToOutgoingContext(context.Background(), mux.KDSVersionHeaderKey, mux.KDSVersionV3)
 }
 
 var _ = Describe("Multiplex Session", func() {
@@ -46,28 +47,28 @@ var _ = Describe("Multiplex Session", func() {
 		})
 
 		It("should Send to clientSession's ClientStream and Recv from serverSession's ServerStream", func() {
-			err := clientSession.ClientStream().Send(&envoy_api_v2.DiscoveryRequest{VersionInfo: "1"})
+			err := clientSession.ClientStream().Send(&envoy_sd.DiscoveryRequest{VersionInfo: "1"})
 			Expect(err).ToNot(HaveOccurred())
 			msg, err := serverSession.ServerStream().Recv()
 			Expect(err).ToNot(HaveOccurred())
 			Expect(msg.VersionInfo).To(Equal("1"))
 		})
 		It("should Send to serverSession's ServerStream and Recv from clientSession's ClientStream", func() {
-			err := serverSession.ServerStream().Send(&envoy_api_v2.DiscoveryResponse{VersionInfo: "2"})
+			err := serverSession.ServerStream().Send(&envoy_sd.DiscoveryResponse{VersionInfo: "2"})
 			Expect(err).ToNot(HaveOccurred())
 			msg, err := clientSession.ClientStream().Recv()
 			Expect(err).ToNot(HaveOccurred())
 			Expect(msg.VersionInfo).To(Equal("2"))
 		})
 		It("should Send to clientSession's ServerStream and Recv from serverSession's ClientStream", func() {
-			err := clientSession.ServerStream().Send(&envoy_api_v2.DiscoveryResponse{VersionInfo: "3"})
+			err := clientSession.ServerStream().Send(&envoy_sd.DiscoveryResponse{VersionInfo: "3"})
 			Expect(err).ToNot(HaveOccurred())
 			msg, err := serverSession.ClientStream().Recv()
 			Expect(err).ToNot(HaveOccurred())
 			Expect(msg.VersionInfo).To(Equal("3"))
 		})
 		It("should Send to serverSession's ClientStream and Recv from clientSession's ServerStream", func() {
-			err := serverSession.ClientStream().Send(&envoy_api_v2.DiscoveryRequest{VersionInfo: "4"})
+			err := serverSession.ClientStream().Send(&envoy_sd.DiscoveryRequest{VersionInfo: "4"})
 			Expect(err).ToNot(HaveOccurred())
 			msg, err := clientSession.ServerStream().Recv()
 			Expect(err).ToNot(HaveOccurred())
@@ -98,9 +99,9 @@ var _ = Describe("Multiplex Session", func() {
 					wg.Done()
 				}()
 
-				err := clientSession.ServerStream().Send(&envoy_api_v2.DiscoveryResponse{VersionInfo: "2"})
+				err := clientSession.ServerStream().Send(&envoy_sd.DiscoveryResponse{VersionInfo: "2"})
 				Expect(err).ToNot(HaveOccurred())
-				err = clientSession.ClientStream().Send(&envoy_api_v2.DiscoveryRequest{VersionInfo: "1"})
+				err = clientSession.ClientStream().Send(&envoy_sd.DiscoveryRequest{VersionInfo: "1"})
 				Expect(err).ToNot(HaveOccurred())
 
 				resp, err := serverSession.ClientStream().Recv()
diff --git a/pkg/kds/server/server_test.go b/pkg/kds/server/server_test.go
index 2cc707e43..d82382f3a 100644
--- a/pkg/kds/server/server_test.go
+++ b/pkg/kds/server/server_test.go
@@ -6,8 +6,8 @@ import (
 	"sync"
 	"time"
 
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"github.com/golang/protobuf/proto"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
@@ -50,8 +50,8 @@ var _ = Describe("KDS Server", func() {
 			ResourceStore:      s,
 			MockStream:         stream,
 			Wg:                 wg,
-			Responses:          map[string]*v2.DiscoveryResponse{},
-			LastACKedResponses: map[string]*v2.DiscoveryResponse{},
+			Responses:          map[string]*envoy_sd.DiscoveryResponse{},
+			LastACKedResponses: map[string]*envoy_sd.DiscoveryResponse{},
 		}
 	})
 
diff --git a/pkg/test/grpc/clientstream.go b/pkg/test/grpc/clientstream.go
index a4427b062..099824cef 100644
--- a/pkg/test/grpc/clientstream.go
+++ b/pkg/test/grpc/clientstream.go
@@ -3,15 +3,15 @@ package grpc
 import (
 	"context"
 
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"github.com/pkg/errors"
 	"google.golang.org/grpc"
 )
 
 type MockClientStream struct {
 	Ctx    context.Context
-	SentCh chan *v2.DiscoveryRequest
-	RecvCh chan *v2.DiscoveryResponse
+	SentCh chan *envoy_sd.DiscoveryRequest
+	RecvCh chan *envoy_sd.DiscoveryResponse
 	grpc.ClientStream
 }
 
@@ -19,12 +19,12 @@ func (stream *MockClientStream) Context() context.Context {
 	return stream.Ctx
 }
 
-func (stream *MockClientStream) Send(resp *v2.DiscoveryRequest) error {
+func (stream *MockClientStream) Send(resp *envoy_sd.DiscoveryRequest) error {
 	stream.SentCh <- resp
 	return nil
 }
 
-func (stream *MockClientStream) Recv() (*v2.DiscoveryResponse, error) {
+func (stream *MockClientStream) Recv() (*envoy_sd.DiscoveryResponse, error) {
 	req, more := <-stream.RecvCh
 	if !more {
 		return nil, errors.New("empty")
@@ -35,8 +35,8 @@ func (stream *MockClientStream) Recv() (*v2.DiscoveryResponse, error) {
 func MakeMockClientStream() *MockClientStream {
 	return &MockClientStream{
 		Ctx:    context.Background(),
-		RecvCh: make(chan *v2.DiscoveryResponse, 10),
-		SentCh: make(chan *v2.DiscoveryRequest, 10),
+		RecvCh: make(chan *envoy_sd.DiscoveryResponse, 10),
+		SentCh: make(chan *envoy_sd.DiscoveryRequest, 10),
 	}
 }
 
diff --git a/pkg/test/grpc/serverstream.go b/pkg/test/grpc/serverstream.go
index 74a48cdb5..a9b95ac96 100644
--- a/pkg/test/grpc/serverstream.go
+++ b/pkg/test/grpc/serverstream.go
@@ -3,15 +3,15 @@ package grpc
 import (
 	"context"
 
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"github.com/pkg/errors"
 	"google.golang.org/grpc"
 )
 
 type MockServerStream struct {
 	Ctx    context.Context
-	RecvCh chan *v2.DiscoveryRequest
-	SentCh chan *v2.DiscoveryResponse
+	RecvCh chan *envoy_sd.DiscoveryRequest
+	SentCh chan *envoy_sd.DiscoveryResponse
 	Nonce  int
 	grpc.ServerStream
 }
@@ -20,14 +20,14 @@ func (stream *MockServerStream) Context() context.Context {
 	return stream.Ctx
 }
 
-func (stream *MockServerStream) Send(resp *v2.DiscoveryResponse) error {
+func (stream *MockServerStream) Send(resp *envoy_sd.DiscoveryResponse) error {
 	// check that nonce is monotonically incrementing
 	stream.Nonce++
 	stream.SentCh <- resp
 	return nil
 }
 
-func (stream *MockServerStream) Recv() (*v2.DiscoveryRequest, error) {
+func (stream *MockServerStream) Recv() (*envoy_sd.DiscoveryRequest, error) {
 	req, more := <-stream.RecvCh
 	if !more {
 		return nil, errors.New("empty")
@@ -36,8 +36,8 @@ func (stream *MockServerStream) Recv() (*v2.DiscoveryRequest, error) {
 }
 
 func (stream *MockServerStream) ClientStream(stopCh chan struct{}) *MockClientStream {
-	sentCh := make(chan *v2.DiscoveryRequest)
-	recvCh := make(chan *v2.DiscoveryResponse)
+	sentCh := make(chan *envoy_sd.DiscoveryRequest)
+	recvCh := make(chan *envoy_sd.DiscoveryResponse)
 	go func() {
 		for {
 			r, more := <-sentCh
@@ -70,7 +70,7 @@ func (stream *MockServerStream) ClientStream(stopCh chan struct{}) *MockClientSt
 func MakeMockStream() *MockServerStream {
 	return &MockServerStream{
 		Ctx:    context.Background(),
-		SentCh: make(chan *v2.DiscoveryResponse, 10),
-		RecvCh: make(chan *v2.DiscoveryRequest, 10),
+		SentCh: make(chan *envoy_sd.DiscoveryResponse, 10),
+		RecvCh: make(chan *envoy_sd.DiscoveryRequest, 10),
 	}
 }
diff --git a/pkg/test/kds/verifier/context.go b/pkg/test/kds/verifier/context.go
index 1fbf483f6..5e064013c 100644
--- a/pkg/test/kds/verifier/context.go
+++ b/pkg/test/kds/verifier/context.go
@@ -3,7 +3,7 @@ package verifier
 import (
 	"sync"
 
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	test_grpc "github.com/kumahq/kuma/pkg/test/grpc"
@@ -14,10 +14,10 @@ type TestContext interface {
 	ServerStream() *test_grpc.MockServerStream
 	ClientStream() *test_grpc.MockClientStream
 	Stop() chan struct{}
-	SaveLastResponse(typ string, response *v2.DiscoveryResponse)
-	LastResponse(typeURL string) *v2.DiscoveryResponse
-	SaveLastACKedResponse(typ string, response *v2.DiscoveryResponse)
-	LastACKedResponse(typ string) *v2.DiscoveryResponse
+	SaveLastResponse(typ string, response *envoy_sd.DiscoveryResponse)
+	LastResponse(typeURL string) *envoy_sd.DiscoveryResponse
+	SaveLastACKedResponse(typ string, response *envoy_sd.DiscoveryResponse)
+	LastACKedResponse(typ string) *envoy_sd.DiscoveryResponse
 	WaitGroup() *sync.WaitGroup
 }
 
@@ -27,8 +27,8 @@ type TestContextImpl struct {
 	MockClientStream   *test_grpc.MockClientStream
 	StopCh             chan struct{}
 	Wg                 *sync.WaitGroup
-	Responses          map[string]*v2.DiscoveryResponse
-	LastACKedResponses map[string]*v2.DiscoveryResponse
+	Responses          map[string]*envoy_sd.DiscoveryResponse
+	LastACKedResponses map[string]*envoy_sd.DiscoveryResponse
 }
 
 func (t *TestContextImpl) Store() store.ResourceStore {
@@ -47,19 +47,19 @@ func (t *TestContextImpl) Stop() chan struct{} {
 	return t.StopCh
 }
 
-func (t *TestContextImpl) SaveLastResponse(typ string, response *v2.DiscoveryResponse) {
+func (t *TestContextImpl) SaveLastResponse(typ string, response *envoy_sd.DiscoveryResponse) {
 	t.Responses[typ] = response
 }
 
-func (t *TestContextImpl) LastResponse(typ string) *v2.DiscoveryResponse {
+func (t *TestContextImpl) LastResponse(typ string) *envoy_sd.DiscoveryResponse {
 	return t.Responses[typ]
 }
 
-func (t *TestContextImpl) SaveLastACKedResponse(typ string, response *v2.DiscoveryResponse) {
+func (t *TestContextImpl) SaveLastACKedResponse(typ string, response *envoy_sd.DiscoveryResponse) {
 	t.LastACKedResponses[typ] = response
 }
 
-func (t *TestContextImpl) LastACKedResponse(typ string) *v2.DiscoveryResponse {
+func (t *TestContextImpl) LastACKedResponse(typ string) *envoy_sd.DiscoveryResponse {
 	return t.LastACKedResponses[typ]
 }
 
diff --git a/pkg/test/kds/verifier/executables.go b/pkg/test/kds/verifier/executables.go
index 812d3d924..cd1061018 100644
--- a/pkg/test/kds/verifier/executables.go
+++ b/pkg/test/kds/verifier/executables.go
@@ -5,8 +5,8 @@ import (
 	"fmt"
 	"time"
 
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	"github.com/golang/protobuf/ptypes/any"
 
 	"github.com/kumahq/kuma/pkg/core/resources/model"
@@ -23,7 +23,7 @@ func Create(ctx context.Context, r model.Resource, opts ...store.CreateOptionsFu
 
 func DiscoveryRequest(node *envoy_core.Node, resourceType model.ResourceType) Executable {
 	return func(tc TestContext) error {
-		tc.ServerStream().RecvCh <- &v2.DiscoveryRequest{
+		tc.ServerStream().RecvCh <- &envoy_sd.DiscoveryRequest{
 			Node:    node,
 			TypeUrl: string(resourceType),
 		}
@@ -33,7 +33,7 @@ func DiscoveryRequest(node *envoy_core.Node, resourceType model.ResourceType) Ex
 
 func ACK(node *envoy_core.Node, resourceType model.ResourceType) Executable {
 	return func(tc TestContext) error {
-		tc.ServerStream().RecvCh <- &v2.DiscoveryRequest{
+		tc.ServerStream().RecvCh <- &envoy_sd.DiscoveryRequest{
 			Node:          node,
 			TypeUrl:       string(resourceType),
 			ResponseNonce: tc.LastResponse(string(resourceType)).Nonce,
@@ -46,7 +46,7 @@ func ACK(node *envoy_core.Node, resourceType model.ResourceType) Executable {
 
 func NACK(node *envoy_core.Node, resourceType model.ResourceType) Executable {
 	return func(tc TestContext) error {
-		tc.ServerStream().RecvCh <- &v2.DiscoveryRequest{
+		tc.ServerStream().RecvCh <- &envoy_sd.DiscoveryRequest{
 			Node:          node,
 			TypeUrl:       string(resourceType),
 			ResponseNonce: tc.LastResponse(string(resourceType)).Nonce,
@@ -94,7 +94,7 @@ func CloseStream() Executable {
 	}
 }
 
-func WaitRequest(timeout time.Duration, testFunc func(rs *v2.DiscoveryRequest)) Executable {
+func WaitRequest(timeout time.Duration, testFunc func(rs *envoy_sd.DiscoveryRequest)) Executable {
 	return func(tc TestContext) error {
 		select {
 		case req := <-tc.ClientStream().SentCh:
@@ -120,7 +120,7 @@ func DiscoveryResponse(rs model.ResourceList, nonce, version string) Executable
 			}
 			resources = append(resources, pbaby)
 		}
-		tc.ClientStream().RecvCh <- &v2.DiscoveryResponse{
+		tc.ClientStream().RecvCh <- &envoy_sd.DiscoveryResponse{
 			TypeUrl:     string(rs.GetItemType()),
 			Nonce:       nonce,
 			VersionInfo: version,
diff --git a/test/e2e/compatibility/compatibility_suite_test.go b/test/e2e/compatibility/compatibility_suite_test.go
index 0d5077d9e..62ad6d9e8 100644
--- a/test/e2e/compatibility/compatibility_suite_test.go
+++ b/test/e2e/compatibility/compatibility_suite_test.go
@@ -9,7 +9,7 @@ import (
 
 func TestE2ECompatibility(t *testing.T) {
 	if framework.IsK8sClustersStarted() {
-		test.RunSpecs(t, "E2E Deploy Suite")
+		test.RunSpecs(t, "E2E Compatibility Suite")
 	} else {
 		t.SkipNow()
 	}
diff --git a/test/e2e/compatibility/cp_compatibility_kubernetes_multizone.go b/test/e2e/compatibility/cp_compatibility_kubernetes_multizone.go
new file mode 100644
index 000000000..76c6d9851
--- /dev/null
+++ b/test/e2e/compatibility/cp_compatibility_kubernetes_multizone.go
@@ -0,0 +1,134 @@
+package compatibility
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	"github.com/gruntwork-io/terratest/modules/random"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/kumahq/kuma/pkg/config/core"
+	. "github.com/kumahq/kuma/test/framework"
+)
+
+var OldChart = "0.6.3"
+var UpstreamImageRegistry = "kumahq"
+
+func CpCompatibilityMultizoneKubernetes() {
+	var globalCluster Cluster
+	var globalReleaseName string
+	var globalDeployOptsFuncs = KumaK8sDeployOpts
+
+	var zoneCluster Cluster
+	var zoneDeployOptsFuncs = KumaZoneK8sDeployOpts
+	var zoneReleaseName string
+
+	BeforeEach(func() {
+		// Global CP
+		c, err := NewK8sClusterWithTimeout(
+			NewTestingT(),
+			Kuma1,
+			Silent,
+			6*time.Second)
+		Expect(err).ToNot(HaveOccurred())
+		globalCluster = c.WithRetries(60)
+
+		globalReleaseName = fmt.Sprintf(
+			"kuma-%s",
+			strings.ToLower(random.UniqueId()),
+		)
+
+		globalDeployOptsFuncs = append(globalDeployOptsFuncs,
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmChartPath(HelmRepo),
+			WithHelmReleaseName(globalReleaseName),
+			WithHelmChartVersion(OldChart),
+			WithoutHelmOpt("global.image.tag"),
+			WithHelmOpt("global.image.registry", UpstreamImageRegistry))
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Global, globalDeployOptsFuncs...)).
+			Setup(globalCluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		Expect(globalCluster.VerifyKuma()).To(Succeed())
+
+		// Zone CP
+		c, err = NewK8sClusterWithTimeout(
+			NewTestingT(),
+			Kuma2,
+			Silent,
+			6*time.Second)
+		Expect(err).ToNot(HaveOccurred())
+		zoneCluster = c.WithRetries(60)
+
+		zoneReleaseName = fmt.Sprintf(
+			"kuma-%s",
+			strings.ToLower(random.UniqueId()),
+		)
+
+		zoneDeployOptsFuncs = append(zoneDeployOptsFuncs,
+			WithInstallationMode(HelmInstallationMode),
+			WithHelmChartPath(HelmRepo),
+			WithHelmReleaseName(zoneReleaseName),
+			WithHelmChartVersion(OldChart),
+			WithoutHelmOpt("global.image.tag"),
+			WithHelmOpt("global.image.registry", UpstreamImageRegistry),
+			WithGlobalAddress(globalCluster.GetKuma().GetKDSServerAddress()),
+			WithHelmOpt("ingress.enabled", "true"),
+		)
+
+		err = NewClusterSetup().
+			Install(Kuma(core.Zone, zoneDeployOptsFuncs...)).
+			Install(NamespaceWithSidecarInjection(TestNamespace)).
+			Setup(zoneCluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		Expect(zoneCluster.VerifyKuma()).To(Succeed())
+	})
+
+	AfterEach(func() {
+		if ShouldSkipCleanup() {
+			return
+		}
+
+		Expect(zoneCluster.DeleteKuma(zoneDeployOptsFuncs...)).To(Succeed())
+		Expect(zoneCluster.DismissCluster()).To(Succeed())
+
+		Expect(globalCluster.DeleteKuma(globalDeployOptsFuncs...)).To(Succeed())
+		Expect(globalCluster.DismissCluster()).To(Succeed())
+	})
+
+	It("should sync resources between new global and old zone", func() {
+		// when global is upgraded
+		upgradeOptsFuncs := append(KumaK8sDeployOpts, WithHelmReleaseName(globalReleaseName))
+		err := globalCluster.(*K8sCluster).UpgradeKuma(core.Global, upgradeOptsFuncs...)
+		Expect(err).ToNot(HaveOccurred())
+
+		// and new resource is created on Global
+		err = YamlK8s(`
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: demo
+`)(globalCluster)
+
+		// then the resource is synchronized when old remote is connected (KDS is backwards compatible)
+		Expect(err).ToNot(HaveOccurred())
+		Eventually(func() (string, error) {
+			return k8s.RunKubectlAndGetOutputE(zoneCluster.GetTesting(), zoneCluster.GetKubectlOptions(), "get", "meshes")
+		}, "30s", "1s").Should(ContainSubstring("demo"))
+
+		// when new resources is created on Zone
+		err = DemoClientK8s("default")(zoneCluster)
+
+		// then resource is synchronized to Global
+		Expect(err).ToNot(HaveOccurred())
+		Eventually(func() (string, error) {
+			return k8s.RunKubectlAndGetOutputE(globalCluster.GetTesting(), globalCluster.GetKubectlOptions("default"), "get", "dataplanes")
+		}, "30s", "1s").Should(ContainSubstring("demo-client"))
+	})
+}
diff --git a/test/e2e/compatibility/cp_compatibility_kubernetes_multizone_test.go b/test/e2e/compatibility/cp_compatibility_kubernetes_multizone_test.go
new file mode 100644
index 000000000..1e1549b5f
--- /dev/null
+++ b/test/e2e/compatibility/cp_compatibility_kubernetes_multizone_test.go
@@ -0,0 +1,9 @@
+package compatibility_test
+
+import (
+	. "github.com/onsi/ginkgo"
+
+	"github.com/kumahq/kuma/test/e2e/compatibility"
+)
+
+var _ = Describe("Test Kubernetes Multizone Compatibility", compatibility.CpCompatibilityMultizoneKubernetes)
diff --git a/test/framework/setup.go b/test/framework/setup.go
index b65a0f130..f9f14a455 100644
--- a/test/framework/setup.go
+++ b/test/framework/setup.go
@@ -277,6 +277,17 @@ spec:
 	)
 }
 
+func NamespaceWithSidecarInjection(namespace string) InstallFunc {
+	return YamlK8s(fmt.Sprintf(`
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: %s
+  annotations:
+    kuma.io/sidecar-injection: "enabled"
+`, namespace))
+}
+
 func DemoClientJobK8s(mesh, destination string) InstallFunc {
 	const name = "demo-job-client"
 	deployment := `