Repository: Azure/ARO-RP
Pull request #1233 (2020-12-17 11:52:22)

Add feature gate to operator
### Which issue this PR addresses:

Fixes cases where we enabled Prometheus in production and it is broken due to storage being not available

### What this PR does / why we need it:

Introduces feature flag into the operator to enable/disable features

### Test plan for issue:

unit

### Is there any documentation that needs to be updated for this PR?

Will need to be done after



Patch:
diff --git a/cmd/aro/operator.go b/cmd/aro/operator.go
index dd7f1c13c..78eba80d2 100644
--- a/cmd/aro/operator.go
+++ b/cmd/aro/operator.go
@@ -19,7 +19,7 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 
 	pkgoperator "github.com/Azure/ARO-RP/pkg/operator"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers/alertwebhook"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers/checker"
@@ -98,7 +98,7 @@ func operator(ctx context.Context, log *logrus.Entry) error {
 		}
 		if err = (pullsecret.NewReconciler(
 			log.WithField("controller", controllers.PullSecretControllerName),
-			kubernetescli, arocli)).SetupWithManager(mgr); err != nil {
+			kubernetescli)).SetupWithManager(mgr); err != nil {
 			return fmt.Errorf("unable to create controller PullSecret: %v", err)
 		}
 		if err = (alertwebhook.NewReconciler(
@@ -118,7 +118,7 @@ func operator(ctx context.Context, log *logrus.Entry) error {
 		}
 		if err = (monitoring.NewReconciler(
 			log.WithField("controller", controllers.MonitoringControllerName),
-			kubernetescli)).SetupWithManager(mgr); err != nil {
+			kubernetescli, arocli)).SetupWithManager(mgr); err != nil {
 			return fmt.Errorf("unable to create controller Monitoring: %v", err)
 		}
 		if err = (rbac.NewReconciler(
diff --git a/pkg/metrics/statsd/statsd.go b/pkg/metrics/statsd/statsd.go
index 924544941..7bc871e7b 100644
--- a/pkg/metrics/statsd/statsd.go
+++ b/pkg/metrics/statsd/statsd.go
@@ -8,6 +8,7 @@ import (
 	"context"
 	"fmt"
 	"net"
+	"os"
 	"time"
 
 	"github.com/sirupsen/logrus"
@@ -22,6 +23,7 @@ type statsd struct {
 	log *logrus.Entry
 	env env.Core
 
+	hostname  string
 	account   string
 	namespace string
 
@@ -45,6 +47,12 @@ func New(ctx context.Context, log *logrus.Entry, env env.Core, account, namespac
 		now: time.Now,
 	}
 
+	hostname, err := os.Hostname()
+	if err != nil {
+		return nil, err
+	}
+	s.hostname = hostname
+
 	if s.account == "" {
 		s.account = "*"
 	}
@@ -83,7 +91,7 @@ func (s *statsd) emitMetric(m *metric) {
 		m.dims = map[string]string{}
 	}
 	m.dims["location"] = s.env.Location()
-	m.dims["hostname"] = s.env.Hostname()
+	m.dims["hostname"] = s.hostname
 	m.ts = s.now()
 
 	s.ch <- m
diff --git a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
index 168ea1cee..f75d491c1 100644
--- a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
+++ b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
@@ -39,6 +39,13 @@ type ClusterSpec struct {
 	Location        string              `json:"location,omitempty"`
 	GenevaLogging   GenevaLoggingSpec   `json:"genevaLogging,omitempty"`
 	InternetChecker InternetCheckerSpec `json:"internetChecker,omitempty"`
+
+	Features FeaturesSpec `json:"features,omitempty"`
+}
+
+// FeaturesSpec defines ARO operator feature gates
+type FeaturesSpec struct {
+	PersistentPrometheus bool `json:"persistentPrometheus,omitempty"`
 }
 
 // ClusterStatus defines the observed state of Cluster
diff --git a/pkg/operator/apis/aro.openshift.io/v1alpha1/zz_generated.deepcopy.go b/pkg/operator/apis/aro.openshift.io/v1alpha1/zz_generated.deepcopy.go
index 5f03f5772..0b0f34262 100644
--- a/pkg/operator/apis/aro.openshift.io/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/operator/apis/aro.openshift.io/v1alpha1/zz_generated.deepcopy.go
@@ -73,6 +73,7 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) {
 	*out = *in
 	out.GenevaLogging = in.GenevaLogging
 	in.InternetChecker.DeepCopyInto(&out.InternetChecker)
+	out.Features = in.Features
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSpec.
@@ -107,6 +108,21 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *FeaturesSpec) DeepCopyInto(out *FeaturesSpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeaturesSpec.
+func (in *FeaturesSpec) DeepCopy() *FeaturesSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(FeaturesSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GenevaLoggingSpec) DeepCopyInto(out *GenevaLoggingSpec) {
 	*out = *in
diff --git a/pkg/operator/controllers/checker/checker_controller.go b/pkg/operator/controllers/checker/checker_controller.go
index 3e89ce20e..efcb498b6 100644
--- a/pkg/operator/controllers/checker/checker_controller.go
+++ b/pkg/operator/controllers/checker/checker_controller.go
@@ -17,7 +17,7 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/operator"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/deployment"
 )
@@ -29,7 +29,7 @@ type CheckerController struct {
 	checkers []Checker
 }
 
-func NewReconciler(log *logrus.Entry, maocli maoclient.Interface, arocli aroclient.AroV1alpha1Interface, role string, deploymentMode deployment.Mode) *CheckerController {
+func NewReconciler(log *logrus.Entry, maocli maoclient.Interface, arocli aroclient.Interface, role string, deploymentMode deployment.Mode) *CheckerController {
 	checkers := []Checker{NewInternetChecker(log, arocli, role)}
 
 	if role == operator.RoleMaster {
diff --git a/pkg/operator/controllers/checker/internetchecker.go b/pkg/operator/controllers/checker/internetchecker.go
index 07b9ea3d1..94197c277 100644
--- a/pkg/operator/controllers/checker/internetchecker.go
+++ b/pkg/operator/controllers/checker/internetchecker.go
@@ -17,18 +17,18 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/operator"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 )
 
 // InternetChecker reconciles a Cluster object
 type InternetChecker struct {
-	arocli aroclient.AroV1alpha1Interface
+	arocli aroclient.Interface
 	log    *logrus.Entry
 	role   string
 }
 
-func NewInternetChecker(log *logrus.Entry, arocli aroclient.AroV1alpha1Interface, role string) *InternetChecker {
+func NewInternetChecker(log *logrus.Entry, arocli aroclient.Interface, role string) *InternetChecker {
 	return &InternetChecker{
 		arocli: arocli,
 		log:    log,
@@ -70,7 +70,7 @@ func (r *InternetChecker) Check(ctx context.Context) error {
 		},
 	}
 
-	instance, err := r.arocli.Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
+	instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
 	if err != nil {
 		return err
 	}
diff --git a/pkg/operator/controllers/checker/machinechecker.go b/pkg/operator/controllers/checker/machinechecker.go
index 595357e00..39d5a619a 100644
--- a/pkg/operator/controllers/checker/machinechecker.go
+++ b/pkg/operator/controllers/checker/machinechecker.go
@@ -20,7 +20,7 @@ import (
 	"github.com/Azure/ARO-RP/pkg/api"
 	"github.com/Azure/ARO-RP/pkg/api/validate"
 	aro "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/deployment"
 	_ "github.com/Azure/ARO-RP/pkg/util/scheme"
@@ -33,13 +33,13 @@ const (
 // MachineChecker reconciles the alertmanager webhook
 type MachineChecker struct {
 	clustercli     maoclient.Interface
-	arocli         aroclient.AroV1alpha1Interface
+	arocli         aroclient.Interface
 	log            *logrus.Entry
 	deploymentMode deployment.Mode
 	role           string
 }
 
-func NewMachineChecker(log *logrus.Entry, clustercli maoclient.Interface, arocli aroclient.AroV1alpha1Interface, role string, deploymentMode deployment.Mode) *MachineChecker {
+func NewMachineChecker(log *logrus.Entry, clustercli maoclient.Interface, arocli aroclient.Interface, role string, deploymentMode deployment.Mode) *MachineChecker {
 	return &MachineChecker{
 		clustercli:     clustercli,
 		arocli:         arocli,
diff --git a/pkg/operator/controllers/condition.go b/pkg/operator/controllers/condition.go
index 8700d2d14..2eb3e7b83 100644
--- a/pkg/operator/controllers/condition.go
+++ b/pkg/operator/controllers/condition.go
@@ -12,13 +12,13 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/operator"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/util/version"
 )
 
-func SetCondition(ctx context.Context, arocli aroclient.AroV1alpha1Interface, cond *status.Condition, role string) error {
+func SetCondition(ctx context.Context, arocli aroclient.Interface, cond *status.Condition, role string) error {
 	return retry.RetryOnConflict(retry.DefaultRetry, func() error {
-		cluster, err := arocli.Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
+		cluster, err := arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
 		if err != nil {
 			return err
 		}
@@ -33,7 +33,7 @@ func SetCondition(ctx context.Context, arocli aroclient.AroV1alpha1Interface, co
 			return nil
 		}
 
-		_, err = arocli.Clusters().UpdateStatus(ctx, cluster, metav1.UpdateOptions{})
+		_, err = arocli.AroV1alpha1().Clusters().UpdateStatus(ctx, cluster, metav1.UpdateOptions{})
 		return err
 	})
 }
diff --git a/pkg/operator/controllers/genevalogging/genevalogging_controller.go b/pkg/operator/controllers/genevalogging/genevalogging_controller.go
index a44cc1838..fcd128c83 100644
--- a/pkg/operator/controllers/genevalogging/genevalogging_controller.go
+++ b/pkg/operator/controllers/genevalogging/genevalogging_controller.go
@@ -19,7 +19,7 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/operator"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/dynamichelper"
 )
@@ -28,12 +28,12 @@ import (
 type GenevaloggingReconciler struct {
 	kubernetescli kubernetes.Interface
 	securitycli   securityclient.Interface
-	arocli        aroclient.AroV1alpha1Interface
+	arocli        aroclient.Interface
 	restConfig    *rest.Config
 	log           *logrus.Entry
 }
 
-func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, securitycli securityclient.Interface, arocli aroclient.AroV1alpha1Interface, restConfig *rest.Config) *GenevaloggingReconciler {
+func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, securitycli securityclient.Interface, arocli aroclient.Interface, restConfig *rest.Config) *GenevaloggingReconciler {
 	return &GenevaloggingReconciler{
 		securitycli:   securitycli,
 		kubernetescli: kubernetescli,
@@ -51,7 +51,7 @@ func (r *GenevaloggingReconciler) Reconcile(request ctrl.Request) (ctrl.Result,
 		return reconcile.Result{}, nil
 	}
 
-	instance, err := r.arocli.Clusters().Get(ctx, request.Name, metav1.GetOptions{})
+	instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, request.Name, metav1.GetOptions{})
 	if err != nil {
 		return reconcile.Result{}, err
 	}
diff --git a/pkg/operator/controllers/monitoring/monitoring_controller.go b/pkg/operator/controllers/monitoring/monitoring_controller.go
index 2bafd6461..298bb2745 100644
--- a/pkg/operator/controllers/monitoring/monitoring_controller.go
+++ b/pkg/operator/controllers/monitoring/monitoring_controller.go
@@ -25,6 +25,7 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/api"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 )
 
@@ -64,13 +65,15 @@ var defaultConfig = `prometheusK8s:
 `
 
 type Reconciler struct {
+	arocli        aroclient.Interface
 	kubernetescli kubernetes.Interface
 	log           *logrus.Entry
 	jsonHandle    *codec.JsonHandle
 }
 
-func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface) *Reconciler {
+func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, arocli aroclient.Interface) *Reconciler {
 	return &Reconciler{
+		arocli:        arocli,
 		kubernetescli: kubernetescli,
 		log:           log,
 		jsonHandle:    new(codec.JsonHandle),
@@ -85,6 +88,12 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) {
 		return reconcile.Result{}, nil
 	}
 
+	// check feature gates and if set to false remove any persistence
+	cluster, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
+	if err != nil {
+		return reconcile.Result{}, err
+	}
+
 	return reconcile.Result{}, retry.RetryOnConflict(retry.DefaultRetry, func() error {
 		cm, isCreate, err := r.monitoringConfigMap(ctx)
 		if err != nil {
@@ -106,14 +115,28 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) {
 		}
 
 		changed := false
-		if configData.PrometheusK8s.Retention != "15d" {
-			configData.PrometheusK8s.Retention = "15d"
-			changed = true
-		}
-
-		if configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage != "100Gi" {
-			configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage = "100Gi"
-			changed = true
+		switch cluster.Spec.Features.PersistentPrometheus {
+		case true:
+			// we are enabling persistence
+			if configData.PrometheusK8s.Retention != "15d" {
+				configData.PrometheusK8s.Retention = "15d"
+				changed = true
+			}
+			if configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage != "100Gi" {
+				configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage = "100Gi"
+				changed = true
+			}
+			// we are disabling persistence. We use omitempty on the struct to
+			// clean the fields
+		case false:
+			if configData.PrometheusK8s.Retention != "" {
+				configData.PrometheusK8s.Retention = ""
+				changed = true
+			}
+			if configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage != "" {
+				configData.PrometheusK8s.VolumeClaimTemplate.Spec.Resources.Requests.Storage = ""
+				changed = true
+			}
 		}
 
 		if !isCreate && !changed {
@@ -133,10 +156,10 @@ func (r *Reconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) {
 		cm.Data["config.yaml"] = string(cmYaml)
 
 		if isCreate {
-			r.log.Info("re-creating monitoring configmap")
+			r.log.Infof("re-creating monitoring configmap. featureFlag %t", cluster.Spec.Features.PersistentPrometheus)
 			_, err = r.kubernetescli.CoreV1().ConfigMaps(monitoringName.Namespace).Create(ctx, cm, metav1.CreateOptions{})
 		} else {
-			r.log.Info("updating monitoring configmap")
+			r.log.Infof("updating monitoring configmap. featureFlag %t", cluster.Spec.Features.PersistentPrometheus)
 			_, err = r.kubernetescli.CoreV1().ConfigMaps(monitoringName.Namespace).Update(ctx, cm, metav1.UpdateOptions{})
 		}
 		return err
diff --git a/pkg/operator/controllers/pullsecret/pullsecret_controller.go b/pkg/operator/controllers/pullsecret/pullsecret_controller.go
index 4d4ac9b6d..8c5a83cdb 100644
--- a/pkg/operator/controllers/pullsecret/pullsecret_controller.go
+++ b/pkg/operator/controllers/pullsecret/pullsecret_controller.go
@@ -24,7 +24,6 @@ import (
 
 	"github.com/Azure/ARO-RP/pkg/operator"
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/pullsecret"
 )
@@ -34,15 +33,13 @@ var pullSecretName = types.NamespacedName{Name: "pull-secret", Namespace: "opens
 // PullSecretReconciler reconciles a Cluster object
 type PullSecretReconciler struct {
 	kubernetescli kubernetes.Interface
-	arocli        aroclient.AroV1alpha1Interface
 	log           *logrus.Entry
 }
 
-func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, arocli aroclient.AroV1alpha1Interface) *PullSecretReconciler {
+func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface) *PullSecretReconciler {
 	return &PullSecretReconciler{
 		log:           log,
 		kubernetescli: kubernetescli,
-		arocli:        arocli,
 	}
 }
 
diff --git a/pkg/operator/controllers/rbac/rbac_controller.go b/pkg/operator/controllers/rbac/rbac_controller.go
index cfa2c9fd6..f4e852872 100644
--- a/pkg/operator/controllers/rbac/rbac_controller.go
+++ b/pkg/operator/controllers/rbac/rbac_controller.go
@@ -15,7 +15,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/dynamichelper"
 )
@@ -23,11 +23,11 @@ import (
 type RBACReconciler struct {
 	log *logrus.Entry
 
-	arocli aroclient.AroV1alpha1Interface
+	arocli aroclient.Interface
 	dh     dynamichelper.Interface
 }
 
-func NewReconciler(log *logrus.Entry, arocli aroclient.AroV1alpha1Interface, dh dynamichelper.Interface) *RBACReconciler {
+func NewReconciler(log *logrus.Entry, arocli aroclient.Interface, dh dynamichelper.Interface) *RBACReconciler {
 	return &RBACReconciler{
 		log:    log,
 		arocli: arocli,
@@ -42,7 +42,7 @@ func (r *RBACReconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) {
 		return reconcile.Result{}, nil
 	}
 
-	instance, err := r.arocli.Clusters().Get(ctx, request.Name, metav1.GetOptions{})
+	instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, request.Name, metav1.GetOptions{})
 	if err != nil {
 		r.log.Error(err)
 		return reconcile.Result{}, err
diff --git a/pkg/operator/controllers/routefix/routefix_controller.go b/pkg/operator/controllers/routefix/routefix_controller.go
index 61fe7e112..e3f2c40cd 100644
--- a/pkg/operator/controllers/routefix/routefix_controller.go
+++ b/pkg/operator/controllers/routefix/routefix_controller.go
@@ -16,7 +16,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/dynamichelper"
 )
@@ -25,13 +25,13 @@ import (
 type RouteFixReconciler struct {
 	kubernetescli kubernetes.Interface
 	securitycli   securityclient.Interface
-	arocli        aroclient.AroV1alpha1Interface
+	arocli        aroclient.Interface
 	restConfig    *rest.Config
 	log           *logrus.Entry
 }
 
 //NewReconciler creates a new Reconciler
-func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, securitycli securityclient.Interface, arocli aroclient.AroV1alpha1Interface, restConfig *rest.Config) *RouteFixReconciler {
+func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, securitycli securityclient.Interface, arocli aroclient.Interface, restConfig *rest.Config) *RouteFixReconciler {
 	return &RouteFixReconciler{
 		securitycli:   securitycli,
 		kubernetescli: kubernetescli,
@@ -48,7 +48,7 @@ func (r *RouteFixReconciler) Reconcile(request ctrl.Request) (ctrl.Result, error
 		return reconcile.Result{}, nil
 	}
 
-	instance, err := r.arocli.Clusters().Get(ctx, request.Name, metav1.GetOptions{})
+	instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, request.Name, metav1.GetOptions{})
 	if err != nil {
 		r.log.Error(err)
 		return reconcile.Result{}, err
diff --git a/pkg/operator/controllers/workaround/workaround_controller.go b/pkg/operator/controllers/workaround/workaround_controller.go
index 8fa8106a3..c9393ee6a 100644
--- a/pkg/operator/controllers/workaround/workaround_controller.go
+++ b/pkg/operator/controllers/workaround/workaround_controller.go
@@ -19,7 +19,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
-	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/typed/aro.openshift.io/v1alpha1"
+	aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
 	"github.com/Azure/ARO-RP/pkg/operator/controllers"
 	"github.com/Azure/ARO-RP/pkg/util/dynamichelper"
 	"github.com/Azure/ARO-RP/pkg/util/version"
@@ -30,13 +30,13 @@ import (
 type WorkaroundReconciler struct {
 	kubernetescli kubernetes.Interface
 	configcli     configclient.Interface
-	arocli        aroclient.AroV1alpha1Interface
+	arocli        aroclient.Interface
 	restConfig    *rest.Config
 	workarounds   []Workaround
 	log           *logrus.Entry
 }
 
-func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, configcli configclient.Interface, mcocli mcoclient.Interface, arocli aroclient.AroV1alpha1Interface, restConfig *rest.Config) *WorkaroundReconciler {
+func NewReconciler(log *logrus.Entry, kubernetescli kubernetes.Interface, configcli configclient.Interface, mcocli mcoclient.Interface, arocli aroclient.Interface, restConfig *rest.Config) *WorkaroundReconciler {
 	dh, err := dynamichelper.New(log, restConfig)
 	if err != nil {
 		panic(err)
diff --git a/pkg/operator/deploy/bindata.go b/pkg/operator/deploy/bindata.go
index 1b73cf221..ce0110e90 100644
--- a/pkg/operator/deploy/bindata.go
+++ b/pkg/operator/deploy/bindata.go
@@ -86,7 +86,7 @@ func (fi bindataFileInfo) Sys() interface{} {
 	return nil
 }
 
-var _aroOpenshiftIo_clustersYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\x4f\x6f\xe3\xd8\x0d\xbf\xfb\x53\x10\xe9\x61\x0e\x8d\x95\x19\xec\xa5\xf5\x6d\x90\xd9\x16\x41\xb7\xdb\xc1\x24\x98\xcb\x66\x0f\xd4\x13\x2d\xb3\x79\x7a\x4f\xe5\xa3\x92\xf1\x14\xfd\xee\x05\x9f\x24\x5b\x76\xa4\x75\x5a\x60\x75\x31\xc4\xf7\xf8\x9f\xfc\x91\xd6\x6a\xbd\x5e\xaf\xb0\xe5\xaf\x24\x89\x63\xd8\x00\xb6\x4c\xdf\x94\x82\xbd\xa5\xe2\xe9\x4f\xa9\xe0\x78\xf3\xfc\xa1\x24\xc5\x0f\xab\x27\x0e\xd5\x06\x6e\xbb\xa4\xb1\xf9\x42\x29\x76\xe2\xe8\x13\x6d\x39\xb0\x72\x0c\xab\x86\x14\x2b\x54\xdc\xac\x00\x30\x84\xa8\x68\xe4\x64\xaf\x00\x2e\x06\x95\xe8\x3d\xc9\xba\xa6\x50\x3c\x75\x25\x95\x1d\xfb\x8a\x24\x6b\x18\xf5\x3f\xbf\x2f\x7e\x28\xde\xaf\x00\x9c\x50\x66\x7f\xe0\x86\x92\x62\xd3\x6e\x20\x74\xde\xaf\x00\x02\x36\xb4\x01\xe7\xbb\xa4\x24\xa9\x40\x89\x45\x6c\x29\xa4\x1d\x6f\xb5\xe0\xb8\x4a\x2d\x39\xd3\x59\x4b\xec\xda\x0d\xbc\x3a\xef\x25\x0c\x66\x0d\x2e\xf5\xc2\x32\xc5\x73\xd2\xbf\x4d\xa9\x3f\x71\xd2\x7c\xd2\xfa\x4e\xd0\x1f\x55\x67\x62\xe2\x50\x77\x1e\xe5\x40\x5e\x01\x24\x17\x5b\x9a\x4a\x4d\x5d\x29\x43\xbc\x06\xbd\x49\x51\xbb\xb4\x81\x7f\xff\x67\x05\xf0\x8c\x9e\xab\xec\x6d\x7f\x68\xe6\x7e\xfc\x7c\xf7\xf5\x87\x7b\xb7\xa3\x06\x7b\x22\x40\x45\xc9\x09\xb7\xf9\xde\x28\x1c\x38\x81\xee\x08\xfa\x9b\xb0\x8d\x92\x5f\x47\x13\xe1\xe3\xe7\xbb\x81\xbb\x95\xd8\x92\x28\x8f\x16\xd8\x33\xc9\xfc\x81\x76\xa6\xe7\x9d\x19\xd2\xdf\x81\xca\x72\x4d\xbd\xc2\x21\x63\x54\x41\xea\x55\xc7\x2d\xe8\x8e\x13\x08\xb5\x42\x89\x42\x9f\xfd\x89\x58\xb0\x2b\x18\x20\x96\xff\x24\xa7\x05\xdc\x93\x98\x10\x48\xbb\xd8\xf9\xca\x0a\xe4\x99\x44\x41\xc8\xc5\x3a\xf0\xf7\x83\xe4\x04\x1a\xb3\x4a\x8f\x4a\x43\x2a\xc6\x87\x83\x92\x04\xf4\x16\xc2\x8e\xae\x01\x43\x05\x0d\xee\x41\xc8\x74\x40\x17\x26\xd2\xf2\x95\x54\xc0\xdf\xa3\x10\x70\xd8\xc6\x0d\xec\x54\xdb\xb4\xb9\xb9\xa9\x59\xc7\x5a\x77\xb1\x69\xba\xc0\xba\xbf\xc9\x15\xcb\x65\xa7\x51\xd2\x4d\x45\xcf\xe4\x6f\x12\xd7\x6b\x14\xb7\x63\x25\xa7\x9d\xd0\x0d\xb6\xbc\xce\x86\x87\x5c\xea\x45\x53\xfd\xe1\x90\xe8\x77\x13\x4b\x75\x6f\x05\x91\x54\x38\xd4\x07\x72\xae\xbd\xc5\xb8\x5b\x0d\x5a\x76\x71\x60\xeb\xed\x3f\x86\xd7\x48\x16\x95\x2f\x3f\xde\x3f\xc0\xa8\x34\xa7\xe0\x34\xe6\x39\xda\x47\xb6\x74\x0c\xbc\x05\x8a\xc3\x96\xa4\x4f\xdc\x56\x62\x93\x25\x52\xa8\xda\xc8\x41\x87\x4a\x62\x0a\xa7\x41\x4f\x5d\xd9\xb0\x5a\xa6\xff\xd5\x51\x52\xcb\x4f\x01\xb7\xb9\xe3\xa1\x24\xe8\xda\x0a\x95\xaa\x02\xee\x02\xdc\x62\x43\xfe\x16\x13\xfd\xee\x61\xb7\x08\xa7\xb5\x85\xf4\x72\xe0\xa7\x40\x75\x7a\xb1\x8f\xd6\x81\x3c\x42\xc9\x6c\x86\x86\x0e\xbc\x6f\xc9\x9d\x74\x46\x45\x89\xc5\xaa\x57\x51\xc9\x6a\x7e\x8a\x2e\xcb\xbd\x98\xfb\xd1\xc9\xa7\xd8\x20\x87\x53\xf2\x82\x1b\x03\xc7\xcf\x86\x87\x6f\xbd\x5f\x53\xa0\x67\xfc\x29\xd6\x35\x87\xfa\x9c\x6b\xc9\x2c\xe8\xf1\x7b\xcb\xf5\x0c\x5a\x1c\x98\x51\xad\x17\x37\xf0\xee\x97\xf7\xeb\x3f\xff\xfa\xc7\xa2\xff\x79\x37\x73\x75\xd1\x3a\x7b\x9a\x18\x58\xa3\x1d\xfd\xf5\xf6\xfe\xc7\xf0\xcc\x12\x43\x43\x41\xe7\x74\x52\xe8\x9a\x39\xfa\x1a\x3e\x31\xd6\x21\x26\x65\x97\x3e\x4b\xac\x66\xef\x3c\x9c\xa3\xc9\x45\xeb\x66\x8b\x04\x0e\x38\x44\x7a\xbb\x23\xf7\x44\xf2\xbf\x04\xb6\x13\x3f\x43\x05\x60\xa5\x66\xf6\xe0\x42\xfc\xc6\x63\x14\xc1\xfd\x5b\xed\xf7\xd1\x4d\xe6\xcf\x1b\x34\x8d\x80\x73\x57\x9d\xb3\x9c\xf4\xc8\xb8\x25\xdc\x7d\x1a\x07\xd5\xc7\xef\x9d\xd0\x84\xbd\x9f\x1b\x34\x99\x9e\x6f\xd0\x3f\xdf\xac\xfd\x48\xbd\xd4\xae\xf9\xd6\x49\xc3\xc6\x32\x19\x22\xfe\x5f\x1d\xeb\x62\xa8\x78\xb2\xe6\x2c\x29\x3f\x5c\x1b\x20\x9d\x34\xeb\x19\xc9\xc0\x21\x29\x06\x47\xa9\x38\x13\xb3\x50\x06\x27\xd2\xaf\x8e\x72\x8e\x38\xdf\x8f\x5a\xf3\x2c\x67\xf6\x64\xf8\xbe\x4b\xbd\xaf\xe7\xca\xec\x99\x98\x8a\x42\xc6\x73\x58\x08\xa1\x21\xb7\xc3\xc0\xa9\xc9\x05\x1f\x2a\xaa\x6c\x36\x1b\xe6\x27\xaa\xe0\x65\x47\xc1\x02\x3a\x23\xb4\x22\x45\xf6\xe9\x60\xc4\xd1\x2c\xd3\x61\x83\x03\xa1\x15\x8e\xc2\xf0\x14\xe2\x4b\x80\x28\xf0\x92\xd7\x82\x7c\xd6\xb6\xfe\xbc\x94\x73\x19\x44\x40\xef\x8f\xb1\xcb\xe2\xa1\xe6\x67\x0a\x60\xe3\xb3\x80\xc7\x30\xf5\x67\xd8\x34\x4a\x02\xac\x2a\x9a\x03\x05\x8d\x40\xdf\x5a\xcf\x8e\xd5\xef\xfb\x95\x64\x3f\xc9\x3d\xe8\x0e\xd5\x9c\x95\x94\x57\x0d\x17\x9b\x36\x86\x1c\x6d\x97\x83\x55\xc6\x6e\x0e\x52\x04\x75\x97\xc7\x2c\x86\x3c\x35\x59\xfa\xe9\x1d\x13\x9d\x48\xcf\xb1\xcc\x23\xd9\x06\x48\x1e\xc8\xd1\x38\x67\x44\x4e\x62\x98\x0a\xf8\x47\x70\x34\xd4\x74\x75\x9d\x8b\xba\x21\x0c\xa6\x24\x87\xe4\x58\x1f\x0e\x03\xf4\x73\x7a\x46\xa6\x25\xb7\xa6\x0a\x50\x4a\x56\x41\x61\xbf\x87\x35\xb0\xdd\x76\xb1\xa1\x04\x2d\x8a\x8e\x0d\xfb\xf1\xf3\x5d\xbf\x6f\xed\xb0\x6f\xa3\x84\xcd\x9c\xd0\x12\xdd\xd3\x0b\x4a\x95\xd6\xf9\xf6\x36\x4a\xff\x66\xb1\x43\xe5\x92\x3d\x6b\x0e\xb5\x23\x09\x43\x85\xec\x7b\xb7\xb3\xbe\x39\xdf\x0f\x16\x14\x57\xaf\x8e\x7f\x0b\x6b\x01\x3c\x26\x7d\x10\x0c\x89\xc7\x3f\x17\xf3\x10\xbb\x8d\xd2\xa0\x6e\xc0\x56\x99\xb5\xf2\xac\x67\x17\x81\xb8\xa1\x94\xb0\x5e\xd0\x70\x81\x57\x08\xd3\xfc\xa0\x5d\x82\x96\x2f\x99\xc3\xf0\xe5\xac\x39\x11\x62\xa0\xf5\x4b\x94\xea\xfa\xb8\x90\xcd\x0a\x86\xb3\xed\xfd\x00\xce\xa8\x54\x47\xd9\xdb\xbb\xc3\x2e\xd1\xe1\xa0\x13\xa1\xa0\x03\xf6\xce\xc1\x89\x3d\x77\x3a\x63\x55\x86\x0c\x0e\x39\xf3\x6c\x12\x3b\x6d\x3b\xbd\x86\xd4\xb9\x1d\x60\xca\x36\x7b\x0e\x4b\x86\xda\x1f\x48\xa7\x1e\x6a\x43\xd2\x81\xd5\xea\x8b\x03\xa4\xae\x69\x50\xf8\x7b\x2e\x7f\xd7\x9b\x38\xa0\x43\x36\x7e\xc1\xce\x0b\x09\x79\x3d\x5e\xde\xcc\x9a\x8f\x2f\x67\xf2\x08\xe3\x0f\xfb\x96\xc6\x81\x69\xcc\x87\x70\x1f\xfa\x38\xbb\x9a\x16\x62\xa3\xfb\x96\x1d\x7a\xbf\xb7\xd6\x1f\x13\x5e\x81\x55\x80\x01\x6b\xda\x45\x51\x68\x77\x92\xf7\xf2\x29\x44\x66\x65\x4b\x52\x07\xf4\xe4\x50\xb1\xd5\xc3\x30\x2d\x39\x43\x3e\x3c\x5e\x61\x19\xac\x67\xfc\x5a\xa5\xa3\xc7\x2b\x68\xa3\x47\x61\xdd\x17\xf0\x97\x38\x07\x60\xf6\xd0\x37\x6c\x5a\x4f\xd7\xc0\xe7\xfe\x8d\x5a\x52\x3f\x55\xd0\xc4\xb1\xdb\xf7\x75\x94\xff\x2f\x5f\x2f\x39\x9f\xad\xe1\xd4\xff\xab\x7e\xbc\x02\x87\x29\x07\xb3\x95\x58\x62\xe9\xf7\xf9\x86\xd9\x7a\x0d\x29\x9e\xaa\xfd\x6d\xcf\x4b\x6b\x04\xef\xa9\x82\xc7\xab\xbb\x30\x88\x9f\x41\x20\xb8\x54\x11\xfd\x08\xa0\x57\xab\x93\x2d\xa5\x7d\x99\xcd\x1c\x98\xc4\x57\xe4\xc5\x85\x6e\x79\x0b\x34\x74\x44\x8d\xb2\xb0\xca\xbf\x7d\xe3\x3a\x23\x1d\x3f\xe1\x7c\x40\xdf\xee\xf0\xc3\x91\x96\xbb\x66\x3d\x7c\xb8\x99\x1c\x03\xf4\x8b\xd7\x06\xac\x66\x86\xef\x22\x51\x0c\x32\x7b\xca\xb1\xe7\xd0\x39\x6a\x95\xaa\x9f\xcf\x3f\xdd\x5c\x5d\x9d\x7c\x9b\xc9\xaf\x93\xa5\x0c\x7e\xf9\x75\xd5\x4b\xa5\xea\xeb\x68\x8d\x11\xff\x1b\x00\x00\xff\xff\xff\xcb\xfc\x9c\xfa\x12\x00\x00")
+var _aroOpenshiftIo_clustersYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\xcd\x6e\xe3\xc8\x11\xbe\xeb\x29\x0a\xce\xc1\x87\x58\xf4\x0c\xf6\x92\xe8\x66\x78\x76\x03\x23\x9b\x5d\x63\x6c\xcc\x65\xbd\x87\x62\xb3\x44\x55\xdc\xec\x66\xaa\x8b\xf6\x68\x82\xbc\x7b\x50\x4d\x52\xa2\x64\x6a\xe4\x04\x58\x5e\x04\x56\x77\xd7\x7f\x7d\xfd\x89\x8b\xe5\x72\xb9\xc0\x96\xbf\x90\x24\x8e\x61\x05\xd8\x32\x7d\x55\x0a\xf6\x96\x8a\xe7\xbf\xa4\x82\xe3\xf5\xcb\xc7\x92\x14\x3f\x2e\x9e\x39\x54\x2b\xb8\xed\x92\xc6\xe6\x33\xa5\xd8\x89\xa3\x4f\xb4\xe6\xc0\xca\x31\x2c\x1a\x52\xac\x50\x71\xb5\x00\xc0\x10\xa2\xa2\x89\x93\xbd\x02\xb8\x18\x54\xa2\xf7\x24\xcb\x9a\x42\xf1\xdc\x95\x54\x76\xec\x2b\x92\x6c\x61\xb4\xff\xf2\xa1\xf8\xa1\xf8\xb0\x00\x70\x42\xf9\xf8\x23\x37\x94\x14\x9b\x76\x05\xa1\xf3\x7e\x01\x10\xb0\xa1\x15\x38\xdf\x25\x25\x49\x05\x4a\x2c\x62\x4b\x21\x6d\x78\xad\x05\xc7\x45\x6a\xc9\x99\xcd\x5a\x62\xd7\xae\xe0\xcd\x7a\xaf\x61\x70\x6b\x08\xa9\x57\x96\x25\x9e\x93\xfe\x7d\x2a\xfd\x99\x93\xe6\x95\xd6\x77\x82\x7e\x6f\x3a\x0b\x13\x87\xba\xf3\x28\x3b\xf1\x02\x20\xb9\xd8\xd2\x54\x6b\xea\x4a\x19\xf2\x35\xd8\x4d\x8a\xda\xa5\x15\xfc\xfb\x3f\x0b\x80\x17\xf4\x5c\xe5\x68\xfb\x45\x73\xf7\xe6\xfe\xee\xcb\x0f\x0f\x6e\x43\x0d\xf6\x42\x80\x8a\x92\x13\x6e\xf3\xbe\x51\x39\x70\x02\xdd\x10\xf4\x3b\x61\x1d\x25\xbf\x8e\x2e\xc2\xcd\xfd\xdd\x70\xba\x95\xd8\x92\x28\x8f\x1e\xd8\x33\xa9\xfc\x4e\x76\x64\xe7\xd2\x1c\xe9\xf7\x40\x65\xb5\xa6\xde\xe0\x50\x31\xaa\x20\xf5\xa6\xe3\x1a\x74\xc3\x09\x84\x5a\xa1\x44\xa1\xaf\xfe\x44\x2d\xd8\x16\x0c\x10\xcb\x7f\x92\xd3\x02\x1e\x48\x4c\x09\xa4\x4d\xec\x7c\x65\x0d\xf2\x42\xa2\x20\xe4\x62\x1d\xf8\xdb\x4e\x73\x02\x8d\xd9\xa4\x47\xa5\xa1\x14\xe3\xc3\x41\x49\x02\x7a\x4b\x61\x47\x57\x80\xa1\x82\x06\xb7\x20\x64\x36\xa0\x0b\x13\x6d\x79\x4b\x2a\xe0\x1f\x51\x08\x38\xac\xe3\x0a\x36\xaa\x6d\x5a\x5d\x5f\xd7\xac\x63\xaf\xbb\xd8\x34\x5d\x60\xdd\x5e\xe7\x8e\xe5\xb2\xd3\x28\xe9\xba\xa2\x17\xf2\xd7\x89\xeb\x25\x8a\xdb\xb0\x92\xd3\x4e\xe8\x1a\x5b\x5e\x66\xc7\x43\x6e\xf5\xa2\xa9\xfe\xb4\x2b\xf4\xe5\xc4\x53\xdd\x5a\x43\x24\x15\x0e\xf5\x4e\x9c\x7b\xef\x64\xde\xad\x07\xad\xba\x38\x1c\xeb\xfd\xdf\xa7\xd7\x44\x96\x95\xcf\x3f\x3e\x3c\xc2\x68\x34\x97\xe0\x30\xe7\x39\xdb\xfb\x63\x69\x9f\x78\x4b\x14\x87\x35\x49\x5f\xb8\xb5\xc4\x26\x6b\xa4\x50\xb5\x91\x83\x0e\x9d\xc4\x14\x0e\x93\x9e\xba\xb2\x61\xb5\x4a\xff\xab\xa3\xa4\x56\x9f\x02\x6e\xf3\xc4\x43\x49\xd0\xb5\x15\x2a\x55\x05\xdc\x05\xb8\xc5\x86\xfc\x2d\x26\xfa\xc3\xd3\x6e\x19\x4e\x4b\x4b\xe9\xf9\xc4\x4f\x81\xea\x70\x63\x9f\xad\x9d\x78\x84\x92\xd9\x0a\x0d\x13\xf8\xd0\x92\x3b\x98\x8c\x8a\x12\x8b\x75\xaf\xa2\x92\xf5\xfc\x14\x5d\x4e\xcf\x62\x9e\x47\x27\x9f\x62\x83\x1c\x0e\xc5\x27\xc2\x18\x4e\xfc\x62\x78\xf8\xde\xfd\x6b\x42\xcb\x60\x3a\x3e\x70\x10\xd9\x4f\xc3\xa6\x83\xd0\x6e\x3e\xff\x6a\xc0\x24\xa8\x51\x46\x35\x50\xdb\x48\x1e\xa9\x3a\x15\x5c\x5e\x33\xd4\x48\x4a\x41\xef\x25\x36\xa4\x1b\xea\x66\x76\x8d\xfe\x97\x31\x7a\xc2\x30\x1b\xdb\x51\xa5\xec\xa9\x29\xd0\x0b\xfe\x1c\xeb\x9a\x43\x7d\xac\xf5\x7b\x5e\xb9\x18\xd6\x5c\xcf\x20\xe1\xee\x30\xaa\xe1\xcc\x0a\x2e\x7f\xfb\xb0\xfc\xeb\xef\x7f\x2e\xfa\x9f\xcb\x93\x9e\xcf\x64\xde\x9e\x26\x06\xd6\x68\x4b\x7f\xbb\x7d\xf8\x31\xbc\xb0\xc4\xd0\x50\xd0\x39\x9b\x14\xba\x66\x4e\xbe\x84\x4f\x8c\x75\x88\x49\xd9\xa5\x7b\x89\xd5\xec\x9e\xc7\x63\xa4\x3c\xeb\xdd\xc9\xb4\xf6\x18\x4b\x7a\xbb\x21\xf7\x4c\xf2\xbf\x24\xb6\x13\x3f\x5b\x5e\x56\x6a\x66\x17\xce\xe4\x6f\x5c\x46\x11\xdc\xbe\xd7\x7f\x1f\xdd\xe4\x6e\x7d\x87\xa5\x11\x4c\xef\xaa\xef\x4e\xc9\xc8\x80\xee\x3e\x8d\x97\xf0\xcd\x37\x9b\x89\xfd\xf1\xfe\x4e\xa4\x09\x33\x78\x87\xfd\x79\x20\xea\xe9\xc2\x39\x28\xca\xbb\x0e\xc0\x28\x96\xc9\xd0\xfe\xff\x42\x23\x17\x43\xc5\x13\x0a\x77\xca\xf8\x6e\xdb\x70\x5d\x91\x66\x3b\xa3\x18\x38\x24\xc5\xe0\x28\x15\x47\x6a\x4e\xb4\xc1\x81\xf6\x8b\xbd\x9e\xfd\x1d\xd6\xd3\x08\x8b\x2c\x57\xf6\x80\x58\x5c\xa6\x3e\xd6\x63\x63\xf6\x4c\x5c\x45\x21\x3b\xb3\x23\xbb\xd0\x90\xdb\x60\xe0\xd4\xe4\x86\x0f\x15\x55\xc6\x3b\xec\x3e\x4b\x54\xc1\xeb\x86\x82\x25\x74\x46\x69\x45\x8a\xec\xd3\xce\x89\xbd\x5b\x66\xc3\x2e\x45\x84\x56\x38\x0a\xc3\x73\x88\xaf\x01\xa2\xc0\x6b\xa6\x3c\x79\xad\x6d\xfd\x71\x2b\xe7\x36\x88\x80\xde\xef\x73\x97\xd5\x43\xcd\x2f\x14\xc0\xa8\x41\x01\x4f\x61\x1a\xcf\xc0\xa2\x4a\x02\xac\x2a\x9a\x03\x05\x8d\x40\x5f\x5b\xcf\x8e\xd5\x6f\x7b\xba\xb5\x9d\xd4\x1e\x74\x83\x6a\xc1\x4a\xca\x34\xca\xc5\xa6\x8d\x21\x67\xdb\xe5\x64\x95\xb1\x9b\x83\x14\x41\xdd\x64\x0a\x81\x21\x33\x02\x96\x9e\x99\xc4\x44\x07\xda\x73\x2e\x33\xdd\xb0\xcb\x31\x93\x8d\x68\x27\x67\x54\x4e\x72\x98\x0a\xf8\x35\x38\x1a\x7a\xba\xba\xca\x4d\xdd\x10\x06\x33\x92\x53\xb2\xef\x0f\x87\x01\x7a\x0e\x32\xa3\xd3\x8a\x5b\x53\x05\x28\x25\xab\xa0\xb0\xdf\xc2\x12\xd8\x76\xbb\xd8\x50\x82\x16\x45\xc7\x81\xbd\xb9\xbf\xeb\xb9\xe4\x06\xfb\x31\x4a\xd8\xcc\x29\x2d\xd1\x3d\xbf\xa2\x54\x69\x99\x77\xaf\xa3\xf4\x6f\x96\x3b\x54\x2e\xd9\xb3\xe6\x54\x3b\x92\x30\x74\xc8\xb6\x0f\x3b\xdb\x9b\x8b\x7d\xe7\x41\x71\xf1\xf6\xfa\xfc\x0e\xd6\x02\x78\x4c\xfa\x28\x18\x12\x8f\x7f\x9c\xe6\x21\x76\x1d\xa5\x41\x5d\x81\xd1\xb4\xa5\xf2\x6c\x64\x67\x81\xb8\xa1\x94\xb0\x3e\x61\xe1\xcc\x59\x21\x4c\xf3\x17\xed\x29\x68\xf9\x9c\x4f\x18\xbe\x1c\x0d\x27\x42\x0c\xb4\x7c\x8d\x52\x5d\xed\xc9\xe6\xac\x62\x38\xfa\x67\xb2\x03\x67\x54\xaa\xa3\x6c\xed\xdd\x61\x97\x68\xb7\xd0\x89\x50\xd0\x01\x7b\xe7\xe0\xc4\x9e\x3b\x9d\xf1\x2a\x43\x06\x87\x5c\x79\x36\x8d\x9d\xb6\x9d\x5e\x41\xea\xdc\x06\x30\x65\x9f\x3d\x87\x53\x8e\xda\x9f\x63\xa7\x1e\x6a\x43\xd2\xe1\xa8\xf5\x17\x07\x48\x5d\xd3\xa0\xf0\xb7\xdc\xfe\xae\x77\x71\x40\x87\xec\xfc\x09\x3f\xcf\x14\xe4\xed\xf5\xf2\xee\xa3\x79\xf9\x7c\x25\xf7\x30\xfe\xb8\x6d\x69\xbc\x30\xed\xf0\x2e\xdd\xbb\x39\xce\xa1\x1e\xb3\xca\x89\x3d\x76\xe8\xfd\xd6\x46\x7f\x2c\x78\x05\xd6\x01\x06\xac\x69\x13\x45\xa1\xdd\x48\xfe\xcf\x31\x85\xc8\x6c\xec\x94\xd6\x01\x3d\x39\x54\x6c\xfd\x30\xdc\x96\x9c\x21\x1f\x9e\x2e\xb0\x0c\x36\x33\x7e\xa9\xd2\xd1\xd3\x05\xb4\xd1\xa3\xb0\x6e\x0b\xf8\x29\xce\x01\x98\x3d\xf4\x15\x9b\xd6\xd3\x15\xf0\x71\x7c\xa3\x95\xd4\xdf\x2a\x68\xea\xd8\x6d\xfb\x3e\xca\xdf\x02\xae\x4e\x05\x9f\xbd\xe1\xd4\x7f\x31\x78\xba\x00\x87\x29\x27\xb3\x95\x58\x62\xe9\xb7\x79\x87\xf9\x7a\x05\x29\x1e\x9a\xfd\x7e\xe4\xa5\x0d\x82\xf7\x54\xc1\xd3\xc5\x5d\x18\xd4\xcf\x20\x10\x9c\xeb\x88\xfe\x0a\xa0\x37\xd4\xc9\x48\x69\xdf\x66\x33\x0b\xa6\xf1\x8d\xf8\x24\xa1\x3b\xcd\x02\xc7\xff\x27\x27\xa8\xfc\xfb\x19\xd7\x91\x68\xff\x79\xea\x23\xfa\x76\x83\x1f\xf7\xb2\x3c\x35\xcb\xe1\xa3\xd4\x64\x19\xa0\x27\x5e\x2b\xb0\x9e\x19\xbe\xf9\x44\x31\xc8\xec\x25\xfb\x99\x43\xe7\xa8\x55\xaa\x7e\x39\xfe\x2c\x75\x71\x71\xf0\xdd\x29\xbf\x4e\x48\x19\xfc\xf6\xfb\xa2\xd7\x4a\xd5\x97\xd1\x1b\x13\xfe\x37\x00\x00\xff\xff\x98\x33\xf5\x3b\xd6\x13\x00\x00")
 
 func aroOpenshiftIo_clustersYamlBytes() ([]byte, error) {
 	return bindataRead(
diff --git a/pkg/operator/deploy/deploy.go b/pkg/operator/deploy/deploy.go
index cf94a5b90..8f02b6f63 100644
--- a/pkg/operator/deploy/deploy.go
+++ b/pkg/operator/deploy/deploy.go
@@ -172,6 +172,9 @@ func (o *operator) resources() ([]runtime.Object, error) {
 						monitoringEndpoint,
 					},
 				},
+				Features: arov1alpha1.FeaturesSpec{
+					PersistentPrometheus: true,
+				},
 			},
 		},
 	), nil
diff --git a/pkg/operator/deploy/staticresources/aro.openshift.io_clusters.yaml b/pkg/operator/deploy/staticresources/aro.openshift.io_clusters.yaml
index 7929a8355..f7262dc89 100644
--- a/pkg/operator/deploy/staticresources/aro.openshift.io_clusters.yaml
+++ b/pkg/operator/deploy/staticresources/aro.openshift.io_clusters.yaml
@@ -40,6 +40,12 @@ spec:
               type: string
             acrName:
               type: string
+            features:
+              description: FeaturesSpec defines ARO operator feature gates
+              properties:
+                persistentPrometheus:
+                  type: boolean
+              type: object
             genevaLogging:
               properties:
                 configVersion:
diff --git a/pkg/util/instancemetadata/dev.go b/pkg/util/instancemetadata/dev.go
index a307b7507..335db9a85 100644
--- a/pkg/util/instancemetadata/dev.go
+++ b/pkg/util/instancemetadata/dev.go
@@ -33,13 +33,7 @@ func NewDev(checkEnv bool) (InstanceMetadata, error) {
 		}
 	}
 
-	hostname, err := os.Hostname()
-	if err != nil {
-		return nil, err
-	}
-
 	return &instanceMetadata{
-		hostname:       hostname,
 		tenantID:       os.Getenv("AZURE_TENANT_ID"),
 		subscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"),
 		location:       os.Getenv("LOCATION"),
diff --git a/pkg/util/instancemetadata/instancemetadata.go b/pkg/util/instancemetadata/instancemetadata.go
index 1ef532e02..361dd5474 100644
--- a/pkg/util/instancemetadata/instancemetadata.go
+++ b/pkg/util/instancemetadata/instancemetadata.go
@@ -12,7 +12,6 @@ import (
 )
 
 type InstanceMetadata interface {
-	Hostname() string
 	TenantID() string
 	SubscriptionID() string
 	Location() string
@@ -21,7 +20,6 @@ type InstanceMetadata interface {
 }
 
 type instanceMetadata struct {
-	hostname       string
 	tenantID       string
 	subscriptionID string
 	location       string
@@ -29,10 +27,6 @@ type instanceMetadata struct {
 	environment    *azure.Environment
 }
 
-func (im *instanceMetadata) Hostname() string {
-	return im.hostname
-}
-
 func (im *instanceMetadata) TenantID() string {
 	return im.tenantID
 }
diff --git a/pkg/util/instancemetadata/prod.go b/pkg/util/instancemetadata/prod.go
index 8e0219215..0b2b1a36f 100644
--- a/pkg/util/instancemetadata/prod.go
+++ b/pkg/util/instancemetadata/prod.go
@@ -8,7 +8,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
-	"os"
 	"strings"
 
 	"github.com/Azure/go-autorest/autorest/adal"
@@ -127,11 +126,5 @@ func (p *prod) populateInstanceMetadata() error {
 	p.location = m.Location
 	p.resourceGroup = m.ResourceGroupName
 
-	hostname, err := os.Hostname()
-	if err != nil {
-		return err
-	}
-	p.hostname = hostname
-
 	return nil
 }
diff --git a/pkg/util/log/audit/audit.go b/pkg/util/log/audit/audit.go
deleted file mode 100644
index 160510032..000000000
--- a/pkg/util/log/audit/audit.go
+++ /dev/null
@@ -1,197 +0,0 @@
-package audit
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"encoding/json"
-	"sync"
-	"time"
-
-	uuid "github.com/satori/go.uuid"
-	"github.com/sirupsen/logrus"
-
-	"github.com/Azure/ARO-RP/pkg/env"
-)
-
-const (
-	MetadataCreatedTime = "createdTime"
-	MetadataPayload     = "payload"
-	MetadataLogKind     = "logKind"
-	MetadataSource      = "source"
-
-	SourceAdminPortal = "aro-admin"
-	SourceRP          = "aro-rp"
-
-	EnvKeyAppID               = "envAppID"
-	EnvKeyAppVer              = "envAppVer"
-	EnvKeyCloudDeploymentUnit = "envCloudDeploymentUnit"
-	EnvKeyCloudRole           = "envCloudRole"
-	EnvKeyCloudRoleVer        = "envCloudRoleVer"
-	EnvKeyCorrelationID       = "envCorrelationID"
-	EnvKeyIKey                = "envIKey"
-
-	PayloadKeyCallerIdentities = "payloadCallerIdentities"
-	PayloadKeyCategory         = "payloadCategory"
-	PayloadKeyNCloud           = "payloadNCloud"
-	PayloadKeyOperationName    = "payloadOperationName"
-	PayloadKeyResult           = "payloadResult"
-	PayloadKeyRequestID        = "payloadRequestID"
-	PayloadKeyTargetResources  = "payloadTargetResources"
-
-	ifxAuditCloudVer = 1.0
-	ifxAuditLogKind  = "ifxaudit"
-	ifxAuditName     = "#Ifx.AuditSchema"
-	ifxAuditVersion  = 2.1
-
-	// ifxAuditFlags is a collection of values bit-packed into a 64-bit integer.
-	// These properties describe how the event should be processed by the pipeline
-	// in an implementation-independent way.
-	ifxAuditFlags = 257
-)
-
-var (
-	// epoch is an unique identifier associated with the current session of the
-	// telemetry library running on the platform. It must be stable during a
-	// session, and has no implied ordering across sessions.
-	epoch = uuid.NewV4().String()
-
-	// seqNum is used to track absolute order of uploaded events, per session.
-	// It is reset when the ARO component is restarted. The first log will have
-	// its sequence number set to 1.
-	seqNum      uint64
-	seqNumMutex sync.Mutex
-)
-
-// NewEntry returns a log entry that embeds the provided logger. It has a hook
-// that knows how to hydrate an IFxAudit log payload before logging it.
-func NewEntry(env env.Core, logger *logrus.Logger) *logrus.Entry {
-	logger.AddHook(&payloadHook{
-		payload: &AuditPayload{},
-		env:     env,
-	})
-
-	return logrus.NewEntry(logger)
-}
-
-// payloadHook, when fires, hydrates an IFxAudit log payload using data in a log
-// entry.
-type payloadHook struct {
-	payload *AuditPayload
-	env     env.Core
-}
-
-func (payloadHook) Levels() []logrus.Level {
-	return logrus.AllLevels
-}
-
-func (h *payloadHook) Fire(entry *logrus.Entry) error {
-	payload := *h.payload // shallow copy
-
-	// Part-A
-	payload.EnvVer = ifxAuditVersion
-	payload.EnvName = ifxAuditName
-
-	logTime := entry.Time.UTC().Format(time.RFC3339)
-	payload.EnvTime = logTime
-
-	payload.EnvEpoch = epoch
-	payload.EnvSeqNum = nextSeqNum()
-
-	if v, ok := entry.Data[EnvKeyIKey].(string); ok {
-		payload.EnvIKey = v
-		delete(entry.Data, EnvKeyIKey)
-	}
-
-	payload.EnvFlags = ifxAuditFlags
-
-	if v, ok := entry.Data[EnvKeyAppID].(string); ok {
-		payload.EnvAppId = v
-		delete(entry.Data, EnvKeyAppID)
-	}
-
-	if v, ok := entry.Data[EnvKeyAppVer].(string); ok {
-		payload.EnvAppVer = v
-		delete(entry.Data, EnvKeyAppVer)
-	}
-
-	if v, ok := entry.Data[EnvKeyCorrelationID].(string); ok {
-		payload.EnvCV = v
-		delete(entry.Data, EnvKeyCorrelationID)
-	}
-
-	payload.EnvCloudName = h.env.Environment().Name
-
-	if v, ok := entry.Data[EnvKeyCloudRole].(string); ok {
-		payload.EnvCloudRole = v
-		delete(entry.Data, EnvKeyCloudRole)
-	}
-
-	if v, ok := entry.Data[EnvKeyCloudRoleVer].(string); ok {
-		payload.EnvCloudRoleVer = v
-		delete(entry.Data, EnvKeyCloudRoleVer)
-	}
-
-	payload.EnvCloudRoleInstance = h.env.Hostname()
-	payload.EnvCloudEnvironment = h.env.Environment().Name
-	payload.EnvCloudLocation = h.env.Location()
-
-	if v, ok := entry.Data[EnvKeyCloudDeploymentUnit].(string); ok {
-		payload.EnvCloudDeploymentUnit = v
-		delete(entry.Data, EnvKeyCloudDeploymentUnit)
-	}
-
-	payload.EnvCloudVer = ifxAuditCloudVer
-
-	// Part-B
-	if ids, ok := entry.Data[PayloadKeyCallerIdentities].([]*CallerIdentity); ok {
-		payload.CallerIdentities = append(payload.CallerIdentities, ids...)
-		delete(entry.Data, PayloadKeyCallerIdentities)
-	}
-
-	if v, ok := entry.Data[PayloadKeyCategory].(string); ok {
-		payload.Category = Category(v)
-		delete(entry.Data, PayloadKeyCategory)
-	}
-
-	if v, ok := entry.Data[PayloadKeyOperationName].(string); ok {
-		payload.OperationName = v
-		delete(entry.Data, PayloadKeyOperationName)
-	}
-
-	if v, ok := entry.Data[PayloadKeyResult].(*Result); ok {
-		payload.Result = v
-		delete(entry.Data, PayloadKeyResult)
-	}
-
-	if v, ok := entry.Data[PayloadKeyRequestID].(string); ok {
-		payload.RequestID = v
-		delete(entry.Data, PayloadKeyRequestID)
-	}
-
-	if rs, ok := entry.Data[PayloadKeyTargetResources].([]*TargetResource); ok {
-		payload.TargetResources = append(payload.TargetResources, rs...)
-		delete(entry.Data, PayloadKeyTargetResources)
-	}
-
-	// add the audit payload
-	b, err := json.Marshal(payload)
-	if err != nil {
-		return err
-	}
-	entry.Data[MetadataPayload] = string(b)
-
-	// add non-IFxAudit metadata for our own use
-	entry.Data[MetadataCreatedTime] = logTime
-	entry.Data[MetadataLogKind] = ifxAuditLogKind
-
-	return nil
-}
-
-func nextSeqNum() uint64 {
-	seqNumMutex.Lock()
-	defer seqNumMutex.Unlock()
-
-	seqNum++
-	return seqNum
-}
diff --git a/pkg/util/log/audit/schema.go b/pkg/util/log/audit/schema.go
deleted file mode 100644
index e23c712ee..000000000
--- a/pkg/util/log/audit/schema.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package audit
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-// AuditPayload is the IFxAudit log payload that will be sent to Geneva. It has
-// all the required and optional fields defined in IFxAudit Part-A and Part-B
-// schema.
-//
-// Fields that are marked as optional or "required when applicable" in the
-// schema are marked with the omitempty tag. Fields that are marked as "unused"
-// are not included.
-type AuditPayload struct {
-	// Part-A
-	EnvVer                 float64 `json:"env_ver"`
-	EnvName                string  `json:"env_name"`
-	EnvTime                string  `json:"env_time"`
-	EnvEpoch               string  `json:"env_epoch,omitempty"`
-	EnvSeqNum              uint64  `json:"env_seqNum,omitempty"`
-	EnvIKey                string  `json:"env_iKey,omitempty"`
-	EnvFlags               int     `json:"env_flags,omitempty"`
-	EnvAppId               string  `json:"env_appId"`
-	EnvAppVer              string  `json:"env_appVer,omitempty"`
-	EnvCV                  string  `json:"env_cv,omitempty"`
-	EnvCloudName           string  `json:"env_cloud_name"`
-	EnvCloudRole           string  `json:"env_cloud_role"`
-	EnvCloudRoleVer        string  `json:"env_cloud_roleVer,omitempty"`
-	EnvCloudRoleInstance   string  `json:"env_cloud_roleInstance"`
-	EnvCloudEnvironment    string  `json:"env_cloud_environment,omitempty"`
-	EnvCloudLocation       string  `json:"env_cloud_location"`
-	EnvCloudDeploymentUnit string  `json:"env_cloud_deploymentUnit,omitempty"`
-	EnvCloudVer            float64 `json:"env_cloud_ver"`
-
-	// Part-B
-	CallerIdentities []*CallerIdentity `json:"CallerIdentities"`
-	Category         Category          `json:"Category"`
-	OperationName    string            `json:"OperationName"`
-	Result           *Result           `json:"Result"`
-	RequestID        string            `json:"requestId"`
-	TargetResources  []*TargetResource `json:"TargetResources"`
-}
-
-// CallerIdentity has identity information on the entity that invoke the
-// operation described in the audit log.
-type CallerIdentity struct {
-	CallerDisplayName   string             `json:"CallerDisplayName,omitempty"`
-	CallerIdentityType  CallerIdentityType `json:"CallerIdentityType"`
-	CallerIdentityValue string             `json:"CallerIdentityValue"`
-	CallerIPAddress     string             `json:"CallerIpAddress,omitempty"`
-}
-
-// CallerIdentityType represents the type of identity used in an auditable event.
-type CallerIdentityType string
-
-// Category provides information for the category of the operation.
-type Category string
-
-// Result provides information on the result of the operation.
-type Result struct {
-	ResultType        string `json:"ResultType"`
-	ResultDescription string `json:"ResultDescription,omitempty"`
-}
-
-// ResultType indicates the outcome of the operation.
-type ResultType string
-
-// TargetResource has identity information on the entity affected by the
-// operation described in the audit log.
-type TargetResource struct {
-	TargetResourceType string `json:"TargetResourceType"`
-	TargetResourceName string `json:"TargetResourceName"`
-}
-
-const (
-	CallerIdentityTypeUPN            = "UPN"
-	CallerIdentityTypePUID           = "PUID"
-	CallerIdentityTypeObjectID       = "ObjectID"
-	CallerIdentityTypeCertificate    = "Certificate"
-	CallerIdentityTypeClaim          = "Claim"
-	CallerIdentityTypeUsername       = "Username"
-	CallerIdentityTypeKeyName        = "KeyName"
-	CallerIdentityTypeApplicationID  = "ApplicationID"
-	CallerIdentityTypeSubscriptionID = "SubscriptionID"
-
-	CategoryAuthentication        = "Authentication"
-	CategoryAuthorization         = "Authorization"
-	CategoryUserManagement        = "UserManagement"
-	CategoryGroupManagement       = "GroupManagement"
-	CategoryRoleManagement        = "RoleManagement"
-	CategoryApplicationManagement = "ApplicationManagement"
-	CategoryKeyManagement         = "KeyManagement"
-	CategoryDirectoryManagement   = "DirectoryManagement"
-	CategoryResourceManagement    = "ResourceManagement"
-	CategoryPolicyManagement      = "PolicyManagement"
-	CategoryDeviceManagement      = "DeviceManagement"
-	CategoryEntitlementManagement = "EntitlementManagement"
-	CategoryPasswordManagement    = "PasswordManagement"
-	CategoryObjectManagement      = "ObjectManagement"
-	CategoryIdentityProtection    = "IdentityProtection"
-	CategoryOther                 = "Other"
-
-	ResultTypeSuccess     = "Success"
-	ResultTypeFail        = "Fail"
-	ResultTypeTimeout     = "Timeout"
-	ResultTypeClientError = "Client Error"
-)
diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go
index ff2a58535..a4a45ddff 100644
--- a/pkg/util/log/log.go
+++ b/pkg/util/log/log.go
@@ -29,33 +29,21 @@ var (
 	rxTolerantResourceID = regexp.MustCompile(`(?i)^(?:/admin)?/subscriptions/([^/]+)(?:/resourceGroups/([^/]+)(?:/providers/([^/]+)/([^/]+)(?:/([^/]+))?)?)?`)
 )
 
-func GetBaseLogger() *logrus.Logger {
-	logger := logrus.New()
-
-	logger.SetFormatter(&logrus.TextFormatter{
-		FullTimestamp: true,
-	})
-
-	if journal.Enabled() {
-		logger.AddHook(&journaldHook{})
-	}
-
-	return logger
-}
-
 // GetLogger returns a consistently configured log entry
 func GetLogger() *logrus.Entry {
-	logger := GetBaseLogger()
-
-	logger.SetReportCaller(true)
-	logger.SetFormatter(&logrus.TextFormatter{
+	logrus.SetReportCaller(true)
+	logrus.SetFormatter(&logrus.TextFormatter{
 		FullTimestamp:    true,
 		CallerPrettyfier: relativeFilePathPrettier,
 	})
 
-	logger.AddHook(&logrHook{})
+	logrus.AddHook(&logrHook{})
+
+	if journal.Enabled() {
+		logrus.AddHook(&journaldHook{})
+	}
 
-	log := logrus.NewEntry(logger)
+	log := logrus.NewEntry(logrus.StandardLogger())
 
 	l, err := logrus.ParseLevel(*loglevel)
 	if err == nil {
diff --git a/pkg/util/mocks/env/env.go b/pkg/util/mocks/env/env.go
index 0b491d36a..c95fb198a 100644
--- a/pkg/util/mocks/env/env.go
+++ b/pkg/util/mocks/env/env.go
@@ -72,20 +72,6 @@ func (mr *MockCoreMockRecorder) Environment() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Environment", reflect.TypeOf((*MockCore)(nil).Environment))
 }
 
-// Hostname mocks base method
-func (m *MockCore) Hostname() string {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Hostname")
-	ret0, _ := ret[0].(string)
-	return ret0
-}
-
-// Hostname indicates an expected call of Hostname
-func (mr *MockCoreMockRecorder) Hostname() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hostname", reflect.TypeOf((*MockCore)(nil).Hostname))
-}
-
 // Location mocks base method
 func (m *MockCore) Location() string {
 	m.ctrl.T.Helper()
@@ -393,20 +379,6 @@ func (mr *MockInterfaceMockRecorder) FPAuthorizer(arg0, arg1 interface{}) *gomoc
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FPAuthorizer", reflect.TypeOf((*MockInterface)(nil).FPAuthorizer), arg0, arg1)
 }
 
-// Hostname mocks base method
-func (m *MockInterface) Hostname() string {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Hostname")
-	ret0, _ := ret[0].(string)
-	return ret0
-}
-
-// Hostname indicates an expected call of Hostname
-func (mr *MockInterfaceMockRecorder) Hostname() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hostname", reflect.TypeOf((*MockInterface)(nil).Hostname))
-}
-
 // InitializeAuthorizers mocks base method
 func (m *MockInterface) InitializeAuthorizers() error {
 	m.ctrl.T.Helper()
diff --git a/pkg/util/mocks/instancemetadata/instancemetadata.go b/pkg/util/mocks/instancemetadata/instancemetadata.go
index b5bad8cb6..0d6fcf385 100644
--- a/pkg/util/mocks/instancemetadata/instancemetadata.go
+++ b/pkg/util/mocks/instancemetadata/instancemetadata.go
@@ -100,20 +100,6 @@ func (mr *MockInstanceMetadataMockRecorder) Environment() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Environment", reflect.TypeOf((*MockInstanceMetadata)(nil).Environment))
 }
 
-// Hostname mocks base method
-func (m *MockInstanceMetadata) Hostname() string {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Hostname")
-	ret0, _ := ret[0].(string)
-	return ret0
-}
-
-// Hostname indicates an expected call of Hostname
-func (mr *MockInstanceMetadataMockRecorder) Hostname() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hostname", reflect.TypeOf((*MockInstanceMetadata)(nil).Hostname))
-}
-
 // Location mocks base method
 func (m *MockInstanceMetadata) Location() string {
 	m.ctrl.T.Helper()
diff --git a/python/az/aro/azext_aro/custom.py b/python/az/aro/azext_aro/custom.py
index 1f108f6c0..ddca9caa2 100644
--- a/python/az/aro/azext_aro/custom.py
+++ b/python/az/aro/azext_aro/custom.py
@@ -311,12 +311,7 @@ def get_route_tables_from_subnets(cli_ctx, subnets):
 
 def get_cluster_network_resources(cli_ctx, oc):
     master_subnet = oc.master_profile.subnet_id
-    worker_subnets = set()
-
-    # Ensure that worker_profiles exists
-    # it will not be returned if the cluster resources do not exist
-    if oc.worker_profiles is not None:
-        worker_subnets = {w.subnet_id for w in oc.worker_profiles if w}
+    worker_subnets = {w.subnet_id for w in oc.worker_profiles}
 
     master_parts = parse_resource_id(master_subnet)
     vnet = resource_id(


Test patch:
diff --git a/pkg/metrics/statsd/statsd_test.go b/pkg/metrics/statsd/statsd_test.go
index 79f2bfa41..142b9edb0 100644
--- a/pkg/metrics/statsd/statsd_test.go
+++ b/pkg/metrics/statsd/statsd_test.go
@@ -20,7 +20,6 @@ func TestEmitGauge(t *testing.T) {
 
 	env := mock_env.NewMockInterface(controller)
 	env.EXPECT().Location().AnyTimes().Return("eastus")
-	env.EXPECT().Hostname().AnyTimes().Return("test-host")
 
 	c1, c2 := net.Pipe()
 
@@ -44,7 +43,7 @@ func TestEmitGauge(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if m != `{"Metric":"tests.test_key","Account":"*","Namespace":"*","Dims":{"hostname":"test-host","key":"value","location":"eastus"},"TS":"0001-01-01T00:00:00.000"}:42|g`+"\n" {
+	if m != `{"Metric":"tests.test_key","Account":"*","Namespace":"*","Dims":{"hostname":"","key":"value","location":"eastus"},"TS":"0001-01-01T00:00:00.000"}:42|g`+"\n" {
 		t.Error(m)
 	}
 }
@@ -55,7 +54,6 @@ func TestEmitFloat(t *testing.T) {
 
 	env := mock_env.NewMockInterface(controller)
 	env.EXPECT().Location().AnyTimes().Return("eastus")
-	env.EXPECT().Hostname().AnyTimes().Return("test-host")
 
 	c1, c2 := net.Pipe()
 
@@ -79,7 +77,7 @@ func TestEmitFloat(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if m != `{"Metric":"tests.test_key","Account":"*","Namespace":"*","Dims":{"hostname":"test-host","key":"value","location":"eastus"},"TS":"0001-01-01T00:00:00.000"}:5.000000|f`+"\n" {
+	if m != `{"Metric":"tests.test_key","Account":"*","Namespace":"*","Dims":{"hostname":"","key":"value","location":"eastus"},"TS":"0001-01-01T00:00:00.000"}:5.000000|f`+"\n" {
 		t.Error(m)
 	}
 }
diff --git a/pkg/operator/controllers/monitoring/monitoring_controller_test.go b/pkg/operator/controllers/monitoring/monitoring_controller_test.go
index 191a261f6..46bb8d96c 100644
--- a/pkg/operator/controllers/monitoring/monitoring_controller_test.go
+++ b/pkg/operator/controllers/monitoring/monitoring_controller_test.go
@@ -14,9 +14,13 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/fake"
 	ctrl "sigs.k8s.io/controller-runtime"
+
+	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
+	arofake "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/fake"
 )
 
 var cmMetadata = metav1.ObjectMeta{Name: "cluster-monitoring-config", Namespace: "openshift-monitoring"}
+var custerMetadata = metav1.ObjectMeta{Name: arov1alpha1.SingletonClusterName}
 
 func TestReconcileMonitoringConfig(t *testing.T) {
 	log := logrus.NewEntry(logrus.StandardLogger())
@@ -28,9 +32,17 @@ func TestReconcileMonitoringConfig(t *testing.T) {
 
 	for _, tt := range []*test{
 		{
-			name: "ConfigMap does not exist",
+			name: "ConfigMap does not exist - enable",
 			setConfigMap: func() *Reconciler {
 				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: true,
+							},
+						},
+					}),
 					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{}),
 					log:           log,
 					jsonHandle:    new(codec.JsonHandle),
@@ -50,6 +62,14 @@ prometheusK8s:
 			name: "ConfigMap does not have data",
 			setConfigMap: func() *Reconciler {
 				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: true,
+							},
+						},
+					}),
 					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{
 						ObjectMeta: cmMetadata,
 					}),
@@ -71,6 +91,14 @@ prometheusK8s:
 			name: "empty config.yaml",
 			setConfigMap: func() *Reconciler {
 				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: true,
+							},
+						},
+					}),
 					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{
 						ObjectMeta: cmMetadata,
 						Data: map[string]string{
@@ -95,6 +123,14 @@ prometheusK8s:
 			name: "settings restored to default and extra fields are preserved",
 			setConfigMap: func() *Reconciler {
 				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: true,
+							},
+						},
+					}),
 					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{
 						ObjectMeta: cmMetadata,
 						Data: map[string]string{
@@ -131,6 +167,14 @@ prometheusK8s:
 			name: "other monitoring components are configured",
 			setConfigMap: func() *Reconciler {
 				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: true,
+							},
+						},
+					}),
 					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{
 						ObjectMeta: cmMetadata,
 						Data: map[string]string{
@@ -156,6 +200,45 @@ prometheusK8s:
       resources:
         requests:
           storage: 100Gi
+`,
+		},
+		{
+			name: "enabled and we want to disable",
+			setConfigMap: func() *Reconciler {
+				return &Reconciler{
+					arocli: arofake.NewSimpleClientset(&arov1alpha1.Cluster{
+						ObjectMeta: custerMetadata,
+						Spec: arov1alpha1.ClusterSpec{
+							Features: arov1alpha1.FeaturesSpec{
+								PersistentPrometheus: false,
+							},
+						},
+					}),
+					kubernetescli: fake.NewSimpleClientset(&v1.ConfigMap{
+						ObjectMeta: cmMetadata,
+						Data: map[string]string{
+							"config.yaml": `
+alertmanagerMain:
+  nodeSelector:
+    foo: bar
+prometheusK8s:
+    retention: 15d
+    volumeClaimTemplate:
+      spec:
+        resources:
+          requests:
+            storage: 100Gi
+`,
+						},
+					}),
+					log:        log,
+					jsonHandle: new(codec.JsonHandle),
+				}
+			},
+			wantConfig: `
+alertmanagerMain:
+  nodeSelector:
+    foo: bar
 `,
 		},
 	} {
diff --git a/pkg/util/log/audit/audit_test.go b/pkg/util/log/audit/audit_test.go
deleted file mode 100644
index f62655dbd..000000000
--- a/pkg/util/log/audit/audit_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package audit
-
-// Copyright (c) Microsoft Corporation.
-// Licensed under the Apache License 2.0.
-
-import (
-	"testing"
-	"time"
-
-	"github.com/Azure/go-autorest/autorest/azure"
-	"github.com/golang/mock/gomock"
-	"github.com/onsi/gomega"
-	"github.com/onsi/gomega/types"
-	uuid "github.com/satori/go.uuid"
-	"github.com/sirupsen/logrus"
-	"github.com/sirupsen/logrus/hooks/test"
-
-	mock_env "github.com/Azure/ARO-RP/pkg/util/mocks/env"
-	utillog "github.com/Azure/ARO-RP/test/util/log"
-)
-
-func TestAudit(t *testing.T) {
-	controller := gomock.NewController(t)
-	defer controller.Finish()
-
-	env := mock_env.NewMockInterface(controller)
-	env.EXPECT().Environment().AnyTimes().Return(&azure.PublicCloud)
-	env.EXPECT().Location().AnyTimes().Return("eastus")
-	env.EXPECT().Hostname().AnyTimes().Return("test-host")
-
-	epoch = uuid.NewV4().String()
-	var (
-		now          = time.Now().UTC()
-		formattedNow = now.Format(time.RFC3339)
-		logger, h    = test.NewNullLogger()
-	)
-
-	auditLog := NewEntry(env, logger)
-	auditLog.WithFields(logrus.Fields{
-		PayloadKeyCategory:      CategoryAuthorization,
-		PayloadKeyOperationName: "initializeAuthorizers",
-		MetadataSource:          SourceRP,
-		MetadataCreatedTime:     formattedNow,
-	}).Print("see auditFullPayload field for full log data")
-
-	if err := utillog.AssertLoggingOutput(h, []map[string]types.GomegaMatcher{
-		{
-			"level":         gomega.Equal(logrus.InfoLevel),
-			"msg":           gomega.Equal("see auditFullPayload field for full log data"),
-			MetadataSource:  gomega.Equal("aro-rp"),
-			MetadataLogKind: gomega.Equal("ifxaudit"),
-			MetadataCreatedTime: gomega.WithTransform(
-				func(s string) time.Time {
-					t, err := time.Parse(time.RFC3339, s)
-					if err != nil {
-						panic(err)
-					}
-					return t
-				},
-				gomega.BeTemporally("~", now, time.Second),
-			),
-			MetadataPayload: gomega.Equal(`{"env_ver":2.1,"env_name":"#Ifx.AuditSchema","env_time":"` + formattedNow + `","env_epoch":"` + epoch + `","env_seqNum":1,"env_flags":257,"env_appId":"","env_cloud_name":"AzurePublicCloud","env_cloud_role":"","env_cloud_roleInstance":"test-host","env_cloud_environment":"AzurePublicCloud","env_cloud_location":"eastus","env_cloud_ver":1,"CallerIdentities":null,"Category":"Authorization","OperationName":"initializeAuthorizers","Result":null,"requestId":"","TargetResources":null}`),
-		},
-	}); err != nil {
-		t.Error(err)
-	}
-}