Repository: linkerd/linkerd2
Pull request #5461 (2020-12-31 06:14:04)

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



Patch:
diff --git a/.editorconfig b/.editorconfig
index 80f0001b6..00bfa6a08 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -29,6 +29,9 @@ indent_style = space
 [*.yml]
 indent_size = 2
 
+[*.yaml]
+indent_size = 2
+
 [*.proto]
 indent_size = 2
 indent_style = space
diff --git a/ADOPTERS.md b/ADOPTERS.md
index ad67e5cc4..4c6dca8a5 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -23,8 +23,10 @@
 - [Kairos](https://kairos.com)
 - [Kurio](https://kurio.id)
 - [LeadIQ](https://leadiq.com)
+- [Lendico](https://www.lendico.de/)
 - [M1 Finance](https://www.m1finance.com/)
 - [MattePaint](https://www.mattepaint.com/)
+- [Mentum](https://mentumqr.com/)
 - [MercedesBenz.io](https://www.mercedes-benz.io/)
 - [Mulligan Funding](https://www.mulliganfunding.com/)
 - [Mythical Games](https://mythicalgames.com/)
@@ -33,6 +35,7 @@
 - [Novolabs](https://novolabs.com)
 - [Paybase](https://paybase.io/)
 - [PlayStudios Asia](https://www.playstudios.asia)
+- [PlexTrac](https://plextrac.com)
 - [PriceKinetics (GVC Australia)](https://www.pricekinetics.com.au/)
 - [Projector](https://projector.com)
 - [Purdue University Global](https://www.purdueglobal.edu/)
diff --git a/bin/docker-build-jaeger-webhook b/bin/docker-build-jaeger-webhook
index dd1164461..3e7e20a03 100755
--- a/bin/docker-build-jaeger-webhook
+++ b/bin/docker-build-jaeger-webhook
@@ -15,7 +15,7 @@ rootdir=$( cd "$bindir"/.. && pwd )
 # shellcheck source=_tag.sh
 . "$bindir"/_tag.sh
 
-dockerfile=$rootdir/jaeger/proxy-mutator/Dockerfile
+dockerfile=$rootdir/jaeger/injector/Dockerfile
 tag=$(head_root_tag)
 docker_build jaeger-webhook "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag"
 
diff --git a/bin/k3d b/bin/k3d
index 644d490ca..8023a0b64 100755
--- a/bin/k3d
+++ b/bin/k3d
@@ -2,7 +2,7 @@
 
 set -eu
 
-k3dversion=v3.0.2
+k3dversion=v3.4.0
 
 bindir=$( cd "${0%/*}" && pwd )
 targetbin=$( cd "$bindir"/.. && pwd )/target/bin
diff --git a/bin/web b/bin/web
index af460b49f..5cdc8dd10 100755
--- a/bin/web
+++ b/bin/web
@@ -17,7 +17,7 @@ USAGE: web <command>
   * integration (cloud|local) - run the integration tests
       Note: Sauce Connect tunnel must be open for cloud tests
   * port-forward - setup a tunnel to a controller component and port
-      Example: port-forward controller 8085
+      Example: port-forward linkerd controller 8085
   * run - run a local server (sans. reloading)
   * setup - get the environment setup for development
       Note: any command line options are passed on to yarn
@@ -27,8 +27,8 @@ USAGE
 }; function --help { '-h' ;}
 
 check-for-linkerd() {
-  controller_pod=$(get-pod controller)
-  grafana_pod=$(get-pod grafana)
+  controller_pod=$(get-pod linkerd controller)
+  grafana_pod=$(get-pod linkerd-viz grafana)
 
   if [[ -z "${controller_pod// }" ]]; then
     err 'Controller is not running. Have you installed Linkerd?'
@@ -59,25 +59,31 @@ build() {
 }
 
 get-pod() {
-  if [ $# -ne 1 ]; then
-    echo "usage: bin/${0##*/} get-pod component-name" >&2
+  if [ $# -ne 2 ]; then
+    echo "usage: bin/${0##*/} get-pod namespace component-name" >&2
     exit 1
   fi
 
-  kubectl --namespace=linkerd get po \
-    --selector=linkerd.io/control-plane-component="$1" \
+
+  selector="linkerd.io/control-plane-component=$2"
+  if [ "$1" == "linkerd-viz" ]; then
+    selector="component=$2"
+  fi
+
+  kubectl --namespace="$1" get po \
+    --selector="$selector" \
     --field-selector='status.phase==Running' \
     -o jsonpath='{.items[*].metadata.name}'
 }
 
 port-forward() {
-  if [ $# -ne 2 ]; then
-    echo "usage: bin/${0##*/} port-forward component-name port-number" >&2
+  if [ $# -ne 3 ]; then
+    echo "usage: bin/${0##*/} port-forward namespace component-name port-number" >&2
     exit 1
   fi
 
   nc -z localhost "$2" || \
-    kubectl --namespace=linkerd port-forward "$(get-pod "$1")" "$2:$2"
+    kubectl --namespace="$1" port-forward "$(get-pod "$1" "$2")" "$3:$3"
 }
 
 run() {
@@ -89,8 +95,8 @@ run() {
   build
 
   check-for-linkerd && (
-    port-forward controller 8085 &
-    port-forward grafana 3000 &
+    port-forward linkerd controller 8085 &
+    port-forward linkerd-viz grafana 3000 &
   )
 
   cd "$ROOT"/web && \
diff --git a/charts/linkerd2/templates/controller.yaml b/charts/linkerd2/templates/controller.yaml
index 06477f197..a4f880a98 100644
--- a/charts/linkerd2/templates/controller.yaml
+++ b/charts/linkerd2/templates/controller.yaml
@@ -21,6 +21,24 @@ spec:
   - name: http
     port: 8085
     targetPort: 8085
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: controller
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: controller
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/destination.yaml b/charts/linkerd2/templates/destination.yaml
index e68ec7f3d..e26a0b94c 100644
--- a/charts/linkerd2/templates/destination.yaml
+++ b/charts/linkerd2/templates/destination.yaml
@@ -40,6 +40,24 @@ spec:
   - name: grpc
     port: 8086
     targetPort: 8086
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: destination
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: destination
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/identity.yaml b/charts/linkerd2/templates/identity.yaml
index 3821957f2..12a7f3d9f 100644
--- a/charts/linkerd2/templates/identity.yaml
+++ b/charts/linkerd2/templates/identity.yaml
@@ -60,6 +60,24 @@ spec:
   - name: grpc
     port: 8080
     targetPort: 8080
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: identity
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: identity
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/charts/linkerd2/templates/proxy-injector.yaml b/charts/linkerd2/templates/proxy-injector.yaml
index 21d0194d2..1feff26df 100644
--- a/charts/linkerd2/templates/proxy-injector.yaml
+++ b/charts/linkerd2/templates/proxy-injector.yaml
@@ -127,3 +127,21 @@ spec:
   - name: proxy-injector
     port: 443
     targetPort: proxy-injector
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: proxy-injector
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: proxy-injector
+{{- end }}
diff --git a/charts/linkerd2/templates/sp-validator.yaml b/charts/linkerd2/templates/sp-validator.yaml
index d3723822c..00d33cd3d 100644
--- a/charts/linkerd2/templates/sp-validator.yaml
+++ b/charts/linkerd2/templates/sp-validator.yaml
@@ -21,6 +21,24 @@ spec:
   - name: sp-validator
     port: 443
     targetPort: sp-validator
+{{- if .Values.enablePodAntiAffinity }}
+---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: {{.Values.global.namespace}}
+  labels:
+    {{.Values.global.controllerComponentLabel}}: sp-validator
+    {{.Values.global.controllerNamespaceLabel}}: {{.Values.global.namespace}}
+  annotations:
+    {{.Values.global.createdByAnnotation}}: {{default (printf "linkerd/helm %s" .Values.global.linkerdVersion) .Values.global.cliVersion}}
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      {{.Values.global.controllerComponentLabel}}: sp-validator
+{{- end }}
 ---
 {{- $tree := deepCopy . }}
 {{ $_ := set $tree.Values.global.proxy "workloadKind" "deployment" -}}
diff --git a/controller/api/destination/endpoint_translator.go b/controller/api/destination/endpoint_translator.go
index 496151703..65bc4d4f4 100644
--- a/controller/api/destination/endpoint_translator.go
+++ b/controller/api/destination/endpoint_translator.go
@@ -216,7 +216,11 @@ func (et *endpointTranslator) sendClientAdd(set watcher.AddressSet) {
 			err error
 		)
 		if address.Pod != nil {
-			wa, err = toWeightedAddr(address, et.enableH2Upgrade, et.identityTrustDomain, et.controllerNS, et.log)
+			opaquePorts, getErr := getOpaquePortsAnnotations(address.Pod)
+			if getErr != nil {
+				et.log.Errorf("failed getting opaque ports annotation for pod: %s", getErr)
+			}
+			wa, err = toWeightedAddr(address, opaquePorts, et.enableH2Upgrade, et.identityTrustDomain, et.controllerNS, et.log)
 		} else {
 			var authOverride *pb.AuthorityOverride
 			if address.AuthorityOverride != "" {
@@ -306,44 +310,30 @@ func toAddr(address watcher.Address) (*net.TcpAddress, error) {
 	}, nil
 }
 
-func toWeightedAddr(address watcher.Address, enableH2Upgrade bool, identityTrustDomain string, controllerNS string, log *logging.Entry) (*pb.WeightedAddr, error) {
+func toWeightedAddr(address watcher.Address, opaquePorts map[uint32]struct{}, enableH2Upgrade bool, identityTrustDomain string, controllerNS string, log *logging.Entry) (*pb.WeightedAddr, error) {
 	controllerNSLabel := address.Pod.Labels[k8s.ControllerNSLabel]
 	sa, ns := k8s.GetServiceAccountAndNS(address.Pod)
 	labels := k8s.GetPodLabels(address.OwnerKind, address.OwnerName, address.Pod)
 
 	// If the pod is controlled by any Linkerd control plane, then it can be
 	// hinted that this destination knows H2 (and handles our orig-proto
-	// translation) and supports receiving opaque traffic.
+	// translation)
 	var hint *pb.ProtocolHint
 	if enableH2Upgrade && controllerNSLabel != "" {
-		// Get the inbound port from the proxy container's environment
-		// variable so that it can be set in the protocol hint.
-		var inboundPort uint32
-	loop:
-		for _, containerSpec := range address.Pod.Spec.Containers {
-			if containerSpec.Name != k8s.ProxyContainerName {
-				continue
-			}
-			for _, envVar := range containerSpec.Env {
-				if envVar.Name != envInboundListenAddr {
-					continue
-				}
-				addr := strings.Split(envVar.Value, ":")
-				port, err := strconv.ParseUint(addr[1], 10, 32)
-				if err != nil {
-					log.Errorf("failed to parse inbound port for proxy container: %s", err)
-				}
-				inboundPort = uint32(port)
-				break loop
-			}
-		}
 		hint = &pb.ProtocolHint{
 			Protocol: &pb.ProtocolHint_H2_{
 				H2: &pb.ProtocolHint_H2{},
 			},
-			OpaqueTransport: &pb.ProtocolHint_OpaqueTransport{
-				InboundPort: inboundPort,
-			},
+		}
+		if _, ok := opaquePorts[address.Port]; ok {
+			port, err := getInboundPort(&address.Pod.Spec)
+			if err != nil {
+				log.Error(err)
+			} else {
+				hint.OpaqueTransport = &pb.ProtocolHint_OpaqueTransport{
+					InboundPort: port,
+				}
+			}
 		}
 	}
 
@@ -406,3 +396,25 @@ func newEmptyAddressSet() watcher.AddressSet {
 		TopologicalPref: []string{},
 	}
 }
+
+// getInboundPort gets the inbound port from the proxy container's environment
+// variable.
+func getInboundPort(podSpec *corev1.PodSpec) (uint32, error) {
+	for _, containerSpec := range podSpec.Containers {
+		if containerSpec.Name != k8s.ProxyContainerName {
+			continue
+		}
+		for _, envVar := range containerSpec.Env {
+			if envVar.Name != envInboundListenAddr {
+				continue
+			}
+			addr := strings.Split(envVar.Value, ":")
+			port, err := strconv.ParseUint(addr[1], 10, 32)
+			if err != nil {
+				return 0, fmt.Errorf("failed to parse inbound port for proxy container: %s", err)
+			}
+			return uint32(port), nil
+		}
+	}
+	return 0, fmt.Errorf("failed to find %s environment variable in any container for given pod spec", envInboundListenAddr)
+}
diff --git a/controller/api/destination/opaque_ports_adaptor.go b/controller/api/destination/opaque_ports_adaptor.go
index 0fcf9da0f..88531dd26 100644
--- a/controller/api/destination/opaque_ports_adaptor.go
+++ b/controller/api/destination/opaque_ports_adaptor.go
@@ -1,7 +1,6 @@
 package destination
 
 import (
-	"fmt"
 	"strconv"
 
 	"github.com/linkerd/linkerd2/controller/api/destination/watcher"
@@ -11,7 +10,6 @@ import (
 	"github.com/linkerd/linkerd2/pkg/util"
 	logging "github.com/sirupsen/logrus"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/labels"
 )
 
 // opaquePortsAdaptor implements EndpointUpdateListener so that it can watch
@@ -73,7 +71,7 @@ func (opa *opaquePortsAdaptor) getOpaquePorts(set watcher.AddressSet) map[uint32
 	for _, address := range set.Addresses {
 		pod := address.Pod
 		if pod != nil {
-			override, err := getOpaquePortsAnnotations(opa.k8sAPI, pod)
+			override, err := getOpaquePortsAnnotations(pod)
 			if err != nil {
 				opa.log.Errorf("Failed to get opaque ports annotation for pod %s: %s", pod, err)
 			}
@@ -91,36 +89,14 @@ func (opa *opaquePortsAdaptor) publish() {
 		merged = *opa.profile
 	}
 	merged.Spec.OpaquePorts = opa.opaquePorts
-	fmt.Printf("publishing SP: %v", merged)
 	opa.listener.Update(&merged)
 }
 
-func getOpaquePortsAnnotations(k8sAPI *k8s.API, pod *corev1.Pod) (map[uint32]struct{}, error) {
+func getOpaquePortsAnnotations(pod *corev1.Pod) (map[uint32]struct{}, error) {
 	opaquePorts := make(map[uint32]struct{})
-	obj, err := k8sAPI.GetObjects("", pkgk8s.Namespace, pod.Namespace, labels.Everything())
-	if err != nil {
-		return nil, err
-	}
-	if len(obj) > 1 {
-		return nil, fmt.Errorf("Namespace conflict: %v, %v", obj[0], obj[1])
-	}
-	if len(obj) != 1 {
-		return nil, fmt.Errorf("Namespace not found: %v", pod.Namespace)
-	}
-	ns, ok := obj[0].(*corev1.Namespace)
-	if !ok {
-		// This is very unlikely due to how `GetObjects` works
-		return nil, fmt.Errorf("Object with name %s was not a namespace", pod.Namespace)
-	}
-	override := ns.Annotations[pkgk8s.ProxyOpaquePortsAnnotation]
-
-	// Pod annotations override namespace annotations
-	if podOverride, ok := pod.Annotations[pkgk8s.ProxyOpaquePortsAnnotation]; ok {
-		override = podOverride
-	}
-
-	if override != "" {
-		for _, portStr := range util.ParseOpaquePorts(override, pod.Spec.Containers) {
+	annotation := pod.Annotations[pkgk8s.ProxyOpaquePortsAnnotation]
+	if annotation != "" {
+		for _, portStr := range util.ParseOpaquePorts(annotation, pod.Spec.Containers) {
 			port, err := strconv.ParseUint(portStr, 10, 32)
 			if err != nil {
 				return nil, err
@@ -128,6 +104,5 @@ func getOpaquePortsAnnotations(k8sAPI *k8s.API, pod *corev1.Pod) (map[uint32]str
 			opaquePorts[uint32(port)] = struct{}{}
 		}
 	}
-
 	return opaquePorts, nil
 }
diff --git a/controller/api/destination/server.go b/controller/api/destination/server.go
index 762c51d5d..408d073d4 100644
--- a/controller/api/destination/server.go
+++ b/controller/api/destination/server.go
@@ -208,19 +208,21 @@ func (s *server) GetProfile(dest *pb.GetDestination, stream pb.Destination_GetPr
 					Namespace: pod.Namespace,
 					Name:      pod.Name,
 				}
-				endpoint, err = toWeightedAddr(podSet.Addresses[podID], s.enableH2Upgrade, s.identityTrustDomain, s.controllerNS, log)
+				err := watcher.SetPodOpaquePortAnnotation(s.k8sAPI, pod, pod.Namespace)
+				if err != nil {
+					log.Errorf("failed to set opaque port annotation on pod: %s", err)
+				}
+				opaquePorts, err = getOpaquePortsAnnotations(pod)
+				if err != nil {
+					log.Errorf("failed to get opaque ports annotation for pod: %s", err)
+				}
+				endpoint, err = toWeightedAddr(podSet.Addresses[podID], opaquePorts, s.enableH2Upgrade, s.identityTrustDomain, s.controllerNS, log)
 				if err != nil {
 					return err
 				}
-
 				// `Get` doesn't include the namespace in the per-endpoint
 				// metadata, so it needs to be special-cased.
 				endpoint.MetricLabels["namespace"] = pod.Namespace
-
-				opaquePorts, err = getOpaquePortsAnnotations(s.k8sAPI, pod)
-				if err != nil {
-					return err
-				}
 			}
 
 			// When the IP does not map to a service, the default profile is
diff --git a/controller/api/destination/watcher/endpoints_watcher.go b/controller/api/destination/watcher/endpoints_watcher.go
index 363108988..3950974ec 100644
--- a/controller/api/destination/watcher/endpoints_watcher.go
+++ b/controller/api/destination/watcher/endpoints_watcher.go
@@ -799,6 +799,10 @@ func (pp *portPublisher) endpointsToAddresses(endpoints *corev1.Endpoints) Addre
 					pp.log.Errorf("Unable to create new address:%v", err)
 					continue
 				}
+				err = SetPodOpaquePortAnnotation(pp.k8sAPI, address.Pod, endpoints.Namespace)
+				if err != nil {
+					pp.log.Errorf("failed to set opaque port annotation on pod: %s", err)
+				}
 				addresses[id] = address
 			}
 		}
@@ -1081,3 +1085,22 @@ func isValidSlice(es *discovery.EndpointSlice) bool {
 
 	return true
 }
+
+// SetPodOpaquePortAnnotation ensures that if there is no opaque port
+// annotation on the pod, then it inherits the annotation from the namespace.
+// If there is also no annotation on the namespace, then it remains unset.
+func SetPodOpaquePortAnnotation(k8sAPI *k8s.API, pod *corev1.Pod, ns string) error {
+	if _, ok := pod.Annotations[consts.ProxyOpaquePortsAnnotation]; !ok {
+		ns, err := k8sAPI.NS().Lister().Get(ns)
+		if err != nil {
+			return fmt.Errorf("failed to get namespace annotation: %s", err)
+		}
+		if annotation, ok := ns.Annotations[consts.ProxyOpaquePortsAnnotation]; ok {
+			if pod.Annotations == nil {
+				pod.Annotations = make(map[string]string)
+			}
+			pod.Annotations[consts.ProxyOpaquePortsAnnotation] = annotation
+		}
+	}
+	return nil
+}
diff --git a/controller/api/public/grpc_server.go b/controller/api/public/grpc_server.go
index de1ae4ec9..70e19a2c4 100644
--- a/controller/api/public/grpc_server.go
+++ b/controller/api/public/grpc_server.go
@@ -45,11 +45,11 @@ type podReport struct {
 }
 
 const (
-	podQuery                   = "max(process_start_time_seconds{%s}) by (pod, namespace)"
-	k8sClientSubsystemName     = "kubernetes"
-	k8sClientCheckDescription  = "control plane can talk to Kubernetes"
-	promClientSubsystemName    = "prometheus"
-	promClientCheckDescription = "control plane can talk to Prometheus"
+	podQuery                  = "max(process_start_time_seconds{%s}) by (pod, namespace)"
+	k8sClientSubsystemName    = "kubernetes"
+	k8sClientCheckDescription = "control plane can talk to Kubernetes"
+	// promClientSubsystemName    = "prometheus"
+	// promClientCheckDescription = "control plane can talk to Prometheus"
 )
 
 func newGrpcServer(
@@ -206,20 +206,23 @@ func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckR
 		},
 	}
 
-	if s.prometheusAPI != nil {
-		promClientCheck := &healthcheckPb.CheckResult{
-			SubsystemName:    promClientSubsystemName,
-			CheckDescription: promClientCheckDescription,
-			Status:           healthcheckPb.CheckStatus_OK,
-		}
-		_, err = s.queryProm(ctx, fmt.Sprintf(podQuery, ""))
-		if err != nil {
-			promClientCheck.Status = healthcheckPb.CheckStatus_ERROR
-			promClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling Prometheus from the control plane: %s", err)
-		}
+	// TODO: viz: Enable this check once controller moves to viz
+	/*
+		if s.prometheusAPI != nil {
+			promClientCheck := &healthcheckPb.CheckResult{
+				SubsystemName:    promClientSubsystemName,
+				CheckDescription: promClientCheckDescription,
+				Status:           healthcheckPb.CheckStatus_OK,
+			}
+			_, err = s.queryProm(ctx, fmt.Sprintf(podQuery, ""))
+			if err != nil {
+				promClientCheck.Status = healthcheckPb.CheckStatus_ERROR
+				promClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling Prometheus from the control plane: %s", err)
+			}
 
-		response.Results = append(response.Results, promClientCheck)
-	}
+			response.Results = append(response.Results, promClientCheck)
+		}
+	*/
 
 	return response, nil
 }
diff --git a/jaeger/charts/jaeger/templates/proxy-mutator.yaml b/jaeger/charts/jaeger/templates/jaeger-injector.yaml
similarity index 74%
rename from jaeger/charts/jaeger/templates/proxy-mutator.yaml
rename to jaeger/charts/jaeger/templates/jaeger-injector.yaml
index 7dd4ded99..d2e9c8fbd 100644
--- a/jaeger/charts/jaeger/templates/proxy-mutator.yaml
+++ b/jaeger/charts/jaeger/templates/jaeger-injector.yaml
@@ -1,28 +1,28 @@
 ---
 ###
-### Proxy Mutator
+### Jaeger Injector
 ###
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   labels:
-    app.kubernetes.io/name: proxy-mutator
+    app.kubernetes.io/name: jaeger-injector
     app.kubernetes.io/part-of: Linkerd
-    component: proxy-mutator
-  name: proxy-mutator
+    component: jaeger-injector
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
 spec:
   replicas: 1
   selector:
     matchLabels:
-      component: proxy-mutator
+      component: jaeger-injector
   template:
     metadata:
       annotations:
         checksum/config: {{ include (print $.Template.BasePath "/rbac.yaml") . | sha256sum }}
       labels:
-        component: proxy-mutator
+        component: jaeger-injector
     spec:
       containers:
       - args:
@@ -36,10 +36,10 @@ spec:
             path: /ping
             port: 9995
           initialDelaySeconds: 10
-        name: proxy-mutator
+        name: jaeger-injector
         ports:
         - containerPort: 8443
-          name: proxy-mutator
+          name: jaeger-injector
         - containerPort: 9995
           name: admin-http
         readinessProbe:
@@ -51,25 +51,25 @@ spec:
         - mountPath: /var/run/linkerd/tls
           name: tls
           readOnly: true
-      serviceAccountName: proxy-mutator
+      serviceAccountName: jaeger-injector
       volumes:
       - name: tls
         secret:
-          secretName: proxy-mutator-k8s-tls
+          secretName: jaeger-injector-k8s-tls
 ---
 kind: Service
 apiVersion: v1
 metadata:
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
   labels:
-    component: proxy-mutator
+    component: jaeger-injector
 spec:
   type: ClusterIP
   selector:
-    component: proxy-mutator
+    component: jaeger-injector
   ports:
-  - name: proxy-mutator
+  - name: jaeger-injector
     port: 443
-    targetPort: proxy-mutator
+    targetPort: jaeger-injector
 
diff --git a/jaeger/charts/jaeger/templates/rbac.yaml b/jaeger/charts/jaeger/templates/rbac.yaml
index ae613a10a..c3004a17f 100644
--- a/jaeger/charts/jaeger/templates/rbac.yaml
+++ b/jaeger/charts/jaeger/templates/rbac.yaml
@@ -21,13 +21,13 @@ metadata:
 
 ---
 ###
-### Proxy Mutator RBAC
+### Jaeger Injector RBAC
 ###
 ---
 kind: ClusterRole
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   labels:
     linkerd.io/extension: linkerd-jaeger
 rules:
@@ -38,32 +38,32 @@ rules:
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   labels:
     linkerd.io/extension: linkerd-jaeger
 subjects:
 - kind: ServiceAccount
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
   apiGroup: ""
 roleRef:
   kind: ClusterRole
-  name: linkerd-jaeger-{{.Values.namespace}}-proxy-mutator
+  name: linkerd-jaeger-injector
   apiGroup: rbac.authorization.k8s.io
 ---
 kind: ServiceAccount
 apiVersion: v1
 metadata:
-  name: proxy-mutator
+  name: jaeger-injector
   namespace: {{.Values.namespace}}
 ---
-{{- $host := printf "proxy-mutator.%s.svc" .Values.namespace }}
+{{- $host := printf "jaeger-injector.%s.svc" .Values.namespace }}
 {{- $ca := genSelfSignedCert $host (list) (list $host) 365 }}
 {{- if (not .Values.webhook.externalSecret) }}
 kind: Secret
 apiVersion: v1
 metadata:
-  name: proxy-mutator-k8s-tls
+  name: jaeger-injector-k8s-tls
   namespace: {{ .Values.namespace }}
 type: kubernetes.io/tls
 data:
@@ -74,11 +74,11 @@ data:
 apiVersion: admissionregistration.k8s.io/v1beta1
 kind: MutatingWebhookConfiguration
 metadata:
-  name: linkerd-proxy-mutator-webhook-config
+  name: linkerd-jaeger-injector-webhook-config
   labels:
     linkerd.io/extension: linkerd-jaeger
 webhooks:
-- name: proxy-mutator.linkerd.io
+- name: jaeger-injector.linkerd.io
   {{- if .Values.webhook.namespaceSelector }}
   namespaceSelector: 
 {{ toYaml .Values.webhook.namespaceSelector | trim | indent 4 -}}
@@ -89,7 +89,7 @@ webhooks:
   {{- end }}
   clientConfig:
     service:
-      name: proxy-mutator
+      name: jaeger-injector
       namespace: {{ .Values.namespace }}
       path: "/"
 {{- if and (.Values.webhook.externalSecret) (empty .Values.webhook.caBundle) }}
diff --git a/jaeger/cmd/install.go b/jaeger/cmd/install.go
index 88b2ada43..d5ac37938 100644
--- a/jaeger/cmd/install.go
+++ b/jaeger/cmd/install.go
@@ -23,7 +23,7 @@ import (
 var (
 	templatesJaeger = []string{
 		"templates/namespace.yaml",
-		"templates/proxy-mutator.yaml",
+		"templates/jaeger-injector.yaml",
 		"templates/rbac.yaml",
 		"templates/tracing.yaml",
 	}
diff --git a/jaeger/proxy-mutator/Dockerfile b/jaeger/injector/Dockerfile
similarity index 79%
rename from jaeger/proxy-mutator/Dockerfile
rename to jaeger/injector/Dockerfile
index af37ea2e6..aafe5fadc 100644
--- a/jaeger/proxy-mutator/Dockerfile
+++ b/jaeger/injector/Dockerfile
@@ -19,14 +19,14 @@ COPY controller controller
 COPY charts/partials charts/partials
 
 ARG TARGETARCH
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/proxy-mutator -tags prod -mod=readonly -ldflags "-s -w" ./jaeger/proxy-mutator/cmd
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/injector -tags prod -mod=readonly -ldflags "-s -w" ./jaeger/injector/cmd
 
 ## package runtime
 FROM scratch
 ENV PATH=$PATH:/go/bin
-COPY --from=golang /out/proxy-mutator /go/bin/proxy-mutator
+COPY --from=golang /out/injector /go/bin/injector
 
 ARG LINKERD_VERSION
 ENV LINKERD_CONTAINER_VERSION_OVERRIDE=${LINKERD_VERSION}
 
-ENTRYPOINT ["/go/bin/proxy-mutator"]
+ENTRYPOINT ["/go/bin/injector"]
diff --git a/jaeger/proxy-mutator/cmd/main.go b/jaeger/injector/cmd/main.go
similarity index 86%
rename from jaeger/proxy-mutator/cmd/main.go
rename to jaeger/injector/cmd/main.go
index a9b35a5e1..2c6d19d07 100644
--- a/jaeger/proxy-mutator/cmd/main.go
+++ b/jaeger/injector/cmd/main.go
@@ -8,12 +8,12 @@ import (
 
 	"github.com/linkerd/linkerd2/controller/k8s"
 	"github.com/linkerd/linkerd2/controller/webhook"
-	"github.com/linkerd/linkerd2/jaeger/proxy-mutator/mutator"
+	"github.com/linkerd/linkerd2/jaeger/injector/mutator"
 	"github.com/linkerd/linkerd2/pkg/flags"
 )
 
 func main() {
-	cmd := flag.NewFlagSet("proxy-mutator", flag.ExitOnError)
+	cmd := flag.NewFlagSet("injector", flag.ExitOnError)
 	metricsAddr := cmd.String("metrics-addr", fmt.Sprintf(":%d", 9995),
 		"address to serve scrapable metrics on")
 	addr := cmd.String("addr", ":8443", "address to serve on")
@@ -29,7 +29,7 @@ func main() {
 		context.Background(),
 		[]k8s.APIResource{k8s.NS},
 		mutator.Mutate(*collectorSvcAddr, *collectorSvcAccount),
-		"linkerd-proxy-mutator",
+		"linkerd-jaeger-injector",
 		*metricsAddr,
 		*addr,
 		*kubeconfig,
diff --git a/jaeger/proxy-mutator/mutator/patch.go b/jaeger/injector/mutator/patch.go
similarity index 100%
rename from jaeger/proxy-mutator/mutator/patch.go
rename to jaeger/injector/mutator/patch.go
diff --git a/jaeger/proxy-mutator/mutator/webhook.go b/jaeger/injector/mutator/webhook.go
similarity index 100%
rename from jaeger/proxy-mutator/mutator/webhook.go
rename to jaeger/injector/mutator/webhook.go
diff --git a/jaeger/values/values.go b/jaeger/values/values.go
deleted file mode 100644
index df8e16e44..000000000
--- a/jaeger/values/values.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package values
-
-import (
-	"fmt"
-
-	"github.com/linkerd/linkerd2/jaeger/static"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
-	"github.com/linkerd/linkerd2/pkg/version"
-	"helm.sh/helm/v3/pkg/chart/loader"
-	"helm.sh/helm/v3/pkg/chartutil"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"sigs.k8s.io/yaml"
-)
-
-// Values represents the values of jaeger template
-type Values struct {
-	Namespace      string    `json:"namespace"`
-	CliVersion     string    `json:"cliVersion"`
-	Collector      collector `json:"collector"`
-	Jaeger         jaeger    `json:"jaeger"`
-	LinkerdVersion string    `json:"linkerdVersion"`
-	Webhook        webhook   `json:"webhook"`
-}
-
-type collector struct {
-	Resources l5dcharts.Resources `json:"resources"`
-	Image     l5dcharts.Image     `json:"image"`
-}
-
-type jaeger struct {
-	Resources l5dcharts.Resources `json:"resources"`
-	Image     l5dcharts.Image     `json:"image"`
-}
-
-type webhook struct {
-	ExternalSecret      bool                  `json:"externalSecret"`
-	CrtPEM              string                `json:"crtPEM"`
-	KeyPEM              string                `json:"keyPEM"`
-	CaBundle            string                `json:"caBundle"`
-	CollectorSvcAddr    string                `json:"collectorSvcAddr"`
-	CollectorSvcAccount string                `json:"collectorSvcAccount"`
-	FailurePolicy       string                `json:"failurePolicy"`
-	Image               l5dcharts.Image       `json:"image"`
-	LogLevel            string                `json:"logLevel"`
-	NamespaceSelector   *metav1.LabelSelector `json:"namespaceSelector"`
-	ObjectSelector      *metav1.LabelSelector `json:"objectSelector"`
-}
-
-// NewValues returns a new instance of the Values type.
-// TODO: Add HA logic
-func NewValues() (*Values, error) {
-	chartDir := fmt.Sprintf("%s/", "jaeger")
-	v, err := readDefaults(chartDir)
-	if err != nil {
-		return nil, err
-	}
-	v.CliVersion = version.Version
-
-	return v, nil
-}
-
-// readDefaults read all the default variables from the values.yaml file.
-// chartDir is the root directory of the Helm chart where values.yaml is.
-func readDefaults(chartDir string) (*Values, error) {
-	valuesFile := &loader.BufferedFile{
-		Name: chartutil.ValuesfileName,
-	}
-
-	if err := charts.ReadFile(static.Templates, chartDir, valuesFile); err != nil {
-		return nil, err
-	}
-
-	var values Values
-	if err := yaml.Unmarshal(valuesFile.Data, &values); err != nil {
-		return nil, err
-	}
-
-	return &values, nil
-}
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index 6628f6c8d..2b97ecb39 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -1733,12 +1733,12 @@ func (hc *HealthChecker) fetchCredsFromOldSecret(ctx context.Context, secretName
 
 	crt, ok := secret.Data[certOldKeyName]
 	if !ok {
-		return nil, fmt.Errorf("key %s needs to exist in secret %s", certKeyName, secretName)
+		return nil, fmt.Errorf("key %s needs to exist in secret %s", certOldKeyName, secretName)
 	}
 
 	key, ok := secret.Data[keyOldKeyName]
 	if !ok {
-		return nil, fmt.Errorf("key %s needs to exist in secret %s", keyKeyName, secretName)
+		return nil, fmt.Errorf("key %s needs to exist in secret %s", keyOldKeyName, secretName)
 	}
 
 	cred, err := tls.ValidateAndCreateCreds(string(crt), string(key))
diff --git a/pkg/inject/inject.go b/pkg/inject/inject.go
index 59efbf9d9..1d5e9ad74 100644
--- a/pkg/inject/inject.go
+++ b/pkg/inject/inject.go
@@ -599,7 +599,7 @@ func (conf *ResourceConfig) serviceAccountVolumeMount() *corev1.VolumeMount {
 // annotations.
 func (conf *ResourceConfig) injectObjectMeta(values *patch) {
 
-	values.Annotations[k8s.ProxyVersionAnnotation] = conf.values.GetGlobal().Proxy.Image.Version
+	values.Annotations[k8s.ProxyVersionAnnotation] = values.GetGlobal().Proxy.Image.Version
 
 	if values.Identity == nil || values.GetGlobal().Proxy.DisableIdentity {
 		values.Annotations[k8s.IdentityModeAnnotation] = k8s.IdentityModeDisabled
diff --git a/pkg/k8s/portforward.go b/pkg/k8s/portforward.go
index 94b190490..e501e909b 100644
--- a/pkg/k8s/portforward.go
+++ b/pkg/k8s/portforward.go
@@ -8,7 +8,6 @@ import (
 	"net/http"
 	"net/url"
 	"os"
-	"strings"
 
 	log "github.com/sirupsen/logrus"
 	corev1 "k8s.io/api/core/v1"
@@ -81,7 +80,12 @@ func NewPortForward(
 	podName := ""
 	for _, pod := range podList.Items {
 		if pod.Status.Phase == corev1.PodRunning {
-			if strings.HasPrefix(pod.Name, deployName) {
+			grandparent, err := getDeploymentForPod(ctx, k8sAPI, pod)
+			if err != nil {
+				log.Warnf("Failed to get deploy for pod [%s]: %s", pod.Name, err)
+				continue
+			}
+			if grandparent == deployName {
 				podName = pod.Name
 				break
 			}
@@ -95,6 +99,22 @@ func NewPortForward(
 	return newPortForward(k8sAPI, namespace, podName, host, localPort, remotePort, emitLogs)
 }
 
+func getDeploymentForPod(ctx context.Context, k8sAPI *KubernetesAPI, pod corev1.Pod) (string, error) {
+	parents := pod.GetOwnerReferences()
+	if len(parents) != 1 {
+		return "", nil
+	}
+	rs, err := k8sAPI.AppsV1().ReplicaSets(pod.Namespace).Get(ctx, parents[0].Name, metav1.GetOptions{})
+	if err != nil {
+		return "", err
+	}
+	grandparents := rs.GetOwnerReferences()
+	if len(grandparents) != 1 {
+		return "", nil
+	}
+	return grandparents[0].Name, nil
+}
+
 func newPortForward(
 	k8sAPI *KubernetesAPI,
 	namespace, podName string,
diff --git a/viz/charts/linkerd-viz/README.md b/viz/charts/linkerd-viz/README.md
index e58a8b3cb..0a8b0aebd 100644
--- a/viz/charts/linkerd-viz/README.md
+++ b/viz/charts/linkerd-viz/README.md
@@ -75,7 +75,8 @@ Kubernetes: `>=1.13.0-0`
 | createdByAnnotation | string | `"linkerd.io/created-by"` |  |
 | dashboard.UID | int | `2103` |  |
 | dashboard.enforcedHostRegexp | string | `""` | Host header validation regex for the dashboard. See the [Linkerd documentation](https://linkerd.io/2/tasks/exposing-dashboard) for more information |
-| dashboard.image.name | string | `"ghcr.io/linkerd/web"` | Docker image name for the web instance |
+| dashboard.image.name | string | `"web"` | Docker image name for the web instance |
+| dashboard.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the web instance |
 | dashboard.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the web instance |
 | dashboard.logLevel | string | `"info"` | log level of the dashboard component |
 | dashboard.replicas | int | `1` | Number of replicas of dashboard |
@@ -84,11 +85,13 @@ Kubernetes: `>=1.13.0-0`
 | dashboard.resources.memory.limit | string | `nil` | Maximum amount of memory that web container can use |
 | dashboard.resources.memory.request | string | `nil` | Amount of memory that the web container requests |
 | dashboard.restrictPrivileges | bool | `false` | Restrict the Linkerd Dashboard's default privileges to disallow Tap and Check |
+| defaultRegistry | string | `"ghcr.io/linkerd"` | Default Docker Registry |
 | extensionAnnotation | string | `"linkerd.io/extension"` |  |
 | globalLogLevel | string | `"info"` | Log level for all the viz components |
 | globalUID | int | `2103` | UID for all the viz components |
 | grafana.enabled | bool | `true` | toggle field to enable or disable grafana |
-| grafana.image.name | string | `"ghcr.io/linkerd/grafana"` | Docker image name for the grafana instance |
+| grafana.image.name | string | `"grafana"` | Docker image name for the grafana instance |
+| grafana.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the grafana instance |
 | grafana.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the grafana instance |
 | grafana.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the grafana container can use |
 | grafana.resources.cpu.request | string | `nil` | Amount of CPU units that the grafana container requests |
@@ -105,8 +108,9 @@ Kubernetes: `>=1.13.0-0`
 | prometheus.args | object | `{"config.file":"/etc/prometheus/prometheus.yml","log.level":"info","storage.tsdb.path":"/data","storage.tsdb.retention.time":"6h"}` | Command line options for Prometheus binary |
 | prometheus.enabled | bool | `true` | toggle field to enable or disable prometheus |
 | prometheus.globalConfig | object | `{"evaluation_interval":"10s","scrape_interval":"10s","scrape_timeout":"10s"}` | The global configuration specifies parameters that are valid in all other configuration contexts. |
-| prometheus.image.name | string | `"prom/prometheus"` | Docker image name for the prometheus instance |
+| prometheus.image.name | string | `"prometheus"` | Docker image name for the prometheus instance |
 | prometheus.image.pullPolicy | string | `"Always"` |  |
+| prometheus.image.registry | string | `"prom"` | Docker registry for the prometheus instance |
 | prometheus.image.tag | string | `"v2.19.3"` | Docker image tag for the prometheus instance |
 | prometheus.remoteWrite | string | `nil` | Allows transparently sending samples to an endpoint. Mostly used for long term storage. |
 | prometheus.resources.cpu.limit | string | `nil` | Maximum amount of CPU units that the prometheus container can use |
@@ -121,8 +125,9 @@ Kubernetes: `>=1.13.0-0`
 | tap.caBundle | string | `""` | Bundle of CA certificates for Tap component. If not provided then Helm will use the certificate generated  for `tap.crtPEM`. If `tap.externalSecret` is set to true, this value must be set, as no certificate will be generated. |
 | tap.crtPEM | string | `""` | Certificate for the Tap component. If not provided then Helm will generate one. |
 | tap.externalSecret | bool | `false` | Do not create a secret resource for the Tap component. If this is set to `true`, the value `tap.caBundle` must be set (see below). |
-| tap.image.name | string | `"ghcr.io/linkerd/controller"` | Docker image name for the grafana instance |
-| tap.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the grafana instance |
+| tap.image.name | string | `"controller"` | Docker image name for the tap instance |
+| tap.image.registry | string | `"ghcr.io/linkerd"` | Docker registry for the tap instance |
+| tap.image.tag | string | `"linkerdVersionValue"` | Docker image tag for the tap instance |
 | tap.keyPEM | string | `""` | Certificate key for Tap component. If not provided then Helm will generate one.  |
 | tap.logLevel | string | `"info"` | log level of the tap component |
 | tap.replicas | int | `1` |  |
diff --git a/viz/charts/linkerd-viz/templates/grafana.yaml b/viz/charts/linkerd-viz/templates/grafana.yaml
index 391b2814d..521494307 100644
--- a/viz/charts/linkerd-viz/templates/grafana.yaml
+++ b/viz/charts/linkerd-viz/templates/grafana.yaml
@@ -127,7 +127,7 @@ spec:
         # see https://github.com/grafana/grafana/issues/20096
         - name: GODEBUG
           value: netdns=go
-        image: {{.Values.grafana.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.grafana.image.tag}}
+        image: {{.Values.grafana.image.registry}}/{{.Values.grafana.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.grafana.image.tag}}
         imagePullPolicy: {{.Values.imagePullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/prometheus.yaml b/viz/charts/linkerd-viz/templates/prometheus.yaml
index aab1cc3b1..fc9af7058 100644
--- a/viz/charts/linkerd-viz/templates/prometheus.yaml
+++ b/viz/charts/linkerd-viz/templates/prometheus.yaml
@@ -242,7 +242,7 @@ spec:
         {{- range $key, $value := .Values.prometheus.args}}
         - --{{ $key }}{{ if $value }}={{ $value }}{{ end }}
         {{- end }}
-        image: {{.Values.prometheus.image.name}}:{{.Values.prometheus.image.tag}}
+        image: {{.Values.prometheus.image.registry}}/{{.Values.prometheus.image.name}}:{{.Values.prometheus.image.tag}}
         imagePullPolicy: {{.Values.prometheus.imagepullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/tap.yaml b/viz/charts/linkerd-viz/templates/tap.yaml
index cbca18e07..6cce931e8 100644
--- a/viz/charts/linkerd-viz/templates/tap.yaml
+++ b/viz/charts/linkerd-viz/templates/tap.yaml
@@ -81,7 +81,7 @@ spec:
         - -controller-namespace={{.Values.linkerdNamespace}}
         - -log-level={{.Values.tap.logLevel}}
         - -identity-trust-domain={{.Values.identityTrustDomain}}
-        image: {{.Values.tap.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.tap.image.tag}}
+        image: {{.Values.tap.image.registry}}/{{.Values.tap.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.tap.image.tag}}
         imagePullPolicy: {{.Values.tap.image.pullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/templates/web.yaml b/viz/charts/linkerd-viz/templates/web.yaml
index 1ced39e46..038c6013c 100644
--- a/viz/charts/linkerd-viz/templates/web.yaml
+++ b/viz/charts/linkerd-viz/templates/web.yaml
@@ -84,7 +84,7 @@ spec:
         {{- $hostAbbrev := replace "." "\\." (printf "linkerd-web.%s.svc" .Values.namespace) }}
         - -enforced-host=^(localhost|127\.0\.0\.1|{{ $hostFull }}|{{ $hostAbbrev }}|\[::1\])(:\d+)?$
         {{- end}}
-        image: {{.Values.dashboard.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.dashboard.image.tag}}
+        image: {{.Values.dashboard.image.registry}}/{{.Values.dashboard.image.name}}:{{ default (default .Values.linkerdVersion .Values.controllerImageVersion) .Values.dashboard.image.tag}}
         imagePullPolicy: {{.Values.dashboard.image.pullPolicy}}
         livenessProbe:
           httpGet:
diff --git a/viz/charts/linkerd-viz/values.yaml b/viz/charts/linkerd-viz/values.yaml
index 75d9015c9..2b16cf3c1 100644
--- a/viz/charts/linkerd-viz/values.yaml
+++ b/viz/charts/linkerd-viz/values.yaml
@@ -7,6 +7,9 @@
 # -- control plane version. See Proxy section for proxy version
 linkerdVersion: &linkerd_version linkerdVersionValue
 
+# -- Default Docker Registry
+defaultRegistry: &default_registry ghcr.io/linkerd
+
 # -- Kubernetes DNS Domain name to use  
 clusterDomain: &cluster_domain cluster.local
 
@@ -59,9 +62,11 @@ tap:
   # -- log level of the tap component
   logLevel: *log_level
   image:
-    # -- Docker image name for the grafana instance
-    name: ghcr.io/linkerd/controller
-    # -- Docker image tag for the grafana instance
+    # -- Docker registry for the tap instance
+    registry: *default_registry
+    # -- Docker image name for the tap instance
+    name: controller
+    # -- Docker image tag for the tap instance
     tag: *linkerd_version
   # -- Do not create a secret resource for the Tap component. If this is set to
   # `true`, the value `tap.caBundle` must be set (see below).
@@ -101,8 +106,10 @@ dashboard:
   # -- log level of the dashboard component
   logLevel: *log_level
   image:
+    # -- Docker registry for the web instance
+    registry: *default_registry
     # -- Docker image name for the web instance
-    name: ghcr.io/linkerd/web
+    name: web
     # -- Docker image tag for the web instance
     tag: *linkerd_version
 
@@ -132,8 +139,10 @@ grafana:
   # -- toggle field to enable or disable grafana
   enabled: true
   image:
+    # -- Docker registry for the grafana instance
+    registry: *default_registry
     # -- Docker image name for the grafana instance
-    name: ghcr.io/linkerd/grafana
+    name: grafana
     # -- Docker image tag for the grafana instance
     tag: *linkerd_version
 
@@ -152,9 +161,11 @@ grafana:
 prometheus:
   # -- toggle field to enable or disable prometheus
   enabled: true
-  image: 
+  image:
+    # -- Docker registry for the prometheus instance
+    registry: prom
     # -- Docker image name for the prometheus instance
-    name: prom/prometheus
+    name: prometheus
     # -- Docker image tag for the prometheus instance
     tag: v2.19.3
     # == Pull policy for the prometheus instance


Test patch:
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index a537c7a74..262b9134e 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -809,6 +809,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1081,6 +1097,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1364,6 +1396,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1930,6 +1978,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 ###
 ### Service Profile Validator
 ###
@@ -1953,6 +2017,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index efa561a9f..e19da9e03 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -809,6 +809,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1081,6 +1097,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1364,6 +1396,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1930,6 +1978,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 ###
 ### Service Profile Validator
 ###
@@ -1953,6 +2017,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/cli dev-undefined
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden
index c385be75c..a9dab5938 100644
--- a/cli/cmd/testdata/install_helm_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_output_ha.golden
@@ -813,6 +813,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1076,6 +1092,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1350,6 +1382,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1898,6 +1946,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1923,6 +1987,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index 1a37c9edb..1129319f4 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -817,6 +817,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1084,6 +1100,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1362,6 +1394,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1922,6 +1970,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1947,6 +2011,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
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 ffdbdda6a..a7d069ef3 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -813,6 +813,22 @@ spec:
     port: 8080
     targetPort: 8080
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-identity
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: identity
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: identity
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1076,6 +1092,22 @@ spec:
     port: 8085
     targetPort: 8085
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-controller-api
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: controller
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: controller
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1350,6 +1382,22 @@ spec:
     port: 8086
     targetPort: 8086
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-dst
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: destination
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: destination
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -1898,6 +1946,22 @@ spec:
     port: 443
     targetPort: proxy-injector
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-proxy-injector
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: proxy-injector
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: proxy-injector
+---
 # Source: linkerd2/templates/sp-validator.yaml
 ---
 ###
@@ -1923,6 +1987,22 @@ spec:
     port: 443
     targetPort: sp-validator
 ---
+kind: PodDisruptionBudget
+apiVersion: policy/v1beta1
+metadata:
+  name: linkerd-sp-validator
+  namespace: linkerd
+  labels:
+    linkerd.io/control-plane-component: sp-validator
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    linkerd.io/created-by: linkerd/helm linkerd-version
+spec:
+  maxUnavailable: 1
+  selector:
+    matchLabels:
+      linkerd.io/control-plane-component: sp-validator
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/controller/api/destination/server_test.go b/controller/api/destination/server_test.go
index 6418a5277..49a701d1e 100644
--- a/controller/api/destination/server_test.go
+++ b/controller/api/destination/server_test.go
@@ -14,9 +14,12 @@ import (
 )
 
 const fullyQualifiedName = "name1.ns.svc.mycluster.local"
+const fullyQualifiedNameOpaque = "name3.ns.svc.mycluster.local"
 const clusterIP = "172.17.12.0"
+const clusterIPOpaque = "172.17.12.1"
 const podIP1 = "172.17.0.12"
 const podIP2 = "172.17.0.13"
+const podIPOpaque = "172.17.0.14"
 const port uint32 = 8989
 const opaquePort uint32 = 4242
 
@@ -41,7 +44,7 @@ func (m *mockDestinationGetProfileServer) Send(profile *pb.DestinationProfile) e
 }
 
 func makeServer(t *testing.T) *server {
-	k8sAPI, err := k8s.NewFakeAPI(`
+	meshedPodResources := []string{`
 apiVersion: v1
 kind: Namespace
 metadata:
@@ -78,8 +81,6 @@ kind: Pod
 metadata:
   labels:
     linkerd.io/control-plane-ns: linkerd
-  annotations:
-    config.linkerd.io/opaque-ports: "4242"
   name: name1-1
   namespace: ns
 status:
@@ -92,15 +93,6 @@ spec:
         value: 0.0.0.0:4143
       name: linkerd-proxy`,
 		`
-apiVersion: v1
-kind: Pod
-metadata:
-  name: name2-1
-  namespace: ns
-status:
-  phase: Running
-  podIP: 172.17.0.13`,
-		`
 apiVersion: linkerd.io/v1alpha2
 kind: ServiceProfile
 metadata:
@@ -112,6 +104,9 @@ spec:
     isRetryable: false
     condition:
       pathRegex: "/a/b/c"`,
+	}
+
+	clientSP := []string{
 		`
 apiVersion: linkerd.io/v1alpha2
 kind: ServiceProfile
@@ -124,7 +119,69 @@ spec:
     isRetryable: true
     condition:
       pathRegex: "/x/y/z"`,
-	)
+	}
+
+	unmeshedPod := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: name2
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.13`
+
+	meshedOpaquePodResources := []string{
+		`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name3
+  namespace: ns
+spec:
+  type: LoadBalancer
+  clusterIP: 172.17.12.1
+  ports:
+  - port: 4242`,
+		`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: name3
+  namespace: ns
+subsets:
+- addresses:
+  - ip: 172.17.0.14
+    targetRef:
+      kind: Pod
+      name: name3
+      namespace: ns
+  ports:
+  - port: 4242`,
+		`
+apiVersion: v1
+kind: Pod
+metadata:
+  labels:
+    linkerd.io/control-plane-ns: linkerd
+  annotations:
+    config.linkerd.io/opaque-ports: "4242"
+  name: name3
+  namespace: ns
+status:
+  phase: Running
+  podIP: 172.17.0.14
+spec:
+  containers:
+    - env:
+      - name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR
+        value: 0.0.0.0:4143
+      name: linkerd-proxy`,
+	}
+	res := append(meshedPodResources, clientSP...)
+	res = append(res, unmeshedPod)
+	res = append(res, meshedOpaquePodResources...)
+	k8sAPI, err := k8s.NewFakeAPI(res...)
 	if err != nil {
 		t.Fatalf("NewFakeAPI returned an error: %s", err)
 	}
@@ -482,8 +539,8 @@ func TestGetProfiles(t *testing.T) {
 		if first.Endpoint.ProtocolHint == nil {
 			t.Fatalf("Expected protocol hint but found none")
 		}
-		if first.Endpoint.ProtocolHint.GetOpaqueTransport().InboundPort != 4143 {
-			t.Fatalf("Expected pod to support opaque traffic on port 4143")
+		if first.Endpoint.ProtocolHint.GetOpaqueTransport() != nil {
+			t.Fatalf("Expected pod to not support opaque traffic on port %d", port)
 		}
 		if first.Endpoint.Addr.String() != epAddr.String() {
 			t.Fatalf("Expected endpoint IP to be %s, but it was %s", epAddr.Ip, first.Endpoint.Addr.Ip)
@@ -556,7 +613,7 @@ func TestGetProfiles(t *testing.T) {
 		stream.Cancel()
 		err := server.GetProfile(&pb.GetDestination{
 			Scheme: "k8s",
-			Path:   fmt.Sprintf("%s:%d", clusterIP, opaquePort),
+			Path:   fmt.Sprintf("%s:%d", clusterIPOpaque, opaquePort),
 		}, stream)
 		if err != nil {
 			t.Fatalf("Got error: %s", err)
@@ -569,16 +626,12 @@ func TestGetProfiles(t *testing.T) {
 		}
 
 		last := stream.updates[len(stream.updates)-1]
-		if last.FullyQualifiedName != fullyQualifiedName {
-			t.Fatalf("Expected fully qualified name '%s', but got '%s'", fullyQualifiedName, last.FullyQualifiedName)
+		if last.FullyQualifiedName != fullyQualifiedNameOpaque {
+			t.Fatalf("Expected fully qualified name '%s', but got '%s'", fullyQualifiedNameOpaque, last.FullyQualifiedName)
 		}
 		if !last.OpaqueProtocol {
 			t.Fatalf("Expected port %d to be an opaque protocol, but it was not", opaquePort)
 		}
-		routes := last.GetRoutes()
-		if len(routes) != 1 {
-			t.Fatalf("Expected 1 route but got %d: %v", len(routes), routes)
-		}
 	})
 
 	t.Run("Return opaque protocol profile with endpoint when using pod IP and opaque protocol port", func(t *testing.T) {
@@ -589,14 +642,14 @@ func TestGetProfiles(t *testing.T) {
 		}
 		stream.Cancel()
 
-		epAddr, err := toAddress(podIP1, opaquePort)
+		epAddr, err := toAddress(podIPOpaque, opaquePort)
 		if err != nil {
 			t.Fatalf("Got error: %s", err)
 		}
 
 		err = server.GetProfile(&pb.GetDestination{
 			Scheme: "k8s",
-			Path:   fmt.Sprintf("%s:%d", podIP1, opaquePort),
+			Path:   fmt.Sprintf("%s:%d", podIPOpaque, opaquePort),
 		}, stream)
 		if err != nil {
 			t.Fatalf("Got error: %s", err)
diff --git a/test/integration/install_test.go b/test/integration/install_test.go
index 7c4bd98d3..db4f80d15 100644
--- a/test/integration/install_test.go
+++ b/test/integration/install_test.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"os"
-	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -815,6 +814,10 @@ func TestInstallSP(t *testing.T) {
 	}
 }
 
+// This test fails because no web component is installed during this phase of
+// the extension split. It should be renabled after the issue below is closed.
+// Issue: https://github.com/linkerd/linkerd2/issues/5478
+/*
 func TestDashboard(t *testing.T) {
 	dashboardPort := 52237
 	dashboardURL := fmt.Sprintf("http://localhost:%d", dashboardPort)
@@ -851,6 +854,7 @@ func TestDashboard(t *testing.T) {
 			resp, TestHelper.GetVersion())
 	}
 }
+*/
 
 func TestInject(t *testing.T) {
 	resources, err := testutil.ReadFile("testdata/smoke_test.yaml")
diff --git a/test/integration/opaqueports/opaque_ports_test.go b/test/integration/opaqueports/opaque_ports_test.go
index 53ccb5a53..385be30b3 100644
--- a/test/integration/opaqueports/opaque_ports_test.go
+++ b/test/integration/opaqueports/opaque_ports_test.go
@@ -19,7 +19,7 @@ var (
 
 	// With the app's port marked as opaque, we expect to find a single open
 	// TCP connection that is not TLS'd because the port is skipped.
-	tcpMetric = "tcp_open_total{peer=\"src\",direction=\"inbound\",tls=\"no_identity\",no_tls_reason=\"port_skipped\"}"
+	tcpMetric = "tcp_open_connections{peer=\"src\",direction=\"inbound\",tls=\"true\",client_id=\"default.linkerd-opaque-ports-test.serviceaccount.identity.linkerd.cluster.local\"}"
 )
 
 func TestMain(m *testing.M) {
@@ -64,7 +64,7 @@ func TestOpaquePorts(t *testing.T) {
 		testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", appName, err)
 	}
 
-	t.Run("expect inbound TCP connection with no TLS for port_skipped reason", func(t *testing.T) {
+	t.Run("expect inbound TCP connection metric with expected TLS identity", func(t *testing.T) {
 		pods, err := TestHelper.GetPods(ctx, opaquePortsNs, map[string]string{"app": appName})
 		if err != nil {
 			testutil.AnnotatedFatalf(t, "error getting opaque ports app pods", "error getting opaque ports app pods\n%s", err)
diff --git a/test/integration/testdata/check.cni.golden b/test/integration/testdata/check.cni.golden
index 0a2f3233a..d57f3efa7 100644
--- a/test/integration/testdata/check.cni.golden
+++ b/test/integration/testdata/check.cni.golden
@@ -64,7 +64,6 @@ linkerd-api
 √ control plane pods are ready
 √ control plane self-check
 √ [kubernetes] control plane can talk to Kubernetes
-√ [prometheus] control plane can talk to Prometheus
 
 linkerd-version
 ---------------
diff --git a/test/integration/testdata/check.cni.proxy.golden b/test/integration/testdata/check.cni.proxy.golden
index c3d60a74e..9df457879 100644
--- a/test/integration/testdata/check.cni.proxy.golden
+++ b/test/integration/testdata/check.cni.proxy.golden
@@ -68,7 +68,6 @@ linkerd-api
 √ control plane pods are ready
 √ control plane self-check
 √ [kubernetes] control plane can talk to Kubernetes
-√ [prometheus] control plane can talk to Prometheus
 
 linkerd-version
 ---------------
diff --git a/test/integration/testdata/check.golden b/test/integration/testdata/check.golden
index 912b24960..770dda173 100644
--- a/test/integration/testdata/check.golden
+++ b/test/integration/testdata/check.golden
@@ -52,7 +52,6 @@ linkerd-api
 √ control plane pods are ready
 √ control plane self-check
 √ [kubernetes] control plane can talk to Kubernetes
-√ [prometheus] control plane can talk to Prometheus
 
 linkerd-version
 ---------------
diff --git a/test/integration/testdata/check.multicluster.proxy.golden b/test/integration/testdata/check.multicluster.proxy.golden
index 60b51024b..4d788221e 100644
--- a/test/integration/testdata/check.multicluster.proxy.golden
+++ b/test/integration/testdata/check.multicluster.proxy.golden
@@ -56,7 +56,6 @@ linkerd-api
 √ control plane pods are ready
 √ control plane self-check
 √ [kubernetes] control plane can talk to Kubernetes
-√ [prometheus] control plane can talk to Prometheus
 
 linkerd-version
 ---------------
diff --git a/test/integration/testdata/check.proxy.golden b/test/integration/testdata/check.proxy.golden
index 3fc9f3dd4..880cc9b32 100644
--- a/test/integration/testdata/check.proxy.golden
+++ b/test/integration/testdata/check.proxy.golden
@@ -56,7 +56,6 @@ linkerd-api
 √ control plane pods are ready
 √ control plane self-check
 √ [kubernetes] control plane can talk to Kubernetes
-√ [prometheus] control plane can talk to Prometheus
 
 linkerd-version
 ---------------
diff --git a/test/integration/tracing/tracing_test.go b/test/integration/tracing/tracing_test.go
index 6b4c0d4af..a87ac580d 100644
--- a/test/integration/tracing/tracing_test.go
+++ b/test/integration/tracing/tracing_test.go
@@ -61,8 +61,8 @@ func TestTracing(t *testing.T) {
 			"'kubectl apply' command failed\n%s", out)
 	}
 
-	// wait for jaeger proxy mutator to start
-	if err := TestHelper.CheckPods(ctx, tracingNs, "proxy-mutator", 1); err != nil {
+	// wait for jaeger injector to start
+	if err := TestHelper.CheckPods(ctx, tracingNs, "jaeger-injector", 1); err != nil {
 		if rce, ok := err.(*testutil.RestartCountError); ok {
 			testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
 		} else {
@@ -70,8 +70,8 @@ func TestTracing(t *testing.T) {
 		}
 	}
 
-	if err := TestHelper.CheckDeployment(ctx, tracingNs, "proxy-mutator", 1); err != nil {
-		testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", "proxy-mutator", err)
+	if err := TestHelper.CheckDeployment(ctx, tracingNs, "jaeger-injector", 1); err != nil {
+		testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", "jaeger-injector", err)
 	}
 
 	// Emojivoto components
@@ -156,12 +156,13 @@ func TestTracing(t *testing.T) {
 
 	t.Run("expect full trace", func(t *testing.T) {
 
-		url, err := TestHelper.URLFor(ctx, tracingNs, "jaeger", 16686)
-		if err != nil {
-			testutil.AnnotatedFatal(t, "error building URL", err)
-		}
 		timeout := 120 * time.Second
 		err = TestHelper.RetryFor(timeout, func() error {
+			url, err := TestHelper.URLFor(ctx, tracingNs, "jaeger", 16686)
+			if err != nil {
+				return err
+			}
+
 			tracesJSON, err := TestHelper.HTTPGetURL(url + "/jaeger/api/traces?lookback=1h&service=nginx")
 			if err != nil {
 				return err
diff --git a/testutil/kubernetes_helper.go b/testutil/kubernetes_helper.go
index ca08f5695..db040b581 100644
--- a/testutil/kubernetes_helper.go
+++ b/testutil/kubernetes_helper.go
@@ -211,33 +211,31 @@ func (h *KubernetesHelper) CheckPods(ctx context.Context, namespace string, depl
 
 	err := h.retryFor(6*time.Minute, func() error {
 		checkedPods = []corev1.Pod{}
-		pods, err := h.clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
+		pods, err := h.GetPodsForDeployment(ctx, namespace, deploymentName)
 		if err != nil {
 			return err
 		}
 
 		var deploymentReplicas int
-		for _, pod := range pods.Items {
-			if strings.HasPrefix(pod.Name, deploymentName) {
-				checkedPods = append(checkedPods, pod)
-
-				deploymentReplicas++
-				if pod.Status.Phase != "Running" {
-					return fmt.Errorf("Pod [%s] in namespace [%s] is not running",
-						pod.Name, pod.Namespace)
-				}
-				for _, container := range pod.Status.ContainerStatuses {
-					if !container.Ready {
-						return fmt.Errorf("Container [%s] in pod [%s] in namespace [%s] is not running",
-							container.Name, pod.Name, pod.Namespace)
-					}
+		for _, pod := range pods {
+			checkedPods = append(checkedPods, pod)
+
+			deploymentReplicas++
+			if pod.Status.Phase != "Running" {
+				return fmt.Errorf("Pod [%s] in namespace [%s] is not running",
+					pod.Name, pod.Namespace)
+			}
+			for _, container := range pod.Status.ContainerStatuses {
+				if !container.Ready {
+					return fmt.Errorf("Container [%s] in pod [%s] in namespace [%s] is not running",
+						container.Name, pod.Name, pod.Namespace)
 				}
 			}
 		}
 
 		if deploymentReplicas != replicas {
-			return fmt.Errorf("Expected deployment [%s] in namespace [%s] to have [%d] running pods, but found [%d]",
-				deploymentName, namespace, replicas, deploymentReplicas)
+			return fmt.Errorf("Expected there to be [%d] pods in deployment [%s] in namespace [%s], but found [%d]",
+				replicas, deploymentName, namespace, deploymentReplicas)
 		}
 
 		return nil