Repository: kumahq/kuma
Pull request #1416 (2021-01-13 09:55:04)

feat(kuma-cp) user specified load balancer type per route (bp #1402)
This is an automated backport of pull request #1402 done by Mergify
---


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

<br />

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

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

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

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

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

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



Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 62e9dbb44..e402b1a69 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -222,8 +222,6 @@ jobs:
         keys:
         # prefer the exact match
         - go.mod-{{ .Branch }}-{{ checksum "go.sum" }}
-        # otherwise, fallback to the most recently generated cache on any branch
-        - go.mod-
     - run:
         name: "Install pre-requirements"
         # `unzip` is necessary to install `protoc`
@@ -258,10 +256,6 @@ jobs:
         keys:
         # prefer the exact match
         - api-go.mod-{{ .Branch }}-{{ checksum "api/go.sum" }}
-        # otherwise, fallback to the most recently generated cache on that branch
-        - api-go.mod-{{ .Branch }}-
-        # otherwise, fallback to the most recently generated cache on any branch
-        - api-go.mod-
     - run:
         name: "Download Go modules"
         command: |
@@ -291,8 +285,6 @@ jobs:
         keys:
         # prefer the exact match
         - go.mod-{{ .Branch }}-{{ checksum "go.sum" }}
-        # otherwise, fallback to the most recently generated cache on any branch
-        - go.mod-
     - run:
         name: "Install pre-requirements"
         # `unzip` is necessary to install `protoc`
@@ -328,10 +320,6 @@ jobs:
         keys:
         # prefer the exact match
         - vm-executor-go.mod-{{ .Branch }}-{{ checksum "go.sum" }}
-        # otherwise, fallback to the most recently generated cache on that branch
-        - vm-executor-go.mod-{{ .Branch }}-
-        # otherwise, fallback to the most recently generated cache on any branch
-        - vm-executor-go.mod-
     - run:
         name: "Download Go modules"
         command: |
@@ -364,8 +352,6 @@ jobs:
         keys:
         # prefer the exact match
         - go.mod-{{ .Branch }}-{{ checksum "go.sum" }}
-        # otherwise, fallback to the most recently generated cache on any branch
-        - go.mod-
     - run:
         name: Build all Kuma binaries (such as, kumactl, kuma-cp, kuma-dp, kuma-prometheus-sd)
         command: make build
@@ -524,8 +510,6 @@ jobs:
         keys:
         # prefer the exact match
         - go.mod-{{ .Branch }}-{{ checksum "go.sum" }}
-        # otherwise, fallback to the most recently generated cache on any branch
-        - go.mod-
     - run:
         name: "Install Docker client"
         command: |
diff --git a/api/go.sum b/api/go.sum
index 078b0812e..e083d924b 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -1,5 +1,6 @@
 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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -15,7 +16,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM=
 github.com/envoyproxy/protoc-gen-validate v0.4.1/go.mod h1:E+IEazqdaWv3FrnGtZIu3b9fPFMK8AzeTTrk9SfVwWs=
 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=
@@ -116,6 +119,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 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 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=
@@ -142,6 +146,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
 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=
diff --git a/api/mesh/v1alpha1/traffic_route.pb.go b/api/mesh/v1alpha1/traffic_route.pb.go
index fc2703384..90d6756d6 100644
--- a/api/mesh/v1alpha1/traffic_route.pb.go
+++ b/api/mesh/v1alpha1/traffic_route.pb.go
@@ -160,6 +160,129 @@ func (x *TrafficRoute_Split) GetDestination() map[string]string {
 	return nil
 }
 
+// LoadBalancer defines the load balancing policy and configuration
+type TrafficRoute_LoadBalancer struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to LbType:
+	//	*TrafficRoute_LoadBalancer_RoundRobin_
+	//	*TrafficRoute_LoadBalancer_LeastRequest_
+	//	*TrafficRoute_LoadBalancer_RingHash_
+	//	*TrafficRoute_LoadBalancer_Random_
+	//	*TrafficRoute_LoadBalancer_Maglev_
+	LbType isTrafficRoute_LoadBalancer_LbType `protobuf_oneof:"lb_type"`
+}
+
+func (x *TrafficRoute_LoadBalancer) Reset() {
+	*x = TrafficRoute_LoadBalancer{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1}
+}
+
+func (m *TrafficRoute_LoadBalancer) GetLbType() isTrafficRoute_LoadBalancer_LbType {
+	if m != nil {
+		return m.LbType
+	}
+	return nil
+}
+
+func (x *TrafficRoute_LoadBalancer) GetRoundRobin() *TrafficRoute_LoadBalancer_RoundRobin {
+	if x, ok := x.GetLbType().(*TrafficRoute_LoadBalancer_RoundRobin_); ok {
+		return x.RoundRobin
+	}
+	return nil
+}
+
+func (x *TrafficRoute_LoadBalancer) GetLeastRequest() *TrafficRoute_LoadBalancer_LeastRequest {
+	if x, ok := x.GetLbType().(*TrafficRoute_LoadBalancer_LeastRequest_); ok {
+		return x.LeastRequest
+	}
+	return nil
+}
+
+func (x *TrafficRoute_LoadBalancer) GetRingHash() *TrafficRoute_LoadBalancer_RingHash {
+	if x, ok := x.GetLbType().(*TrafficRoute_LoadBalancer_RingHash_); ok {
+		return x.RingHash
+	}
+	return nil
+}
+
+func (x *TrafficRoute_LoadBalancer) GetRandom() *TrafficRoute_LoadBalancer_Random {
+	if x, ok := x.GetLbType().(*TrafficRoute_LoadBalancer_Random_); ok {
+		return x.Random
+	}
+	return nil
+}
+
+func (x *TrafficRoute_LoadBalancer) GetMaglev() *TrafficRoute_LoadBalancer_Maglev {
+	if x, ok := x.GetLbType().(*TrafficRoute_LoadBalancer_Maglev_); ok {
+		return x.Maglev
+	}
+	return nil
+}
+
+type isTrafficRoute_LoadBalancer_LbType interface {
+	isTrafficRoute_LoadBalancer_LbType()
+}
+
+type TrafficRoute_LoadBalancer_RoundRobin_ struct {
+	RoundRobin *TrafficRoute_LoadBalancer_RoundRobin `protobuf:"bytes,1,opt,name=round_robin,json=roundRobin,proto3,oneof"`
+}
+
+type TrafficRoute_LoadBalancer_LeastRequest_ struct {
+	LeastRequest *TrafficRoute_LoadBalancer_LeastRequest `protobuf:"bytes,2,opt,name=least_request,json=leastRequest,proto3,oneof"`
+}
+
+type TrafficRoute_LoadBalancer_RingHash_ struct {
+	RingHash *TrafficRoute_LoadBalancer_RingHash `protobuf:"bytes,3,opt,name=ring_hash,json=ringHash,proto3,oneof"`
+}
+
+type TrafficRoute_LoadBalancer_Random_ struct {
+	Random *TrafficRoute_LoadBalancer_Random `protobuf:"bytes,4,opt,name=random,proto3,oneof"`
+}
+
+type TrafficRoute_LoadBalancer_Maglev_ struct {
+	Maglev *TrafficRoute_LoadBalancer_Maglev `protobuf:"bytes,5,opt,name=maglev,proto3,oneof"`
+}
+
+func (*TrafficRoute_LoadBalancer_RoundRobin_) isTrafficRoute_LoadBalancer_LbType() {}
+
+func (*TrafficRoute_LoadBalancer_LeastRequest_) isTrafficRoute_LoadBalancer_LbType() {}
+
+func (*TrafficRoute_LoadBalancer_RingHash_) isTrafficRoute_LoadBalancer_LbType() {}
+
+func (*TrafficRoute_LoadBalancer_Random_) isTrafficRoute_LoadBalancer_LbType() {}
+
+func (*TrafficRoute_LoadBalancer_Maglev_) isTrafficRoute_LoadBalancer_LbType() {}
+
 // Conf defines the destination configuration
 type TrafficRoute_Conf struct {
 	state         protoimpl.MessageState
@@ -167,13 +290,14 @@ type TrafficRoute_Conf struct {
 	unknownFields protoimpl.UnknownFields
 
 	// List of destinations with weights assigned to them.
-	Split []*TrafficRoute_Split `protobuf:"bytes,1,rep,name=split,proto3" json:"split,omitempty"`
+	Split        []*TrafficRoute_Split      `protobuf:"bytes,1,rep,name=split,proto3" json:"split,omitempty"`
+	LoadBalancer *TrafficRoute_LoadBalancer `protobuf:"bytes,2,opt,name=load_balancer,json=loadBalancer,proto3" json:"load_balancer,omitempty"`
 }
 
 func (x *TrafficRoute_Conf) Reset() {
 	*x = TrafficRoute_Conf{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[2]
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -186,7 +310,7 @@ func (x *TrafficRoute_Conf) String() string {
 func (*TrafficRoute_Conf) ProtoMessage() {}
 
 func (x *TrafficRoute_Conf) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[2]
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -199,7 +323,7 @@ func (x *TrafficRoute_Conf) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TrafficRoute_Conf.ProtoReflect.Descriptor instead.
 func (*TrafficRoute_Conf) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1}
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 2}
 }
 
 func (x *TrafficRoute_Conf) GetSplit() []*TrafficRoute_Split {
@@ -209,6 +333,251 @@ func (x *TrafficRoute_Conf) GetSplit() []*TrafficRoute_Split {
 	return nil
 }
 
+func (x *TrafficRoute_Conf) GetLoadBalancer() *TrafficRoute_LoadBalancer {
+	if x != nil {
+		return x.LoadBalancer
+	}
+	return nil
+}
+
+// RoundRobin is a simple policy in which each available upstream host is
+// selected in round robin order
+type TrafficRoute_LoadBalancer_RoundRobin struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *TrafficRoute_LoadBalancer_RoundRobin) Reset() {
+	*x = TrafficRoute_LoadBalancer_RoundRobin{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer_RoundRobin) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer_RoundRobin) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer_RoundRobin) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer_RoundRobin.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer_RoundRobin) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1, 0}
+}
+
+// LeastRequest uses different algorithms depending on whether hosts have the
+// same or different weights
+type TrafficRoute_LoadBalancer_LeastRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The number of random healthy hosts from which the host with the fewest
+	// active requests will be chosen. Defaults to 2 so that we perform
+	// two-choice selection if the field is not set.
+	ChoiceCount uint32 `protobuf:"varint,1,opt,name=choice_count,json=choiceCount,proto3" json:"choice_count,omitempty"`
+}
+
+func (x *TrafficRoute_LoadBalancer_LeastRequest) Reset() {
+	*x = TrafficRoute_LoadBalancer_LeastRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer_LeastRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer_LeastRequest) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer_LeastRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer_LeastRequest.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer_LeastRequest) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1, 1}
+}
+
+func (x *TrafficRoute_LoadBalancer_LeastRequest) GetChoiceCount() uint32 {
+	if x != nil {
+		return x.ChoiceCount
+	}
+	return 0
+}
+
+// RingHash implements consistent hashing to upstream hosts
+type TrafficRoute_LoadBalancer_RingHash struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The hash function used to hash hosts onto the ketama ring. The value
+	// defaults to 'XX_HASH'
+	HashFunction string `protobuf:"bytes,1,opt,name=hash_function,json=hashFunction,proto3" json:"hash_function,omitempty"`
+	// Minimum hash ring size
+	MinRingSize uint64 `protobuf:"varint,2,opt,name=min_ring_size,json=minRingSize,proto3" json:"min_ring_size,omitempty"`
+	// Maximum hash ring size.
+	MaxRingSize uint64 `protobuf:"varint,3,opt,name=max_ring_size,json=maxRingSize,proto3" json:"max_ring_size,omitempty"`
+}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) Reset() {
+	*x = TrafficRoute_LoadBalancer_RingHash{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer_RingHash) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer_RingHash.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer_RingHash) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1, 2}
+}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) GetHashFunction() string {
+	if x != nil {
+		return x.HashFunction
+	}
+	return ""
+}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) GetMinRingSize() uint64 {
+	if x != nil {
+		return x.MinRingSize
+	}
+	return 0
+}
+
+func (x *TrafficRoute_LoadBalancer_RingHash) GetMaxRingSize() uint64 {
+	if x != nil {
+		return x.MaxRingSize
+	}
+	return 0
+}
+
+// Random selects a random available host
+type TrafficRoute_LoadBalancer_Random struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *TrafficRoute_LoadBalancer_Random) Reset() {
+	*x = TrafficRoute_LoadBalancer_Random{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer_Random) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer_Random) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer_Random) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer_Random.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer_Random) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1, 3}
+}
+
+// Maglev implements consistent hashing to upstream hosts
+type TrafficRoute_LoadBalancer_Maglev struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *TrafficRoute_LoadBalancer_Maglev) Reset() {
+	*x = TrafficRoute_LoadBalancer_Maglev{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TrafficRoute_LoadBalancer_Maglev) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TrafficRoute_LoadBalancer_Maglev) ProtoMessage() {}
+
+func (x *TrafficRoute_LoadBalancer_Maglev) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_traffic_route_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TrafficRoute_LoadBalancer_Maglev.ProtoReflect.Descriptor instead.
+func (*TrafficRoute_LoadBalancer_Maglev) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP(), []int{0, 1, 4}
+}
+
 var File_mesh_v1alpha1_traffic_route_proto protoreflect.FileDescriptor
 
 var file_mesh_v1alpha1_traffic_route_proto_rawDesc = []byte{
@@ -218,8 +587,8 @@ var file_mesh_v1alpha1_traffic_route_proto_rawDesc = []byte{
 	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1c, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31,
 	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f,
-	0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d,
-	0x04, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
+	0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x83,
+	0x0a, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
 	0x40, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
 	0x32, 0x1c, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
 	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x08,
@@ -246,16 +615,63 @@ var file_mesh_v1alpha1_traffic_route_proto_rawDesc = []byte{
 	0x0a, 0x10, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74,
 	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
 	0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4e,
-	0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x46, 0x0a, 0x05, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73,
-	0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66,
-	0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x42, 0x08, 0xfa,
-	0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x05, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x42, 0x2a,
-	0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d,
-	0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73,
-	0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x9e,
+	0x05, 0x0a, 0x0c, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x12,
+	0x5b, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69,
+	0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e,
+	0x63, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x62, 0x69, 0x6e, 0x48, 0x00,
+	0x52, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x62, 0x69, 0x6e, 0x12, 0x61, 0x0a, 0x0d,
+	0x6c, 0x65, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63,
+	0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
+	0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48,
+	0x00, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x55, 0x0a, 0x09, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x36, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52,
+	0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
+	0x72, 0x2e, 0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x48, 0x00, 0x52, 0x08, 0x72, 0x69,
+	0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4e, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65,
+	0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66,
+	0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c,
+	0x61, 0x6e, 0x63, 0x65, 0x72, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x48, 0x00, 0x52, 0x06,
+	0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, 0x4e, 0x0a, 0x06, 0x6d, 0x61, 0x67, 0x6c, 0x65, 0x76,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d, 0x65,
+	0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x66,
+	0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c,
+	0x61, 0x6e, 0x63, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x67, 0x6c, 0x65, 0x76, 0x48, 0x00, 0x52, 0x06,
+	0x6d, 0x61, 0x67, 0x6c, 0x65, 0x76, 0x1a, 0x0c, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52,
+	0x6f, 0x62, 0x69, 0x6e, 0x1a, 0x31, 0x0a, 0x0c, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x68, 0x6f, 0x69,
+	0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x77, 0x0a, 0x08, 0x52, 0x69, 0x6e, 0x67, 0x48,
+	0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x68,
+	0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f,
+	0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0b, 0x6d, 0x69, 0x6e, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d,
+	0x6d, 0x61, 0x78, 0x5f, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65,
+	0x1a, 0x08, 0x0a, 0x06, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x1a, 0x08, 0x0a, 0x06, 0x4d, 0x61,
+	0x67, 0x6c, 0x65, 0x76, 0x42, 0x09, 0x0a, 0x07, 0x6c, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x1a,
+	0xa2, 0x01, 0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x46, 0x0a, 0x05, 0x73, 0x70, 0x6c, 0x69,
+	0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d,
+	0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61,
+	0x66, 0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x42,
+	0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x05, 0x73, 0x70, 0x6c, 0x69, 0x74,
+	0x12, 0x52, 0x0a, 0x0d, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
+	0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d,
+	0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61,
+	0x66, 0x66, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61,
+	0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x52, 0x0c, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61,
+	0x6e, 0x63, 0x65, 0x72, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -270,25 +686,37 @@ func file_mesh_v1alpha1_traffic_route_proto_rawDescGZIP() []byte {
 	return file_mesh_v1alpha1_traffic_route_proto_rawDescData
 }
 
-var file_mesh_v1alpha1_traffic_route_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_mesh_v1alpha1_traffic_route_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
 var file_mesh_v1alpha1_traffic_route_proto_goTypes = []interface{}{
-	(*TrafficRoute)(nil),       // 0: kuma.mesh.v1alpha1.TrafficRoute
-	(*TrafficRoute_Split)(nil), // 1: kuma.mesh.v1alpha1.TrafficRoute.Split
-	(*TrafficRoute_Conf)(nil),  // 2: kuma.mesh.v1alpha1.TrafficRoute.Conf
-	nil,                        // 3: kuma.mesh.v1alpha1.TrafficRoute.Split.DestinationEntry
-	(*Selector)(nil),           // 4: kuma.mesh.v1alpha1.Selector
+	(*TrafficRoute)(nil),              // 0: kuma.mesh.v1alpha1.TrafficRoute
+	(*TrafficRoute_Split)(nil),        // 1: kuma.mesh.v1alpha1.TrafficRoute.Split
+	(*TrafficRoute_LoadBalancer)(nil), // 2: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer
+	(*TrafficRoute_Conf)(nil),         // 3: kuma.mesh.v1alpha1.TrafficRoute.Conf
+	nil,                               // 4: kuma.mesh.v1alpha1.TrafficRoute.Split.DestinationEntry
+	(*TrafficRoute_LoadBalancer_RoundRobin)(nil),   // 5: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.RoundRobin
+	(*TrafficRoute_LoadBalancer_LeastRequest)(nil), // 6: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.LeastRequest
+	(*TrafficRoute_LoadBalancer_RingHash)(nil),     // 7: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.RingHash
+	(*TrafficRoute_LoadBalancer_Random)(nil),       // 8: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.Random
+	(*TrafficRoute_LoadBalancer_Maglev)(nil),       // 9: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.Maglev
+	(*Selector)(nil),                               // 10: kuma.mesh.v1alpha1.Selector
 }
 var file_mesh_v1alpha1_traffic_route_proto_depIdxs = []int32{
-	4, // 0: kuma.mesh.v1alpha1.TrafficRoute.sources:type_name -> kuma.mesh.v1alpha1.Selector
-	4, // 1: kuma.mesh.v1alpha1.TrafficRoute.destinations:type_name -> kuma.mesh.v1alpha1.Selector
-	2, // 2: kuma.mesh.v1alpha1.TrafficRoute.conf:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Conf
-	3, // 3: kuma.mesh.v1alpha1.TrafficRoute.Split.destination:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Split.DestinationEntry
-	1, // 4: kuma.mesh.v1alpha1.TrafficRoute.Conf.split:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Split
-	5, // [5:5] is the sub-list for method output_type
-	5, // [5:5] is the sub-list for method input_type
-	5, // [5:5] is the sub-list for extension type_name
-	5, // [5:5] is the sub-list for extension extendee
-	0, // [0:5] is the sub-list for field type_name
+	10, // 0: kuma.mesh.v1alpha1.TrafficRoute.sources:type_name -> kuma.mesh.v1alpha1.Selector
+	10, // 1: kuma.mesh.v1alpha1.TrafficRoute.destinations:type_name -> kuma.mesh.v1alpha1.Selector
+	3,  // 2: kuma.mesh.v1alpha1.TrafficRoute.conf:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Conf
+	4,  // 3: kuma.mesh.v1alpha1.TrafficRoute.Split.destination:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Split.DestinationEntry
+	5,  // 4: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.round_robin:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.RoundRobin
+	6,  // 5: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.least_request:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.LeastRequest
+	7,  // 6: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.ring_hash:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.RingHash
+	8,  // 7: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.random:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.Random
+	9,  // 8: kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.maglev:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer.Maglev
+	1,  // 9: kuma.mesh.v1alpha1.TrafficRoute.Conf.split:type_name -> kuma.mesh.v1alpha1.TrafficRoute.Split
+	2,  // 10: kuma.mesh.v1alpha1.TrafficRoute.Conf.load_balancer:type_name -> kuma.mesh.v1alpha1.TrafficRoute.LoadBalancer
+	11, // [11:11] is the sub-list for method output_type
+	11, // [11:11] is the sub-list for method input_type
+	11, // [11:11] is the sub-list for extension type_name
+	11, // [11:11] is the sub-list for extension extendee
+	0,  // [0:11] is the sub-list for field type_name
 }
 
 func init() { file_mesh_v1alpha1_traffic_route_proto_init() }
@@ -323,6 +751,18 @@ func file_mesh_v1alpha1_traffic_route_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_traffic_route_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*TrafficRoute_Conf); i {
 			case 0:
 				return &v.state
@@ -334,6 +774,73 @@ func file_mesh_v1alpha1_traffic_route_proto_init() {
 				return nil
 			}
 		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer_RoundRobin); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer_LeastRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer_RingHash); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer_Random); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_traffic_route_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TrafficRoute_LoadBalancer_Maglev); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_mesh_v1alpha1_traffic_route_proto_msgTypes[2].OneofWrappers = []interface{}{
+		(*TrafficRoute_LoadBalancer_RoundRobin_)(nil),
+		(*TrafficRoute_LoadBalancer_LeastRequest_)(nil),
+		(*TrafficRoute_LoadBalancer_RingHash_)(nil),
+		(*TrafficRoute_LoadBalancer_Random_)(nil),
+		(*TrafficRoute_LoadBalancer_Maglev_)(nil),
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -341,7 +848,7 @@ func file_mesh_v1alpha1_traffic_route_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_mesh_v1alpha1_traffic_route_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   4,
+			NumMessages:   10,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/api/mesh/v1alpha1/traffic_route.proto b/api/mesh/v1alpha1/traffic_route.proto
index 3f43db9fc..d1efcb3a2 100644
--- a/api/mesh/v1alpha1/traffic_route.proto
+++ b/api/mesh/v1alpha1/traffic_route.proto
@@ -40,10 +40,55 @@ message TrafficRoute {
     } ];
   };
 
