Repository: kumahq/kuma
Pull request #1290 (2020-12-10 17:40:12)

chore(kuma-cp) upgrade go-control-plane
# Upgrade of Go Control Plane

We are mostly interested in up to date Envoy Protocol Buffers for API V3, but Go Control Plane brings a couple of changes along the way which results in an opportunity to clean some of our code. We had a lot of copy paste code of Go Control Plane with some adjustments. This tech debt results in many difficulties when upgrading the library.

## XDS

In XDS, we had a copy paste of pkg/xds/server/server.go with [fix for cluster warming](https://github.com/kumahq/kuma/pull/331) that was never merged into upstream.
The problem itself was brought up multiple times on the Envoy Slack and issues and the most up to date discussion is [here](https://github.com/envoyproxy/envoy/issues/13009).
I've rewritten the fix into callbacks with a slight change (instead of sending same EDS response twice, I change the version which results in sending EDS).
This is slightly less optimal, but we don't update Clusters that often. Read `resourceWarmingForcer` for more.

The result is that I removed custom server.go and the fix is implemented with callback, which will make upgrades much easier.

## Custom resources in Go Control Plane

Go Control Plane's **server** now supports custom resources.
Just to remind an architecture of the Go Control Plane:
Server requires ConfigWatcher (SnapshotCache). You put a Snapshot (list of resources) into SnapshotCache which triggers sending a response to the client.
The server now supports custom resources, but the regular SnapshotCache still only supports Envoy resources.
Instead, GCP maintainers introduced LinearCache which supports custom resources (of 1 type and you can compose many caches with MuxCache).
The problem with this cache is that it versions every single resource individually and it triggers update on [every single resource change](https://github.com/envoyproxy/go-control-plane/blob/master/pkg/cache/v2/linear.go#L163).
The interface of `LinearCache` is also not convenient for us because of `UpdateResource` and `DeleteResource`. We are rather relying on "replace" operation in our code.

## MADS

MADS is our custom resource that we send using GCP. I leveraged server custom resource support.

The result is that I removed custom pkg/mads/server/mads.go
Our pkg/util/cache.go unfortunately have to stay for now. I'll start a discussion with GCP maintainers about the changes so we can get rid of this. 

## KDS

Similar to the situation above.
Additionally, to make it work I had to introduce a small workaround described in `typeAdjustCallbacks`

The result is that I removed custom pkg/kds/server/kds.go but pkg/util/cache.go stays as is.

## Linter
The linter now loudly complains about deprecation from Envoy protobufs.
I ignored `l.UseOriginalDst` for now as fixing this is out of the scope of this PR.
I removed Zipkin V1 since it does not work anyway anymore with the version that we are using.

## Callbacks
Since now we have so many callbacks, I noticed that we are implementing empty methods with no operation many times, so I introduced NoopCallbacks to ignore the implementation of methods that we don't care about.

## Docs

No docs, internal changes.

jakubdyszkiewicz: > One question: as a result of all this, are we getting easier go-control-plane upgrades now?

Yes, upgrades should be much easier now. I'm still looking forward to remove `pkg/util/cache.go` so it will be just a regular upgrade, not a special task

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 87b63bf2d..7f521a536 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -33,3 +33,6 @@ issues:
     - linters:
         - staticcheck
       text: "SA1019: proto.MessageType is deprecated" # TODO ignore deprecation of proto library. We don't want to migrate yet because go-control-plane is not ready
+    - linters:
+        - staticcheck
+      text: "SA1019: l.UseOriginalDst is deprecated: Do not use." # TODO What is the up-to-date alternative ?
diff --git a/api/go.mod b/api/go.mod
index 9e2a341d2..6b00c5b7a 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -3,7 +3,7 @@ module github.com/kumahq/kuma/api
 go 1.15
 
 require (
-	github.com/envoyproxy/go-control-plane v0.9.5
+	github.com/envoyproxy/go-control-plane v0.9.8
 	github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9
 	github.com/ghodss/yaml v1.0.0
 	github.com/golang/protobuf v1.4.3
diff --git a/api/go.sum b/api/go.sum
index 05b1b536e..e7289a1af 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -1,16 +1,24 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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 v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
+github.com/envoyproxy/go-control-plane v0.9.8 h1:bbmjRkjmP0ZggMoahdNMmJFFnK7v5H+/j5niP5QH6bg=
+github.com/envoyproxy/go-control-plane v0.9.8/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9 h1:mU3oOAYTK9ZRKMj//H1EWk1vWbkYeWKBPYKMnIwcs/A=
 github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -28,6 +36,7 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
@@ -42,7 +51,10 @@ github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -82,15 +94,18 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f h1:2wh8dWY8959cBGQvk1RD+/eQBgRYYDaZ+hT0/zsARoA=
 google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -100,9 +115,12 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/go.mod b/go.mod
index f4cba3f2d..31d3a709a 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/emicklei/go-restful v2.14.2+incompatible
-	github.com/envoyproxy/go-control-plane v0.9.5
+	github.com/envoyproxy/go-control-plane v0.9.8
 	github.com/envoyproxy/protoc-gen-validate v0.4.1
 	github.com/ghodss/yaml v1.0.0
 	github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0
@@ -45,7 +45,7 @@ require (
 	go.uber.org/multierr v1.3.0
 	go.uber.org/zap v1.13.0
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0
-	golang.org/x/tools v0.0.0-20201211025543-abf6a1d87e11 // indirect
+	golang.org/x/tools v0.0.0-20201210164618-f31efc5a5c28 // indirect
 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
 	google.golang.org/grpc v1.30.0
 	google.golang.org/protobuf v1.25.0
diff --git a/go.sum b/go.sum
index e3c4a060c..f06a5310f 100644
--- a/go.sum
+++ b/go.sum
@@ -23,6 +23,7 @@ github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
 github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v38.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+QMLQEuy3wREyY4oc=
 github.com/Azure/go-autorest v13.3.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
@@ -62,6 +63,7 @@ github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhH
 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
 github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190405210948-c70a36b8193f/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
 github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
@@ -69,10 +71,13 @@ github.com/InVisionApp/go-health v2.1.0+incompatible/go.mod h1:/+Gv1o8JUsrjC6pi6
 github.com/InVisionApp/go-logger v1.0.1/go.mod h1:+cGTDSn+P8105aZkeOfIhdd7vFO5X1afUHcjvanY0L8=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
+github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
 github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/sprig v2.20.0+incompatible h1:dJTKKuUkYW3RMFdQFXPU/s6hg10RgctmTjRcbZ98Ap8=
 github.com/Masterminds/sprig v2.20.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y=
 github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
@@ -104,6 +109,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
 github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -111,6 +117,7 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-metrics v0.3.2 h1:EyUnxyP2yaGpLgMiuyyz8sHnByqeTJUfGs72pdH0i4A=
 github.com/armon/go-metrics v0.3.2/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
@@ -130,6 +137,7 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw
 github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
@@ -149,9 +157,12 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
 github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -163,7 +174,8 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@@ -204,6 +216,7 @@ github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2
 github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
 github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
@@ -211,8 +224,10 @@ github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zA
 github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
 github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dhui/dktest v0.3.1 h1:NVUdB50k8tml431Ho1hcQBNeC52Qe8oSDPAjseA67Y8=
 github.com/dhui/dktest v0.3.1/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc=
 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
@@ -221,6 +236,7 @@ github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop
 github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
 github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@@ -230,9 +246,11 @@ github.com/docker/docker v1.4.2-0.20191008235115-448db5a783a0/go.mod h1:eEKB0N0r
 github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce h1:KXS1Jg+ddGcWA8e1N7cupxaHHZhit5rB9tfDU+mfjyY=
 github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
 github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@@ -260,7 +278,8 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
 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 v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
+github.com/envoyproxy/go-control-plane v0.9.8 h1:bbmjRkjmP0ZggMoahdNMmJFFnK7v5H+/j5niP5QH6bg=
+github.com/envoyproxy/go-control-plane v0.9.8/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM=
@@ -277,10 +296,12 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
 github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
@@ -299,7 +320,9 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
 github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
 github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
@@ -352,6 +375,7 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
 github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
@@ -370,7 +394,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang-migrate/migrate/v4 v4.8.0 h1:zcamXqBH0W8hHwpaikOGnaFTRrQWU+X8ukBeY1dYucU=
 github.com/golang-migrate/migrate/v4 v4.8.0/go.mod h1:F6bGIGAA7xSb2k17sF1+eHl2gRHa+DWNZpoIKbThPLE=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -378,6 +404,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -415,6 +442,7 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -422,12 +450,14 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
 github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
 github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
 github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
@@ -446,6 +476,7 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 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=
@@ -460,15 +491,20 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMW
 github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
 github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
+github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE=
 github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
@@ -482,14 +518,18 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/hcl v1.0.1-0.20190430135223-99e2f22d1c94 h1:LaH4JWe6Q7ICdxL5raxQjSRw7Pj8uTtAENrjejIYZIg=
 github.com/hashicorp/hcl v1.0.1-0.20190430135223-99e2f22d1c94/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
 github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
+github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69 h1:umaj0TCQ9lWUUKy2DxAhEzPbwd0jnxiw1EI2z3FiILM=
 github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69/go.mod h1:zdLK9ilQRSMjSeLKoZ4BqUfBT7jswTGF8zRlKEsiRXA=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
@@ -503,6 +543,7 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
 github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
 github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imkira/go-observer v1.0.3/go.mod h1:zLzElv2cGTHufQG17IEILJMPDg32TD85fFgKyFv00wU=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
@@ -533,6 +574,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
 github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
@@ -543,9 +585,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
 github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
@@ -588,6 +632,7 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -611,11 +656,14 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQ
 github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+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-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -633,6 +681,7 @@ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -662,6 +711,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
 github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
@@ -678,7 +728,9 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
 github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
 github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -696,6 +748,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
@@ -717,6 +770,7 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
@@ -766,6 +820,7 @@ github.com/sethvargo/go-retry v0.1.0/go.mod h1:JzIOdZqQDNpPkQDmcqgtteAcxFLtYpNF/
 github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -793,24 +848,30 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
 github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spiffe/go-spiffe v0.0.0-20190717182101-d8657cb50cae/go.mod h1:HyNeJnVYkDyQgB2qcSPxVYkAA2F3lQu51bDxNpFcKxY=
+github.com/spiffe/go-spiffe v0.0.0-20190820222348-6adcf1eecbcc h1:YeGvOZLHZxE8nrxPGDNH1LGv8zzkAIDfIRgy256rotk=
 github.com/spiffe/go-spiffe v0.0.0-20190820222348-6adcf1eecbcc/go.mod h1:HyNeJnVYkDyQgB2qcSPxVYkAA2F3lQu51bDxNpFcKxY=
+github.com/spiffe/spire v0.10.0 h1:UuBHgHI/4Uqcn0SpVMXWPgWtgKLJkBXOWP9Vs/82sZ4=
 github.com/spiffe/spire v0.10.0/go.mod h1:6dqxKqiDPREetq8Z62bcb+zFybVr/cj2ncTt0iLwREw=
+github.com/spiffe/spire/proto/spire v0.10.0 h1:NJ6HK9/XlnvqTox3fJUTAt6180105ZknpDS6sJC+QUM=
 github.com/spiffe/spire/proto/spire v0.10.0/go.mod h1:QmfLOnjxfc4o9KAe1f8bwYX3WcMLWSiKljTEHdGNFYU=
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -825,6 +886,7 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/uber-go/tally v3.3.12+incompatible h1:Qa0XrHsKXclmhEpHmBHTTEZotwvQHAbm3lvtJ6RNn+0=
 github.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -859,6 +921,7 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
 github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
 github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
 github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+github.com/zeebo/errs v1.2.0 h1:Tk8UszIOLEjtx6DWnvfmMJe6N8q7vu03Bj95HMWDUkc=
 github.com/zeebo/errs v1.2.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
 github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
 gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
@@ -983,6 +1046,7 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1099,13 +1163,14 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200113040837-eac381796e91/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20201211025543-abf6a1d87e11 h1:9j/upNXDRpADUw2RpUfJ7E7GHtfhDih62kX6JM8vs2c=
-golang.org/x/tools v0.0.0-20201211025543-abf6a1d87e11/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201210164618-f31efc5a5c28 h1:VgBpncx/x8r0uyVJrAj/3+59Sty1KTH5bBAyeIMRPEA=
+golang.org/x/tools v0.0.0-20201210164618-f31efc5a5c28/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
 gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
@@ -1178,6 +1243,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -1186,11 +1252,13 @@ gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
 gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -1205,6 +1273,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 helm.sh/helm/v3 v3.3.4 h1:tbad6WQVMxEw1HlVBvI2rQqOblmI5lgXOrWAMwJ198M=
 helm.sh/helm/v3 v3.3.4/go.mod h1:CyCGQa53/k1JFxXvXveGwtfJ4cuB9zkaBSGa5rnAiHU=
@@ -1251,10 +1320,12 @@ k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
 k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+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.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
 k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
 k8s.io/kubectl v0.18.8/go.mod h1:PlEgIAjOMua4hDFTEkVf+W5M0asHUKfE4y7VDZkpLHM=
 k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
@@ -1285,11 +1356,15 @@ sigs.k8s.io/controller-runtime v0.6.1 h1:LcK2+nk0kmaOnKGN+vBcWHqY5WDJNJNB/c5pW+s
 sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
 sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs=
 sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
 vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
diff --git a/pkg/kds/server/components.go b/pkg/kds/server/components.go
index 5c5aa0e1b..cfd8faf45 100644
--- a/pkg/kds/server/components.go
+++ b/pkg/kds/server/components.go
@@ -34,6 +34,8 @@ func New(log logr.Logger, rt core_runtime.Runtime, providedTypes []model.Resourc
 		return nil, err
 	}
 	callbacks := util_xds.CallbacksChain{
+		&typeAdjustCallbacks{},
+		util_xds.NewControlPlaneIdCallbacks(serverID),
 		util_xds.LoggingCallbacks{Log: log},
 		statsCallbacks,
 		syncTracker,
@@ -41,7 +43,7 @@ func New(log logr.Logger, rt core_runtime.Runtime, providedTypes []model.Resourc
 	if insight {
 		callbacks = append(callbacks, DefaultStatusTracker(rt, log))
 	}
-	return NewServer(cache, callbacks, log, serverID), nil
+	return NewServer(cache, callbacks, log), nil
 }
 
 func DefaultStatusTracker(rt core_runtime.Runtime, log logr.Logger) StatusTracker {
diff --git a/pkg/kds/server/kds.go b/pkg/kds/server/kds.go
index 83bb19e2e..e456c4819 100644
--- a/pkg/kds/server/kds.go
+++ b/pkg/kds/server/kds.go
@@ -2,536 +2,40 @@ package server
 
 import (
 	"context"
-	"strconv"
-	"sync/atomic"
 
-	"github.com/kumahq/kuma/pkg/kds"
-
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/envoyproxy/go-control-plane/pkg/server/sotw/v2"
 
 	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
 	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
 	envoy_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
 	"github.com/go-logr/logr"
-	"github.com/golang/protobuf/proto"
-	"github.com/golang/protobuf/ptypes/any"
-	"github.com/pkg/errors"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
-	mesh_core "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 )
 
 type Server interface {
 	mesh_proto.KumaDiscoveryServiceServer
 }
 
-func NewServer(config envoy_cache.Cache, callbacks envoy_server.Callbacks, log logr.Logger, clusterID string) Server {
-	return &server{cache: config, callbacks: callbacks, log: log, clusterID: clusterID}
-}
-
-// server is a simplified version of the original XDS server at
-// https://github.com/envoyproxy/go-control-plane/blob/master/pkg/server/server.go
-type server struct {
-	cache     envoy_cache.Cache
-	callbacks envoy_server.Callbacks
-
-	// streamCount for counting bi-di streams
-	streamCount int64
-
-	log logr.Logger
-
-	clusterID string
-}
-
-type stream interface {
-	grpc.ServerStream
-
-	Send(*envoy.DiscoveryResponse) error
-	Recv() (*envoy.DiscoveryRequest, error)
-}
-
-// watches for all xDS resource types
-type watches struct {
-	meshes             chan envoy_cache.Response
-	ingresses          chan envoy_cache.Response
-	dataplaneInsights  chan envoy_cache.Response
-	externalServices   chan envoy_cache.Response
-	circuitBreakers    chan envoy_cache.Response
-	faultInjections    chan envoy_cache.Response
-	healthChecks       chan envoy_cache.Response
-	trafficLogs        chan envoy_cache.Response
-	trafficPermissions chan envoy_cache.Response
-	trafficRoutes      chan envoy_cache.Response
-	trafficTraces      chan envoy_cache.Response
-	proxyTemplates     chan envoy_cache.Response
-	secrets            chan envoy_cache.Response
-	config             chan envoy_cache.Response
-
-	meshesCancel             func()
-	ingressesCancel          func()
-	dataplaneInsightsCancel  func()
-	externalServicesCancel   func()
-	circuitBreakersCancel    func()
-	faultInjectionsCancel    func()
-	healthChecksCancel       func()
-	trafficLogsCancel        func()
-	trafficPermissionsCancel func()
-	trafficRoutesCancel      func()
-	trafficTracesCancel      func()
-	proxyTemplatesCancel     func()
-	secretsCancel            func()
-	configCancel             func()
-
-	meshesNonce             string
-	ingressesNonce          string
-	dataplaneInsightsNonce  string
-	externalServiceNonce    string
-	circuitBreakersNonce    string
-	faultInjectionsNonce    string
-	healthChecksNonce       string
-	trafficLogsNonce        string
-	trafficPermissionsNonce string
-	trafficRoutesNonce      string
-	trafficTracesNonce      string
-	proxyTemplatesNonce     string
-	secretsNonce            string
-	configNonce             string
+func NewServer(config envoy_cache.Cache, callbacks envoy_server.Callbacks, log logr.Logger) Server {
+	sotwServer := sotw.NewServer(context.Background(), config, callbacks)
+	return &server{sotwServer}
 }
 
-// Cancel all watches
-func (values watches) Cancel() {
-	if values.meshesCancel != nil {
-		values.meshesCancel()
-	}
-	if values.ingressesCancel != nil {
-		values.ingressesCancel()
-	}
-	if values.dataplaneInsightsCancel != nil {
-		values.dataplaneInsightsCancel()
-	}
-	if values.circuitBreakersCancel != nil {
-		values.circuitBreakersCancel()
-	}
-	if values.faultInjectionsCancel != nil {
-		values.faultInjectionsCancel()
-	}
-	if values.healthChecksCancel != nil {
-		values.healthChecksCancel()
-	}
-	if values.trafficLogsCancel != nil {
-		values.trafficLogsCancel()
-	}
-	if values.trafficPermissionsCancel != nil {
-		values.trafficPermissionsCancel()
-	}
-	if values.trafficRoutesCancel != nil {
-		values.trafficRoutesCancel()
-	}
-	if values.trafficTracesCancel != nil {
-		values.trafficTracesCancel()
-	}
-	if values.proxyTemplatesCancel != nil {
-		values.proxyTemplatesCancel()
-	}
-	if values.secretsCancel != nil {
-		values.secretsCancel()
-	}
-	if values.configCancel != nil {
-		values.configCancel()
-	}
-}
-
-func createResponse(resp *envoy_cache.Response, typeURL string, clusterID string) (*envoy.DiscoveryResponse, error) {
-	if resp == nil {
-		return nil, errors.New("missing response")
-	}
-	resources := make([]*any.Any, len(resp.Resources))
-	for i := 0; i < len(resp.Resources); i++ {
-		// Envoy relies on serialized protobuf bytes for detecting changes to the resources.
-		// This requires deterministic serialization.
-		b := proto.NewBuffer(nil)
-		b.SetDeterministic(true)
-		err := b.Marshal(resp.Resources[i])
-		if err != nil {
-			return nil, err
-		}
-		resources[i] = &any.Any{
-			TypeUrl: kds.KumaResource,
-			Value:   b.Bytes(),
-		}
-	}
-	out := &envoy.DiscoveryResponse{
-		ControlPlane: &envoy_core.ControlPlane{
-			Identifier: clusterID,
-		},
-		VersionInfo: resp.Version,
-		Resources:   resources,
-		TypeUrl:     typeURL,
-	}
-	return out, nil
-}
-
-// process handles a bi-di stream request
-func (s *server) process(stream stream, reqCh <-chan *envoy.DiscoveryRequest) (err error) {
-	// increment stream count
-	streamID := atomic.AddInt64(&s.streamCount, 1)
-
-	log := s.log.WithValues("streamID", streamID)
-	defer func() {
-		if err != nil {
-			log.Error(err, "xDS stream terminated with an error")
-		}
-	}()
-
-	// unique nonce generator for req-resp pairs per xDS stream; the server
-	// ignores stale nonces. nonce is only modified within send() function.
-	var streamNonce int64
-
-	// a collection of watches per request type
-	var values watches
-	defer func() {
-		values.Cancel()
-		if s.callbacks != nil {
-			s.callbacks.OnStreamClosed(streamID)
-		}
-	}()
-
-	// sends a response by serializing to protobuf Any
-	send := func(resp envoy_cache.Response, resourceType model.ResourceType) (string, error) {
-		out, err := createResponse(&resp, string(resourceType), s.clusterID)
-		if err != nil {
-			return "", err
-		}
-
-		// increment nonce
-		streamNonce++
-		out.Nonce = strconv.FormatInt(streamNonce, 10)
-		if s.callbacks != nil {
-			s.callbacks.OnStreamResponse(streamID, &resp.Request, out)
-		}
-
-		return out.Nonce, stream.Send(out)
-	}
-
-	if s.callbacks != nil {
-		if err := s.callbacks.OnStreamOpen(stream.Context(), streamID, ""); err != nil {
-			return err
-		}
-	}
-
-	// node may only be set on the first discovery request
-	var node = &envoy_core.Node{}
-
-	for {
-		select {
-		case resp, more := <-values.meshes:
-			if !more {
-				return status.Errorf(codes.Unavailable, "meshes watch failed")
-			}
-			nonce, err := send(resp, mesh_core.MeshType)
-			if err != nil {
-				return err
-			}
-			values.meshesNonce = nonce
+var _ Server = &server{}
 
-		case resp, more := <-values.ingresses:
-			if !more {
-				return status.Errorf(codes.Unavailable, "ingresses watch failed")
-			}
-			nonce, err := send(resp, mesh_core.DataplaneType)
-			if err != nil {
-				return err
-			}
-			values.ingressesNonce = nonce
-
-		case resp, more := <-values.dataplaneInsights:
-			if !more {
-				return status.Errorf(codes.Unavailable, "dataplane insights watch failed")
-			}
-			nonce, err := send(resp, mesh_core.DataplaneInsightType)
-			if err != nil {
-				return err
-			}
-			values.dataplaneInsightsNonce = nonce
-
-		case resp, more := <-values.externalServices:
-			if !more {
-				return status.Errorf(codes.Unavailable, "externalServices watch failed")
-			}
-			nonce, err := send(resp, mesh_core.ExternalServiceType)
-			if err != nil {
-				return err
-			}
-			values.externalServiceNonce = nonce
-
-		case resp, more := <-values.circuitBreakers:
-			if !more {
-				return status.Errorf(codes.Unavailable, "circuitBreakers watch failed")
-			}
-			nonce, err := send(resp, mesh_core.CircuitBreakerType)
-			if err != nil {
-				return err
-			}
-			values.circuitBreakersNonce = nonce
-
-		case resp, more := <-values.faultInjections:
-			if !more {
-				return status.Errorf(codes.Unavailable, "faultInjections watch failed")
-			}
-			nonce, err := send(resp, mesh_core.FaultInjectionType)
-			if err != nil {
-				return err
-			}
-			values.faultInjectionsNonce = nonce
-
-		case resp, more := <-values.healthChecks:
-			if !more {
-				return status.Errorf(codes.Unavailable, "healthChecks watch failed")
-			}
-			nonce, err := send(resp, mesh_core.HealthCheckType)
-			if err != nil {
-				return err
-			}
-			values.healthChecksNonce = nonce
-
-		case resp, more := <-values.trafficLogs:
-			if !more {
-				return status.Errorf(codes.Unavailable, "trafficLogs watch failed")
-			}
-			nonce, err := send(resp, mesh_core.TrafficLogType)
-			if err != nil {
-				return err
-			}
-			values.trafficLogsNonce = nonce
-
-		case resp, more := <-values.trafficPermissions:
-			if !more {
-				return status.Errorf(codes.Unavailable, "trafficPermissions watch failed")
-			}
-			nonce, err := send(resp, mesh_core.TrafficPermissionType)
-			if err != nil {
-				return err
-			}
-			values.trafficPermissionsNonce = nonce
-
-		case resp, more := <-values.trafficRoutes:
-			if !more {
-				return status.Errorf(codes.Unavailable, "trafficRoutes watch failed")
-			}
-			nonce, err := send(resp, mesh_core.TrafficRouteType)
-			if err != nil {
-				return err
-			}
-			values.trafficRoutesNonce = nonce
-
-		case resp, more := <-values.trafficTraces:
-			if !more {
-				return status.Errorf(codes.Unavailable, "trafficTraces watch failed")
-			}
-			nonce, err := send(resp, mesh_core.TrafficTraceType)
-			if err != nil {
-				return err
-			}
-			values.trafficTracesNonce = nonce
-
-		case resp, more := <-values.proxyTemplates:
-			if !more {
-				return status.Errorf(codes.Unavailable, "proxyTemplates watch failed")
-			}
-			nonce, err := send(resp, mesh_core.ProxyTemplateType)
-			if err != nil {
-				return err
-			}
-			values.proxyTemplatesNonce = nonce
-
-		case resp, more := <-values.secrets:
-			if !more {
-				return status.Errorf(codes.Unavailable, "secrets watch failed")
-			}
-			nonce, err := send(resp, system.SecretType)
-			if err != nil {
-				return err
-			}
-			values.secretsNonce = nonce
-
-		case resp, more := <-values.config:
-			if !more {
-				return status.Errorf(codes.Unavailable, "config watch failed")
-			}
-			nonce, err := send(resp, system.ConfigType)
-			if err != nil {
-				return err
-			}
-			values.configNonce = nonce
-
-		case req, more := <-reqCh:
-			// input stream ended or errored out
-			if !more {
-				return nil
-			}
-			if req == nil {
-				return status.Errorf(codes.Unavailable, "empty request")
-			}
-
-			// node field in discovery request is delta-compressed
-			if req.Node != nil {
-				node = req.Node
-			} else {
-				req.Node = node
-			}
-
-			// nonces can be reused across streams; we verify nonce only if nonce is not initialized
-			nonce := req.GetResponseNonce()
-
-			// type URL is required for ADS but is implicit for xDS
-			if req.TypeUrl == "" {
-				return status.Errorf(codes.InvalidArgument, "type URL is required for KDS")
-			}
-
-			if s.callbacks != nil {
-				if err := s.callbacks.OnStreamRequest(streamID, req); err != nil {
-					return err
-				}
-			}
-
-			requestResourceType := model.ResourceType(req.TypeUrl)
-			// cancel existing watches to (re-)request a newer version
-			switch {
-			case requestResourceType == mesh_core.MeshType && (values.meshesNonce == "" || values.meshesNonce == nonce):
-				if values.meshesCancel != nil {
-					values.meshesCancel()
-				}
-				values.meshes, values.meshesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.DataplaneType && (values.ingressesNonce == "" || values.ingressesNonce == nonce):
-				if values.ingressesCancel != nil {
-					values.ingressesCancel()
-				}
-				values.ingresses, values.ingressesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.DataplaneInsightType && (values.dataplaneInsightsNonce == "" || values.dataplaneInsightsNonce == nonce):
-				if values.dataplaneInsightsCancel != nil {
-					values.dataplaneInsightsCancel()
-				}
-				values.dataplaneInsights, values.dataplaneInsightsCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.ExternalServiceType && (values.externalServiceNonce == "" || values.externalServiceNonce == nonce):
-				if values.externalServicesCancel != nil {
-					values.externalServicesCancel()
-				}
-				values.externalServices, values.externalServicesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.CircuitBreakerType && (values.circuitBreakersNonce == "" || values.circuitBreakersNonce == nonce):
-				if values.circuitBreakersCancel != nil {
-					values.circuitBreakersCancel()
-				}
-				values.circuitBreakers, values.circuitBreakersCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.FaultInjectionType && (values.faultInjectionsNonce == "" || values.faultInjectionsNonce == nonce):
-				if values.faultInjectionsCancel != nil {
-					values.faultInjectionsCancel()
-				}
-				values.faultInjections, values.faultInjectionsCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.HealthCheckType && (values.healthChecksNonce == "" || values.healthChecksNonce == nonce):
-				if values.healthChecksCancel != nil {
-					values.healthChecksCancel()
-				}
-				values.healthChecks, values.healthChecksCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.TrafficLogType && (values.trafficLogsNonce == "" || values.trafficLogsNonce == nonce):
-				if values.trafficLogsCancel != nil {
-					values.trafficLogsCancel()
-				}
-				values.trafficLogs, values.trafficLogsCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.TrafficPermissionType && (values.trafficPermissionsNonce == "" || values.trafficPermissionsNonce == nonce):
-				if values.trafficPermissionsCancel != nil {
-					values.trafficPermissionsCancel()
-				}
-				values.trafficPermissions, values.trafficPermissionsCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.TrafficRouteType && (values.trafficRoutesNonce == "" || values.trafficRoutesNonce == nonce):
-				if values.trafficRoutesCancel != nil {
-					values.trafficRoutesCancel()
-				}
-				values.trafficRoutes, values.trafficRoutesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.TrafficTraceType && (values.trafficTracesNonce == "" || values.trafficTracesNonce == nonce):
-				if values.trafficTracesCancel != nil {
-					values.trafficTracesCancel()
-				}
-				values.trafficTraces, values.trafficTracesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == mesh_core.ProxyTemplateType && (values.proxyTemplatesNonce == "" || values.proxyTemplatesNonce == nonce):
-				if values.proxyTemplatesCancel != nil {
-					values.proxyTemplatesCancel()
-				}
-				values.proxyTemplates, values.proxyTemplatesCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == system.SecretType && (values.secretsNonce == "" || values.secretsNonce == nonce):
-				if values.secretsCancel != nil {
-					values.secretsCancel()
-				}
-				values.secrets, values.secretsCancel = s.cache.CreateWatch(*req)
-			case requestResourceType == system.ConfigType && (values.configNonce == "" || values.configNonce == nonce):
-				if values.configCancel != nil {
-					values.configCancel()
-				}
-				values.config, values.configCancel = s.cache.CreateWatch(*req)
-			}
-		}
-	}
+type server struct {
+	sotw.Server
 }
 
-// handler converts a blocking read call to channels and initiates stream processing
-func (s *server) handler(stream stream) error {
-	// a channel for receiving incoming requests
-	reqCh := make(chan *envoy.DiscoveryRequest)
-	reqStop := int32(0)
-	go func() {
-		for {
-			req, err := stream.Recv()
-			if atomic.LoadInt32(&reqStop) != 0 {
-				return
-			}
-			if err != nil {
-				close(reqCh)
-				return
-			}
-			reqCh <- req
-		}
-	}()
-
-	err := s.process(stream, reqCh)
-
-	atomic.StoreInt32(&reqStop, 1)
-
-	return err
+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.handler(stream)
-}
-
-func (s *server) DeltaKumaResources(_ mesh_proto.KumaDiscoveryService_DeltaKumaResourcesServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) FetchKumaResources(ctx context.Context, req *envoy.DiscoveryRequest) (*envoy.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	return s.Fetch(ctx, req)
+	return s.StreamHandler(stream, "")
 }
 
-// Fetch is the universal fetch method.
-func (s *server) Fetch(ctx context.Context, req *envoy.DiscoveryRequest) (*envoy.DiscoveryResponse, error) {
-	if s.callbacks != nil {
-		if err := s.callbacks.OnFetchRequest(ctx, req); err != nil {
-			return nil, err
-		}
-	}
-	resp, err := s.cache.Fetch(ctx, *req)
-	if err != nil {
-		return nil, err
-	}
-	out, err := createResponse(resp, req.TypeUrl, s.clusterID)
-	if s.callbacks != nil {
-		s.callbacks.OnFetchResponse(req, out)
-	}
-	return out, err
+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 a123aae2c..6d0e5db48 100644
--- a/pkg/kds/server/status_tracker.go
+++ b/pkg/kds/server/status_tracker.go
@@ -7,6 +7,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/kds/util"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 
 	"github.com/go-logr/logr"
 
@@ -44,6 +45,7 @@ func NewStatusTracker(runtimeInfo core_runtime.RuntimeInfo,
 var _ StatusTracker = &statusTracker{}
 
 type statusTracker struct {
+	util_xds.NoopCallbacks
 	runtimeInfo      core_runtime.RuntimeInfo
 	createStatusSink ZoneInsightSinkFactoryFunc
 	mu               sync.RWMutex // protects access to the fields below
@@ -157,15 +159,6 @@ func (c *statusTracker) OnStreamResponse(streamID int64, req *envoy.DiscoveryReq
 	c.log.V(1).Info("OnStreamResponse", "streamid", streamID, "request", req, "response", resp, "subscription", subscription)
 }
 
-// OnFetchRequest is called for each Fetch request. Returning an error will end processing of the
-// request and respond with an error.
-func (c *statusTracker) OnFetchRequest(context.Context, *envoy.DiscoveryRequest) error {
-	return nil
-}
-
-// OnFetchResponse is called immediately prior to sending a response.
-func (c *statusTracker) OnFetchResponse(*envoy.DiscoveryRequest, *envoy.DiscoveryResponse) {}
-
 func (c *statusTracker) GetStatusAccessor(streamID int64) (StatusAccessor, bool) {
 	state, ok := c.streams[streamID]
 	return state, ok
diff --git a/pkg/kds/server/type_adjust_callbacks.go b/pkg/kds/server/type_adjust_callbacks.go
new file mode 100644
index 000000000..a88a60055
--- /dev/null
+++ b/pkg/kds/server/type_adjust_callbacks.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+
+	"github.com/kumahq/kuma/pkg/kds"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+)
+
+// We are using go-control-plane's server and cache for KDS exchange.
+// We are setting TypeURL for DiscoveryRequest/DiscoveryResponse for our resource name like "TrafficRoute" / "Mesh" etc.
+// but the actual resource which we are sending is kuma.mesh.v1alpha1.KumaResource
+//
+// The function which is marshalling DiscoveryResponse
+// func (r *RawResponse) GetDiscoveryResponse() (*discovery.DiscoveryResponse, error)
+// 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.NoopCallbacks
+}
+
+func (c *typeAdjustCallbacks) OnStreamResponse(streamID int64, req *envoy_api.DiscoveryRequest, resp *envoy_api.DiscoveryResponse) {
+	for _, res := range resp.Resources {
+		res.TypeUrl = kds.KumaResource
+	}
+}
diff --git a/pkg/mads/server/mads.go b/pkg/mads/server/mads.go
index 7c13538be..cac701a0a 100644
--- a/pkg/mads/server/mads.go
+++ b/pkg/mads/server/mads.go
@@ -2,258 +2,39 @@ package server
 
 import (
 	"context"
-	"strconv"
-	"sync/atomic"
 
-	"github.com/go-logr/logr"
-	"github.com/pkg/errors"
-
-	"github.com/golang/protobuf/proto"
-	any "github.com/golang/protobuf/ptypes/any"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
+	envoy_api "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"
 
 	observability_proto "github.com/kumahq/kuma/api/observability/v1alpha1"
 	"github.com/kumahq/kuma/pkg/mads"
-
-	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	envoy_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
 )
 
 type Server interface {
 	observability_proto.MonitoringAssignmentDiscoveryServiceServer
 }
 
-func NewServer(config envoy_cache.Cache, callbacks envoy_server.Callbacks, log logr.Logger) Server {
-	return &server{cache: config, callbacks: callbacks, log: log}
-}
-
-// server is a simplified version of the original XDS server at
-// https://github.com/envoyproxy/go-control-plane/blob/master/pkg/server/server.go
-type server struct {
-	cache     envoy_cache.Cache
-	callbacks envoy_server.Callbacks
-
-	// streamCount for counting bi-di streams
-	streamCount int64
-
-	log logr.Logger
-}
-
-type stream interface {
-	grpc.ServerStream
-
-	Send(*envoy.DiscoveryResponse) error
-	Recv() (*envoy.DiscoveryRequest, error)
-}
-
-// watches for all xDS resource types
-type watches struct {
-	assignments chan envoy_cache.Response
-
-	assignmentsCancel func()
-
-	assignmentsNonce string
-}
-
-// Cancel all watches
-func (values watches) Cancel() {
-	if values.assignmentsCancel != nil {
-		values.assignmentsCancel()
-	}
-}
-
-func createResponse(resp *envoy_cache.Response, typeURL string) (*envoy.DiscoveryResponse, error) {
-	if resp == nil {
-		return nil, errors.New("missing response")
-	}
-	resources := make([]*any.Any, len(resp.Resources))
-	for i := 0; i < len(resp.Resources); i++ {
-		// Envoy relies on serialized protobuf bytes for detecting changes to the resources.
-		// This requires deterministic serialization.
-		b := proto.NewBuffer(nil)
-		b.SetDeterministic(true)
-		err := b.Marshal(resp.Resources[i])
-		if err != nil {
-			return nil, err
-		}
-		resources[i] = &any.Any{
-			TypeUrl: typeURL,
-			Value:   b.Bytes(),
-		}
-	}
-	out := &envoy.DiscoveryResponse{
-		VersionInfo: resp.Version,
-		Resources:   resources,
-		TypeUrl:     typeURL,
-	}
-	return out, nil
+func NewServer(config envoy_cache.Cache, callbacks envoy_server.Callbacks) Server {
+	sotwServer := sotw.NewServer(context.Background(), config, callbacks)
+	return &server{sotwServer}
 }
 
-// process handles a bi-di stream request
-func (s *server) process(stream stream, reqCh <-chan *envoy.DiscoveryRequest, defaultTypeURL string) (err error) {
-	// increment stream count
-	streamID := atomic.AddInt64(&s.streamCount, 1)
+var _ Server = &server{}
 
-	log := s.log.WithValues("streamID", streamID)
-	defer func() {
-		if err != nil {
-			log.Error(err, "xDS stream terminated with an error")
-		}
-	}()
-
-	// unique nonce generator for req-resp pairs per xDS stream; the server
-	// ignores stale nonces. nonce is only modified within send() function.
-	var streamNonce int64
-
-	// a collection of watches per request type
-	var values watches
-	defer func() {
-		values.Cancel()
-		if s.callbacks != nil {
-			s.callbacks.OnStreamClosed(streamID)
-		}
-	}()
-
-	// sends a response by serializing to protobuf Any
-	send := func(resp envoy_cache.Response, typeURL string) (string, error) {
-		out, err := createResponse(&resp, typeURL)
-		if err != nil {
-			return "", err
-		}
-
-		// increment nonce
-		streamNonce++
-		out.Nonce = strconv.FormatInt(streamNonce, 10)
-		if s.callbacks != nil {
-			s.callbacks.OnStreamResponse(streamID, &resp.Request, out)
-		}
-
-		return out.Nonce, stream.Send(out)
-	}
-
-	if s.callbacks != nil {
-		if err := s.callbacks.OnStreamOpen(stream.Context(), streamID, defaultTypeURL); err != nil {
-			return err
-		}
-	}
-
-	// node may only be set on the first discovery request
-	var node = &envoy_core.Node{}
-
-	for {
-		select {
-		case resp, more := <-values.assignments:
-			if !more {
-				return status.Errorf(codes.Unavailable, "MonitoringAssignment watch failed")
-			}
-			nonce, err := send(resp, mads.MonitoringAssignmentType)
-			if err != nil {
-				return err
-			}
-			values.assignmentsNonce = nonce
-
-		case req, more := <-reqCh:
-			// input stream ended or errored out
-			if !more {
-				return nil
-			}
-			if req == nil {
-				return status.Errorf(codes.Unavailable, "empty request")
-			}
-
-			// node field in discovery request is delta-compressed
-			if req.Node != nil {
-				node = req.Node
-			} else {
-				req.Node = node
-			}
-
-			// nonces can be reused across streams; we verify nonce only if nonce is not initialized
-			nonce := req.GetResponseNonce()
-
-			// type URL is required for ADS but is implicit for xDS
-			if req.TypeUrl == "" {
-				req.TypeUrl = defaultTypeURL
-			}
-
-			if s.callbacks != nil {
-				if err := s.callbacks.OnStreamRequest(streamID, req); err != nil {
-					return err
-				}
-			}
-
-			// cancel existing watches to (re-)request a newer version
-			switch {
-			case req.TypeUrl == mads.MonitoringAssignmentType && (values.assignmentsNonce == "" || values.assignmentsNonce == nonce):
-				if values.assignmentsCancel != nil {
-					values.assignmentsCancel()
-				}
-				values.assignments, values.assignmentsCancel = s.cache.CreateWatch(*req)
-			}
-		}
-	}
+type server struct {
+	sotw.Server
 }
 
-// handler converts a blocking read call to channels and initiates stream processing
-func (s *server) handler(stream stream, typeURL string) error {
-	// a channel for receiving incoming requests
-	reqCh := make(chan *envoy.DiscoveryRequest)
-	reqStop := int32(0)
-	go func() {
-		for {
-			req, err := stream.Recv()
-			if atomic.LoadInt32(&reqStop) != 0 {
-				return
-			}
-			if err != nil {
-				close(reqCh)
-				return
-			}
-			reqCh <- req
-		}
-	}()
-
-	err := s.process(stream, reqCh, typeURL)
-
-	atomic.StoreInt32(&reqStop, 1)
-
-	return err
+func (s *server) DeltaMonitoringAssignments(stream observability_proto.MonitoringAssignmentDiscoveryService_DeltaMonitoringAssignmentsServer) error {
+	panic("not implemented") // we don't use delta on MADS for now
 }
 
 func (s *server) StreamMonitoringAssignments(stream observability_proto.MonitoringAssignmentDiscoveryService_StreamMonitoringAssignmentsServer) error {
-	return s.handler(stream, mads.MonitoringAssignmentType)
-}
-
-func (s *server) DeltaMonitoringAssignments(_ observability_proto.MonitoringAssignmentDiscoveryService_DeltaMonitoringAssignmentsServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) FetchMonitoringAssignments(ctx context.Context, req *envoy.DiscoveryRequest) (*envoy.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = mads.MonitoringAssignmentType
-	return s.Fetch(ctx, req)
+	return s.StreamHandler(stream, mads.MonitoringAssignmentType)
 }
 
-// Fetch is the universal fetch method.
-func (s *server) Fetch(ctx context.Context, req *envoy.DiscoveryRequest) (*envoy.DiscoveryResponse, error) {
-	if s.callbacks != nil {
-		if err := s.callbacks.OnFetchRequest(ctx, req); err != nil {
-			return nil, err
-		}
-	}
-	resp, err := s.cache.Fetch(ctx, *req)
-	if err != nil {
-		return nil, err
-	}
-	out, err := createResponse(resp, req.TypeUrl)
-	if s.callbacks != nil {
-		s.callbacks.OnFetchResponse(req, out)
-	}
-	return out, err
+func (s *server) FetchMonitoringAssignments(ctx context.Context, request *envoy_api.DiscoveryRequest) (*envoy_api.DiscoveryResponse, error) {
+	panic("not implemented") // we don't need Fetch operation on MADS for now
 }
diff --git a/pkg/mads/server/server.go b/pkg/mads/server/server.go
index b4978db84..d6d236ad6 100644
--- a/pkg/mads/server/server.go
+++ b/pkg/mads/server/server.go
@@ -21,7 +21,7 @@ func SetupServer(rt core_runtime.Runtime) error {
 		util_xds.LoggingCallbacks{Log: madsServerLog},
 		syncTracker,
 	}
-	srv := NewServer(cache, callbacks, madsServerLog)
+	srv := NewServer(cache, callbacks)
 	return rt.Add(
 		&grpcServer{
 			server:  srv,
diff --git a/pkg/plugins/resources/k8s/native/go.sum b/pkg/plugins/resources/k8s/native/go.sum
index 2d657754f..c8a907ab5 100644
--- a/pkg/plugins/resources/k8s/native/go.sum
+++ b/pkg/plugins/resources/k8s/native/go.sum
@@ -1,5 +1,6 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
@@ -25,13 +26,16 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@@ -45,6 +49,7 @@ github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfc
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@@ -60,8 +65,10 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT
 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 v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s=
+github.com/envoyproxy/go-control-plane v0.9.8 h1:bbmjRkjmP0ZggMoahdNMmJFFnK7v5H+/j5niP5QH6bg=
+github.com/envoyproxy/go-control-plane v0.9.8/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9 h1:mU3oOAYTK9ZRKMj//H1EWk1vWbkYeWKBPYKMnIwcs/A=
 github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200311152155-ab56c3dd1cf9/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -69,14 +76,18 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo
 github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
 github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -124,9 +135,11 @@ github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85n
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -148,16 +161,20 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
 github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -168,6 +185,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -188,9 +206,11 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -202,12 +222,15 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -235,14 +258,18 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -263,6 +290,7 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -271,6 +299,8 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -285,8 +315,11 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
 go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -325,6 +358,7 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARV
 golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -363,6 +397,7 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -378,22 +413,27 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
 golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f h1:2wh8dWY8959cBGQvk1RD+/eQBgRYYDaZ+hT0/zsARoA=
 google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -406,13 +446,16 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -443,9 +486,11 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
 k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+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.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
 k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
 k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
@@ -454,6 +499,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT
 sigs.k8s.io/controller-runtime v0.6.1 h1:LcK2+nk0kmaOnKGN+vBcWHqY5WDJNJNB/c5pW+sU8fc=
 sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/pkg/util/xds/cache.go b/pkg/util/xds/cache.go
index 0e5e7e2b5..7fbf4ee36 100644
--- a/pkg/util/xds/cache.go
+++ b/pkg/util/xds/cache.go
@@ -99,7 +99,15 @@ type SnapshotCache interface {
 	GetStatusKeys() []string
 }
 
+// The part below is a exact copy of snapshotCache and statusInfo of go-control-plane with exception marked with `Kuma modification start`
+
 type snapshotCache struct {
+	// watchCount is an atomic counter incremented for each watch. This needs to
+	// be the first field in the struct to guarantee that it is 64-bit aligned,
+	// which is a requirement for atomic operations on 64-bit operands to work on
+	// 32-bit machines.
+	watchCount int64
+
 	log envoy_log.Logger
 
 	// ads flag to hold responses until all resources are named
@@ -114,9 +122,6 @@ type snapshotCache struct {
 	// hash is the hashing function for Envoy nodes
 	hash envoy_cache.NodeHash
 
-	// watchCount is an atomic counter incremented for each watch
-	watchCount int64
-
 	mu sync.RWMutex
 }
 
@@ -156,7 +161,7 @@ func (cache *snapshotCache) SetSnapshot(node string, snapshot Snapshot) error {
 			version := snapshot.GetVersion(watch.Request.TypeUrl)
 			if version != watch.Request.VersionInfo {
 				if cache.log != nil {
-					cache.log.Infof("respond open watch %d%v with new version %q", id, watch.Request.ResourceNames, version)
+					cache.log.Debugf("respond open watch %d%v with new version %q", id, watch.Request.ResourceNames, version)
 				}
 				cache.respond(watch.Request, watch.Response, snapshot.GetResources(watch.Request.TypeUrl), version)
 
@@ -211,7 +216,7 @@ func superset(names map[string]bool, resources map[string]envoy_types.Resource)
 }
 
 // CreateWatch returns a watch for an xDS request.
-func (cache *snapshotCache) CreateWatch(request envoy_cache.Request) (chan envoy_cache.Response, func()) {
+func (cache *snapshotCache) CreateWatch(request *envoy_cache.Request) (chan envoy_cache.Response, func()) {
 	nodeID := cache.hash.ID(request.Node)
 
 	cache.mu.Lock()
@@ -232,16 +237,18 @@ func (cache *snapshotCache) CreateWatch(request envoy_cache.Request) (chan envoy
 	value := make(chan envoy_cache.Response, 1)
 
 	snapshot, exists := cache.snapshots[nodeID]
+	// Kuma modification start (we use interface and snapshot can be nil, in the original code it's a struct so it's never nil)
 	version := ""
 	if exists {
 		version = snapshot.GetVersion(request.TypeUrl)
 	}
+	// Kuma modification end
 
 	// if the requested version is up-to-date or missing a response, leave an open watch
 	if !exists || request.VersionInfo == version {
 		watchID := cache.nextWatchID()
 		if cache.log != nil {
-			cache.log.Infof("open watch %d for %s%v from nodeID %q, version %q", watchID,
+			cache.log.Debugf("open watch %d for %s%v from nodeID %q, version %q", watchID,
 				request.TypeUrl, request.ResourceNames, nodeID, request.VersionInfo)
 		}
 		info.mu.Lock()
@@ -276,26 +283,26 @@ func (cache *snapshotCache) cancelWatch(nodeID string, watchID int64) func() {
 
 // Respond to a watch with the snapshot value. The value channel should have capacity not to block.
 // TODO(kuat) do not respond always, see issue https://github.com/envoyproxy/go-control-plane/issues/46
-func (cache *snapshotCache) respond(request envoy_cache.Request, value chan envoy_cache.Response, resources map[string]envoy_types.Resource, version string) {
+func (cache *snapshotCache) respond(request *envoy_cache.Request, value chan envoy_cache.Response, resources map[string]envoy_types.Resource, version string) {
 	// for ADS, the request names must match the snapshot names
 	// if they do not, then the watch is never responded, and it is expected that envoy makes another request
 	if len(request.ResourceNames) != 0 && cache.ads {
 		if err := superset(nameSet(request.ResourceNames), resources); err != nil {
 			if cache.log != nil {
-				cache.log.Infof("ADS mode: not responding to request: %v", err)
+				cache.log.Debugf("ADS mode: not responding to request: %v", err)
 			}
 			return
 		}
 	}
 	if cache.log != nil {
-		cache.log.Infof("respond %s%v version %q with version %q",
+		cache.log.Debugf("respond %s%v version %q with version %q",
 			request.TypeUrl, request.ResourceNames, request.VersionInfo, version)
 	}
 
-	value <- createResponse(request, resources, version)
+	value <- cache.createResponse(request, resources, version)
 }
 
-func createResponse(request envoy_cache.Request, resources map[string]envoy_types.Resource, version string) envoy_cache.Response {
+func (cache *snapshotCache) createResponse(request *envoy_cache.Request, resources map[string]envoy_types.Resource, version string) envoy_cache.Response {
 	filtered := make([]envoy_types.Resource, 0, len(resources))
 
 	// Reply only with the requested resources. Envoy may ask each resource
@@ -314,7 +321,7 @@ func createResponse(request envoy_cache.Request, resources map[string]envoy_type
 		}
 	}
 
-	return envoy_cache.Response{
+	return &envoy_cache.RawResponse{
 		Request:   request,
 		Version:   version,
 		Resources: filtered,
@@ -323,7 +330,7 @@ func createResponse(request envoy_cache.Request, resources map[string]envoy_type
 
 // Fetch implements the cache fetch function.
 // Fetch is called on multiple streams, so responding to individual names with the same version works.
-func (cache *snapshotCache) Fetch(ctx context.Context, request envoy_cache.Request) (*envoy_cache.Response, error) {
+func (cache *snapshotCache) Fetch(ctx context.Context, request *envoy_cache.Request) (envoy_cache.Response, error) {
 	nodeID := cache.hash.ID(request.Node)
 
 	cache.mu.RLock()
@@ -334,12 +341,15 @@ func (cache *snapshotCache) Fetch(ctx context.Context, request envoy_cache.Reque
 		// It might be beneficial to hold the request since Envoy will re-attempt the refresh.
 		version := snapshot.GetVersion(request.TypeUrl)
 		if request.VersionInfo == version {
+			if cache.log != nil {
+				cache.log.Warnf("skip fetch: version up to date")
+			}
 			return nil, &envoy_types.SkipFetchError{}
 		}
 
 		resources := snapshot.GetResources(request.TypeUrl)
-		out := createResponse(request, resources, version)
-		return &out, nil
+		out := cache.createResponse(request, resources, version)
+		return out, nil
 	}
 
 	return nil, fmt.Errorf("missing snapshot for %q", nodeID)
@@ -352,6 +362,9 @@ func (cache *snapshotCache) GetStatusInfo(node string) envoy_cache.StatusInfo {
 
 	info, exists := cache.status[node]
 	if !exists {
+		if cache.log != nil {
+			cache.log.Warnf("node does not exist")
+		}
 		return nil
 	}
 
diff --git a/pkg/util/xds/control_plane_id_callbacks.go b/pkg/util/xds/control_plane_id_callbacks.go
new file mode 100644
index 000000000..46dcfbce2
--- /dev/null
+++ b/pkg/util/xds/control_plane_id_callbacks.go
@@ -0,0 +1,29 @@
+package xds
+
+import (
+	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+)
+
+// controlPlaneIdCallbacks adds Control Plane ID to the DiscoveryResponse
+type controlPlaneIdCallbacks struct {
+	NoopCallbacks
+	id string
+}
+
+var _ envoy_xds.Callbacks = &controlPlaneIdCallbacks{}
+
+func NewControlPlaneIdCallbacks(id string) envoy_xds.Callbacks {
+	return &controlPlaneIdCallbacks{
+		id: id,
+	}
+}
+
+func (c *controlPlaneIdCallbacks) OnStreamResponse(streamID int64, request *envoy_api.DiscoveryRequest, response *envoy_api.DiscoveryResponse) {
+	if c.id != "" {
+		response.ControlPlane = &envoy_core.ControlPlane{
+			Identifier: c.id,
+		}
+	}
+}
diff --git a/pkg/util/xds/noop_callbacks.go b/pkg/util/xds/noop_callbacks.go
new file mode 100644
index 000000000..3500ceedb
--- /dev/null
+++ b/pkg/util/xds/noop_callbacks.go
@@ -0,0 +1,34 @@
+package xds
+
+import (
+	"context"
+
+	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+)
+
+type NoopCallbacks struct {
+}
+
+func (c *NoopCallbacks) OnFetchRequest(context.Context, *envoy_api.DiscoveryRequest) error {
+	return nil
+}
+
+func (c *NoopCallbacks) OnFetchResponse(*envoy_api.DiscoveryRequest, *envoy_api.DiscoveryResponse) {
+}
+
+func (c *NoopCallbacks) OnStreamOpen(context.Context, int64, string) error {
+	return nil
+}
+
+func (c *NoopCallbacks) OnStreamClosed(int64) {
+}
+
+func (c *NoopCallbacks) OnStreamRequest(int64, *envoy_api.DiscoveryRequest) error {
+	return nil
+}
+
+func (c *NoopCallbacks) OnStreamResponse(int64, *envoy_api.DiscoveryRequest, *envoy_api.DiscoveryResponse) {
+}
+
+var _ envoy_xds.Callbacks = &NoopCallbacks{}
diff --git a/pkg/util/xds/stats_callbacks.go b/pkg/util/xds/stats_callbacks.go
index 29895cf5e..88627bd2d 100644
--- a/pkg/util/xds/stats_callbacks.go
+++ b/pkg/util/xds/stats_callbacks.go
@@ -10,6 +10,7 @@ import (
 )
 
 type StatsCallbacks struct {
+	NoopCallbacks
 	ResponsesSentMetric    *prometheus.CounterVec
 	RequestsReceivedMetric *prometheus.CounterVec
 	StreamsActive          int
@@ -78,11 +79,4 @@ func (s *StatsCallbacks) OnStreamResponse(stream int64, request *envoy_api.Disco
 	s.ResponsesSentMetric.WithLabelValues(response.TypeUrl).Inc()
 }
 
-func (s *StatsCallbacks) OnFetchRequest(ctx context.Context, request *envoy_api.DiscoveryRequest) error {
-	return nil
-}
-
-func (s *StatsCallbacks) OnFetchResponse(request *envoy_api.DiscoveryRequest, response *envoy_api.DiscoveryResponse) {
-}
-
 var _ envoy_xds.Callbacks = &StatsCallbacks{}
diff --git a/pkg/util/xds/watchdog_callbacks.go b/pkg/util/xds/watchdog_callbacks.go
index 98011eb5f..e581d48ef 100644
--- a/pkg/util/xds/watchdog_callbacks.go
+++ b/pkg/util/xds/watchdog_callbacks.go
@@ -21,6 +21,7 @@ func NewWatchdogCallbacks(newNodeWatchdog NewNodeWatchdogFunc) envoy_xds.Callbac
 }
 
 type watchdogCallbacks struct {
+	NoopCallbacks
 	newNodeWatchdog NewNodeWatchdogFunc
 
 	mu      sync.RWMutex // protects access to the fields below
@@ -91,17 +92,3 @@ func (cb *watchdogCallbacks) OnStreamRequest(streamID int64, req *envoy.Discover
 	}
 	return nil
 }
-
-// OnStreamResponse is called immediately prior to sending a response on a stream.
-func (cb *watchdogCallbacks) OnStreamResponse(streamID int64, req *envoy.DiscoveryRequest, resp *envoy.DiscoveryResponse) {
-}
-
-// OnFetchRequest is called for each Fetch request. Returning an error will end processing of the
-// request and respond with an error.
-func (cb *watchdogCallbacks) OnFetchRequest(context.Context, *envoy.DiscoveryRequest) error {
-	return nil
-}
-
-// OnFetchResponse is called immediately prior to sending a response.
-func (cb *watchdogCallbacks) OnFetchResponse(*envoy.DiscoveryRequest, *envoy.DiscoveryResponse) {
-}
diff --git a/pkg/xds/envoy/imports.go b/pkg/xds/envoy/imports.go
index 59a3151b7..04f150fab 100644
--- a/pkg/xds/envoy/imports.go
+++ b/pkg/xds/envoy/imports.go
@@ -5,6 +5,7 @@ package envoy
 import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/admin/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/annotations"
 	_ "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
@@ -16,15 +17,21 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/aggregate/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/dynamic_forward_proxy/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/redis"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/common/dynamic_forward_proxy/v2alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/common/matcher/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/common/matcher/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/common/tap/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/core/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/dubbo/router/v2alpha1"
@@ -83,21 +90,23 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/zookeeper_proxy/v1alpha1"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/rate_limit/v2alpha1"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/router/v2alpha1"
-	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/udp/dns_filter/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/filter/udp/udp_proxy/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/health_checker/redis/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/listener/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/overload/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/overload/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/fixed_heap/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/injected_resource/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/retry/omit_canary_hosts/v2"
@@ -105,14 +114,16 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/retry/previous_hosts/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/retry/previous_priorities"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/route/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/tap/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/tap/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/alts/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/raw_buffer/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/tap/v2alpha"
-	_ "github.com/envoyproxy/go-control-plane/envoy/config/wasm/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v2alpha"
@@ -121,30 +132,43 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/core/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/dns/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/dns/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/data/dns/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/wasm/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/redis/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/tap/v3"
-	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filter/udp/dns_filter/v3alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/tap/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/decompressor/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/adaptive_concurrency/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/admission_control/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_lambda/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_request_signing/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cache/v3alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cache/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cdn_loop/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/decompressor/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamo/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_bridge/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_json_transcoder/v3"
@@ -152,17 +176,28 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gzip/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_to_metadata/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_to_metadata/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ip_tagging/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/kill_request/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v3alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/on_demand/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/original_src/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/squash/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_src/v3"
@@ -172,45 +207,92 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/direct_response/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/router/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/echo/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ext_authz/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ext_authz/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/kafka_broker/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/local_ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mongo_proxy/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mysql_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/postgres_proxy/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rocketmq_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rocketmq_proxy/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_cluster/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/zookeeper_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/dns_filter/v3alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/dns_filter/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/allow_listed_routes/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/previous_routes/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/safe_cross_scheme/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/network/socket_interface/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/omit_host_metadata/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/wasm/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/datadog/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/dynamic_ot/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/lightstep/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opencensus/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/skywalking/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/xray/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/zipkin/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/alts/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tap/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tap/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/generic/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/http/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/tcp/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/tcp/generic/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/extensions/watchdog/profile_action/v3alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v4alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3alpha"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/extension/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/health/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/health/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/route/v3"
@@ -218,16 +300,21 @@ import (
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/status/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/status/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/status/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/tap/v2alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/tap/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/tap/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/matcher"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v4alpha"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v2"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3"
 	_ "github.com/envoyproxy/go-control-plane/envoy/type/v3"
+	_ "github.com/envoyproxy/go-control-plane/envoy/watchdog/v3alpha"
 )
diff --git a/pkg/xds/envoy/listeners/tracing_configurer.go b/pkg/xds/envoy/listeners/tracing_configurer.go
index 61b269c7e..797051762 100644
--- a/pkg/xds/envoy/listeners/tracing_configurer.go
+++ b/pkg/xds/envoy/listeners/tracing_configurer.go
@@ -83,15 +83,11 @@ func zipkinConfig(cfgStr *structpb.Struct, backendName string) (*envoy_trace.Tra
 
 func apiVersion(zipkin *mesh_proto.ZipkinTracingBackendConfig, url *net_url.URL) envoy_trace.ZipkinConfig_CollectorEndpointVersion {
 	if zipkin.ApiVersion == "" { // try to infer it from the URL
-		if url.Path == "/api/v1/spans" {
-			return envoy_trace.ZipkinConfig_HTTP_JSON_V1
-		} else if url.Path == "/api/v2/spans" {
+		if url.Path == "/api/v2/spans" {
 			return envoy_trace.ZipkinConfig_HTTP_JSON
 		}
 	} else {
 		switch zipkin.ApiVersion {
-		case "httpJsonV1":
-			return envoy_trace.ZipkinConfig_HTTP_JSON_V1
 		case "httpJson":
 			return envoy_trace.ZipkinConfig_HTTP_JSON
 		case "httpProto":
diff --git a/pkg/xds/server/components.go b/pkg/xds/server/components.go
index f00c78bf1..e9bc12a54 100644
--- a/pkg/xds/server/components.go
+++ b/pkg/xds/server/components.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"time"
 
+	envoy_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
 	"google.golang.org/grpc"
 
 	dp_server "github.com/kumahq/kuma/pkg/config/dp-server"
@@ -92,6 +93,8 @@ func RegisterXDS(rt core_runtime.Runtime, server *grpc.Server) error {
 		return err
 	}
 
+	forcer := newResourceWarmingForcer(rt.XDS().Cache(), rt.XDS().Hasher())
+
 	statsCallbacks, err := util_xds.NewStatsCallbacks(rt.Metrics(), "xds")
 	if err != nil {
 		return err
@@ -104,9 +107,10 @@ func RegisterXDS(rt core_runtime.Runtime, server *grpc.Server) error {
 		metadataTracker,
 		lifecycle,
 		statusTracker,
+		forcer,
 	}
 
-	srv := NewServer(rt.XDS().Cache(), callbacks)
+	srv := envoy_server.NewServer(context.Background(), rt.XDS().Cache(), callbacks)
 
 	xdsServerLog.Info("registering Aggregated Discovery Service in Dataplane Server")
 	envoy_service_discovery_v2.RegisterAggregatedDiscoveryServiceServer(server, srv)
diff --git a/pkg/xds/server/dataplane_lifecycle.go b/pkg/xds/server/dataplane_lifecycle.go
index 7bb694ee6..d6616fbf4 100644
--- a/pkg/xds/server/dataplane_lifecycle.go
+++ b/pkg/xds/server/dataplane_lifecycle.go
@@ -13,6 +13,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	"github.com/kumahq/kuma/pkg/core/xds"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 )
 
 // DataplaneLifecycle is responsible for creating a deleting dataplanes that are passed through metadata
@@ -25,6 +26,7 @@ import (
 //
 // This flow is optional, you may still want to go with 1. an example of this is Kubernetes deployment.
 type DataplaneLifecycle struct {
+	util_xds.NoopCallbacks
 	resManager manager.ResourceManager
 	// createdDpForStream stores map from StreamID to created ResourceKey of Dataplane.
 	// we store nil values for streams without Dataplane in metadata to avoid accessing metadata with every DiscoveryRequest
@@ -41,10 +43,6 @@ func NewDataplaneLifecycle(resManager manager.ResourceManager) *DataplaneLifecyc
 	}
 }
 
-func (d *DataplaneLifecycle) OnStreamOpen(_ context.Context, _ int64, _ string) error {
-	return nil
-}
-
 func (d *DataplaneLifecycle) OnStreamClosed(streamID int64) {
 	d.Lock()
 	defer d.Unlock()
@@ -101,13 +99,3 @@ func (d *DataplaneLifecycle) registerDataplane(dp *core_mesh.DataplaneResource)
 func (d *DataplaneLifecycle) unregisterDataplane(key model.ResourceKey) error {
 	return d.resManager.Delete(context.Background(), core_mesh.NewDataplaneResource(), store.DeleteBy(key))
 }
-
-func (d *DataplaneLifecycle) OnStreamResponse(_ int64, _ *envoy_api_v2.DiscoveryRequest, _ *envoy_api_v2.DiscoveryResponse) {
-}
-
-func (d *DataplaneLifecycle) OnFetchRequest(_ context.Context, _ *envoy_api_v2.DiscoveryRequest) error {
-	return nil
-}
-
-func (d *DataplaneLifecycle) OnFetchResponse(request *envoy_api_v2.DiscoveryRequest, response *envoy_api_v2.DiscoveryResponse) {
-}
diff --git a/pkg/xds/server/dataplane_metadata_tracker.go b/pkg/xds/server/dataplane_metadata_tracker.go
index 889ce4014..b98c95543 100644
--- a/pkg/xds/server/dataplane_metadata_tracker.go
+++ b/pkg/xds/server/dataplane_metadata_tracker.go
@@ -1,16 +1,17 @@
 package server
 
 import (
-	"context"
 	"sync"
 
 	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 	go_cp_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
 
 	"github.com/kumahq/kuma/pkg/core/xds"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 )
 
 type DataplaneMetadataTracker struct {
+	util_xds.NoopCallbacks
 	mutex             sync.RWMutex
 	metadataForStream map[int64]*xds.DataplaneMetadata
 }
@@ -35,10 +36,6 @@ func (d *DataplaneMetadataTracker) Metadata(streamId int64) *xds.DataplaneMetada
 
 var _ go_cp_server.Callbacks = &DataplaneMetadataTracker{}
 
-func (d *DataplaneMetadataTracker) OnStreamOpen(context.Context, int64, string) error {
-	return nil
-}
-
 func (d *DataplaneMetadataTracker) OnStreamClosed(stream int64) {
 	d.mutex.Lock()
 	defer d.mutex.Unlock()
@@ -61,13 +58,3 @@ func (d *DataplaneMetadataTracker) OnStreamRequest(stream int64, req *envoy.Disc
 	d.metadataForStream[stream] = xds.DataplaneMetadataFromNode(req.Node)
 	return nil
 }
-
-func (d *DataplaneMetadataTracker) OnStreamResponse(int64, *envoy.DiscoveryRequest, *envoy.DiscoveryResponse) {
-}
-
-func (d *DataplaneMetadataTracker) OnFetchRequest(context.Context, *envoy.DiscoveryRequest) error {
-	return nil
-}
-
-func (d *DataplaneMetadataTracker) OnFetchResponse(*envoy.DiscoveryRequest, *envoy.DiscoveryResponse) {
-}
diff --git a/pkg/xds/server/dataplane_status_tracker.go b/pkg/xds/server/dataplane_status_tracker.go
index ba5867fa4..6222f67cf 100644
--- a/pkg/xds/server/dataplane_status_tracker.go
+++ b/pkg/xds/server/dataplane_status_tracker.go
@@ -17,6 +17,7 @@ import (
 	core_runtime "github.com/kumahq/kuma/pkg/core/runtime"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 )
 
 var (
@@ -48,6 +49,7 @@ func NewDataplaneStatusTracker(runtimeInfo core_runtime.RuntimeInfo,
 var _ DataplaneStatusTracker = &dataplaneStatusTracker{}
 
 type dataplaneStatusTracker struct {
+	util_xds.NoopCallbacks
 	runtimeInfo      core_runtime.RuntimeInfo
 	createStatusSink DataplaneInsightSinkFactoryFunc
 	mu               sync.RWMutex // protects access to the fields below
@@ -169,15 +171,6 @@ func (c *dataplaneStatusTracker) OnStreamResponse(streamID int64, req *envoy.Dis
 	xdsServerLog.V(1).Info("OnStreamResponse", "streamid", streamID, "request", req, "response", resp, "subscription", subscription)
 }
 
-// OnFetchRequest is called for each Fetch request. Returning an error will end processing of the
-// request and respond with an error.
-func (c *dataplaneStatusTracker) OnFetchRequest(context.Context, *envoy.DiscoveryRequest) error {
-	return nil
-}
-
-// OnFetchResponse is called immediately prior to sending a response.
-func (c *dataplaneStatusTracker) OnFetchResponse(*envoy.DiscoveryRequest, *envoy.DiscoveryResponse) {}
-
 func (c *dataplaneStatusTracker) GetStatusAccessor(streamID int64) (SubscriptionStatusAccessor, bool) {
 	state, ok := c.streams[streamID]
 	return state, ok
diff --git a/pkg/xds/server/resource_warming_forcer.go b/pkg/xds/server/resource_warming_forcer.go
new file mode 100644
index 000000000..85ec9fcc8
--- /dev/null
+++ b/pkg/xds/server/resource_warming_forcer.go
@@ -0,0 +1,127 @@
+package server
+
+import (
+	"sync"
+
+	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	"github.com/envoyproxy/go-control-plane/pkg/cache/types"
+	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
+	envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
+	envoy_xds "github.com/envoyproxy/go-control-plane/pkg/server/v2"
+	"github.com/pkg/errors"
+
+	"github.com/kumahq/kuma/pkg/core"
+	"github.com/kumahq/kuma/pkg/core/xds"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+)
+
+var warmingForcerLog = xdsServerLog.WithName("warming-forcer")
+
+// The problem
+//   When you send Cluster of type EDS to Envoy, it updates the config, but Cluster is marked as warming and is not used until you send EDS request.
+//   https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#xds-protocol-resource-warming
+//   The main problem is when you update the Cluster itself (for example enable mTLS or change a cluster property via ProxyTemplate)
+//   If you don't send EDS request after that (no endpoint has changed) then the cluster is stuck in warming state indefinitely.
+//
+// The solution
+//   The easiest solution would be to just set a new version of endpoints also when cluster changes (in pkg/xds/server/SnapshotReconciler) . The problem is that go-control-plane does not support resource ordering
+//   * https://github.com/envoyproxy/go-control-plane/issues/59
+//   * https://github.com/envoyproxy/go-control-plane/issues/218
+//   * https://github.com/envoyproxy/go-control-plane/issues/235
+//   Therefore even if we were to set a new version of EDS + CDS on one snapshot, there is no guarantee that EDS will be delivered after CDS.
+//
+//   The alternative solution is this based on a callbacks.
+//   Nonce is a sequence indicator of a sent DiscoveryResponse on a stream. We use ADS therefore every single DiscoveryResponse regardless of a type is sent with incremented nonce.
+//   Typical sequence of CDS + EDS looks like this:
+//   1) Envoy sends DiscoveryRequest [type=CDS, version="", nonce=""] // ask for the clusters
+//   2) Kuma sends DiscoveryResponse [type=CDS, version="UUID-1", nonce="1"] // response with clusters
+//   3) Envoy sends DiscoveryRequest [type=EDS, version="", nonce=""] // ask for the endpoints
+//   4) Envoy sends DiscoveryRequest [type=CDS, version="UUID-1", nonce="1"] // confirmations that it received clusters (ACK)
+//   5) Kuma sends DiscoveryResponse [type=EDS, version="UUID-2", nonce="2"] // response with endpoints
+//   6) Envoy sends DiscoveryRequest [type=EDS, version="UUID-2", nonce="2"] // confirmations that it received endpoints (ACK)
+//
+//   Then if we send a Cluster update (continuing the flow above)
+//   7) Kuma sends DiscoveryResponse [type=CDS, version="UUID-2", nonce="3"] // response with cluster update
+//   8) Envoy sends DiscoveryRequest [type=CDS, version="UUID-2", nonce="3"] // ACK
+//   9) Envoy sends DiscoveryRequest [type=EDS, version="UUID-2", nonce="2"] // Envoy sends a request which looks like the second ACK for the previous endpoints
+//
+//   Updated Cluster is now in warming state until we send DiscoveryResponse with EDS.
+//   We could sent the same DiscoveryResponse again: DiscoveryRequest [type=EDS, version="UUID-2", nonce="2"], but there is no API in go-control-plane to do it.
+//   Instead we set a new version of the Endpoints to force a new EDS exchange:
+//   10) Kuma sends DiscoveryResponse [type=EDS, version="UUID-3", nonce="3"] // triggered because we set snapshot with a new version
+//   11) Envoy sends DiscoveryRequest [type=EDS, version="UUID-3", nonce="3"] // ACK
+//   After this exchange, cluster is now out of the warming state.
+//
+// The same problem is with Listeners and Routes (change of the Listener that uses RDS requires RDS DiscoveryResponse), but since we don't use RDS now, the implementation is for EDS only.
+// More reading of how Envoy is trying to solve it https://github.com/envoyproxy/envoy/issues/13009
+type resourceWarmingForcer struct {
+	util_xds.NoopCallbacks
+	cache  envoy_cache.SnapshotCache
+	hasher envoy_cache.NodeHash
+
+	sync.Mutex
+	lastEndpointNonces map[xds.StreamID]string
+	nodeIDs            map[xds.StreamID]string
+}
+
+func newResourceWarmingForcer(cache envoy_cache.SnapshotCache, hasher envoy_cache.NodeHash) *resourceWarmingForcer {
+	return &resourceWarmingForcer{
+		cache:              cache,
+		hasher:             hasher,
+		lastEndpointNonces: map[xds.StreamID]string{},
+		nodeIDs:            map[xds.StreamID]string{},
+	}
+}
+
+func (r *resourceWarmingForcer) OnStreamClosed(streamID int64) {
+	r.Lock()
+	defer r.Unlock()
+	delete(r.lastEndpointNonces, streamID)
+	delete(r.nodeIDs, streamID)
+}
+
+func (r *resourceWarmingForcer) OnStreamRequest(streamID xds.StreamID, request *envoy_api.DiscoveryRequest) error {
+	if request.TypeUrl != envoy_resource.EndpointType {
+		return nil // we force Cluster warming only on receiving the same EDS Discovery Request
+	}
+	if request.ResponseNonce == "" {
+		return nil // initial request, no need to force warming
+	}
+	if request.ErrorDetail != nil {
+		return nil // we only care about ACKs, otherwise we can get 2 Nonces with multiple NACKs
+	}
+
+	r.Lock()
+	lastEndpointNonce := r.lastEndpointNonces[streamID]
+	r.lastEndpointNonces[streamID] = request.ResponseNonce
+	nodeID := r.nodeIDs[streamID]
+	if nodeID == "" {
+		nodeID = r.hasher.ID(request.Node) // request.Node can be set only on first request therefore we need to save it
+		r.nodeIDs[streamID] = nodeID
+	}
+	r.Unlock()
+
+	if lastEndpointNonce == request.ResponseNonce {
+		warmingForcerLog.V(1).Info("received second Endpoint DiscoveryRequest with same Nonce. Forcing new version of Endpoints to warm the Cluster")
+		if err := r.forceNewEndpointsVersion(nodeID); err != nil {
+			warmingForcerLog.Error(err, "could not force cluster warming")
+		}
+	}
+	return nil
+}
+
+func (r *resourceWarmingForcer) forceNewEndpointsVersion(nodeID string) error {
+	snapshot, err := r.cache.GetSnapshot(nodeID)
+	if err != nil {
+		return nil // GetSnapshot returns an error if there is no snapshot. We don't need to force on a new snapshot
+	}
+	endpoints := snapshot.Resources[types.Endpoint]
+	endpoints.Version = core.NewUUID()
+	snapshot.Resources[types.Endpoint] = endpoints
+	if err := r.cache.SetSnapshot(nodeID, snapshot); err != nil {
+		return errors.Wrap(err, "could not set snapshot")
+	}
+	return nil
+}
+
+var _ envoy_xds.Callbacks = &resourceWarmingForcer{}
diff --git a/pkg/xds/server/server.go b/pkg/xds/server/server.go
deleted file mode 100644
index 88ab4b853..000000000
--- a/pkg/xds/server/server.go
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2018 Envoyproxy Authors
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-// Package server provides an implementation of a streaming xDS server.
-package server
-
-import (
-	"context"
-	"errors"
-	"strconv"
-	"sync/atomic"
-
-	"github.com/golang/protobuf/proto"
-	"github.com/golang/protobuf/ptypes/any"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
-	gcp_server "github.com/envoyproxy/go-control-plane/pkg/server/v2"
-)
-
-// NewServer creates handlers from a config watcher and callbacks.
-func NewServer(config envoy_cache.Cache, callbacks gcp_server.Callbacks) gcp_server.Server {
-	return &server{cache: config, callbacks: callbacks}
-}
-
-type server struct {
-	cache     envoy_cache.Cache
-	callbacks gcp_server.Callbacks
-
-	// streamCount for counting bi-di streams
-	streamCount int64
-}
-
-type stream interface {
-	grpc.ServerStream
-
-	Send(*v2.DiscoveryResponse) error
-	Recv() (*v2.DiscoveryRequest, error)
-}
-
-// watches for all xDS resource types
-type watches struct {
-	endpoints chan envoy_cache.Response
-	clusters  chan envoy_cache.Response
-	routes    chan envoy_cache.Response
-	listeners chan envoy_cache.Response
-	secrets   chan envoy_cache.Response
-	runtimes  chan envoy_cache.Response
-
-	endpointCancel func()
-	clusterCancel  func()
-	routeCancel    func()
-	listenerCancel func()
-	secretCancel   func()
-	runtimeCancel  func()
-
-	endpointNonce string
-	clusterNonce  string
-	routeNonce    string
-	listenerNonce string
-	secretNonce   string
-	runtimeNonce  string
-
-	endpointNonceAcked bool
-}
-
-// Cancel all watches
-func (values watches) Cancel() {
-	if values.endpointCancel != nil {
-		values.endpointCancel()
-	}
-	if values.clusterCancel != nil {
-		values.clusterCancel()
-	}
-	if values.routeCancel != nil {
-		values.routeCancel()
-	}
-	if values.listenerCancel != nil {
-		values.listenerCancel()
-	}
-	if values.secretCancel != nil {
-		values.secretCancel()
-	}
-	if values.runtimeCancel != nil {
-		values.runtimeCancel()
-	}
-}
-
-func createResponse(resp *envoy_cache.Response, typeURL string) (*v2.DiscoveryResponse, error) {
-	if resp == nil {
-		return nil, errors.New("missing response")
-	}
-	resources := make([]*any.Any, len(resp.Resources))
-	for i := 0; i < len(resp.Resources); i++ {
-		// Envoy relies on serialized protobuf bytes for detecting changes to the resources.
-		// This requires deterministic serialization.
-		b := proto.NewBuffer(nil)
-		b.SetDeterministic(true)
-		err := b.Marshal(resp.Resources[i])
-		if err != nil {
-			return nil, err
-		}
-		resources[i] = &any.Any{
-			TypeUrl: typeURL,
-			Value:   b.Bytes(),
-		}
-	}
-	out := &v2.DiscoveryResponse{
-		VersionInfo: resp.Version,
-		Resources:   resources,
-		TypeUrl:     typeURL,
-	}
-	return out, nil
-}
-
-// process handles a bi-di stream request
-func (s *server) process(stream stream, reqCh <-chan *v2.DiscoveryRequest, defaultTypeURL string) error {
-	// increment stream count
-	streamID := atomic.AddInt64(&s.streamCount, 1)
-
-	// unique nonce generator for req-resp pairs per xDS stream; the server
-	// ignores stale nonces. nonce is only modified within send() function.
-	var streamNonce int64
-
-	// a collection of watches per request type
-	var values watches
-	defer func() {
-		values.Cancel()
-		if s.callbacks != nil {
-			s.callbacks.OnStreamClosed(streamID)
-		}
-	}()
-
-	// sends a response by serializing to protobuf Any
-	send := func(resp envoy_cache.Response, typeURL string) (string, error) {
-		out, err := createResponse(&resp, typeURL)
-		if err != nil {
-			return "", err
-		}
-
-		// increment nonce
-		streamNonce = streamNonce + 1
-		out.Nonce = strconv.FormatInt(streamNonce, 10)
-		if s.callbacks != nil {
-			s.callbacks.OnStreamResponse(streamID, &resp.Request, out)
-		}
-		return out.Nonce, stream.Send(out)
-	}
-
-	if s.callbacks != nil {
-		if err := s.callbacks.OnStreamOpen(stream.Context(), streamID, defaultTypeURL); err != nil {
-			return err
-		}
-	}
-
-	peekResponse := func(req envoy_cache.Request) *envoy_cache.Response {
-		versionInfo := req.VersionInfo
-		req.VersionInfo = "" // trick cache into returning a response despite unchanged version
-		resp, err := s.cache.Fetch(stream.Context(), req)
-		if err != nil || resp == nil {
-			return nil
-		}
-		resp.Request.VersionInfo = versionInfo // restore the original value
-		return resp
-	}
-	scheduleResponse := func(resp envoy_cache.Response) (value chan envoy_cache.Response, cancel func()) {
-		value = make(chan envoy_cache.Response, 1)
-		value <- resp
-		cancel = func() {
-			close(value)
-
-			// consume channel
-			select {
-			case <-value:
-			default:
-			}
-		}
-		return
-	}
-
-	// node may only be set on the first discovery request
-	var node = &envoy_core.Node{}
-
-	for {
-		select {
-		// config watcher can send the requested resources types in any order
-		case resp, more := <-values.endpoints:
-			if !more {
-				return status.Errorf(codes.Unavailable, "endpoints watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.EndpointType)
-			if err != nil {
-				return err
-			}
-			values.endpointNonce = nonce
-			values.endpointNonceAcked = false
-
-		case resp, more := <-values.clusters:
-			if !more {
-				return status.Errorf(codes.Unavailable, "clusters watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.ClusterType)
-			if err != nil {
-				return err
-			}
-			values.clusterNonce = nonce
-
-		case resp, more := <-values.routes:
-			if !more {
-				return status.Errorf(codes.Unavailable, "routes watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.RouteType)
-			if err != nil {
-				return err
-			}
-			values.routeNonce = nonce
-
-		case resp, more := <-values.listeners:
-			if !more {
-				return status.Errorf(codes.Unavailable, "listeners watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.ListenerType)
-			if err != nil {
-				return err
-			}
-			values.listenerNonce = nonce
-
-		case resp, more := <-values.secrets:
-			if !more {
-				return status.Errorf(codes.Unavailable, "secrets watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.SecretType)
-			if err != nil {
-				return err
-			}
-			values.secretNonce = nonce
-
-		case resp, more := <-values.runtimes:
-			if !more {
-				return status.Errorf(codes.Unavailable, "runtimes watch failed")
-			}
-			nonce, err := send(resp, envoy_resource.RuntimeType)
-			if err != nil {
-				return err
-			}
-			values.runtimeNonce = nonce
-
-		case req, more := <-reqCh:
-			// input stream ended or errored out
-			if !more {
-				return nil
-			}
-			if req == nil {
-				return status.Errorf(codes.Unavailable, "empty request")
-			}
-
-			// node field in discovery request is delta-compressed
-			if req.Node != nil {
-				node = req.Node
-			} else {
-				req.Node = node
-			}
-
-			// nonces can be reused across streams; we verify nonce only if nonce is not initialized
-			nonce := req.GetResponseNonce()
-
-			// type URL is required for ADS but is implicit for xDS
-			if defaultTypeURL == envoy_resource.AnyType {
-				if req.TypeUrl == "" {
-					return status.Errorf(codes.InvalidArgument, "type URL is required for ADS")
-				}
-			} else if req.TypeUrl == "" {
-				req.TypeUrl = defaultTypeURL
-			}
-
-			if s.callbacks != nil {
-				if err := s.callbacks.OnStreamRequest(streamID, req); err != nil {
-					return err
-				}
-			}
-
-			// cancel existing watches to (re-)request a newer version
-			switch {
-			case req.TypeUrl == envoy_resource.EndpointType && (values.endpointNonce == "" || values.endpointNonce == nonce):
-				// If Envoy uses the same Nonce for the second time, it probably means that
-				// a Cluster has been created or updated and goes through the warming stage.
-				// In that case we must respond even if EDS configuration hasn't changed.
-				var resp *envoy_cache.Response
-				if values.endpointNonceAcked {
-					resp = peekResponse(*req)
-				}
-				values.endpointNonceAcked = values.endpointNonce != "" && values.endpointNonce == nonce
-				if values.endpointCancel != nil {
-					values.endpointCancel()
-				}
-				if resp != nil {
-					values.endpoints, values.endpointCancel = scheduleResponse(*resp)
-				} else {
-					values.endpoints, values.endpointCancel = s.cache.CreateWatch(*req)
-				}
-			case req.TypeUrl == envoy_resource.ClusterType && (values.clusterNonce == "" || values.clusterNonce == nonce):
-				if values.clusterCancel != nil {
-					values.clusterCancel()
-				}
-				values.clusters, values.clusterCancel = s.cache.CreateWatch(*req)
-			case req.TypeUrl == envoy_resource.RouteType && (values.routeNonce == "" || values.routeNonce == nonce):
-				if values.routeCancel != nil {
-					values.routeCancel()
-				}
-				values.routes, values.routeCancel = s.cache.CreateWatch(*req)
-			case req.TypeUrl == envoy_resource.ListenerType && (values.listenerNonce == "" || values.listenerNonce == nonce):
-				if values.listenerCancel != nil {
-					values.listenerCancel()
-				}
-				values.listeners, values.listenerCancel = s.cache.CreateWatch(*req)
-			case req.TypeUrl == envoy_resource.SecretType && (values.secretNonce == "" || values.secretNonce == nonce):
-				if values.secretCancel != nil {
-					values.secretCancel()
-				}
-				values.secrets, values.secretCancel = s.cache.CreateWatch(*req)
-			case req.TypeUrl == envoy_resource.RuntimeType && (values.runtimeNonce == "" || values.runtimeNonce == nonce):
-				if values.runtimeCancel != nil {
-					values.runtimeCancel()
-				}
-				values.runtimes, values.runtimeCancel = s.cache.CreateWatch(*req)
-			}
-		}
-	}
-}
-
-// handler converts a blocking read call to channels and initiates stream processing
-func (s *server) handler(stream stream, typeURL string) error {
-	// a channel for receiving incoming requests
-	reqCh := make(chan *v2.DiscoveryRequest)
-	reqStop := int32(0)
-	go func() {
-		for {
-			req, err := stream.Recv()
-			if atomic.LoadInt32(&reqStop) != 0 {
-				return
-			}
-			if err != nil {
-				close(reqCh)
-				return
-			}
-			reqCh <- req
-		}
-	}()
-
-	err := s.process(stream, reqCh, typeURL)
-
-	// prevents writing to a closed channel if send failed on blocked recv
-	// TODO(kuat) figure out how to unblock recv through gRPC API
-	atomic.StoreInt32(&reqStop, 1)
-
-	return err
-}
-
-func (s *server) StreamAggregatedResources(stream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error {
-	return s.handler(stream, envoy_resource.AnyType)
-}
-
-func (s *server) StreamEndpoints(stream v2.EndpointDiscoveryService_StreamEndpointsServer) error {
-	return s.handler(stream, envoy_resource.EndpointType)
-}
-
-func (s *server) StreamClusters(stream v2.ClusterDiscoveryService_StreamClustersServer) error {
-	return s.handler(stream, envoy_resource.ClusterType)
-}
-
-func (s *server) StreamRoutes(stream v2.RouteDiscoveryService_StreamRoutesServer) error {
-	return s.handler(stream, envoy_resource.RouteType)
-}
-
-func (s *server) StreamListeners(stream v2.ListenerDiscoveryService_StreamListenersServer) error {
-	return s.handler(stream, envoy_resource.ListenerType)
-}
-
-func (s *server) StreamSecrets(stream discovery.SecretDiscoveryService_StreamSecretsServer) error {
-	return s.handler(stream, envoy_resource.SecretType)
-}
-
-func (s *server) StreamRuntime(stream discovery.RuntimeDiscoveryService_StreamRuntimeServer) error {
-	return s.handler(stream, envoy_resource.RuntimeType)
-}
-
-// Fetch is the universal fetch method.
-func (s *server) Fetch(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if s.callbacks != nil {
-		if err := s.callbacks.OnFetchRequest(ctx, req); err != nil {
-			return nil, err
-		}
-	}
-	resp, err := s.cache.Fetch(ctx, *req)
-	if err != nil {
-		return nil, err
-	}
-	out, err := createResponse(resp, req.TypeUrl)
-	if s.callbacks != nil {
-		s.callbacks.OnFetchResponse(req, out)
-	}
-	return out, err
-}
-
-func (s *server) FetchEndpoints(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.EndpointType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) FetchClusters(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.ClusterType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) FetchRoutes(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.RouteType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) FetchListeners(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.ListenerType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) FetchSecrets(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.SecretType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) FetchRuntime(ctx context.Context, req *v2.DiscoveryRequest) (*v2.DiscoveryResponse, error) {
-	if req == nil {
-		return nil, status.Errorf(codes.Unavailable, "empty request")
-	}
-	req.TypeUrl = envoy_resource.RuntimeType
-	return s.Fetch(ctx, req)
-}
-
-func (s *server) DeltaAggregatedResources(_ discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaEndpoints(_ v2.EndpointDiscoveryService_DeltaEndpointsServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaClusters(_ v2.ClusterDiscoveryService_DeltaClustersServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaRoutes(_ v2.RouteDiscoveryService_DeltaRoutesServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaListeners(_ v2.ListenerDiscoveryService_DeltaListenersServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaSecrets(_ discovery.SecretDiscoveryService_DeltaSecretsServer) error {
-	return errors.New("not implemented")
-}
-
-func (s *server) DeltaRuntime(_ discovery.RuntimeDiscoveryService_DeltaRuntimeServer) error {
-	return errors.New("not implemented")
-}


Test patch:
diff --git a/pkg/core/xds/metadata_test.go b/pkg/core/xds/metadata_test.go
index 81239e80a..801252307 100644
--- a/pkg/core/xds/metadata_test.go
+++ b/pkg/core/xds/metadata_test.go
@@ -2,32 +2,32 @@ package xds_test
 
 import (
 	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	pstruct "github.com/golang/protobuf/ptypes/struct"
 	. "github.com/onsi/ginkgo/extensions/table"
 	. "github.com/onsi/gomega"
+	pstruct "google.golang.org/protobuf/types/known/structpb"
 
 	"github.com/kumahq/kuma/pkg/core/xds"
 )
 
 type testCase struct {
-	node     envoy_core.Node
+	node     *envoy_core.Node
 	expected xds.DataplaneMetadata
 }
 
 var _ = DescribeTable("DataplaneMetadataFromNode",
 	func(given testCase) {
 		// when
-		metadata := xds.DataplaneMetadataFromNode(&given.node)
+		metadata := xds.DataplaneMetadataFromNode(given.node)
 
 		// then
 		Expect(*metadata).To(Equal(given.expected))
 	},
 	Entry("should parse metadata from empty node", testCase{
-		node:     envoy_core.Node{},
+		node:     &envoy_core.Node{},
 		expected: xds.DataplaneMetadata{},
 	}),
 	Entry("should parse metadata", testCase{
-		node: envoy_core.Node{
+		node: &envoy_core.Node{
 			Metadata: &pstruct.Struct{
 				Fields: map[string]*pstruct.Value{
 					"dataplaneTokenPath": &pstruct.Value{
diff --git a/pkg/util/xds/cache_test.go b/pkg/util/xds/cache_test.go
deleted file mode 100644
index e0921319c..000000000
--- a/pkg/util/xds/cache_test.go
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright 2018 Envoyproxy Authors
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-package xds_test
-
-import (
-	"context"
-	"fmt"
-	"reflect"
-	"testing"
-	"time"
-
-	. "github.com/kumahq/kuma/pkg/util/xds"
-
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
-	"github.com/envoyproxy/go-control-plane/pkg/test/resource/v2"
-)
-
-const (
-	clusterName  = "cluster0"
-	routeName    = "route0"
-	listenerName = "listener0"
-	runtimeName  = "runtime0"
-)
-
-var (
-	endpoint = resource.MakeEndpoint(clusterName, 8080)
-	cluster  = resource.MakeCluster(resource.Ads, clusterName)
-	route    = resource.MakeRoute(routeName, clusterName)
-	listener = resource.MakeHTTPListener(resource.Ads, listenerName, 80, routeName)
-	runtime  = resource.MakeRuntime(runtimeName)
-)
-
-type group struct{}
-
-const (
-	key = "node"
-)
-
-func (group) ID(node *core.Node) string {
-	if node != nil {
-		return node.Id
-	}
-	return key
-}
-
-type SampleSnapshot struct {
-	envoy_cache.Snapshot
-}
-
-// NewSampleSnapshot creates a snapshot from response types and a version.
-func NewSampleSnapshot(version string,
-	endpoints []envoy_types.Resource,
-	clusters []envoy_types.Resource,
-	routes []envoy_types.Resource,
-	listeners []envoy_types.Resource,
-	runtimes []envoy_types.Resource) *SampleSnapshot {
-	return &SampleSnapshot{
-		envoy_cache.NewSnapshot(version, endpoints, clusters, routes, listeners, runtimes),
-	}
-}
-
-// GetSupportedTypes returns a list of xDS types supported by this snapshot.
-func (s *SampleSnapshot) GetSupportedTypes() []string {
-	return []string{
-		envoy_resource.EndpointType,
-		envoy_resource.ClusterType,
-		envoy_resource.RouteType,
-		envoy_resource.ListenerType,
-		envoy_resource.SecretType,
-		envoy_resource.RuntimeType,
-	}
-}
-
-// WithVersion creates a new snapshot with a different version for a given resource type.
-func (s *SampleSnapshot) WithVersion(typ string, version string) Snapshot {
-	if s == nil {
-		return nil
-	}
-	if s.GetVersion(typ) == version {
-		return s
-	}
-	new := &SampleSnapshot{
-		Snapshot: envoy_cache.Snapshot{
-			Resources: s.Resources,
-		},
-	}
-
-	new.Resources[envoy_cache.GetResponseType(typ)].Version = version
-	return new
-}
-
-var (
-	version  = "x"
-	version2 = "y"
-
-	snapshot = NewSampleSnapshot(version,
-		[]envoy_types.Resource{endpoint},
-		[]envoy_types.Resource{cluster},
-		[]envoy_types.Resource{route},
-		[]envoy_types.Resource{listener},
-		[]envoy_types.Resource{runtime})
-
-	names = map[string][]string{
-		envoy_resource.EndpointType: {clusterName},
-		envoy_resource.ClusterType:  nil,
-		envoy_resource.RouteType:    {routeName},
-		envoy_resource.ListenerType: nil,
-		envoy_resource.RuntimeType:  nil,
-	}
-
-	testTypes = []string{
-		envoy_resource.EndpointType,
-		envoy_resource.ClusterType,
-		envoy_resource.RouteType,
-		envoy_resource.ListenerType,
-		envoy_resource.RuntimeType,
-	}
-)
-
-type logger struct {
-	t *testing.T
-}
-
-func (log logger) Debugf(format string, args ...interface{}) { log.t.Logf(format, args...) }
-func (log logger) Warnf(format string, args ...interface{})  { log.t.Logf(format, args...) }
-func (log logger) Infof(format string, args ...interface{})  { log.t.Logf(format, args...) }
-func (log logger) Errorf(format string, args ...interface{}) { log.t.Logf(format, args...) }
-
-func TestSnapshotCache(t *testing.T) {
-	c := NewSnapshotCache(true, group{}, logger{t: t})
-
-	if _, err := c.GetSnapshot(key); err == nil {
-		t.Errorf("unexpected snapshot found for key %q", key)
-	}
-
-	if err := c.SetSnapshot(key, snapshot); err != nil {
-		t.Fatal(err)
-	}
-
-	snap, err := c.GetSnapshot(key)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !reflect.DeepEqual(snap, snapshot) {
-		t.Errorf("expect snapshot: %v, got: %v", snapshot, snap)
-	}
-
-	// try to get endpoints with incorrect list of names
-	// should not receive response
-	value, _ := c.CreateWatch(v2.DiscoveryRequest{TypeUrl: envoy_resource.EndpointType, ResourceNames: []string{"none"}})
-	select {
-	case out := <-value:
-		t.Errorf("watch for endpoints and mismatched names => got %v, want none", out)
-	case <-time.After(time.Second / 4):
-	}
-
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			value, _ := c.CreateWatch(v2.DiscoveryRequest{TypeUrl: typ, ResourceNames: names[typ]})
-			select {
-			case out := <-value:
-				if out.Version != version {
-					t.Errorf("got version %q, want %q", out.Version, version)
-				}
-				if !reflect.DeepEqual(envoy_cache.IndexResourcesByName(out.Resources), snapshot.GetResources(typ)) {
-					t.Errorf("get resources %v, want %v", out.Resources, snapshot.GetResources(typ))
-				}
-			case <-time.After(time.Second):
-				t.Fatal("failed to receive snapshot response")
-			}
-		})
-	}
-}
-
-func TestSnapshotCacheFetch(t *testing.T) {
-	c := NewSnapshotCache(true, group{}, logger{t: t})
-	if err := c.SetSnapshot(key, snapshot); err != nil {
-		t.Fatal(err)
-	}
-
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			resp, err := c.Fetch(context.Background(), v2.DiscoveryRequest{TypeUrl: typ, ResourceNames: names[typ]})
-			if err != nil || resp == nil {
-				t.Fatal("unexpected error or null response")
-			}
-			if resp.Version != version {
-				t.Errorf("got version %q, want %q", resp.Version, version)
-			}
-		})
-	}
-
-	// no response for missing snapshot
-	if resp, err := c.Fetch(context.Background(),
-		v2.DiscoveryRequest{TypeUrl: envoy_resource.ClusterType, Node: &core.Node{Id: "oof"}}); resp != nil || err == nil {
-		t.Errorf("missing snapshot: response is not nil %v", resp)
-	}
-
-	// no response for latest version
-	if resp, err := c.Fetch(context.Background(),
-		v2.DiscoveryRequest{TypeUrl: envoy_resource.ClusterType, VersionInfo: version}); resp != nil || err == nil {
-		t.Errorf("latest version: response is not nil %v", resp)
-	}
-}
-
-func TestSnapshotCacheWatch(t *testing.T) {
-	c := NewSnapshotCache(true, group{}, logger{t: t})
-	watches := make(map[string]chan envoy_cache.Response)
-	for _, typ := range testTypes {
-		watches[typ], _ = c.CreateWatch(v2.DiscoveryRequest{TypeUrl: typ, ResourceNames: names[typ]})
-	}
-	if err := c.SetSnapshot(key, snapshot); err != nil {
-		t.Fatal(err)
-	}
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			select {
-			case out := <-watches[typ]:
-				if out.Version != version {
-					t.Errorf("got version %q, want %q", out.Version, version)
-				}
-				if !reflect.DeepEqual(envoy_cache.IndexResourcesByName(out.Resources), snapshot.GetResources(typ)) {
-					t.Errorf("get resources %v, want %v", out.Resources, snapshot.GetResources(typ))
-				}
-			case <-time.After(time.Second):
-				t.Fatal("failed to receive snapshot response")
-			}
-		})
-	}
-
-	// open new watches with the latest version
-	for _, typ := range testTypes {
-		watches[typ], _ = c.CreateWatch(v2.DiscoveryRequest{TypeUrl: typ, ResourceNames: names[typ], VersionInfo: version})
-	}
-	if count := c.GetStatusInfo(key).GetNumWatches(); count != len(testTypes) {
-		t.Errorf("watches should be created for the latest version: %d", count)
-	}
-
-	// set partially-versioned snapshot
-	snapshot2 := snapshot
-	snapshot2.Resources[envoy_types.Endpoint] = envoy_cache.NewResources(version2, []envoy_types.Resource{resource.MakeEndpoint(clusterName, 9090)})
-	if err := c.SetSnapshot(key, snapshot2); err != nil {
-		t.Fatal(err)
-	}
-	if count := c.GetStatusInfo(key).GetNumWatches(); count != len(testTypes)-1 {
-		t.Errorf("watches should be preserved for all but one: %d", count)
-	}
-
-	// validate response for endpoints
-	select {
-	case out := <-watches[envoy_resource.EndpointType]:
-		if out.Version != version2 {
-			t.Errorf("got version %q, want %q", out.Version, version2)
-		}
-		if !reflect.DeepEqual(envoy_cache.IndexResourcesByName(out.Resources), snapshot2.Resources[envoy_types.Endpoint].Items) {
-			t.Errorf("get resources %v, want %v", out.Resources, snapshot2.Resources[envoy_types.Endpoint].Items)
-		}
-	case <-time.After(time.Second):
-		t.Fatal("failed to receive snapshot response")
-	}
-}
-
-func TestConcurrentSetWatch(t *testing.T) {
-	c := NewSnapshotCache(false, group{}, logger{t: t})
-	for i := 0; i < 50; i++ {
-		func(i int) {
-			t.Run(fmt.Sprintf("worker%d", i), func(t *testing.T) {
-				t.Parallel()
-				id := fmt.Sprintf("%d", i%2)
-				var cancel func()
-				if i < 25 {
-					_ = c.SetSnapshot(id, &SampleSnapshot{envoy_cache.Snapshot{
-						Resources: [envoy_types.UnknownType]envoy_cache.Resources{
-							envoy_types.Endpoint: envoy_cache.NewResources(fmt.Sprintf("v%d", i), []envoy_types.Resource{resource.MakeEndpoint(clusterName, uint32(i))}),
-						},
-					}})
-				} else {
-					if cancel != nil {
-						cancel()
-					}
-					_, _ = c.CreateWatch(v2.DiscoveryRequest{
-						Node:    &core.Node{Id: id},
-						TypeUrl: envoy_resource.EndpointType,
-					})
-				}
-			})
-		}(i)
-	}
-}
-
-func TestSnapshotCacheWatchCancel(t *testing.T) {
-	c := NewSnapshotCache(true, group{}, logger{t: t})
-	for _, typ := range testTypes {
-		_, cancel := c.CreateWatch(v2.DiscoveryRequest{TypeUrl: typ, ResourceNames: names[typ]})
-		cancel()
-	}
-	// should be status info for the node
-	if keys := c.GetStatusKeys(); len(keys) == 0 {
-		t.Error("got 0, want status info for the node")
-	}
-
-	for _, typ := range testTypes {
-		if count := c.GetStatusInfo(key).GetNumWatches(); count > 0 {
-			t.Errorf("watches should be released for %s", typ)
-		}
-	}
-
-	if empty := c.GetStatusInfo("missing"); empty != nil {
-		t.Errorf("should not return a status for unknown key: got %#v", empty)
-	}
-}
-
-func TestSnapshotClear(t *testing.T) {
-	c := NewSnapshotCache(true, group{}, logger{t: t})
-	if err := c.SetSnapshot(key, snapshot); err != nil {
-		t.Fatal(err)
-	}
-	c.ClearSnapshot(key)
-	if empty := c.GetStatusInfo(key); empty != nil {
-		t.Errorf("cache should be cleared")
-	}
-	if keys := c.GetStatusKeys(); len(keys) != 0 {
-		t.Errorf("keys should be empty")
-	}
-}
diff --git a/pkg/util/xds/versioner_test.go b/pkg/util/xds/versioner_test.go
index 96481ff05..d03016be9 100644
--- a/pkg/util/xds/versioner_test.go
+++ b/pkg/util/xds/versioner_test.go
@@ -6,10 +6,12 @@ import (
 	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
 	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
+	envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/ginkgo/extensions/table"
 	. "github.com/onsi/gomega"
 
+	. "github.com/kumahq/kuma/pkg/test/matchers"
 	. "github.com/kumahq/kuma/pkg/util/xds"
 )
 
@@ -51,7 +53,14 @@ var _ = Describe("SnapshotAutoVersioner", func() {
 				// when
 				actual := versioner.Version(given.new, given.old)
 				// then
-				Expect(actual).To(Equal(given.expected))
+				for _, typ := range given.expected.GetSupportedTypes() {
+					actualRes := actual.GetResources(typ)
+					expectedRes := given.expected.GetResources(typ)
+					Expect(len(actualRes)).To(Equal(len(expectedRes)))
+					for name := range expectedRes {
+						Expect(actualRes[name]).To(MatchProto(expectedRes[name]))
+					}
+				}
 			},
 			Entry("when 'old' = `nil` and 'new' has empty version", testCase{
 				old: nil,
@@ -445,3 +454,49 @@ var _ = Describe("SnapshotAutoVersioner", func() {
 		)
 	})
 })
+
+type SampleSnapshot struct {
+	envoy_cache.Snapshot
+}
+
+// NewSampleSnapshot creates a snapshot from response types and a version.
+func NewSampleSnapshot(version string,
+	endpoints []envoy_types.Resource,
+	clusters []envoy_types.Resource,
+	routes []envoy_types.Resource,
+	listeners []envoy_types.Resource,
+	runtimes []envoy_types.Resource) *SampleSnapshot {
+	return &SampleSnapshot{
+		envoy_cache.NewSnapshot(version, endpoints, clusters, routes, listeners, runtimes, nil),
+	}
+}
+
+// GetSupportedTypes returns a list of xDS types supported by this snapshot.
+func (s *SampleSnapshot) GetSupportedTypes() []string {
+	return []string{
+		envoy_resource.EndpointType,
+		envoy_resource.ClusterType,
+		envoy_resource.RouteType,
+		envoy_resource.ListenerType,
+		envoy_resource.SecretType,
+		envoy_resource.RuntimeType,
+	}
+}
+
+// WithVersion creates a new snapshot with a different version for a given resource type.
+func (s *SampleSnapshot) WithVersion(typ string, version string) Snapshot {
+	if s == nil {
+		return nil
+	}
+	if s.GetVersion(typ) == version {
+		return s
+	}
+	new := &SampleSnapshot{
+		Snapshot: envoy_cache.Snapshot{
+			Resources: s.Resources,
+		},
+	}
+
+	new.Resources[envoy_cache.GetResponseType(typ)].Version = version
+	return new
+}
diff --git a/pkg/xds/server/server_suite_test.go b/pkg/xds/server/server_suite_test.go
deleted file mode 100644
index f7d4fed7f..000000000
--- a/pkg/xds/server/server_suite_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package server_test
-
-import (
-	"testing"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
-	util_test "github.com/kumahq/kuma/pkg/util/test"
-)
-
-func TestServer(t *testing.T) {
-	RegisterFailHandler(Fail)
-
-	RunSpecsWithDefaultAndCustomReporters(t,
-		"Server Suite",
-		[]Reporter{util_test.NewlineReporter{}})
-}
diff --git a/pkg/xds/server/server_test.go b/pkg/xds/server/server_test.go
deleted file mode 100644
index 4e9a9ec15..000000000
--- a/pkg/xds/server/server_test.go
+++ /dev/null
@@ -1,574 +0,0 @@
-// Copyright 2018 Envoyproxy Authors
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-package server_test
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"reflect"
-	"testing"
-	"time"
-
-	"google.golang.org/grpc"
-
-	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
-	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
-	envoy_cache "github.com/envoyproxy/go-control-plane/pkg/cache/v2"
-	envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
-	"github.com/envoyproxy/go-control-plane/pkg/test/resource/v2"
-
-	"github.com/kumahq/kuma/pkg/xds/server"
-)
-
-type hasher struct {
-}
-
-func (h hasher) ID(node *envoy_core.Node) string {
-	if node == nil {
-		return "unknown"
-	}
-	return node.Id
-}
-
-type mockConfigWatcher struct {
-	counts     map[string]int
-	responses  map[string][]envoy_cache.Response
-	closeWatch bool
-}
-
-func (config *mockConfigWatcher) CreateWatch(req v2.DiscoveryRequest) (chan envoy_cache.Response, func()) {
-	config.counts[req.TypeUrl] = config.counts[req.TypeUrl] + 1
-	out := make(chan envoy_cache.Response, 1)
-	if len(config.responses[req.TypeUrl]) > 0 {
-		out <- config.responses[req.TypeUrl][0]
-		config.responses[req.TypeUrl] = config.responses[req.TypeUrl][1:]
-	} else if config.closeWatch {
-		close(out)
-	}
-	return out, func() {}
-}
-
-func (config *mockConfigWatcher) Fetch(ctx context.Context, req v2.DiscoveryRequest) (*envoy_cache.Response, error) {
-	if len(config.responses[req.TypeUrl]) > 0 {
-		out := config.responses[req.TypeUrl][0]
-		config.responses[req.TypeUrl] = config.responses[req.TypeUrl][1:]
-		return &out, nil
-	}
-	return nil, errors.New("missing")
-}
-
-func (config *mockConfigWatcher) GetStatusInfo(string) envoy_cache.StatusInfo { return nil }
-func (config *mockConfigWatcher) GetStatusKeys() []string                     { return nil }
-
-func makeMockConfigWatcher() *mockConfigWatcher {
-	return &mockConfigWatcher{
-		counts: make(map[string]int),
-	}
-}
-
-type callbacks struct {
-	fetchReq      int
-	fetchResp     int
-	callbackError bool
-}
-
-func (c *callbacks) OnStreamOpen(context.Context, int64, string) error {
-	if c.callbackError {
-		return errors.New("stream open error")
-	}
-	return nil
-}
-func (c *callbacks) OnStreamClosed(int64)                                                {}
-func (c *callbacks) OnStreamRequest(int64, *v2.DiscoveryRequest) error                   { return nil }
-func (c *callbacks) OnStreamResponse(int64, *v2.DiscoveryRequest, *v2.DiscoveryResponse) {}
-func (c *callbacks) OnFetchRequest(context.Context, *v2.DiscoveryRequest) error {
-	if c.callbackError {
-		return errors.New("fetch request error")
-	}
-	c.fetchReq++
-	return nil
-}
-func (c *callbacks) OnFetchResponse(*v2.DiscoveryRequest, *v2.DiscoveryResponse) {
-	c.fetchResp++
-}
-
-type mockStream struct {
-	t         *testing.T
-	ctx       context.Context
-	recv      chan *v2.DiscoveryRequest
-	sent      chan *v2.DiscoveryResponse
-	nonce     int
-	sendError bool
-	grpc.ServerStream
-}
-
-func (stream *mockStream) Context() context.Context {
-	return stream.ctx
-}
-
-func (stream *mockStream) Send(resp *v2.DiscoveryResponse) error {
-	// check that nonce is monotonically incrementing
-	stream.nonce = stream.nonce + 1
-	if resp.Nonce != fmt.Sprintf("%d", stream.nonce) {
-		stream.t.Errorf("Nonce => got %q, want %d", resp.Nonce, stream.nonce)
-	}
-	// check that version is set
-	if resp.VersionInfo == "" {
-		stream.t.Error("VersionInfo => got none, want non-empty")
-	}
-	// check resources are non-empty
-	if len(resp.Resources) == 0 {
-		stream.t.Error("Resources => got none, want non-empty")
-	}
-	// check that type URL matches in resources
-	if resp.TypeUrl == "" {
-		stream.t.Error("TypeUrl => got none, want non-empty")
-	}
-	for _, res := range resp.Resources {
-		if res.TypeUrl != resp.TypeUrl {
-			stream.t.Errorf("TypeUrl => got %q, want %q", res.TypeUrl, resp.TypeUrl)
-		}
-	}
-	stream.sent <- resp
-	if stream.sendError {
-		return errors.New("send error")
-	}
-	return nil
-}
-
-func (stream *mockStream) Recv() (*v2.DiscoveryRequest, error) {
-	req, more := <-stream.recv
-	if !more {
-		return nil, errors.New("empty")
-	}
-	return req, nil
-}
-
-func makeMockStream(t *testing.T) *mockStream {
-	return &mockStream{
-		t:    t,
-		ctx:  context.Background(),
-		sent: make(chan *v2.DiscoveryResponse, 10),
-		recv: make(chan *v2.DiscoveryRequest, 10),
-	}
-}
-
-const (
-	clusterName  = "cluster0"
-	routeName    = "route0"
-	listenerName = "listener0"
-)
-
-var (
-	node = &envoy_core.Node{
-		Id:      "test-id",
-		Cluster: "test-cluster",
-	}
-	endpoint  = resource.MakeEndpoint(clusterName, 8080)
-	cluster   = resource.MakeCluster(resource.Ads, clusterName)
-	route     = resource.MakeRoute(routeName, clusterName)
-	listener  = resource.MakeHTTPListener(resource.Ads, listenerName, 80, routeName)
-	testTypes = []string{
-		envoy_resource.EndpointType,
-		envoy_resource.ClusterType,
-		envoy_resource.RouteType,
-		envoy_resource.ListenerType,
-	}
-)
-
-func makeResponses() map[string][]envoy_cache.Response {
-	return map[string][]envoy_cache.Response{
-		envoy_resource.EndpointType: {{
-			Version:   "1",
-			Resources: []envoy_types.Resource{endpoint},
-		}},
-		envoy_resource.ClusterType: {{
-			Version:   "2",
-			Resources: []envoy_types.Resource{cluster},
-		}},
-		envoy_resource.RouteType: {{
-			Version:   "3",
-			Resources: []envoy_types.Resource{route},
-		}},
-		envoy_resource.ListenerType: {{
-			Version:   "4",
-			Resources: []envoy_types.Resource{listener},
-		}},
-	}
-}
-
-func TestResponseHandlers(t *testing.T) {
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			config := makeMockConfigWatcher()
-			config.responses = makeResponses()
-			s := server.NewServer(config, &callbacks{})
-
-			// make a request
-			resp := makeMockStream(t)
-			resp.recv <- &v2.DiscoveryRequest{Node: node}
-			go func() {
-				var err error
-				switch typ {
-				case envoy_resource.EndpointType:
-					err = s.StreamEndpoints(resp)
-				case envoy_resource.ClusterType:
-					err = s.StreamClusters(resp)
-				case envoy_resource.RouteType:
-					err = s.StreamRoutes(resp)
-				case envoy_resource.ListenerType:
-					err = s.StreamListeners(resp)
-				}
-				if err != nil {
-					t.Errorf("Stream() => got %v, want no error", err)
-				}
-			}()
-
-			// check a response
-			select {
-			case <-resp.sent:
-				close(resp.recv)
-				if want := map[string]int{typ: 1}; !reflect.DeepEqual(want, config.counts) {
-					t.Errorf("watch counts => got %v, want %v", config.counts, want)
-				}
-			case <-time.After(1 * time.Second):
-				t.Fatalf("got no response")
-			}
-		})
-	}
-}
-
-func TestFetch(t *testing.T) {
-	config := makeMockConfigWatcher()
-	config.responses = makeResponses()
-	cb := &callbacks{}
-	s := server.NewServer(config, cb)
-	if out, err := s.FetchEndpoints(context.Background(), &v2.DiscoveryRequest{Node: node}); out == nil || err != nil {
-		t.Errorf("unexpected empty or error for endpoints: %v", err)
-	}
-	if out, err := s.FetchClusters(context.Background(), &v2.DiscoveryRequest{Node: node}); out == nil || err != nil {
-		t.Errorf("unexpected empty or error for clusters: %v", err)
-	}
-	if out, err := s.FetchRoutes(context.Background(), &v2.DiscoveryRequest{Node: node}); out == nil || err != nil {
-		t.Errorf("unexpected empty or error for routes: %v", err)
-	}
-	if out, err := s.FetchListeners(context.Background(), &v2.DiscoveryRequest{Node: node}); out == nil || err != nil {
-		t.Errorf("unexpected empty or error for listeners: %v", err)
-	}
-
-	// try again and expect empty results
-	if out, err := s.FetchEndpoints(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil {
-		t.Errorf("expected empty or error for endpoints: %v", err)
-	}
-	if out, err := s.FetchClusters(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil {
-		t.Errorf("expected empty or error for clusters: %v", err)
-	}
-	if out, err := s.FetchRoutes(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil {
-		t.Errorf("expected empty or error for routes: %v", err)
-	}
-	if out, err := s.FetchListeners(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil {
-		t.Errorf("expected empty or error for listeners: %v", err)
-	}
-
-	// try empty requests: not valid in a real gRPC server
-	if out, err := s.FetchEndpoints(context.Background(), nil); out != nil {
-		t.Errorf("expected empty on empty request: %v", err)
-	}
-	if out, err := s.FetchClusters(context.Background(), nil); out != nil {
-		t.Errorf("expected empty on empty request: %v", err)
-	}
-	if out, err := s.FetchRoutes(context.Background(), nil); out != nil {
-		t.Errorf("expected empty on empty request: %v", err)
-	}
-	if out, err := s.FetchListeners(context.Background(), nil); out != nil {
-		t.Errorf("expected empty on empty request: %v", err)
-	}
-
-	// send error from callback
-	cb.callbackError = true
-	if out, err := s.FetchEndpoints(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil || err == nil {
-		t.Errorf("expected empty or error due to callback error")
-	}
-	if out, err := s.FetchClusters(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil || err == nil {
-		t.Errorf("expected empty or error due to callback error")
-	}
-	if out, err := s.FetchRoutes(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil || err == nil {
-		t.Errorf("expected empty or error due to callback error")
-	}
-	if out, err := s.FetchListeners(context.Background(), &v2.DiscoveryRequest{Node: node}); out != nil || err == nil {
-		t.Errorf("expected empty or error due to callback error")
-	}
-
-	// verify fetch callbacks
-	if want := 8; cb.fetchReq != want {
-		t.Errorf("unexpected number of fetch requests: got %d, want %d", cb.fetchReq, want)
-	}
-	if want := 4; cb.fetchResp != want {
-		t.Errorf("unexpected number of fetch responses: got %d, want %d", cb.fetchResp, want)
-	}
-}
-
-func TestWatchClosed(t *testing.T) {
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			config := makeMockConfigWatcher()
-			config.closeWatch = true
-			s := server.NewServer(config, &callbacks{})
-
-			// make a request
-			resp := makeMockStream(t)
-			resp.recv <- &v2.DiscoveryRequest{
-				Node:    node,
-				TypeUrl: typ,
-			}
-
-			// check that response fails since watch gets closed
-			if err := s.StreamAggregatedResources(resp); err == nil {
-				t.Error("Stream() => got no error, want watch failed")
-			}
-
-			close(resp.recv)
-		})
-	}
-}
-
-func TestSendError(t *testing.T) {
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			config := makeMockConfigWatcher()
-			config.responses = makeResponses()
-			s := server.NewServer(config, &callbacks{})
-
-			// make a request
-			resp := makeMockStream(t)
-			resp.sendError = true
-			resp.recv <- &v2.DiscoveryRequest{
-				Node:    node,
-				TypeUrl: typ,
-			}
-
-			// check that response fails since send returns error
-			if err := s.StreamAggregatedResources(resp); err == nil {
-				t.Error("Stream() => got no error, want send error")
-			}
-
-			close(resp.recv)
-		})
-	}
-}
-
-func TestStaleNonce(t *testing.T) {
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			config := makeMockConfigWatcher()
-			config.responses = makeResponses()
-			s := server.NewServer(config, &callbacks{})
-
-			resp := makeMockStream(t)
-			resp.recv <- &v2.DiscoveryRequest{
-				Node:    node,
-				TypeUrl: typ,
-			}
-			stop := make(chan struct{})
-			go func() {
-				if err := s.StreamAggregatedResources(resp); err != nil {
-					t.Errorf("StreamAggregatedResources() => got %v, want no error", err)
-				}
-				// should be two watches called
-				if want := map[string]int{typ: 2}; !reflect.DeepEqual(want, config.counts) {
-					t.Errorf("watch counts => got %v, want %v", config.counts, want)
-				}
-				close(stop)
-			}()
-			select {
-			case <-resp.sent:
-				// stale request
-				resp.recv <- &v2.DiscoveryRequest{
-					Node:          node,
-					TypeUrl:       typ,
-					ResponseNonce: "xyz",
-				}
-				// fresh request
-				resp.recv <- &v2.DiscoveryRequest{
-					VersionInfo:   "1",
-					Node:          node,
-					TypeUrl:       typ,
-					ResponseNonce: "1",
-				}
-				close(resp.recv)
-			case <-time.After(1 * time.Second):
-				t.Fatalf("got %d messages on the stream, not 4", resp.nonce)
-			}
-			<-stop
-		})
-	}
-}
-
-func TestAggregatedHandlers(t *testing.T) {
-	config := makeMockConfigWatcher()
-	config.responses = makeResponses()
-	resp := makeMockStream(t)
-
-	resp.recv <- &v2.DiscoveryRequest{
-		Node:    node,
-		TypeUrl: envoy_resource.ListenerType,
-	}
-	resp.recv <- &v2.DiscoveryRequest{
-		Node:    node,
-		TypeUrl: envoy_resource.ClusterType,
-	}
-	resp.recv <- &v2.DiscoveryRequest{
-		Node:          node,
-		TypeUrl:       envoy_resource.EndpointType,
-		ResourceNames: []string{clusterName},
-	}
-	resp.recv <- &v2.DiscoveryRequest{
-		Node:          node,
-		TypeUrl:       envoy_resource.RouteType,
-		ResourceNames: []string{routeName},
-	}
-
-	s := server.NewServer(config, &callbacks{})
-	go func() {
-		if err := s.StreamAggregatedResources(resp); err != nil {
-			t.Errorf("StreamAggregatedResources() => got %v, want no error", err)
-		}
-	}()
-
-	count := 0
-	for {
-		select {
-		case <-resp.sent:
-			count++
-			if count >= 4 {
-				close(resp.recv)
-				if want := map[string]int{
-					envoy_resource.EndpointType: 1,
-					envoy_resource.ClusterType:  1,
-					envoy_resource.RouteType:    1,
-					envoy_resource.ListenerType: 1,
-				}; !reflect.DeepEqual(want, config.counts) {
-					t.Errorf("watch counts => got %v, want %v", config.counts, want)
-				}
-
-				// got all messages
-				return
-			}
-		case <-time.After(1 * time.Second):
-			t.Fatalf("got %d messages on the stream, not 4", count)
-		}
-	}
-}
-
-func TestClusterWarming(t *testing.T) {
-	config := envoy_cache.NewSnapshotCache(true, hasher{}, nil)
-	err := config.SetSnapshot(node.Id, envoy_cache.NewSnapshot("1", []envoy_types.Resource{endpoint}, nil, nil, nil, nil))
-	if err != nil {
-		t.Fatalf("got %v, want no error", err)
-	}
-	resp := makeMockStream(t)
-
-	s := server.NewServer(config, &callbacks{})
-	go func() {
-		if err := s.StreamAggregatedResources(resp); err != nil {
-			t.Errorf("StreamAggregatedResources() => got %v, want no error", err)
-		}
-	}()
-
-	// simulate initial EDS request
-
-	resp.recv <- &v2.DiscoveryRequest{
-		Node:          node,
-		TypeUrl:       envoy_resource.EndpointType,
-		ResourceNames: []string{clusterName},
-	}
-
-	var resp1 *v2.DiscoveryResponse
-resp1:
-	for {
-		select {
-		case resp := <-resp.sent:
-			if resp.TypeUrl != envoy_resource.EndpointType {
-				t.Errorf("TypeUrl => got %v, want %v", resp.TypeUrl, envoy_resource.EndpointType)
-			}
-			resp1 = resp
-			break resp1
-		case <-time.After(1 * time.Second):
-			t.Fatalf("got %d messages on the stream, not 1", 0)
-		}
-	}
-
-	// simulate EDS request as part of cluster warming
-
-	req2 := &v2.DiscoveryRequest{
-		VersionInfo:   resp1.VersionInfo,
-		ResponseNonce: resp1.Nonce,
-		Node:          node,
-		TypeUrl:       envoy_resource.EndpointType,
-		ResourceNames: []string{clusterName},
-	}
-
-	resp.recv <- req2 // ACK to resp1
-	resp.recv <- req2 // cluster warming
-
-resp2:
-	for {
-		select {
-		case resp := <-resp.sent:
-			if resp.TypeUrl != envoy_resource.EndpointType {
-				t.Errorf("TypeUrl => got %v, want %v", resp.TypeUrl, envoy_resource.EndpointType)
-			}
-			break resp2
-		case <-time.After(1 * time.Second):
-			t.Fatalf("got %d messages on the stream, not 1", 0)
-		}
-	}
-}
-
-func TestAggregateRequestType(t *testing.T) {
-	config := makeMockConfigWatcher()
-	s := server.NewServer(config, &callbacks{})
-	resp := makeMockStream(t)
-	resp.recv <- &v2.DiscoveryRequest{Node: node}
-	if err := s.StreamAggregatedResources(resp); err == nil {
-		t.Error("StreamAggregatedResources() => got nil, want an error")
-	}
-}
-
-func TestCallbackError(t *testing.T) {
-	for _, typ := range testTypes {
-		t.Run(typ, func(t *testing.T) {
-			config := makeMockConfigWatcher()
-			config.responses = makeResponses()
-			s := server.NewServer(config, &callbacks{callbackError: true})
-
-			// make a request
-			resp := makeMockStream(t)
-			resp.recv <- &v2.DiscoveryRequest{
-				Node:    node,
-				TypeUrl: typ,
-			}
-
-			// check that response fails since stream open returns error
-			if err := s.StreamAggregatedResources(resp); err == nil {
-				t.Error("Stream() => got no error, want error")
-			}
-
-			close(resp.recv)
-		})
-	}
-}