Repository: linkerd/linkerd2
Pull request #6333 (2021-06-21 20:41:54)

Update protoc
Update protoc from 3.6.0 to 3.15.7

Signed-off-by: Alex Leong <alex@buoyant.io>

<!--  Thanks for sending a pull request!

If you already have a well-structured git commit message, chances are GitHub
set the title and description of this PR to the git commit message subject and
body, respectively. If so, you may delete these instructions and submit your PR.

If this is your first time, please read our contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md

The title and description of your Pull Request should match the git commit
subject and body, respectively. Git commit messages are structured as follows:

```
Subject

Problem

Solution

Validation

Fixes #[GitHub issue ID]

DCO Sign off
```

Example git commit message:

```
Introduce Pull Request Template

GitHub's community guidelines recommend a pull request template, the repo was
lacking one.

Introduce a `PULL_REQUEST_TEMPLATE.md` file.

Once merged, the
[Community profile checklist](https://github.com/linkerd/linkerd2/community)
should indicate the repo now provides a pull request template.

Fixes #3321

Signed-off-by: Jane Smith <jane.smith@example.com>
```

Note the git commit message subject becomes the pull request title.

For more details around git commits, see the section on Committing in our
contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md#committing
-->



Patch:
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index a5270fe33..76338f386 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -13,10 +13,10 @@ The Linkerd maintainers are:
 
 The Linkerd Steering Committee members are:
 
-* Chris Campbell @campbel
-* Christian Hüning @christianhuening
-* Justin Turner @justin-turner-heb
-* William King @quentusrex
+* Chris Campbell @campbel (HP)
+* Christian Hüning @christianhuening (finleap connect)
+* Justin Turner @justin-turner-heb (HEB)
+* William King @quentusrex (Subspace)
 
 ## Emeriti
 
diff --git a/bin/protoc b/bin/protoc
index 0939a3f55..184d796ec 100755
--- a/bin/protoc
+++ b/bin/protoc
@@ -11,7 +11,7 @@ arch=$(uname -m)
 
 bindir=$( cd "${0%/*}" && pwd )
 targetbin=$( cd "$bindir"/.. && pwd )/target/bin
-protocversion=3.6.0
+protocversion=3.15.7
 protocbin=$targetbin/protoc-$protocversion
 protocurl=https://github.com/google/protobuf/releases/download/v$protocversion/protoc-$protocversion-$os-$arch.zip
 
diff --git a/bin/protoc-go.sh b/bin/protoc-go.sh
index 16bdde589..48a62cf5d 100755
--- a/bin/protoc-go.sh
+++ b/bin/protoc-go.sh
@@ -5,14 +5,17 @@ set -eu
 bindir=$( cd "${0%/*}" && pwd )
 
 go install -mod=readonly github.com/golang/protobuf/protoc-gen-go
+go install -mod=readonly google.golang.org/grpc/cmd/protoc-gen-go-grpc
 
 rm -rf controller/gen/common controller/gen/config viz/metrics-api/gen viz/tap/gen
 mkdir -p controller/gen/common/net viz/metrics-api/gen/viz viz/tap/gen/tap
 
-"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/common/net.proto
-"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/config/config.proto
-"$bindir"/protoc -I proto -I viz/metrics-api/proto --go_out=plugins=grpc,paths=source_relative:viz/metrics-api/gen viz/metrics-api/proto/viz.proto
-"$bindir"/protoc -I proto -I viz/tap/proto -I viz/metrics-api/proto --go_out=plugins=grpc,paths=source_relative:viz/tap/gen viz/tap/proto/viz_tap.proto
+"$bindir"/protoc -I proto --go_out=paths=source_relative:controller/gen proto/common/net.proto
+"$bindir"/protoc -I proto --go_out=paths=source_relative:controller/gen proto/config/config.proto
+"$bindir"/protoc -I proto -I viz/metrics-api/proto --go_out=paths=source_relative:viz/metrics-api/gen viz/metrics-api/proto/viz.proto
+"$bindir"/protoc -I proto -I viz/metrics-api/proto --go-grpc_out=paths=source_relative:viz/metrics-api/gen/viz viz/metrics-api/proto/viz.proto
+"$bindir"/protoc -I proto -I viz/tap/proto -I viz/metrics-api/proto --go_out=paths=source_relative:viz/tap/gen viz/tap/proto/viz_tap.proto
+"$bindir"/protoc -I proto -I viz/tap/proto -I viz/metrics-api/proto --go-grpc_out=paths=source_relative:viz/tap/gen/tap viz/tap/proto/viz_tap.proto
 
 mv controller/gen/common/net.pb.go   controller/gen/common/net/
 mv viz/metrics-api/gen/viz.pb.go viz/metrics-api/gen/viz/viz.pb.go
diff --git a/controller/gen/common/net/net.pb.go b/controller/gen/common/net/net.pb.go
index ceeeba61b..fc5f5979c 100644
--- a/controller/gen/common/net/net.pb.go
+++ b/controller/gen/common/net/net.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.26.0
-// 	protoc        v3.6.0
+// 	protoc        v3.15.7
 // source: common/net.proto
 
 package net
diff --git a/controller/gen/config/config.pb.go b/controller/gen/config/config.pb.go
index efbb46fcf..88675c9ef 100644
--- a/controller/gen/config/config.pb.go
+++ b/controller/gen/config/config.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.26.0
-// 	protoc        v3.6.0
+// 	protoc        v3.15.7
 // source: config/config.proto
 
 package config