+  // LoadBalancer defines the load balancing policy and configuration
+  message LoadBalancer {
+
+    // RoundRobin is a simple policy in which each available upstream host is
+    // selected in round robin order
+    message RoundRobin {}
+
+    // LeastRequest uses different algorithms depending on whether hosts have
+    // the same or different weights
+    message LeastRequest {
+      // The number of random healthy hosts from which the host with the fewest
+      // active requests will be chosen. Defaults to 2 so that we perform
+      // two-choice selection if the field is not set.
+      uint32 choice_count = 1;
+    }
+
+    // RingHash implements consistent hashing to upstream hosts
+    message RingHash {
+      // The hash function used to hash hosts onto the ketama ring. The value
+      // defaults to 'XX_HASH'
+      string hash_function = 1;
+
+      // Minimum hash ring size
+      uint64 min_ring_size = 2;
+
+      // Maximum hash ring size.
+      uint64 max_ring_size = 3;
+    }
+
+    // Random selects a random available host
+    message Random {}
+
+    // Maglev implements consistent hashing to upstream hosts
+    message Maglev {}
+
+    oneof lb_type {
+      RoundRobin round_robin = 1;
+      LeastRequest least_request = 2;
+      RingHash ring_hash = 3;
+      Random random = 4;
+      Maglev maglev = 5;
+    }
+  };
+
   // Conf defines the destination configuration
   message Conf {
     // List of destinations with weights assigned to them.
     repeated Split split = 1 [ (validate.rules).repeated .min_items = 1 ];
+    LoadBalancer load_balancer = 2;
   };
 
   // Configuration for the route
