Repository: linkerd/linkerd2
Pull request #7756 (2022-02-02 02:01:31)

dev: Configure locales
The default devcontainer image doesn't configure locales so many tools
(less, zsh, etc), do not properly render unicode characters.

This change updates the devcontainer image with locales configured and
`en_US.UTF-8` enabled by default. This default can be overridden in
per-user configurations.



Patch:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index b7a64d574..d0d24b456 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -11,12 +11,15 @@ RUN apt install -y \
     jq \
     libssl-dev \
     lldb \
+    locales \
     lsb-release \
     npm \
     sudo \
     time
 RUN npm install markdownlint-cli@0.23.1 --global
 
+RUN sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && locale-gen
+
 ARG USER=code
 ARG USER_UID=1000
 ARG USER_GID=1000
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index defc95fec..736a3e0cf 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,7 +1,8 @@
 {
     "name": "linkerd2",
-    "image": "ghcr.io/linkerd/dev:v5",
-    //"dockerFile": "./Dockerfile",
+    "image": "ghcr.io/linkerd/dev:v6",
+    // "dockerFile": "./Dockerfile",
+    // "context": "..",
     "extensions": [
         "DavidAnson.vscode-markdownlint",
         "golang.go",
diff --git a/Cargo.lock b/Cargo.lock
index e6d205c0e..d99bf735d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -123,15 +123,14 @@ dependencies = [
  "num-integer",
  "num-traits",
  "serde",
- "time",
  "winapi",
 ]
 
 [[package]]
 name = "clap"
-version = "3.0.13"
+version = "3.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08799f92c961c7a1cf0cc398a9073da99e21ce388b46372c37f3191f2f3eed3e"
+checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
 dependencies = [
  "bitflags",
  "clap_derive",
@@ -143,9 +142,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "3.0.12"
+version = "3.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fd2078197a22f338bd4fbf7d6387eb6f0d6a3c69e6cbc09f5c93e97321fd92a"
+checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
 dependencies = [
  "heck 0.4.0",
  "proc-macro-error",
@@ -684,9 +683,9 @@ dependencies = [
 
 [[package]]
 name = "kube"
-version = "0.67.0"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "067efafecb445fc68e1f3071880fcf3eb9ca09a16115432033a0f4c1e5ad60ef"
+checksum = "d41f782ddd187a0d8965607679bbd741052106b75330696ce7d7712b13194d88"
 dependencies = [
  "k8s-openapi",
  "kube-client",
@@ -697,9 +696,9 @@ dependencies = [
 
 [[package]]
 name = "kube-client"
-version = "0.67.0"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec03dc0f21b475b32fe7768e1c1448a490c0400309b4358a68bee804cdc0701"
+checksum = "cced1a3e738c2a4bccb52d33066632369c668f366a71c9c58139d9360e1a341d"
 dependencies = [
  "base64",
  "bytes",
@@ -736,9 +735,9 @@ dependencies = [
 
 [[package]]
 name = "kube-core"
-version = "0.67.0"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d82994f9c9b0e13367db9f9b98b30c798da531d4ea2c4c5bebc22e45ba8c67c"
+checksum = "6878656f5e349ef08b0a48d2b1841f1e68c4cf1ef42524feadad2f9a109dd888"
 dependencies = [
  "chrono",
  "form_urlencoded",
@@ -754,9 +753,9 @@ dependencies = [
 
 [[package]]
 name = "kube-derive"
-version = "0.67.0"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9eee59135b9152ec8049f86684b60be68800fbb952a48c272e5e2ad136ef4df"
+checksum = "73f5fa510a64791242047fea4d807a5da06514ee714cace4b942d38a1126f0a8"
 dependencies = [
  "darling",
  "proc-macro2",
@@ -767,9 +766,9 @@ dependencies = [
 
 [[package]]
 name = "kube-runtime"
-version = "0.67.0"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "023a562b3e722d3932654fa6be3772184d73699ad44d3161853874bd79452999"
+checksum = "0990fe3ab89a1217e6a4d4952e1ab0a24783265ed413228efbc489a00f86b3da"
 dependencies = [
  "ahash",
  "backoff",
@@ -1702,17 +1701,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "time"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
-dependencies = [
- "libc",
- "wasi",
- "winapi",
-]
-
 [[package]]
 name = "tokio"
 version = "1.16.1"
diff --git a/charts/linkerd-control-plane/README.md b/charts/linkerd-control-plane/README.md
index 2dede0f2e..975b5f96b 100644
--- a/charts/linkerd-control-plane/README.md
+++ b/charts/linkerd-control-plane/README.md
@@ -242,7 +242,8 @@ Kubernetes: `>=1.20.0-0`
 | proxyInjector.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. |
 | proxyInjector.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. |
 | proxyInjector.keyPEM | string | `""` | Certificate key for the proxy injector. If not provided and not using an external secret then Helm will generate one. |
-| proxyInjector.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook. If not set defaults to all namespaces without the annotation config.linkerd.io/admission-webhooks=disabled |
+| proxyInjector.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]},{"key":"kubernetes.io/metadata.name","operator":"NotIn","values":["kube-system","cert-manager"]}]}` | Namespace selector used by admission webhook. |
+| proxyInjector.objectSelector | object | `{"matchExpressions":[{"key":"linkerd.io/control-plane-component","operator":"DoesNotExist"},{"key":"linkerd.io/cni-resource","operator":"DoesNotExist"}]}` | Object selector used by admission webhook. |
 | webhookFailurePolicy | string | `"Ignore"` | Failure policy for the proxy injector |
 
 ----------------------------------------------
diff --git a/charts/linkerd-control-plane/templates/proxy-injector-rbac.yaml b/charts/linkerd-control-plane/templates/proxy-injector-rbac.yaml
index 1e90181e4..9c34a157f 100644
--- a/charts/linkerd-control-plane/templates/proxy-injector-rbac.yaml
+++ b/charts/linkerd-control-plane/templates/proxy-injector-rbac.yaml
@@ -93,6 +93,8 @@ webhooks:
 - name: linkerd-proxy-injector.linkerd.io
   namespaceSelector:
     {{- toYaml .Values.proxyInjector.namespaceSelector | trim | nindent 4 }}
+  objectSelector:
+    {{- toYaml .Values.proxyInjector.objectSelector | trim | nindent 4 }}
   clientConfig:
     service:
       name: linkerd-proxy-injector
diff --git a/charts/linkerd-control-plane/values.yaml b/charts/linkerd-control-plane/values.yaml
index 72d5757aa..4fd9c35fb 100644
--- a/charts/linkerd-control-plane/values.yaml
+++ b/charts/linkerd-control-plane/values.yaml
@@ -294,15 +294,26 @@ proxyInjector:
   # `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below).
   externalSecret: false
 
-  # -- Namespace selector used by admission webhook. If not set defaults to all
-  # namespaces without the annotation
-  # config.linkerd.io/admission-webhooks=disabled
+  # -- Namespace selector used by admission webhook.
   namespaceSelector:
     matchExpressions:
     - key: config.linkerd.io/admission-webhooks
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+
+  # -- Object selector used by admission webhook.
+  objectSelector:
+    matchExpressions:
+    - key: linkerd.io/control-plane-component
+      operator: DoesNotExist
+    - key: linkerd.io/cni-resource
+      operator: DoesNotExist
 
   # -- Certificate for the proxy injector. If not provided and not using an external secret
   # then Helm will generate one.
diff --git a/charts/linkerd2-cni/templates/cni-plugin.yaml b/charts/linkerd2-cni/templates/cni-plugin.yaml
index c21f3f933..265aeea33 100644
--- a/charts/linkerd2-cni/templates/cni-plugin.yaml
+++ b/charts/linkerd2-cni/templates/cni-plugin.yaml
@@ -22,8 +22,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: {{.Release.Namespace}}
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -189,6 +187,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         {{ include "partials.annotations.created-by" . }}
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       {{- if .Values.tolerations }}
       {{- include "linkerd.tolerations" . | nindent 6 }}
diff --git a/multicluster/charts/linkerd-multicluster-link/templates/service-mirror.yaml b/multicluster/charts/linkerd-multicluster-link/templates/service-mirror.yaml
index b7d38482f..a90d48590 100644
--- a/multicluster/charts/linkerd-multicluster-link/templates/service-mirror.yaml
+++ b/multicluster/charts/linkerd-multicluster-link/templates/service-mirror.yaml
@@ -5,7 +5,7 @@ metadata:
   name: linkerd-service-mirror-access-local-resources-{{.Values.targetClusterName}}
   labels:
     linkerd.io/extension: multicluster
-    linkerd.io/control-plane-component: service-mirror
+    component: service-mirror
     mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
 rules:
 - apiGroups: [""]
@@ -21,7 +21,7 @@ metadata:
   name: linkerd-service-mirror-access-local-resources-{{.Values.targetClusterName}}
   labels:
     linkerd.io/extension: multicluster
-    linkerd.io/control-plane-component: service-mirror
+    component: service-mirror
     mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
 roleRef:
   apiGroup: rbac.authorization.k8s.io
@@ -39,7 +39,7 @@ metadata:
   {{ include "partials.namespace" . }}
   labels:
       linkerd.io/extension: multicluster
-      linkerd.io/control-plane-component: service-mirror
+      component: service-mirror
       mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
 rules:
   - apiGroups: [""]
@@ -57,7 +57,7 @@ metadata:
   {{ include "partials.namespace" . }}
   labels:
       linkerd.io/extension: multicluster
-      linkerd.io/control-plane-component: service-mirror
+      component: service-mirror
       mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
 roleRef:
   apiGroup: rbac.authorization.k8s.io
@@ -75,7 +75,7 @@ metadata:
   {{ include "partials.namespace" . }}
   labels:
     linkerd.io/extension: multicluster
-    linkerd.io/control-plane-component: service-mirror
+    component: service-mirror
     mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
 ---
 apiVersion: apps/v1
@@ -83,7 +83,7 @@ kind: Deployment
 metadata:
   labels:
     linkerd.io/extension: multicluster
-    linkerd.io/control-plane-component: service-mirror
+    component: service-mirror
     mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
   name: linkerd-service-mirror-{{.Values.targetClusterName}}
   {{ include "partials.namespace" . }}
@@ -91,14 +91,14 @@ spec:
   replicas: 1
   selector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
       mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
   template:
     metadata:
       annotations:
         linkerd.io/inject: enabled
       labels:
-        linkerd.io/control-plane-component: linkerd-service-mirror
+        component: linkerd-service-mirror
         mirror.linkerd.io/cluster-name: {{.Values.targetClusterName}}
     spec:
       containers:
diff --git a/multicluster/charts/linkerd-multicluster/templates/gateway.yaml b/multicluster/charts/linkerd-multicluster/templates/gateway.yaml
index 415c2f849..a89d6c02d 100644
--- a/multicluster/charts/linkerd-multicluster/templates/gateway.yaml
+++ b/multicluster/charts/linkerd-multicluster/templates/gateway.yaml
@@ -9,7 +9,7 @@ metadata:
     app.kubernetes.io/name: gateway
     app.kubernetes.io/part-of: Linkerd
     app.kubernetes.io/version: {{.Values.linkerdVersion}}
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     app: {{.Values.gateway.name}}
     linkerd.io/extension: multicluster
   name: {{.Values.gateway.name}}
@@ -72,7 +72,7 @@ metadata:
     mirror.linkerd.io/probe-period: "{{.Values.gateway.probe.seconds}}"
     mirror.linkerd.io/probe-path: {{.Values.gateway.probe.path}}
     mirror.linkerd.io/multicluster-gateway: "true"
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     {{ include "partials.annotations.created-by" . }}
     {{- with .Values.gateway.serviceAnnotations }}{{ toYaml . | trim | nindent 4 }}{{- end }}
 spec:
diff --git a/multicluster/charts/linkerd-multicluster/templates/proxy-admin-policy.yaml b/multicluster/charts/linkerd-multicluster/templates/proxy-admin-policy.yaml
index 3f5b2853e..4eac89c3d 100644
--- a/multicluster/charts/linkerd-multicluster/templates/proxy-admin-policy.yaml
+++ b/multicluster/charts/linkerd-multicluster/templates/proxy-admin-policy.yaml
@@ -41,7 +41,7 @@ metadata:
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: linkerd-admin
   proxyProtocol: HTTP/1
 ---
diff --git a/multicluster/charts/linkerd-multicluster/templates/service-mirror-policy.yaml b/multicluster/charts/linkerd-multicluster/templates/service-mirror-policy.yaml
index 271a1c0e6..c32fd1385 100644
--- a/multicluster/charts/linkerd-multicluster/templates/service-mirror-policy.yaml
+++ b/multicluster/charts/linkerd-multicluster/templates/service-mirror-policy.yaml
@@ -5,11 +5,11 @@ metadata:
   {{ include "partials.namespace" . }}
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: admin-http
   proxyProtocol: HTTP/1
 ---
@@ -19,7 +19,7 @@ metadata:
   {{ include "partials.namespace" . }}
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   server:
     name: service-mirror
diff --git a/policy-controller/Cargo.toml b/policy-controller/Cargo.toml
index e10e2cce8..36656b741 100644
--- a/policy-controller/Cargo.toml
+++ b/policy-controller/Cargo.toml
@@ -16,7 +16,7 @@ clap = { version = "3", default-features = false, features = ["derive", "env", "
 drain = "0.1"
 futures = { version = "0.3", default-features = false }
 hyper = { version = "0.14", features = ["http1", "http2", "runtime", "server"] }
-kube = { version = "0.67", default-features = false, features = ["admission", "client"] }
+kube = { version = "0.68", 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" }
diff --git a/policy-controller/k8s/api/Cargo.toml b/policy-controller/k8s/api/Cargo.toml
index 1741529ee..a642af8bb 100644
--- a/policy-controller/k8s/api/Cargo.toml
+++ b/policy-controller/k8s/api/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
 [dependencies]
 futures = { version = "0.3", default-features = false }
 k8s-openapi = { version = "0.14", default-features = false, features = ["v1_20"] }
-kube = { version = "0.67", default-features = false, features = ["client", "derive", "runtime"] }
+kube = { version = "0.68", default-features = false, features = ["client", "derive", "runtime"] }
 schemars = "0.8"
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
diff --git a/viz/charts/linkerd-viz/templates/prometheus.yaml b/viz/charts/linkerd-viz/templates/prometheus.yaml
index b03d8fc3f..3ca042195 100644
--- a/viz/charts/linkerd-viz/templates/prometheus.yaml
+++ b/viz/charts/linkerd-viz/templates/prometheus.yaml
@@ -77,7 +77,7 @@ data:
       - role: pod
       relabel_configs:
       - source_labels:
-        - __meta_kubernetes_pod_label_linkerd_io_control_plane_component
+        - __meta_kubernetes_pod_label_component
         - __meta_kubernetes_pod_container_port_name
         action: keep
         regex: linkerd-service-mirror;admin-http$


Test patch:
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index e1e3c5714..bf9f0179c 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -62,11 +62,9 @@ jobs:
         - cluster-domain
         - cni-calico-deep
         - deep
+        - viz
         - default-policy-deny
-        - external-issuer
-        - external-prometheus-deep
-        - external-resources
-        - helm-deep
+        - external
         # Skipping Helm upgrade test given chart in 2.11 is backwards-incompatible
         #- helm-upgrade
         - multicluster
diff --git a/bin/_test-helpers.sh b/bin/_test-helpers.sh
index 080e1b37a..b2f567186 100644
--- a/bin/_test-helpers.sh
+++ b/bin/_test-helpers.sh
@@ -6,7 +6,7 @@ set +e
 
 ##### Test setup helpers #####
 
-export default_test_names=(deep external-issuer external-prometheus-deep helm-deep helm-upgrade uninstall upgrade-edge upgrade-stable default-policy-deny)
+export default_test_names=(deep viz external helm-upgrade uninstall upgrade-edge upgrade-stable default-policy-deny)
 export external_resource_test_names=(external-resources)
 export all_test_names=(cluster-domain cni-calico-deep multicluster "${default_test_names[*]}" "${external_resource_test_names[*]}")
 images_load_default=(proxy controller policy-controller web metrics-api tap)
@@ -293,7 +293,7 @@ Help:
 image_load() {
   cluster_name=$1
   images_load=("${images_load_default[@]}")
-  if [[ "$cluster_name" = *deep ]]; then
+  if [[ "$cluster_name" = *viz ]]; then
     images_load+=(jaeger-webhook)
   fi
   if [ "$cluster_name" = "cni-calico-deep" ]; then
@@ -381,7 +381,7 @@ run_test(){
 
   printf 'Test script: [%s] Params: [%s]\n' "${filename##*/}" "$*"
   # Exit on failure here
-  GO111MODULE=on go test -test.timeout=60m --failfast --mod=readonly "$filename" --linkerd="$linkerd_path" --helm-path="$helm_path" --default-allow-policy="$default_allow_policy" --k8s-context="$context" --integration-tests "$@" || exit 1
+  GO111MODULE=on go test -p 1 -test.timeout=60m --failfast --mod=readonly "$filename" --linkerd="$linkerd_path" --helm-path="$helm_path" --default-allow-policy="$default_allow_policy" --k8s-context="$context" --integration-tests "$@" || exit 1
 }
 
 # Returns the latest version for the release channel
@@ -428,7 +428,7 @@ install_version() {
     kubectl --context="$context" label namespaces "$test_app_namespace" 'test.linkerd.io/is-test-data-plane'='true' > /dev/null 2>&1
     (
         set -x
-        "$linkerd_path" inject "$test_directory/testdata/upgrade_test.yaml" | kubectl --context="$context" apply --namespace="$test_app_namespace" -f - 2>&1
+        "$linkerd_path" inject "$test_directory/install/testdata/upgrade_test.yaml" | kubectl --context="$context" apply --namespace="$test_app_namespace" -f - 2>&1
     )
     exit_on_err 'install_version() - linkerd inject failed'
 }
@@ -455,7 +455,7 @@ upgrade_test() {
   )
   exit_on_err "upgrade_test() - installing viz extension in $upgrade_version failed"
 
-  run_test "$test_directory/install_test.go" --upgrade-from-version="$upgrade_version"
+  run_test "$test_directory/install/1_install_test.go" --upgrade-from-version="$upgrade_version"
 }
 
 # Run the upgrade-edge test by upgrading the most-recent edge release to the
@@ -465,6 +465,10 @@ run_upgrade-edge_test() {
   upgrade_test "edge" "$edge_install_url"
 }
 
+run_viz_test() {
+  run_test "$test_directory/viz/..."
+}
+
 # Run the upgrade-stable test by upgrading the most-recent stable release to the
 # HEAD of this branch.
 run_upgrade-stable_test() {
@@ -511,13 +515,13 @@ run_helm-upgrade_test() {
 
   setup_helm
   helm_viz_chart="$( cd "$bindir"/.. && pwd )"/viz/charts/linkerd-viz
-  run_test "$test_directory/install_test.go" --helm-path="$helm_path" --helm-charts="$helm_charts" \
+  run_test "$test_directory/install/1_install_test.go" --helm-path="$helm_path" --helm-charts="$helm_charts" \
   --viz-helm-chart="$helm_viz_chart" --helm-stable-chart='linkerd/linkerd2' --viz-helm-stable-chart="linkerd/linkerd-viz" --helm-release="$helm_release_name" --upgrade-helm-from-version="$stable_version"
   helm_cleanup
 }
 
 run_uninstall_test() {
-  run_test "$test_directory/uninstall/uninstall_test.go" --uninstall=true
+  run_test "$test_directory/install/uninstall/uninstall_test.go" --uninstall=true
 }
 
 run_multicluster_test() {
@@ -527,85 +531,42 @@ run_multicluster_test() {
   "$bindir"/certs-openssl
   cd "$pwd"
   export context="k3d-target"
-  run_test "$test_directory/install_test.go" --multicluster --certs-path "$tmp"
-  run_test "$test_directory/multicluster/target1" --multicluster
+  run_test "$test_directory/multicluster/1_install_test.go" --certs-path "$tmp"
+  run_test "$test_directory/multicluster/target1"
   link=$(multicluster_link target)
 
   export context="k3d-source"
   # Create the emojivoto namespace in the source cluster so that mirror services
   # can be created there.
   kubectl --context="$context" create namespace emojivoto
-  run_test "$test_directory/install_test.go" --multicluster --certs-path "$tmp"
+  run_test "$test_directory/multicluster/1_install_test.go" --certs-path "$tmp"
   echo "$link" | kubectl --context="$context" apply -f -
-  run_test "$test_directory/multicluster/source" --multicluster
-
+  run_test "$test_directory/multicluster/source" 
   export context="k3d-target"
-  run_test "$test_directory/multicluster/target2" --multicluster
-
+  run_test "$test_directory/multicluster/target2" 
   export context="k3d-target"
-  run_test "$test_directory/multicluster/target-statefulset" --multicluster
+  run_test "$test_directory/multicluster/target-statefulset" 
 }
 
 run_deep_test() {
-  local tests=()
-  run_test "$test_directory/install_test.go"
-  while IFS= read -r line; do tests+=("$line"); done <<< "$(go list "$test_directory"/.../...)"
-  for test in "${tests[@]}"; do
-    run_test "$test"
-  done
+  run_test "$test_directory/deep/..."
 }
 
 run_default-policy-deny_test() {
-  local tests=()
   export default_allow_policy='deny'
-  run_test "$test_directory/install_test.go"
+  run_test "$test_directory/install/1_install_test.go" 
 }
 
 run_cni-calico-deep_test() {
-  local tests=()
-  run_test "$test_directory/install_test.go" --cni --calico
-  while IFS= read -r line; do tests+=("$line"); done <<< "$(go list "$test_directory"/.../...)"
-  for test in "${tests[@]}"; do
-    run_test "$test" --cni
-  done
+  run_test "$test_directory/deep/..." --cni
 }
 
-run_helm-deep_test() {
-  local tests=()
-  setup_helm
-  helm_multicluster_chart="$( cd "$bindir"/.. && pwd )"/multicluster/charts/linkerd-multicluster
-  helm_viz_chart="$( cd "$bindir"/.. && pwd )"/viz/charts/linkerd-viz
-  run_test "$test_directory/install_test.go" --helm-path="$helm_path" --helm-charts="$helm_charts" \
-  --helm-release="$helm_release_name" --multicluster-helm-chart="$helm_multicluster_chart" \
-  --viz-helm-chart="$helm_viz_chart" --multicluster-helm-release="$helm_multicluster_release_name"
-  while IFS= read -r line; do tests+=("$line"); done <<< "$(go list "$test_directory"/.../...)"
-  for test in "${tests[@]}"; do
-    run_test "$test"
-  done
-  helm_cleanup
-}
-
-run_external-issuer_test() {
-  run_test "$test_directory/install_test.go" --external-issuer=true
-  run_test "$test_directory/externalissuer/external_issuer_test.go" --external-issuer=true
-}
-
-run_external-prometheus-deep_test() {
-  run_test "$test_directory/install_test.go" --external-prometheus=true
-  while IFS= read -r line; do tests+=("$line"); done <<< "$(go list "$test_directory"/.../...)"
-  for test in "${tests[@]}"; do
-    run_test "$test" --external-prometheus=true
-  done
+run_external_test() {
+  run_test "$test_directory/external/..."
 }
 
 run_cluster-domain_test() {
-  run_test "$test_directory/install_test.go" --cluster-domain='custom.domain'
-}
-
-# wrapper to implement external tests
-run_external-resources_test(){
-   run_test "$test_directory/install_test.go"
-   run_test "$test_directory/externalresources/rabbitmq_test.go"
+  run_test "$test_directory/install/1_install_test.go" --cluster-domain='custom.domain' 
 }
 
 # exit_on_err should be called right after a command to check the result status
diff --git a/cli/cmd/testdata/install-cni-plugin_default.golden b/cli/cmd/testdata/install-cni-plugin_default.golden
index b6ea82f9d..5048ca232 100644
--- a/cli/cmd/testdata/install-cni-plugin_default.golden
+++ b/cli/cmd/testdata/install-cni-plugin_default.golden
@@ -2,8 +2,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: linkerd-cni
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -102,6 +100,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install-cni-plugin_fully_configured.golden b/cli/cmd/testdata/install-cni-plugin_fully_configured.golden
index 475f197a1..eae7c4c94 100644
--- a/cli/cmd/testdata/install-cni-plugin_fully_configured.golden
+++ b/cli/cmd/testdata/install-cni-plugin_fully_configured.golden
@@ -2,8 +2,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: linkerd-cni
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -102,6 +100,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install-cni-plugin_fully_configured_equal_dsts.golden b/cli/cmd/testdata/install-cni-plugin_fully_configured_equal_dsts.golden
index 4adb84485..7b6cdafb2 100644
--- a/cli/cmd/testdata/install-cni-plugin_fully_configured_equal_dsts.golden
+++ b/cli/cmd/testdata/install-cni-plugin_fully_configured_equal_dsts.golden
@@ -2,8 +2,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: linkerd-cni
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -102,6 +100,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install-cni-plugin_fully_configured_no_namespace.golden b/cli/cmd/testdata/install-cni-plugin_fully_configured_no_namespace.golden
index 475f197a1..eae7c4c94 100644
--- a/cli/cmd/testdata/install-cni-plugin_fully_configured_no_namespace.golden
+++ b/cli/cmd/testdata/install-cni-plugin_fully_configured_no_namespace.golden
@@ -2,8 +2,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: linkerd-cni
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -102,6 +100,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install-cni-plugin_skip_ports.golden b/cli/cmd/testdata/install-cni-plugin_skip_ports.golden
index 87d8c777c..550f9b801 100644
--- a/cli/cmd/testdata/install-cni-plugin_skip_ports.golden
+++ b/cli/cmd/testdata/install-cni-plugin_skip_ports.golden
@@ -2,8 +2,6 @@ kind: Namespace
 apiVersion: v1
 metadata:
   name: linkerd-cni
-  annotations:
-    linkerd.io/inject: disabled
   labels:
     linkerd.io/cni-resource: "true"
     config.linkerd.io/admission-webhooks: disabled
@@ -103,6 +101,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install_cni_helm_default_output.golden b/cli/cmd/testdata/install_cni_helm_default_output.golden
index 7c3206c82..7050f90db 100644
--- a/cli/cmd/testdata/install_cni_helm_default_output.golden
+++ b/cli/cmd/testdata/install_cni_helm_default_output.golden
@@ -94,6 +94,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: linkerd/cli dev-undefined
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install_cni_helm_override_output.golden b/cli/cmd/testdata/install_cni_helm_override_output.golden
index 877911c9e..f369b7185 100644
--- a/cli/cmd/testdata/install_cni_helm_override_output.golden
+++ b/cli/cmd/testdata/install_cni_helm_override_output.golden
@@ -94,6 +94,8 @@ spec:
         k8s-app: linkerd-cni
       annotations:
         linkerd.io/created-by: test-version
+        linkerd.io/cni-resource: "true"
+        linkerd.io/inject: disabled
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index 38ada41cd..62b8f90cc 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden
index d7e5994c0..3b8571fd0 100644
--- a/cli/cmd/testdata/install_custom_domain.golden
+++ b/cli/cmd/testdata/install_custom_domain.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index 4f3798462..724dd4433 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index d7e5994c0..3b8571fd0 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
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 1036b4197..1d10f56aa 100644
--- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden
+++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_default_token.golden b/cli/cmd/testdata/install_default_token.golden
index a267c1330..7998695d2 100644
--- a/cli/cmd/testdata/install_default_token.golden
+++ b/cli/cmd/testdata/install_default_token.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index 94fe02fa7..f47aa254d 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1310,6 +1317,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources:
       cpu:
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index 77f14dc75..ee8bed99a 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1310,6 +1317,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources:
       cpu:
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index b90bbc9b1..6575e4ef2 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -994,6 +994,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1214,6 +1221,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_helm_control_plane_output.golden b/cli/cmd/testdata/install_helm_control_plane_output.golden
index a7c8d906c..84efc055f 100644
--- a/cli/cmd/testdata/install_helm_control_plane_output.golden
+++ b/cli/cmd/testdata/install_helm_control_plane_output.golden
@@ -349,6 +349,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -549,6 +556,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tap:
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 fba742cab..92cd8a66f 100644
--- a/cli/cmd/testdata/install_helm_control_plane_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_control_plane_output_ha.golden
@@ -349,6 +349,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -576,6 +583,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources:
       cpu:
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index a5ee342fd..074e09f1e 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -349,6 +349,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -580,6 +587,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources:
       cpu:
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 7dcacf8b8..aa791747e 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -349,6 +349,8 @@ webhooks:
       operator: In
       values:
       - enabled
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index 2eabf742e..fd6daeea0 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 9d397d0a4..3ac30a276 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -1060,6 +1060,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1280,6 +1287,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index c7529bcd2..2c35c0a81 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden
index 94bb784c1..064e0859d 100644
--- a/cli/cmd/testdata/install_values_file.golden
+++ b/cli/cmd/testdata/install_values_file.golden
@@ -1063,6 +1063,13 @@ webhooks:
       operator: NotIn
       values:
       - disabled
+    - key: kubernetes.io/metadata.name
+      operator: NotIn
+      values:
+      - kube-system
+      - cert-manager
+  objectSelector:
+    null
   clientConfig:
     service:
       name: linkerd-proxy-injector
@@ -1283,6 +1290,11 @@ data:
           operator: NotIn
           values:
           - disabled
+        - key: kubernetes.io/metadata.name
+          operator: NotIn
+          values:
+          - kube-system
+          - cert-manager
     proxyInjectorProxyResources: null
     proxyInjectorResources: null
     tolerations: null
diff --git a/multicluster/cmd/testdata/install_default.golden b/multicluster/cmd/testdata/install_default.golden
index ef1bed122..4f77cb988 100644
--- a/multicluster/cmd/testdata/install_default.golden
+++ b/multicluster/cmd/testdata/install_default.golden
@@ -14,7 +14,7 @@ metadata:
     app.kubernetes.io/name: gateway
     app.kubernetes.io/part-of: Linkerd
     app.kubernetes.io/version: linkerdVersionValue
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     app: linkerd-gateway
     linkerd.io/extension: multicluster
   name: linkerd-gateway
@@ -51,7 +51,7 @@ metadata:
     mirror.linkerd.io/probe-period: "3"
     mirror.linkerd.io/probe-path: /ready
     mirror.linkerd.io/multicluster-gateway: "true"
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     linkerd.io/created-by: linkerd/helm linkerdVersionValue
 spec:
   ports:
@@ -115,7 +115,7 @@ metadata:
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: linkerd-admin
   proxyProtocol: HTTP/1
 ---
@@ -336,11 +336,11 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: admin-http
   proxyProtocol: HTTP/1
 ---
@@ -350,7 +350,7 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   server:
     name: service-mirror
diff --git a/multicluster/cmd/testdata/install_ha.golden b/multicluster/cmd/testdata/install_ha.golden
index 373e6ae0a..d37aa64c2 100644
--- a/multicluster/cmd/testdata/install_ha.golden
+++ b/multicluster/cmd/testdata/install_ha.golden
@@ -14,7 +14,7 @@ metadata:
     app.kubernetes.io/name: gateway
     app.kubernetes.io/part-of: Linkerd
     app.kubernetes.io/version: linkerdVersionValue
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     app: linkerd-gateway
     linkerd.io/extension: multicluster
   name: linkerd-gateway
@@ -89,7 +89,7 @@ metadata:
     mirror.linkerd.io/probe-period: "3"
     mirror.linkerd.io/probe-path: /ready
     mirror.linkerd.io/multicluster-gateway: "true"
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     linkerd.io/created-by: linkerd/helm linkerdVersionValue
 spec:
   ports:
@@ -153,7 +153,7 @@ metadata:
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: linkerd-admin
   proxyProtocol: HTTP/1
 ---
@@ -405,11 +405,11 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: admin-http
   proxyProtocol: HTTP/1
 ---
@@ -419,7 +419,7 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   server:
     name: service-mirror
diff --git a/multicluster/cmd/testdata/install_psp.golden b/multicluster/cmd/testdata/install_psp.golden
index 0c2c084ce..53e0dfc46 100644
--- a/multicluster/cmd/testdata/install_psp.golden
+++ b/multicluster/cmd/testdata/install_psp.golden
@@ -14,7 +14,7 @@ metadata:
     app.kubernetes.io/name: gateway
     app.kubernetes.io/part-of: Linkerd
     app.kubernetes.io/version: linkerdVersionValue
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     app: linkerd-gateway
     linkerd.io/extension: multicluster
   name: linkerd-gateway
@@ -51,7 +51,7 @@ metadata:
     mirror.linkerd.io/probe-period: "3"
     mirror.linkerd.io/probe-path: /ready
     mirror.linkerd.io/multicluster-gateway: "true"
-    linkerd.io/control-plane-component: gateway
+    component: gateway
     linkerd.io/created-by: linkerd/helm linkerdVersionValue
 spec:
   ports:
@@ -115,7 +115,7 @@ metadata:
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: linkerd-admin
   proxyProtocol: HTTP/1
 ---
@@ -367,11 +367,11 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   podSelector:
     matchLabels:
-      linkerd.io/control-plane-component: linkerd-service-mirror
+      component: linkerd-service-mirror
   port: admin-http
   proxyProtocol: HTTP/1
 ---
@@ -381,7 +381,7 @@ metadata:
   namespace: linkerd-multicluster
   name: service-mirror
   labels:
-    linkerd.io/control-plane-component: linkerd-service-mirror
+    component: linkerd-service-mirror
 spec:
   server:
     name: service-mirror
diff --git a/pkg/charts/linkerd2/values_test.go b/pkg/charts/linkerd2/values_test.go
index 60b65b024..6f54c427a 100644
--- a/pkg/charts/linkerd2/values_test.go
+++ b/pkg/charts/linkerd2/values_test.go
@@ -17,15 +17,22 @@ func TestNewValues(t *testing.T) {
 
 	testVersion := "linkerd-dev"
 
-	namespaceSelector := &metav1.LabelSelector{
-		MatchExpressions: []metav1.LabelSelectorRequirement{
-			{
-				Key:      "config.linkerd.io/admission-webhooks",
-				Operator: "NotIn",
-				Values:   []string{"disabled"},
-			},
+	matchExpressionsSimple := []metav1.LabelSelectorRequirement{
+		{
+			Key:      "config.linkerd.io/admission-webhooks",
+			Operator: "NotIn",
+			Values:   []string{"disabled"},
 		},
 	}
+	matchExpressionsInjector := append(matchExpressionsSimple, metav1.LabelSelectorRequirement{
+		Key:      "kubernetes.io/metadata.name",
+		Operator: "NotIn",
+		Values:   []string{"kube-system", "cert-manager"},
+	},
+	)
+
+	namespaceSelectorSimple := &metav1.LabelSelector{MatchExpressions: matchExpressionsSimple}
+	namespaceSelectorInjector := &metav1.LabelSelector{MatchExpressions: matchExpressionsInjector}
 
 	expected := &Values{
 		ControllerImage:              "cr.l5d.io/linkerd/controller",
@@ -143,9 +150,9 @@ func TestNewValues(t *testing.T) {
 			},
 		},
 
-		ProxyInjector:    &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelector},
-		ProfileValidator: &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelector},
-		PolicyValidator:  &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelector},
+		ProxyInjector:    &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelectorInjector},
+		ProfileValidator: &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelectorSimple},
+		PolicyValidator:  &Webhook{TLS: &TLS{}, NamespaceSelector: namespaceSelectorSimple},
 	}
 
 	// pin the versions to ensure consistent test result.
diff --git a/test/integration/deep/1_install_test.go b/test/integration/deep/1_install_test.go
new file mode 100644
index 000000000..e2b5a1904
--- /dev/null
+++ b/test/integration/deep/1_install_test.go
@@ -0,0 +1,116 @@
+package deeptest
+
+import (
+	"fmt"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/linkerd/linkerd2/testutil"
+)
+
+//////////////////////
+///   TEST SETUP   ///
+//////////////////////
+
+var (
+	TestHelper *testutil.TestHelper
+)
+
+func TestMain(m *testing.M) {
+	TestHelper = testutil.NewTestHelper()
+	os.Exit(m.Run())
+}
+
+func TestInstallCalico(t *testing.T) {
+	if !TestHelper.CNI() {
+		return
+	}
+
+	out, err := TestHelper.Kubectl("", []string{"apply", "-f", "https://k3d.io/v5.1.0/usage/advanced/calico.yaml"}...)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"kubectl apply command failed\n%s", out)
+	}
+
+	time.Sleep(10 * time.Second)
+	o, err := TestHelper.Kubectl("", "--namespace=kube-system", "wait", "--for=condition=available", "--timeout=120s", "deploy/calico-kube-controllers")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "failed to wait for condition=available for calico resources",
+			"failed to wait for condition=available for calico resources: %s: %s", err, o)
+	}
+}
+
+func TestInstallCNIPlugin(t *testing.T) {
+	if !TestHelper.CNI() {
+		return
+	}
+
+	// install the CNI plugin in the cluster
+	var (
+		cmd  = "install-cni"
+		args = []string{
+			"--use-wait-flag",
+			"--cni-log-level=debug",
+			// For Flannel (k3d's default CNI) the following settings are required.
+			// For Calico the default ones are fine.
+			//"--dest-cni-net-dir=/var/lib/rancher/k3s/agent/etc/cni/net.d",
+			//"--dest-cni-bin-dir=/bin",
+		}
+	)
+
+	exec := append([]string{cmd}, args...)
+	out, err := TestHelper.LinkerdRun(exec...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd install-cni' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApply(out, "")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	// perform a linkerd check with --linkerd-cni-enabled
+	timeout := time.Minute
+	err = TestHelper.RetryFor(timeout, func() error {
+		out, err = TestHelper.LinkerdRun("check", "--pre", "--linkerd-cni-enabled", "--wait=60m")
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	if err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd check' command timed-out (%s)", timeout), err)
+	}
+}
+
+// TestInstall will install the linkerd control plane to be used in the rest of
+// the deep suite tests.
+func TestInstall(t *testing.T) {
+	cmd := []string{
+		"install",
+		"--controller-log-level", "debug",
+		"--proxy-version", TestHelper.GetVersion(),
+		"--set", "heartbeatSchedule=1 2 3 4 5",
+	}
+
+	// If testing deep suite with CNI, set --cni-enabled to true
+	if TestHelper.CNI() {
+		cmd = append(cmd, "--linkerd-cni-enabled")
+	}
+
+	// Pipe cmd & args to `linkerd`
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdDeployReplicasEdge)
+}
diff --git a/test/integration/egress/egress_test.go b/test/integration/deep/egress/egress_test.go
similarity index 100%
rename from test/integration/egress/egress_test.go
rename to test/integration/deep/egress/egress_test.go
diff --git a/test/integration/egress/testdata/proxy.yaml b/test/integration/deep/egress/testdata/proxy.yaml
similarity index 100%
rename from test/integration/egress/testdata/proxy.yaml
rename to test/integration/deep/egress/testdata/proxy.yaml
diff --git a/test/integration/endpoints/endpoints_test.go b/test/integration/deep/endpoints/endpoints_test.go
similarity index 100%
rename from test/integration/endpoints/endpoints_test.go
rename to test/integration/deep/endpoints/endpoints_test.go
diff --git a/test/integration/endpoints/testdata/nginx.yaml b/test/integration/deep/endpoints/testdata/nginx.yaml
similarity index 100%
rename from test/integration/endpoints/testdata/nginx.yaml
rename to test/integration/deep/endpoints/testdata/nginx.yaml
diff --git a/test/integration/localhost/localhost_test.go b/test/integration/deep/localhost/localhost_test.go
similarity index 100%
rename from test/integration/localhost/localhost_test.go
rename to test/integration/deep/localhost/localhost_test.go
diff --git a/test/integration/localhost/testdata/nginx.yaml b/test/integration/deep/localhost/testdata/nginx.yaml
similarity index 100%
rename from test/integration/localhost/testdata/nginx.yaml
rename to test/integration/deep/localhost/testdata/nginx.yaml
diff --git a/test/integration/localhost/testdata/slow-cooker.yaml b/test/integration/deep/localhost/testdata/slow-cooker.yaml
similarity index 100%
rename from test/integration/localhost/testdata/slow-cooker.yaml
rename to test/integration/deep/localhost/testdata/slow-cooker.yaml
diff --git a/test/integration/opaqueports/opaque_ports_test.go b/test/integration/deep/opaqueports/opaque_ports_test.go
similarity index 100%
rename from test/integration/opaqueports/opaque_ports_test.go
rename to test/integration/deep/opaqueports/opaque_ports_test.go
diff --git a/test/integration/opaqueports/testdata/opaque_ports_application.yaml b/test/integration/deep/opaqueports/testdata/opaque_ports_application.yaml
similarity index 100%
rename from test/integration/opaqueports/testdata/opaque_ports_application.yaml
rename to test/integration/deep/opaqueports/testdata/opaque_ports_application.yaml
diff --git a/test/integration/opaqueports/testdata/opaque_ports_client.yaml b/test/integration/deep/opaqueports/testdata/opaque_ports_client.yaml
similarity index 100%
rename from test/integration/opaqueports/testdata/opaque_ports_client.yaml
rename to test/integration/deep/opaqueports/testdata/opaque_ports_client.yaml
diff --git a/test/integration/serviceaccounts/serviceaccounts_test.go b/test/integration/deep/serviceaccounts/serviceaccounts_test.go
similarity index 100%
rename from test/integration/serviceaccounts/serviceaccounts_test.go
rename to test/integration/deep/serviceaccounts/serviceaccounts_test.go
diff --git a/test/integration/skipports/skip_ports_test.go b/test/integration/deep/skipports/skip_ports_test.go
similarity index 100%
rename from test/integration/skipports/skip_ports_test.go
rename to test/integration/deep/skipports/skip_ports_test.go
diff --git a/test/integration/skipports/testdata/skip_ports_application.yaml b/test/integration/deep/skipports/testdata/skip_ports_application.yaml
similarity index 100%
rename from test/integration/skipports/testdata/skip_ports_application.yaml
rename to test/integration/deep/skipports/testdata/skip_ports_application.yaml
diff --git a/test/integration/edges/testdata/external_prometheus/direct_edges.golden b/test/integration/edges/testdata/external_prometheus/direct_edges.golden
deleted file mode 100644
index f59eaa25e..000000000
--- a/test/integration/edges/testdata/external_prometheus/direct_edges.golden
+++ /dev/null
@@ -1,29 +0,0 @@
-\[
-  \{
-    "src": "prometheus",
-    "src_namespace": "external\-prometheus",
-    "dst": "slow-cooker",
-    "dst_namespace": "{{.Ns}}",
-    "client_id": "prometheus.external\-prometheus",
-    "server_id": "default.{{.Ns}}",
-    "no_tls_reason": ""
-  \},
-  \{
-    "src": "prometheus",
-    "src_namespace": "external\-prometheus",
-    "dst": "terminus",
-    "dst_namespace": "{{.Ns}}",
-    "client_id": "prometheus.external\-prometheus",
-    "server_id": "default.{{.Ns}}",
-    "no_tls_reason": ""
-  \},
-  \{
-    "src": "slow-cooker",
-    "src_namespace": "{{.Ns}}",
-    "dst": "terminus",
-    "dst_namespace": "{{.Ns}}",
-    "client_id": "default.{{.Ns}}",
-    "server_id": "default.{{.Ns}}",
-    "no_tls_reason": ""
-  \}
-\]
diff --git a/test/integration/external/1_install_test.go b/test/integration/external/1_install_test.go
new file mode 100644
index 000000000..33e87cd7b
--- /dev/null
+++ b/test/integration/external/1_install_test.go
@@ -0,0 +1,188 @@
+package externaltest
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"os"
+	"testing"
+	"text/template"
+	"time"
+
+	"github.com/linkerd/linkerd2/pkg/healthcheck"
+	"github.com/linkerd/linkerd2/pkg/k8s"
+	"github.com/linkerd/linkerd2/pkg/tls"
+	"github.com/linkerd/linkerd2/pkg/version"
+	"github.com/linkerd/linkerd2/testutil"
+)
+
+//////////////////////
+///   TEST SETUP   ///
+//////////////////////
+
+var (
+	TestHelper *testutil.TestHelper
+)
+
+func TestMain(m *testing.M) {
+	TestHelper = testutil.NewTestHelper()
+	os.Exit(m.Run())
+}
+
+//////////////////////
+/// TEST EXECUTION ///
+//////////////////////
+
+// TestInstallLinkerd will install the linkerd control plane to be used in the rest of
+// the deep suite tests
+func TestInstallLinkerd(t *testing.T) {
+	// Install with a short cert lifetime to put some pressure on the CSR
+	// request, response code path.
+	cmd := []string{
+		"install",
+		"--controller-log-level", "debug",
+		"--proxy-version", TestHelper.GetVersion(),
+		"--set", "heartbeatSchedule=1 2 3 4 5",
+		"--identity-issuance-lifetime=15s",
+		"--identity-external-issuer=true",
+	}
+
+	err := TestHelper.CreateControlPlaneNamespaceIfNotExists(context.Background(), TestHelper.GetLinkerdNamespace())
+	if err != nil {
+		testutil.AnnotatedFatalf(t, fmt.Sprintf("failed to create %s namespace", TestHelper.GetLinkerdNamespace()),
+			"failed to create %s namespace: %s", TestHelper.GetLinkerdNamespace(), err)
+	}
+
+	identity := fmt.Sprintf("identity.%s.%s", TestHelper.GetLinkerdNamespace(), TestHelper.GetClusterDomain())
+
+	root, err := tls.GenerateRootCAWithDefaults(identity)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "error generating root CA", err)
+	}
+
+	// instead of passing the roots and key around we generate
+	// two secrets here. The second one will be used in the
+	// external_issuer_test to update the first one and trigger
+	// cert rotation in the identity service. That allows us
+	// to generated the certs on the fly and use custom domain.
+	if err = TestHelper.CreateTLSSecret(
+		k8s.IdentityIssuerSecretName,
+		root.Cred.Crt.EncodeCertificatePEM(),
+		root.Cred.Crt.EncodeCertificatePEM(),
+		root.Cred.EncodePrivateKeyPEM()); err != nil {
+		testutil.AnnotatedFatal(t, "error creating TLS secret", err)
+	}
+
+	crt2, err := root.GenerateCA(identity, -1)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "error generating CA", err)
+	}
+
+	if err = TestHelper.CreateTLSSecret(
+		k8s.IdentityIssuerSecretName+"-new",
+		root.Cred.Crt.EncodeCertificatePEM(),
+		crt2.Cred.EncodeCertificatePEM(),
+		crt2.Cred.EncodePrivateKeyPEM()); err != nil {
+		testutil.AnnotatedFatal(t, "error creating TLS secret (-new)", err)
+	}
+
+	// Pipe cmd & args to `linkerd`
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdDeployReplicasEdge)
+}
+
+// TestInstallViz will install the viz extension to be used by the rest of the
+// tests in the viz suite
+func TestInstallViz(t *testing.T) {
+	// Install external prometheus
+	out, err := TestHelper.LinkerdRun("inject", "testdata/external_prometheus.yaml", "--manual")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'linkerd inject' command failed", "'linkerd inject' command failed: %s", err)
+	}
+
+	out, err = TestHelper.KubectlApply(out, "")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"kubectl apply command failed\n%s", out)
+	}
+
+	cmd := []string{
+		"viz",
+		"install",
+		"--set", fmt.Sprintf("namespace=%s", TestHelper.GetVizNamespace()),
+		"--set", "prometheusUrl=http://prometheus.external-prometheus.svc.cluster.local:9090",
+		"--set", "prometheus.enabled=false",
+	}
+
+	out, err = TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd viz install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	expectedDeployments := make(map[string]testutil.DeploySpec, len(testutil.LinkerdVizDeployReplicas))
+	for k, v := range testutil.LinkerdVizDeployReplicas {
+		if k == "prometheus" {
+			v = testutil.DeploySpec{Namespace: "external-prometheus", Replicas: 1}
+		}
+
+		expectedDeployments[k] = v
+	}
+	TestHelper.WaitRollout(t, expectedDeployments)
+
+}
+
+func TestCheckVizWithExternalPrometheus(t *testing.T) {
+	cmd := []string{"viz", "check", "--wait=60m"}
+	golden := "check.viz.external-prometheus.golden"
+	pods, err := TestHelper.KubernetesHelper.GetPods(context.Background(), TestHelper.GetVizNamespace(), nil)
+	if err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to retrieve pods: %s", err), err)
+	}
+
+	tpl := template.Must(template.ParseFiles("testdata" + "/" + golden))
+	vars := struct {
+		ProxyVersionErr string
+		HintURL         string
+	}{
+		healthcheck.CheckProxyVersionsUpToDate(pods, version.Channels{}).Error(),
+		healthcheck.HintBaseURL(TestHelper.GetVersion()),
+	}
+
+	var expected bytes.Buffer
+	if err := tpl.Execute(&expected, vars); err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to parse %s template: %s", golden, err), err)
+	}
+
+	timeout := 5 * time.Minute
+	err = TestHelper.RetryFor(timeout, func() error {
+		out, err := TestHelper.LinkerdRun(cmd...)
+		if err != nil {
+			return fmt.Errorf("'linkerd viz check' command failed\n%s", err)
+		}
+
+		if out != expected.String() {
+			return fmt.Errorf(
+				"Expected:\n%s\nActual:\n%s", expected.String(), out)
+		}
+		return nil
+	})
+	if err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd viz check' command timed-out (%s)", timeout), err)
+	}
+}
diff --git a/test/integration/externalissuer/external_issuer_test.go b/test/integration/external/externalissuer/external_issuer_test.go
similarity index 97%
rename from test/integration/externalissuer/external_issuer_test.go
rename to test/integration/external/externalissuer/external_issuer_test.go
index 23f362a0d..6cc14b6bf 100644
--- a/test/integration/externalissuer/external_issuer_test.go
+++ b/test/integration/external/externalissuer/external_issuer_test.go
@@ -21,10 +21,6 @@ const (
 
 func TestMain(m *testing.M) {
 	TestHelper = testutil.NewTestHelper()
-	if !TestHelper.ExternalIssuer() {
-		fmt.Fprintln(os.Stdout, "Skipping as --external-issuer=false")
-		os.Exit(0)
-	}
 	os.Exit(m.Run())
 }
 
diff --git a/test/integration/externalissuer/testdata/external_issuer_application.yaml b/test/integration/external/externalissuer/testdata/external_issuer_application.yaml
similarity index 100%
rename from test/integration/externalissuer/testdata/external_issuer_application.yaml
rename to test/integration/external/externalissuer/testdata/external_issuer_application.yaml
diff --git a/test/integration/externalresources/rabbitmq_test.go b/test/integration/external/externalresources/rabbitmq_test.go
similarity index 100%
rename from test/integration/externalresources/rabbitmq_test.go
rename to test/integration/external/externalresources/rabbitmq_test.go
diff --git a/test/integration/externalresources/testdata/check.rabbitmq.golden b/test/integration/external/externalresources/testdata/check.rabbitmq.golden
similarity index 100%
rename from test/integration/externalresources/testdata/check.rabbitmq.golden
rename to test/integration/external/externalresources/testdata/check.rabbitmq.golden
diff --git a/test/integration/externalresources/testdata/rabbitmq-client.yaml b/test/integration/external/externalresources/testdata/rabbitmq-client.yaml
similarity index 100%
rename from test/integration/externalresources/testdata/rabbitmq-client.yaml
rename to test/integration/external/externalresources/testdata/rabbitmq-client.yaml
diff --git a/test/integration/externalresources/testdata/rabbitmq-server.yaml b/test/integration/external/externalresources/testdata/rabbitmq-server.yaml
similarity index 100%
rename from test/integration/externalresources/testdata/rabbitmq-server.yaml
rename to test/integration/external/externalresources/testdata/rabbitmq-server.yaml
diff --git a/test/integration/external/stat/stat_test.go b/test/integration/external/stat/stat_test.go
new file mode 100644
index 000000000..1f716c49d
--- /dev/null
+++ b/test/integration/external/stat/stat_test.go
@@ -0,0 +1,321 @@
+package get
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/linkerd/linkerd2/testutil"
+)
+
+//////////////////////
+///   TEST SETUP   ///
+//////////////////////
+
+var TestHelper *testutil.TestHelper
+
+func TestMain(m *testing.M) {
+	TestHelper = testutil.NewTestHelper()
+	os.Exit(m.Run())
+}
+
+//////////////////////
+/// TEST EXECUTION ///
+//////////////////////
+
+// These tests retry for up to 20 seconds, since each call to "linkerd stat"
+// generates traffic to the components in the linkerd namespace, and we're
+// testing that those components are properly reporting stats. It's ok if the
+// first few attempts fail due to missing stats, since the requests from those
+// failed attempts will eventually be recorded in the stats that we're
+// requesting, and the test will pass.
+func TestCliStatForLinkerdNamespace(t *testing.T) {
+	ctx := context.Background()
+	var prometheusPod, prometheusAuthority, prometheusNamespace, prometheusDeployment, metricsPod string
+	// Get Metrics Pod
+	pods, err := TestHelper.GetPodNamesForDeployment(ctx, TestHelper.GetVizNamespace(), "metrics-api")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "failed to get pods for metrics-api",
+			"failed to get pods for metrics-api: %s", err)
+	}
+	if len(pods) != 1 {
+		testutil.Fatalf(t, "expected 1 pod for metrics-api, got %d", len(pods))
+	}
+	metricsPod = pods[0]
+
+	// Retrieve Prometheus pod details
+	prometheusNamespace = "external-prometheus"
+	prometheusDeployment = "prometheus"
+
+	pods, err = TestHelper.GetPodNamesForDeployment(ctx, prometheusNamespace, prometheusDeployment)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "failed to get pods for prometheus",
+			"failed to get pods for prometheus: %s", err)
+	}
+	if len(pods) != 1 {
+		testutil.Fatalf(t, "expected 1 pod for prometheus, got %d", len(pods))
+	}
+	prometheusPod = pods[0]
+	prometheusAuthority = prometheusDeployment + "." + prometheusNamespace + ".svc.cluster.local:9090"
+
+	testCases := []struct {
+		args         []string
+		expectedRows map[string]string
+		status       string
+		isAuthority  bool
+	}{
+		{
+			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace()},
+			expectedRows: map[string]string{
+				"linkerd-destination":    "1/1",
+				"linkerd-identity":       "1/1",
+				"linkerd-proxy-injector": "1/1",
+			},
+		},
+		{
+			args: []string{"viz", "stat", "ns", TestHelper.GetLinkerdNamespace()},
+			expectedRows: map[string]string{
+				TestHelper.GetLinkerdNamespace(): "3/3",
+			},
+		},
+		{
+			args: []string{"viz", "stat", fmt.Sprintf("po/%s", prometheusPod), "-n", prometheusNamespace, "--from", fmt.Sprintf("po/%s", metricsPod), "--from-namespace", TestHelper.GetVizNamespace()},
+			expectedRows: map[string]string{
+				prometheusPod: "1/1",
+			},
+			status: "Running",
+		},
+		{
+			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
+			expectedRows: map[string]string{
+				"metrics-api": "1/1",
+			},
+		},
+		{
+			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("svc/%s", prometheusDeployment), "--to-namespace", prometheusNamespace},
+			expectedRows: map[string]string{
+				"metrics-api": "1/1",
+			},
+		},
+		{
+			args: []string{"viz", "stat", "po", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("au/%s", prometheusAuthority), "--to-namespace", prometheusNamespace},
+			expectedRows: map[string]string{
+				metricsPod: "1/1",
+			},
+			status: "Running",
+		},
+		{
+			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
+			expectedRows: map[string]string{
+				prometheusAuthority: "-",
+			},
+			isAuthority: true,
+		},
+		{
+			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
+			expectedRows: map[string]string{
+				prometheusAuthority: "-",
+			},
+			isAuthority: true,
+		},
+	}
+
+	if !TestHelper.ExternalPrometheus() {
+		testCases = append(testCases, []struct {
+			args         []string
+			expectedRows map[string]string
+			status       string
+			isAuthority  bool
+		}{
+			{
+				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
+				expectedRows: map[string]string{
+					"metrics-api":  "1/1",
+					"tap":          "1/1",
+					"web":          "1/1",
+					"tap-injector": "1/1",
+				},
+			},
+			{
+				args: []string{"viz", "stat", "ns", TestHelper.GetVizNamespace()},
+				expectedRows: map[string]string{
+					TestHelper.GetVizNamespace(): "4/4",
+				},
+			},
+			{
+				args: []string{"viz", "stat", "svc", "prometheus", "-n", prometheusNamespace, "--from", "deploy/metrics-api", "--from-namespace", TestHelper.GetVizNamespace()},
+
+				expectedRows: map[string]string{
+					"prometheus": "-",
+				},
+			},
+		}...,
+		)
+	} else {
+		testCases = append(testCases, []struct {
+			args         []string
+			expectedRows map[string]string
+			status       string
+			isAuthority  bool
+		}{
+			{
+				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
+				expectedRows: map[string]string{
+					"metrics-api":  "1/1",
+					"tap":          "1/1",
+					"web":          "1/1",
+					"tap-injector": "1/1",
+				},
+			},
+			{
+				args: []string{"viz", "stat", "ns", TestHelper.GetVizNamespace()},
+				expectedRows: map[string]string{
+					TestHelper.GetVizNamespace(): "4/4",
+				},
+			},
+		}...,
+		)
+	}
+
+	// Apply a sample application
+	TestHelper.WithDataPlaneNamespace(ctx, "stat-test", map[string]string{}, t, func(t *testing.T, prefixedNs string) {
+		out, err := TestHelper.LinkerdRun("inject", "--manual", "./testdata/application.yaml")
+		if err != nil {
+			testutil.AnnotatedFatal(t, "'linkerd inject' command failed", err)
+		}
+
+		out, err = TestHelper.KubectlApply(out, prefixedNs)
+		if err != nil {
+			testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+				"'kubectl apply' command failed\n%s", out)
+		}
+
+		// wait for deployments to start
+		for _, deploy := range []string{"backend", "failing", "slow-cooker"} {
+			if err := TestHelper.CheckPods(ctx, prefixedNs, deploy, 1); err != nil {
+				if rce, ok := err.(*testutil.RestartCountError); ok {
+					testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
+				} else {
+					testutil.AnnotatedError(t, "CheckPods timed-out", err)
+				}
+			}
+		}
+
+		testCases = append(testCases, []struct {
+			args         []string
+			expectedRows map[string]string
+			status       string
+			isAuthority  bool
+		}{
+			{
+				args: []string{"viz", "stat", "svc", "-n", prefixedNs},
+				expectedRows: map[string]string{
+					"backend-svc": "-",
+				},
+			},
+			{
+				args: []string{"viz", "stat", "svc/backend-svc", "-n", prefixedNs, "--from", "deploy/slow-cooker"},
+				expectedRows: map[string]string{
+					"backend-svc": "-",
+				},
+			},
+		}...,
+		)
+
+		for _, tt := range testCases {
+			tt := tt // pin
+			timeout := 20 * time.Second
+			t.Run("linkerd "+strings.Join(tt.args, " "), func(t *testing.T) {
+				err := TestHelper.RetryFor(timeout, func() error {
+					// Use a short time window so that transient errors at startup
+					// fall out of the window.
+					tt.args = append(tt.args, "-t", "30s")
+					out, err := TestHelper.LinkerdRun(tt.args...)
+					if err != nil {
+						testutil.AnnotatedFatalf(t, "unexpected stat error",
+							"unexpected stat error: %s\n%s", err, out)
+					}
+
+					expectedColumnCount := 8
+					if tt.isAuthority {
+						expectedColumnCount = 7
+					}
+					if tt.status != "" {
+						expectedColumnCount++
+					}
+					rowStats, err := testutil.ParseRows(out, len(tt.expectedRows), expectedColumnCount)
+					if err != nil {
+						return err
+					}
+
+					for name, meshed := range tt.expectedRows {
+						if err := validateRowStats(name, meshed, tt.status, rowStats, tt.isAuthority); err != nil {
+							return err
+						}
+					}
+
+					return nil
+				})
+				if err != nil {
+					testutil.AnnotatedFatal(t, fmt.Sprintf("timed-out checking stats (%s)", timeout), err)
+				}
+			})
+		}
+	})
+}
+
+func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat, isAuthority bool) error {
+	stat, ok := rowStats[name]
+	if !ok {
+		return fmt.Errorf("No stats found for [%s]", name)
+	}
+
+	if stat.Status != expectedStatus {
+		return fmt.Errorf("Expected status '%s' for '%s', got '%s'",
+			expectedStatus, name, stat.Status)
+	}
+
+	if stat.Meshed != expectedMeshCount {
+		return fmt.Errorf("Expected mesh count [%s] for [%s], got [%s]",
+			expectedMeshCount, name, stat.Meshed)
+	}
+
+	expectedSuccessRate := "100.00%"
+	if stat.Success != expectedSuccessRate {
+		return fmt.Errorf("Expected success rate [%s] for [%s], got [%s]",
+			expectedSuccessRate, name, stat.Success)
+	}
+
+	if !strings.HasSuffix(stat.Rps, "rps") {
+		return fmt.Errorf("Unexpected rps for [%s], got [%s]",
+			name, stat.Rps)
+	}
+
+	if !strings.HasSuffix(stat.P50Latency, "ms") {
+		return fmt.Errorf("Unexpected p50 latency for [%s], got [%s]",
+			name, stat.P50Latency)
+	}
+
+	if !strings.HasSuffix(stat.P95Latency, "ms") {
+		return fmt.Errorf("Unexpected p95 latency for [%s], got [%s]",
+			name, stat.P95Latency)
+	}
+
+	if !strings.HasSuffix(stat.P99Latency, "ms") {
+		return fmt.Errorf("Unexpected p99 latency for [%s], got [%s]",
+			name, stat.P99Latency)
+	}
+
+	if stat.TCPOpenConnections != "-" && !isAuthority {
+		_, err := strconv.Atoi(stat.TCPOpenConnections)
+		if err != nil {
+			return fmt.Errorf("Error parsing number of TCP connections [%s]: %s", stat.TCPOpenConnections, err.Error())
+		}
+	}
+
+	return nil
+}
diff --git a/test/integration/trafficsplit/testdata/application.yaml b/test/integration/external/stat/testdata/application.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/application.yaml
rename to test/integration/external/stat/testdata/application.yaml
diff --git a/test/integration/testdata/check.viz.external-prometheus.golden b/test/integration/external/testdata/check.viz.external-prometheus.golden
similarity index 100%
rename from test/integration/testdata/check.viz.external-prometheus.golden
rename to test/integration/external/testdata/check.viz.external-prometheus.golden
diff --git a/test/integration/testdata/external_prometheus.yaml b/test/integration/external/testdata/external_prometheus.yaml
similarity index 99%
rename from test/integration/testdata/external_prometheus.yaml
rename to test/integration/external/testdata/external_prometheus.yaml
index 4af8efe32..b61d23fbb 100644
--- a/test/integration/testdata/external_prometheus.yaml
+++ b/test/integration/external/testdata/external_prometheus.yaml
@@ -203,6 +203,7 @@ spec:
     metadata:
       labels:
         app: prometheus
