Repository: Azure/ARO-RP
Pull request #2637 (2023-01-13 21:20:23)

Feature/aro-1608 return hive deployment cr info
### Which issue this PR addresses:

<!--
Please include a link to the ADO work item as well as any GitHub issues.

Usage: `Fixes #<GitHub issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes https://issues.redhat.com/browse/ARO-1608

### What this PR does / why we need it:

Will add an admin endpoint to be wired for a Geneva Action to pull the Cluster Deployment of a hive managed cluster.

### Test plan for issue:

Manually tested using local RP as well as unit test and a development mode e2e test

### Is there any documentation that needs to be updated for this PR?

not yet.


s-amann: /azp run e2e
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>

Patch:
diff --git a/cmd/aro/rp.go b/cmd/aro/rp.go
index 71dd863e3..64fff58cc 100644
--- a/cmd/aro/rp.go
+++ b/cmd/aro/rp.go
@@ -26,6 +26,7 @@ import (
 	"github.com/Azure/ARO-RP/pkg/env"
 	"github.com/Azure/ARO-RP/pkg/frontend"
 	"github.com/Azure/ARO-RP/pkg/frontend/adminactions"
+	"github.com/Azure/ARO-RP/pkg/hive"
 	"github.com/Azure/ARO-RP/pkg/metrics/statsd"
 	"github.com/Azure/ARO-RP/pkg/metrics/statsd/azure"
 	"github.com/Azure/ARO-RP/pkg/metrics/statsd/golang"
@@ -146,8 +147,11 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error {
 	if err != nil {
 		return err
 	}
-
-	f, err := frontend.NewFrontend(ctx, audit, log.WithField("component", "frontend"), _env, dbAsyncOperations, dbClusterManagerConfiguration, dbOpenShiftClusters, dbSubscriptions, dbOpenShiftVersions, api.APIs, m, feAead, adminactions.NewKubeActions, adminactions.NewAzureActions, clusterdata.NewBestEffortEnricher)
+	hiveClusterManager, err := hive.NewFromEnv(ctx, log, _env)
+	if err != nil {
+		return err
+	}
+	f, err := frontend.NewFrontend(ctx, audit, log.WithField("component", "frontend"), _env, dbAsyncOperations, dbClusterManagerConfiguration, dbOpenShiftClusters, dbSubscriptions, dbOpenShiftVersions, api.APIs, m, feAead, hiveClusterManager, adminactions.NewKubeActions, adminactions.NewAzureActions, clusterdata.NewBestEffortEnricher)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/frontend/admin_hive_clusterdeployment_get.go b/pkg/frontend/admin_hive_clusterdeployment_get.go
new file mode 100644
index 000000000..323ae4762
--- /dev/null
+++ b/pkg/frontend/admin_hive_clusterdeployment_get.go
@@ -0,0 +1,60 @@
+package frontend
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+	"net/http"
+	"path/filepath"
+	"strings"
+
+	"github.com/sirupsen/logrus"
+	"github.com/ugorji/go/codec"
+
+	"github.com/Azure/ARO-RP/pkg/api"
+	"github.com/Azure/ARO-RP/pkg/frontend/middleware"
+)
+
+func (f *frontend) getAdminHiveClusterDeployment(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+	log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
+	resourceID := strings.TrimPrefix(filepath.Dir(r.URL.Path), "/admin")
+	b, err := f._getAdminHiveClusterDeployment(ctx, resourceID)
+
+	if cloudErr, ok := err.(*api.CloudError); ok {
+		api.WriteCloudError(w, cloudErr)
+		return
+	}
+
+	adminReply(log, w, nil, b, err)
+}
+
+func (f *frontend) _getAdminHiveClusterDeployment(ctx context.Context, resourceID string) ([]byte, error) {
+	// we have to check if the frontend has a valid clustermanager since hive is not everywhere.
+	if f.hiveClusterManager == nil {
+		return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "hive is not enabled")
+	}
+
+	doc, err := f.dbOpenShiftClusters.Get(ctx, resourceID)
+	if err != nil {
+		return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "", "cluster not found")
+	}
+
+	if doc.OpenShiftCluster.Properties.HiveProfile.Namespace == "" {
+		return nil, api.NewCloudError(http.StatusNoContent, api.CloudErrorCodeResourceNotFound, "", "cluster is not managed by hive")
+	}
+
+	cd, err := f.hiveClusterManager.GetClusterDeployment(ctx, doc)
+	if err != nil {
+		return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "", "cluster deployment not found")
+	}
+
+	var b []byte
+	err = codec.NewEncoderBytes(&b, &codec.JsonHandle{}).Encode(cd)
+	if err != nil {
+		return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "unable to marshal response")
+	}
+
+	return b, nil
+}
diff --git a/pkg/frontend/frontend.go b/pkg/frontend/frontend.go
index 6d0c320ce..8e937fcc5 100644
--- a/pkg/frontend/frontend.go
+++ b/pkg/frontend/frontend.go
@@ -24,6 +24,7 @@ import (
 	"github.com/Azure/ARO-RP/pkg/env"
 	"github.com/Azure/ARO-RP/pkg/frontend/adminactions"
 	"github.com/Azure/ARO-RP/pkg/frontend/middleware"
+	"github.com/Azure/ARO-RP/pkg/hive"
 	"github.com/Azure/ARO-RP/pkg/metrics"
 	"github.com/Azure/ARO-RP/pkg/proxy"
 	"github.com/Azure/ARO-RP/pkg/util/bucket"
@@ -71,6 +72,7 @@ type frontend struct {
 
 	aead encryption.AEAD
 
+	hiveClusterManager  hive.ClusterManager
 	kubeActionsFactory  kubeActionsFactory
 	azureActionsFactory azureActionsFactory
 	ocEnricherFactory   ocEnricherFactory
@@ -115,6 +117,7 @@ func NewFrontend(ctx context.Context,
 	apis map[string]*api.Version,
 	m metrics.Emitter,
 	aead encryption.AEAD,
+	hiveClusterManager hive.ClusterManager,
 	kubeActionsFactory kubeActionsFactory,
 	azureActionsFactory azureActionsFactory,
 	ocEnricherFactory ocEnricherFactory) (*frontend, error) {
@@ -145,6 +148,7 @@ func NewFrontend(ctx context.Context,
 		apis:                          apis,
 		m:                             middleware.MetricsMiddleware{Emitter: m},
 		aead:                          aead,
+		hiveClusterManager:            hiveClusterManager,
 		kubeActionsFactory:            kubeActionsFactory,
 		azureActionsFactory:           azureActionsFactory,
 		ocEnricherFactory:             ocEnricherFactory,
@@ -401,6 +405,12 @@ func (f *frontend) authenticatedRoutes(r *mux.Router) {
 
 	s.Methods(http.MethodPost).HandlerFunc(f.postAdminOpenShiftClusterDrainNode).Name("postAdminOpenShiftClusterDrainNode")
 
+	s = r.
+		Path("/admin/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}/clusterdeployment").
+		Subrouter()
+
+	s.Methods(http.MethodGet).HandlerFunc(f.getAdminHiveClusterDeployment).Name("getAdminHiveClusterDeployment")
+
 	s = r.
 		Path("/admin/versions").
 		Subrouter()
diff --git a/pkg/hive/manager.go b/pkg/hive/manager.go
index 3c88440c0..726538ce4 100644
--- a/pkg/hive/manager.go
+++ b/pkg/hive/manager.go
@@ -38,6 +38,7 @@ type ClusterManager interface {
 	Install(ctx context.Context, sub *api.SubscriptionDocument, doc *api.OpenShiftClusterDocument, version *api.OpenShiftVersion) error
 	IsClusterDeploymentReady(ctx context.Context, doc *api.OpenShiftClusterDocument) (bool, error)
 	IsClusterInstallationComplete(ctx context.Context, doc *api.OpenShiftClusterDocument) (bool, error)
+	GetClusterDeployment(ctx context.Context, doc *api.OpenShiftClusterDocument) (*hivev1.ClusterDeployment, error)
 	ResetCorrelationData(ctx context.Context, doc *api.OpenShiftClusterDocument) error
 }
 
@@ -51,6 +52,31 @@ type clusterManager struct {
 	dh dynamichelper.Interface
 }
 
+// NewFromEnv can return a nil ClusterManager when hive features are disabled. This exists to support regions where we don't have hive,
+// and we do not want to restrict the frontend from starting up successfully.
+// It has the caveat of requiring a nil check on any operations performed with the returned ClusterManager
+// until this conditional return is removed (we have hive everywhere).
+func NewFromEnv(ctx context.Context, log *logrus.Entry, env env.Interface) (ClusterManager, error) {
+	adoptByHive, err := env.LiveConfig().AdoptByHive(ctx)
+	if err != nil {
+		return nil, err
+	}
+	installViaHive, err := env.LiveConfig().InstallViaHive(ctx)
+	if err != nil {
+		return nil, err
+	}
+	if !adoptByHive && !installViaHive {
+		log.Infof("hive is disabled, skipping creation of ClusterManager")
+		return nil, nil
+	}
+	hiveShard := 1
+	hiveRestConfig, err := env.LiveConfig().HiveRestConfig(ctx, hiveShard)
+	if err != nil {
+		return nil, fmt.Errorf("failed getting RESTConfig for Hive shard %d: %w", hiveShard, err)
+	}
+	return NewFromConfig(log, env, hiveRestConfig)
+}
+
 // NewFromConfig creates a ClusterManager.
 // It MUST NOT take cluster or subscription document as values
 // in these structs can be change during the lifetime of the cluster manager.
@@ -132,7 +158,7 @@ func (hr *clusterManager) Delete(ctx context.Context, doc *api.OpenShiftClusterD
 }
 
 func (hr *clusterManager) IsClusterDeploymentReady(ctx context.Context, doc *api.OpenShiftClusterDocument) (bool, error) {
-	cd, err := hr.hiveClientset.HiveV1().ClusterDeployments(doc.OpenShiftCluster.Properties.HiveProfile.Namespace).Get(ctx, ClusterDeploymentName, metav1.GetOptions{})
+	cd, err := hr.GetClusterDeployment(ctx, doc)
 	if err != nil {
 		return false, err
 	}
@@ -183,6 +209,10 @@ func (hr *clusterManager) IsClusterInstallationComplete(ctx context.Context, doc
 	return false, nil
 }
 
+func (hr *clusterManager) GetClusterDeployment(ctx context.Context, doc *api.OpenShiftClusterDocument) (*hivev1.ClusterDeployment, error) {
+	return hr.hiveClientset.HiveV1().ClusterDeployments(doc.OpenShiftCluster.Properties.HiveProfile.Namespace).Get(ctx, ClusterDeploymentName, metav1.GetOptions{})
+}
+
 func (hr *clusterManager) ResetCorrelationData(ctx context.Context, doc *api.OpenShiftClusterDocument) error {
 	return retry.RetryOnConflict(retry.DefaultRetry, func() error {
 		cd, err := hr.hiveClientset.HiveV1().ClusterDeployments(doc.OpenShiftCluster.Properties.HiveProfile.Namespace).Get(ctx, ClusterDeploymentName, metav1.GetOptions{})
diff --git a/pkg/util/mocks/hive/hive.go b/pkg/util/mocks/hive/hive.go
index e34891b7d..a5a9408bb 100644
--- a/pkg/util/mocks/hive/hive.go
+++ b/pkg/util/mocks/hive/hive.go
@@ -9,7 +9,8 @@ import (
 	reflect "reflect"
 
 	gomock "github.com/golang/mock/gomock"
-	v1 "k8s.io/api/core/v1"
+	v1 "github.com/openshift/hive/apis/hive/v1"
+	v10 "k8s.io/api/core/v1"
 
 	api "github.com/Azure/ARO-RP/pkg/api"
 )
@@ -38,10 +39,10 @@ func (m *MockClusterManager) EXPECT() *MockClusterManagerMockRecorder {
 }
 
 // CreateNamespace mocks base method.
-func (m *MockClusterManager) CreateNamespace(arg0 context.Context) (*v1.Namespace, error) {
+func (m *MockClusterManager) CreateNamespace(arg0 context.Context) (*v10.Namespace, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "CreateNamespace", arg0)
-	ret0, _ := ret[0].(*v1.Namespace)
+	ret0, _ := ret[0].(*v10.Namespace)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -80,6 +81,21 @@ func (mr *MockClusterManagerMockRecorder) Delete(arg0, arg1 interface{}) *gomock
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClusterManager)(nil).Delete), arg0, arg1)
 }
 
+// GetClusterDeployment mocks base method.
+func (m *MockClusterManager) GetClusterDeployment(arg0 context.Context, arg1 *api.OpenShiftClusterDocument) (*v1.ClusterDeployment, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetClusterDeployment", arg0, arg1)
+	ret0, _ := ret[0].(*v1.ClusterDeployment)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetClusterDeployment indicates an expected call of GetClusterDeployment.
+func (mr *MockClusterManagerMockRecorder) GetClusterDeployment(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterDeployment", reflect.TypeOf((*MockClusterManager)(nil).GetClusterDeployment), arg0, arg1)
+}
+
 // Install mocks base method.
 func (m *MockClusterManager) Install(arg0 context.Context, arg1 *api.SubscriptionDocument, arg2 *api.OpenShiftClusterDocument, arg3 *api.OpenShiftVersion) error {
 	m.ctrl.T.Helper()


Test patch:
diff --git a/pkg/frontend/admin_hive_clusterdeployment_get_test.go b/pkg/frontend/admin_hive_clusterdeployment_get_test.go
new file mode 100644
index 000000000..f991be8ed
--- /dev/null
+++ b/pkg/frontend/admin_hive_clusterdeployment_get_test.go
@@ -0,0 +1,118 @@
+package frontend
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	hivev1 "github.com/openshift/hive/apis/hive/v1"
+
+	"github.com/Azure/ARO-RP/pkg/api"
+	"github.com/Azure/ARO-RP/pkg/metrics/noop"
+	mock_env "github.com/Azure/ARO-RP/pkg/util/mocks/env"
+	mock_hive "github.com/Azure/ARO-RP/pkg/util/mocks/hive"
+)
+
+func Test_getAdminHiveClusterDeployment(t *testing.T) {
+	fakeUUID := "00000000-0000-0000-0000-000000000000"
+	ctx := context.Background()
+	clusterDeployment := hivev1.ClusterDeployment{
+		Spec: hivev1.ClusterDeploymentSpec{
+			ClusterName: "abc123",
+		},
+	}
+	type test struct {
+		name                                  string
+		resourceID                            string
+		properties                            api.OpenShiftClusterProperties
+		hiveEnabled                           bool
+		expectedGetClusterDeploymentCallCount int
+		wantStatusCode                        int
+		wantResponse                          []byte
+		wantError                             string
+	}
+
+	for _, tt := range []*test{
+		{
+			name:                                  "cluster has hive profile with namespace",
+			resourceID:                            fmt.Sprintf("/subscriptions/%s/resourcegroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/hive", fakeUUID),
+			properties:                            api.OpenShiftClusterProperties{HiveProfile: api.HiveProfile{Namespace: fmt.Sprintf("aro-%s", fakeUUID)}},
+			hiveEnabled:                           true,
+			expectedGetClusterDeploymentCallCount: 1,
+			wantResponse:                          []byte(`{"spec":{"clusterName":"abc123","baseDomain":"","platform":{},"installed":false}}`),
+		},
+		{
+			name:                                  "cluster does not have hive profile with namespace",
+			resourceID:                            fmt.Sprintf("/subscriptions/%s/resourcegroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/nonHive", fakeUUID),
+			hiveEnabled:                           true,
+			expectedGetClusterDeploymentCallCount: 0,
+			wantStatusCode:                        http.StatusNoContent,
+			wantError:                             "204: ResourceNotFound: : cluster is not managed by hive",
+		},
+		{
+			name:                                  "hive is not enabled",
+			resourceID:                            fmt.Sprintf("/subscriptions/%s/resourcegroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/nonHive", fakeUUID),
+			hiveEnabled:                           false,
+			expectedGetClusterDeploymentCallCount: 0,
+			wantStatusCode:                        http.StatusInternalServerError,
+			wantError:                             "500: InternalServerError: : hive is not enabled",
+		},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			ti := newTestInfra(t).WithOpenShiftClusters().WithSubscriptions()
+			controller := gomock.NewController(t)
+			defer ti.done()
+			defer controller.Finish()
+
+			ti.fixture.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
+				Key: strings.ToLower(tt.resourceID),
+				OpenShiftCluster: &api.OpenShiftCluster{
+					ID:         tt.resourceID,
+					Name:       "hive",
+					Type:       "Microsoft.RedHatOpenShift/openshiftClusters",
+					Properties: tt.properties,
+				},
+			})
+
+			err := ti.buildFixtures(nil)
+			if err != nil {
+				t.Fatal(err)
+			}
+			_env := ti.env.(*mock_env.MockInterface)
+			var f *frontend
+			if tt.hiveEnabled {
+				clusterManager := mock_hive.NewMockClusterManager(controller)
+				clusterManager.EXPECT().GetClusterDeployment(gomock.Any(), gomock.Any()).Return(&clusterDeployment, nil).Times(tt.expectedGetClusterDeploymentCallCount)
+				f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase,
+					ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, clusterManager, nil, nil, nil)
+			} else {
+				f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase,
+					ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
+			}
+
+			if err != nil {
+				t.Fatal(err)
+			}
+			hiveClusterDeployment, err := f._getAdminHiveClusterDeployment(ctx, strings.ToLower(tt.resourceID))
+			cloudErr, isCloudErr := err.(*api.CloudError)
+			if tt.wantError != "" && isCloudErr && cloudErr != nil {
+				if tt.wantError != cloudErr.Error() {
+					t.Fatalf("got %q but wanted %q", cloudErr.Error(), tt.wantError)
+				}
+				if tt.wantStatusCode != 0 && tt.wantStatusCode != cloudErr.StatusCode {
+					t.Fatalf("got %q but wanted %q", cloudErr.Error(), tt.wantError)
+				}
+			}
+
+			if !strings.EqualFold(string(hiveClusterDeployment), string(tt.wantResponse)) {
+				t.Fatalf("got %q and expected %q", hiveClusterDeployment, tt.wantResponse)
+			}
+		})
+	}
+}
diff --git a/pkg/frontend/admin_openshiftcluster_approvecsr_test.go b/pkg/frontend/admin_openshiftcluster_approvecsr_test.go
index 5de793122..851ad1490 100644
--- a/pkg/frontend/admin_openshiftcluster_approvecsr_test.go
+++ b/pkg/frontend/admin_openshiftcluster_approvecsr_test.go
@@ -91,7 +91,7 @@ func TestAdminApproveCSR(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_cordonnode_test.go b/pkg/frontend/admin_openshiftcluster_cordonnode_test.go
index eb75108d0..91cee0414 100644
--- a/pkg/frontend/admin_openshiftcluster_cordonnode_test.go
+++ b/pkg/frontend/admin_openshiftcluster_cordonnode_test.go
@@ -152,7 +152,7 @@ func TestAdminCordonUncordonNode(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_drainnode_test.go b/pkg/frontend/admin_openshiftcluster_drainnode_test.go
index 7388d9f1e..5c2506747 100644
--- a/pkg/frontend/admin_openshiftcluster_drainnode_test.go
+++ b/pkg/frontend/admin_openshiftcluster_drainnode_test.go
@@ -84,7 +84,7 @@ func TestAdminDrainNode(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_kubernetesobjects_test.go b/pkg/frontend/admin_openshiftcluster_kubernetesobjects_test.go
index aa2d19ea9..5d104a482 100644
--- a/pkg/frontend/admin_openshiftcluster_kubernetesobjects_test.go
+++ b/pkg/frontend/admin_openshiftcluster_kubernetesobjects_test.go
@@ -210,7 +210,7 @@ func TestAdminKubernetesObjectsGetAndDelete(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 			if err != nil {
@@ -323,7 +323,7 @@ func TestAdminPostKubernetesObjects(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 			if err != nil {
diff --git a/pkg/frontend/admin_openshiftcluster_kubernetespods_logs_test.go b/pkg/frontend/admin_openshiftcluster_kubernetespods_logs_test.go
index ff92d4134..3989ebff8 100644
--- a/pkg/frontend/admin_openshiftcluster_kubernetespods_logs_test.go
+++ b/pkg/frontend/admin_openshiftcluster_kubernetespods_logs_test.go
@@ -125,7 +125,7 @@ func TestAdminKubernetesGetPodLogs(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 				return k, nil
 			}, nil, nil)
 			if err != nil {
diff --git a/pkg/frontend/admin_openshiftcluster_list_test.go b/pkg/frontend/admin_openshiftcluster_list_test.go
index c49f2bf6e..7a25c80c5 100644
--- a/pkg/frontend/admin_openshiftcluster_list_test.go
+++ b/pkg/frontend/admin_openshiftcluster_list_test.go
@@ -123,7 +123,7 @@ func TestAdminListOpenShiftCluster(t *testing.T) {
 				ti.openShiftClustersClient.SetError(tt.throwsError)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, aead, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, aead, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 				return ti.enricher
 			})
 			if err != nil {
diff --git a/pkg/frontend/admin_openshiftcluster_redeployvm_test.go b/pkg/frontend/admin_openshiftcluster_redeployvm_test.go
index dc3e2e457..6620636e6 100644
--- a/pkg/frontend/admin_openshiftcluster_redeployvm_test.go
+++ b/pkg/frontend/admin_openshiftcluster_redeployvm_test.go
@@ -84,7 +84,7 @@ func TestAdminRedeployVM(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_resources_list_test.go b/pkg/frontend/admin_openshiftcluster_resources_list_test.go
index de920e70e..d9bc7464c 100644
--- a/pkg/frontend/admin_openshiftcluster_resources_list_test.go
+++ b/pkg/frontend/admin_openshiftcluster_resources_list_test.go
@@ -93,7 +93,7 @@ func TestAdminListResourcesList(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 			mockResponder := mock_frontend.NewMockStreamResponder(ti.controller)
diff --git a/pkg/frontend/admin_openshiftcluster_startvm_test.go b/pkg/frontend/admin_openshiftcluster_startvm_test.go
index 80f358adc..d60133bba 100644
--- a/pkg/frontend/admin_openshiftcluster_startvm_test.go
+++ b/pkg/frontend/admin_openshiftcluster_startvm_test.go
@@ -84,7 +84,7 @@ func TestAdminStartVM(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_stopvm_test.go b/pkg/frontend/admin_openshiftcluster_stopvm_test.go
index e50a1abe7..146af5206 100644
--- a/pkg/frontend/admin_openshiftcluster_stopvm_test.go
+++ b/pkg/frontend/admin_openshiftcluster_stopvm_test.go
@@ -86,7 +86,7 @@ func TestAdminStopVM(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 
diff --git a/pkg/frontend/admin_openshiftcluster_vmresize_test.go b/pkg/frontend/admin_openshiftcluster_vmresize_test.go
index 5bc3c5b29..4aa9b5977 100644
--- a/pkg/frontend/admin_openshiftcluster_vmresize_test.go
+++ b/pkg/frontend/admin_openshiftcluster_vmresize_test.go
@@ -211,7 +211,7 @@ func TestAdminVMResize(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil,
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil,
 				func(e *logrus.Entry, i env.Interface, osc *api.OpenShiftCluster) (adminactions.KubeActions, error) {
 					return k, nil
 				}, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
diff --git a/pkg/frontend/admin_openshiftcluster_vmsizelist_test.go b/pkg/frontend/admin_openshiftcluster_vmsizelist_test.go
index 409d1d97d..c37f081b7 100644
--- a/pkg/frontend/admin_openshiftcluster_vmsizelist_test.go
+++ b/pkg/frontend/admin_openshiftcluster_vmsizelist_test.go
@@ -136,7 +136,7 @@ func TestAdminListVMSizeList(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 
diff --git a/pkg/frontend/admin_openshiftversion_list_test.go b/pkg/frontend/admin_openshiftversion_list_test.go
index ec6f65b3d..308d2d95a 100644
--- a/pkg/frontend/admin_openshiftversion_list_test.go
+++ b/pkg/frontend/admin_openshiftversion_list_test.go
@@ -110,7 +110,7 @@ func TestOpenShiftVersionList(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 
 			if err != nil {
 				t.Fatal(err)
diff --git a/pkg/frontend/admin_openshiftversion_put_test.go b/pkg/frontend/admin_openshiftversion_put_test.go
index 9c547c7c3..c0883d8fa 100644
--- a/pkg/frontend/admin_openshiftversion_put_test.go
+++ b/pkg/frontend/admin_openshiftversion_put_test.go
@@ -265,7 +265,7 @@ func TestOpenShiftVersionPut(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/asyncoperationresult_get_test.go b/pkg/frontend/asyncoperationresult_get_test.go
index 71d2a32c2..155b85a38 100644
--- a/pkg/frontend/asyncoperationresult_get_test.go
+++ b/pkg/frontend/asyncoperationresult_get_test.go
@@ -133,7 +133,7 @@ func TestGetAsyncOperationResult(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/asyncoperationsstatus_get_test.go b/pkg/frontend/asyncoperationsstatus_get_test.go
index afb1c82f2..b0eb972fe 100644
--- a/pkg/frontend/asyncoperationsstatus_get_test.go
+++ b/pkg/frontend/asyncoperationsstatus_get_test.go
@@ -183,7 +183,7 @@ func TestGetAsyncOperationsStatus(t *testing.T) {
 				ti.asyncOperationsClient.SetError(tt.dbError)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/clustermanager_delete_test.go b/pkg/frontend/clustermanager_delete_test.go
index 054f91ec7..e62f38f8a 100644
--- a/pkg/frontend/clustermanager_delete_test.go
+++ b/pkg/frontend/clustermanager_delete_test.go
@@ -120,7 +120,7 @@ func TestDeleteClusterManagerConfiguration(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/clustermanager_get_test.go b/pkg/frontend/clustermanager_get_test.go
index c319cd824..f549e3342 100644
--- a/pkg/frontend/clustermanager_get_test.go
+++ b/pkg/frontend/clustermanager_get_test.go
@@ -123,7 +123,7 @@ func TestGetClusterManagerConfiguration(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, nil, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, nil, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/clustermanager_putorpatch_test.go b/pkg/frontend/clustermanager_putorpatch_test.go
index 2c9aada47..e8d710c4f 100644
--- a/pkg/frontend/clustermanager_putorpatch_test.go
+++ b/pkg/frontend/clustermanager_putorpatch_test.go
@@ -201,7 +201,7 @@ func TestPutOrPatchClusterManagerConfiguration(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/openshiftcluster_applensdetectors_test.go b/pkg/frontend/openshiftcluster_applensdetectors_test.go
index 31046a885..ec50862c0 100644
--- a/pkg/frontend/openshiftcluster_applensdetectors_test.go
+++ b/pkg/frontend/openshiftcluster_applensdetectors_test.go
@@ -108,7 +108,7 @@ func TestAppLensDetectors(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(*logrus.Entry, env.Interface, *api.OpenShiftCluster, *api.SubscriptionDocument) (adminactions.AzureActions, error) {
 				return a, nil
 			}, nil)
 
diff --git a/pkg/frontend/openshiftcluster_delete_test.go b/pkg/frontend/openshiftcluster_delete_test.go
index 9e884f324..65c384cdb 100644
--- a/pkg/frontend/openshiftcluster_delete_test.go
+++ b/pkg/frontend/openshiftcluster_delete_test.go
@@ -114,7 +114,7 @@ func TestDeleteOpenShiftCluster(t *testing.T) {
 				ti.subscriptionsClient.SetError(tt.dbError)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/openshiftcluster_get_test.go b/pkg/frontend/openshiftcluster_get_test.go
index 821361203..528dc11e0 100644
--- a/pkg/frontend/openshiftcluster_get_test.go
+++ b/pkg/frontend/openshiftcluster_get_test.go
@@ -97,7 +97,7 @@ func TestGetOpenShiftCluster(t *testing.T) {
 				ti.openShiftClustersClient.SetError(tt.dbError)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 				return ti.enricher
 			})
 			if err != nil {
diff --git a/pkg/frontend/openshiftcluster_list_test.go b/pkg/frontend/openshiftcluster_list_test.go
index 282833992..323b3261d 100644
--- a/pkg/frontend/openshiftcluster_list_test.go
+++ b/pkg/frontend/openshiftcluster_list_test.go
@@ -197,7 +197,7 @@ func TestListOpenShiftCluster(t *testing.T) {
 
 					aead := testdatabase.NewFakeAEAD()
 
-					f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, aead, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+					f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, aead, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 						return ti.enricher
 					})
 					if err != nil {
diff --git a/pkg/frontend/openshiftcluster_putorpatch_test.go b/pkg/frontend/openshiftcluster_putorpatch_test.go
index f35241f66..25d159dc9 100644
--- a/pkg/frontend/openshiftcluster_putorpatch_test.go
+++ b/pkg/frontend/openshiftcluster_putorpatch_test.go
@@ -581,7 +581,7 @@ func TestPutOrPatchOpenShiftClusterAdminAPI(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 				return ti.enricher
 			})
 			if err != nil {
@@ -1469,7 +1469,7 @@ func TestPutOrPatchOpenShiftCluster(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, ti.openShiftVersionsDatabase, apis, &noop.Noop{}, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, ti.openShiftVersionsDatabase, apis, &noop.Noop{}, nil, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 				return ti.enricher
 			})
 			if err != nil {
@@ -1774,7 +1774,7 @@ func TestPutOrPatchOpenShiftClusterValidated(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil, func(log *logrus.Entry, dialer proxy.Dialer, m metrics.Emitter) clusterdata.OpenShiftClusterEnricher {
 				return ti.enricher
 			})
 			if err != nil {
diff --git a/pkg/frontend/openshiftclustercredentials_post_test.go b/pkg/frontend/openshiftclustercredentials_post_test.go
index 74ae504c9..a0174aa71 100644
--- a/pkg/frontend/openshiftclustercredentials_post_test.go
+++ b/pkg/frontend/openshiftclustercredentials_post_test.go
@@ -267,7 +267,7 @@ func TestPostOpenShiftClusterCredentials(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/openshiftclusterkubeconfigcredentials_post_test.go b/pkg/frontend/openshiftclusterkubeconfigcredentials_post_test.go
index bd256d32d..f6bdf1259 100644
--- a/pkg/frontend/openshiftclusterkubeconfigcredentials_post_test.go
+++ b/pkg/frontend/openshiftclusterkubeconfigcredentials_post_test.go
@@ -241,7 +241,7 @@ func TestPostOpenShiftClusterKubeConfigCredentials(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, apis, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/openshiftversions_list_test.go b/pkg/frontend/openshiftversions_list_test.go
index 3e2d77796..410de56fa 100644
--- a/pkg/frontend/openshiftversions_list_test.go
+++ b/pkg/frontend/openshiftversions_list_test.go
@@ -89,7 +89,7 @@ func TestListInstallVersions(t *testing.T) {
 			ti := newTestInfra(t).WithSubscriptions().WithOpenShiftVersions()
 			defer ti.done()
 
-			frontend, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			frontend, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, nil, nil, nil, ti.openShiftVersionsDatabase, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/frontend/security_test.go b/pkg/frontend/security_test.go
index 512b31389..2a8cf32ce 100644
--- a/pkg/frontend/security_test.go
+++ b/pkg/frontend/security_test.go
@@ -77,7 +77,7 @@ func TestSecurity(t *testing.T) {
 
 	log := logrus.NewEntry(logrus.StandardLogger())
 	auditHook, auditEntry := testlog.NewAudit()
-	f, err := NewFrontend(ctx, auditEntry, log, _env, nil, nil, nil, nil, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+	f, err := NewFrontend(ctx, auditEntry, log, _env, nil, nil, nil, nil, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/pkg/frontend/subscriptions_put_test.go b/pkg/frontend/subscriptions_put_test.go
index 7257bf4fc..b6c335264 100644
--- a/pkg/frontend/subscriptions_put_test.go
+++ b/pkg/frontend/subscriptions_put_test.go
@@ -244,7 +244,7 @@ func TestPutSubscription(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil)
+			f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.asyncOperationsDatabase, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, api.APIs, &noop.Noop{}, nil, nil, nil, nil, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/pkg/hive/manager_test.go b/pkg/hive/manager_test.go
index 01e93757a..3ac414914 100644
--- a/pkg/hive/manager_test.go
+++ b/pkg/hive/manager_test.go
@@ -399,3 +399,52 @@ func TestCreateNamespace(t *testing.T) {
 		})
 	}
 }
+
+func TestGetClusterDeployment(t *testing.T) {
+	fakeNamespace := "fake-namespace"
+	doc := &api.OpenShiftClusterDocument{
+		OpenShiftCluster: &api.OpenShiftCluster{
+			Properties: api.OpenShiftClusterProperties{
+				HiveProfile: api.HiveProfile{
+					Namespace: fakeNamespace,
+				},
+			},
+		},
+	}
+
+	cd := &hivev1.ClusterDeployment{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      ClusterDeploymentName,
+			Namespace: fakeNamespace,
+		},
+	}
+
+	for _, tt := range []struct {
+		name    string
+		wantErr string
+	}{
+		{name: "cd exists and is returned"},
+		{name: "cd does not exist err returned", wantErr: `clusterdeployments.hive.openshift.io "cluster" not found`},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			fakeClientset := hivefake.NewSimpleClientset()
+			if tt.wantErr == "" {
+				_ = fakeClientset.Tracker().Add(cd)
+			}
+			c := clusterManager{
+				hiveClientset: fakeClientset,
+				log:           logrus.NewEntry(logrus.StandardLogger()),
+			}
+
+			result, err := c.GetClusterDeployment(context.Background(), doc)
+			if err != nil && err.Error() != tt.wantErr ||
+				err == nil && tt.wantErr != "" {
+				t.Fatal(err)
+			}
+
+			if result != nil && result.Name != cd.Name && result.Namespace != cd.Namespace {
+				t.Fatal("Unexpected cluster deployment returned", result)
+			}
+		})
+	}
+}
diff --git a/test/e2e/adminapi_hiveclusterdeployment.go b/test/e2e/adminapi_hiveclusterdeployment.go
new file mode 100644
index 000000000..bd91c25e7
--- /dev/null
+++ b/test/e2e/adminapi_hiveclusterdeployment.go
@@ -0,0 +1,39 @@
+package e2e
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+	"net/http"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	hivev1 "github.com/openshift/hive/apis/hive/v1"
+)
+
+var _ = Describe("[Admin API] Get Hive Cluster Deployment action", func() {
+	BeforeEach(skipIfNotInDevelopmentEnv)
+
+	When("A hive managed cluster has its cluster deployment requested", func() {
+		It("is managed by hive", func(ctx context.Context) {
+			By("requesting the cluster document via RP admin API")
+			oc := adminGetCluster(Default, ctx, clusterResourceID)
+			By("checking that we received the expected cluster")
+			Expect(oc.ID).To(Equal(clusterResourceID))
+			By("checking the cluster is managed by hive via its hiveProfile")
+			if oc.Properties.HiveProfile.Namespace == "" {
+				Skip("Cluster is not managed by hive")
+				// NOTE: if we end up with e2e creating clusters via hive we should fail here instead
+			}
+			clusterDeployment := hivev1.ClusterDeployment{}
+			By("requesting the cluster deployment cr")
+			resp, err := adminRequest(ctx, http.MethodGet, "/admin"+clusterResourceID+"/clusterdeployment", nil, true, nil, &clusterDeployment)
+			Expect(err).NotTo(HaveOccurred())
+			Expect(resp.StatusCode).To(Equal(http.StatusOK))
+			Expect(resp.Header.Get("Content-Type")).To(Equal("application/json"))
+			Expect(clusterDeployment.Spec.ClusterName).To(Equal(clusterName))
+		})
+	})
+})