diff --git a/api/system/v1alpha1/zone_insight.pb.go b/api/system/v1alpha1/zone_insight.pb.go
index 65144effe..65f2543db 100644
--- a/api/system/v1alpha1/zone_insight.pb.go
+++ b/api/system/v1alpha1/zone_insight.pb.go
@@ -101,6 +101,8 @@ type KDSSubscription struct {
 	DisconnectTime *timestamp.Timestamp `protobuf:"bytes,4,opt,name=disconnect_time,json=disconnectTime,proto3" json:"disconnect_time,omitempty"`
 	// Status of the KDS subscription.
 	Status *KDSSubscriptionStatus `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
+	// Version of Remote Kuma CP.
+	Version *Version `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"`
 }
 
 func (x *KDSSubscription) Reset() {
@@ -170,6 +172,13 @@ func (x *KDSSubscription) GetStatus() *KDSSubscriptionStatus {
 	return nil
 }
 
+func (x *KDSSubscription) GetVersion() *Version {
+	if x != nil {
+		return x.Version
+	}
+	return nil
+}
+
 // KDSSubscriptionStatus defines status of an KDS subscription.
 type KDSSubscriptionStatus struct {
 	state         protoimpl.MessageState
@@ -303,6 +312,131 @@ func (x *KDSServiceStats) GetResponsesRejected() uint64 {
 	return 0
 }
 
+// Version defines version of Kuma ControlPlane
+type Version struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Version of Remote Kuma CP
+	KumaCp *KumaCpVersion `protobuf:"bytes,1,opt,name=kumaCp,proto3" json:"kumaCp,omitempty"`
+}
+
+func (x *Version) Reset() {
+	*x = Version{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_system_v1alpha1_zone_insight_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Version) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Version) ProtoMessage() {}
+
+func (x *Version) ProtoReflect() protoreflect.Message {
+	mi := &file_system_v1alpha1_zone_insight_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Version.ProtoReflect.Descriptor instead.
+func (*Version) Descriptor() ([]byte, []int) {
+	return file_system_v1alpha1_zone_insight_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *Version) GetKumaCp() *KumaCpVersion {
+	if x != nil {
+		return x.KumaCp
+	}
+	return nil
+}
+
+// KumaCpVersion describes details of Kuma ControlPlane version
+type KumaCpVersion struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Version number of Kuma ControlPlane
+	Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
+	// Git tag of Kuma ControlPlane version
+	GitTag string `protobuf:"bytes,2,opt,name=gitTag,proto3" json:"gitTag,omitempty"`
+	// Git commit of Kuma ControlPlane version
+	GitCommit string `protobuf:"bytes,3,opt,name=gitCommit,proto3" json:"gitCommit,omitempty"`
+	// Build date of Kuma ControlPlane version
+	BuildDate string `protobuf:"bytes,4,opt,name=buildDate,proto3" json:"buildDate,omitempty"`
+}
+
+func (x *KumaCpVersion) Reset() {
+	*x = KumaCpVersion{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_system_v1alpha1_zone_insight_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *KumaCpVersion) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*KumaCpVersion) ProtoMessage() {}
+
+func (x *KumaCpVersion) ProtoReflect() protoreflect.Message {
+	mi := &file_system_v1alpha1_zone_insight_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use KumaCpVersion.ProtoReflect.Descriptor instead.
+func (*KumaCpVersion) Descriptor() ([]byte, []int) {
+	return file_system_v1alpha1_zone_insight_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *KumaCpVersion) GetVersion() string {
+	if x != nil {
+		return x.Version
+	}
+	return ""
+}
+
+func (x *KumaCpVersion) GetGitTag() string {
+	if x != nil {
+		return x.GitTag
+	}
+	return ""
+}
+
+func (x *KumaCpVersion) GetGitCommit() string {
+	if x != nil {
+		return x.GitCommit
+	}
+	return ""
+}
+
+func (x *KumaCpVersion) GetBuildDate() string {
+	if x != nil {
+		return x.BuildDate
+	}
+	return ""
+}
+
 var File_system_v1alpha1_zone_insight_proto protoreflect.FileDescriptor
 
 var file_system_v1alpha1_zone_insight_proto_rawDesc = []byte{
@@ -319,7 +453,7 @@ var file_system_v1alpha1_zone_insight_proto_rawDesc = []byte{
 	0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
 	0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x22, 0xbe, 0x02, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
+	0x22, 0xf7, 0x02, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
 	0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
 	0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x35, 0x0a,
 	0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
@@ -339,37 +473,53 @@ var file_system_v1alpha1_zone_insight_proto_rawDesc = []byte{
 	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62,
 	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42,
 	0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x22, 0xc5, 0x02, 0x0a, 0x15, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c,
-	0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-	0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
-	0x65, 0x12, 0x3b, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x73, 0x12, 0x37, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x02, 0x0a, 0x15, 0x4b,
+	0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64,
+	0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74,
+	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x74, 0x6f,
+	0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61,
+	0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
+	0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x49, 0x0a, 0x04, 0x73, 0x74, 0x61, 0x74, 0x18,
+	0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53,
+	0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x74,
+	0x61, 0x74, 0x1a, 0x5e, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
 	0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76,
 	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x49,
-	0x0a, 0x04, 0x73, 0x74, 0x61, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6b,
-	0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e,
-	0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x1a, 0x5e, 0x0a, 0x09, 0x53, 0x74, 0x61,
-	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73,
-	0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b,
-	0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0f, 0x4b, 0x44,
-	0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a,
-	0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73,
-	0x53, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x73, 0x5f, 0x61, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x41,
-	0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65,
-	0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x73, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69,
+	0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+	0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d,
+	0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a,
+	0x16, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x61, 0x63, 0x6b, 0x6e, 0x6f,
+	0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x72,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65,
+	0x64, 0x67, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x73, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x11, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x65, 0x6a, 0x65, 0x63,
+	0x74, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b,
+	0x0a, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
+	0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x56, 0x65, 0x72, 0x73,
+	0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x22, 0x7d, 0x0a, 0x0d, 0x4b,
+	0x75, 0x6d, 0x61, 0x43, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07,
+	0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x69, 0x74, 0x54, 0x61, 0x67,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x69, 0x74, 0x54, 0x61, 0x67, 0x12, 0x1c,
+	0x0a, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1c, 0x0a, 0x09,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69,
 	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f,
 	0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f,
 	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
@@ -387,29 +537,33 @@ func file_system_v1alpha1_zone_insight_proto_rawDescGZIP() []byte {
 	return file_system_v1alpha1_zone_insight_proto_rawDescData
 }
 
-var file_system_v1alpha1_zone_insight_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_system_v1alpha1_zone_insight_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
 var file_system_v1alpha1_zone_insight_proto_goTypes = []interface{}{
 	(*ZoneInsight)(nil),           // 0: kuma.system.v1alpha1.ZoneInsight
 	(*KDSSubscription)(nil),       // 1: kuma.system.v1alpha1.KDSSubscription
 	(*KDSSubscriptionStatus)(nil), // 2: kuma.system.v1alpha1.KDSSubscriptionStatus
 	(*KDSServiceStats)(nil),       // 3: kuma.system.v1alpha1.KDSServiceStats
-	nil,                           // 4: kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry
-	(*timestamp.Timestamp)(nil),   // 5: google.protobuf.Timestamp
+	(*Version)(nil),               // 4: kuma.system.v1alpha1.Version
+	(*KumaCpVersion)(nil),         // 5: kuma.system.v1alpha1.KumaCpVersion
+	nil,                           // 6: kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry
+	(*timestamp.Timestamp)(nil),   // 7: google.protobuf.Timestamp
 }
 var file_system_v1alpha1_zone_insight_proto_depIdxs = []int32{
-	1, // 0: kuma.system.v1alpha1.ZoneInsight.subscriptions:type_name -> kuma.system.v1alpha1.KDSSubscription
-	5, // 1: kuma.system.v1alpha1.KDSSubscription.connect_time:type_name -> google.protobuf.Timestamp
-	5, // 2: kuma.system.v1alpha1.KDSSubscription.disconnect_time:type_name -> google.protobuf.Timestamp
-	2, // 3: kuma.system.v1alpha1.KDSSubscription.status:type_name -> kuma.system.v1alpha1.KDSSubscriptionStatus
-	5, // 4: kuma.system.v1alpha1.KDSSubscriptionStatus.last_update_time:type_name -> google.protobuf.Timestamp
-	3, // 5: kuma.system.v1alpha1.KDSSubscriptionStatus.total:type_name -> kuma.system.v1alpha1.KDSServiceStats
-	4, // 6: kuma.system.v1alpha1.KDSSubscriptionStatus.stat:type_name -> kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry
-	3, // 7: kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry.value:type_name -> kuma.system.v1alpha1.KDSServiceStats
-	8, // [8:8] is the sub-list for method output_type
-	8, // [8:8] is the sub-list for method input_type
-	8, // [8:8] is the sub-list for extension type_name
-	8, // [8:8] is the sub-list for extension extendee
-	0, // [0:8] is the sub-list for field type_name
+	1,  // 0: kuma.system.v1alpha1.ZoneInsight.subscriptions:type_name -> kuma.system.v1alpha1.KDSSubscription
+	7,  // 1: kuma.system.v1alpha1.KDSSubscription.connect_time:type_name -> google.protobuf.Timestamp
+	7,  // 2: kuma.system.v1alpha1.KDSSubscription.disconnect_time:type_name -> google.protobuf.Timestamp
+	2,  // 3: kuma.system.v1alpha1.KDSSubscription.status:type_name -> kuma.system.v1alpha1.KDSSubscriptionStatus
+	4,  // 4: kuma.system.v1alpha1.KDSSubscription.version:type_name -> kuma.system.v1alpha1.Version
+	7,  // 5: kuma.system.v1alpha1.KDSSubscriptionStatus.last_update_time:type_name -> google.protobuf.Timestamp
+	3,  // 6: kuma.system.v1alpha1.KDSSubscriptionStatus.total:type_name -> kuma.system.v1alpha1.KDSServiceStats
+	6,  // 7: kuma.system.v1alpha1.KDSSubscriptionStatus.stat:type_name -> kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry
+	5,  // 8: kuma.system.v1alpha1.Version.kumaCp:type_name -> kuma.system.v1alpha1.KumaCpVersion
+	3,  // 9: kuma.system.v1alpha1.KDSSubscriptionStatus.StatEntry.value:type_name -> kuma.system.v1alpha1.KDSServiceStats
+	10, // [10:10] is the sub-list for method output_type
+	10, // [10:10] is the sub-list for method input_type
+	10, // [10:10] is the sub-list for extension type_name
+	10, // [10:10] is the sub-list for extension extendee
+	0,  // [0:10] is the sub-list for field type_name
 }
 
 func init() { file_system_v1alpha1_zone_insight_proto_init() }
@@ -466,6 +620,30 @@ func file_system_v1alpha1_zone_insight_proto_init() {
 				return nil
 			}
 		}
+		file_system_v1alpha1_zone_insight_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Version); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_system_v1alpha1_zone_insight_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*KumaCpVersion); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -473,7 +651,7 @@ func file_system_v1alpha1_zone_insight_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_system_v1alpha1_zone_insight_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   5,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/api/system/v1alpha1/zone_insight.proto b/api/system/v1alpha1/zone_insight.proto
index 0071bb53f..2bd7babf5 100644
--- a/api/system/v1alpha1/zone_insight.proto
+++ b/api/system/v1alpha1/zone_insight.proto
@@ -41,6 +41,9 @@ message KDSSubscription {
 
   // Status of the KDS subscription.
   KDSSubscriptionStatus status = 5 [ (validate.rules).message.required = true ];
+
+  // Version of Remote Kuma CP.
+  Version version = 6;
 }
 
 // KDSSubscriptionStatus defines status of an KDS subscription.
@@ -67,3 +70,26 @@ message KDSServiceStats {
   // Number of xDS responses NACKed by the Dataplane.
   uint64 responses_rejected = 3;
 }
+
+// Version defines version of Kuma ControlPlane
+message Version {
+
+  // Version of Remote Kuma CP
+  KumaCpVersion kumaCp = 1;
+}
+
+// KumaCpVersion describes details of Kuma ControlPlane version
+message KumaCpVersion {
+
+  // Version number of Kuma ControlPlane
+  string version = 1;
+
+  // Git tag of Kuma ControlPlane version
+  string gitTag = 2;
+
+  // Git commit of Kuma ControlPlane version
+  string gitCommit = 3;
+
+  // Build date of Kuma ControlPlane version
+  string buildDate = 4;
+}
diff --git a/api/system/v1alpha1/zone_insight_helpers.go b/api/system/v1alpha1/zone_insight_helpers.go
index 8e3d35e79..dade4d3ad 100644
--- a/api/system/v1alpha1/zone_insight_helpers.go
+++ b/api/system/v1alpha1/zone_insight_helpers.go
@@ -69,3 +69,14 @@ func (m *ZoneInsight) UpdateSubscription(s *KDSSubscription) {
 		m.Subscriptions = append(m.Subscriptions, s)
 	}
 }
+
+func NewVersion() *Version {
+	return &Version{
+		KumaCp: &KumaCpVersion{
+			Version:   "",
+			GitTag:    "",
+			GitCommit: "",
+			BuildDate: "",
+		},
+	}
+}
diff --git a/app/kumactl/cmd/inspect/inspect_zones.go b/app/kumactl/cmd/inspect/inspect_zones.go
index bbb0cc870..b59f5b01e 100644
--- a/app/kumactl/cmd/inspect/inspect_zones.go
+++ b/app/kumactl/cmd/inspect/inspect_zones.go
@@ -50,7 +50,7 @@ func newInspectZonesCmd(ctx *inspectContext) *cobra.Command {
 
 func printZoneOverviews(now time.Time, zoneOverviews *system.ZoneOverviewResourceList, out io.Writer) error {
 	data := printers.Table{
-		Headers: []string{"MESH", "NAME", "STATUS", "LAST CONNECTED AGO", "LAST UPDATED AGO", "TOTAL UPDATES", "TOTAL ERRORS"},
+		Headers: []string{"NAME", "STATUS", "LAST CONNECTED AGO", "LAST UPDATED AGO", "TOTAL UPDATES", "TOTAL ERRORS", "REMOTE-CP VERSION"},
 		NextRow: func() func() []string {
 			i := 0
 			return func() []string {
@@ -75,14 +75,21 @@ func printZoneOverviews(now time.Time, zoneOverviews *system.ZoneOverviewResourc
 				}
 				lastUpdated := util_proto.MustTimestampFromProto(lastSubscription.GetStatus().GetLastUpdateTime())
 
+				var remoteCpVersion string
+				if lastSubscription.GetVersion() != nil {
+					if lastSubscription.Version.KumaCp != nil {
+						remoteCpVersion = lastSubscription.Version.KumaCp.Version
+					}
+				}
+
 				return []string{
-					meta.GetMesh(),                       // MESH
 					meta.GetName(),                       // NAME,
 					onlineStatus,                         // STATUS
 					table.Ago(lastConnected, now),        // LAST CONNECTED AGO
 					table.Ago(lastUpdated, now),          // LAST UPDATED AGO
 					table.Number(totalResponsesSent),     // TOTAL UPDATES
 					table.Number(totalResponsesRejected), // TOTAL ERRORS
+					remoteCpVersion,                      // REMOTE-CP VERSION
 				}
 			}
 		}(),
diff --git a/go.sum b/go.sum
index 7960b3a37..3af10e965 100644
--- a/go.sum
+++ b/go.sum
@@ -99,6 +99,7 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
 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 v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+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=
@@ -106,8 +107,10 @@ 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.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+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=
@@ -158,6 +161,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=
@@ -179,6 +183,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=
@@ -200,9 +205,12 @@ github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
 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/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
+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/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw=
@@ -267,6 +275,7 @@ github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs
 github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
 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/dchest/siphash v1.1.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
@@ -276,6 +285,7 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xb
 github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/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.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA=
@@ -356,10 +366,12 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
 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.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
 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=
@@ -381,6 +393,7 @@ 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/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
@@ -464,6 +477,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.0/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.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE=
 github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0=
@@ -475,6 +489,7 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er
 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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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=
@@ -526,6 +541,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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -547,6 +563,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 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=
@@ -583,6 +600,7 @@ github.com/hashicorp/consul v1.3.1/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+
 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=
@@ -590,6 +608,7 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9
 github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-hclog v0.14.0 h1:1X+ga+2ki9aUJJaliI2isvjNLI8rNCGHFkZ1FaVpvCA=
 github.com/hashicorp/go-hclog v0.14.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+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-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -616,8 +635,10 @@ 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=
@@ -631,6 +652,7 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh
 github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/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/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -705,6 +727,7 @@ github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNE
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 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=
@@ -780,6 +803,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/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
@@ -807,9 +831,11 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx
 github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
 github.com/mitchellh/reflectwalk v1.0.1/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 v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@@ -835,6 +861,7 @@ github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk
 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=
@@ -893,6 +920,7 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
 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=
@@ -915,6 +943,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=
@@ -939,6 +968,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=
@@ -1000,6 +1030,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=
@@ -1033,6 +1064,7 @@ 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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
@@ -1041,11 +1073,13 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
 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/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
 github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
+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/go-spiffe/v2 v2.0.0-beta.4 h1:tF4to8mhz24XGez/Vn9YPdmKrhg50M+zLGt1cbGuZbI=
 github.com/spiffe/go-spiffe/v2 v2.0.0-beta.4/go.mod h1:TEfgrEcyFhuSuvqohJt6IxENUNeHfndWCCV1EX7UaVk=
@@ -1079,6 +1113,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
 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/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
+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/uber/jaeger-client-go v0.0.0-20190228190846-ecf2d03a9e80/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
 github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
@@ -1282,6 +1317,7 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/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=
@@ -1460,6 +1496,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 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=
@@ -1594,6 +1631,7 @@ 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/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -1603,6 +1641,7 @@ 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/square/go-jose.v2 v2.4.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=
@@ -1699,11 +1738,13 @@ k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 k8s.io/klog v0.2.0/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-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
+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.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
 k8s.io/kubectl v0.18.3/go.mod h1:k/EpvXBDgEsHBzWr0A44l9+ArvYi3txBBnzXBjQasUQ=
@@ -1743,11 +1784,15 @@ sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WG
 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
 sigs.k8s.io/service-apis v0.0.0-20200708220836-10c7cb28ed93/go.mod h1:s/e4e7RMwEcieVQ33+YMaKbSeaQyatebjinhC/kIJ3U=
 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.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
 sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
 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/core/resources/apis/mesh/traffic_route_validator.go b/pkg/core/resources/apis/mesh/traffic_route_validator.go
index 76a88ed44..be7eda98b 100644
--- a/pkg/core/resources/apis/mesh/traffic_route_validator.go
+++ b/pkg/core/resources/apis/mesh/traffic_route_validator.go
@@ -1,6 +1,7 @@
 package mesh
 
 import (
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/validators"
 )
 
@@ -43,5 +44,28 @@ func (d *TrafficRouteResource) validateConf() (err validators.ValidationError) {
 			RequireService:       true,
 		}))
 	}
+	err.Add(d.validateLb())
+
+	return
+}
+
+func (d *TrafficRouteResource) validateLb() (err validators.ValidationError) {
+	lb := d.Spec.GetConf().GetLoadBalancer()
+	if lb == nil {
+		return
+	}
+
+	switch lb.LbType.(type) {
+	case *mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_:
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_RingHash_:
+		lbConfig := lb.GetRingHash()
+		switch lbConfig.HashFunction {
+		case "XX_HASH", "MURMUR_HASH_2":
+		default:
+			root := validators.RootedAt("conf.loadBalancer.ringHash.hashFunction")
+			err.AddViolationAt(root, "must have a valid hash function")
+		}
+	}
 	return
 }