+        linkerd.io/inject: "enabled"
     spec:
       nodeSelector:
         kubernetes.io/os: linux
diff --git a/test/integration/install_test.go b/test/integration/install/1_install_test.go
similarity index 79%
rename from test/integration/install_test.go
rename to test/integration/install/1_install_test.go
index 715d1894f..0ba582014 100644
--- a/test/integration/install_test.go
+++ b/test/integration/install/1_install_test.go
@@ -16,7 +16,6 @@ import (
 	"github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
-	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/tls"
 	"github.com/linkerd/linkerd2/pkg/tree"
 	"github.com/linkerd/linkerd2/pkg/version"
@@ -44,8 +43,7 @@ var (
 	linkerdSvcEdge = []testutil.Service{
 		{Namespace: "linkerd", Name: "linkerd-dst"},
 		{Namespace: "linkerd", Name: "linkerd-identity"},
-		{Namespace: "linkerd-viz", Name: "web"},
-		{Namespace: "linkerd-viz", Name: "tap"},
+
 		{Namespace: "linkerd", Name: "linkerd-dst-headless"},
 		{Namespace: "linkerd", Name: "linkerd-identity-headless"},
 	}
@@ -53,10 +51,6 @@ var (
 	// Override in case edge starts to deviate from stable service-wise
 	linkerdSvcStable = linkerdSvcEdge
 
-	multiclusterSvcs = []testutil.Service{
-		{Namespace: "linkerd-multicluster", Name: "linkerd-gateway"},
-	}
-
 	//skippedInboundPorts lists some ports to be marked as skipped, which will
 	// be verified in test/integration/inject
 	skippedInboundPorts       = "1234,5678"
@@ -141,69 +135,6 @@ func TestRetrieveUidPreUpgrade(t *testing.T) {
 	}
 }
 
-func TestInstallCalico(t *testing.T) {
-	if !TestHelper.Calico() {
-		return
-	}
-
-	out, err := TestHelper.Kubectl("", []string{"apply", "-f", "https://k3d.io/v5.1.0/usage/advanced/calico.yaml"}...)
-	if err != nil {
-		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
-			"kubectl apply command failed\n%s", out)
-	}
-
-	time.Sleep(10 * time.Second)
-	o, err := TestHelper.Kubectl("", "--namespace=kube-system", "wait", "--for=condition=available", "--timeout=120s", "deploy/calico-kube-controllers")
-	if err != nil {
-		testutil.AnnotatedFatalf(t, "failed to wait for condition=available for calico resources",
-			"failed to wait for condition=available for calico resources: %s: %s", err, o)
-	}
-}
-
-func TestInstallCNIPlugin(t *testing.T) {
-	if !TestHelper.CNI() {
-		return
-	}
-
-	// install the CNI plugin in the cluster
-	var (
-		cmd  = "install-cni"
-		args = []string{
-			"--use-wait-flag",
-			"--cni-log-level=debug",
-			// For Flannel (k3d's default CNI) the following settings are required.
-			// For Calico the default ones are fine.
-			//"--dest-cni-net-dir=/var/lib/rancher/k3s/agent/etc/cni/net.d",
-			//"--dest-cni-bin-dir=/bin",
-		}
-	)
-
-	exec := append([]string{cmd}, args...)
-	out, err := TestHelper.LinkerdRun(exec...)
-	if err != nil {
-		testutil.AnnotatedFatal(t, "'linkerd install-cni' command failed", err)
-	}
-
-	out, err = TestHelper.KubectlApply(out, "")
-	if err != nil {
-		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
-			"'kubectl apply' command failed\n%s", out)
-	}
-
-	// perform a linkerd check with --linkerd-cni-enabled
-	timeout := time.Minute
-	err = TestHelper.RetryFor(timeout, func() error {
-		out, err = TestHelper.LinkerdRun("check", "--pre", "--linkerd-cni-enabled", "--wait=60m")
-		if err != nil {
-			return err
-		}
-		return nil
-	})
-	if err != nil {
-		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd check' command timed-out (%s)", timeout), err)
-	}
-}
-
 func TestInstallOrUpgradeCli(t *testing.T) {
 	if TestHelper.GetHelmReleaseName() != "" {
 		return
@@ -236,60 +167,10 @@ func TestInstallOrUpgradeCli(t *testing.T) {
 		vizArgs = append(vizArgs, "--set", fmt.Sprintf("clusterDomain=%s", TestHelper.GetClusterDomain()))
 	}
 
-	if TestHelper.CNI() {
-		args = append(args, "--linkerd-cni-enabled")
-	}
-
 	if policy := TestHelper.DefaultAllowPolicy(); policy != "" {
 		args = append(args, "--set", "policyController.defaultAllowPolicy="+policy)
 	}
 
-	if TestHelper.ExternalIssuer() {
-
-		// short cert lifetime to put some pressure on the CSR request, response code path
-		args = append(args, "--identity-issuance-lifetime=15s", "--identity-external-issuer=true")
-
-		err := TestHelper.CreateControlPlaneNamespaceIfNotExists(context.Background(), TestHelper.GetLinkerdNamespace())
-		if err != nil {
-			testutil.AnnotatedFatalf(t, fmt.Sprintf("failed to create %s namespace", TestHelper.GetLinkerdNamespace()),
-				"failed to create %s namespace: %s", TestHelper.GetLinkerdNamespace(), err)
-		}
-
-		identity := fmt.Sprintf("identity.%s.%s", TestHelper.GetLinkerdNamespace(), TestHelper.GetClusterDomain())
-
-		root, err := tls.GenerateRootCAWithDefaults(identity)
-		if err != nil {
-			testutil.AnnotatedFatal(t, "error generating root CA", err)
-		}
-
-		// instead of passing the roots and key around we generate
-		// two secrets here. The second one will be used in the
-		// external_issuer_test to update the first one and trigger
-		// cert rotation in the identity service. That allows us
-		// to generated the certs on the fly and use custom domain.
-
-		if err = TestHelper.CreateTLSSecret(
-			k8s.IdentityIssuerSecretName,
-			root.Cred.Crt.EncodeCertificatePEM(),
-			root.Cred.Crt.EncodeCertificatePEM(),
-			root.Cred.EncodePrivateKeyPEM()); err != nil {
-			testutil.AnnotatedFatal(t, "error creating TLS secret", err)
-		}
-
-		crt2, err := root.GenerateCA(identity, -1)
-		if err != nil {
-			testutil.AnnotatedFatal(t, "error generating CA", err)
-		}
-
-		if err = TestHelper.CreateTLSSecret(
-			k8s.IdentityIssuerSecretName+"-new",
-			root.Cred.Crt.EncodeCertificatePEM(),
-			crt2.Cred.EncodeCertificatePEM(),
-			crt2.Cred.EncodePrivateKeyPEM()); err != nil {
-			testutil.AnnotatedFatal(t, "error creating TLS secret (-new)", err)
-		}
-	}
-
 	if TestHelper.UpgradeFromVersion() != "" {
 
 		cmd = "upgrade"
@@ -402,24 +283,24 @@ func TestInstallOrUpgradeCli(t *testing.T) {
 		expectedDeployments[k] = v
 	}
 
-	if TestHelper.ExternalPrometheus() {
-
-		// Install external prometheus
-		out, err := TestHelper.LinkerdRun("inject", "testdata/external_prometheus.yaml", "--manual")
+	// Install Linkerd Viz Extension
+	if TestHelper.UpgradeFromVersion() != "" {
+		exec = append(vizCmd, vizArgs...)
+		out, err = TestHelper.LinkerdRun(exec...)
 		if err != nil {
-			testutil.AnnotatedFatalf(t, "'linkerd inject' command failed", "'linkerd inject' command failed: %s", err)
+			testutil.AnnotatedFatal(t, "'linkerd viz install' command failed", err)
 		}
 
-		out, err = TestHelper.KubectlApply(out, "")
+		out, err = TestHelper.KubectlApplyWithArgs(out, []string{
+			"--prune",
+			"-l", "linkerd.io/extension=viz",
+		}...)
 		if err != nil {
 			testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
-				"kubectl apply command failed\n%s", out)
+				"'kubectl apply' command failed\n%s", out)
 		}
 
-		expectedDeployments["prometheus"] = testutil.DeploySpec{Namespace: "external-prometheus", Replicas: 1}
-
-		// Update args to use external prometheus
-		vizArgs = append(vizArgs, "--set", "prometheusUrl=http://prometheus.external-prometheus.svc.cluster.local:9090", "--set", "prometheus.enabled=false")
+		TestHelper.WaitRollout(t, expectedDeployments)
 	}
 
 	// Install Linkerd Viz Extension
@@ -439,6 +320,7 @@ func TestInstallOrUpgradeCli(t *testing.T) {
 	}
 
 	TestHelper.WaitRollout(t, expectedDeployments)
+
 }
 
 // These need to be updated (if there are changes) once a new stable is released
@@ -551,7 +433,12 @@ func TestControlPlaneResourcesPostInstall(t *testing.T) {
 	expectedServices := linkerdSvcEdge
 	expectedDeployments := testutil.LinkerdDeployReplicasEdge
 	if !TestHelper.ExternalPrometheus() {
-		expectedServices = append(expectedServices, testutil.Service{Namespace: "linkerd-viz", Name: "prometheus"})
+		vizServices := []testutil.Service{
+			{Namespace: "linkerd-viz", Name: "web"},
+			{Namespace: "linkerd-viz", Name: "tap"},
+			{Namespace: "linkerd-viz", Name: "prometheus"},
+		}
+		expectedServices = append(expectedServices, vizServices...)
 		expectedDeployments["prometheus"] = testutil.DeploySpec{Namespace: "linkerd-viz", Replicas: 1}
 	}
 
@@ -563,41 +450,6 @@ func TestControlPlaneResourcesPostInstall(t *testing.T) {
 	testutil.TestResourcesPostInstall(TestHelper.GetLinkerdNamespace(), expectedServices, expectedDeployments, TestHelper, t)
 }
 
-func TestInstallMulticluster(t *testing.T) {
-	if TestHelper.GetMulticlusterHelmReleaseName() != "" {
-		flags := []string{
-			"--set", "linkerdVersion=" + TestHelper.GetVersion(),
-		}
-		if stdout, stderr, err := TestHelper.HelmInstallMulticluster(TestHelper.GetMulticlusterHelmChart(), flags...); err != nil {
-			testutil.AnnotatedFatalf(t, "'helm install' command failed",
-				"'helm install' command failed\n%s\n%s", stdout, stderr)
-		}
-		TestHelper.AddInstalledExtension(multiclusterExtensionName)
-	} else if TestHelper.Multicluster() {
-		exec := append([]string{"multicluster"}, []string{
-			"install",
-		}...)
-		out, err := TestHelper.LinkerdRun(exec...)
-		if err != nil {
-			testutil.AnnotatedFatal(t, "'linkerd multicluster install' command failed", err)
-		}
-
-		out, err = TestHelper.KubectlApply(out, "")
-		if err != nil {
-			testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
-				"'kubectl apply' command failed\n%s", out)
-		}
-		TestHelper.AddInstalledExtension(multiclusterExtensionName)
-	}
-}
-
-func TestMulticlusterResourcesPostInstall(t *testing.T) {
-	if !TestHelper.Multicluster() {
-		return
-	}
-	testutil.TestResourcesPostInstall(TestHelper.GetMulticlusterNamespace(), multiclusterSvcs, testutil.MulticlusterDeployReplicas, TestHelper, t)
-}
-
 func TestCheckHelmStableBeforeUpgrade(t *testing.T) {
 	if TestHelper.UpgradeHelmFromVersion() == "" {
 		t.Skip("Skipping as this is not a helm upgrade test")
@@ -1000,50 +852,6 @@ func TestCheckPostInstall(t *testing.T) {
 	testCheckCommand(t, "proxy", TestHelper.GetVersion(), TestHelper.GetLinkerdNamespace(), "")
 }
 
-func TestCheckViz(t *testing.T) {
-	cmd := []string{"viz", "check", "--wait=60m"}
-	golden := "check.viz.golden"
-	if TestHelper.ExternalPrometheus() {
-		golden = "check.viz.external-prometheus.golden"
-	}
-
-	pods, err := TestHelper.KubernetesHelper.GetPods(context.Background(), TestHelper.GetVizNamespace(), nil)
-	if err != nil {
-		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to retrieve pods: %s", err), err)
-	}
-
-	tpl := template.Must(template.ParseFiles("testdata" + "/" + golden))
-	vars := struct {
-		ProxyVersionErr string
-		HintURL         string
-	}{
-		healthcheck.CheckProxyVersionsUpToDate(pods, version.Channels{}).Error(),
-		healthcheck.HintBaseURL(TestHelper.GetVersion()),
-	}
-
-	var expected bytes.Buffer
-	if err := tpl.Execute(&expected, vars); err != nil {
-		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to parse check.viz.golden template: %s", err), err)
-	}
-
-	timeout := 5 * time.Minute
-	err = TestHelper.RetryFor(timeout, func() error {
-		out, err := TestHelper.LinkerdRun(cmd...)
-		if err != nil {
-			return fmt.Errorf("'linkerd viz check' command failed\n%s", err)
-		}
-
-		if out != expected.String() {
-			return fmt.Errorf(
-				"Expected:\n%s\nActual:\n%s", expected.String(), out)
-		}
-		return nil
-	})
-	if err != nil {
-		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd viz check' command timed-out (%s)", timeout), err)
-	}
-}
-
 func TestUpgradeTestAppWorksAfterUpgrade(t *testing.T) {
 	if TestHelper.UpgradeFromVersion() != "" {
 		testAppNamespace := "upgrade-test"
@@ -1108,27 +916,3 @@ func TestRestarts(t *testing.T) {
 		}
 	}
 }
-
-func TestCheckMulticluster(t *testing.T) {
-	if TestHelper.GetMulticlusterHelmReleaseName() != "" || TestHelper.Multicluster() {
-		cmd := []string{"multicluster", "check", "--wait=60m"}
-		golden := "check.multicluster.golden"
-		timeout := time.Minute
-		err := TestHelper.RetryFor(timeout, func() error {
-			out, err := TestHelper.LinkerdRun(cmd...)
-			if err != nil {
-				return fmt.Errorf("'linkerd multicluster check' command failed\n%s", err)
-			}
-			err = TestHelper.ValidateOutput(out, golden)
-			if err != nil {
-				return fmt.Errorf("received unexpected output\n%s", err.Error())
-			}
-			return nil
-		})
-		if err != nil {
-			testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd multicluster check' command timed-out (%s)", timeout), err)
-		}
-	} else {
-		t.Skip("Skipping for non multicluster test")
-	}
-}
diff --git a/test/integration/inject/inject_test.go b/test/integration/install/inject/inject_test.go
similarity index 100%
rename from test/integration/inject/inject_test.go
rename to test/integration/install/inject/inject_test.go
diff --git a/test/integration/inject/testdata/inject_test.yaml b/test/integration/install/inject/testdata/inject_test.yaml
similarity index 100%
rename from test/integration/inject/testdata/inject_test.yaml
rename to test/integration/install/inject/testdata/inject_test.yaml
diff --git a/test/integration/inject/testdata/pods.yaml b/test/integration/install/inject/testdata/pods.yaml
similarity index 100%
rename from test/integration/inject/testdata/pods.yaml
rename to test/integration/install/inject/testdata/pods.yaml
diff --git a/test/integration/inject/testdata/services.yaml b/test/integration/install/inject/testdata/services.yaml
similarity index 100%
rename from test/integration/inject/testdata/services.yaml
rename to test/integration/install/inject/testdata/services.yaml
diff --git a/test/integration/smoke/install_smoke_test.go b/test/integration/install/smoke/install_smoke_test.go
similarity index 100%
rename from test/integration/smoke/install_smoke_test.go
rename to test/integration/install/smoke/install_smoke_test.go
diff --git a/test/integration/smoke/testdata/check.proxy.golden b/test/integration/install/smoke/testdata/check.proxy.golden
similarity index 100%
rename from test/integration/smoke/testdata/check.proxy.golden
rename to test/integration/install/smoke/testdata/check.proxy.golden
diff --git a/test/integration/smoke/testdata/inject.report.golden b/test/integration/install/smoke/testdata/inject.report.golden
similarity index 100%
rename from test/integration/smoke/testdata/inject.report.golden
rename to test/integration/install/smoke/testdata/inject.report.golden
diff --git a/test/integration/smoke/testdata/smoke_test.yaml b/test/integration/install/smoke/testdata/smoke_test.yaml
similarity index 100%
rename from test/integration/smoke/testdata/smoke_test.yaml
rename to test/integration/install/smoke/testdata/smoke_test.yaml
diff --git a/test/integration/testdata/check.cni.proxy.golden b/test/integration/install/testdata/check.cni.proxy.golden
similarity index 100%
rename from test/integration/testdata/check.cni.proxy.golden
rename to test/integration/install/testdata/check.cni.proxy.golden
diff --git a/test/integration/testdata/check.config.golden b/test/integration/install/testdata/check.config.golden
similarity index 100%
rename from test/integration/testdata/check.config.golden
rename to test/integration/install/testdata/check.config.golden
diff --git a/test/integration/testdata/check.multicluster.proxy.golden b/test/integration/install/testdata/check.multicluster.proxy.golden
similarity index 100%
rename from test/integration/testdata/check.multicluster.proxy.golden
rename to test/integration/install/testdata/check.multicluster.proxy.golden
diff --git a/test/integration/testdata/check.pre.golden b/test/integration/install/testdata/check.pre.golden
similarity index 100%
rename from test/integration/testdata/check.pre.golden
rename to test/integration/install/testdata/check.pre.golden
diff --git a/test/integration/testdata/check.proxy.golden b/test/integration/install/testdata/check.proxy.golden
similarity index 100%
rename from test/integration/testdata/check.proxy.golden
rename to test/integration/install/testdata/check.proxy.golden
diff --git a/test/integration/testdata/check.viz.proxy.golden b/test/integration/install/testdata/check.viz.proxy.golden
similarity index 100%
rename from test/integration/testdata/check.viz.proxy.golden
rename to test/integration/install/testdata/check.viz.proxy.golden
diff --git a/test/integration/testdata/inject.report.golden b/test/integration/install/testdata/inject.report.golden
similarity index 100%
rename from test/integration/testdata/inject.report.golden
rename to test/integration/install/testdata/inject.report.golden
diff --git a/test/integration/install/testdata/smoke_test.yaml b/test/integration/install/testdata/smoke_test.yaml
new file mode 100644
index 000000000..a75c47acf
--- /dev/null
+++ b/test/integration/install/testdata/smoke_test.yaml
@@ -0,0 +1,120 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: smoke-test-terminus
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: smoke-test-terminus
+  template:
+    metadata:
+      labels:
+        app: smoke-test-terminus
+    spec:
+      containers:
+      - name: http-to-grpc
+        image: buoyantio/bb:v0.0.6
+        args: ["terminus", "--grpc-server-port", "9090", "--response-text", "BANANA"]
+        ports:
+        - containerPort: 9090
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: smoke-test-terminus-svc
+spec:
+  selector:
+    app: smoke-test-terminus
+  ports:
+  - name: grpc
+    port: 9090
+    targetPort: 9090
+---
+apiVersion: policy.linkerd.io/v1beta1
+kind: Server
+metadata:
+  name: smoke-test-terminus
+spec:
+  podSelector:
+    matchLabels:
+      app: smoke-test-terminus
+  port: 9090
+  proxyProtocol: gRPC
+---
+apiVersion: policy.linkerd.io/v1beta1
+kind: ServerAuthorization
+metadata:
+  name: smoke-test-terminus
+spec:
+  server:
+    name: smoke-test-terminus
+  client:
+    meshTLS:
+      serviceAccounts:
+      - name: default
+        namespace: linkerd-smoke-test
+      - name: default
+        namespace: linkerd-smoke-test-manual
+      - name: default
+        namespace: linkerd-smoke-test-ann
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: smoke-test-gateway
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: smoke-test-gateway
+  template:
+    metadata:
+      labels:
+        app: smoke-test-gateway
+    spec:
+      containers:
+      - name: http-to-grpc
+        image: buoyantio/bb:v0.0.6
+        args: ["point-to-point-channel", "--grpc-downstream-server", "smoke-test-terminus-svc:9090", "--h1-server-port", "8080"]
+        ports:
+        - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: smoke-test-gateway-svc
+spec:
+  selector:
+    app: smoke-test-gateway
+  ports:
+  - name: http
+    port: 8080
+    targetPort: 8080
+---
+apiVersion: policy.linkerd.io/v1beta1
+kind: Server
+metadata:
+  name: smoke-test-proxy-admin
+spec:
+  podSelector:
+    matchExpressions:
+    - key: app
+      operator: In
+      values:
+      - smoke-test-terminus
+      - smoke-test-gateway
+  port: linkerd-admin
+  proxyProtocol: HTTP/1
+---
+apiVersion: policy.linkerd.io/v1beta1
+kind: ServerAuthorization
+metadata:
+  name: smoke-test-proxy-admin
+spec:
+  server:
+    name: smoke-test-proxy-admin
+  client:
+    # for kubelet probes
+    unauthenticated: true
diff --git a/test/integration/testdata/upgrade_test.yaml b/test/integration/install/testdata/upgrade_test.yaml
similarity index 100%
rename from test/integration/testdata/upgrade_test.yaml
rename to test/integration/install/testdata/upgrade_test.yaml
diff --git a/test/integration/uninstall/testdata/check.pre.golden b/test/integration/install/uninstall/testdata/check.pre.golden
similarity index 100%
rename from test/integration/uninstall/testdata/check.pre.golden
rename to test/integration/install/uninstall/testdata/check.pre.golden
diff --git a/test/integration/uninstall/uninstall_test.go b/test/integration/install/uninstall/uninstall_test.go
similarity index 100%
rename from test/integration/uninstall/uninstall_test.go
rename to test/integration/install/uninstall/uninstall_test.go
diff --git a/test/integration/multicluster/1_install_test.go b/test/integration/multicluster/1_install_test.go
new file mode 100644
index 000000000..b9911de5a
--- /dev/null
+++ b/test/integration/multicluster/1_install_test.go
@@ -0,0 +1,127 @@
+package multiclustertest
+
+import (
+	"fmt"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/linkerd/linkerd2/testutil"
+)
+
+//////////////////////
+///   TEST SETUP   ///
+//////////////////////
+
+var (
+	TestHelper *testutil.TestHelper
+)
+
+func TestMain(m *testing.M) {
+	TestHelper = testutil.NewTestHelper()
+	os.Exit(m.Run())
+}
+
+// TestInstall will install the linkerd control plane to be used in the rest of
+// the deep suite tests.
+func TestInstall(t *testing.T) {
+	certsPath := TestHelper.CertsPath()
+	if certsPath == "" {
+		testutil.AnnotatedFatal(t, "cannot run multicluster test without a valid certificate path")
+	}
+
+	cmd := []string{
+		"install",
+		"--controller-log-level", "debug",
+		"--proxy-version", TestHelper.GetVersion(),
+		"--set", "heartbeatSchedule=1 2 3 4 5",
+		"--identity-trust-anchors-file", certsPath + "/ca.crt",
+		"--identity-issuer-certificate-file", certsPath + "/issuer.crt",
+		"--identity-issuer-key-file", certsPath + "/issuer.key",
+	}
+
+	// Pipe cmd & args to `linkerd`
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdDeployReplicasEdge)
+}
+
+func TestInstallMulticluster(t *testing.T) {
+	out, err := TestHelper.LinkerdRun("multicluster", "install")
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd multicluster install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApply(out, "")
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+	TestHelper.AddInstalledExtension("multicluster")
+}
+
+func TestMulticlusterResourcesPostInstall(t *testing.T) {
+	multiclusterSvcs := []testutil.Service{
+		{Namespace: "linkerd-multicluster", Name: "linkerd-gateway"},
+	}
+
+	testutil.TestResourcesPostInstall(TestHelper.GetMulticlusterNamespace(), multiclusterSvcs, testutil.MulticlusterDeployReplicas, TestHelper, t)
+}
+
+// TestInstallViz will install the viz extension, needed to verify whether the
+// gateway probe succeeded.
+// TODO (matei): can the dependency on viz be removed?
+func TestInstallViz(t *testing.T) {
+	cmd := []string{
+		"viz",
+		"install",
+		"--set", fmt.Sprintf("namespace=%s", TestHelper.GetVizNamespace()),
+	}
+
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd viz install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdVizDeployReplicas)
+
+}
+
+func TestCheckMulticluster(t *testing.T) {
+	if TestHelper.GetMulticlusterHelmReleaseName() != "" || TestHelper.Multicluster() {
+		cmd := []string{"multicluster", "check", "--wait=60m"}
+		golden := "check.multicluster.golden"
+		timeout := time.Minute
+		err := TestHelper.RetryFor(timeout, func() error {
+			out, err := TestHelper.LinkerdRun(cmd...)
+			if err != nil {
+				return fmt.Errorf("'linkerd multicluster check' command failed\n%s", err)
+			}
+			err = TestHelper.ValidateOutput(out, golden)
+			if err != nil {
+				return fmt.Errorf("received unexpected output\n%s", err.Error())
+			}
+			return nil
+		})
+		if err != nil {
+			testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd multicluster check' command timed-out (%s)", timeout), err)
+		}
+	} else {
+		t.Skip("Skipping for non multicluster test")
+	}
+}
diff --git a/test/integration/multicluster/source/source_test.go b/test/integration/multicluster/source/source_test.go
index fd0d8759b..0532c2b33 100644
--- a/test/integration/multicluster/source/source_test.go
+++ b/test/integration/multicluster/source/source_test.go
@@ -16,10 +16,6 @@ var TestHelper *testutil.TestHelper
 
 func TestMain(m *testing.M) {
 	TestHelper = testutil.NewTestHelper()
-	if !TestHelper.Multicluster() {
-		fmt.Fprintln(os.Stderr, "Multicluster test disabled")
-		os.Exit(0)
-	}
 	os.Exit(m.Run())
 }
 
diff --git a/test/integration/multicluster/target-statefulset/target_statefulset_test.go b/test/integration/multicluster/target-statefulset/target_statefulset_test.go
index 443179040..264c6ad52 100644
--- a/test/integration/multicluster/target-statefulset/target_statefulset_test.go
+++ b/test/integration/multicluster/target-statefulset/target_statefulset_test.go
@@ -26,10 +26,6 @@ var (
 
 func TestMain(m *testing.M) {
 	TestHelper = testutil.NewTestHelper()
-	if !TestHelper.Multicluster() {
-		fmt.Fprintln(os.Stderr, "Multicluster test disabled")
-		os.Exit(0)
-	}
 	os.Exit(m.Run())
 }
 
diff --git a/test/integration/multicluster/target1/target_test.go b/test/integration/multicluster/target1/target_test.go
index 6504f8037..78b048ccf 100644
--- a/test/integration/multicluster/target1/target_test.go
+++ b/test/integration/multicluster/target1/target_test.go
@@ -2,7 +2,6 @@ package target1
 
 import (
 	"context"
-	"fmt"
 	"os"
 	"testing"
 
@@ -13,10 +12,6 @@ var TestHelper *testutil.TestHelper
 
 func TestMain(m *testing.M) {
 	TestHelper = testutil.NewTestHelper()
-	if !TestHelper.Multicluster() {
-		fmt.Fprintln(os.Stderr, "Multicluster test disabled")
-		os.Exit(0)
-	}
 	os.Exit(m.Run())
 }
 
diff --git a/test/integration/multicluster/target2/target_test.go b/test/integration/multicluster/target2/target_test.go
index 093a966e5..341e09fe8 100644
--- a/test/integration/multicluster/target2/target_test.go
+++ b/test/integration/multicluster/target2/target_test.go
@@ -14,10 +14,6 @@ var TestHelper *testutil.TestHelper
 
 func TestMain(m *testing.M) {
 	TestHelper = testutil.NewTestHelper()
-	if !TestHelper.Multicluster() {
-		fmt.Fprintln(os.Stderr, "Multicluster test disabled")
-		os.Exit(0)
-	}
 	os.Exit(m.Run())
 }
 
diff --git a/test/integration/testdata/check.multicluster.golden b/test/integration/multicluster/testdata/check.multicluster.golden
similarity index 100%
rename from test/integration/testdata/check.multicluster.golden
rename to test/integration/multicluster/testdata/check.multicluster.golden
diff --git a/test/integration/testdata/check.cni.golden b/test/integration/testdata/check.cni.golden
deleted file mode 100644
index 0f4b63b1c..000000000
--- a/test/integration/testdata/check.cni.golden
+++ /dev/null
@@ -1,78 +0,0 @@
-Linkerd core checks
-===================
-
-kubernetes-api
---------------
-√ can initialize the client
-√ can query the Kubernetes API
-
-kubernetes-version
-------------------
-√ is running the minimum Kubernetes API version
-√ is running the minimum kubectl version
-
-linkerd-existence
------------------
-√ 'linkerd-config' config map exists
-√ heartbeat ServiceAccount exist
-√ control plane replica sets are ready
-√ no unschedulable pods
-√ control plane pods are ready
-√ cluster networks contains all node podCIDRs
-
-linkerd-config
---------------
-√ control plane Namespace exists
-√ control plane ClusterRoles exist
-√ control plane ClusterRoleBindings exist
-√ control plane ServiceAccounts exist
-√ control plane CustomResourceDefinitions exist
-√ control plane MutatingWebhookConfigurations exist
-√ control plane ValidatingWebhookConfigurations exist
-√ proxy-init container runs as root user if docker container runtime is used
-
-linkerd-cni-plugin
-------------------
-√ cni plugin ConfigMap exists
-√ cni plugin ClusterRole exists
-√ cni plugin ClusterRoleBinding exists
-√ cni plugin ServiceAccount exists
-√ cni plugin DaemonSet exists
-√ cni plugin pod is running on all nodes
-
-linkerd-identity
-----------------
-√ certificate config is valid
-√ trust anchors are using supported crypto algorithm
-√ trust anchors are within their validity period
-√ trust anchors are valid for at least 60 days
-√ issuer cert is using supported crypto algorithm
-√ issuer cert is within its validity period
-√ issuer cert is valid for at least 60 days
-√ issuer cert is issued by the trust anchor
-
-linkerd-webhooks-and-apisvc-tls
--------------------------------
-√ proxy-injector webhook has valid cert
-√ proxy-injector cert is valid for at least 60 days
-√ sp-validator webhook has valid cert
-√ sp-validator cert is valid for at least 60 days
-√ policy-validator webhook has valid cert
-√ policy-validator cert is valid for at least 60 days
-
-linkerd-version
----------------
-√ can determine the latest version
-√ cli is up-to-date
-
-control-plane-version
----------------------
-√ can retrieve the control plane version
-√ control plane is up-to-date
-√ control plane and cli versions match
-
-linkerd-control-plane-proxy
----------------------------
-√ control plane proxies are healthy
-√ control plane proxies are up-to-date
-√ control plane proxies and cli versions match
diff --git a/test/integration/testdata/check.golden b/test/integration/testdata/check.golden
deleted file mode 100644
index f4b2eb732..000000000
--- a/test/integration/testdata/check.golden
+++ /dev/null
@@ -1,69 +0,0 @@
-Linkerd core checks
-===================
-
-kubernetes-api
---------------
-√ can initialize the client
-√ can query the Kubernetes API
-
-kubernetes-version
-------------------
-√ is running the minimum Kubernetes API version
-√ is running the minimum kubectl version
-
-linkerd-existence
------------------
-√ 'linkerd-config' config map exists
-√ heartbeat ServiceAccount exist
-√ control plane replica sets are ready
-√ no unschedulable pods
-√ control plane pods are ready
-√ cluster networks contains all node podCIDRs
-
-linkerd-config
---------------
-√ control plane Namespace exists
-√ control plane ClusterRoles exist
-√ control plane ClusterRoleBindings exist
-√ control plane ServiceAccounts exist
-√ control plane CustomResourceDefinitions exist
-√ control plane MutatingWebhookConfigurations exist
-√ control plane ValidatingWebhookConfigurations exist
-√ proxy-init container runs as root user if docker container runtime is used
-
-linkerd-identity
-----------------
-√ certificate config is valid
-√ trust anchors are using supported crypto algorithm
-√ trust anchors are within their validity period
-√ trust anchors are valid for at least 60 days
-√ issuer cert is using supported crypto algorithm
-√ issuer cert is within its validity period
-√ issuer cert is valid for at least 60 days
-√ issuer cert is issued by the trust anchor
-
-linkerd-webhooks-and-apisvc-tls
--------------------------------
-√ proxy-injector webhook has valid cert
-√ proxy-injector cert is valid for at least 60 days
-√ sp-validator webhook has valid cert
-√ sp-validator cert is valid for at least 60 days
-√ policy-validator webhook has valid cert
-√ policy-validator cert is valid for at least 60 days
-
-linkerd-version
----------------
-√ can determine the latest version
-√ cli is up-to-date
-
-control-plane-version
----------------------
-√ can retrieve the control plane version
-√ control plane is up-to-date
-√ control plane and cli versions match
-
-linkerd-control-plane-proxy
----------------------------
-√ control plane proxies are healthy
-√ control plane proxies are up-to-date
-√ control plane proxies and cli versions match
diff --git a/test/integration/viz/1_install_test.go b/test/integration/viz/1_install_test.go
new file mode 100644
index 000000000..13264dcf4
--- /dev/null
+++ b/test/integration/viz/1_install_test.go
@@ -0,0 +1,122 @@
+package viztest
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"html/template"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/linkerd/linkerd2/pkg/healthcheck"
+	"github.com/linkerd/linkerd2/pkg/version"
+	"github.com/linkerd/linkerd2/testutil"
+)
+
+//////////////////////
+///   TEST SETUP   ///
+//////////////////////
+
+var (
+	TestHelper *testutil.TestHelper
+)
+
+func TestMain(m *testing.M) {
+	TestHelper = testutil.NewTestHelper()
+	os.Exit(m.Run())
+}
+
+//////////////////////
+/// TEST EXECUTION ///
+//////////////////////
+
+// TestInstallLinkerd will install the linkerd control plane to be used in the rest of
+// the deep suite tests
+func TestInstallLinkerd(t *testing.T) {
+	cmd := []string{
+		"install",
+		"--controller-log-level", "debug",
+		"--proxy-version", TestHelper.GetVersion(),
+		"--set", "heartbeatSchedule=1 2 3 4 5",
+	}
+
+	// Pipe cmd & args to `linkerd`
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdDeployReplicasEdge)
+}
+
+// TestInstallViz will install the viz extension to be used by the rest of the
+// tests in the viz suite
+func TestInstallViz(t *testing.T) {
+	cmd := []string{
+		"viz",
+		"install",
+		"--set", fmt.Sprintf("namespace=%s", TestHelper.GetVizNamespace()),
+	}
+
+	out, err := TestHelper.LinkerdRun(cmd...)
+	if err != nil {
+		testutil.AnnotatedFatal(t, "'linkerd viz install' command failed", err)
+	}
+
+	out, err = TestHelper.KubectlApplyWithArgs(out)
+	if err != nil {
+		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
+			"'kubectl apply' command failed\n%s", out)
+	}
+
+	TestHelper.WaitRollout(t, testutil.LinkerdVizDeployReplicas)
+
+}
+
+func TestCheckViz(t *testing.T) {
+	cmd := []string{"viz", "check", "--wait=60m"}
+	golden := "check.viz.golden"
+
+	pods, err := TestHelper.KubernetesHelper.GetPods(context.Background(), TestHelper.GetVizNamespace(), nil)
+	if err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to retrieve pods: %s", err), err)
+	}
+
+	tpl := template.Must(template.ParseFiles("testdata" + "/" + golden))
+	vars := struct {
+		ProxyVersionErr string
+		HintURL         string
+	}{
+		healthcheck.CheckProxyVersionsUpToDate(pods, version.Channels{}).Error(),
+		healthcheck.HintBaseURL(TestHelper.GetVersion()),
+	}
+
+	var expected bytes.Buffer
+	if err := tpl.Execute(&expected, vars); err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("failed to parse %s template: %s", golden, err), err)
+	}
+
+	timeout := 5 * time.Minute
+	err = TestHelper.RetryFor(timeout, func() error {
+		out, err := TestHelper.LinkerdRun(cmd...)
+		if err != nil {
+			return fmt.Errorf("'linkerd viz check' command failed\n%s", err)
+		}
+
+		if out != expected.String() {
+			return fmt.Errorf(
+				"Expected:\n%s\nActual:\n%s", expected.String(), out)
+		}
+		return nil
+	})
+	if err != nil {
+		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd viz check' command timed-out (%s)", timeout), err)
+	}
+}
diff --git a/test/integration/edges/edges_test.go b/test/integration/viz/edges/edges_test.go
similarity index 96%
rename from test/integration/edges/edges_test.go
rename to test/integration/viz/edges/edges_test.go
index e8c5232a9..d76cee12e 100644
--- a/test/integration/edges/edges_test.go
+++ b/test/integration/viz/edges/edges_test.go
@@ -29,9 +29,6 @@ func TestMain(m *testing.M) {
 func TestEdges(t *testing.T) {
 	ns := TestHelper.GetLinkerdNamespace()
 	promNs := TestHelper.GetVizNamespace()
-	if TestHelper.ExternalPrometheus() {
-		promNs = "external-prometheus"
-	}
 	vars := struct {
 		Ns     string
 		PromNs string
@@ -141,9 +138,6 @@ func TestDirectEdges(t *testing.T) {
 		// check edges
 		timeout := 50 * time.Second
 		testDataPath := "testdata"
-		if TestHelper.ExternalPrometheus() {
-			testDataPath += "/external_prometheus"
-		}
 		err = TestHelper.RetryFor(timeout, func() error {
 			out, err = TestHelper.LinkerdRun("-n", testNamespace, "-o", "json", "viz", "edges", "deploy")
 			if err != nil {
diff --git a/test/integration/edges/testdata/direct_edges.golden b/test/integration/viz/edges/testdata/direct_edges.golden
similarity index 100%
rename from test/integration/edges/testdata/direct_edges.golden
rename to test/integration/viz/edges/testdata/direct_edges.golden
diff --git a/test/integration/edges/testdata/linkerd_edges.golden b/test/integration/viz/edges/testdata/linkerd_edges.golden
similarity index 100%
rename from test/integration/edges/testdata/linkerd_edges.golden
rename to test/integration/viz/edges/testdata/linkerd_edges.golden
diff --git a/test/integration/edges/testdata/slow-cooker.yaml b/test/integration/viz/edges/testdata/slow-cooker.yaml
similarity index 100%
rename from test/integration/edges/testdata/slow-cooker.yaml
rename to test/integration/viz/edges/testdata/slow-cooker.yaml
diff --git a/test/integration/edges/testdata/terminus.yaml b/test/integration/viz/edges/testdata/terminus.yaml
similarity index 100%
rename from test/integration/edges/testdata/terminus.yaml
rename to test/integration/viz/edges/testdata/terminus.yaml
diff --git a/test/integration/policy/policy_test.go b/test/integration/viz/policy/policy_test.go
similarity index 100%
rename from test/integration/policy/policy_test.go
rename to test/integration/viz/policy/policy_test.go
diff --git a/test/integration/policy/testdata/emoji-policy.yaml b/test/integration/viz/policy/testdata/emoji-policy.yaml
similarity index 100%
rename from test/integration/policy/testdata/emoji-policy.yaml
rename to test/integration/viz/policy/testdata/emoji-policy.yaml
diff --git a/test/integration/policy/testdata/emojivoto.yaml b/test/integration/viz/policy/testdata/emojivoto.yaml
similarity index 100%
rename from test/integration/policy/testdata/emojivoto.yaml
rename to test/integration/viz/policy/testdata/emojivoto.yaml
diff --git a/test/integration/routes/routes_test.go b/test/integration/viz/routes/routes_test.go
similarity index 100%
rename from test/integration/routes/routes_test.go
rename to test/integration/viz/routes/routes_test.go
diff --git a/test/integration/serviceprofiles/serviceprofiles_test.go b/test/integration/viz/serviceprofiles/serviceprofiles_test.go
similarity index 100%
rename from test/integration/serviceprofiles/serviceprofiles_test.go
rename to test/integration/viz/serviceprofiles/serviceprofiles_test.go
diff --git a/test/integration/serviceprofiles/testdata/hello_world.yaml b/test/integration/viz/serviceprofiles/testdata/hello_world.yaml
similarity index 100%
rename from test/integration/serviceprofiles/testdata/hello_world.yaml
rename to test/integration/viz/serviceprofiles/testdata/hello_world.yaml
diff --git a/test/integration/serviceprofiles/testdata/t3.swagger b/test/integration/viz/serviceprofiles/testdata/t3.swagger
similarity index 100%
rename from test/integration/serviceprofiles/testdata/t3.swagger
rename to test/integration/viz/serviceprofiles/testdata/t3.swagger
diff --git a/test/integration/serviceprofiles/testdata/tap_application.yaml b/test/integration/viz/serviceprofiles/testdata/tap_application.yaml
similarity index 100%
rename from test/integration/serviceprofiles/testdata/tap_application.yaml
rename to test/integration/viz/serviceprofiles/testdata/tap_application.yaml
diff --git a/test/integration/serviceprofiles/testdata/world.swagger b/test/integration/viz/serviceprofiles/testdata/world.swagger
similarity index 100%
rename from test/integration/serviceprofiles/testdata/world.swagger
rename to test/integration/viz/serviceprofiles/testdata/world.swagger
diff --git a/test/integration/stat/stat_test.go b/test/integration/viz/stat/stat_test.go
similarity index 97%
rename from test/integration/stat/stat_test.go
rename to test/integration/viz/stat/stat_test.go
index 925c2e853..dfed3f0c9 100644
--- a/test/integration/stat/stat_test.go
+++ b/test/integration/viz/stat/stat_test.go
@@ -48,13 +48,8 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 	metricsPod = pods[0]
 
 	// Retrieve Prometheus pod details
-	if TestHelper.ExternalPrometheus() {
-		prometheusNamespace = "external-prometheus"
-		prometheusDeployment = "prometheus"
-	} else {
-		prometheusNamespace = TestHelper.GetVizNamespace()
-		prometheusDeployment = "prometheus"
-	}
+	prometheusNamespace = TestHelper.GetVizNamespace()
+	prometheusDeployment = "prometheus"
 
 	pods, err = TestHelper.GetPodNamesForDeployment(ctx, prometheusNamespace, prometheusDeployment)
 	if err != nil {
diff --git a/test/integration/tap/tap_test.go b/test/integration/viz/tap/tap_test.go
similarity index 100%
rename from test/integration/tap/tap_test.go
rename to test/integration/viz/tap/tap_test.go
diff --git a/test/integration/tap/testdata/tap_application.yaml b/test/integration/viz/tap/testdata/tap_application.yaml
similarity index 100%
rename from test/integration/tap/testdata/tap_application.yaml
rename to test/integration/viz/tap/testdata/tap_application.yaml
diff --git a/test/integration/testdata/check.viz.golden b/test/integration/viz/testdata/check.viz.golden
similarity index 100%
rename from test/integration/testdata/check.viz.golden
rename to test/integration/viz/testdata/check.viz.golden
diff --git a/test/integration/tracing/testdata/check.jaeger.golden b/test/integration/viz/tracing/testdata/check.jaeger.golden
similarity index 100%
rename from test/integration/tracing/testdata/check.jaeger.golden
rename to test/integration/viz/tracing/testdata/check.jaeger.golden
diff --git a/test/integration/tracing/testdata/emojivoto.yaml b/test/integration/viz/tracing/testdata/emojivoto.yaml
similarity index 100%
rename from test/integration/tracing/testdata/emojivoto.yaml
rename to test/integration/viz/tracing/testdata/emojivoto.yaml
diff --git a/test/integration/tracing/tracing_test.go b/test/integration/viz/tracing/tracing_test.go
similarity index 100%
rename from test/integration/tracing/tracing_test.go
rename to test/integration/viz/tracing/tracing_test.go
diff --git a/test/integration/viz/trafficsplit/testdata/application.yaml b/test/integration/viz/trafficsplit/testdata/application.yaml
new file mode 100644
index 000000000..118e188c6
--- /dev/null
+++ b/test/integration/viz/trafficsplit/testdata/application.yaml
@@ -0,0 +1,116 @@
+# Two backend pods, one always failing
+# and another one returning OK response
+# Slowcooker is used to generate traffic
+# that will be routed via traffic split
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: backend
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: backend
+  template:
+    metadata:
+      labels:
+        app: backend
+    spec:
+      containers:
+      - name: backend
+        image: buoyantio/bb:v0.0.6
+        args:
+        - terminus
+        - "--h1-server-port=8080"
+        - "--response-text=backend1"
+        ports:
+        - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: backend-svc
+spec:
+  selector:
+    app: backend
+  ports:
+  - name: http
+    port: 8080
+    targetPort: 8080
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: failing
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: failing
+  template:
+    metadata:
+      labels:
+        app: failing
+    spec:
+      containers:
+      - name: failing
+        image: buoyantio/bb:v0.0.6
+        args:
+        - terminus
+        - "--h1-server-port=8080"
+        - "--response-text=failing"
+        - "--percent-failure=100"
+        ports:
+        - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: failing-svc
+spec:
+  selector:
+    app: failing
+  ports:
+  - name: http
+    port: 8080
+    targetPort: 8080
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: slow-cooker
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: slow-cooker
+  template:
+    metadata:
+      labels:
+        app: slow-cooker
+    spec:
+      containers:
+      - name: slow-cooker
+        image: buoyantio/slow_cooker:1.3.0
+        command:
+        - "/bin/sh"
+        args:
+        - "-c"
+        - |
+          sleep 5 # wait for pods to start
+          /slow_cooker/slow_cooker http://backend-svc:8080
+        ports:
+        - containerPort: 9999
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: slow-cooker
+spec:
+  selector:
+    app: slow-cooker
+  ports:
+  - name: metrics
+    port: 9999
+    targetPort: 9999
diff --git a/test/integration/trafficsplit/testdata/applications-at-diff-ports.yaml b/test/integration/viz/trafficsplit/testdata/applications-at-diff-ports.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/applications-at-diff-ports.yaml
rename to test/integration/viz/trafficsplit/testdata/applications-at-diff-ports.yaml
diff --git a/test/integration/trafficsplit/testdata/sp/traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/sp/traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/sp/traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/sp/traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/testdata/sp/updated-traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/sp/updated-traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/sp/updated-traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/sp/updated-traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/testdata/v1alpha1/traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/v1alpha1/traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/v1alpha1/traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/v1alpha1/traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/testdata/v1alpha1/updated-traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/v1alpha1/updated-traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/v1alpha1/updated-traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/v1alpha1/updated-traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/testdata/v1alpha2/traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/v1alpha2/traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/v1alpha2/traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/v1alpha2/traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/testdata/v1alpha2/updated-traffic-split-leaf-weights.yaml b/test/integration/viz/trafficsplit/testdata/v1alpha2/updated-traffic-split-leaf-weights.yaml
similarity index 100%
rename from test/integration/trafficsplit/testdata/v1alpha2/updated-traffic-split-leaf-weights.yaml
rename to test/integration/viz/trafficsplit/testdata/v1alpha2/updated-traffic-split-leaf-weights.yaml
diff --git a/test/integration/trafficsplit/trafficsplit_test.go b/test/integration/viz/trafficsplit/trafficsplit_test.go
similarity index 100%
rename from test/integration/trafficsplit/trafficsplit_test.go
rename to test/integration/viz/trafficsplit/trafficsplit_test.go
diff --git a/testutil/test_helper.go b/testutil/test_helper.go
index 3312c2a60..3eadcacd7 100644
--- a/testutil/test_helper.go
+++ b/testutil/test_helper.go
@@ -90,6 +90,12 @@ var LinkerdVizDeployReplicas = map[string]DeploySpec{
 	"web":          {"linkerd-viz", 1},
 }
 
+// MulticlusterDeployReplicas is a map containing the number of replicas for each Deployment and the main
+// container name for multicluster components
+var MulticlusterDeployReplicas = map[string]DeploySpec{
+	"linkerd-gateway": {"linkerd-multicluster", 1},
+}
+
 // NewGenericTestHelper returns a new *TestHelper from the options provided as function parameters.
 // This helper was created to be able to reuse this package without hard restrictions
 // as seen in `NewTestHelper()` which is primarily used with integration tests
@@ -143,12 +149,6 @@ func NewGenericTestHelper(
 	}
 }
 
-// MulticlusterDeployReplicas is a map containing the number of replicas for each Deployment and the main
-// container name for multicluster components
-var MulticlusterDeployReplicas = map[string]DeploySpec{
-	"linkerd-gateway": {"linkerd-multicluster", 1},
-}
-
 // NewTestHelper creates a new instance of TestHelper for the current test run.
 // The new TestHelper can be configured via command line flags.
 func NewTestHelper() *TestHelper {
@@ -157,6 +157,7 @@ func NewTestHelper() *TestHelper {
 		os.Exit(code)
 	}
 
+	// TODO (matei): clean-up flags
 	k8sContext := flag.String("k8s-context", "", "kubernetes context associated with the test cluster")
 	linkerd := flag.String("linkerd", "", "path to the linkerd binary to test")
 	namespace := flag.String("linkerd-namespace", "linkerd", "the namespace where linkerd is installed")
diff --git a/viz/cmd/testdata/install_default.golden b/viz/cmd/testdata/install_default.golden
index d4de5f160..739503966 100644
--- a/viz/cmd/testdata/install_default.golden
+++ b/viz/cmd/testdata/install_default.golden
@@ -625,7 +625,7 @@ data:
       - role: pod
       relabel_configs:
       - source_labels:
-        - __meta_kubernetes_pod_label_linkerd_io_control_plane_component
+        - __meta_kubernetes_pod_label_component
         - __meta_kubernetes_pod_container_port_name
         action: keep
         regex: linkerd-service-mirror;admin-http$
diff --git a/viz/cmd/testdata/install_default_overrides.golden b/viz/cmd/testdata/install_default_overrides.golden
index 8958594ad..8ed533d9e 100644
--- a/viz/cmd/testdata/install_default_overrides.golden
+++ b/viz/cmd/testdata/install_default_overrides.golden
@@ -625,7 +625,7 @@ data:
       - role: pod
       relabel_configs:
       - source_labels:
-        - __meta_kubernetes_pod_label_linkerd_io_control_plane_component
+        - __meta_kubernetes_pod_label_component
         - __meta_kubernetes_pod_container_port_name
         action: keep
         regex: linkerd-service-mirror;admin-http$
diff --git a/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden b/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
index d74133b88..d842e447d 100644
--- a/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
+++ b/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
@@ -625,7 +625,7 @@ data:
       - role: pod
       relabel_configs:
       - source_labels:
-        - __meta_kubernetes_pod_label_linkerd_io_control_plane_component
+        - __meta_kubernetes_pod_label_component
         - __meta_kubernetes_pod_container_port_name
         action: keep
         regex: linkerd-service-mirror;admin-http$
diff --git a/viz/cmd/testdata/install_proxy_resources.golden b/viz/cmd/testdata/install_proxy_resources.golden
index 98f839f27..4115f2ce2 100644
--- a/viz/cmd/testdata/install_proxy_resources.golden
+++ b/viz/cmd/testdata/install_proxy_resources.golden
@@ -625,7 +625,7 @@ data:
       - role: pod
       relabel_configs:
       - source_labels:
-        - __meta_kubernetes_pod_label_linkerd_io_control_plane_component
+        - __meta_kubernetes_pod_label_component
         - __meta_kubernetes_pod_container_port_name
         action: keep
         regex: linkerd-service-mirror;admin-http$