Repository: openservicemesh/osm
Pull request #5199 (2022-10-12 19:22:12)

Decouple debugger and k8s.Controller
Signed-off-by: nshankar13 <nshankar@microsoft.com>

<!--

Please describe the motivation for this PR and provide enough
information so that others can review it.

-->
**Description**: This PR decouples the k8s controller from the debug config. Instead of implementing GetPodForProxy in the k8s client, we now use ConfigFromProxy in the compute/kube client. 

This, as well as other methods moved from k8s and debugger to compute do not have functional changes, aside from getEnvoyConfig - which now uses the k8s portforward method instead of its own portforward method.

Resolves: https://github.com/openservicemesh/osm/issues/5158

<!--

Please describe how this change was tested. You could include supporting information
such as logs, snippets, and screenshots.

-->
**Testing done**:

<!--

Please mark with X for applicable areas.

-->
**Affected area**:
| Functional Area            |     |
| -------------------------- | --- |
| New Functionality          | [ ] |
| CI System                  | [ ] |
| CLI Tool                   | [ ] |
| Certificate Management     | [ ] |
| Control Plane              | [ ] |
| Demo                       | [ ] |
| Documentation              | [ ] |
| Egress                     | [ ] |
| Ingress                    | [ ] |
| Install                    | [ ] |
| Networking                 | [ ] |
| Observability              | [ ] |
| Performance                | [ ] |
| SMI Policy                 | [ ] |
| Security                   | [ ] |
| Sidecar Injection          | [ ] |
| Tests                      | [ ] |
| Upgrade                    | [ ] |
| Other                      | [ ] |


Please answer the following questions with yes/no.

1. Does this change contain code from or inspired by another project?
    -   Did you notify the maintainers and provide attribution?

2. Is this a breaking change?