diff --git a/pkg/defaults/mesh/traffic_route.go b/pkg/defaults/mesh/traffic_route.go
index d8efdc154..31aec984e 100644
--- a/pkg/defaults/mesh/traffic_route.go
+++ b/pkg/defaults/mesh/traffic_route.go
@@ -27,6 +27,9 @@ var (
 				Weight:      100,
 				Destination: mesh_proto.MatchAnyService(),
 			}},
+			LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_{},
+			},
 		},
 	}
 )
diff --git a/pkg/kds/client/stream.go b/pkg/kds/client/stream.go
index 676f0f02b..9e71d72ff 100644
--- a/pkg/kds/client/stream.go
+++ b/pkg/kds/client/stream.go
@@ -7,10 +7,14 @@ import (
 
 	envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 	envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
+	pstruct "github.com/golang/protobuf/ptypes/struct"
 	"google.golang.org/genproto/googleapis/rpc/status"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	kuma_version "github.com/kumahq/kuma/pkg/version"
 )
 
 type KDSStream interface {
@@ -40,11 +44,27 @@ func NewKDSStream(s mesh_proto.KumaDiscoveryService_StreamKumaResourcesClient, c
 }
 
 func (s *stream) DiscoveryRequest(resourceType model.ResourceType) error {
+	cpVersion, err := util_proto.ToStruct(&system_proto.Version{
+		KumaCp: &system_proto.KumaCpVersion{
+			Version:   kuma_version.Build.Version,
+			GitTag:    kuma_version.Build.GitTag,
+			GitCommit: kuma_version.Build.GitCommit,
+			BuildDate: kuma_version.Build.BuildDate,
+		},
+	})
+	if err != nil {
+		return err
+	}
 	return s.streamClient.Send(&envoy.DiscoveryRequest{
 		VersionInfo:   "",
 		ResponseNonce: "",
 		Node: &envoy_core.Node{
 			Id: s.clientId,
+			Metadata: &pstruct.Struct{
+				Fields: map[string]*pstruct.Value{
+					"version": {Kind: &pstruct.Value_StructValue{StructValue: cpVersion}},
+				},
+			},
 		},
 		ResourceNames: []string{},
 		TypeUrl:       string(resourceType),
diff --git a/pkg/kds/server/components.go b/pkg/kds/server/components.go
index 2f6bab0f1..c05a5c178 100644
--- a/pkg/kds/server/components.go
+++ b/pkg/kds/server/components.go
@@ -55,7 +55,7 @@ func DefaultStatusTracker(rt core_runtime.Runtime, log logr.Logger) StatusTracke
 				return time.NewTicker(rt.Config().Multizone.Global.KDS.ZoneInsightFlushInterval)
 			},
 			rt.Config().Multizone.Global.KDS.ZoneInsightFlushInterval/10,
-			NewDataplaneInsightStore(rt.ResourceManager()),
+			NewZonesInsightStore(rt.ResourceManager()),
 			l)
 	}, log)
 }
