Repository: linkerd/linkerd2
Pull request #7967 (2022-02-25 17:48:57)

Clarify that `multicluster link` is one direction
linkerd/website#1304

This explicitly states in `multicluster link` docs that the Link is one direction and should avoid any confusion about linking two clusters together so that they _both_ mirror each other.

The CLI docs are generated based off these descriptions and if there is a a `Long` field than that is used.

Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>



Patch:
diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml
index 1f3b0b3b4..2b3ae421c 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/codecov.yml
@@ -16,7 +16,7 @@ jobs:
     container:
       image: golang:1.17.3
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: go get gotest.tools/gotestsum@v0.4.2
       - run: gotestsum -- -cover -coverprofile=coverage.out -v -mod=readonly ./...
       - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b
@@ -31,7 +31,7 @@ jobs:
     container:
       image: node:14-stretch
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - name: Yarn setup
         run: bin/scurl -o- https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 --network-concurrency 1
       - name: Unit tests
@@ -53,7 +53,7 @@ jobs:
       image: docker://rust:1.59.0
       options: --security-opt seccomp=unconfined
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: mkdir -p target && cd target && bin/scurl -v https://github.com/xd009642/tarpaulin/releases/download/0.18.0/cargo-tarpaulin-0.18.0-travis.tar.gz | tar zxvf - && chmod 755 cargo-tarpaulin
       - run: target/cargo-tarpaulin tarpaulin --workspace --out Xml
       - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 01fe940b4..de683899e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -35,7 +35,7 @@ jobs:
 
     steps:
     - name: Checkout
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
 
     - name: Initialize
       # Unpinned action version so that we automatically get analyzer updates.
diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml
index 3c51fbd4a..f89a0e02c 100644
--- a/.github/workflows/devcontainer.yml
+++ b/.github/workflows/devcontainer.yml
@@ -16,13 +16,13 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: docker build . -f .devcontainer/Dockerfile
 
   rust-version:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: |
           versions=$(sed -nE 's/^ARG RUST_TOOLCHAIN=([^ ]+)/\1/p' .devcontainer/Dockerfile)
           ex=0
@@ -35,7 +35,7 @@ jobs:
   devcontainer-image:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: |
           export DEBIAN_FRONTEND=noninteractive
           sudo apt-get update
diff --git a/.github/workflows/policy_controller.yml b/.github/workflows/policy_controller.yml
index edc61deba..2be0a44ed 100644
--- a/.github/workflows/policy_controller.yml
+++ b/.github/workflows/policy_controller.yml
@@ -27,7 +27,7 @@ jobs:
     container:
       image: docker://rust:1.59.0
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: rustup component add rustfmt
       - run: cargo fmt --all -- --check
 
@@ -42,7 +42,7 @@ jobs:
     # Prevent sudden announcement of a new advisory from failing Ci.
     continue-on-error: ${{ matrix.checks == 'advisories' }}
     steps:
-    - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+    - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - uses: EmbarkStudios/cargo-deny-action@8acbae97b5d01b0481ae14cee8fcd8f5aa9e374d
       with:
         command: check ${{ matrix.checks }}
@@ -54,7 +54,7 @@ jobs:
       image: docker://rust:1.59.0
     steps:
       - run: rustup component add clippy
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: |
           bin/scurl -o /usr/local/bin/cargo-action-fmt "https://github.com/olix0r/cargo-action-fmt/releases/download/release%2F${CARGO_ACTION_FMT_VERSION}/cargo-action-fmt-x86_64-unknown-linux-gnu"
           chmod 755 /usr/local/bin/cargo-action-fmt
@@ -67,7 +67,7 @@ jobs:
     container:
       image: docker://rust:1.59.0
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: |
           bin/scurl -o /usr/local/bin/cargo-action-fmt "https://github.com/olix0r/cargo-action-fmt/releases/download/release%2F${CARGO_ACTION_FMT_VERSION}/cargo-action-fmt-x86_64-unknown-linux-gnu"
           chmod 755 /usr/local/bin/cargo-action-fmt
@@ -88,7 +88,7 @@ jobs:
     container:
       image: docker://rust:1.59.0
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: cargo fetch
       - run: cargo test --workspace --frozen --no-run
       - run: cargo test --workspace --frozen
@@ -98,7 +98,7 @@ jobs:
     runs-on: ubuntu-latest
     timeout-minutes: 2
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - run: |
           ex=0
 
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3b10ea11a..19401ffef 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
     timeout-minutes: 30
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - uses: ./.github/actions/docker-build
       with:
         docker-registry: ${{ env.DOCKER_REGISTRY }}
@@ -77,7 +77,7 @@ jobs:
     - name: Set up Docker Buildx
       uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - run: |
         . bin/_tag.sh
         echo "TAG=$(CI_FORCE_CLEAN=1 bin/root-tag)" >> $GITHUB_ENV
@@ -109,7 +109,7 @@ jobs:
     name: Policy controller manifest
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - run: |
         . bin/_tag.sh
         echo "TAG=$(CI_FORCE_CLEAN=1 bin/root-tag)" >> $GITHUB_ENV
@@ -139,12 +139,12 @@ jobs:
     needs: [docker_build]
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
-    - uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+    - uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab
       with:
         go-version: '1.17'
     - name: Download image archives
-      uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
+      uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
       with:
         name: image-archives
         path: image-archives
@@ -172,8 +172,8 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
-    - uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+    - uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab
       with:
         go-version: '1.17'
     - name: Set environment variables from scripts
@@ -198,8 +198,8 @@ jobs:
     needs: [docker_build, policy_controller_manifest]
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
-    - uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+    - uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab
       with:
         go-version: '1.17'
     - name: Install linkerd CLI
@@ -242,7 +242,7 @@ jobs:
     steps:
     - name: Checkout code
       if: startsWith(github.ref, 'refs/tags/stable')
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Chocolatey - update nuspec
       if: startsWith(github.ref, 'refs/tags/stable')
       run: |
@@ -270,7 +270,7 @@ jobs:
       contents: write
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Set environment variables from scripts
       run: |
         . bin/_tag.sh
@@ -279,7 +279,7 @@ jobs:
         extract_release_notes NOTES.md
     - name: Download choco package
       if: startsWith(github.ref, 'refs/tags/stable')
-      uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
+      uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
       with:
         name: choco
         path: choco
@@ -327,7 +327,7 @@ jobs:
     needs: [website_publish]
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Set environment variables from scripts
       run: |
         . bin/_tag.sh
@@ -357,7 +357,7 @@ jobs:
     needs: [gh_release]
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Log into GCP
       uses: 'google-github-actions/auth@8d125895b958610ec414ca4dae010257eaa814d3'
       with:
diff --git a/.github/workflows/rust_audit.yml b/.github/workflows/rust_audit.yml
index dc9cf8e46..ce7b205bc 100644
--- a/.github/workflows/rust_audit.yml
+++ b/.github/workflows/rust_audit.yml
@@ -41,7 +41,7 @@ jobs:
 
     name: RUSTSEC audit (${{ matrix.repo }})
     steps:
-      - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
         with:
           repository: linkerd/${{ matrix.repo }}
       - uses: olix0r/rustsecbot@560c817c7a019e1f0c2318721b55156409be7f8c
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 91b4a5ed4..f2ed2597d 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -20,7 +20,7 @@ jobs:
       options: --user root
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Go lint
       run: golangci-lint run --verbose --timeout=10m
   go_format:
@@ -31,7 +31,7 @@ jobs:
       image: golang:1.17.3
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Format
       run: bin/fmt
   proto_diff:
@@ -44,7 +44,7 @@ jobs:
       - name: Prerequisites
         run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install unzip
       - name: Checkout code
-        uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
       - name: Diff proto files
         run: bin/protoc-diff
   shellcheck:
@@ -53,7 +53,7 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: shellcheck
       # For more information on shellcheck failures:
       # https://github.com/koalaman/shellcheck/wiki/Checks
@@ -65,7 +65,7 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Chocolatey - lint
       uses: devblackops/github-action-psscriptanalyzer@854038567344559afaaa8ccb7a014452b99d86ee
       env:
@@ -80,7 +80,7 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Markdown lint
       run: bin/markdownlint-all
   chart_docs_diff:
@@ -88,6 +88,6 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Check docs for diff
       run: bin/helm-docs-diff