3. Has documentation corresponding to this change been updated in the [osm-docs](https://github.com/openservicemesh/osm-docs) repo (if applicable)?

steeling: @nshankar13 friendly reminder to avoid stomping prior commits, so your reviewers can view just what has changed between reviews
shashankram: Could you also indicate in the commit && PR description if there are functional changes to methods moved across files?
codecov-commenter: # [Codecov](https://codecov.io/gh/openservicemesh/osm/pull/5199?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#5199](https://codecov.io/gh/openservicemesh/osm/pull/5199?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (bfc35db) into [main](https://codecov.io/gh/openservicemesh/osm/commit/26080f96baaaea237ee7c64cec8e90535b2f1c60?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (26080f9) will **increase** coverage by `0.10%`.
> The diff coverage is `42.93%`.

> :exclamation: Current head bfc35db differs from pull request most recent head 8ca7298. Consider uploading reports for the commit 8ca7298 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #5199      +/-   ##
==========================================
+ Coverage   66.94%   67.04%   +0.10%     
==========================================
  Files         200      199       -1     
  Lines       16403    16449      +46     
==========================================
+ Hits        10981    11029      +48     
+ Misses       5369     5366       -3     
- Partials       53       54       +1     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `67.04% <42.93%> (+0.10%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/openservicemesh/osm/pull/5199?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [cmd/osm-controller/osm-controller.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y21kL29zbS1jb250cm9sbGVyL29zbS1jb250cm9sbGVyLmdv) | `17.85% <0.00%> (ø)` | |
| [pkg/catalog/mock\_catalog\_generated.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2NhdGFsb2cvbW9ja19jYXRhbG9nX2dlbmVyYXRlZC5nbw==) | `0.00% <0.00%> (ø)` | |
| [pkg/compute/kube/envoy.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2NvbXB1dGUva3ViZS9lbnZveS5nbw==) | `0.00% <0.00%> (ø)` | |
| [pkg/debugger/feature\_flags.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2RlYnVnZ2VyL2ZlYXR1cmVfZmxhZ3MuZ28=) | `25.00% <0.00%> (ø)` | |
| [pkg/debugger/policy.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2RlYnVnZ2VyL3BvbGljeS5nbw==) | `61.90% <0.00%> (ø)` | |
| [pkg/debugger/proxy.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2RlYnVnZ2VyL3Byb3h5Lmdv) | `2.56% <0.00%> (+0.06%)` | :arrow_up: |
| [pkg/k8s/client.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy9jbGllbnQuZ28=) | `71.85% <0.00%> (-8.86%)` | :arrow_down: |
| [pkg/k8s/informers.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy9pbmZvcm1lcnMuZ28=) | `90.17% <ø> (ø)` | |
| [pkg/k8s/mock\_controller\_generated.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy9tb2NrX2NvbnRyb2xsZXJfZ2VuZXJhdGVkLmdv) | `0.00% <0.00%> (ø)` | |
| [pkg/k8s/types.go](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGtnL2s4cy90eXBlcy5nbw==) | `100.00% <ø> (ø)` | |
| ... and [19 more](https://codecov.io/gh/openservicemesh/osm/pull/5199/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/cmd/osm-controller/osm-controller.go b/cmd/osm-controller/osm-controller.go
index 400e788a..ae43f38b 100644
--- a/cmd/osm-controller/osm-controller.go
+++ b/cmd/osm-controller/osm-controller.go
@@ -253,7 +253,7 @@ func main() {
 
 	// Create DebugServer and start its config event listener.
 	// Listener takes care to start and stop the debug server as appropriate
-	debugConfig := debugger.NewDebugConfig(certManager, xdsGenerator, meshCatalog, proxyRegistry, kubeConfig, kubeClient, k8sClient, msgBroker)
+	debugConfig := debugger.NewDebugConfig(certManager, xdsGenerator, meshCatalog, proxyRegistry, kubeConfig, kubeClient, computeClient, msgBroker)
 	go debugConfig.StartDebugServerConfigListener(stop)
 
 	// Start the k8s pod watcher that updates corresponding k8s secrets
diff --git a/pkg/catalog/mock_catalog_generated.go b/pkg/catalog/mock_catalog_generated.go
index 8116f9da..ba66136d 100644
--- a/pkg/catalog/mock_catalog_generated.go
+++ b/pkg/catalog/mock_catalog_generated.go
@@ -21,6 +21,7 @@ import (
 	v1alpha4 "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/specs/v1alpha4"
 	v1alpha20 "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/split/v1alpha2"
 	types "k8s.io/apimachinery/pkg/types"
+	rest "k8s.io/client-go/rest"
 	cache "k8s.io/client-go/tools/cache"
 	v1alpha10 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
 )
@@ -344,6 +345,21 @@ func (mr *MockMeshCatalogerMockRecorder) GetOutboundMeshTrafficMatches(arg0 inte
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOutboundMeshTrafficMatches", reflect.TypeOf((*MockMeshCataloger)(nil).GetOutboundMeshTrafficMatches), arg0)
 }
 
+// GetProxyConfig mocks base method.
+func (m *MockMeshCataloger) GetProxyConfig(arg0 *models.Proxy, arg1 string, arg2 *rest.Config) (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetProxyConfig", arg0, arg1, arg2)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetProxyConfig indicates an expected call of GetProxyConfig.
+func (mr *MockMeshCatalogerMockRecorder) GetProxyConfig(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyConfig", reflect.TypeOf((*MockMeshCataloger)(nil).GetProxyConfig), arg0, arg1, arg2)
+}
+
 // GetProxyStatsHeaders mocks base method.
 func (m *MockMeshCataloger) GetProxyStatsHeaders(arg0 *models.Proxy) (map[string]string, error) {
 	m.ctrl.T.Helper()
diff --git a/pkg/compute/kube/client.go b/pkg/compute/kube/client.go
index e0105cdf..c8acda1c 100644
--- a/pkg/compute/kube/client.go
+++ b/pkg/compute/kube/client.go
@@ -2,6 +2,7 @@ package kube
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net"
 	"strconv"
@@ -9,22 +10,40 @@ import (
 
 	mapset "github.com/deckarep/golang-set"
 	corev1 "k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
 
 	"k8s.io/utils/pointer"
 
+	configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
 	policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
 
 	"github.com/openservicemesh/osm/pkg/constants"
 
 	"github.com/openservicemesh/osm/pkg/endpoint"
+	"github.com/openservicemesh/osm/pkg/errcode"
 	"github.com/openservicemesh/osm/pkg/identity"
 	"github.com/openservicemesh/osm/pkg/k8s"
 	"github.com/openservicemesh/osm/pkg/models"
 	"github.com/openservicemesh/osm/pkg/service"
 )
 
+var (
+	// errDidNotFindPodForUUID is an error for when OSM cannot not find a pod for the given xDS certificate.
+	errDidNotFindPodForUUID = errors.New("did not find pod for uuid")
+
+	// errServiceAccountDoesNotMatchProxy is an error for when the service account of a Pod does not match the xDS certificate.
+	errServiceAccountDoesNotMatchProxy = errors.New("service account does not match proxy")
+
+	// errMoreThanOnePodForUUID is an error for when OSM finds more than one pod for a given xDS certificate. There should always be exactly one Pod for a given xDS certificate.
+	errMoreThanOnePodForUUID = errors.New("found more than one pod for xDS uuid")
+
+	// errNamespaceDoesNotMatchProxy is an error for when the namespace of the Pod does not match the xDS certificate.
+	errNamespaceDoesNotMatchProxy = errors.New("namespace does not match proxy")
+)
+
 // NewClient returns a client that has all components necessary to connect to and maintain state of a Kubernetes cluster.
 func NewClient(kubeController k8s.Controller) *client { //nolint: revive // unexported-return
 	return &client{
@@ -136,7 +155,7 @@ func (c *client) GetServicesForServiceIdentity(svcIdentity identity.ServiceIdent
 
 // ListServicesForProxy maps an Envoy instance to a number of Kubernetes services.
 func (c *client) ListServicesForProxy(p *models.Proxy) ([]service.MeshService, error) {
-	pod, err := c.kubeController.GetPodForProxy(p)
+	pod, err := c.getPodForProxy(p)
 	if err != nil {
 		return nil, err
 	}
@@ -184,6 +203,11 @@ func (c *client) getServicesByLabels(podLabels map[string]string, targetNamespac
 	return finalList
 }
 
+// GetMeshConfig returns the current MeshConfig
+func (c *client) GetMeshConfig() configv1alpha2.MeshConfig {
+	return c.kubeController.GetMeshConfig()
+}
+
 // GetResolvableEndpointsForService returns the expected endpoints that are to be reached when the service
 // FQDN is resolved
 func (c *client) GetResolvableEndpointsForService(svc service.MeshService) []endpoint.Endpoint {
@@ -248,7 +272,7 @@ func (c *client) ListServices() []service.MeshService {
 
 // IsMetricsEnabled checks if prometheus metrics scraping are enabled on this pod.
 func (c *client) IsMetricsEnabled(proxy *models.Proxy) (bool, error) {
-	pod, err := c.kubeController.GetPodForProxy(proxy)
+	pod, err := c.getPodForProxy(proxy)
 	if err != nil {
 		return false, err
 	}
@@ -410,7 +434,7 @@ func DetectIngressBackendConflicts(x policyv1alpha1.IngressBackend, y policyv1al
 
 // GetProxyStatsHeaders returns stats headers for the given proxy.
 func (c *client) GetProxyStatsHeaders(p *models.Proxy) (map[string]string, error) {
-	pod, err := c.kubeController.GetPodForProxy(p)
+	pod, err := c.getPodForProxy(p)
 	if err != nil {
 		log.Warn().Str("proxy", p.String()).Msg("Could not find pod for connecting proxy. No metadata was recorded.")
 		return nil, err
@@ -444,10 +468,135 @@ func (c *client) GetProxyStatsHeaders(p *models.Proxy) (map[string]string, error
 
 // VerifyProxy attempts to lookup a pod that matches the given proxy instance by service identity, namespace, and UUID.
 func (c *client) VerifyProxy(proxy *models.Proxy) error {
-	_, err := c.kubeController.GetPodForProxy(proxy)
+	_, err := c.getPodForProxy(proxy)
 	return err
 }
 
+// GetPodForProxy returns the pod that the given proxy is attached to, based on the UUID and service identity.
+func (c *client) getPodForProxy(proxy *models.Proxy) (*v1.Pod, error) {
+	proxyUUID, svcAccount := proxy.UUID.String(), proxy.Identity.ToK8sServiceAccount()
+	log.Trace().Msgf("Looking for pod with label %q=%q", constants.EnvoyUniqueIDLabelName, proxyUUID)
+	podList := c.kubeController.ListPods()
+	var pods []v1.Pod
+
+	for _, pod := range podList {
+		if uuid, labelFound := pod.Labels[constants.EnvoyUniqueIDLabelName]; labelFound && uuid == proxyUUID {
+			pods = append(pods, *pod)
+		}
+	}
+
+	if len(pods) == 0 {
+		log.Error().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
+			Msgf("Did not find Pod with label %s = %s in namespace %s",
+				constants.EnvoyUniqueIDLabelName, proxyUUID, svcAccount.Namespace)
+		return nil, errDidNotFindPodForUUID
+	}
+
+	// Each pod is assigned a unique UUID at the time of sidecar injection.
+	// The certificate's CommonName encodes this UUID, and we lookup the pod
+	// whose label matches this UUID.
+	// Only 1 pod must match the UUID encoded in the given certificate. If multiple
+	// pods match, it is an error.
+	if len(pods) > 1 {
+		log.Error().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrPodBelongsToMultipleServices)).
+			Msgf("Found more than one pod with label %s = %s in namespace %s. There can be only one!",
+				constants.EnvoyUniqueIDLabelName, proxyUUID, svcAccount.Namespace)
+		return nil, errMoreThanOnePodForUUID
+	}
+
+	pod := pods[0]
+	log.Trace().Msgf("Found Pod with UID=%s for proxyID %s", pod.ObjectMeta.UID, proxyUUID)
+
+	if pod.Namespace != svcAccount.Namespace {
+		log.Warn().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
+			Msgf("Pod with UID=%s belongs to Namespace %s. The pod's xDS certificate was issued for Namespace %s",
+				pod.ObjectMeta.UID, pod.Namespace, svcAccount.Namespace)
+		return nil, errNamespaceDoesNotMatchProxy
+	}
+
+	// Ensure the Name encoded in the certificate matches that of the Pod
+	// TODO(draychev): check that the Kind matches too! [https://github.com/openservicemesh/osm/issues/3173]
+	if pod.Spec.ServiceAccountName != svcAccount.Name {
+		// Since we search for the pod in the namespace we obtain from the certificate -- these namespaces will always match.
+		log.Warn().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
+			Msgf("Pod with UID=%s belongs to ServiceAccount=%s. The pod's xDS certificate was issued for ServiceAccount=%s",
+				pod.ObjectMeta.UID, pod.Spec.ServiceAccountName, svcAccount)
+		return nil, errServiceAccountDoesNotMatchProxy
+	}
+
+	return &pod, nil
+}
+
+// GetProxyConfig takes the given proxy, port forwards to the pod from this proxy, and returns the envoy config
+func (c *client) GetProxyConfig(proxy *models.Proxy, configType string, kubeConfig *rest.Config) (string, error) {
+	pod, err := c.getPodForProxy(proxy)
+	if err != nil {
+		msg := fmt.Sprintf("Error getting Pod from proxy %s", proxy)
+		log.Error().Err(err).Msg(msg)
+		return "", err
+	}
+	envoyConfig := c.getEnvoyConfig(pod, configType, kubeConfig)
+	return envoyConfig, nil
+}
+
+// getTelemetryPolicy returns the Telemetry policy for the given proxy instance.
+// It returns the most specific match if multiple matching policies exist, in the following
+// order of preference: 1. selector match, 2. namespace match, 3. global match
+func (c *client) getTelemetryPolicy(proxy *models.Proxy) *policyv1alpha1.Telemetry {
+	pod, _ := c.getPodForProxy(proxy)
+	if pod == nil {
+		return nil
+	}
+
+	var policy *policyv1alpha1.Telemetry
+
+	for _, t := range c.kubeController.ListTelemetryPolicies() {
+		// If there is a global policy and a more specific policy hasn't been
+		// found yet, consider the global policy as a candidate
+		if policy == nil && t.Namespace == c.kubeController.GetOSMNamespace() {
+			policy = t
+			continue
+		}
+
+		// If the policy matches the namespace of the proxy's pod,
+		// consider this policy to be a candidate, but continue
+		// to look for a more specific policy that matches the pod
+		// based on a selector
+		if t.Namespace == pod.Namespace {
+			policy = t
+		}
+
+		// Look for a more specific match based on pod selector on the Telemetry resource.
+		// If we find a Telemetry resource that matches the pod's selector, this is
+		// the best match for this proxy.
+		selector := t.Spec.Selector
+		if len(selector) == 0 {
+			continue
+		}
+		sel := labels.Set(selector).AsSelector()
+		if sel.Matches(labels.Set(pod.Labels)) {
+			return t
+		}
+	}
+
+	return policy
+}
+
+// GetTelemetryConfig returns the Telemetry config for the given proxy instance.
+// It returns the most specific match if multiple matching policies exist, in the following
+// order of preference: 1. selector match, 2. namespace match, 3. global match
+func (c *client) GetTelemetryConfig(proxy *models.Proxy) models.TelemetryConfig {
+	var config models.TelemetryConfig
+
+	config.Policy = c.getTelemetryPolicy(proxy)
+
+	if config.Policy != nil && config.Policy.Spec.AccessLog != nil && config.Policy.Spec.AccessLog.OpenTelemetry != nil {
+		config.OpenTelemetryService = c.kubeController.GetExtensionService(config.Policy.Spec.AccessLog.OpenTelemetry.ExtensionService)
+	}
+
+	return config
+}
+
 // ListServiceIdentitiesForService lists ServiceAccounts associated with the given service
 func (c *client) ListServiceIdentitiesForService(name, namespace string) ([]identity.ServiceIdentity, error) {
 	var identities []identity.ServiceIdentity
diff --git a/pkg/compute/kube/envoy.go b/pkg/compute/kube/envoy.go
new file mode 100644
index 00000000..faf20aa3
--- /dev/null
+++ b/pkg/compute/kube/envoy.go
@@ -0,0 +1,74 @@
+package kube
+
+import (
+	"fmt"
+	"io/ioutil"
+	"math/rand"
+	"net/http"
+	"net/url"
+	"strings"
+
+	v1 "k8s.io/api/core/v1"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/transport/spdy"
+
+	"github.com/openservicemesh/osm/pkg/k8s"
+)
+
+func (c *client) getEnvoyConfig(pod *v1.Pod, envoyURL string, kubeConfig *rest.Config) string {
+	log.Debug().Msgf("Getting Envoy config on Pod with UID=%s", pod.ObjectMeta.UID)
+
+	minPort := 16000
+	maxPort := 18000
+	localPort := rand.Intn(maxPort-minPort) + minPort // #nosec G404
+	podPort := 15000
+
+	path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", pod.Namespace, pod.Name)
+	hostIP := strings.TrimLeft(kubeConfig.Host, "htps:/")
+
+	transport, upgrader, err := spdy.RoundTripperFor(kubeConfig)
+	if err != nil {
+		log.Error().Err(err).Msg("error getting spdy RoundTripper")
+	}
+
+	client := &http.Client{Transport: transport}
+	u := &url.URL{Scheme: "https", Path: path, Host: hostIP}
+
+	pf, err := k8s.NewPortForwarder(spdy.NewDialer(upgrader, client, http.MethodPost, u), fmt.Sprintf("%d:%d", localPort, podPort))
+	if err != nil {
+		log.Error().Err(err).Msgf("Error creating portforwarder")
+	}
+
+	var resp *http.Response
+
+	err = pf.Start(func(pf *k8s.PortForwarder) error {
+		defer pf.Stop()
+
+		resp, err := client.Get(fmt.Sprintf("http://%s:%d/%s", "localhost", localPort, envoyURL))
+		if err != nil {
+			log.Error().Err(err).Msgf("Error getting Pod with UID=%s", pod.ObjectMeta.UID)
+			return err
+		}
+
+		//nolint: errcheck
+		defer resp.Body.Close()
+
+		if resp.StatusCode != http.StatusOK {
+			log.Error().Msgf("Error getting Envoy config on Pod with UID=%s; HTTP Error %d", pod.ObjectMeta.UID, resp.StatusCode)
+			return err
+		}
+		return nil
+	})
+	if err != nil {
+		log.Error().Err(err).Msgf("Error getting Pod with UID=%s", pod.ObjectMeta.UID)
+		return fmt.Sprintf("Error: %s", err)
+	}
+
+	bodyBytes, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		log.Error().Err(err).Msgf("Error getting Pod with UID=%s", pod.ObjectMeta.UID)
+		return fmt.Sprintf("Error: %s", err)
+	}
+
+	return string(bodyBytes)
+}
diff --git a/pkg/compute/mock_compute_client_generated.go b/pkg/compute/mock_compute_client_generated.go
index 8642931d..bab44bb0 100644
--- a/pkg/compute/mock_compute_client_generated.go
+++ b/pkg/compute/mock_compute_client_generated.go
@@ -20,6 +20,7 @@ import (
 	v1alpha4 "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/specs/v1alpha4"
 	v1alpha20 "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/split/v1alpha2"
 	types "k8s.io/apimachinery/pkg/types"
+	rest "k8s.io/client-go/rest"
 	cache "k8s.io/client-go/tools/cache"
 	v1alpha10 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
 )
@@ -158,6 +159,21 @@ func (mr *MockInterfaceMockRecorder) GetOSMNamespace() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOSMNamespace", reflect.TypeOf((*MockInterface)(nil).GetOSMNamespace))
 }
 
+// GetProxyConfig mocks base method.
+func (m *MockInterface) GetProxyConfig(arg0 *models.Proxy, arg1 string, arg2 *rest.Config) (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetProxyConfig", arg0, arg1, arg2)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetProxyConfig indicates an expected call of GetProxyConfig.
+func (mr *MockInterfaceMockRecorder) GetProxyConfig(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyConfig", reflect.TypeOf((*MockInterface)(nil).GetProxyConfig), arg0, arg1, arg2)
+}
+
 // GetProxyStatsHeaders mocks base method.
 func (m *MockInterface) GetProxyStatsHeaders(arg0 *models.Proxy) (map[string]string, error) {
 	m.ctrl.T.Helper()
diff --git a/pkg/compute/types.go b/pkg/compute/types.go
index 7779c197..08209da6 100644
--- a/pkg/compute/types.go
+++ b/pkg/compute/types.go
@@ -4,7 +4,9 @@ import (
 	"context"
 
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
 
+	configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
 	policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
 
 	"github.com/openservicemesh/osm/pkg/endpoint"
@@ -76,9 +78,20 @@ type Interface interface {
 
 	GetProxyStatsHeaders(p *models.Proxy) (map[string]string, error)
 
+	// GetProxyConfig takes the given proxy, port forwards to the pod from this proxy, and returns the envoy config
+	GetProxyConfig(proxy *models.Proxy, configType string, kubeConfig *rest.Config) (string, error)
+
 	// VerifyProxy attempts to lookup a pod that matches the given proxy instance by service identity, namespace, and UUID
 	VerifyProxy(proxy *models.Proxy) error
 
 	// ListNamespaces returns the namespaces monitored by the mesh
 	ListNamespaces() ([]string, error)
+
+	// GetTelemetryConfig returns the Telemetry config for the given proxy instance.
+	// It returns the most specific match if multiple matching policies exist, in the following
+	// order of preference: 1. selector match, 2. namespace match, 3. global match
+	GetTelemetryConfig(*models.Proxy) models.TelemetryConfig
+
+	// GetMeshConfig returns the current MeshConfig
+	GetMeshConfig() configv1alpha2.MeshConfig
 }
diff --git a/pkg/debugger/envoy.go b/pkg/debugger/envoy.go
deleted file mode 100644
index 47639741..00000000
--- a/pkg/debugger/envoy.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package debugger
-
-import (
-	"fmt"
-	"io/ioutil"
-	"math/rand"
-	"net/http"
-
-	v1 "k8s.io/api/core/v1"
-)
-
-func (ds DebugConfig) getEnvoyConfig(pod *v1.Pod, url string) string {
-	log.Debug().Msgf("Getting Envoy config on Pod with UID=%s", pod.ObjectMeta.UID)
-
-	minPort := 16000
-	maxPort := 18000
-
-	// #nosec G404
-	portFwdRequest := portForward{
-		Pod:       pod,
-		LocalPort: rand.Intn(maxPort-minPort) + minPort,
-		PodPort:   15000,
-		Stop:      make(chan struct{}),
-		Ready:     make(chan struct{}),
-	}
-	go ds.forwardPort(portFwdRequest)
-
-	<-portFwdRequest.Ready
-
-	client := &http.Client{}
-	resp, err := client.Get(fmt.Sprintf("http://%s:%d/%s", "localhost", portFwdRequest.LocalPort, url))
-	if err != nil {
-		log.Error().Err(err).Msgf("Error getting Pod with UID=%s", pod.ObjectMeta.UID)
-		return fmt.Sprintf("Error: %s", err)
-	}
-
-	defer func() {
-		portFwdRequest.Stop <- struct{}{}
-		_ = resp.Body.Close()
-	}()
-
-	if resp.StatusCode != http.StatusOK {
-		log.Error().Msgf("Error getting Envoy config on Pod with UID=%s; HTTP Error %d", pod.ObjectMeta.UID, resp.StatusCode)
-		portFwdRequest.Stop <- struct{}{}
-		return fmt.Sprintf("Error: %s", err)
-	}
-	bodyBytes, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		log.Error().Err(err).Msgf("Error getting Pod with UID=%s", pod.ObjectMeta.UID)
-		return fmt.Sprintf("Error: %s", err)
-	}
-
-	return string(bodyBytes)
-}
diff --git a/pkg/debugger/feature_flags.go b/pkg/debugger/feature_flags.go
index 6ead6dab..b9b46e1d 100644
--- a/pkg/debugger/feature_flags.go
+++ b/pkg/debugger/feature_flags.go
@@ -8,7 +8,7 @@ import (
 
 func (ds DebugConfig) getFeatureFlags() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		featureFlags := ds.meshCatalog.GetMeshConfig().Spec.FeatureFlags
+		featureFlags := ds.computeClient.GetMeshConfig().Spec.FeatureFlags
 		if featureFlagsJSON, err := json.Marshal(featureFlags); err != nil {
 			log.Error().Err(err).Msgf("Error marshaling feature flags struct: %+v", featureFlags)
 		} else {
diff --git a/pkg/debugger/policy.go b/pkg/debugger/policy.go
index c4433fc7..21b63675 100644
--- a/pkg/debugger/policy.go
+++ b/pkg/debugger/policy.go
@@ -22,7 +22,7 @@ type policies struct {
 
 func (ds DebugConfig) getOSMConfigHandler() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		confJSON, err := utils.MeshConfigToJSON(ds.kubeController.GetMeshConfig())
+		confJSON, err := utils.MeshConfigToJSON(ds.computeClient.GetMeshConfig())
 		if err != nil {
 			log.Error().Err(err).Msg("error getting MeshConfig JSON")
 			return
diff --git a/pkg/debugger/port_forward.go b/pkg/debugger/port_forward.go
deleted file mode 100644
index 6eacafb0..00000000
--- a/pkg/debugger/port_forward.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package debugger
-
-import (
-	"fmt"
-	"net/http"
-	"net/url"
-	"strings"
-
-	"k8s.io/cli-runtime/pkg/genericclioptions"
-
-	v1 "k8s.io/api/core/v1"
-	"k8s.io/client-go/tools/portforward"
-	"k8s.io/client-go/transport/spdy"
-)
-
-type portForward struct {
-	// Pod for which port forwarding is done.
-	Pod *v1.Pod
-
-	// LocalPort is port on local host, which will be used.
-	LocalPort int
-
-	// PodPort is the port on the target Pod, which will be forwarded.
-	PodPort int
-
-	// Stop is a channel managing the port forward lifecycle.
-	Stop chan struct{}
-
-	// Ready is a channel informing us when the tunnel is ready.
-	Ready chan struct{}
-}
-
-func (ds DebugConfig) forwardPort(req portForward) {
-	log.Debug().Msgf("Start port forward to pod with UID=%s on PodPort=%d to LocalPort=%d", req.Pod.ObjectMeta.UID, req.PodPort, req.LocalPort)
-	path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", req.Pod.Namespace, req.Pod.Name)
-	hostIP := strings.TrimLeft(ds.kubeConfig.Host, "htps:/")
-
-	transport, upgrader, err := spdy.RoundTripperFor(ds.kubeConfig)
-	if err != nil {
-		log.Error().Err(err).Msg("error getting spdy RoundTripper")
-	}
-
-	client := &http.Client{Transport: transport}
-	u := &url.URL{Scheme: "https", Path: path, Host: hostIP}
-	streams := genericclioptions.IOStreams{}
-	fw, err := portforward.New(
-		spdy.NewDialer(upgrader, client, http.MethodPost, u),
-		[]string{fmt.Sprintf("%d:%d", req.LocalPort, req.PodPort)},
-		req.Stop,
-		req.Ready,
-		streams.Out,
-		streams.ErrOut,
-	)
-	if err != nil {
-		log.Error().Err(err).Msg("error initializing port forward")
-	}
-
-	if err = fw.ForwardPorts(); err != nil {
-		log.Error().Err(err).Msgf("error starting port forward")
-	}
-}
diff --git a/pkg/debugger/proxy.go b/pkg/debugger/proxy.go
index bd4ef2f4..6eac68a3 100644
--- a/pkg/debugger/proxy.go
+++ b/pkg/debugger/proxy.go
@@ -74,15 +74,14 @@ func (ds DebugConfig) getConfigDump(streamID int64, w http.ResponseWriter) {
 		http.Error(w, msg, http.StatusNotFound)
 		return
 	}
-	pod, err := ds.kubeController.GetPodForProxy(proxy)
+	envoyConfig, err := ds.computeClient.GetProxyConfig(proxy, "config_dump", ds.kubeConfig)
 	if err != nil {
-		msg := fmt.Sprintf("Error getting Pod from proxy %s", proxy)
+		msg := fmt.Sprintf("Error getting envoy config from proxy %s", proxy)
 		log.Error().Err(err).Msg(msg)
 		http.Error(w, msg, http.StatusNotFound)
 		return
 	}
 	w.Header().Set("Content-Type", "application/json")
-	envoyConfig := ds.getEnvoyConfig(pod, "config_dump")
 	_, _ = fmt.Fprintf(w, "%s", envoyConfig)
 }
 
@@ -94,14 +93,13 @@ func (ds DebugConfig) getProxy(streamID int64, w http.ResponseWriter) {
 		http.Error(w, msg, http.StatusNotFound)
 		return
 	}
-	pod, err := ds.kubeController.GetPodForProxy(proxy)
+	envoyConfig, err := ds.computeClient.GetProxyConfig(proxy, "certs", ds.kubeConfig)
 	if err != nil {
-		msg := fmt.Sprintf("Error getting Pod from proxy %s", proxy)
+		msg := fmt.Sprintf("Error getting envoy config from proxy %s", proxy)
 		log.Error().Err(err).Msg(msg)
 		http.Error(w, msg, http.StatusNotFound)
 		return
 	}
 	w.Header().Set("Content-Type", "application/json")
-	envoyConfig := ds.getEnvoyConfig(pod, "certs")
 	_, _ = fmt.Fprintf(w, "%s", envoyConfig)
 }
diff --git a/pkg/debugger/server.go b/pkg/debugger/server.go
index daf2b66e..f67a488f 100644
--- a/pkg/debugger/server.go
+++ b/pkg/debugger/server.go
@@ -9,8 +9,8 @@ import (
 
 	"github.com/openservicemesh/osm/pkg/catalog"
 	"github.com/openservicemesh/osm/pkg/certificate"
+	"github.com/openservicemesh/osm/pkg/compute"
 	"github.com/openservicemesh/osm/pkg/envoy/registry"
-	"github.com/openservicemesh/osm/pkg/k8s"
 	"github.com/openservicemesh/osm/pkg/messaging"
 )
 
@@ -42,14 +42,14 @@ func (ds DebugConfig) GetHandlers() map[string]http.Handler {
 // NewDebugConfig returns an implementation of DebugConfig interface.
 func NewDebugConfig(certDebugger *certificate.Manager, xdsDebugger XDSDebugger, meshCatalog catalog.MeshCataloger,
 	proxyRegistry *registry.ProxyRegistry, kubeConfig *rest.Config, kubeClient kubernetes.Interface,
-	kubeController k8s.Controller, msgBroker *messaging.Broker) DebugConfig {
+	computeClient compute.Interface, msgBroker *messaging.Broker) DebugConfig {
 	return DebugConfig{
-		certDebugger:   certDebugger,
-		xdsDebugger:    xdsDebugger,
-		meshCatalog:    meshCatalog,
-		proxyRegistry:  proxyRegistry,
-		kubeClient:     kubeClient,
-		kubeController: kubeController,
+		certDebugger:  certDebugger,
+		xdsDebugger:   xdsDebugger,
+		meshCatalog:   meshCatalog,
+		proxyRegistry: proxyRegistry,
+		kubeClient:    kubeClient,
+		computeClient: computeClient,
 
 		// We need the Kubernetes config to be able to establish port forwarding to the Envoy pod we want to debug.
 		kubeConfig: kubeConfig,
diff --git a/pkg/debugger/types.go b/pkg/debugger/types.go
index ef1462a0..5901cd89 100644
--- a/pkg/debugger/types.go
+++ b/pkg/debugger/types.go
@@ -9,9 +9,9 @@ import (
 
 	"github.com/openservicemesh/osm/pkg/catalog"
 	"github.com/openservicemesh/osm/pkg/certificate"
+	"github.com/openservicemesh/osm/pkg/compute"
 	"github.com/openservicemesh/osm/pkg/envoy"
 	"github.com/openservicemesh/osm/pkg/envoy/registry"
-	"github.com/openservicemesh/osm/pkg/k8s"
 	"github.com/openservicemesh/osm/pkg/logger"
 	"github.com/openservicemesh/osm/pkg/messaging"
 )
@@ -20,14 +20,14 @@ var log = logger.New("debugger")
 
 // DebugConfig implements the DebugServer interface.
 type DebugConfig struct {
-	certDebugger   *certificate.Manager
-	xdsDebugger    XDSDebugger
-	meshCatalog    catalog.MeshCataloger
-	proxyRegistry  *registry.ProxyRegistry
-	kubeConfig     *rest.Config
-	kubeClient     kubernetes.Interface
-	kubeController k8s.Controller
-	msgBroker      *messaging.Broker
+	certDebugger  *certificate.Manager
+	xdsDebugger   XDSDebugger
+	meshCatalog   catalog.MeshCataloger
+	proxyRegistry *registry.ProxyRegistry
+	kubeConfig    *rest.Config
+	kubeClient    kubernetes.Interface
+	computeClient compute.Interface
+	msgBroker     *messaging.Broker
 }
 
 // XDSDebugger is an interface providing debugging server with methods introspecting XDS.
diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go
index 3ff8b11f..f87da037 100644
--- a/pkg/k8s/client.go
+++ b/pkg/k8s/client.go
@@ -8,9 +8,7 @@ import (
 	smiSpecs "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/specs/v1alpha4"
 	smiSplit "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/split/v1alpha2"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/tools/cache"
 	mcs "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
@@ -18,7 +16,6 @@ import (
 	configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
 	policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
 
-	"github.com/openservicemesh/osm/pkg/constants"
 	"github.com/openservicemesh/osm/pkg/errcode"
 	"github.com/openservicemesh/osm/pkg/messaging"
 	"github.com/openservicemesh/osm/pkg/models"
@@ -229,62 +226,6 @@ func (c *Client) GetMeshConfig() configv1alpha2.MeshConfig {
 	return configv1alpha2.MeshConfig{}
 }
 
-// GetPodForProxy returns the pod that the given proxy is attached to, based on the UUID and service identity.
-// TODO(4863): move this to kube/client.go
-func (c *Client) GetPodForProxy(proxy *models.Proxy) (*v1.Pod, error) {
-	proxyUUID, svcAccount := proxy.UUID.String(), proxy.Identity.ToK8sServiceAccount()
-	log.Trace().Msgf("Looking for pod with label %q=%q", constants.EnvoyUniqueIDLabelName, proxyUUID)
-	podList := c.ListPods()
-	var pods []v1.Pod
-
-	for _, pod := range podList {
-		if uuid, labelFound := pod.Labels[constants.EnvoyUniqueIDLabelName]; labelFound && uuid == proxyUUID {
-			pods = append(pods, *pod)
-		}
-	}
-
-	if len(pods) == 0 {
-		log.Error().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
-			Msgf("Did not find Pod with label %s = %s in namespace %s",
-				constants.EnvoyUniqueIDLabelName, proxyUUID, svcAccount.Namespace)
-		return nil, errDidNotFindPodForUUID
-	}
-
-	// Each pod is assigned a unique UUID at the time of sidecar injection.
-	// The certificate's CommonName encodes this UUID, and we lookup the pod
-	// whose label matches this UUID.
-	// Only 1 pod must match the UUID encoded in the given certificate. If multiple
-	// pods match, it is an error.
-	if len(pods) > 1 {
-		log.Error().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrPodBelongsToMultipleServices)).
-			Msgf("Found more than one pod with label %s = %s in namespace %s. There can be only one!",
-				constants.EnvoyUniqueIDLabelName, proxyUUID, svcAccount.Namespace)
-		return nil, errMoreThanOnePodForUUID
-	}
-
-	pod := pods[0]
-	log.Trace().Msgf("Found Pod with UID=%s for proxyID %s", pod.ObjectMeta.UID, proxyUUID)
-
-	if pod.Namespace != svcAccount.Namespace {
-		log.Warn().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
-			Msgf("Pod with UID=%s belongs to Namespace %s. The pod's xDS certificate was issued for Namespace %s",
-				pod.ObjectMeta.UID, pod.Namespace, svcAccount.Namespace)
-		return nil, errNamespaceDoesNotMatchProxy
-	}
-
-	// Ensure the Name encoded in the certificate matches that of the Pod
-	// TODO(draychev): check that the Kind matches too! [https://github.com/openservicemesh/osm/issues/3173]
-	if pod.Spec.ServiceAccountName != svcAccount.Name {
-		// Since we search for the pod in the namespace we obtain from the certificate -- these namespaces will always match.
-		log.Warn().Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrFetchingPodFromCert)).
-			Msgf("Pod with UID=%s belongs to ServiceAccount=%s. The pod's xDS certificate was issued for ServiceAccount=%s",
-				pod.ObjectMeta.UID, pod.Spec.ServiceAccountName, svcAccount)
-		return nil, errServiceAccountDoesNotMatchProxy
-	}
-
-	return &pod, nil
-}
-
 // GetOSMNamespace returns the namespace in which the OSM controller pod resides.
 func (c *Client) GetOSMNamespace() string {
 	return c.osmNamespace
@@ -322,6 +263,15 @@ func (c *Client) ListIngressBackendPolicies() []*policyv1alpha1.IngressBackend {
 	return backends
 }
 
+// GetExtensionService returns the extension service for the given service ref
+func (c *Client) GetExtensionService(svc policyv1alpha1.ExtensionServiceRef) *configv1alpha2.ExtensionService {
+	resource, exists, err := c.getByKey(informerKeyExtensionService, key(svc.Name, svc.Namespace))
+	if exists && err == nil {
+		return resource.(*configv1alpha2.ExtensionService)
+	}
+	return nil
+}
+
 // ListRetryPolicies returns the retry policies for the given source identity based on service accounts.
 func (c *Client) ListRetryPolicies() []*policyv1alpha1.Retry {
 	var retries []*policyv1alpha1.Retry
@@ -338,6 +288,23 @@ func (c *Client) ListRetryPolicies() []*policyv1alpha1.Retry {
 	return retries
 }
 
+// ListTelemetryPolicies returns all the telemetry policies.
+func (c *Client) ListTelemetryPolicies() []*policyv1alpha1.Telemetry {
+	var telemetryPolicies []*policyv1alpha1.Telemetry
+
+	for _, resource := range c.list(informerKeyTelemetry) {
+		t := resource.(*policyv1alpha1.Telemetry)
+
+		if !c.IsMonitoredNamespace(t.Namespace) {
+			continue
+		}
+
+		telemetryPolicies = append(telemetryPolicies, t)
+	}
+
+	return telemetryPolicies
+}
+
 // ListUpstreamTrafficSettings returns the all UpstreamTrafficSetting resources
 func (c *Client) ListUpstreamTrafficSettings() []*policyv1alpha1.UpstreamTrafficSetting {
 	var settings []*policyv1alpha1.UpstreamTrafficSetting
@@ -491,55 +458,6 @@ func (c *Client) ListTrafficTargets() []*smiAccess.TrafficTarget {
 	return trafficTargets
 }
 
-// getTelemetryPolicy returns the Telemetry policy for the given proxy instance.
-// It returns the most specific match if multiple matching policies exist, in the following
-// order of preference: 1. selector match, 2. namespace match, 3. global match
-func (c *Client) getTelemetryPolicy(proxy *models.Proxy) *policyv1alpha1.Telemetry {
-	pod, _ := c.GetPodForProxy(proxy)
-	if pod == nil {
-		return nil
-	}
-
-	var policy *policyv1alpha1.Telemetry
-
-	for _, resource := range c.list(informerKeyTelemetry) {
-		t := resource.(*policyv1alpha1.Telemetry)
-
-		// If there is a global policy and a more specific policy hasn't been
-		// found yet, consider the global policy as a candidate
-		if policy == nil && t.Namespace == c.osmNamespace {
-			policy = t
-			continue
-		}
-
-		if !c.IsMonitoredNamespace(t.Namespace) {
-			continue
-		}
-
-		// If the policy matches the namespace of the proxy's pod,
-		// consider this policy to be a candidate, but continue
-		// to look for a more specific policy that matches the pod
-		// based on a selector
-		if t.Namespace == pod.Namespace {
-			policy = t
-		}
-
-		// Look for a more specific match based on pod selector on the Telemetry resource.
-		// If we find a Telemetry resource that matches the pod's selector, this is
-		// the best match for this proxy.
-		selector := t.Spec.Selector
-		if len(selector) == 0 {
-			continue
-		}
-		sel := labels.Set(selector).AsSelector()
-		if sel.Matches(labels.Set(pod.Labels)) {
-			return t
-		}
-	}
-
-	return policy
-}
-
 // ListServiceImports returns all ServiceImport resources
 func (c *Client) ListServiceImports() []*mcs.ServiceImport {
 	var serviceImports []*mcs.ServiceImport
@@ -574,26 +492,3 @@ func (c *Client) ListServiceExports() []*mcs.ServiceExport {
 func (c *Client) AddMeshRootCertificateEventHandler(handler cache.ResourceEventHandler, resyncInterval time.Duration) {
 	c.informers[informerKeyMeshRootCertificate].AddEventHandlerWithResyncPeriod(handler, resyncInterval)
 }
-
-func (c *Client) getExtensionService(svc policyv1alpha1.ExtensionServiceRef) *configv1alpha2.ExtensionService {
-	resource, exists, err := c.getByKey(informerKeyExtensionService, key(svc.Name, svc.Namespace))
-	if exists && err == nil {
-		return resource.(*configv1alpha2.ExtensionService)
-	}
-	return nil
-}
-
-// GetTelemetryConfig returns the Telemetry config for the given proxy instance.
-// It returns the most specific match if multiple matching policies exist, in the following
-// order of preference: 1. selector match, 2. namespace match, 3. global match
-func (c *Client) GetTelemetryConfig(proxy *models.Proxy) models.TelemetryConfig {
-	var config models.TelemetryConfig
-
-	config.Policy = c.getTelemetryPolicy(proxy)
-
-	if config.Policy != nil && config.Policy.Spec.AccessLog != nil && config.Policy.Spec.AccessLog.OpenTelemetry != nil {
-		config.OpenTelemetryService = c.getExtensionService(config.Policy.Spec.AccessLog.OpenTelemetry.ExtensionService)
-	}
-
-	return config
-}
diff --git a/pkg/k8s/errors.go b/pkg/k8s/errors.go
index fec83b00..83e8147e 100644
--- a/pkg/k8s/errors.go
+++ b/pkg/k8s/errors.go
@@ -4,16 +4,4 @@ import "fmt"
 
 var (
 	errListingNamespaces = fmt.Errorf("Failed to list monitored namespaces")
-
-	// errMoreThanOnePodForUUID is an error for when OSM finds more than one pod for a given xDS certificate. There should always be exactly one Pod for a given xDS certificate.
-	errMoreThanOnePodForUUID = fmt.Errorf("found more than one pod for xDS uuid")
-
-	// errDidNotFindPodForUUID is an error for when OSM cannot not find a pod for the given xDS certificate.
-	errDidNotFindPodForUUID = fmt.Errorf("did not find pod for uuid")
-
-	// errServiceAccountDoesNotMatchProxy is an error for when the service account of a Pod does not match the xDS certificate.
-	errServiceAccountDoesNotMatchProxy = fmt.Errorf("service account does not match proxy")
-
-	// errNamespaceDoesNotMatchProxy is an error for when the namespace of the Pod does not match the xDS certificate.
-	errNamespaceDoesNotMatchProxy = fmt.Errorf("namespace does not match proxy")
 )
diff --git a/pkg/k8s/mock_controller_generated.go b/pkg/k8s/mock_controller_generated.go
index 9a01eb9a..82cc2634 100644
--- a/pkg/k8s/mock_controller_generated.go
+++ b/pkg/k8s/mock_controller_generated.go
@@ -72,6 +72,20 @@ func (mr *MockControllerMockRecorder) GetEndpoints(arg0, arg1 interface{}) *gomo
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEndpoints", reflect.TypeOf((*MockController)(nil).GetEndpoints), arg0, arg1)
 }
 
+// GetExtensionService mocks base method.
+func (m *MockController) GetExtensionService(arg0 v1alpha1.ExtensionServiceRef) *v1alpha2.ExtensionService {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetExtensionService", arg0)
+	ret0, _ := ret[0].(*v1alpha2.ExtensionService)
+	return ret0
+}
+
+// GetExtensionService indicates an expected call of GetExtensionService.
+func (mr *MockControllerMockRecorder) GetExtensionService(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtensionService", reflect.TypeOf((*MockController)(nil).GetExtensionService), arg0)
+}
+
 // GetHTTPRouteGroup mocks base method.
 func (m *MockController) GetHTTPRouteGroup(arg0 string) *v1alpha4.HTTPRouteGroup {
 	m.ctrl.T.Helper()
@@ -142,21 +156,6 @@ func (mr *MockControllerMockRecorder) GetOSMNamespace() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOSMNamespace", reflect.TypeOf((*MockController)(nil).GetOSMNamespace))
 }
 
-// GetPodForProxy mocks base method.
-func (m *MockController) GetPodForProxy(arg0 *models.Proxy) (*v1.Pod, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetPodForProxy", arg0)
-	ret0, _ := ret[0].(*v1.Pod)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetPodForProxy indicates an expected call of GetPodForProxy.
-func (mr *MockControllerMockRecorder) GetPodForProxy(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPodForProxy", reflect.TypeOf((*MockController)(nil).GetPodForProxy), arg0)
-}
-
 // GetSecret mocks base method.
 func (m *MockController) GetSecret(arg0, arg1 string) *models.Secret {
 	m.ctrl.T.Helper()
@@ -199,20 +198,6 @@ func (mr *MockControllerMockRecorder) GetTCPRoute(arg0 interface{}) *gomock.Call
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTCPRoute", reflect.TypeOf((*MockController)(nil).GetTCPRoute), arg0)
 }
 
-// GetTelemetryConfig mocks base method.
-func (m *MockController) GetTelemetryConfig(arg0 *models.Proxy) models.TelemetryConfig {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetTelemetryConfig", arg0)
-	ret0, _ := ret[0].(models.TelemetryConfig)
-	return ret0
-}
-
-// GetTelemetryConfig indicates an expected call of GetTelemetryConfig.
-func (mr *MockControllerMockRecorder) GetTelemetryConfig(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTelemetryConfig", reflect.TypeOf((*MockController)(nil).GetTelemetryConfig), arg0)
-}
-
 // GetUpstreamTrafficSetting mocks base method.
 func (m *MockController) GetUpstreamTrafficSetting(arg0 *types.NamespacedName) *v1alpha1.UpstreamTrafficSetting {
 	m.ctrl.T.Helper()
@@ -425,6 +410,20 @@ func (mr *MockControllerMockRecorder) ListTCPTrafficSpecs() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTCPTrafficSpecs", reflect.TypeOf((*MockController)(nil).ListTCPTrafficSpecs))
 }
 
+// ListTelemetryPolicies mocks base method.
+func (m *MockController) ListTelemetryPolicies() []*v1alpha1.Telemetry {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ListTelemetryPolicies")
+	ret0, _ := ret[0].([]*v1alpha1.Telemetry)
+	return ret0
+}
+
+// ListTelemetryPolicies indicates an expected call of ListTelemetryPolicies.
+func (mr *MockControllerMockRecorder) ListTelemetryPolicies() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTelemetryPolicies", reflect.TypeOf((*MockController)(nil).ListTelemetryPolicies))
+}
+
 // ListTrafficSplits mocks base method.
 func (m *MockController) ListTrafficSplits() []*v1alpha20.TrafficSplit {
 	m.ctrl.T.Helper()
diff --git a/pkg/k8s/types.go b/pkg/k8s/types.go
index 16e9c264..f4db1234 100644
--- a/pkg/k8s/types.go
+++ b/pkg/k8s/types.go
@@ -82,6 +82,9 @@ type Controller interface {
 	// GetService returns a corev1 Service representation if the MeshService exists in cache, otherwise nil
 	GetService(name, namespace string) *corev1.Service
 
+	// GetExtensionService returns the extension service for the given service ref
+	GetExtensionService(svc policyv1alpha1.ExtensionServiceRef) *configv1alpha2.ExtensionService
+
 	// ListNamespaces returns the namespaces monitored by the mesh
 	ListNamespaces() ([]*corev1.Namespace, error)
 
@@ -94,8 +97,8 @@ type Controller interface {
 	// GetEndpoints returns the endpoints for a given service, if found
 	GetEndpoints(name, namespace string) (*corev1.Endpoints, error)
 
-	// GetPodForProxy returns the pod that the given proxy is attached to, based on the UUID and service identity.
-	GetPodForProxy(proxy *models.Proxy) (*corev1.Pod, error)
+	// ListTelemetryPolices returns all the telemetry policies.
+	ListTelemetryPolicies() []*policyv1alpha1.Telemetry
 }
 
 // PassthroughInterface is the interface for methods that are implemented by the k8s.Client, but are not considered
@@ -161,9 +164,4 @@ type PassthroughInterface interface {
 
 	// ListServiceExports returns all the ServiceExport resources
 	ListServiceExports() []*mcs.ServiceExport
-
-	// GetTelemetryConfig returns the Telemetry config for the given proxy instance.
-	// It returns the most specific match if multiple matching policies exist, in the following
-	// order of preference: 1. selector match, 2. namespace match, 3. global match
-	GetTelemetryConfig(*models.Proxy) models.TelemetryConfig
 }


Test patch:
diff --git a/pkg/compute/kube/client_test.go b/pkg/compute/kube/client_test.go
index 4c09ccfe..f6674ed5 100644
--- a/pkg/compute/kube/client_test.go
+++ b/pkg/compute/kube/client_test.go
@@ -2,7 +2,6 @@ package kube
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"net"
 	"testing"
@@ -24,7 +23,9 @@ import (
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 
+	configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
 	policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
+
 	"github.com/openservicemesh/osm/pkg/models"
 
 	"github.com/openservicemesh/osm/pkg/constants"
@@ -36,6 +37,10 @@ import (
 	"github.com/openservicemesh/osm/pkg/tests"
 )
 
+var (
+	testMeshName = "mesh"
+)
+
 var _ = Describe("Test Kube client Provider (w/o kubecontroller)", func() {
 	var (
 		mockCtrl           *gomock.Controller
@@ -551,6 +556,12 @@ func TestGetHostnamesForServicePort(t *testing.T) {
 }
 
 func TestIsMetricsEnabled(t *testing.T) {
+	proxyUUID := uuid.New()
+	podlabels := map[string]string{
+		constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+	}
+	serviceAccountName := tests.BookstoreServiceAccountName
+	namespace := tests.BookstoreServiceAccount.Namespace
 	testCases := []struct {
 		name string
 
@@ -563,6 +574,11 @@ func TestIsMetricsEnabled(t *testing.T) {
 			pod: &corev1.Pod{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: nil,
+					Labels:      podlabels,
+					Namespace:   namespace,
+				},
+				Spec: corev1.PodSpec{
+					ServiceAccountName: serviceAccountName,
 				},
 			},
 			expectEnabled: false,
@@ -574,6 +590,11 @@ func TestIsMetricsEnabled(t *testing.T) {
 					Annotations: map[string]string{
 						constants.PrometheusScrapeAnnotation: "true",
 					},
+					Labels:    podlabels,
+					Namespace: namespace,
+				},
+				Spec: corev1.PodSpec{
+					ServiceAccountName: serviceAccountName,
 				},
 			},
 			expectEnabled: true,
@@ -585,6 +606,11 @@ func TestIsMetricsEnabled(t *testing.T) {
 					Annotations: map[string]string{
 						constants.PrometheusScrapeAnnotation: "false",
 					},
+					Labels:    podlabels,
+					Namespace: namespace,
+				},
+				Spec: corev1.PodSpec{
+					ServiceAccountName: serviceAccountName,
 				},
 			},
 			expectEnabled: false,
@@ -596,6 +622,11 @@ func TestIsMetricsEnabled(t *testing.T) {
 					Annotations: map[string]string{
 						constants.PrometheusScrapeAnnotation: "no",
 					},
+					Labels:    podlabels,
+					Namespace: namespace,
+				},
+				Spec: corev1.PodSpec{
+					ServiceAccountName: serviceAccountName,
 				},
 			},
 			expectEnabled: false,
@@ -608,14 +639,11 @@ func TestIsMetricsEnabled(t *testing.T) {
 			assert := tassert.New(t)
 			mockCtrl := gomock.NewController(t)
 			k := k8s.NewMockController(mockCtrl)
-			if tc.pod != nil {
-				k.EXPECT().GetPodForProxy(gomock.Any()).Return(tc.pod, nil)
-			} else {
-				k.EXPECT().GetPodForProxy(gomock.Any()).Return(nil, errors.New("not found"))
-			}
+			podArr := []*corev1.Pod{tc.pod}
+			k.EXPECT().ListPods().Return(podArr).AnyTimes()
 			c := NewClient(k)
 
-			actual, err := c.IsMetricsEnabled(&models.Proxy{})
+			actual, err := c.IsMetricsEnabled(&models.Proxy{UUID: proxyUUID, Identity: tests.BookstoreServiceIdentity})
 			assert.Equal(tc.expectEnabled, actual)
 			if tc.expectErr {
 				assert.Error(err)
@@ -626,6 +654,259 @@ func TestIsMetricsEnabled(t *testing.T) {
 	}
 }
 
+func monitoredNS(name string) *corev1.Namespace {
+	return &corev1.Namespace{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: name,
+			Labels: map[string]string{
+				constants.OSMKubeResourceMonitorAnnotation: testMeshName,
+			},
+		},
+	}
+}
+
+func TestGetPodForProxy(t *testing.T) {
+	assert := tassert.New(t)
+	stop := make(chan struct{})
+	defer close(stop)
+
+	proxyUUID := uuid.New()
+	someOtherEnvoyUID := uuid.New()
+	namespace := tests.BookstoreServiceAccount.Namespace
+
+	podlabels := map[string]string{
+		constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+	}
+
+	pod := tests.NewPodFixture(namespace, "pod-1", tests.BookstoreServiceAccountName, podlabels)
+	someOthePodLabels := map[string]string{
+		constants.AppLabel:               tests.SelectorValue,
+		constants.EnvoyUniqueIDLabelName: someOtherEnvoyUID.String(),
+	}
+
+	kubeClient := fake.NewSimpleClientset(
+		monitoredNS(namespace),
+		monitoredNS("bad-namespace"),
+		tests.NewPodFixture(namespace, "pod-0", tests.BookstoreServiceAccountName, someOthePodLabels),
+		pod,
+		tests.NewPodFixture(namespace, "pod-2", tests.BookstoreServiceAccountName, someOthePodLabels),
+	)
+
+	broker := messaging.NewBroker(stop)
+
+	k8sClient, err := k8s.NewClient(tests.OsmNamespace, tests.OsmMeshConfigName, broker, k8s.WithKubeClient(kubeClient, testMeshName))
+	assert.NoError(err)
+
+	testCases := []struct {
+		name  string
+		pod   *corev1.Pod
+		proxy *models.Proxy
+		err   error
+	}{
+		{
+			name:  "fails when UUID does not match",
+			proxy: models.NewProxy(models.KindSidecar, uuid.New(), tests.BookstoreServiceIdentity, nil, 1),
+			err:   errDidNotFindPodForUUID,
+		},
+		{
+			name:  "fails when service account does not match certificate",
+			proxy: &models.Proxy{UUID: proxyUUID, Identity: identity.New("bad-name", namespace)},
+			err:   errServiceAccountDoesNotMatchProxy,
+		},
+		{
+			name:  "2 pods with same uuid",
+			proxy: models.NewProxy(models.KindSidecar, someOtherEnvoyUID, tests.BookstoreServiceIdentity, nil, 1),
+			err:   errMoreThanOnePodForUUID,
+		},
+		{
+			name:  "fails when namespace does not match certificate",
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, identity.New(tests.BookstoreServiceAccountName, "bad-namespace"), nil, 1),
+			err:   errNamespaceDoesNotMatchProxy,
+		},
+		{
+			name:  "works as expected",
+			pod:   pod,
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, tests.BookstoreServiceIdentity, nil, 1),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			assert := tassert.New(t)
+			c := NewClient(k8sClient)
+			pod, err := c.getPodForProxy(tc.proxy)
+
+			assert.Equal(tc.pod, pod)
+			assert.Equal(tc.err, err)
+		})
+	}
+}
+
+func TestGetTelemetryConfig(t *testing.T) {
+	proxyUUID := uuid.New()
+	appNamespace := "test"
+	osmNamespace := "global"
+
+	globalPolicy := &policyv1alpha1.Telemetry{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: osmNamespace,
+			Name:      "t1",
+		},
+	}
+
+	namespacePolicy := &policyv1alpha1.Telemetry{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: appNamespace,
+			Name:      "t2",
+		},
+	}
+
+	selectorPolicy := &policyv1alpha1.Telemetry{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: appNamespace,
+			Name:      "t3",
+		},
+		Spec: policyv1alpha1.TelemetrySpec{
+			Selector: map[string]string{"app": "foo"},
+		},
+	}
+
+	otelPolicy := &policyv1alpha1.Telemetry{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: appNamespace,
+			Name:      "t2",
+		},
+		Spec: policyv1alpha1.TelemetrySpec{
+			Selector: map[string]string{"app": "foo"},
+			AccessLog: &policyv1alpha1.EnvoyAccessLogConfig{
+				OpenTelemetry: &policyv1alpha1.EnvoyAccessLogOpenTelemetryConfig{
+					ExtensionService: policyv1alpha1.ExtensionServiceRef{
+						Namespace: "otel-ns",
+						Name:      "otel-collector",
+					},
+				},
+			},
+		},
+	}
+
+	otelExtSvc := &configv1alpha2.ExtensionService{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "otel-ns",
+			Name:      "otel-collector",
+		},
+	}
+
+	testCases := []struct {
+		name              string
+		proxy             *models.Proxy
+		pod               *corev1.Pod
+		telemetryPolicies []*policyv1alpha1.Telemetry
+		extensionServices []runtime.Object
+		expected          models.TelemetryConfig
+	}{
+		{
+			name:  "matches global scope policy",
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
+			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
+				map[string]string{
+					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+					"app":                            "foo",
+				}),
+			telemetryPolicies: []*policyv1alpha1.Telemetry{
+				globalPolicy,
+			},
+			expected: models.TelemetryConfig{
+				Policy: globalPolicy,
+			},
+		},
+		{
+			name:  "matches namespace scope policy",
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
+			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
+				map[string]string{
+					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+					"app":                            "foo",
+				}),
+			telemetryPolicies: []*policyv1alpha1.Telemetry{
+				globalPolicy,
+				namespacePolicy,
+			},
+			expected: models.TelemetryConfig{
+				Policy: namespacePolicy,
+			},
+		},
+		{
+			name:  "matches selector scope policy",
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
+			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
+				map[string]string{
+					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+					"app":                            "foo",
+				}),
+			telemetryPolicies: []*policyv1alpha1.Telemetry{
+				globalPolicy,
+				namespacePolicy,
+				selectorPolicy,
+			},
+			expected: models.TelemetryConfig{
+				Policy: selectorPolicy,
+			},
+		},
+		{
+			name:  "matches policy with OpenTelemetry config",
+			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
+			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
+				map[string]string{
+					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+					"app":                            "foo",
+				}),
+			telemetryPolicies: []*policyv1alpha1.Telemetry{
+				otelPolicy,
+			},
+			extensionServices: []runtime.Object{
+				otelExtSvc,
+			},
+			expected: models.TelemetryConfig{
+				Policy:               otelPolicy,
+				OpenTelemetryService: otelExtSvc,
+			},
+		},
+		{
+			name:  "no policy when proxy does not match pod",
+			proxy: models.NewProxy(models.KindSidecar, uuid.New(), "sa-1.test", nil, 1), // new UUID to avoid matching proxyUUID
+			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
+				map[string]string{
+					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
+					"app":                            "foo",
+				}),
+			telemetryPolicies: []*policyv1alpha1.Telemetry{
+				globalPolicy,
+				namespacePolicy,
+				selectorPolicy,
+			},
+			expected: models.TelemetryConfig{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			a := assert.New(t)
+
+			mockCtrl := gomock.NewController(t)
+			k := k8s.NewMockController(mockCtrl)
+			podArr := []*corev1.Pod{tc.pod}
+			k.EXPECT().ListPods().Return(podArr).AnyTimes()
+			k.EXPECT().ListTelemetryPolicies().Return(tc.telemetryPolicies).AnyTimes()
+			k.EXPECT().GetOSMNamespace().Return(osmNamespace).AnyTimes()
+			k.EXPECT().GetExtensionService(otelPolicy.Spec.AccessLog.OpenTelemetry.ExtensionService).Return(otelExtSvc).AnyTimes()
+			c := NewClient(k)
+
+			actual := c.GetTelemetryConfig(tc.proxy)
+			a.Equal(tc.expected, actual)
+		})
+	}
+}
+
 func TestListServicesForProxy(t *testing.T) {
 	goodUUID := uuid.New()
 	badUUID := uuid.New()
diff --git a/pkg/debugger/server_test.go b/pkg/debugger/server_test.go
index 066290f1..3c99e489 100644
--- a/pkg/debugger/server_test.go
+++ b/pkg/debugger/server_test.go
@@ -10,8 +10,8 @@ import (
 
 	"github.com/openservicemesh/osm/pkg/catalog"
 	tresorFake "github.com/openservicemesh/osm/pkg/certificate/providers/tresor/fake"
+	"github.com/openservicemesh/osm/pkg/compute"
 	"github.com/openservicemesh/osm/pkg/envoy/registry"
-	"github.com/openservicemesh/osm/pkg/k8s"
 )
 
 // Tests GetHandlers returns the expected debug endpoints and non-nil handlers
@@ -23,7 +23,7 @@ func TestGetHandlers(t *testing.T) {
 	mockXdsDebugger := NewMockXDSDebugger(mockCtrl)
 	mockCatalogDebugger := catalog.NewMockMeshCataloger(mockCtrl)
 	client := testclient.NewSimpleClientset()
-	mockKubeController := k8s.NewMockController(mockCtrl)
+	mockCompute := compute.NewMockInterface(mockCtrl)
 	proxyRegistry := registry.NewProxyRegistry()
 
 	ds := NewDebugConfig(cm,
@@ -32,7 +32,7 @@ func TestGetHandlers(t *testing.T) {
 		proxyRegistry,
 		nil,
 		client,
-		mockKubeController,
+		mockCompute,
 		nil)
 
 	handlers := ds.GetHandlers()
diff --git a/pkg/k8s/client_test.go b/pkg/k8s/client_test.go
index 4f8ec9dd..bf5f0028 100644
--- a/pkg/k8s/client_test.go
+++ b/pkg/k8s/client_test.go
@@ -6,7 +6,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/google/uuid"
 	smiSpecs "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/specs/v1alpha4"
 	smiAccessClientFake "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/access/clientset/versioned/fake"
 	smiSpecClientFake "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/specs/clientset/versioned/fake"
@@ -29,7 +28,6 @@ import (
 	fakePolicyClient "github.com/openservicemesh/osm/pkg/gen/client/policy/clientset/versioned/fake"
 
 	"github.com/openservicemesh/osm/pkg/constants"
-	"github.com/openservicemesh/osm/pkg/identity"
 	"github.com/openservicemesh/osm/pkg/messaging"
 	"github.com/openservicemesh/osm/pkg/metricsstore"
 	"github.com/openservicemesh/osm/pkg/models"
@@ -843,81 +841,6 @@ func TestConfigUpdateStatus(t *testing.T) {
 	}
 }
 
-func TestGetPodForProxy(t *testing.T) {
-	assert := tassert.New(t)
-	stop := make(chan struct{})
-	defer close(stop)
-
-	proxyUUID := uuid.New()
-	someOtherEnvoyUID := uuid.New()
-	namespace := tests.BookstoreServiceAccount.Namespace
-
-	podlabels := map[string]string{
-		constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-	}
-	someOthePodLabels := map[string]string{
-		constants.AppLabel:               tests.SelectorValue,
-		constants.EnvoyUniqueIDLabelName: someOtherEnvoyUID.String(),
-	}
-
-	pod := tests.NewPodFixture(namespace, "pod-1", tests.BookstoreServiceAccountName, podlabels)
-	kubeClient := fake.NewSimpleClientset(
-		monitoredNS(namespace),
-		monitoredNS("bad-namespace"),
-		tests.NewPodFixture(namespace, "pod-0", tests.BookstoreServiceAccountName, someOthePodLabels),
-		pod,
-		tests.NewPodFixture(namespace, "pod-2", tests.BookstoreServiceAccountName, someOthePodLabels),
-	)
-
-	broker := messaging.NewBroker(stop)
-
-	c, err := NewClient(tests.OsmNamespace, tests.OsmMeshConfigName, broker, WithKubeClient(kubeClient, testMeshName))
-	assert.NoError(err)
-
-	testCases := []struct {
-		name  string
-		pod   *corev1.Pod
-		proxy *models.Proxy
-		err   error
-	}{
-		{
-			name:  "fails when UUID does not match",
-			proxy: models.NewProxy(models.KindSidecar, uuid.New(), tests.BookstoreServiceIdentity, nil, 1),
-			err:   errDidNotFindPodForUUID,
-		},
-		{
-			name:  "fails when service account does not match certificate",
-			proxy: &models.Proxy{UUID: proxyUUID, Identity: identity.New("bad-name", namespace)},
-			err:   errServiceAccountDoesNotMatchProxy,
-		},
-		{
-			name:  "2 pods with same uuid",
-			proxy: models.NewProxy(models.KindSidecar, someOtherEnvoyUID, tests.BookstoreServiceIdentity, nil, 1),
-			err:   errMoreThanOnePodForUUID,
-		},
-		{
-			name:  "fails when namespace does not match certificate",
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, identity.New(tests.BookstoreServiceAccountName, "bad-namespace"), nil, 1),
-			err:   errNamespaceDoesNotMatchProxy,
-		},
-		{
-			name:  "works as expected",
-			pod:   pod,
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, tests.BookstoreServiceIdentity, nil, 1),
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			assert := tassert.New(t)
-			pod, err := c.GetPodForProxy(tc.proxy)
-
-			assert.Equal(tc.pod, pod)
-			assert.Equal(tc.err, err)
-		})
-	}
-}
-
 func monitoredNS(name string) *v1.Namespace {
 	return &v1.Namespace{
 		ObjectMeta: metav1.ObjectMeta{
@@ -1571,171 +1494,6 @@ func TestGetTCPRoute(t *testing.T) {
 	a.Nil(invalid)
 }
 
-func TestGetTelemetryConfig(t *testing.T) {
-	proxyUUID := uuid.New()
-	appNamespace := "test"
-	osmNamespace := "global"
-
-	globalPolicy := &policyv1alpha1.Telemetry{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: osmNamespace,
-			Name:      "t1",
-		},
-	}
-
-	namespacePolicy := &policyv1alpha1.Telemetry{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: appNamespace,
-			Name:      "t2",
-		},
-	}
-
-	selectorPolicy := &policyv1alpha1.Telemetry{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: appNamespace,
-			Name:      "t3",
-		},
-		Spec: policyv1alpha1.TelemetrySpec{
-			Selector: map[string]string{"app": "foo"},
-		},
-	}
-
-	otelPolicy := &policyv1alpha1.Telemetry{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: appNamespace,
-			Name:      "t2",
-		},
-		Spec: policyv1alpha1.TelemetrySpec{
-			Selector: map[string]string{"app": "foo"},
-			AccessLog: &policyv1alpha1.EnvoyAccessLogConfig{
-				OpenTelemetry: &policyv1alpha1.EnvoyAccessLogOpenTelemetryConfig{
-					ExtensionService: policyv1alpha1.ExtensionServiceRef{
-						Namespace: "otel-ns",
-						Name:      "otel-collector",
-					},
-				},
-			},
-		},
-	}
-
-	otelExtSvc := &configv1alpha2.ExtensionService{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "otel-ns",
-			Name:      "otel-collector",
-		},
-	}
-
-	testCases := []struct {
-		name              string
-		proxy             *models.Proxy
-		pod               *corev1.Pod
-		telemetryPolicies []runtime.Object
-		extensionServices []runtime.Object
-		expected          models.TelemetryConfig
-	}{
-		{
-			name:  "matches global scope policy",
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
-			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
-				map[string]string{
-					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-					"app":                            "foo",
-				}),
-			telemetryPolicies: []runtime.Object{
-				globalPolicy,
-			},
-			expected: models.TelemetryConfig{
-				Policy: globalPolicy,
-			},
-		},
-		{
-			name:  "matches namespace scope policy",
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
-			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
-				map[string]string{
-					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-					"app":                            "foo",
-				}),
-			telemetryPolicies: []runtime.Object{
-				globalPolicy,
-				namespacePolicy,
-			},
-			expected: models.TelemetryConfig{
-				Policy: namespacePolicy,
-			},
-		},
-		{
-			name:  "matches selector scope policy",
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
-			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
-				map[string]string{
-					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-					"app":                            "foo",
-				}),
-			telemetryPolicies: []runtime.Object{
-				globalPolicy,
-				namespacePolicy,
-				selectorPolicy,
-			},
-			expected: models.TelemetryConfig{
-				Policy: selectorPolicy,
-			},
-		},
-		{
-			name:  "matches policy with OpenTelemetry config",
-			proxy: models.NewProxy(models.KindSidecar, proxyUUID, "sa-1.test", nil, 1),
-			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
-				map[string]string{
-					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-					"app":                            "foo",
-				}),
-			telemetryPolicies: []runtime.Object{
-				otelPolicy,
-			},
-			extensionServices: []runtime.Object{
-				otelExtSvc,
-			},
-			expected: models.TelemetryConfig{
-				Policy:               otelPolicy,
-				OpenTelemetryService: otelExtSvc,
-			},
-		},
-		{
-			name:  "no policy when proxy does not match pod",
-			proxy: models.NewProxy(models.KindSidecar, uuid.New(), "sa-1.test", nil, 1), // new UUID to avoid matching proxyUUID
-			pod: tests.NewPodFixture(appNamespace, "pod-1", "sa-1",
-				map[string]string{
-					constants.EnvoyUniqueIDLabelName: proxyUUID.String(),
-					"app":                            "foo",
-				}),
-			telemetryPolicies: []runtime.Object{
-				globalPolicy,
-				namespacePolicy,
-				selectorPolicy,
-			},
-			expected: models.TelemetryConfig{},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			a := assert.New(t)
-
-			stop := make(chan struct{})
-			broker := messaging.NewBroker(stop)
-
-			c, err := NewClient(osmNamespace, testMeshName, broker,
-				WithKubeClient(fake.NewSimpleClientset(monitoredNS(appNamespace), tc.pod), testMeshName),
-				WithPolicyClient(fakePolicyClient.NewSimpleClientset(tc.telemetryPolicies...)),
-				WithConfigClient(fakeConfigClient.NewSimpleClientset(tc.extensionServices...)))
-			a.NoError(err)
-
-			actual := c.GetTelemetryConfig(tc.proxy)
-			a.Equal(tc.expected, actual)
-		})
-	}
-}
-
 func TestListServiceImports(t *testing.T) {
 	a := assert.New(t)