diff --git a/pkg/kds/server/status_sink.go b/pkg/kds/server/status_sink.go
index ff49ebf7a..e2db492c8 100644
--- a/pkg/kds/server/status_sink.go
+++ b/pkg/kds/server/status_sink.go
@@ -85,7 +85,7 @@ func (s *zoneInsightSink) Start(stop <-chan struct{}) {
 	}
 }
 
-func NewDataplaneInsightStore(resManager manager.ResourceManager) ZoneInsightStore {
+func NewZonesInsightStore(resManager manager.ResourceManager) ZoneInsightStore {
 	return &zoneInsightStore{resManager}
 }
 
diff --git a/pkg/kds/server/status_tracker.go b/pkg/kds/server/status_tracker.go
index a2e727d10..40ef5c252 100644
--- a/pkg/kds/server/status_tracker.go
+++ b/pkg/kds/server/status_tracker.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"sync"
 
+	pstruct "github.com/golang/protobuf/ptypes/struct"
+
 	"github.com/kumahq/kuma/pkg/core"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/kds/util"
@@ -72,6 +74,7 @@ func (c *statusTracker) OnStreamOpen(ctx context.Context, streamID int64, typ st
 		GlobalInstanceId: c.runtimeInfo.GetInstanceId(),
 		ConnectTime:      util_proto.MustTimestampProto(core.Now()),
 		Status:           system_proto.NewSubscriptionStatus(),
+		Version:          system_proto.NewVersion(),
 	}
 	// initialize state per ADS stream
 	state := &streamState{
@@ -117,9 +120,12 @@ func (c *statusTracker) OnStreamRequest(streamID int64, req *envoy.DiscoveryRequ
 	state.mu.Lock() // write access to the per Dataplane info
 	defer state.mu.Unlock()
 
-	// infer Dataplane id
+	// infer zone
 	if state.zone == "" {
 		state.zone = req.Node.Id
+		if err := readVersion(req.Node.GetMetadata(), state.subscription.Version); err != nil {
+			c.log.Error(err, "failed to extract version out of the Envoy metadata", "streamid", streamID, "metadata", req.Node.GetMetadata())
+		}
 		go c.createStatusSink(state, c.log).Start(state.stop)
 	}
 
@@ -175,3 +181,17 @@ func (s *streamState) GetStatus() (string, *system_proto.KDSSubscription) {
 func (s *streamState) Close() {
 	close(s.stop)
 }
+
+func readVersion(metadata *pstruct.Struct, version *system_proto.Version) error {
+	if metadata == nil {
+		return nil
+	}
+	rawVersion := metadata.Fields["version"].GetStructValue()
+	if rawVersion != nil {
+		err := util_proto.ToTyped(rawVersion, version)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/pkg/plugins/resources/k8s/native/go.sum b/pkg/plugins/resources/k8s/native/go.sum
index 411e7061e..c62fd831e 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=
@@ -24,6 +25,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 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 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 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=
@@ -46,6 +48,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=
@@ -72,6 +75,7 @@ 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=
@@ -80,7 +84,9 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
 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=
@@ -128,9 +134,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=
@@ -156,6 +164,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 github.com/google/go-cmp v0.5.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=
@@ -166,6 +175,7 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 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=
@@ -176,6 +186,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=
@@ -215,12 +226,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=
@@ -253,11 +267,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 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=
@@ -280,6 +297,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=
@@ -306,8 +324,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=
@@ -352,6 +373,7 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/Lt
 golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/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=
@@ -390,6 +412,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=
@@ -409,7 +432,9 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 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 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=
@@ -447,10 +472,12 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
 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=
@@ -483,9 +510,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=
@@ -494,6 +523,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT
 sigs.k8s.io/controller-runtime v0.6.4 h1:4013CKsBs5bEqo+LevzDett+LLxag/FjQWG94nVZ/9g=
 sigs.k8s.io/controller-runtime v0.6.4/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJUzRP4N4JpdAY=
 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/xds/envoy/clusters/configurers.go b/pkg/xds/envoy/clusters/configurers.go
index da29a0543..faf4b7f0b 100644
--- a/pkg/xds/envoy/clusters/configurers.go
+++ b/pkg/xds/envoy/clusters/configurers.go
@@ -1,6 +1,7 @@
 package clusters
 
 import (
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	mesh_core "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
@@ -134,6 +135,17 @@ func LbSubset(keySets [][]string) ClusterBuilderOptFunc {
 	}
 }
 
+func LB(lb *mesh_proto.TrafficRoute_LoadBalancer) ClusterBuilderOpt {
+	return ClusterBuilderOptFunc(func(config *ClusterBuilderConfig) {
+		config.AddV2(&v2.LbConfigurer{
+			Lb: lb,
+		})
+		config.AddV3(&v3.LbConfigurer{
+			Lb: lb,
+		})
+	})
+}
+
 func PassThroughCluster(name string) ClusterBuilderOpt {
 	return ClusterBuilderOptFunc(func(config *ClusterBuilderConfig) {
 		config.AddV2(&v2.PassThroughClusterConfigurer{
diff --git a/pkg/xds/envoy/clusters/v2/lb_configurer.go b/pkg/xds/envoy/clusters/v2/lb_configurer.go
new file mode 100644
index 000000000..f2ecdc7d1
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v2/lb_configurer.go
@@ -0,0 +1,71 @@
+package clusters
+
+import (
+	"fmt"
+
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+
+	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
+	"github.com/golang/protobuf/ptypes/wrappers"
+	"github.com/pkg/errors"
+)
+
+type LbConfigurer struct {
+	Lb *mesh_proto.TrafficRoute_LoadBalancer
+}
+
+var _ ClusterConfigurer = &LbConfigurer{}
+
+func (e *LbConfigurer) Configure(c *envoy_api.Cluster) error {
+	// default to Round Robin
+	if e.Lb.GetLbType() == nil {
+		c.LbPolicy = envoy_api.Cluster_ROUND_ROBIN
+		return nil
+	}
+
+	switch e.Lb.GetLbType().(type) {
+	case *mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_:
+		c.LbPolicy = envoy_api.Cluster_ROUND_ROBIN
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_:
+		c.LbPolicy = envoy_api.Cluster_LEAST_REQUEST
+
+		lbConfig := e.Lb.GetLeastRequest()
+		c.LbConfig = &envoy_api.Cluster_LeastRequestLbConfig_{
+			LeastRequestLbConfig: &envoy_api.Cluster_LeastRequestLbConfig{
+				ChoiceCount: &wrappers.UInt32Value{
+					Value: lbConfig.ChoiceCount,
+				},
+			},
+		}
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_RingHash_:
+		c.LbPolicy = envoy_api.Cluster_RING_HASH
+
+		lbConfig := e.Lb.GetRingHash()
+		hashfn, ok := envoy_api.Cluster_RingHashLbConfig_HashFunction_value[lbConfig.HashFunction]
+		if !ok {
+			return errors.New(fmt.Sprintf("Invalid ring hash function %s", lbConfig.HashFunction))
+		}
+
+		c.LbConfig = &envoy_api.Cluster_RingHashLbConfig_{
+			RingHashLbConfig: &envoy_api.Cluster_RingHashLbConfig{
+				HashFunction: envoy_api.Cluster_RingHashLbConfig_HashFunction(hashfn),
+				MinimumRingSize: &wrappers.UInt64Value{
+					Value: lbConfig.MinRingSize,
+				},
+				MaximumRingSize: &wrappers.UInt64Value{
+					Value: lbConfig.MaxRingSize,
+				},
+			},
+		}
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_Random_:
+		c.LbPolicy = envoy_api.Cluster_RANDOM
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_Maglev_:
+		c.LbPolicy = envoy_api.Cluster_MAGLEV
+	}
+
+	return nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/lb_configurer.go b/pkg/xds/envoy/clusters/v3/lb_configurer.go
new file mode 100644
index 000000000..a9e8fbb4c
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/lb_configurer.go
@@ -0,0 +1,71 @@
+package clusters
+
+import (
+	"fmt"
+
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+
+	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	"github.com/golang/protobuf/ptypes/wrappers"
+	"github.com/pkg/errors"
+)
+
+type LbConfigurer struct {
+	Lb *mesh_proto.TrafficRoute_LoadBalancer
+}
+
+var _ ClusterConfigurer = &LbConfigurer{}
+
+func (e *LbConfigurer) Configure(c *envoy_cluster.Cluster) error {
+	// default to Round Robin
+	if e.Lb.GetLbType() == nil {
+		c.LbPolicy = envoy_cluster.Cluster_ROUND_ROBIN
+		return nil
+	}
+
+	switch e.Lb.GetLbType().(type) {
+	case *mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_:
+		c.LbPolicy = envoy_cluster.Cluster_ROUND_ROBIN
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_:
+		c.LbPolicy = envoy_cluster.Cluster_LEAST_REQUEST
+
+		lbConfig := e.Lb.GetLeastRequest()
+		c.LbConfig = &envoy_cluster.Cluster_LeastRequestLbConfig_{
+			LeastRequestLbConfig: &envoy_cluster.Cluster_LeastRequestLbConfig{
+				ChoiceCount: &wrappers.UInt32Value{
+					Value: lbConfig.ChoiceCount,
+				},
+			},
+		}
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_RingHash_:
+		c.LbPolicy = envoy_cluster.Cluster_RING_HASH
+
+		lbConfig := e.Lb.GetRingHash()
+		hashfn, ok := envoy_cluster.Cluster_RingHashLbConfig_HashFunction_value[lbConfig.HashFunction]
+		if !ok {
+			return errors.New(fmt.Sprintf("Invalid ring hash function %s", lbConfig.HashFunction))
+		}
+
+		c.LbConfig = &envoy_cluster.Cluster_RingHashLbConfig_{
+			RingHashLbConfig: &envoy_cluster.Cluster_RingHashLbConfig{
+				HashFunction: envoy_cluster.Cluster_RingHashLbConfig_HashFunction(hashfn),
+				MinimumRingSize: &wrappers.UInt64Value{
+					Value: lbConfig.MinRingSize,
+				},
+				MaximumRingSize: &wrappers.UInt64Value{
+					Value: lbConfig.MaxRingSize,
+				},
+			},
+		}
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_Random_:
+		c.LbPolicy = envoy_cluster.Cluster_RANDOM
+
+	case *mesh_proto.TrafficRoute_LoadBalancer_Maglev_:
+		c.LbPolicy = envoy_cluster.Cluster_MAGLEV
+	}
+
+	return nil
+}
diff --git a/pkg/xds/envoy/types.go b/pkg/xds/envoy/types.go
index 26c84dc30..c2ae8635b 100644
--- a/pkg/xds/envoy/types.go
+++ b/pkg/xds/envoy/types.go
@@ -5,6 +5,8 @@ import (
 	"sort"
 	"strings"
 
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+
 	envoy_types "github.com/envoyproxy/go-control-plane/pkg/cache/types"
 	"github.com/pkg/errors"
 )
@@ -14,6 +16,7 @@ type ClusterSubset struct {
 	Weight            uint32
 	Tags              Tags
 	IsExternalService bool
+	Lb                *mesh_proto.TrafficRoute_LoadBalancer
 }
 
 type Tags map[string]string
@@ -87,6 +90,7 @@ func DistinctTags(tags []Tags) []Tags {
 type Cluster struct {
 	subsets            []ClusterSubset
 	hasExternalService bool
+	lb                 *mesh_proto.TrafficRoute_LoadBalancer
 }
 
 func (c *Cluster) Add(subset ClusterSubset) {
@@ -94,6 +98,7 @@ func (c *Cluster) Add(subset ClusterSubset) {
 	if subset.IsExternalService {
 		c.hasExternalService = true
 	}
+	c.lb = subset.Lb
 }
 
 func (c *Cluster) Tags() []Tags {
@@ -140,6 +145,10 @@ func (c Clusters) Tags(name string) []Tags {
 	return c[name].Tags()
 }
 
+func (c Clusters) Lb(name string) *mesh_proto.TrafficRoute_LoadBalancer {
+	return c[name].lb
+}
+
 type NamedResource interface {
 	envoy_types.Resource
 	GetName() string
diff --git a/pkg/xds/generator/outbound_proxy_generator.go b/pkg/xds/generator/outbound_proxy_generator.go
index ec81b805c..c5e239e19 100644
--- a/pkg/xds/generator/outbound_proxy_generator.go
+++ b/pkg/xds/generator/outbound_proxy_generator.go
@@ -155,6 +155,7 @@ func (o OutboundProxyGenerator) generateCDS(ctx xds_context.Context, proxy *mode
 	for _, clusterName := range clusters.ClusterNames() {
 		serviceName := clusters.Tags(clusterName)[0][kuma_mesh.ServiceTag]
 		tags := clusters.Tags(clusterName)
+		lb := clusters.Lb(clusterName)
 		healthCheck := proxy.Policies.HealthChecks[serviceName]
 		circuitBreaker := proxy.Policies.CircuitBreakers[serviceName]
 		edsClusterBuilder := envoy_clusters.NewClusterBuilder(proxy.APIVersion).
@@ -175,6 +176,7 @@ func (o OutboundProxyGenerator) generateCDS(ctx xds_context.Context, proxy *mode
 		} else {
 			edsClusterBuilder = edsClusterBuilder.
 				Configure(envoy_clusters.EdsCluster(clusterName)).
+				Configure(envoy_clusters.LB(lb)).
 				Configure(envoy_clusters.ClientSideMTLS(ctx, proxy.Metadata, serviceName, tags)).
 				Configure(envoy_clusters.Http2())
 		}
@@ -271,6 +273,8 @@ func (_ OutboundProxyGenerator) determineSubsets(proxy *model.Proxy, outbound *k
 			}
 		}
 
+		subset.Lb = route.Spec.GetConf().GetLoadBalancer()
+
 		subsets = append(subsets, subset)
 	}
 	return
diff --git a/pkg/xds/server/callbacks/dataplane_status_tracker.go b/pkg/xds/server/callbacks/dataplane_status_tracker.go
index eea8f9cbd..b1084ff51 100644
--- a/pkg/xds/server/callbacks/dataplane_status_tracker.go
+++ b/pkg/xds/server/callbacks/dataplane_status_tracker.go
@@ -182,6 +182,9 @@ func (s *streamState) Close() {
 }
 
 func readVersion(metadata *pstruct.Struct, version *mesh_proto.Version) error {
+	if metadata == nil {
+		return nil
+	}
 	rawVersion := metadata.Fields["version"].GetStructValue()
 	if rawVersion != nil {
 		err := util_proto.ToTyped(rawVersion, version)


Test patch:
diff --git a/app/kumactl/cmd/inspect/inspect_zones_test.go b/app/kumactl/cmd/inspect/inspect_zones_test.go
index e2ca26210..0c26c1dda 100644
--- a/app/kumactl/cmd/inspect/inspect_zones_test.go
+++ b/app/kumactl/cmd/inspect/inspect_zones_test.go
@@ -61,7 +61,6 @@ var _ = Describe("kumactl inspect zones", func() {
 		sampleZoneOverview = []*system_core.ZoneOverviewResource{
 			{
 				Meta: &test_model.ResourceMeta{
-					Mesh:             "default",
 					Name:             "zone-1",
 					CreationTime:     t1,
 					ModificationTime: now,
@@ -126,6 +125,14 @@ var _ = Describe("kumactl inspect zones", func() {
 										},
 									},
 								},
+								Version: &system_proto.Version{
+									KumaCp: &system_proto.KumaCpVersion{
+										Version:   "1.0.0",
+										GitTag:    "v1.0.0",
+										GitCommit: "91ce236824a9d875601679aa80c63783fb0e8725",
+										BuildDate: "2019-08-07T11:26:06Z",
+									},
+								},
 							},
 							{
 								Id:               "2",
@@ -137,6 +144,14 @@ var _ = Describe("kumactl inspect zones", func() {
 										ResponsesRejected: 2,
 									},
 								},
+								Version: &system_proto.Version{
+									KumaCp: &system_proto.KumaCpVersion{
+										Version:   "1.0.0",
+										GitTag:    "v1.0.0",
+										GitCommit: "91ce236824a9d875601679aa80c63783fb0e8725",
+										BuildDate: "2019-08-07T11:26:06Z",
+									},
+								},
 							},
 						},
 					},
@@ -144,7 +159,6 @@ var _ = Describe("kumactl inspect zones", func() {
 			},
 			{
 				Meta: &test_model.ResourceMeta{
-					Mesh:             "default",
 					Name:             "zone-2",
 					CreationTime:     t1,
 					ModificationTime: now,
@@ -171,7 +185,6 @@ var _ = Describe("kumactl inspect zones", func() {
 			},
 			{
 				Meta: &test_model.ResourceMeta{
-					Mesh:             "default",
 					Name:             "zone-3",
 					CreationTime:     t1,
 					ModificationTime: now,
@@ -184,6 +197,14 @@ var _ = Describe("kumactl inspect zones", func() {
 								Id:               "1",
 								GlobalInstanceId: "node-001",
 								ConnectTime:      util_proto.MustTimestampProto(t2),
+								Version: &system_proto.Version{
+									KumaCp: &system_proto.KumaCpVersion{
+										Version:   "1.0.0",
+										GitTag:    "v1.0.0",
+										GitCommit: "91ce236824a9d875601679aa80c63783fb0e8725",
+										BuildDate: "2019-08-07T11:26:06Z",
+									},
+								},
 							},
 						},
 					},
diff --git a/app/kumactl/cmd/inspect/testdata/inspect-zone.golden.yaml b/app/kumactl/cmd/inspect/testdata/inspect-zone.golden.yaml
index 531d97006..b13394f9c 100644
--- a/app/kumactl/cmd/inspect/testdata/inspect-zone.golden.yaml
+++ b/app/kumactl/cmd/inspect/testdata/inspect-zone.golden.yaml
@@ -48,6 +48,12 @@ items:
             total:
               responsesRejected: "11"
               responsesSent: "22"
+          version:
+            kumaCp:
+              buildDate: "2019-08-07T11:26:06Z"
+              gitCommit: 91ce236824a9d875601679aa80c63783fb0e8725
+              gitTag: v1.0.0
+              version: 1.0.0
         - connectTime: "2019-07-17T16:05:36.995Z"
           globalInstanceId: node-002
           id: "2"
@@ -55,6 +61,12 @@ items:
             total:
               responsesRejected: "2"
               responsesSent: "20"
+          version:
+            kumaCp:
+              buildDate: "2019-08-07T11:26:06Z"
+              gitCommit: 91ce236824a9d875601679aa80c63783fb0e8725
+              gitTag: v1.0.0
+              version: 1.0.0
   - creationTime: "2018-07-17T16:05:36.995Z"
     modificationTime: "2019-07-17T18:08:41Z"
     name: zone-2
@@ -80,5 +92,11 @@ items:
         - connectTime: "2019-07-17T16:05:36.995Z"
           globalInstanceId: node-001
           id: "1"
+          version:
+            kumaCp:
+              buildDate: "2019-08-07T11:26:06Z"
+              gitCommit: 91ce236824a9d875601679aa80c63783fb0e8725
+              gitTag: v1.0.0
+              version: 1.0.0
 next: null
 total: 3
diff --git a/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.json b/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.json
index 6a22496d2..121766cd4 100644
--- a/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.json
+++ b/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.json
@@ -66,6 +66,14 @@
                   "responsesRejected": "1"
                 }
               }
+            },
+            "version": {
+              "kumaCp": {
+                "version": "1.0.0",
+                "gitTag": "v1.0.0",
+                "gitCommit": "91ce236824a9d875601679aa80c63783fb0e8725",
+                "buildDate": "2019-08-07T11:26:06Z"
+              }
             }
           },
           {
@@ -77,6 +85,14 @@
                 "responsesSent": "20",
                 "responsesRejected": "2"
               }
+            },
+            "version": {
+              "kumaCp": {
+                "version": "1.0.0",
+                "gitTag": "v1.0.0",
+                "gitCommit": "91ce236824a9d875601679aa80c63783fb0e8725",
+                "buildDate": "2019-08-07T11:26:06Z"
+              }
             }
           }
         ]
@@ -120,11 +136,19 @@
           {
             "id": "1",
             "globalInstanceId": "node-001",
-            "connectTime": "2019-07-17T16:05:36.995Z"
+            "connectTime": "2019-07-17T16:05:36.995Z",
+            "version": {
+              "kumaCp": {
+                "version": "1.0.0",
+                "gitTag": "v1.0.0",
+                "gitCommit": "91ce236824a9d875601679aa80c63783fb0e8725",
+                "buildDate": "2019-08-07T11:26:06Z"
+              }
+            }
           }
         ]
       }
     }
   ],
   "next": null
-}
+}
\ No newline at end of file
diff --git a/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.txt b/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.txt
index 3ad1cb3e6..29b244563 100644
--- a/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.txt
+++ b/app/kumactl/cmd/inspect/testdata/inspect-zones.golden.txt
@@ -1,4 +1,4 @@
-MESH      NAME     STATUS    LAST CONNECTED AGO   LAST UPDATED AGO   TOTAL UPDATES   TOTAL ERRORS
-default   zone-1   Online    2h                   never              42              13
-default   zone-2   Offline   never                never              0               0
-default   zone-3   Offline   2h                   never              0               0
\ No newline at end of file
+NAME     STATUS    LAST CONNECTED AGO   LAST UPDATED AGO   TOTAL UPDATES   TOTAL ERRORS   REMOTE-CP VERSION
+zone-1   Online    2h                   never              42              13             1.0.0
+zone-2   Offline   never                never              0               0              
+zone-3   Offline   2h                   never              0               0              1.0.0
\ No newline at end of file
diff --git a/pkg/core/resources/apis/mesh/traffic_route_validator_test.go b/pkg/core/resources/apis/mesh/traffic_route_validator_test.go
index aefb4b9f2..ba5995836 100644
--- a/pkg/core/resources/apis/mesh/traffic_route_validator_test.go
+++ b/pkg/core/resources/apis/mesh/traffic_route_validator_test.go
@@ -161,6 +161,28 @@ var _ = Describe("TrafficRoute", func() {
                   message: must have at least one tag
                 - field: conf.split[1].destination
                   message: mandatory tag "kuma.io/service" is missing
+`,
+			}),
+			Entry("wrong ring hash function in the load balancer", testCase{
+				route: `
+                sources:
+                - match:
+                    kuma.io/service: '*'
+                destinations:
+                - match:
+                    kuma.io/service: '*'
+                conf:
+                  split:
+                  - destination:
+                      kuma.io/service: 'backend'
+                  loadBalancer:
+                    ringHash:
+                      hashFunction: 'INVALID_HASH_FUNCTION'
+`,
+				expected: `
+                violations:
+                - field: conf.loadBalancer.ringHash.hashFunction
+                  message: must have a valid hash function
 `,
 			}),
 		)
diff --git a/pkg/xds/envoy/clusters/v2/lb_configurer_test.go b/pkg/xds/envoy/clusters/v2/lb_configurer_test.go
new file mode 100644
index 000000000..c5abb7bef
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v2/lb_configurer_test.go
@@ -0,0 +1,123 @@
+package clusters_test
+
+import (
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	"github.com/kumahq/kuma/pkg/xds/envoy"
+	"github.com/kumahq/kuma/pkg/xds/envoy/clusters"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/extensions/table"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Lb", func() {
+
+	type testCase struct {
+		clusterName string
+		lb          *mesh_proto.TrafficRoute_LoadBalancer
+		expected    string
+	}
+
+	DescribeTable("should generate proper Envoy config",
+		func(given testCase) {
+			// when
+			cluster, err := clusters.NewClusterBuilder(envoy.APIV2).
+				Configure(clusters.EdsCluster(given.clusterName)).
+				Configure(clusters.LB(given.lb)).
+				Build()
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			actual, err := util_proto.ToYAML(cluster)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(actual).To(MatchYAML(given.expected))
+		},
+		Entry("round robin", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+            name: backend
+            type: EDS`,
+		}),
+		Entry("least request", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_{
+					LeastRequest: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest{
+						ChoiceCount: 4,
+					},
+				},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+            lbPolicy: LEAST_REQUEST
+            leastRequestLbConfig:
+              choiceCount: 4
+            name: backend
+            type: EDS`,
+		}),
+		Entry("ring hash", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_RingHash_{
+					RingHash: &mesh_proto.TrafficRoute_LoadBalancer_RingHash{
+						HashFunction: "MURMUR_HASH_2",
+						MinRingSize:  64,
+						MaxRingSize:  1024,
+					},
+				},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+            lbPolicy: RING_HASH
+            name: backend
+            ringHashLbConfig:
+              hashFunction: MURMUR_HASH_2
+              maximumRingSize: "1024"
+              minimumRingSize: "64"
+            type: EDS`,
+		}),
+		Entry("random", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_Random_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+            lbPolicy: RANDOM
+            name: backend
+            type: EDS`,
+		}),
+		Entry("random", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_Maglev_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+            lbPolicy: MAGLEV
+            name: backend
+            type: EDS`,
+		}),
+	)
+})
diff --git a/pkg/xds/envoy/clusters/v3/lb_configurer_test.go b/pkg/xds/envoy/clusters/v3/lb_configurer_test.go
new file mode 100644
index 000000000..16df6436c
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/lb_configurer_test.go
@@ -0,0 +1,128 @@
+package clusters_test
+
+import (
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	"github.com/kumahq/kuma/pkg/xds/envoy"
+	"github.com/kumahq/kuma/pkg/xds/envoy/clusters"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/extensions/table"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Lb", func() {
+
+	type testCase struct {
+		clusterName string
+		lb          *mesh_proto.TrafficRoute_LoadBalancer
+		expected    string
+	}
+
+	DescribeTable("should generate proper Envoy config",
+		func(given testCase) {
+			// when
+			cluster, err := clusters.NewClusterBuilder(envoy.APIV3).
+				Configure(clusters.EdsCluster(given.clusterName)).
+				Configure(clusters.LB(given.lb)).
+				Build()
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			actual, err := util_proto.ToYAML(cluster)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(actual).To(MatchYAML(given.expected))
+		},
+		Entry("round robin", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+                resourceApiVersion: V3
+            name: backend
+            type: EDS`,
+		}),
+		Entry("least request", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_{
+					LeastRequest: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest{
+						ChoiceCount: 4,
+					},
+				},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+                resourceApiVersion: V3
+            lbPolicy: LEAST_REQUEST
+            leastRequestLbConfig:
+              choiceCount: 4
+            name: backend
+            type: EDS`,
+		}),
+		Entry("ring hash", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_RingHash_{
+					RingHash: &mesh_proto.TrafficRoute_LoadBalancer_RingHash{
+						HashFunction: "MURMUR_HASH_2",
+						MinRingSize:  64,
+						MaxRingSize:  1024,
+					},
+				},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+                resourceApiVersion: V3
+            lbPolicy: RING_HASH
+            name: backend
+            ringHashLbConfig:
+              hashFunction: MURMUR_HASH_2
+              maximumRingSize: "1024"
+              minimumRingSize: "64"
+            type: EDS`,
+		}),
+		Entry("random", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_Random_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+                resourceApiVersion: V3
+            lbPolicy: RANDOM
+            name: backend
+            type: EDS`,
+		}),
+		Entry("random", testCase{
+			clusterName: "backend",
+			lb: &mesh_proto.TrafficRoute_LoadBalancer{
+				LbType: &mesh_proto.TrafficRoute_LoadBalancer_Maglev_{},
+			},
+			expected: `
+            connectTimeout: 5s
+            edsClusterConfig:
+              edsConfig:
+                ads: {}
+                resourceApiVersion: V3
+            lbPolicy: MAGLEV
+            name: backend
+            type: EDS`,
+		}),
+	)
+})
diff --git a/pkg/xds/generator/outbound_proxy_generator_test.go b/pkg/xds/generator/outbound_proxy_generator_test.go
index 0e629579a..66912dd35 100644
--- a/pkg/xds/generator/outbound_proxy_generator_test.go
+++ b/pkg/xds/generator/outbound_proxy_generator_test.go
@@ -181,6 +181,9 @@ var _ = Describe("OutboundProxyGenerator", func() {
 										Weight:      100,
 										Destination: mesh_proto.MatchService("api-http"),
 									}},
+									LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+										LbType: &mesh_proto.TrafficRoute_LoadBalancer_RoundRobin_{},
+									},
 								},
 							},
 						},