diff --git a/ADOPTERS.md b/ADOPTERS.md
index a47eebf2d..d2fc9e1f8 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -37,6 +37,7 @@
 - [Kurio](https://kurio.id)
 - [LeadIQ](https://leadiq.com)
 - [Lendico](https://www.lendico.de/)
+- [Livspace](https://www.livspace.com/)
 - [Lumoa](https://www.lumoa.me/)
 - [M1 Finance](https://www.m1finance.com/)
 - [manager.cl](https://www.manager.cl/)
diff --git a/Cargo.lock b/Cargo.lock
index 9b7e9b778..375395aea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -128,9 +128,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.1.2"
+version = "3.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5177fac1ab67102d8989464efd043c6ff44191b1557ec1ddd489b4f7e1447e77"
+checksum = "ced1892c55c910c1219e98d6fc8d71f6bddba7905866ce740066d8bfea859312"
 dependencies = [
  "bitflags",
  "clap_derive",
@@ -142,9 +142,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "3.1.0"
+version = "3.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72"
+checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
 dependencies = [
  "heck 0.4.0",
  "proc-macro-error",
@@ -238,11 +238,10 @@ dependencies = [
 
 [[package]]
 name = "drain"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9701380e51dff309f25d5b3d6639aa757d940821f7bc966faa58b65b5b7e4a18"
+checksum = "2f1a0abf3fcefad9b4dd0e414207a7408e12b68414a01e6bb19b897d5bd7632d"
 dependencies = [
- "futures",
  "tokio",
 ]
 
@@ -569,6 +568,17 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
+[[package]]
+name = "idna"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+dependencies = [
+ "matches",
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.8.0"
@@ -670,9 +680,12 @@ dependencies = [
  "base64",
  "bytes",
  "chrono",
+ "http",
+ "percent-encoding",
  "serde",
  "serde-value",
  "serde_json",
+ "url",
 ]
 
 [[package]]
@@ -713,7 +726,7 @@ dependencies = [
  "pem",
  "pin-project",
  "rustls",
- "rustls-pemfile",
+ "rustls-pemfile 0.2.1",
  "secrecy",
  "serde",
  "serde_json",
@@ -782,6 +795,31 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "kubert"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94e81a95b19fad6a076aabe1d5f9b711c4121b2888d418ca1fcca4f8841531ba"
+dependencies = [
+ "clap",
+ "drain",
+ "futures-core",
+ "futures-util",
+ "hyper",
+ "kube-client",
+ "kube-core",
+ "kube-runtime",
+ "pin-project-lite",
+ "rustls-pemfile 0.3.0",
+ "serde",
+ "thiserror",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "tracing",
+ "tracing-subscriber",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -810,18 +848,18 @@ dependencies = [
  "futures",
  "hyper",
  "jemallocator",
+ "k8s-openapi",
  "kube",
+ "kubert",
  "linkerd-policy-controller-core",
  "linkerd-policy-controller-grpc",
  "linkerd-policy-controller-k8s-api",
  "linkerd-policy-controller-k8s-index",
- "rustls",
- "rustls-pemfile",
+ "parking_lot 0.12.0",
  "serde_json",
+ "thiserror",
  "tokio",
- "tokio-rustls",
  "tracing",
- "tracing-subscriber",
 ]
 
 [[package]]
@@ -857,6 +895,7 @@ dependencies = [
  "futures",
  "k8s-openapi",
  "kube",
+ "parking_lot 0.12.0",
  "schemars",
  "serde",
  "serde_json",
@@ -874,6 +913,7 @@ dependencies = [
  "async-stream",
  "async-trait",
  "futures",
+ "kubert",
  "linkerd-policy-controller-core",
  "linkerd-policy-controller-k8s-api",
  "parking_lot 0.12.0",
@@ -1319,9 +1359,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
 dependencies = [
  "bitflags",
 ]
@@ -1405,7 +1445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943"
 dependencies = [
  "openssl-probe",
- "rustls-pemfile",
+ "rustls-pemfile 0.2.1",
  "schannel",
  "security-framework",
 ]
@@ -1419,6 +1459,15 @@ dependencies = [
  "base64",
 ]
 
+[[package]]
+name = "rustls-pemfile"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
+dependencies = [
+ "base64",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.9"
@@ -1652,9 +1701,9 @@ dependencies = [
 
 [[package]]
 name = "textwrap"
-version = "0.14.2"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
 
 [[package]]
 name = "thiserror"
@@ -1685,6 +1734,21 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "tinyvec"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+
 [[package]]
 name = "tokio"
 version = "1.17.0"
@@ -1982,6 +2046,21 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
+[[package]]
+name = "unicode-bidi"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+dependencies = [
+ "tinyvec",
+]
+
 [[package]]
 name = "unicode-segmentation"
 version = "1.9.0"
@@ -2000,6 +2079,18 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
+[[package]]
+name = "url"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "matches",
+ "percent-encoding",
+]
+
 [[package]]
 name = "valuable"
 version = "0.1.0"
@@ -2195,6 +2286,6 @@ dependencies = [
 
 [[package]]
 name = "zeroize"
-version = "1.5.2"
+version = "1.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006"
+checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608"
diff --git a/Cargo.toml b/Cargo.toml
index 8273bd281..aa3136c62 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ members = [
     "policy-controller/core",
     "policy-controller/grpc",
     "policy-controller/k8s/api",
-    "policy-controller/k8s/index"
+    "policy-controller/k8s/index",
 ]
 
 [profile.release]
diff --git a/charts/linkerd-control-plane/templates/destination.yaml b/charts/linkerd-control-plane/templates/destination.yaml
index 4472e342d..9ac467fb1 100644
--- a/charts/linkerd-control-plane/templates/destination.yaml
+++ b/charts/linkerd-control-plane/templates/destination.yaml
@@ -254,7 +254,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace={{.Release.Namespace}}
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks={{.Values.clusterNetworks}}
         - --identity-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}}
         - --default-policy={{.Values.policyController.defaultAllowPolicy}}
@@ -264,9 +266,8 @@ spec:
         imagePullPolicy: {{.Values.policyController.image.pullPolicy | default .Values.imagePullPolicy}}
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -284,6 +285,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         {{- $res := .Values.policyController.resources | default .Values.destinationResources }}
         {{- if $res }}
         {{- include "partials.resources" $res | nindent 8 }}
diff --git a/deny.toml b/deny.toml
index a10a3ed77..30fe1e93e 100644
--- a/deny.toml
+++ b/deny.toml
@@ -51,6 +51,8 @@ skip = [
     { name = "parking_lot", version = "0.11" },
     { name = "parking_lot_core", version = "0.8" },
     { name = "tokio-util", version = "0.6" },
+    # Waiting on kube
+    { name = "rustls-pemfile", version = "0.2" },
 ]
 
 [sources]
diff --git a/jaeger/charts/linkerd-jaeger/README.md b/jaeger/charts/linkerd-jaeger/README.md
index 8e783d668..e14cc7188 100644
--- a/jaeger/charts/linkerd-jaeger/README.md
+++ b/jaeger/charts/linkerd-jaeger/README.md
@@ -88,6 +88,7 @@ Kubernetes: `>=1.20.0-0`
 | collector.resources.memory.request | string | `nil` | Amount of memory that the collector container requests |
 | collector.tolerations | string | `nil` | Tolerations section, See the [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more information |
 | enablePSP | bool | `false` | Create Roles and RoleBindings to associate this extension's ServiceAccounts to the control plane PSP resource. This requires that `enabledPSP` is set to true on the control plane install. Note PSP has been deprecated since k8s v1.21 |
+| imagePullSecrets | list | `[]` | For Private docker registries, authentication is needed.  Registry secrets are applied to the respective service accounts |
 | jaeger.args | list | `["--query.base-path=/jaeger"]` | CLI arguments for Jaeger, See [Jaeger AIO Memory CLI reference](https://www.jaegertracing.io/docs/1.24/cli/#jaeger-all-in-one-memory) |
 | jaeger.enabled | bool | `true` | Set to false to exclude all-in-one Jaeger installation |
 | jaeger.image.name | string | `"jaegertracing/all-in-one"` |  |
diff --git a/jaeger/charts/linkerd-jaeger/templates/rbac.yaml b/jaeger/charts/linkerd-jaeger/templates/rbac.yaml
index 1c5d1ee9c..78afd7c6a 100644
--- a/jaeger/charts/linkerd-jaeger/templates/rbac.yaml
+++ b/jaeger/charts/linkerd-jaeger/templates/rbac.yaml
@@ -8,6 +8,7 @@ apiVersion: v1
 metadata:
   name: collector
   {{ include "partials.namespace" . }}
+{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }}
 {{ end -}}
 ---
 ###
@@ -45,6 +46,7 @@ apiVersion: v1
 metadata:
   name: jaeger-injector
   {{ include "partials.namespace" . }}
+{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }}
 ---
 {{- $host := printf "jaeger-injector.%s.svc" .Release.Namespace }}
 {{- $ca := genSelfSignedCert $host (list) (list $host) 365 }}
@@ -113,4 +115,5 @@ apiVersion: v1
 metadata:
   name: jaeger
   {{ include "partials.namespace" . }}
+{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }}
 {{ end -}}
diff --git a/jaeger/charts/linkerd-jaeger/values.yaml b/jaeger/charts/linkerd-jaeger/values.yaml
index f2bf8585d..74bd33b52 100644
--- a/jaeger/charts/linkerd-jaeger/values.yaml
+++ b/jaeger/charts/linkerd-jaeger/values.yaml
@@ -6,6 +6,11 @@ linkerdNamespace: linkerd
 nodeSelector: &default_node_selector
   kubernetes.io/os: linux
 
+# -- For Private docker registries, authentication is needed.
+#  Registry secrets are applied to the respective service accounts
+imagePullSecrets: []
+# - name: my-private-docker-registry-login-secret
+
 # -- Default tolerations section, See the
 # [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)
 # for more information
diff --git a/multicluster/cmd/link.go b/multicluster/cmd/link.go
index 79e284b5e..3626af890 100644
--- a/multicluster/cmd/link.go
+++ b/multicluster/cmd/link.go
@@ -61,7 +61,12 @@ func newLinkCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "link",
 		Short: "Outputs resources that allow another cluster to mirror services from this one",
-		Args:  cobra.NoArgs,
+		Long: `Outputs resources that allow another cluster to mirror services from this one.
+
+Note that the Link resource applies only in one direction. In order for two
+clusters to mirror each other, a Link resource will have to be generated for
+each cluster and applied to the other.`,
+		Args: cobra.NoArgs,
 		Example: `  # To link the west cluster to east
   linkerd --context=east multicluster link --cluster-name east | kubectl --context=west apply -f -
 
diff --git a/policy-controller/Cargo.toml b/policy-controller/Cargo.toml
index 3895df8a3..a23687ff4 100644
--- a/policy-controller/Cargo.toml
+++ b/policy-controller/Cargo.toml
@@ -15,33 +15,43 @@ anyhow = "1"
 clap = { version = "3", default-features = false, features = ["derive", "env", "std"] }
 drain = "0.1"
 futures = { version = "0.3", default-features = false }
+k8s-openapi = { version = "0.14", features = ["v1_20"] }
 hyper = { version = "0.14", features = ["http1", "http2", "runtime", "server"] }
-kube = { version = "0.69", default-features = false, features = ["admission", "client"] }
 linkerd-policy-controller-core = { path = "./core" }
 linkerd-policy-controller-grpc = { path = "./grpc" }
 linkerd-policy-controller-k8s-index = { path = "./k8s/index" }
 linkerd-policy-controller-k8s-api = { path = "./k8s/api" }
-rustls = { version = "0.20", default-features = false }
-rustls-pemfile = "0.2"
+parking_lot = "0.12"
 serde_json = "1"
-tokio = { version = "1", features = [
-    "fs",
+thiserror = "1"
+tracing = "0.1"
+
+[dependencies.kube]
+version = "0.69.1"
+default-features = false
+features = [
+    "admission",
+    "derive",
+]
+
+[dependencies.kubert]
+version = "0.3.0"
+default-features = false
+features = [
+    "clap",
+    "runtime",
+    "server",
+]
+
+[dependencies.tokio]
+version = "1"
+features = [
     "macros",
     "parking_lot",
     "rt",
     "rt-multi-thread",
     "signal",
-    "sync",
-] }
-tokio-rustls = { version = "0.23", default-features = false }
-tracing = "0.1"
-tracing-subscriber = { version = "0.3", default-features = false, features = [
-    "env-filter",
-    "fmt",
-    "json",
-    "smallvec",
-    "tracing-log",
-] }
+]
 
 [target.x86_64-unknown-linux-gnu.dependencies]
 jemallocator = "0.3"
diff --git a/policy-controller/amd64.dockerfile b/policy-controller/amd64.dockerfile
index c8f763bcf..22be04857 100644
--- a/policy-controller/amd64.dockerfile
+++ b/policy-controller/amd64.dockerfile
@@ -5,10 +5,14 @@ ARG RUNTIME_IMAGE=gcr.io/distroless/cc
 FROM $RUST_IMAGE as build
 ARG TARGETARCH
 WORKDIR /build
-COPY Cargo.toml Cargo.lock policy-controller/ /build/
+COPY Cargo.toml Cargo.lock .
+COPY policy-controller policy-controller
 RUN --mount=type=cache,target=target \
     --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
-    cargo build --locked --target=x86_64-unknown-linux-gnu --release --package=linkerd-policy-controller && \
+    cargo fetch --locked
+RUN --mount=type=cache,target=target \
+    --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
+    cargo build --frozen --target=x86_64-unknown-linux-gnu --release --package=linkerd-policy-controller && \
     mv target/x86_64-unknown-linux-gnu/release/linkerd-policy-controller /tmp/
 
 # Creates a minimal runtime image with the controller binary.
diff --git a/policy-controller/arm.dockerfile b/policy-controller/arm.dockerfile
index 62280949c..675584922 100644
--- a/policy-controller/arm.dockerfile
+++ b/policy-controller/arm.dockerfile
@@ -8,11 +8,15 @@ RUN apt-get update && \
     rustup target add armv7-unknown-linux-gnueabihf
 ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
 WORKDIR /build
-COPY Cargo.toml Cargo.lock policy-controller/ /build/
+COPY Cargo.toml Cargo.lock .
+COPY policy-controller policy-controller
+RUN --mount=type=cache,target=target \
+    --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
+    cargo fetch --locked
 # XXX(ver) we can't easily cross-compile against openssl, so use rustls on arm.
 RUN --mount=type=cache,target=target \
     --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
-    cargo build --locked --release --target=armv7-unknown-linux-gnueabihf \
+    cargo build --frozen --release --target=armv7-unknown-linux-gnueabihf \
         --package=linkerd-policy-controller --no-default-features --features="rustls-tls" && \
     mv target/armv7-unknown-linux-gnueabihf/release/linkerd-policy-controller /tmp/
 
diff --git a/policy-controller/arm64.dockerfile b/policy-controller/arm64.dockerfile
index d1c1956b3..26e3e6959 100644
--- a/policy-controller/arm64.dockerfile
+++ b/policy-controller/arm64.dockerfile
@@ -8,11 +8,15 @@ RUN apt-get update && \
     rustup target add aarch64-unknown-linux-gnu
 ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
 WORKDIR /build
-COPY Cargo.toml Cargo.lock policy-controller/ /build/
+COPY Cargo.toml Cargo.lock .
+COPY policy-controller policy-controller
+RUN --mount=type=cache,target=target \
+    --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
+    cargo fetch --locked
 # XXX(ver) we can't easily cross-compile against openssl, so use rustls on arm.
 RUN --mount=type=cache,target=target \
     --mount=type=cache,from=rust:1.59.0,source=/usr/local/cargo,target=/usr/local/cargo \
-    cargo build --locked --release --target=aarch64-unknown-linux-gnu \
+    cargo build --frozen --release --target=aarch64-unknown-linux-gnu \
         --package=linkerd-policy-controller --no-default-features --features="rustls-tls" && \
     mv target/aarch64-unknown-linux-gnu/release/linkerd-policy-controller /tmp/
 
diff --git a/policy-controller/k8s/api/Cargo.toml b/policy-controller/k8s/api/Cargo.toml
index 2aa70deac..7d2c4b375 100644
--- a/policy-controller/k8s/api/Cargo.toml
+++ b/policy-controller/k8s/api/Cargo.toml
@@ -9,6 +9,7 @@ publish = false
 futures = { version = "0.3", default-features = false }
 k8s-openapi = { version = "0.14", default-features = false, features = ["v1_20"] }
 kube = { version = "0.69", default-features = false, features = ["client", "derive", "runtime"] }
+parking_lot = "0.12"
 schemars = "0.8"
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
diff --git a/policy-controller/k8s/api/src/lib.rs b/policy-controller/k8s/api/src/lib.rs
index 9e60bfa42..6c8464698 100644
--- a/policy-controller/k8s/api/src/lib.rs
+++ b/policy-controller/k8s/api/src/lib.rs
@@ -3,55 +3,40 @@
 
 pub mod labels;
 pub mod policy;
-mod watch;
 
-pub use self::{
-    labels::Labels,
-    watch::{Event, Watch},
-};
+pub use self::{labels::Labels, watcher::Event};
+use futures::prelude::*;
 pub use k8s_openapi::api::{
     self,
     core::v1::{Namespace, Node, NodeSpec, Pod, PodSpec, PodStatus},
 };
 pub use kube::api::{ObjectMeta, ResourceExt};
-use kube::{
-    api::{Api, ListParams},
-    runtime::watcher,
-};
-use tracing::info_span;
-
-/// Resource watches.
-pub struct ResourceWatches {
-    pub pods_rx: Watch<Pod>,
-    pub servers_rx: Watch<policy::Server>,
-    pub authorizations_rx: Watch<policy::ServerAuthorization>,
-}
-
-// === impl ResourceWatches ===
-
-impl ResourceWatches {
-    /// Limits the amount of time a watch can be idle before being reset.
-    ///
-    /// Must be less than 295 or Kubernetes throws an error.
-    const DEFAULT_TIMEOUT_SECS: u32 = 290;
-}
-
-impl From<kube::Client> for ResourceWatches {
-    fn from(client: kube::Client) -> Self {
-        let params = ListParams::default().timeout(Self::DEFAULT_TIMEOUT_SECS);
-
-        // We only need to watch pods that are injected with a Linkerd sidecar because these are the
-        // only pods that can have inbound policy. We avoid indexing information about uninjected
-        // pods.
-        let pod_params = params.clone().labels("linkerd.io/control-plane-ns");
-
-        Self {
-            pods_rx: Watch::from(watcher(Api::all(client.clone()), pod_params))
-                .instrument(info_span!("pods")),
-            servers_rx: Watch::from(watcher(Api::all(client.clone()), params.clone()))
-                .instrument(info_span!("servers")),
-            authorizations_rx: Watch::from(watcher(Api::all(client), params))
-                .instrument(info_span!("serverauthorizations")),
-        }
+use kube::runtime::watcher;
+use parking_lot::Mutex;
+use std::sync::Arc;
+
+pub type EventResult<T> = watcher::Result<watcher::Event<T>>;
+
+/// Processes an `E`-typed event stream of `T`-typed resources.
+///
+/// The `F`-typed processor is called for each event with exclusive mutable accecss to an `S`-typed
+/// store.
+///
+/// The `H`-typed initialization handle is dropped after the first event is processed to signal to
+/// the application that the index has been updated.
+///
+/// It is assumed that the event stream is infinite. If an error is encountered, the stream is
+/// immediately polled again; and if this attempt to read from the stream fails, a backoff is
+/// employed before attempting to read from the stream again.
+pub async fn index<T, E, H, S, F>(events: E, store: Arc<Mutex<S>>, process: F)
+where
+    E: Stream<Item = watcher::Event<T>>,
+    F: Fn(&mut S, watcher::Event<T>),
+{
+    tokio::pin!(events);
+    while let Some(ev) = events.next().await {
+        process(&mut *store.lock(), ev);
     }
+
+    tracing::warn!("k8s event stream terminated");
 }
diff --git a/policy-controller/k8s/api/src/watch.rs b/policy-controller/k8s/api/src/watch.rs
deleted file mode 100644
index cccffe042..000000000
--- a/policy-controller/k8s/api/src/watch.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use futures::prelude::*;
-pub use kube::runtime::watcher::{Event, Result};
-use std::pin::Pin;
-use tokio::time;
-use tracing::{info, Instrument};
-
-/// Wraps an event stream that never terminates.
-pub struct Watch<T> {
-    initialized: bool,
-    span: tracing::Span,
-    rx: Pin<Box<dyn Stream<Item = Result<Event<T>>> + Send + 'static>>,
-}
-
-// === impl Watch ===
-
-impl<T, W> From<W> for Watch<T>
-where
-    W: Stream<Item = Result<Event<T>>> + Send + 'static,
-{
-    fn from(watch: W) -> Self {
-        Self::new(watch.boxed())
-    }
-}
-
-impl<T> Watch<T> {
-    pub fn new(rx: Pin<Box<dyn Stream<Item = Result<Event<T>>> + Send + 'static>>) -> Watch<T> {
-        Self {
-            rx,
-            initialized: false,
-            span: tracing::Span::current(),
-        }
-    }
-
-    pub fn instrument(mut self, span: tracing::Span) -> Self {
-        self.span = span;
-        self
-    }
-
-    pub fn is_initialized(&self) -> bool {
-        self.initialized
-    }
-
-    /// Receive the next event in the stream.
-    ///
-    /// If the stream fails, log the error and sleep for 1s before polling for a reset event.
-    pub async fn recv(&mut self) -> Event<T> {
-        loop {
-            let ev = self
-                .rx
-                .next()
-                .instrument(self.span.clone())
-                .await
-                .expect("stream must not terminate");
-
-            match ev {
-                Ok(ev) => {
-                    self.initialized = true;
-                    return ev;
-                }
-                Err(error) => {
-                    info!(parent: &self.span, %error, "Failed");
-
-                    // TODO(ver) this backoff may not be fully honored if this call to `recv` is
-                    // canceled. Instead, we should track the backoff on the `Watch` and poll it
-                    // before the inner stream. We probably need to use pin-project-lite for this,
-                    // though.
-                    time::sleep(time::Duration::from_secs(1)).await;
-                    info!(parent: &self.span, "Restarting");
-                }
-            }
-        }
-    }
-}
diff --git a/policy-controller/k8s/index/Cargo.toml b/policy-controller/k8s/index/Cargo.toml
index a4b61bcd0..78e48acba 100644
--- a/policy-controller/k8s/index/Cargo.toml
+++ b/policy-controller/k8s/index/Cargo.toml
@@ -11,6 +11,7 @@ anyhow = "1"
 async-stream = "0.3"
 async-trait = "0.1"
 futures = { version = "0.3", default-features = false }
+kubert = { version = "0.3.0", default-features = false, features = ["initialized"] }
 linkerd-policy-controller-core = { path = "../../core" }
 linkerd-policy-controller-k8s-api = { path = "../api" }
 parking_lot = "0.12"
diff --git a/policy-controller/k8s/index/src/authz.rs b/policy-controller/k8s/index/src/authz.rs
index 636f21f5d..d6a0b08bb 100644
--- a/policy-controller/k8s/index/src/authz.rs
+++ b/policy-controller/k8s/index/src/authz.rs
@@ -1,4 +1,4 @@
-use crate::{server::ServerSelector, ClusterInfo, Errors, Index, SrvIndex};
+use crate::{server::ServerSelector, ClusterInfo, Index, SrvIndex};
 use ahash::{AHashMap as HashMap, AHashSet as HashSet};
 use anyhow::{anyhow, bail, Result};
 use linkerd_policy_controller_core::{
@@ -10,7 +10,7 @@ use linkerd_policy_controller_k8s_api::{
     ResourceExt,
 };
 use std::collections::hash_map::Entry;
-use tracing::{debug, instrument, trace};
+use tracing::{debug, instrument, trace, warn};
 
 /// Indexes `ServerAuthorization` resources within a namespace.
 #[derive(Debug, Default)]
@@ -38,7 +38,7 @@ impl Index {
             name = %authz.name(),
         )
     )]
-    pub(crate) fn apply_authz(&mut self, authz: policy::ServerAuthorization) -> Result<()> {
+    pub(crate) fn apply_serverauthorization(&mut self, authz: policy::ServerAuthorization) {
         let ns = self
             .namespaces
             .get_or_default(authz.namespace().expect("namespace required"));
@@ -53,7 +53,7 @@ impl Index {
             name = %authz.name(),
         )
     )]
-    pub(crate) fn delete_authz(&mut self, authz: policy::ServerAuthorization) {
+    pub(crate) fn delete_serverauthorization(&mut self, authz: policy::ServerAuthorization) {
         if let Some(ns) = self
             .namespaces
             .index
@@ -66,7 +66,7 @@ impl Index {
     }
 
     #[instrument(skip(self, authzs))]
-    pub(crate) fn reset_authzs(&mut self, authzs: Vec<policy::ServerAuthorization>) -> Result<()> {
+    pub(crate) fn reset_serverauthorizations(&mut self, authzs: Vec<policy::ServerAuthorization>) {
         let mut prior = self
             .namespaces
             .index
@@ -77,15 +77,12 @@ impl Index {
             })
             .collect::<HashMap<_, _>>();
 
-        let mut errors = vec![];
         for authz in authzs.into_iter() {
             if let Some(ns) = prior.get_mut(authz.namespace().unwrap().as_str()) {
                 ns.remove(authz.name().as_str());
             }
 
-            if let Err(e) = self.apply_authz(authz) {
-                errors.push(e);
-            }
+            self.apply_serverauthorization(authz);
         }
 
         for (ns_name, authzs) in prior {
@@ -96,8 +93,6 @@ impl Index {
                 }
             }
         }
-
-        Errors::ok_if_empty(errors)
     }
 }
 
@@ -138,9 +133,15 @@ impl AuthzIndex {
         authz: policy::ServerAuthorization,
         servers: &mut SrvIndex,
         cluster: &ClusterInfo,
-    ) -> Result<()> {
+    ) {
         let name = authz.name();
-        let authz = mk_authz(authz, cluster)?;
+        let authz = match mk_serverauthorization(authz, cluster) {
+            Ok(authz) => authz,
+            Err(error) => {
+                warn!(saz = %name, %error);
+                return;
+            }
+        };
 
         match self.index.entry(name) {
             Entry::Vacant(entry) => {
@@ -156,8 +157,6 @@ impl AuthzIndex {
                 }
             }
         }
-
-        Ok(())
     }
 
     fn delete(&mut self, name: &str) {
@@ -166,7 +165,10 @@ impl AuthzIndex {
     }
 }
 
-fn mk_authz(srv: policy::authz::ServerAuthorization, cluster: &ClusterInfo) -> Result<Authz> {
+fn mk_serverauthorization(
+    srv: policy::authz::ServerAuthorization,
+    cluster: &ClusterInfo,
+) -> Result<Authz> {
     let policy::authz::ServerAuthorization { metadata, spec, .. } = srv;
 
     let servers = {
diff --git a/policy-controller/k8s/index/src/defaults.rs b/policy-controller/k8s/index/src/defaults.rs
index 5f2838159..dbd4be9c5 100644
--- a/policy-controller/k8s/index/src/defaults.rs
+++ b/policy-controller/k8s/index/src/defaults.rs
@@ -6,7 +6,7 @@ use linkerd_policy_controller_core::{
     ProxyProtocol,
 };
 use linkerd_policy_controller_k8s_api as k8s;
-use std::hash::Hash;
+use std::{collections::hash_map::Entry, hash::Hash};
 use tokio::{sync::watch, time};
 
 /// Indicates the default behavior to apply when no Server is found for a port.
@@ -130,7 +130,6 @@ impl DefaultPolicyWatches {
     ///
     /// If a watch for this policy does not already exist, one is created.
     pub fn watch(&mut self, default: DefaultPolicy, config: PortDefaults) -> ServerRx {
-        use std::collections::hash_map::Entry;
         match self.watches.entry((default, config)) {
             Entry::Occupied(entry) => entry.get().1.clone(),
             Entry::Vacant(entry) => {
diff --git a/policy-controller/k8s/index/src/lib.rs b/policy-controller/k8s/index/src/lib.rs
index 67883c40e..dfdd596f3 100644
--- a/policy-controller/k8s/index/src/lib.rs
+++ b/policy-controller/k8s/index/src/lib.rs
@@ -42,11 +42,12 @@ use self::{
     namespace::{Namespace, NamespaceIndex},
     server::SrvIndex,
 };
-use anyhow::Context;
+use futures::prelude::*;
 use linkerd_policy_controller_core::{InboundServer, IpNet};
-use linkerd_policy_controller_k8s_api::{self as k8s};
+use linkerd_policy_controller_k8s_api as k8s;
+use parking_lot::Mutex;
+use std::sync::Arc;
 use tokio::{sync::watch, time};
-use tracing::{debug, warn};
 
 /// Watches a server's configuration for server/authorization changes.
 type ServerRx = watch::Receiver<InboundServer>;
@@ -92,9 +93,6 @@ pub struct Index {
     lookups: lookup::Writer,
 }
 
-#[derive(Debug)]
-struct Errors(Vec<anyhow::Error>);
-
 // === impl Index ===
 
 impl Index {
@@ -120,98 +118,43 @@ impl Index {
         };
         (reader, idx)
     }
-
-    /// Drives indexing for all resource types.
-    ///
-    /// This is all driven on a single task, so it's not necessary for any of the indexing logic to
-    /// worry about concurrent access for the internal indexing structures.
-    ///
-    /// All updates are atomically published to the shared `lookups` map after indexing occurs; but
-    /// the indexing task is solely responsible for mutating it.
-    pub async fn run(
-        mut self,
-        resources: impl Into<k8s::ResourceWatches>,
-        ready_tx: watch::Sender<bool>,
-    ) {
-        let k8s::ResourceWatches {
-            mut pods_rx,
-            mut servers_rx,
-            mut authorizations_rx,
-        } = resources.into();
-
-        let mut initialized = false;
-        loop {
-            let res = tokio::select! {
-                // Track pods against the appropriate server.
-                up = pods_rx.recv() => match up {
-                    k8s::Event::Applied(pod) => self.apply_pod(pod).context("applying a pod"),
-                    k8s::Event::Deleted(pod) => self.delete_pod(pod).context("deleting a pod"),
-                    k8s::Event::Restarted(pods) => self.reset_pods(pods).context("resetting pods"),
-                },
-
-                // Track servers and link them with pods.
-                up = servers_rx.recv() => match up {
-                    k8s::Event::Applied(srv) => {
-                        self.apply_server(srv);
-                        Ok(())
-                    }
-                    k8s::Event::Deleted(srv) => self.delete_server(srv).context("deleting a server"),
-                    k8s::Event::Restarted(srvs) => self.reset_servers(srvs).context("resetting servers"),
-                },
-
-                // Track authorizations and update relevant servers.
-                up = authorizations_rx.recv() => match up {
-                    k8s::Event::Applied(authz) => self.apply_authz(authz).context("applying an authorization"),
-                    k8s::Event::Deleted(authz) => {
-                        self.delete_authz(authz);
-                        Ok(())
-                    }
-                    k8s::Event::Restarted(authzs) => self.reset_authzs(authzs).context("resetting authorizations"),
-                },
-            };
-
-            if let Err(error) = res {
-                warn!(?error);
-            }
-
-            // Notify the readiness watch once all watches have updated.
-            if !initialized
-                && pods_rx.is_initialized()
-                && servers_rx.is_initialized()
-                && authorizations_rx.is_initialized()
-            {
-                let _ = ready_tx.send(true);
-                initialized = true;
-                debug!("Ready");
-            }
-        }
-    }
 }
 
-// === impl Errors ===
-
-impl std::fmt::Display for Errors {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.0[0])?;
-        for e in &self.0[1..] {
-            write!(f, "; and {}", e)?;
+pub async fn index_pods(idx: Arc<Mutex<Index>>, events: impl Stream<Item = k8s::Event<k8s::Pod>>) {
+    tokio::pin!(events);
+    while let Some(ev) = events.next().await {
+        match ev {
+            k8s::Event::Applied(pod) => idx.lock().apply_pod(pod),
+            k8s::Event::Deleted(pod) => idx.lock().delete_pod(pod),
+            k8s::Event::Restarted(pods) => idx.lock().reset_pods(pods),
         }
-        Ok(())
     }
 }
 
-impl std::error::Error for Errors {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        Some(&*self.0[0])
+pub async fn index_servers(
+    idx: Arc<Mutex<Index>>,
+    events: impl Stream<Item = k8s::Event<k8s::policy::Server>>,
+) {
+    tokio::pin!(events);
+    while let Some(ev) = events.next().await {
+        match ev {
+            k8s::Event::Applied(srv) => idx.lock().apply_server(srv),
+            k8s::Event::Deleted(srv) => idx.lock().delete_server(srv),
+            k8s::Event::Restarted(srvs) => idx.lock().reset_servers(srvs),
+        }
     }
 }
 
-impl Errors {
-    fn ok_if_empty(errors: Vec<anyhow::Error>) -> anyhow::Result<()> {
-        if errors.is_empty() {
-            Ok(())
-        } else {
-            Err(Self(errors).into())
+pub async fn index_serverauthorizations(
+    idx: Arc<Mutex<Index>>,
+    events: impl Stream<Item = k8s::Event<k8s::policy::ServerAuthorization>>,
+) {
+    tokio::pin!(events);
+    while let Some(ev) = events.next().await {
+        match ev {
+            k8s::Event::Applied(saz) => idx.lock().apply_serverauthorization(saz),
+            k8s::Event::Deleted(saz) => idx.lock().delete_serverauthorization(saz),
+            k8s::Event::Restarted(sazs) => idx.lock().reset_serverauthorizations(sazs),
         }
     }
 }
diff --git a/policy-controller/k8s/index/src/lookup.rs b/policy-controller/k8s/index/src/lookup.rs
index e2207d92a..0eef32556 100644
--- a/policy-controller/k8s/index/src/lookup.rs
+++ b/policy-controller/k8s/index/src/lookup.rs
@@ -5,7 +5,7 @@ use linkerd_policy_controller_core::{DiscoverInboundServer, InboundServer, Inbou
 use parking_lot::RwLock;
 use std::{collections::hash_map::Entry, sync::Arc};
 
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 pub(crate) struct Writer(ByNs);
 
 // Supports lookups in a shared map of pod-ports.
diff --git a/policy-controller/k8s/index/src/namespace.rs b/policy-controller/k8s/index/src/namespace.rs
index a121ec767..1f90da572 100644
--- a/policy-controller/k8s/index/src/namespace.rs
+++ b/policy-controller/k8s/index/src/namespace.rs
@@ -39,7 +39,7 @@ impl NamespaceIndex {
         })
     }
 
-    pub fn iter(&self) -> impl Iterator<Item = (&String, &Namespace)> + ExactSizeIterator {
+    pub fn iter(&self) -> std::collections::hash_map::Iter<'_, String, Namespace> {
         self.index.iter()
     }
 }
diff --git a/policy-controller/k8s/index/src/pod.rs b/policy-controller/k8s/index/src/pod.rs
index a535d07e9..ea6348408 100644
--- a/policy-controller/k8s/index/src/pod.rs
+++ b/policy-controller/k8s/index/src/pod.rs
@@ -1,5 +1,5 @@
 use crate::{
-    defaults::PortDefaults, lookup, DefaultPolicy, DefaultPolicyWatches, Errors, Index, Namespace,
+    defaults::PortDefaults, lookup, DefaultPolicy, DefaultPolicyWatches, Index, Namespace,
     PodServerTx, ServerRx, SrvIndex,
 };
 use ahash::{AHashMap as HashMap, AHashSet as HashSet};
@@ -7,7 +7,7 @@ use anyhow::{anyhow, bail, Context, Result};
 use linkerd_policy_controller_k8s_api::{self as k8s, policy, ResourceExt};
 use std::collections::hash_map::Entry;
 use tokio::sync::watch;
-use tracing::{debug, instrument, trace};
+use tracing::{debug, instrument, trace, warn};
 
 /// Indexes pod state (within a namespace).
 #[derive(Debug, Default)]
@@ -75,7 +75,7 @@ impl Index {
             name = %pod.name(),
         )
     )]
-    pub(crate) fn apply_pod(&mut self, pod: k8s::Pod) -> Result<()> {
+    pub(crate) fn apply_pod(&mut self, pod: k8s::Pod) {
         let Namespace {
             default_policy,
             ref mut pods,
@@ -85,13 +85,15 @@ impl Index {
             .namespaces
             .get_or_default(pod.namespace().expect("namespace must be set"));
 
-        pods.apply(
+        if let Err(error) = pods.apply(
             pod,
             servers,
             &mut self.lookups,
             *default_policy,
             &mut self.default_policy_watches,
-        )
+        ) {
+            warn!(%error, "failed to apply pod");
+        }
     }
 
     #[instrument(
@@ -101,14 +103,15 @@ impl Index {
             name = %pod.name(),
         )
     )]
-    pub(crate) fn delete_pod(&mut self, pod: k8s::Pod) -> Result<()> {
-        let ns_name = pod.namespace().expect("namespace must be set");
-        let pod_name = pod.name();
-        self.rm_pod(ns_name.as_str(), pod_name.as_str())
+    pub(crate) fn delete_pod(&mut self, pod: k8s::Pod) {
+        self.rm_pod(
+            &*pod.namespace().expect("namespace must be set"),
+            pod.name().as_str(),
+        );
     }
 
     #[instrument(skip(self, pods))]
-    pub(crate) fn reset_pods(&mut self, pods: Vec<k8s::Pod>) -> Result<()> {
+    pub(crate) fn reset_pods(&mut self, pods: Vec<k8s::Pod>) {
         let mut prior_pods = self
             .namespaces
             .iter()
@@ -118,44 +121,37 @@ impl Index {
             })
             .collect::<HashMap<_, _>>();
 
-        let mut errors = vec![];
         for pod in pods.into_iter() {
             let ns_name = pod.namespace().unwrap();
             if let Some(ns) = prior_pods.get_mut(ns_name.as_str()) {
                 ns.remove(pod.name().as_str());
             }
 
-            if let Err(error) = self.apply_pod(pod) {
-                errors.push(error);
-            }
+            self.apply_pod(pod);
         }
 
         for (ns, pods) in prior_pods.into_iter() {
             for pod in pods.into_iter() {
-                if let Err(error) = self.rm_pod(ns.as_str(), pod.as_str()) {
-                    errors.push(error);
-                }
+                self.rm_pod(ns.as_str(), pod.as_str());
             }
         }
-
-        Errors::ok_if_empty(errors)
     }
 
-    fn rm_pod(&mut self, ns: &str, pod: &str) -> Result<()> {
-        self.namespaces
-            .index
-            .get_mut(ns)
-            .ok_or_else(|| anyhow!("namespace {} doesn't exist", ns))?
-            .pods
-            .index
-            .remove(pod)
-            .ok_or_else(|| anyhow!("pod {} doesn't exist", pod))?;
+    fn rm_pod(&mut self, ns: &str, pod: &str) {
+        match self.namespaces.index.get_mut(ns) {
+            None => warn!(%ns, "namespace doesn't exist"),
+            Some(idx) => {
+                if idx.pods.index.remove(pod).is_none() {
+                    warn!(%pod, "pod doesn't exist");
+                }
+            }
+        }
 
-        self.lookups.unset(ns, pod)?;
+        if let Err(error) = self.lookups.unset(ns, pod) {
+            warn!(%ns, %error, "failed to unset pod lookup");
+        }
 
         debug!("Removed pod");
-
-        Ok(())
     }
 }
 
@@ -350,7 +346,7 @@ impl Pod {
         if let Some(sn) = port.server_name.as_ref() {
             if sn != name {
                 debug_assert!(false, "Pod port must not match multiple servers");
-                tracing::warn!("Pod port matches multiple servers: {} and {}", sn, name);
+                warn!("Pod port matches multiple servers: {} and {}", sn, name);
             }
             // If the name matched there's no use in proceeding with a redundant update
             return;
diff --git a/policy-controller/k8s/index/src/server.rs b/policy-controller/k8s/index/src/server.rs
index c9b0c5336..600f9d920 100644
--- a/policy-controller/k8s/index/src/server.rs
+++ b/policy-controller/k8s/index/src/server.rs
@@ -1,11 +1,10 @@
-use crate::{authz::AuthzIndex, Errors, Index, Namespace, ServerRx, ServerTx};
+use crate::{authz::AuthzIndex, Index, Namespace, ServerRx, ServerTx};
 use ahash::{AHashMap as HashMap, AHashSet as HashSet};
-use anyhow::{anyhow, bail, Result};
 use linkerd_policy_controller_core::{ClientAuthorization, InboundServer, ProxyProtocol};
 use linkerd_policy_controller_k8s_api::{self as k8s, policy, ResourceExt};
 use std::{collections::hash_map::Entry, sync::Arc};
 use tokio::{sync::watch, time};
-use tracing::{debug, instrument, trace};
+use tracing::{debug, instrument, trace, warn};
 
 /// Holds the state of all `Server`s in a namespace.
 #[derive(Debug, Default)]
@@ -79,30 +78,34 @@ impl Index {
             name = %srv.name(),
         )
     )]
-    pub(crate) fn delete_server(&mut self, srv: policy::Server) -> Result<()> {
-        let ns_name = srv.namespace().expect("servers must be namespaced");
-        self.rm_server(ns_name.as_str(), srv.name().as_str())
+    pub(crate) fn delete_server(&mut self, srv: policy::Server) {
+        self.rm_server(
+            &*srv.namespace().expect("servers must be namespaced"),
+            &*srv.name(),
+        );
     }
 
-    fn rm_server(&mut self, ns_name: &str, srv_name: &str) -> Result<()> {
-        let ns =
-            self.namespaces.index.get_mut(ns_name).ok_or_else(|| {
-                anyhow!("removing server from non-existent namespace {}", ns_name)
-            })?;
+    fn rm_server(&mut self, ns_name: &str, srv_name: &str) {
+        let ns = match self.namespaces.index.get_mut(ns_name) {
+            Some(ns) => ns,
+            None => {
+                warn!(name = %ns_name, "removing server from non-existent namespace");
+                return;
+            }
+        };
 
         if ns.servers.index.remove(srv_name).is_none() {
-            bail!("removing non-existent server {}", srv_name);
+            warn!(name = %srv_name, "unknown server");
         }
 
         // Reset the server config for all pods that were using this server.
         ns.pods.reset_server(srv_name);
 
         debug!("Removed server");
-        Ok(())
     }
 
     #[instrument(skip(self, srvs))]
-    pub(crate) fn reset_servers(&mut self, srvs: Vec<policy::Server>) -> Result<()> {
+    pub(crate) fn reset_servers(&mut self, srvs: Vec<policy::Server>) {
         let mut prior_servers = self
             .namespaces
             .index
@@ -113,7 +116,6 @@ impl Index {
             })
             .collect::<HashMap<_, _>>();
 
-        let mut errors = vec![];
         for srv in srvs.into_iter() {
             let ns_name = srv.namespace().expect("namespace must be set");
             if let Some(ns) = prior_servers.get_mut(&ns_name) {
@@ -125,13 +127,9 @@ impl Index {
 
         for (ns_name, ns_servers) in prior_servers.into_iter() {
             for srv_name in ns_servers.into_iter() {
-                if let Err(e) = self.rm_server(ns_name.as_str(), &srv_name) {
-                    errors.push(e);
-                }
+                self.rm_server(ns_name.as_str(), &srv_name);
             }
         }
-
-        Errors::ok_if_empty(errors)
     }
 }
 
diff --git a/policy-controller/src/admin.rs b/policy-controller/src/admin.rs
deleted file mode 100644
index a57fdd20e..000000000
--- a/policy-controller/src/admin.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use futures::future;
-use hyper::{Body, Request, Response};
-use std::net::SocketAddr;
-use tokio::sync::watch;
-use tracing::{info, instrument};
-
-#[instrument(skip(ready))]
-pub async fn serve(addr: SocketAddr, ready: watch::Receiver<bool>) -> Result<(), hyper::Error> {
-    let server =
-        hyper::server::Server::bind(&addr).serve(hyper::service::make_service_fn(move |_conn| {
-            let ready = ready.clone();
-            future::ok::<_, hyper::Error>(hyper::service::service_fn(
-                move |req: hyper::Request<hyper::Body>| match req.uri().path() {
-                    "/ready" => future::ok(handle_ready(&ready, req)),
-                    _ => future::ok::<_, hyper::Error>(
-                        hyper::Response::builder()
-                            .status(hyper::StatusCode::NOT_FOUND)
-                            .body(hyper::Body::default())
-                            .unwrap(),
-                    ),
-                },
-            ))
-        }));
-    let addr = server.local_addr();
-    info!(%addr, "HTTP admin server listening");
-    server.await
-}
-
-fn handle_ready(ready: &watch::Receiver<bool>, req: Request<Body>) -> Response<Body> {
-    match *req.method() {
-        hyper::Method::GET | hyper::Method::HEAD => {
-            if *ready.borrow() {
-                Response::builder()
-                    .status(hyper::StatusCode::OK)
-                    .header(hyper::header::CONTENT_TYPE, "text/plain")
-                    .body("ready\n".into())
-                    .unwrap()
-            } else {
-                Response::builder()
-                    .status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
-                    .header(hyper::header::CONTENT_TYPE, "text/plain")
-                    .body("not ready\n".into())
-                    .unwrap()
-            }
-        }
-        _ => Response::builder()
-            .status(hyper::StatusCode::METHOD_NOT_ALLOWED)
-            .body(Body::default())
-            .unwrap(),
-    }
-}
diff --git a/policy-controller/src/admission.rs b/policy-controller/src/admission.rs
index ec6e8dffd..55415e3de 100644
--- a/policy-controller/src/admission.rs
+++ b/policy-controller/src/admission.rs
@@ -1,11 +1,11 @@
 use crate::api;
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, bail, Result};
 use api::policy::ServerSpec;
 use futures::future;
 use hyper::{body::Buf, http, Body, Request, Response};
-use kube::{api::Api, core::DynamicObject, ResourceExt};
-use std::convert::TryInto;
+use kube::{core::DynamicObject, Api, ResourceExt};
 use std::task;
+use thiserror::Error;
 use tracing::{debug, info, warn};
 
 #[derive(Clone)]
@@ -13,12 +13,21 @@ pub struct Service {
     pub client: kube::Client,
 }
 
+#[derive(Debug, Error)]
+pub enum Error {
+    #[error("failed to read request body: {0}")]
+    Request(#[from] hyper::Error),
+
+    #[error("failed to encode json response: {0}")]
+    Json(#[from] serde_json::Error),
+}
+
 impl hyper::service::Service<Request<Body>> for Service {
     type Response = Response<Body>;
-    type Error = anyhow::Error;
-    type Future = future::BoxFuture<'static, Result<Response<Body>>>;
+    type Error = Error;
+    type Future = future::BoxFuture<'static, Result<Response<Body>, Error>>;
 
-    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> task::Poll<Result<()>> {
+    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> task::Poll<Result<(), Error>> {
         task::Poll::Ready(Ok(()))
     }
 
@@ -93,8 +102,8 @@ impl hyper::service::Service<Request<Body>> for Service {
     }
 }
 
-fn json_response(rsp: AdmissionReview) -> Result<Response<Body>> {
-    let bytes = serde_json::to_vec(&rsp).context("failed to encode admission review as JSON")?;
+fn json_response(rsp: AdmissionReview) -> Result<Response<Body>, Error> {
+    let bytes = serde_json::to_vec(&rsp)?;
     Ok(Response::builder()
         .status(http::StatusCode::OK)
         .header(http::header::CONTENT_TYPE, "application/json")
diff --git a/policy-controller/src/lib.rs b/policy-controller/src/lib.rs
index 1561dca24..a8e129af6 100644
--- a/policy-controller/src/lib.rs
+++ b/policy-controller/src/lib.rs
@@ -1,7 +1,6 @@
 #![deny(warnings, rust_2018_idioms)]
 #![forbid(unsafe_code)]
 
-pub mod admin;
 pub mod admission;
 
 pub use linkerd_policy_controller_grpc as grpc;
diff --git a/policy-controller/src/main.rs b/policy-controller/src/main.rs
index 2616e8fd8..a8a0b72da 100644
--- a/policy-controller/src/main.rs
+++ b/policy-controller/src/main.rs
@@ -1,27 +1,23 @@
 #![deny(warnings, rust_2018_idioms)]
 #![forbid(unsafe_code)]
 
-use anyhow::{anyhow, bail, Context, Error, Result};
+use anyhow::{bail, Result};
 use clap::Parser;
-use futures::{future, prelude::*};
-use linkerd_policy_controller::k8s::DefaultPolicy;
-use linkerd_policy_controller::{admin, admission};
+use futures::prelude::*;
+use kube::api::ListParams;
+use linkerd_policy_controller::{admission, k8s};
 use linkerd_policy_controller_core::IpNet;
-use std::{io, net::SocketAddr, sync::Arc};
-use tokio::{
-    fs,
-    net::{TcpListener, TcpStream},
-    sync::watch,
-    time,
-};
-use tokio_rustls::TlsAcceptor;
-use tracing::{debug, error, info, info_span, instrument, Instrument};
-use tracing_subscriber::{fmt::format, prelude::*, EnvFilter};
+use parking_lot::Mutex;
+use std::{net::SocketAddr, sync::Arc};
+use tokio::time;
+use tracing::{info, info_span, instrument, Instrument};
 
 #[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
 #[global_allocator]
 static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
 
+const DETECT_TIMEOUT: time::Duration = time::Duration::from_secs(10);
+
 #[derive(Debug, Parser)]
 #[clap(name = "policy", about = "A policy resource prototype")]
 struct Args {
@@ -31,19 +27,26 @@ struct Args {
         default_value = "linkerd=info,warn",
         env = "LINKERD_POLICY_CONTROLLER_LOG"
     )]
-    log_level: EnvFilter,
+    log_level: kubert::LogFilter,
 
     #[clap(long, default_value = "plain")]
-    log_format: LogFormat,
+    log_format: kubert::LogFormat,
 
-    #[clap(long, default_value = "0.0.0.0:8080")]
-    admin_addr: SocketAddr,
+    #[clap(flatten)]
+    client: kubert::ClientArgs,
 
-    #[clap(long, default_value = "0.0.0.0:8090")]
-    grpc_addr: SocketAddr,
+    #[clap(flatten)]
+    server: kubert::ServerArgs,
 
+    #[clap(flatten)]
+    admin: kubert::AdminArgs,
+
+    /// Disables the admission controller server.
     #[clap(long)]
-    admission_addr: Option<SocketAddr>,
+    admission_controller_disabled: bool,
+
+    #[clap(long, default_value = "0.0.0.0:8090")]
+    grpc_addr: SocketAddr,
 
     /// Network CIDRs of pod IPs.
     ///
@@ -58,74 +61,85 @@ struct Args {
     identity_domain: String,
 
     #[clap(long, default_value = "all-unauthenticated")]
-    default_policy: DefaultPolicy,
+    default_policy: k8s::DefaultPolicy,
 
     #[clap(long, default_value = "linkerd")]
     control_plane_namespace: String,
 }
 
-#[derive(Clone, Debug)]
-enum LogFormat {
-    Json,
-    Plain,
-}
-
 #[tokio::main]
 async fn main() -> Result<()> {
     let Args {
-        admin_addr,
+        admin,
+        client,
+        log_level,
+        log_format,
+        server,
         grpc_addr,
-        admission_addr,
+        admission_controller_disabled,
         identity_domain,
         cluster_networks: IpNets(cluster_networks),
         default_policy,
-        log_level,
-        log_format,
         control_plane_namespace,
     } = Args::parse();
 
-    log_init(log_level, log_format)?;
-
-    let (drain_tx, drain_rx) = drain::channel();
-
-    // Load a Kubernetes client from the environment (check for in-cluster configuration first).
-    //
-    // TODO support --kubeconfig and --context command-line arguments.
-    let client = kube::Client::try_default()
-        .await
-        .context("failed to initialize kubernetes client")?;
-
-    // Spawn an admin server, failing readiness checks until the index is updated.
-    let (ready_tx, ready_rx) = watch::channel(false);
-    tokio::spawn(admin::serve(admin_addr, ready_rx));
+    let server = if admission_controller_disabled {
+        None
+    } else {
+        Some(server)
+    };
 
-    // Index cluster resources, returning a handle that supports lookups for the gRPC server.
-    let handle = {
-        const DETECT_TIMEOUT: time::Duration = time::Duration::from_secs(10);
-        let cluster = linkerd_policy_controller::k8s::ClusterInfo {
+    let mut runtime = kubert::Runtime::builder()
+        .with_log(log_level, log_format)
+        .with_admin(admin)
+        .with_client(client)
+        .with_optional_server(server)
+        .build()
+        .await?;
+
+    // Build the index data structure, which will be used to process events from all watches
+    // The lookup handle is used by the gRPC server.
+    let (lookup, index) = {
+        let cluster = k8s::ClusterInfo {
             networks: cluster_networks.clone(),
             identity_domain,
             control_plane_ns: control_plane_namespace,
         };
-        let (handle, index) =
-            linkerd_policy_controller::k8s::Index::new(cluster, default_policy, DETECT_TIMEOUT);
-
-        tokio::spawn(index.run(client.clone(), ready_tx));
-        handle
+        let (lookup, idx) = k8s::Index::new(cluster, default_policy, DETECT_TIMEOUT);
+        (lookup, Arc::new(Mutex::new(idx)))
     };
 
+    // Spawn resource indexers that update the index and publish lookups for the gRPC server.
+
+    let pods = runtime.watch_all(ListParams::default().labels("linkerd.io/control-plane-ns"));
+    tokio::spawn(k8s::index_pods(index.clone(), pods).instrument(info_span!("pods")));
+
+    let servers = runtime.watch_all(ListParams::default());
+    tokio::spawn(k8s::index_servers(index.clone(), servers).instrument(info_span!("servers")));
+
+    let serverauthorizations = runtime.watch_all(ListParams::default());
+    tokio::spawn(
+        k8s::index_serverauthorizations(index, serverauthorizations)
+            .instrument(info_span!("serverauthorizations")),
+    );
+
     // Run the gRPC server, serving results by looking up against the index handle.
-    tokio::spawn(grpc(grpc_addr, cluster_networks, handle, drain_rx.clone()));
+    tokio::spawn(grpc(
+        grpc_addr,
+        cluster_networks,
+        lookup,
+        runtime.shutdown_handle(),
+    ));
 
-    if let Some(bind_addr) = admission_addr {
-        let (listen_addr, serve) = bind_admission_controller(bind_addr, client).await?;
-        tokio::spawn(serve.instrument(info_span!("admission")));
-        info!(addr = %listen_addr, "Admission controller server listening");
-    }
+    // TODO use resource caches instead of a client
+    let client = runtime.client();
+    let runtime = runtime.spawn_server(|| admission::Service { client });
 
     // Block the main thread on the shutdown signal. Once it fires, wait for the background tasks to
     // complete before exiting.
-    shutdown(drain_tx).await;
+    if runtime.run().await.is_err() {
+        bail!("Aborted");
+    }
 
     Ok(())
 }
@@ -166,173 +180,3 @@ async fn grpc(
     }
     Ok(())
 }
-
-async fn shutdown(drain: drain::Signal) {
-    tokio::select! {
-        _ = tokio::signal::ctrl_c() => {
-            debug!("Received ctrl-c");
-        },
-        _ = sigterm() => {
-            debug!("Received SIGTERM");
-        }
-    }
-    info!("Shutting down");
-    drain.drain().await;
-}
-
-async fn sigterm() {
-    use tokio::signal::unix::{signal, SignalKind};
-    match signal(SignalKind::terminate()) {
-        Ok(mut term) => term.recv().await,
-        _ => future::pending().await,
-    };
-}
-
-fn log_init(filter: EnvFilter, format: LogFormat) -> Result<()> {
-    let registry = tracing_subscriber::registry().with(filter);
-
-    match format {
-        LogFormat::Plain => registry.with(tracing_subscriber::fmt::layer()).try_init()?,
-
-        LogFormat::Json => {
-            let event_fmt = tracing_subscriber::fmt::format()
-                // Configure the formatter to output JSON logs.
-                .json()
-                // Output the current span context as a JSON list.
-                .with_span_list(true)
-                // Don't output a field for the current span, since this
-                // would duplicate information already in the span list.
-                .with_current_span(false);
-
-            // Use the JSON event formatter and the JSON field formatter.
-            let fmt = tracing_subscriber::fmt::layer()
-                .event_format(event_fmt)
-                .fmt_fields(format::JsonFields::default());
-
-            registry.with(fmt).try_init()?
-        }
-    };
-
-    Ok(())
-}
-
-// === impl LogFormat ===
-
-impl std::str::FromStr for LogFormat {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Self> {
-        if s == "json" {
-            Ok(Self::Json)
-        } else if s == "plain" {
-            Ok(Self::Plain)
-        } else {
-            bail!("invalid log format: {}", s)
-        }
-    }
-}
-
-// Load public certificate from file.
-async fn load_certs(filename: &str) -> anyhow::Result<Vec<rustls::Certificate>> {
-    // Open certificate file.
-    let pem = fs::read(filename).await?;
-    let mut reader = io::BufReader::new(pem.as_slice());
-
-    // Load and return certificate.
-    let certs = rustls_pemfile::certs(&mut reader)?;
-    Ok(certs.into_iter().map(rustls::Certificate).collect())
-}
-
-// Load private key from file.
-async fn load_private_key(filename: &str) -> anyhow::Result<rustls::PrivateKey> {
-    // Open keyfile.
-    let pem = fs::read(filename).await?;
-    let mut reader = io::BufReader::new(pem.as_slice());
-
-    // Load and return a single private key.
-    let keys = rustls_pemfile::rsa_private_keys(&mut reader)?;
-    if keys.len() != 1 {
-        return Err(anyhow!("expected a single private key"));
-    }
-
-    Ok(rustls::PrivateKey(keys[0].clone()))
-}
-
-/// Bind the specified address and serve the admission controller using the
-/// provided kubernetes client.
-async fn bind_admission_controller(
-    bind_addr: SocketAddr,
-    client: kube::Client,
-) -> Result<(
-    SocketAddr,
-    impl std::future::Future<Output = ()> + Send + 'static,
-)> {
-    let tcp = TcpListener::bind(&bind_addr).await?;
-    let listen_addr = tcp.local_addr()?;
-    // Connection accept loop
-    let serve = async move {
-        loop {
-            let socket = match tcp.accept().await {
-                Ok((socket, _)) => socket,
-                Err(err) => {
-                    error!(%err, "Failed to accept connection");
-                    continue;
-                }
-            };
-            let client_addr = match socket.peer_addr() {
-                Ok(addr) => addr,
-                Err(error) => {
-                    error!(%error, "Failed to get peer address");
-                    continue;
-                }
-            };
-
-            tokio::spawn(
-                serve_admission_controller_conn(socket, client.clone())
-                    .map_err(|error| error!(%error))
-                    .instrument(info_span!("connection", client.addr = %client_addr)),
-            );
-        }
-    };
-
-    Ok((listen_addr, serve))
-}
-
-/// Serve an HTTP server for the admission controller on the given TCP
-/// connection.
-async fn serve_admission_controller_conn(socket: TcpStream, client: kube::Client) -> Result<()> {
-    let tls = {
-        // Load public certificate.
-        let certs = load_certs("/var/run/linkerd/tls/tls.crt")
-            .await
-            .with_context(|| "failed to load certificate")?;
-
-        // Load private key.
-        let key = load_private_key("/var/run/linkerd/tls/tls.key")
-            .await
-            .with_context(|| "failed to load private key")?;
-
-        let mut cfg = rustls::ServerConfig::builder()
-            .with_safe_defaults()
-            // Do not use client certificate authentication.
-            .with_no_client_auth()
-            .with_single_cert(certs, key)
-            .with_context(|| "failed to configure TLS")?;
-
-        // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order.
-        cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
-        TlsAcceptor::from(Arc::new(cfg))
-    };
-
-    let stream = tls.accept(socket).await.with_context(|| "TLS error")?;
-
-    match hyper::server::conn::Http::new()
-        .serve_connection(stream, admission::Service { client })
-        .await
-        .with_context(|| "Connection closed")
-    {
-        Ok(()) => debug!("Connection closed"),
-        Err(error) => info!(%error, "Connection closed"),
-    };
-    Ok(())
-}
diff --git a/web/app/js/components/NetworkGraph.jsx b/web/app/js/components/NetworkGraph.jsx
index d3ac053f0..04345dd53 100644
--- a/web/app/js/components/NetworkGraph.jsx
+++ b/web/app/js/components/NetworkGraph.jsx
@@ -210,6 +210,8 @@ NetworkGraphBase.defaultProps = {
 
 NetworkGraphBase.propTypes = {
   data: PropTypes.arrayOf(metricsPropType.isRequired).isRequired,
+  /* these objects have a lot of stuff */
+  /* eslint-disable react/forbid-prop-types */
   deployments: PropTypes.arrayOf(PropTypes.object),
 };
 
diff --git a/web/app/js/components/StatusTable.jsx b/web/app/js/components/StatusTable.jsx
index 56c961022..358fbf315 100644
--- a/web/app/js/components/StatusTable.jsx
+++ b/web/app/js/components/StatusTable.jsx
@@ -124,7 +124,12 @@ const StatusTable = function({ classes, statusColumnTitle, data }) {
 StatusTable.propTypes = {
   data: PropTypes.arrayOf(PropTypes.shape({
     name: PropTypes.string.isRequired,
-    pods: PropTypes.arrayOf(PropTypes.object).isRequired, // TODO: What's the real shape here.
+    pods: PropTypes.shape({
+      name: PropTypes.string.isRequired,
+      value: PropTypes.string.isRequired,
+      uptime: PropTypes.string.isRequired,
+      uptimeSec: PropTypes.number.isRequired,
+    }).isRequired,
     added: PropTypes.bool,
   })).isRequired,
   statusColumnTitle: PropTypes.string.isRequired,
diff --git a/web/app/package.json b/web/app/package.json
index 42455a286..fc6ee63e4 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -59,16 +59,16 @@
     "css-loader": "^6.6.0",
     "enzyme": "3.11.0",
     "enzyme-adapter-react-16": "^1.15.6",
-    "eslint": "^8.9.0",
+    "eslint": "^8.10.0",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-plugin-import": "^2.25.4",
     "eslint-plugin-jsx-a11y": "^6.5.1",
     "eslint-plugin-promise": "^6.0.0",
-    "eslint-plugin-react": "^7.28.0",
+    "eslint-plugin-react": "^7.29.2",
     "eslint-plugin-react-hooks": "^4.3.0",
     "eslint-webpack-plugin": "^3.1.1",
     "file-loader": "^6.2.0",
-    "history": "5.2.0",
+    "history": "5.3.0",
     "html-webpack-plugin": "^5.5.0",
     "jest": "^27.5.1",
     "lodash-webpack-plugin": "^0.11.6",
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index 05fc79cba..9068cf8c6 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -1051,10 +1051,10 @@
     ts-node "^9"
     tslib "^2"
 
-"@eslint/eslintrc@^1.1.0":
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.1.0.tgz#583d12dbec5d4f22f333f9669f7d0b7c7815b4d3"
-  integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==
+"@eslint/eslintrc@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
+  integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
@@ -3706,22 +3706,22 @@ eslint-plugin-react-hooks@^4.3.0:
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172"
   integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
 
-eslint-plugin-react@^7.28.0:
-  version "7.28.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf"
-  integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==
+eslint-plugin-react@^7.29.2:
+  version "7.29.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.2.tgz#2d4da69d30d0a736efd30890dc6826f3e91f3f7c"
+  integrity sha512-ypEBTKOy5liFQXZWMchJ3LN0JX1uPI6n7MN7OPHKacqXAxq5gYC30TdO7wqGYQyxD1OrzpobdHC3hDmlRWDg9w==
   dependencies:
     array-includes "^3.1.4"
     array.prototype.flatmap "^1.2.5"
     doctrine "^2.1.0"
     estraverse "^5.3.0"
     jsx-ast-utils "^2.4.1 || ^3.0.0"
-    minimatch "^3.0.4"
+    minimatch "^3.1.2"
     object.entries "^1.1.5"
     object.fromentries "^2.0.5"
     object.hasown "^1.1.0"
     object.values "^1.1.5"
-    prop-types "^15.7.2"
+    prop-types "^15.8.1"
     resolve "^2.0.0-next.3"
     semver "^6.3.0"
     string.prototype.matchall "^4.0.6"
@@ -3775,12 +3775,12 @@ eslint-webpack-plugin@^3.1.1:
     normalize-path "^3.0.0"
     schema-utils "^3.1.1"
 
-eslint@^8.9.0:
-  version "8.9.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.9.0.tgz#a2a8227a99599adc4342fd9b854cb8d8d6412fdb"
-  integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==
+eslint@^8.10.0:
+  version "8.10.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
+  integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
   dependencies:
-    "@eslint/eslintrc" "^1.1.0"
+    "@eslint/eslintrc" "^1.2.0"
     "@humanwhocodes/config-array" "^0.9.2"
     ajv "^6.10.0"
     chalk "^4.0.0"
@@ -4335,10 +4335,10 @@ he@^1.2.0:
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
-history@5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/history/-/history-5.2.0.tgz#7cdd31cf9bac3c5d31f09c231c9928fad0007b7c"
-  integrity sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==
+history@5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
+  integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
   dependencies:
     "@babel/runtime" "^7.7.6"
 
@@ -5876,10 +5876,10 @@ minimalistic-assert@^1.0.0:
   resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
   integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
 
-minimatch@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
-  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+minimatch@^3.0.4, minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
   dependencies:
     brace-expansion "^1.1.7"
 


Test patch:
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index 43450dfeb..617bb2c6f 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -34,7 +34,7 @@ jobs:
     timeout-minutes: 30
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - uses: ./.github/actions/docker-build
       with:
         docker-registry: ${{ env.DOCKER_REGISTRY }}
@@ -77,15 +77,15 @@ jobs:
     timeout-minutes: 60
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
-    - uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+    - uses: actions/setup-go@f6164bd8c8acb4a71fb2791a8b6c4024ff038dab
       with:
         go-version: '1.17'
     - name: Set environment variables from scripts
       run: |
         echo "TAG=$(CI_FORCE_CLEAN=1 bin/root-tag)" >> $GITHUB_ENV
     - name: Download image archives
-      uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
+      uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
       with:
         name: image-archives
         path: image-archives
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index 68b5128c9..7848f6125 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -18,7 +18,7 @@ jobs:
       image: golang:1.17.3
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Go unit tests
       run: |
         go get gotest.tools/gotestsum@v0.4.2
@@ -32,7 +32,7 @@ jobs:
       image: node:14-stretch
     steps:
     - name: Checkout code
-      uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
     - name: Yarn setup
       run: bin/scurl --retry=2 https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 --network-concurrency 1
     - name: JS unit tests
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index 992f361c3..65d7ae034 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -1937,7 +1937,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1947,9 +1949,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1963,6 +1964,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden
index c6b15ff9f..bdb7d24ce 100644
--- a/cli/cmd/testdata/install_custom_domain.golden
+++ b/cli/cmd/testdata/install_custom_domain.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index 6ab8dec5e..45f32796d 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index c6b15ff9f..bdb7d24ce 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
index c46118241..dbb6ac969 100644
--- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden
+++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.0.0.0/8
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_default_token.golden b/cli/cmd/testdata/install_default_token.golden
index a81a767aa..4c3079d50 100644
--- a/cli/cmd/testdata/install_default_token.golden
+++ b/cli/cmd/testdata/install_default_token.golden
@@ -1924,7 +1924,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1934,9 +1936,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1950,6 +1951,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index c73579780..c4edfb7bc 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -2071,7 +2071,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -2081,9 +2083,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -2097,6 +2098,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index a23f05517..5a19443b3 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -2071,7 +2071,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -2081,9 +2083,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -2097,6 +2098,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index dc6040da2..51ff6a691 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -1866,7 +1866,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1876,9 +1878,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1892,6 +1893,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_helm_control_plane_output.golden b/cli/cmd/testdata/install_helm_control_plane_output.golden
index e41a5cfef..4a60df10d 100644
--- a/cli/cmd/testdata/install_helm_control_plane_output.golden
+++ b/cli/cmd/testdata/install_helm_control_plane_output.golden
@@ -1198,7 +1198,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd-dev
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
@@ -1208,9 +1210,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1224,6 +1225,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_helm_control_plane_output_ha.golden b/cli/cmd/testdata/install_helm_control_plane_output_ha.golden
index 81fc25df7..92b3dfde8 100644
--- a/cli/cmd/testdata/install_helm_control_plane_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_control_plane_output_ha.golden
@@ -1334,7 +1334,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd-dev
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
@@ -1344,9 +1346,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1360,6 +1361,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index 245b3265c..f5edfe7f7 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -1346,7 +1346,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd-dev
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
@@ -1356,9 +1358,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1372,6 +1373,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
index 2e8b39721..df70e29cd 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -1324,7 +1324,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd-dev
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
@@ -1334,9 +1336,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1350,6 +1351,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index 6da6a5d56..8f98b8f36 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -1898,7 +1898,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1908,9 +1910,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1924,6 +1925,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 99ce526f3..df8a60848 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -1937,7 +1937,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=ClusterNetworks
         - --identity-domain=cluster.local
         - --default-policy=default-allow-policy
@@ -1947,9 +1949,8 @@ spec:
         imagePullPolicy: ImagePullPolicy
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1963,6 +1964,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
           limits:
             cpu: "cpu-limit"
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index e0d54d34d..ecff71681 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden
index badc5f06c..50244b7d7 100644
--- a/cli/cmd/testdata/install_values_file.golden
+++ b/cli/cmd/testdata/install_values_file.golden
@@ -1935,7 +1935,9 @@ spec:
         - --admin-addr=0.0.0.0:9990
         - --control-plane-namespace=linkerd
         - --grpc-addr=0.0.0.0:8090
-        - --admission-addr=0.0.0.0:9443
+        - --server-addr=0.0.0.0:9443
+        - --server-tls-key=/var/run/linkerd/tls/tls.key
+        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=example.com
         - --default-policy=all-unauthenticated
@@ -1945,9 +1947,8 @@ spec:
         imagePullPolicy: IfNotPresent
         livenessProbe:
           httpGet:
-            path: /ready
+            path: /live
             port: admin
-          initialDelaySeconds: 10
         name: policy
         ports:
         - containerPort: 8090
@@ -1961,6 +1962,7 @@ spec:
           httpGet:
             path: /ready
             port: admin
+          initialDelaySeconds: 10
         resources:
         securityContext:
           runAsUser: 2103
diff --git a/policy-controller/k8s/index/src/tests.rs b/policy-controller/k8s/index/src/tests.rs
index e9d1620cc..93451701b 100644
--- a/policy-controller/k8s/index/src/tests.rs
+++ b/policy-controller/k8s/index/src/tests.rs
@@ -37,7 +37,7 @@ async fn incrementally_configure_server() {
         pod_net.hosts().next().unwrap(),
         Some(("container-0", vec![2222, 9999])),
     );
-    idx.apply_pod(pod.clone()).unwrap();
+    idx.apply_pod(pod.clone());
 
     let default = DefaultPolicy::Allow {
         authenticated_only: false,
@@ -94,7 +94,7 @@ async fn incrementally_configure_server() {
             ..Default::default()
         },
     );
-    idx.apply_authz(authz.clone()).unwrap();
+    idx.apply_serverauthorization(authz.clone());
 
     // Check that the watch now has authorized traffic as described above.
     let mut rx = port2222.into_stream();
@@ -116,21 +116,21 @@ async fn incrementally_configure_server() {
     );
 
     // Delete the authorization and check that the watch has reverted to its prior state.
-    idx.delete_authz(authz);
+    idx.delete_serverauthorization(authz);
     assert_eq!(
         time::timeout(time::Duration::from_secs(1), rx.next()).await,
         Ok(Some(basic_config)),
     );
 
     // Delete the server and check that the watch has reverted the default state.
-    idx.delete_server(srv).unwrap();
+    idx.delete_server(srv);
     assert_eq!(
         time::timeout(time::Duration::from_secs(1), rx.next()).await,
         Ok(Some(default_config))
     );
 
     // Delete the pod and check that the watch recognizes that the watch has been closed.
-    idx.delete_pod(pod).unwrap();
+    idx.delete_pod(pod);
     assert_eq!(
         time::timeout(time::Duration::from_secs(1), rx.next()).await,
         Ok(None)
@@ -160,7 +160,7 @@ fn server_update_deselects_pod() {
         pod_net.hosts().next().unwrap(),
         Some(("container-0", vec![2222])),
     );
-    idx.apply_pod(p).unwrap();
+    idx.apply_pod(p);
 
     let srv = {
         let mut srv = mk_server("ns-0", "srv-0", Port::Number(2222), None, None);
@@ -221,7 +221,7 @@ fn default_policy_global() {
             pod_net.hosts().next().unwrap(),
             Some(("container-0", vec![2222])),
         );
-        idx.reset_pods(vec![p]).unwrap();
+        idx.reset_pods(vec![p]);
 
         let config = InboundServer {
             name: format!("default:{}", default),
@@ -277,7 +277,7 @@ fn default_policy_annotated() {
         );
         p.annotations_mut()
             .insert(DefaultPolicy::ANNOTATION.into(), default.to_string());
-        idx.reset_pods(vec![p]).unwrap();
+        idx.reset_pods(vec![p]);
 
         let config = InboundServer {
             name: format!("default:{}", default),
@@ -320,7 +320,7 @@ fn default_policy_annotated_invalid() {
     );
     p.annotations_mut()
         .insert(DefaultPolicy::ANNOTATION.into(), "bogus".into());
-    idx.reset_pods(vec![p]).unwrap();
+    idx.reset_pods(vec![p]);
 
     // Lookup port 2222 -> default config.
     let port2222 = lookup_rx
@@ -367,7 +367,7 @@ fn opaque_annotated() {
         );
         p.annotations_mut()
             .insert("config.linkerd.io/opaque-ports".into(), "2222".into());
-        idx.reset_pods(vec![p]).unwrap();
+        idx.reset_pods(vec![p]);
 
         let config = InboundServer {
             name: format!("default:{}", default),
@@ -407,7 +407,7 @@ fn authenticated_annotated() {
             "config.linkerd.io/proxy-require-identity-inbound-ports".into(),
             "2222".into(),
         );
-        idx.reset_pods(vec![p]).unwrap();
+        idx.reset_pods(vec![p]);
 
         let config = {
             let policy = match *default {