diff --git a/go.mod b/go.mod
index b7174e078..3002b354f 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
 	github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b
 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
 	github.com/imdario/mergo v0.3.11
-	github.com/julienschmidt/httprouter v1.2.0
+	github.com/julienschmidt/httprouter v1.3.0
 	github.com/linkerd/linkerd2-proxy-api v0.1.18
 	github.com/linkerd/linkerd2-proxy-init v1.3.12
 	github.com/mattn/go-isatty v0.0.12
@@ -28,8 +28,8 @@ require (
 	github.com/onsi/gomega v1.8.1 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pkg/browser v0.0.0-20170505125900-c90ca0c84f15
-	github.com/prometheus/client_golang v1.7.1
-	github.com/prometheus/common v0.10.0
+	github.com/prometheus/client_golang v1.11.0
+	github.com/prometheus/common v0.26.0
 	github.com/sergi/go-diff v1.2.0
 	github.com/servicemeshinterface/smi-sdk-go v0.5.0
 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
@@ -41,6 +41,7 @@ require (
 	golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
 	golang.org/x/tools v0.1.3
 	google.golang.org/grpc v1.38.0
+	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
 	google.golang.org/protobuf v1.26.0
 	helm.sh/helm/v3 v3.6.1
 	k8s.io/api v0.21.2
diff --git a/go.sum b/go.sum
index 078a56a79..11f0a30b2 100644
--- a/go.sum
+++ b/go.sum
@@ -92,8 +92,9 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -273,6 +274,7 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -488,16 +490,20 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht
 github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 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=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -592,6 +598,8 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
 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=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
@@ -672,8 +680,9 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
-github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -688,8 +697,9 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
-github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
 github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -697,8 +707,9 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@@ -924,6 +935,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -994,6 +1006,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1003,8 +1016,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
@@ -1146,6 +1160,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
 google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/tools.go b/tools.go
index 5692c18b5..5dc96771e 100644
--- a/tools.go
+++ b/tools.go
@@ -6,4 +6,5 @@ import (
 	_ "github.com/shurcooL/vfsgen"
 	_ "golang.org/x/tools/cmd/goimports"
 	_ "k8s.io/code-generator"
+	_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
 )
diff --git a/viz/metrics-api/gen/viz/viz.pb.go b/viz/metrics-api/gen/viz/viz.pb.go
index 99924ebb6..b2a4972af 100644
--- a/viz/metrics-api/gen/viz/viz.pb.go
+++ b/viz/metrics-api/gen/viz/viz.pb.go
@@ -1,17 +1,13 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.26.0
-// 	protoc        v3.6.0
+// 	protoc        v3.15.7
 // source: viz.proto
 
 package viz
 
 import (
-	context "context"
 	duration "github.com/golang/protobuf/ptypes/duration"
-	grpc "google.golang.org/grpc"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -4513,299 +4509,3 @@ func file_viz_proto_init() {
 	file_viz_proto_goTypes = nil
 	file_viz_proto_depIdxs = nil
 }
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConnInterface
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion6
-
-// ApiClient is the client API for Api service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type ApiClient interface {
-	StatSummary(ctx context.Context, in *StatSummaryRequest, opts ...grpc.CallOption) (*StatSummaryResponse, error)
-	Edges(ctx context.Context, in *EdgesRequest, opts ...grpc.CallOption) (*EdgesResponse, error)
-	Gateways(ctx context.Context, in *GatewaysRequest, opts ...grpc.CallOption) (*GatewaysResponse, error)
-	TopRoutes(ctx context.Context, in *TopRoutesRequest, opts ...grpc.CallOption) (*TopRoutesResponse, error)
-	ListPods(ctx context.Context, in *ListPodsRequest, opts ...grpc.CallOption) (*ListPodsResponse, error)
-	ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error)
-	SelfCheck(ctx context.Context, in *SelfCheckRequest, opts ...grpc.CallOption) (*SelfCheckResponse, error)
-}
-
-type apiClient struct {
-	cc grpc.ClientConnInterface
-}
-
-func NewApiClient(cc grpc.ClientConnInterface) ApiClient {
-	return &apiClient{cc}
-}
-
-func (c *apiClient) StatSummary(ctx context.Context, in *StatSummaryRequest, opts ...grpc.CallOption) (*StatSummaryResponse, error) {
-	out := new(StatSummaryResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/StatSummary", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) Edges(ctx context.Context, in *EdgesRequest, opts ...grpc.CallOption) (*EdgesResponse, error) {
-	out := new(EdgesResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/Edges", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) Gateways(ctx context.Context, in *GatewaysRequest, opts ...grpc.CallOption) (*GatewaysResponse, error) {
-	out := new(GatewaysResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/Gateways", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) TopRoutes(ctx context.Context, in *TopRoutesRequest, opts ...grpc.CallOption) (*TopRoutesResponse, error) {
-	out := new(TopRoutesResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/TopRoutes", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) ListPods(ctx context.Context, in *ListPodsRequest, opts ...grpc.CallOption) (*ListPodsResponse, error) {
-	out := new(ListPodsResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/ListPods", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) {
-	out := new(ListServicesResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/ListServices", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *apiClient) SelfCheck(ctx context.Context, in *SelfCheckRequest, opts ...grpc.CallOption) (*SelfCheckResponse, error) {
-	out := new(SelfCheckResponse)
-	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/SelfCheck", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-// ApiServer is the server API for Api service.
-type ApiServer interface {
-	StatSummary(context.Context, *StatSummaryRequest) (*StatSummaryResponse, error)
-	Edges(context.Context, *EdgesRequest) (*EdgesResponse, error)
-	Gateways(context.Context, *GatewaysRequest) (*GatewaysResponse, error)
-	TopRoutes(context.Context, *TopRoutesRequest) (*TopRoutesResponse, error)
-	ListPods(context.Context, *ListPodsRequest) (*ListPodsResponse, error)
-	ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error)
-	SelfCheck(context.Context, *SelfCheckRequest) (*SelfCheckResponse, error)
-}
-
-// UnimplementedApiServer can be embedded to have forward compatible implementations.
-type UnimplementedApiServer struct {
-}
-
-func (*UnimplementedApiServer) StatSummary(context.Context, *StatSummaryRequest) (*StatSummaryResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method StatSummary not implemented")
-}
-func (*UnimplementedApiServer) Edges(context.Context, *EdgesRequest) (*EdgesResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Edges not implemented")
-}
-func (*UnimplementedApiServer) Gateways(context.Context, *GatewaysRequest) (*GatewaysResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Gateways not implemented")
-}
-func (*UnimplementedApiServer) TopRoutes(context.Context, *TopRoutesRequest) (*TopRoutesResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method TopRoutes not implemented")
-}
-func (*UnimplementedApiServer) ListPods(context.Context, *ListPodsRequest) (*ListPodsResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method ListPods not implemented")
-}
-func (*UnimplementedApiServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented")
-}
-func (*UnimplementedApiServer) SelfCheck(context.Context, *SelfCheckRequest) (*SelfCheckResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method SelfCheck not implemented")
-}
-
-func RegisterApiServer(s *grpc.Server, srv ApiServer) {
-	s.RegisterService(&_Api_serviceDesc, srv)
-}
-
-func _Api_StatSummary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(StatSummaryRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).StatSummary(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/StatSummary",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).StatSummary(ctx, req.(*StatSummaryRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_Edges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(EdgesRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).Edges(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/Edges",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).Edges(ctx, req.(*EdgesRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_Gateways_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(GatewaysRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).Gateways(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/Gateways",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).Gateways(ctx, req.(*GatewaysRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_TopRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(TopRoutesRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).TopRoutes(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/TopRoutes",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).TopRoutes(ctx, req.(*TopRoutesRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_ListPods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(ListPodsRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).ListPods(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/ListPods",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).ListPods(ctx, req.(*ListPodsRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(ListServicesRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).ListServices(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/ListServices",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).ListServices(ctx, req.(*ListServicesRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Api_SelfCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(SelfCheckRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ApiServer).SelfCheck(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/linkerd2.viz.Api/SelfCheck",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ApiServer).SelfCheck(ctx, req.(*SelfCheckRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-var _Api_serviceDesc = grpc.ServiceDesc{
-	ServiceName: "linkerd2.viz.Api",
-	HandlerType: (*ApiServer)(nil),
-	Methods: []grpc.MethodDesc{
-		{
-			MethodName: "StatSummary",
-			Handler:    _Api_StatSummary_Handler,
-		},
-		{
-			MethodName: "Edges",
-			Handler:    _Api_Edges_Handler,
-		},
-		{
-			MethodName: "Gateways",
-			Handler:    _Api_Gateways_Handler,
-		},
-		{
-			MethodName: "TopRoutes",
-			Handler:    _Api_TopRoutes_Handler,
-		},
-		{
-			MethodName: "ListPods",
-			Handler:    _Api_ListPods_Handler,
-		},
-		{
-			MethodName: "ListServices",
-			Handler:    _Api_ListServices_Handler,
-		},
-		{
-			MethodName: "SelfCheck",
-			Handler:    _Api_SelfCheck_Handler,
-		},
-	},
-	Streams:  []grpc.StreamDesc{},
-	Metadata: "viz.proto",
-}
diff --git a/viz/metrics-api/gen/viz/viz_grpc.pb.go b/viz/metrics-api/gen/viz/viz_grpc.pb.go
new file mode 100644
index 000000000..953b4166d
--- /dev/null
+++ b/viz/metrics-api/gen/viz/viz_grpc.pb.go
@@ -0,0 +1,317 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package viz
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// ApiClient is the client API for Api service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type ApiClient interface {
+	StatSummary(ctx context.Context, in *StatSummaryRequest, opts ...grpc.CallOption) (*StatSummaryResponse, error)
+	Edges(ctx context.Context, in *EdgesRequest, opts ...grpc.CallOption) (*EdgesResponse, error)
+	Gateways(ctx context.Context, in *GatewaysRequest, opts ...grpc.CallOption) (*GatewaysResponse, error)
+	TopRoutes(ctx context.Context, in *TopRoutesRequest, opts ...grpc.CallOption) (*TopRoutesResponse, error)
+	ListPods(ctx context.Context, in *ListPodsRequest, opts ...grpc.CallOption) (*ListPodsResponse, error)
+	ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error)
+	SelfCheck(ctx context.Context, in *SelfCheckRequest, opts ...grpc.CallOption) (*SelfCheckResponse, error)
+}
+
+type apiClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewApiClient(cc grpc.ClientConnInterface) ApiClient {
+	return &apiClient{cc}
+}
+
+func (c *apiClient) StatSummary(ctx context.Context, in *StatSummaryRequest, opts ...grpc.CallOption) (*StatSummaryResponse, error) {
+	out := new(StatSummaryResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/StatSummary", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) Edges(ctx context.Context, in *EdgesRequest, opts ...grpc.CallOption) (*EdgesResponse, error) {
+	out := new(EdgesResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/Edges", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) Gateways(ctx context.Context, in *GatewaysRequest, opts ...grpc.CallOption) (*GatewaysResponse, error) {
+	out := new(GatewaysResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/Gateways", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) TopRoutes(ctx context.Context, in *TopRoutesRequest, opts ...grpc.CallOption) (*TopRoutesResponse, error) {
+	out := new(TopRoutesResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/TopRoutes", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) ListPods(ctx context.Context, in *ListPodsRequest, opts ...grpc.CallOption) (*ListPodsResponse, error) {
+	out := new(ListPodsResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/ListPods", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) {
+	out := new(ListServicesResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/ListServices", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *apiClient) SelfCheck(ctx context.Context, in *SelfCheckRequest, opts ...grpc.CallOption) (*SelfCheckResponse, error) {
+	out := new(SelfCheckResponse)
+	err := c.cc.Invoke(ctx, "/linkerd2.viz.Api/SelfCheck", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// ApiServer is the server API for Api service.
+// All implementations must embed UnimplementedApiServer
+// for forward compatibility
+type ApiServer interface {
+	StatSummary(context.Context, *StatSummaryRequest) (*StatSummaryResponse, error)
+	Edges(context.Context, *EdgesRequest) (*EdgesResponse, error)
+	Gateways(context.Context, *GatewaysRequest) (*GatewaysResponse, error)
+	TopRoutes(context.Context, *TopRoutesRequest) (*TopRoutesResponse, error)
+	ListPods(context.Context, *ListPodsRequest) (*ListPodsResponse, error)
+	ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error)
+	SelfCheck(context.Context, *SelfCheckRequest) (*SelfCheckResponse, error)
+	mustEmbedUnimplementedApiServer()
+}
+
+// UnimplementedApiServer must be embedded to have forward compatible implementations.
+type UnimplementedApiServer struct {
+}
+
+func (UnimplementedApiServer) StatSummary(context.Context, *StatSummaryRequest) (*StatSummaryResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method StatSummary not implemented")
+}
+func (UnimplementedApiServer) Edges(context.Context, *EdgesRequest) (*EdgesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Edges not implemented")
+}
+func (UnimplementedApiServer) Gateways(context.Context, *GatewaysRequest) (*GatewaysResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Gateways not implemented")
+}
+func (UnimplementedApiServer) TopRoutes(context.Context, *TopRoutesRequest) (*TopRoutesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method TopRoutes not implemented")
+}
+func (UnimplementedApiServer) ListPods(context.Context, *ListPodsRequest) (*ListPodsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ListPods not implemented")
+}
+func (UnimplementedApiServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented")
+}
+func (UnimplementedApiServer) SelfCheck(context.Context, *SelfCheckRequest) (*SelfCheckResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SelfCheck not implemented")
+}
+func (UnimplementedApiServer) mustEmbedUnimplementedApiServer() {}
+
+// UnsafeApiServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ApiServer will
+// result in compilation errors.
+type UnsafeApiServer interface {
+	mustEmbedUnimplementedApiServer()
+}
+
+func RegisterApiServer(s grpc.ServiceRegistrar, srv ApiServer) {
+	s.RegisterService(&Api_ServiceDesc, srv)
+}
+
+func _Api_StatSummary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(StatSummaryRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).StatSummary(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/StatSummary",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).StatSummary(ctx, req.(*StatSummaryRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_Edges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(EdgesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).Edges(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/Edges",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).Edges(ctx, req.(*EdgesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_Gateways_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GatewaysRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).Gateways(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/Gateways",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).Gateways(ctx, req.(*GatewaysRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_TopRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(TopRoutesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).TopRoutes(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/TopRoutes",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).TopRoutes(ctx, req.(*TopRoutesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_ListPods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ListPodsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).ListPods(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/ListPods",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).ListPods(ctx, req.(*ListPodsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ListServicesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).ListServices(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/ListServices",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).ListServices(ctx, req.(*ListServicesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Api_SelfCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(SelfCheckRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ApiServer).SelfCheck(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/linkerd2.viz.Api/SelfCheck",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ApiServer).SelfCheck(ctx, req.(*SelfCheckRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// Api_ServiceDesc is the grpc.ServiceDesc for Api service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Api_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "linkerd2.viz.Api",
+	HandlerType: (*ApiServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "StatSummary",
+			Handler:    _Api_StatSummary_Handler,
+		},
+		{
+			MethodName: "Edges",
+			Handler:    _Api_Edges_Handler,
+		},
+		{
+			MethodName: "Gateways",
+			Handler:    _Api_Gateways_Handler,
+		},
+		{
+			MethodName: "TopRoutes",
+			Handler:    _Api_TopRoutes_Handler,
+		},
+		{
+			MethodName: "ListPods",
+			Handler:    _Api_ListPods_Handler,
+		},
+		{
+			MethodName: "ListServices",
+			Handler:    _Api_ListServices_Handler,
+		},
+		{
+			MethodName: "SelfCheck",
+			Handler:    _Api_SelfCheck_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "viz.proto",
+}
diff --git a/viz/metrics-api/grpc_server.go b/viz/metrics-api/grpc_server.go
index 62985a8d2..a5af5bf35 100644
--- a/viz/metrics-api/grpc_server.go
+++ b/viz/metrics-api/grpc_server.go
@@ -24,6 +24,7 @@ type Server interface {
 }
 
 type grpcServer struct {
+	pb.UnimplementedApiServer
 	prometheusAPI       promv1.API
 	k8sAPI              *k8s.API
 	controllerNamespace string
diff --git a/viz/tap/api/grpc_server.go b/viz/tap/api/grpc_server.go
index ac674e3ad..2ffb3a731 100644
--- a/viz/tap/api/grpc_server.go
+++ b/viz/tap/api/grpc_server.go
@@ -37,6 +37,7 @@ const defaultMaxRps = 100.0
 
 // GRPCTapServer describes the gRPC server implementing pb.TapServer
 type GRPCTapServer struct {
+	tapPb.UnimplementedTapServer
 	tapPort             uint
 	k8sAPI              *k8s.API
 	controllerNamespace string
diff --git a/viz/tap/gen/tap/viz_tap.pb.go b/viz/tap/gen/tap/viz_tap.pb.go
index bc492174f..20b0f8de8 100644
--- a/viz/tap/gen/tap/viz_tap.pb.go
+++ b/viz/tap/gen/tap/viz_tap.pb.go
@@ -1,19 +1,15 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.26.0
-// 	protoc        v3.6.0
+// 	protoc        v3.15.7
 // source: viz_tap.proto
 
 package tap
 
 import (
-	context "context"
 	duration "github.com/golang/protobuf/ptypes/duration"
 	net "github.com/linkerd/linkerd2/controller/gen/common/net"
 	viz "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
-	grpc "google.golang.org/grpc"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -1935,179 +1931,3 @@ func file_viz_tap_proto_init() {
 	file_viz_tap_proto_goTypes = nil
 	file_viz_tap_proto_depIdxs = nil
 }
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConnInterface
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion6
-
-// TapClient is the client API for Tap service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type TapClient interface {
-	// Deprecated: Do not use.
-	Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Tap_TapClient, error)
-	// Deprecated: Do not use.
-	TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Tap_TapByResourceClient, error)
-}
-
-type tapClient struct {
-	cc grpc.ClientConnInterface
-}
-
-func NewTapClient(cc grpc.ClientConnInterface) TapClient {
-	return &tapClient{cc}
-}
-
-// Deprecated: Do not use.
-func (c *tapClient) Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Tap_TapClient, error) {
-	stream, err := c.cc.NewStream(ctx, &_Tap_serviceDesc.Streams[0], "/linkerd2.tap.Tap/Tap", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &tapTapClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type Tap_TapClient interface {
-	Recv() (*TapEvent, error)
-	grpc.ClientStream
-}
-
-type tapTapClient struct {
-	grpc.ClientStream
-}
-
-func (x *tapTapClient) Recv() (*TapEvent, error) {
-	m := new(TapEvent)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// Deprecated: Do not use.
-func (c *tapClient) TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Tap_TapByResourceClient, error) {
-	stream, err := c.cc.NewStream(ctx, &_Tap_serviceDesc.Streams[1], "/linkerd2.tap.Tap/TapByResource", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &tapTapByResourceClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type Tap_TapByResourceClient interface {
-	Recv() (*TapEvent, error)
-	grpc.ClientStream
-}
-
-type tapTapByResourceClient struct {
-	grpc.ClientStream
-}
-
-func (x *tapTapByResourceClient) Recv() (*TapEvent, error) {
-	m := new(TapEvent)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// TapServer is the server API for Tap service.
-type TapServer interface {
-	// Deprecated: Do not use.
-	Tap(*TapRequest, Tap_TapServer) error
-	// Deprecated: Do not use.
-	TapByResource(*TapByResourceRequest, Tap_TapByResourceServer) error
-}
-
-// UnimplementedTapServer can be embedded to have forward compatible implementations.
-type UnimplementedTapServer struct {
-}
-
-func (*UnimplementedTapServer) Tap(*TapRequest, Tap_TapServer) error {
-	return status.Errorf(codes.Unimplemented, "method Tap not implemented")
-}
-func (*UnimplementedTapServer) TapByResource(*TapByResourceRequest, Tap_TapByResourceServer) error {
-	return status.Errorf(codes.Unimplemented, "method TapByResource not implemented")
-}
-
-func RegisterTapServer(s *grpc.Server, srv TapServer) {
-	s.RegisterService(&_Tap_serviceDesc, srv)
-}
-
-func _Tap_Tap_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(TapRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(TapServer).Tap(m, &tapTapServer{stream})
-}
-
-type Tap_TapServer interface {
-	Send(*TapEvent) error
-	grpc.ServerStream
-}
-
-type tapTapServer struct {
-	grpc.ServerStream
-}
-
-func (x *tapTapServer) Send(m *TapEvent) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func _Tap_TapByResource_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(TapByResourceRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(TapServer).TapByResource(m, &tapTapByResourceServer{stream})
-}
-
-type Tap_TapByResourceServer interface {
-	Send(*TapEvent) error
-	grpc.ServerStream
-}
-
-type tapTapByResourceServer struct {
-	grpc.ServerStream
-}
-
-func (x *tapTapByResourceServer) Send(m *TapEvent) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-var _Tap_serviceDesc = grpc.ServiceDesc{
-	ServiceName: "linkerd2.tap.Tap",
-	HandlerType: (*TapServer)(nil),
-	Methods:     []grpc.MethodDesc{},
-	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "Tap",
-			Handler:       _Tap_Tap_Handler,
-			ServerStreams: true,
-		},
-		{
-			StreamName:    "TapByResource",
-			Handler:       _Tap_TapByResource_Handler,
-			ServerStreams: true,
-		},
-	},
-	Metadata: "viz_tap.proto",
-}
diff --git a/viz/tap/gen/tap/viz_tap_grpc.pb.go b/viz/tap/gen/tap/viz_tap_grpc.pb.go
new file mode 100644
index 000000000..5085a7a2c
--- /dev/null
+++ b/viz/tap/gen/tap/viz_tap_grpc.pb.go
@@ -0,0 +1,197 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package tap
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// TapClient is the client API for Tap service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type TapClient interface {
+	// Deprecated: Do not use.
+	Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Tap_TapClient, error)
+	// Deprecated: Do not use.
+	TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Tap_TapByResourceClient, error)
+}
+
+type tapClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewTapClient(cc grpc.ClientConnInterface) TapClient {
+	return &tapClient{cc}
+}
+
+// Deprecated: Do not use.
+func (c *tapClient) Tap(ctx context.Context, in *TapRequest, opts ...grpc.CallOption) (Tap_TapClient, error) {
+	stream, err := c.cc.NewStream(ctx, &Tap_ServiceDesc.Streams[0], "/linkerd2.tap.Tap/Tap", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &tapTapClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type Tap_TapClient interface {
+	Recv() (*TapEvent, error)
+	grpc.ClientStream
+}
+
+type tapTapClient struct {
+	grpc.ClientStream
+}
+
+func (x *tapTapClient) Recv() (*TapEvent, error) {
+	m := new(TapEvent)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// Deprecated: Do not use.
+func (c *tapClient) TapByResource(ctx context.Context, in *TapByResourceRequest, opts ...grpc.CallOption) (Tap_TapByResourceClient, error) {
+	stream, err := c.cc.NewStream(ctx, &Tap_ServiceDesc.Streams[1], "/linkerd2.tap.Tap/TapByResource", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &tapTapByResourceClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type Tap_TapByResourceClient interface {
+	Recv() (*TapEvent, error)
+	grpc.ClientStream
+}
+
+type tapTapByResourceClient struct {
+	grpc.ClientStream
+}
+
+func (x *tapTapByResourceClient) Recv() (*TapEvent, error) {
+	m := new(TapEvent)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// TapServer is the server API for Tap service.
+// All implementations must embed UnimplementedTapServer
+// for forward compatibility
+type TapServer interface {
+	// Deprecated: Do not use.
+	Tap(*TapRequest, Tap_TapServer) error
+	// Deprecated: Do not use.
+	TapByResource(*TapByResourceRequest, Tap_TapByResourceServer) error
+	mustEmbedUnimplementedTapServer()
+}
+
+// UnimplementedTapServer must be embedded to have forward compatible implementations.
+type UnimplementedTapServer struct {
+}
+
+func (UnimplementedTapServer) Tap(*TapRequest, Tap_TapServer) error {
+	return status.Errorf(codes.Unimplemented, "method Tap not implemented")
+}
+func (UnimplementedTapServer) TapByResource(*TapByResourceRequest, Tap_TapByResourceServer) error {
+	return status.Errorf(codes.Unimplemented, "method TapByResource not implemented")
+}
+func (UnimplementedTapServer) mustEmbedUnimplementedTapServer() {}
+
+// UnsafeTapServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to TapServer will
+// result in compilation errors.
+type UnsafeTapServer interface {
+	mustEmbedUnimplementedTapServer()
+}
+
+func RegisterTapServer(s grpc.ServiceRegistrar, srv TapServer) {
+	s.RegisterService(&Tap_ServiceDesc, srv)
+}
+
+func _Tap_Tap_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(TapRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(TapServer).Tap(m, &tapTapServer{stream})
+}
+
+type Tap_TapServer interface {
+	Send(*TapEvent) error
+	grpc.ServerStream
+}
+
+type tapTapServer struct {
+	grpc.ServerStream
+}
+
+func (x *tapTapServer) Send(m *TapEvent) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func _Tap_TapByResource_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(TapByResourceRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(TapServer).TapByResource(m, &tapTapByResourceServer{stream})
+}
+
+type Tap_TapByResourceServer interface {
+	Send(*TapEvent) error
+	grpc.ServerStream
+}
+
+type tapTapByResourceServer struct {
+	grpc.ServerStream
+}
+
+func (x *tapTapByResourceServer) Send(m *TapEvent) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+// Tap_ServiceDesc is the grpc.ServiceDesc for Tap service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Tap_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "linkerd2.tap.Tap",
+	HandlerType: (*TapServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "Tap",
+			Handler:       _Tap_Tap_Handler,
+			ServerStreams: true,
+		},
+		{
+			StreamName:    "TapByResource",
+			Handler:       _Tap_TapByResource_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "viz_tap.proto",
+}
diff --git a/web/app/package.json b/web/app/package.json
index 91f6346b3..9af76c722 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -12,7 +12,7 @@
     "@lingui/macro": "2.9.2",
     "@lingui/react": "2.9.2",
     "@material-ui/core": "4.11.4",
-    "@material-ui/icons": "4.9.1",
+    "@material-ui/icons": "4.11.2",
     "@material-ui/lab": "^4.0.0-alpha.58",
     "classnames": "2.3.1",
     "css-mediaquery": "^0.1.2",
@@ -32,7 +32,7 @@
     "react-page-visibility": "^6.1.0",
     "react-router": "5.1.2",
     "react-router-dom": "5.1.2",
-    "react-router-prop-types": "1.0.4",
+    "react-router-prop-types": "1.0.5",
     "use-query-params": "1.1.1",
     "whatwg-fetch": "3.0.0"
   },
@@ -77,7 +77,7 @@
     "jest-enzyme": "7.1.2",
     "lodash-webpack-plugin": "^0.11.6",
     "react-test-renderer": "16.13.1",
-    "sinon": "9.0.2",
+    "sinon": "11.1.1",
     "sinon-stub-promise": "4.0.0",
     "style-loader": "^2.0.0",
     "url-loader": "^4.1.1",
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index 47035ddf7..77f4938f3 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -1431,10 +1431,10 @@
     react-is "^16.8.0 || ^17.0.0"
     react-transition-group "^4.4.0"
 
-"@material-ui/icons@4.9.1":
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.9.1.tgz#fdeadf8cb3d89208945b33dbc50c7c616d0bd665"
-  integrity sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==
+"@material-ui/icons@4.11.2":
+  version "4.11.2"
+  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5"
+  integrity sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==
   dependencies:
     "@babel/runtime" "^7.4.4"
 
@@ -1531,32 +1531,31 @@
   resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5"
   integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==
 
-"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2":
-  version "1.8.2"
-  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b"
-  integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==
+"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
+  integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
   dependencies:
     type-detect "4.0.8"
 
-"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
+"@sinonjs/fake-timers@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
   integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
   dependencies:
     "@sinonjs/commons" "^1.7.0"
 
-"@sinonjs/formatio@^5.0.1":
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089"
-  integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==
+"@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0":
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5"
+  integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==
   dependencies:
-    "@sinonjs/commons" "^1"
-    "@sinonjs/samsam" "^5.0.2"
+    "@sinonjs/commons" "^1.7.0"
 
-"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3":
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f"
-  integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==
+"@sinonjs/samsam@^6.0.2":
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.0.2.tgz#a0117d823260f282c04bff5f8704bdc2ac6910bb"
+  integrity sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==
   dependencies:
     "@sinonjs/commons" "^1.6.0"
     lodash.get "^4.4.2"
@@ -1754,7 +1753,7 @@
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0"
   integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==
 
-"@types/prop-types@*", "@types/prop-types@^15.5.3":
+"@types/prop-types@*", "@types/prop-types@^15.7.3":
   version "15.7.3"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
   integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
@@ -4242,10 +4241,10 @@ diff@3.5.0:
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
   integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
 
-diff@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
-  integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+diff@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
+  integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
 
 dir-glob@^3.0.1:
   version "3.0.1"
@@ -8170,13 +8169,13 @@ nice-try@^1.0.4:
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
-nise@^4.0.1:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
-  integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
+nise@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.0.tgz#713ef3ed138252daef20ec035ab62b7a28be645c"
+  integrity sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==
   dependencies:
     "@sinonjs/commons" "^1.7.0"
-    "@sinonjs/fake-timers" "^6.0.0"
+    "@sinonjs/fake-timers" "^7.0.4"
     "@sinonjs/text-encoding" "^0.7.1"
     just-extend "^4.0.2"
     path-to-regexp "^1.7.0"
@@ -9011,7 +9010,7 @@ prop-types-exact@^1.2.0:
     object.assign "^4.1.0"
     reflect.ownkeys "^0.2.0"
 
-prop-types@15.7.2, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@15.7.2, prop-types@^15.6.2, prop-types@^15.7.2:
   version "15.7.2"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
   integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9252,13 +9251,13 @@ react-router-dom@5.1.2:
     tiny-invariant "^1.0.2"
     tiny-warning "^1.0.0"
 
-react-router-prop-types@1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/react-router-prop-types/-/react-router-prop-types-1.0.4.tgz#74716e92d014bb51ee4376ee198f34903cf8dc1d"
-  integrity sha512-tXDQe0pnBTXjFivBa69hYibtgI3hz+Q/VmhMT7vIzYVPv574dd+lIuBIWOBH9dZkHeSuD6iur6zkmss3QVTeew==
+react-router-prop-types@1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/react-router-prop-types/-/react-router-prop-types-1.0.5.tgz#2e671d8412a793106bf70dc15c9ecc83ea4bc15b"
+  integrity sha512-q1xlFU2ol2U5zeVbA5hyBuxD3scHenqgMgCTuJQUanA2SyG8A3Fb1S6DleOo1cnGJB5Q05hnLge64kRj+xsuPA==
   dependencies:
-    "@types/prop-types" "^15.5.3"
-    prop-types "^15.6.1"
+    "@types/prop-types" "^15.7.3"
+    prop-types "^15.7.2"
 
 react-router@5.1.2:
   version "5.1.2"
@@ -10057,18 +10056,17 @@ sinon-stub-promise@4.0.0:
   resolved "https://registry.yarnpkg.com/sinon-stub-promise/-/sinon-stub-promise-4.0.0.tgz#6d498ba1198557cd01e3466af92dc7df725192c2"
   integrity sha1-bUmLoRmFV80B40Zq+S3H33JRksI=
 
-sinon@9.0.2:
-  version "9.0.2"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d"
-  integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==
+sinon@11.1.1:
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-11.1.1.tgz#99a295a8b6f0fadbbb7e004076f3ae54fc6eab91"
+  integrity sha512-ZSSmlkSyhUWbkF01Z9tEbxZLF/5tRC9eojCdFh33gtQaP7ITQVaMWQHGuFM7Cuf/KEfihuh1tTl3/ABju3AQMg==
   dependencies:
-    "@sinonjs/commons" "^1.7.2"
-    "@sinonjs/fake-timers" "^6.0.1"
-    "@sinonjs/formatio" "^5.0.1"
-    "@sinonjs/samsam" "^5.0.3"
-    diff "^4.0.2"
-    nise "^4.0.1"
-    supports-color "^7.1.0"
+    "@sinonjs/commons" "^1.8.3"
+    "@sinonjs/fake-timers" "^7.1.0"
+    "@sinonjs/samsam" "^6.0.2"
+    diff "^5.0.0"
+    nise "^5.1.0"
+    supports-color "^7.2.0"
 
 sirv@^1.0.7:
   version "1.0.11"
@@ -10511,7 +10509,7 @@ supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
   integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==


Test patch:
diff --git a/pkg/prometheus/test_helper.go b/pkg/prometheus/test_helper.go
index b5ab3be3b..6c59b3245 100644
--- a/pkg/prometheus/test_helper.go
+++ b/pkg/prometheus/test_helper.go
@@ -69,8 +69,8 @@ func (m *MockProm) Flags(ctx context.Context) (promv1.FlagsResult, error) {
 	return promv1.FlagsResult{}, nil
 }
 
-// LabelValues performs a query for the values of the given label.
-func (m *MockProm) LabelValues(ctx context.Context, label string, startTime time.Time, endTime time.Time) (model.LabelValues, promv1.Warnings, error) {
+// LabelValues performs a query for the values of the given label, time range and matchers.
+func (m *MockProm) LabelValues(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, promv1.Warnings, error) {
 	return nil, nil, nil
 }
 
@@ -92,8 +92,8 @@ func (m *MockProm) Targets(ctx context.Context) (promv1.TargetsResult, error) {
 	return promv1.TargetsResult{}, nil
 }
 
-// LabelNames returns all the unique label names present in the block in sorted order.
-func (m *MockProm) LabelNames(ctx context.Context, startTime time.Time, endTime time.Time) ([]string, promv1.Warnings, error) {
+// LabelNames returns the unique label names present in the block in sorted order by given time range and matchers.
+func (m *MockProm) LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, promv1.Warnings, error) {
 	return []string{}, nil, nil
 }
 
@@ -116,3 +116,18 @@ func (m *MockProm) Rules(ctx context.Context) (promv1.RulesResult, error) {
 func (m *MockProm) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]promv1.MetricMetadata, error) {
 	return []promv1.MetricMetadata{}, nil
 }
+
+// Buildinfo returns various build information properties about the Prometheus server
+func (m *MockProm) Buildinfo(ctx context.Context) (promv1.BuildinfoResult, error) {
+	return promv1.BuildinfoResult{}, nil
+}
+
+// QueryExemplars performs a query for exemplars by the given query and time range.
+func (m *MockProm) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]promv1.ExemplarQueryResult, error) {
+	return []promv1.ExemplarQueryResult{}, nil
+}
+
+// TSDB returns the cardinality statistics.
+func (m *MockProm) TSDB(ctx context.Context) (promv1.TSDBResult, error) {
+	return promv1.TSDBResult{}, nil
+}
diff --git a/viz/metrics-api/http_server_test.go b/viz/metrics-api/http_server_test.go
index d05bf279b..90a2cc703 100644
--- a/viz/metrics-api/http_server_test.go
+++ b/viz/metrics-api/http_server_test.go
@@ -19,6 +19,7 @@ type mockServer struct {
 }
 
 type mockGrpcServer struct {
+	pb.UnimplementedApiServer
 	mockServer
 }
 
diff --git a/web/app/js/components/BaseTable.test.js b/web/app/js/components/BaseTable.test.js
index 01f4384c0..cd6e1b4fc 100644
--- a/web/app/js/components/BaseTable.test.js
+++ b/web/app/js/components/BaseTable.test.js
@@ -2,6 +2,7 @@ import _merge from 'lodash/merge';
 import ApiHelpers from './util/ApiHelpers.jsx';
 import BaseTable from './BaseTable.jsx';
 import TableBody from '@material-ui/core/TableBody';
+import FilterListIcon from '@material-ui/icons/FilterList';
 import TableRow from '@material-ui/core/TableRow';
 import { routerWrap } from '../../test/testHelpers.jsx';
 import { mount } from 'enzyme';
@@ -107,7 +108,7 @@ describe("Tests for <BaseTable>", () => {
     const table = component.dive();
     expect(table.find(TableBody).find(TableRow)).toHaveLength(2);
     const enableFilter = component.prop("enableFilter");
-    const filterIcon = table.find("FilterListIcon");
+    const filterIcon = table.find(FilterListIcon);
     expect(enableFilter).toEqual(true);
     expect(filterIcon).toHaveLength(1);