@@ -194,6 +197,13 @@ var _ = Describe("OutboundProxyGenerator", func() {
 										Weight:      100,
 										Destination: mesh_proto.MatchService("api-tcp"),
 									}},
+									LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+										LbType: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest_{
+											LeastRequest: &mesh_proto.TrafficRoute_LoadBalancer_LeastRequest{
+												ChoiceCount: 4,
+											},
+										},
+									},
 								},
 							},
 						},
@@ -207,6 +217,15 @@ var _ = Describe("OutboundProxyGenerator", func() {
 										Weight:      100,
 										Destination: mesh_proto.MatchService("api-http2"),
 									}},
+									LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+										LbType: &mesh_proto.TrafficRoute_LoadBalancer_RingHash_{
+											RingHash: &mesh_proto.TrafficRoute_LoadBalancer_RingHash{
+												HashFunction: "MURMUR_HASH_2",
+												MinRingSize:  64,
+												MaxRingSize:  1024,
+											},
+										},
+									},
 								},
 							},
 						},
@@ -220,6 +239,9 @@ var _ = Describe("OutboundProxyGenerator", func() {
 										Weight:      100,
 										Destination: mesh_proto.MatchService("api-grpc"),
 									}},
+									LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+										LbType: &mesh_proto.TrafficRoute_LoadBalancer_Random_{},
+									},
 								},
 							},
 						},
@@ -233,6 +255,9 @@ var _ = Describe("OutboundProxyGenerator", func() {
 										Weight:      100,
 										Destination: mesh_proto.MatchService("backend"),
 									}},
+									LoadBalancer: &mesh_proto.TrafficRoute_LoadBalancer{
+										LbType: &mesh_proto.TrafficRoute_LoadBalancer_Maglev_{},
+									},
 								},
 							},
 						},
diff --git a/pkg/xds/generator/testdata/outbound-proxy/03.envoy.golden.yaml b/pkg/xds/generator/testdata/outbound-proxy/03.envoy.golden.yaml
index 6c76b3b44..ef779ace7 100644
--- a/pkg/xds/generator/testdata/outbound-proxy/03.envoy.golden.yaml
+++ b/pkg/xds/generator/testdata/outbound-proxy/03.envoy.golden.yaml
@@ -151,6 +151,7 @@ resources:
         edsConfig:
           ads: { }
       http2ProtocolOptions: { }
+      lbPolicy: RANDOM
       name: api-grpc
       type: EDS
   - name: api-http
@@ -177,7 +178,12 @@ resources:
         edsConfig:
           ads: {}
       http2ProtocolOptions: {}
+      lbPolicy: RING_HASH
       name: api-http2
+      ringHashLbConfig:
+        hashFunction: MURMUR_HASH_2
+        maximumRingSize: "1024"
+        minimumRingSize: "64"
       type: EDS
   - name: api-tcp
     resource:
@@ -187,6 +193,9 @@ resources:
         edsConfig:
           ads: {}
       http2ProtocolOptions: {}
+      lbPolicy: LEAST_REQUEST
+      leastRequestLbConfig:
+        choiceCount: 4
       name: api-tcp
       type: EDS
   - name: backend
@@ -197,6 +206,7 @@ resources:
         edsConfig:
           ads: {}
       http2ProtocolOptions: {}
+      lbPolicy: MAGLEV
       name: backend
       type: EDS
   - name: db
diff --git a/pkg/xds/generator/testdata/outbound-proxy/04.envoy.golden.yaml b/pkg/xds/generator/testdata/outbound-proxy/04.envoy.golden.yaml
index e017342a2..ff94805c2 100644
--- a/pkg/xds/generator/testdata/outbound-proxy/04.envoy.golden.yaml
+++ b/pkg/xds/generator/testdata/outbound-proxy/04.envoy.golden.yaml
@@ -167,6 +167,9 @@ resources:
         edsConfig:
           ads: {}
       http2ProtocolOptions: {}
+      lbPolicy: LEAST_REQUEST
+      leastRequestLbConfig:
+        choiceCount: 4
       name: api-tcp
       transportSocket:
         name: envoy.transport_sockets.tls
@@ -213,6 +216,7 @@ resources:
         edsConfig:
           ads: {}
       http2ProtocolOptions: {}
+      lbPolicy: MAGLEV
       name: backend
       transportSocket:
         name: envoy.transport_sockets.tls
@@ -390,11 +394,11 @@ resources:
                   - name: envoy.filters.http.router
                 routeConfig:
                   name: outbound:api-http
-                  validateClusters: false
                   requestHeadersToAdd:
                     - header:
                         key: x-kuma-tags
                         value: '&kuma.io/service=web&'
+                  validateClusters: false
                   virtualHosts:
                     - domains:
                         - '*'