From dc193493f73b97ce1098be2db1acc82a7913810a Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Wed, 8 Dec 2021 12:26:24 +0200
Subject: [PATCH 1/6] chore: Refactoring

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 controllers/che/checluster_controller.go      | 184 +----
 controllers/che/checluster_controller_test.go | 670 +++---------------
 controllers/che/create.go                     | 261 -------
 mocks/permission_checker_mock.go              |  50 --
 pkg/deploy/consolelink.go                     | 104 ---
 pkg/deploy/consolelink_test.go                | 111 ---
 pkg/deploy/dashboard/dashboard.go             |  84 +--
 .../dashboard/dashboard_deployment_test.go    | 137 +---
 pkg/deploy/dashboard/dashboard_test.go        | 253 ++-----
 pkg/deploy/dashboard/deployment_dashboard.go  |  46 +-
 pkg/deploy/dashboard/rbac.go                  |   9 +-
 pkg/deploy/devfileregistry/devfileregistry.go |  81 ++-
 .../devfileregistry_configmap.go              |  10 +-
 .../devfileregistry_deployment.go             |  16 +-
 .../devfileregistry_deployment_test.go        |  21 +-
 .../devfileregistry/devfileregistry_test.go   | 154 ++--
 pkg/deploy/gateway/gateway.go                 |  22 +
 .../identity-provider/deployment_keycloak.go  |   2 +-
 .../deployment_keycloak_test.go               |   2 +-
 pkg/deploy/identity-provider/exec.go          |   2 +-
 .../identity-provider/identity_provider.go    | 377 ----------
 .../identity_provider_test.go                 | 211 ------
 pkg/deploy/identity-provider/init_test.go     |   2 +-
 .../identity-provider/keycloak_readiness.go   |   2 +-
 pkg/deploy/oauthclient.go                     |  72 --
 pkg/deploy/pluginregistry/pluginregistry.go   |  88 ++-
 .../pluginregistry_configmap.go               |  10 +-
 .../pluginregistry_deployment.go              |  16 +-
 .../pluginregistry_deployment_test.go         |  26 +-
 .../pluginregistry/pluginregistry_test.go     |  80 +--
 pkg/deploy/postgres/postgres.go               |  87 +--
 pkg/deploy/postgres/postgres_deployment.go    |  26 +-
 pkg/deploy/postgres/postgres_test.go          |  75 +-
 pkg/deploy/server/server.go                   | 334 ---------
 pkg/deploy/server/server_configmap.go         | 142 ++--
 pkg/deploy/server/server_configmap_test.go    |  73 +-
 pkg/deploy/server/server_deployment.go        |  62 +-
 pkg/deploy/server/server_deployment_test.go   |  28 +-
 pkg/deploy/server/server_test.go              | 285 --------
 pkg/deploy/test_util.go                       |   5 +-
 40 files changed, 750 insertions(+), 3470 deletions(-)
 delete mode 100644 controllers/che/create.go
 delete mode 100644 mocks/permission_checker_mock.go
 delete mode 100644 pkg/deploy/consolelink.go
 delete mode 100644 pkg/deploy/consolelink_test.go
 delete mode 100644 pkg/deploy/identity-provider/identity_provider.go
 delete mode 100644 pkg/deploy/identity-provider/identity_provider_test.go
 delete mode 100644 pkg/deploy/oauthclient.go
 delete mode 100644 pkg/deploy/server/server.go
 delete mode 100644 pkg/deploy/server/server_test.go

diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go
index a714ec2840..27fb97da51 100644
--- a/controllers/che/checluster_controller.go
+++ b/controllers/che/checluster_controller.go
@@ -17,10 +17,12 @@ import (
 	"time"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/deploy/consolelink"
 	"github.com/eclipse-che/che-operator/pkg/deploy/dashboard"
 	devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
 	"github.com/eclipse-che/che-operator/pkg/deploy/devfileregistry"
 	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
+	identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
 	imagepuller "github.com/eclipse-che/che-operator/pkg/deploy/image-puller"
 	"github.com/eclipse-che/che-operator/pkg/deploy/migration"
 	openshiftoauth "github.com/eclipse-che/che-operator/pkg/deploy/openshift-oauth"
@@ -30,7 +32,6 @@ import (
 	"github.com/eclipse-che/che-operator/pkg/deploy/server"
 	"github.com/eclipse-che/che-operator/pkg/deploy/tls"
 
-	identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
 	"github.com/eclipse-che/che-operator/pkg/util"
 	"github.com/go-logr/logr"
 	routev1 "github.com/openshift/api/route/v1"
@@ -46,7 +47,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/predicate"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 
 	orgv1 "github.com/eclipse-che/che-operator/api/v1"
@@ -71,7 +71,6 @@ type CheClusterReconciler struct {
 	// A discovery client to check for the existence of certain APIs registered
 	// in the API Server
 	discoveryClient  discovery.DiscoveryInterface
-	tests            bool
 	reconcileManager *deploy.ReconcileManager
 	// the namespace to which to limit the reconciliation. If empty, all namespaces are considered
 	namespace string
@@ -103,6 +102,19 @@ func NewReconciler(
 	reconcileManager.RegisterReconciler(rbac.NewCheServerPermissionsReconciler())
 	reconcileManager.RegisterReconciler(rbac.NewGatewayPermissionsReconciler())
 	reconcileManager.RegisterReconciler(rbac.NewWorkspacePermissionsReconciler())
+	reconcileManager.RegisterReconciler(server.NewDefaultValuesReconciler())
+
+	// we have to expose che endpoint independently of syncing other server
+	// resources since che host is used for dashboard deployment and che config map
+	reconcileManager.RegisterReconciler(server.NewCheHostReconciler())
+	reconcileManager.RegisterReconciler(postgres.NewPostgresReconciler())
+	reconcileManager.RegisterReconciler(identityprovider.NewIdentityProviderReconciler())
+	reconcileManager.RegisterReconciler(devfileregistry.NewDevfileRegistryReconciler())
+	reconcileManager.RegisterReconciler(pluginregistry.NewPluginRegistryReconciler())
+	reconcileManager.RegisterReconciler(dashboard.NewDashboardReconciler())
+	reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler())
+	reconcileManager.RegisterReconciler(server.NewServerReconciler())
+	reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
 
 	return &CheClusterReconciler{
 		Scheme: scheme,
@@ -298,177 +310,17 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
 		result, done, err := r.reconcileManager.ReconcileAll(deployContext)
 		if !done {
 			return result, err
-			// TODO: uncomment when all items added to ReconcilerManager
-			// } else {
-			// 	logrus.Info("Successfully reconciled.")
-			// 	return ctrl.Result{}, nil
+		} else {
+			logrus.Info("Successfully reconciled.")
+			return ctrl.Result{}, nil
 		}
 	} else {
 		r.reconcileManager.FinalizeAll(deployContext)
 	}
 
-	// Reconcile finalizers before CR is deleted
-	// TODO remove in favor of r.reconcileManager.FinalizeAll(deployContext)
-	r.reconcileFinalizers(deployContext)
-
-	if err := r.GenerateAndSaveFields(deployContext); err != nil {
-		_ = deploy.ReloadCheClusterCR(deployContext)
-		return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
-	}
-
-	if !deployContext.CheCluster.Spec.Database.ExternalDb {
-		postgres := postgres.NewPostgres(deployContext)
-		done, err := postgres.SyncAll()
-		if !done {
-			if err != nil {
-				logrus.Error(err)
-			}
-			return ctrl.Result{}, err
-		}
-	}
-
-	// we have to expose che endpoint independently of syncing other server
-	// resources since che host is used for dashboard deployment and che config map
-	server := server.NewServer(deployContext)
-	done, err := server.ExposeCheServiceAndEndpoint()
-	if !done {
-		if err != nil {
-			logrus.Error(err)
-		}
-		return ctrl.Result{}, err
-	}
-
-	// create and provision Keycloak related objects
-	if !checluster.Spec.Auth.ExternalIdentityProvider {
-		provisioned, err := identity_provider.SyncIdentityProviderToCluster(deployContext)
-		if !provisioned {
-			if err != nil {
-				logrus.Errorf("Error provisioning the identity provider to cluster: %v", err)
-			}
-			return ctrl.Result{}, err
-		}
-	} else {
-		keycloakURL := checluster.Spec.Auth.IdentityProviderURL
-		if checluster.Status.KeycloakURL != keycloakURL {
-			checluster.Status.KeycloakURL = keycloakURL
-			if err := deploy.UpdateCheCRStatus(deployContext, "status: Keycloak URL", keycloakURL); err != nil {
-				return reconcile.Result{}, err
-			}
-		}
-	}
-
-	devfileRegistry := devfileregistry.NewDevfileRegistry(deployContext)
-	if !checluster.Spec.Server.ExternalDevfileRegistry {
-		done, err := devfileRegistry.SyncAll()
-		if !done {
-			if err != nil {
-				logrus.Error(err)
-			}
-			return ctrl.Result{}, err
-		}
-	}
-
-	if !checluster.Spec.Server.ExternalPluginRegistry {
-		pluginRegistry := pluginregistry.NewPluginRegistry(deployContext)
-		done, err := pluginRegistry.SyncAll()
-		if !done {
-			if err != nil {
-				logrus.Error(err)
-			}
-			return ctrl.Result{}, err
-		}
-	} else {
-		if checluster.Spec.Server.PluginRegistryUrl != checluster.Status.PluginRegistryURL {
-			checluster.Status.PluginRegistryURL = checluster.Spec.Server.PluginRegistryUrl
-			if err := deploy.UpdateCheCRStatus(deployContext, "status: Plugin Registry URL", checluster.Spec.Server.PluginRegistryUrl); err != nil {
-				return reconcile.Result{}, err
-			}
-		}
-	}
-
-	d := dashboard.NewDashboard(deployContext)
-	done, err = d.Reconcile()
-	if !done {
-		if err != nil {
-			logrus.Errorf("Error provisioning '%s' to cluster: %v", d.GetComponentName(), err)
-		}
-		return ctrl.Result{}, err
-	}
-
-	err = gateway.SyncGatewayToCluster(deployContext)
-	if err != nil {
-		logrus.Errorf("Failed to create the Server Gateway: %s", err)
-		return ctrl.Result{}, err
-	}
-
-	done, err = server.SyncAll()
-	if !done {
-		if err != nil {
-			logrus.Error(err)
-		}
-		return reconcile.Result{}, err
-	}
-
-	// we can now try to create consolelink, after che instance is available
-	done, err = deploy.ReconcileConsoleLink(deployContext)
-	if !done {
-		if err != nil {
-			logrus.Error(err)
-		}
-		// We should `Requeue` since we created cluster object
-		return ctrl.Result{RequeueAfter: time.Second}, err
-	}
-
-	// Delete OpenShift identity provider if OpenShift oAuth is false in spec
-	// but OpenShiftoAuthProvisioned is true in CR status, e.g. when oAuth has been turned on and then turned off
-	deleted, err := identity_provider.ReconcileIdentityProvider(deployContext)
-	if deleted {
-		// ignore error
-		deploy.DeleteFinalizer(deployContext, deploy.OAuthFinalizerName)
-		for {
-			checluster.Status.OpenShiftoAuthProvisioned = false
-			if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "false"); err != nil &&
-				errors.IsConflict(err) {
-				_ = deploy.ReloadCheClusterCR(deployContext)
-				continue
-			}
-			break
-		}
-		for {
-			checluster.Spec.Auth.OAuthSecret = ""
-			checluster.Spec.Auth.OAuthClientName = ""
-			if err := deploy.UpdateCheCRStatus(deployContext, "clean oAuth secret name and client name", ""); err != nil &&
-				errors.IsConflict(err) {
-				_ = deploy.ReloadCheClusterCR(deployContext)
-				continue
-			}
-			break
-		}
-	}
-
-	logrus.Info("Successfully reconciled.")
 	return ctrl.Result{}, nil
 }
 
-func (r *CheClusterReconciler) reconcileFinalizers(deployContext *deploy.DeployContext) {
-	if util.IsOpenShift && deployContext.CheCluster.IsOpenShiftOAuthEnabled() {
-		if err := deploy.ReconcileOAuthClientFinalizer(deployContext); err != nil {
-			logrus.Error(err)
-		}
-	}
-
-	if err := deploy.ReconcileConsoleLinkFinalizer(deployContext); err != nil {
-		logrus.Error(err)
-	}
-
-	if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
-		done, err := dashboard.NewDashboard(deployContext).Finalize()
-		if !done {
-			logrus.Error(err)
-		}
-	}
-}
-
 func (r *CheClusterReconciler) GetCR(request ctrl.Request) (instance *orgv1.CheCluster, err error) {
 	instance = &orgv1.CheCluster{}
 	err = r.client.Get(context.TODO(), request.NamespacedName, instance)
diff --git a/controllers/che/checluster_controller_test.go b/controllers/che/checluster_controller_test.go
index efd6904b97..44e63f32fe 100644
--- a/controllers/che/checluster_controller_test.go
+++ b/controllers/che/checluster_controller_test.go
@@ -15,17 +15,11 @@ package che
 import (
 	"context"
 	"os"
-	"strconv"
 
-	"reflect"
 	"time"
 
 	chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
-	crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
-
-	devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
-	identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/util"
@@ -44,9 +38,7 @@ import (
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
-	"k8s.io/utils/pointer"
 
-	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
@@ -58,8 +50,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	configv1 "github.com/openshift/api/config/v1"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	"testing"
@@ -69,350 +59,21 @@ var (
 	namespace = "eclipse-che"
 )
 
-func TestNativeUserModeEnabled(t *testing.T) {
-	type testCase struct {
-		name                    string
-		initObjects             []runtime.Object
-		isOpenshift             bool
-		devworkspaceEnabled     bool
-		initialNativeUserValue  *bool
-		expectedNativeUserValue *bool
-	}
-
-	testCases := []testCase{
-		{
-			name:                    "che-operator should use nativeUserMode when devworkspaces on openshift and no initial value in CR for nativeUserMode",
-			isOpenshift:             true,
-			devworkspaceEnabled:     true,
-			initialNativeUserValue:  nil,
-			expectedNativeUserValue: pointer.BoolPtr(true),
-		},
-		{
-			name:                    "che-operator should use nativeUserMode value from initial CR",
-			isOpenshift:             true,
-			devworkspaceEnabled:     true,
-			initialNativeUserValue:  pointer.BoolPtr(false),
-			expectedNativeUserValue: pointer.BoolPtr(false),
-		},
-		{
-			name:                    "che-operator should use nativeUserMode value from initial CR",
-			isOpenshift:             true,
-			devworkspaceEnabled:     true,
-			initialNativeUserValue:  pointer.BoolPtr(true),
-			expectedNativeUserValue: pointer.BoolPtr(true),
-		},
-		{
-			name:                    "che-operator should use nativeUserMode when devworkspaces on kubernetes and no initial value in CR for nativeUserMode",
-			isOpenshift:             false,
-			devworkspaceEnabled:     true,
-			initialNativeUserValue:  nil,
-			expectedNativeUserValue: pointer.BoolPtr(true),
-		},
-		{
-			name:                    "che-operator not modify nativeUserMode when devworkspace not enabled",
-			isOpenshift:             true,
-			devworkspaceEnabled:     false,
-			initialNativeUserValue:  nil,
-			expectedNativeUserValue: nil,
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-
-			scheme := scheme.Scheme
-			orgv1.SchemeBuilder.AddToScheme(scheme)
-			scheme.AddKnownTypes(routev1.GroupVersion, &routev1.Route{})
-			scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, &oauthv1.OAuthClient{})
-			scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Proxy{})
-			scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{})
-
-			initCR := InitCheWithSimpleCR().DeepCopy()
-			initCR.Spec.DevWorkspace.Enable = testCase.devworkspaceEnabled
-			initCR.Spec.Auth.NativeUserMode = testCase.initialNativeUserValue
-			testCase.initObjects = append(testCase.initObjects, initCR)
-
-			util.IsOpenShift = testCase.isOpenshift
-
-			// reread templates (workaround after setting IsOpenShift value)
-			devworkspace.DevWorkspaceTemplates = devworkspace.DevWorkspaceTemplatesPath()
-			devworkspace.DevWorkspaceIssuerFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml"
-			devworkspace.DevWorkspaceCertificateFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-serving-cert.Certificate.yaml"
-
-			cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			nonCachedClient := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			clientSet := fakeclientset.NewSimpleClientset()
-			fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
-			fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}
-
-			if !ok {
-				t.Fatal("Error creating fake discovery client")
-			}
-
-			r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
-			r.tests = true
-
-			req := reconcile.Request{
-				NamespacedName: types.NamespacedName{
-					Name:      os.Getenv("CHE_FLAVOR"),
-					Namespace: namespace,
-				},
-			}
-
-			_, err := r.Reconcile(context.TODO(), req)
-			if err != nil {
-				t.Fatalf("Error reconciling: %v", err)
-			}
-			cr := &orgv1.CheCluster{}
-			if err := r.client.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cr); err != nil {
-				t.Errorf("CR not found")
-			}
-
-			if !reflect.DeepEqual(testCase.expectedNativeUserValue, cr.Spec.Auth.NativeUserMode) {
-				expectedValue, actualValue := "nil", "nil"
-				if testCase.expectedNativeUserValue != nil {
-					expectedValue = strconv.FormatBool(*testCase.expectedNativeUserValue)
-				}
-				if cr.Spec.Auth.NativeUserMode != nil {
-					actualValue = strconv.FormatBool(*cr.Spec.Auth.NativeUserMode)
-				}
-
-				t.Errorf("Expected nativeUserMode '%+v', but found '%+v' for input '%+v'",
-					expectedValue, actualValue, testCase)
-			}
-		})
-	}
-}
-
-func TestEnsureServerExposureStrategy(t *testing.T) {
-	type testCase struct {
-		name                string
-		expectedCr          *orgv1.CheCluster
-		devWorkspaceEnabled bool
-		initObjects         []runtime.Object
-	}
-
-	testCases := []testCase{
-		{
-			name: "Single Host should be enabled if devWorkspace is enabled",
-			expectedCr: &orgv1.CheCluster{
-				Spec: orgv1.CheClusterSpec{
-					Server: orgv1.CheClusterSpecServer{
-						ServerExposureStrategy: "single-host",
-					},
-				},
-			},
-			devWorkspaceEnabled: true,
-		},
-		{
-			name: "Multi Host should be enabled if devWorkspace is not enabled",
-			expectedCr: &orgv1.CheCluster{
-				Spec: orgv1.CheClusterSpec{
-					Server: orgv1.CheClusterSpecServer{
-						ServerExposureStrategy: "multi-host",
-					},
-				},
-			},
-			devWorkspaceEnabled: false,
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-
-			scheme := scheme.Scheme
-			orgv1.SchemeBuilder.AddToScheme(scheme)
-			initCR := InitCheWithSimpleCR().DeepCopy()
-			testCase.initObjects = append(testCase.initObjects, initCR)
-			if testCase.devWorkspaceEnabled {
-				initCR.Spec.DevWorkspace.Enable = true
-			}
-			cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			nonCachedClient := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			clientSet := fakeclientset.NewSimpleClientset()
-			fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
-			fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}
-
-			if !ok {
-				t.Fatal("Error creating fake discovery client")
-			}
-
-			r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
-			r.tests = true
-
-			req := reconcile.Request{
-				NamespacedName: types.NamespacedName{
-					Name:      os.Getenv("CHE_FLAVOR"),
-					Namespace: namespace,
-				},
-			}
-
-			util.IsOpenShift = true
-			util.IsOpenShift4 = false
-			_, err := r.Reconcile(context.TODO(), req)
-			if err != nil {
-				t.Fatalf("Error reconciling: %v", err)
-			}
-			cr := &orgv1.CheCluster{}
-			if err := r.client.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cr); err != nil {
-				t.Errorf("CR not found")
-			}
-			if !reflect.DeepEqual(testCase.expectedCr.Spec.Server.ServerExposureStrategy, cr.Spec.Server.ServerExposureStrategy) {
-				t.Errorf("Expected CR and CR returned from API server are different (-want +got): %v", cmp.Diff(testCase.expectedCr.Spec.Server.ServerExposureStrategy, cr.Spec.Server.ServerExposureStrategy))
-			}
-		})
-	}
-}
-
-func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
-	type testCase struct {
-		name                       string
-		isOpenShift                bool
-		isOpenShift4               bool
-		initObjects                []runtime.Object
-		cheCluster                 *orgv1.CheCluster
-		expectedDevfileRegistryURL string
-	}
-
-	testCases := []testCase{
-		{
-			name: "Test Status.DevfileRegistryURL #1",
-			cheCluster: &orgv1.CheCluster{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "CheCluster",
-					APIVersion: "org.eclipse.che/v1",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "eclipse-che",
-					Name:      os.Getenv("CHE_FLAVOR"),
-				},
-				Spec: orgv1.CheClusterSpec{
-					Server: orgv1.CheClusterSpecServer{
-						ExternalDevfileRegistry: false,
-					},
-				},
-			},
-			expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./",
-		},
-		{
-			name: "Test Status.DevfileRegistryURL #2",
-			cheCluster: &orgv1.CheCluster{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "CheCluster",
-					APIVersion: "org.eclipse.che/v1",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "eclipse-che",
-					Name:      os.Getenv("CHE_FLAVOR"),
-				},
-				Spec: orgv1.CheClusterSpec{
-					Server: orgv1.CheClusterSpecServer{
-						ExternalDevfileRegistry: false,
-						DevfileRegistryUrl:      "https://devfile-registry.external.1",
-						ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
-							{Url: "https://devfile-registry.external.2"},
-						},
-					},
-				},
-			},
-			expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./",
-		},
-		{
-			name: "Test Status.DevfileRegistryURL #2",
-			cheCluster: &orgv1.CheCluster{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "CheCluster",
-					APIVersion: "org.eclipse.che/v1",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Namespace: "eclipse-che",
-					Name:      os.Getenv("CHE_FLAVOR"),
-				},
-				Spec: orgv1.CheClusterSpec{
-					Server: orgv1.CheClusterSpecServer{
-						ExternalDevfileRegistry: true,
-						DevfileRegistryUrl:      "https://devfile-registry.external.1",
-						ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
-							{Url: "https://devfile-registry.external.2"},
-						},
-					},
-				},
-			},
-			expectedDevfileRegistryURL: "",
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-
-			scheme := scheme.Scheme
-			orgv1.SchemeBuilder.AddToScheme(scheme)
-			testCase.initObjects = append(testCase.initObjects, testCase.cheCluster)
-			cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			nonCachedClient := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...)
-			clientSet := fakeclientset.NewSimpleClientset()
-			fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
-			if !ok {
-				t.Fatal("Error creating fake discovery client")
-			}
-			fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{}
-
-			r := NewReconciler(cli, nonCachedClient, fakeDiscovery, scheme, "")
-			r.tests = true
-
-			req := reconcile.Request{
-				NamespacedName: types.NamespacedName{
-					Name:      os.Getenv("CHE_FLAVOR"),
-					Namespace: namespace,
-				},
-			}
-
-			util.IsOpenShift = testCase.isOpenShift
-			util.IsOpenShift4 = testCase.isOpenShift4
-
-			_, err := r.Reconcile(context.TODO(), req)
-			if err != nil {
-				t.Fatalf("Error reconciling: %v", err)
-			}
-
-			cr := &orgv1.CheCluster{}
-			if err := r.client.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cr); err != nil {
-				t.Errorf("CR not found")
-			}
-
-			if cr.Status.DevfileRegistryURL != testCase.expectedDevfileRegistryURL {
-				t.Fatalf("Exected: %s, but found: %s", testCase.expectedDevfileRegistryURL, cr.Status.DevfileRegistryURL)
-			}
-		})
-	}
-}
-
 func TestCheController(t *testing.T) {
 	var err error
 
 	util.IsOpenShift = true
-	util.IsOpenShift4 = false
+	util.IsOpenShift4 = true
 
 	cl, dc, scheme := Init()
 
 	// Create a ReconcileChe object with the scheme and fake client
 	r := NewReconciler(cl, cl, dc, &scheme, "")
-	r.tests = true
 
 	// get CR
-	cheCR := &orgv1.CheCluster{
-		Spec: orgv1.CheClusterSpec{
-			Server: orgv1.CheClusterSpecServer{
-				CheHost: "eclipse.org",
-			},
-		},
-	}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cheCR); err != nil {
-		t.Errorf("CR not found")
-	}
+	checluster := &orgv1.CheCluster{}
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
+	assert.Nil(t, err)
 
 	// Mock request to simulate Reconcile() being called on an event for a
 	// watched resource .
@@ -423,270 +84,123 @@ func TestCheController(t *testing.T) {
 		},
 	}
 
-	reconcileLoops := 4
-	for i := 0; i < reconcileLoops; i++ {
-		_, err = r.Reconcile(context.TODO(), req)
-		if err != nil {
-			t.Fatalf("reconcile: (%v)", err)
-		}
-	}
-
-	// get devfile-registry configmap
-	devfilecm := &corev1.ConfigMap{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DevfileRegistryName, Namespace: cheCR.Namespace}, devfilecm); err != nil {
-		t.Errorf("ConfigMap %s not found: %s", devfilecm.Name, err)
-	}
+	_, err = r.Reconcile(context.TODO(), req)
+	assert.Nil(t, err)
 
-	// get plugin-registry configmap
-	pluginRegistrycm := &corev1.ConfigMap{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DevfileRegistryName, Namespace: cheCR.Namespace}, pluginRegistrycm); err != nil {
-		t.Errorf("ConfigMap %s not found: %s", pluginRegistrycm.Name, err)
-	}
+	assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.DevfileRegistryName, Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
+	assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.PluginRegistryName, Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
 
-	// get CR
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cheCR); err != nil {
-		t.Errorf("CR not found")
-	}
+	// reade checluster
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
+	assert.Nil(t, err)
 
 	// update CR and make sure Che configmap has been updated
-	cheCR.Spec.Server.TlsSupport = true
-	if err := cl.Update(context.TODO(), cheCR); err != nil {
-		t.Error("Failed to update CheCluster custom resource")
-	}
+	checluster.Spec.Server.TlsSupport = true
+	err = cl.Update(context.TODO(), checluster)
+	assert.Nil(t, err)
 
-	// reconcile again
+	// reconcile several times
+	reconcileLoops := 4
 	for i := 0; i < reconcileLoops; i++ {
 		_, err = r.Reconcile(context.TODO(), req)
-		if err != nil {
-			t.Fatalf("reconcile: (%v)", err)
-		}
+		assert.Nil(t, err)
 	}
 
 	// get configmap
 	cm := &corev1.ConfigMap{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: cheCR.Namespace}, cm); err != nil {
-		t.Errorf("ConfigMap %s not found: %s", cm.Name, err)
-	}
-
-	customCm := &corev1.ConfigMap{}
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: checluster.Namespace}, cm)
+	assert.Nil(t, err)
+	assert.Equal(t, cm.Data["CHE_INFRA_OPENSHIFT_TLS__ENABLED"], "true")
 
 	// Custom ConfigMap should be gone
-	err = cl.Get(context.TODO(), types.NamespacedName{Name: "custom", Namespace: cheCR.Namespace}, customCm)
-	if !errors.IsNotFound(err) {
-		t.Errorf("Custom config map should be deleted and merged with Che ConfigMap")
-	}
+	assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: "custom", Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
 
 	// Get the custom role binding that should have been created for the role we passed in
-	rb := &rbacv1.RoleBinding{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: "che-workspace-custom", Namespace: cheCR.Namespace}, rb); err != nil {
-		t.Errorf("Custom role binding %s not found: %s", rb.Name, err)
-	}
+	assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: "che-workspace-custom", Namespace: checluster.Namespace}, &rbacv1.RoleBinding{}))
 
-	// run a few checks to make sure the operator reconciled tls routes and updated configmap
-	if cm.Data["CHE_INFRA_OPENSHIFT_TLS__ENABLED"] != "true" {
-		// If the test fails here without obvious reason, it could mean that there was not enought reconcile loops before.
-		// To fix the above problem, just increase reconcileLoops variable above.
-		t.Errorf("ConfigMap wasn't updated. Expecting true, but got: %s", cm.Data["CHE_INFRA_OPENSHIFT_TLS__ENABLED"])
-	}
 	route := &routev1.Route{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultCheFlavor(cheCR), Namespace: cheCR.Namespace}, route); err != nil {
-		t.Errorf("Route %s not found: %s", cm.Name, err)
-	}
-	if route.Spec.TLS.Termination != "edge" {
-		t.Errorf("Test failed as %s %s is not a TLS route", route.Kind, route.Name)
-	}
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultCheFlavor(checluster), Namespace: checluster.Namespace}, route)
+	assert.Nil(t, err)
+	assert.Equal(t, route.Spec.TLS.Termination, routev1.TLSTerminationType("edge"))
 
-	// get CR
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cheCR); err != nil {
-		t.Errorf("CR not found")
-	}
+	// reread checluster
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
+	assert.Nil(t, err)
 
 	// update CR and make sure Che configmap has been updated
-	cheCR.Spec.Auth.OpenShiftoAuth = util.NewBoolPointer(true)
-	if err := cl.Update(context.TODO(), cheCR); err != nil {
-		t.Error("Failed to update CheCluster custom resource")
-	}
+	checluster.Spec.Auth.OpenShiftoAuth = util.NewBoolPointer(true)
+	err = cl.Update(context.TODO(), checluster)
+	assert.Nil(t, err)
 
 	_, err = r.Reconcile(context.TODO(), req)
-	if err != nil {
-		t.Fatalf("reconcile: (%v)", err)
-	}
+	assert.Nil(t, err)
 
 	// get configmap and check if identity provider name and workspace project name are correctly set
 	cm = &corev1.ConfigMap{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: cheCR.Namespace}, cm); err != nil {
-		t.Errorf("ConfigMap %s not found: %s", cm.Name, err)
-	}
-
-	_, isOpenshiftv4, err := util.DetectOpenShift()
-	if err != nil {
-		logrus.Errorf("Error detecting openshift version: %v", err)
-	}
-	expectedIdentityProviderName := "openshift-v3"
-	if isOpenshiftv4 {
-		expectedIdentityProviderName = "openshift-v4"
-	}
-
-	if cm.Data["CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"] != expectedIdentityProviderName {
-		t.Errorf("ConfigMap wasn't updated properly. Expecting '%s', got: '%s'", expectedIdentityProviderName, cm.Data["CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"])
-	}
-
-	clusterAPI := deploy.ClusterAPI{
-		Client:           r.client,
-		NonCachingClient: r.client,
-		Scheme:           r.Scheme,
-	}
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: checluster.Namespace}, cm)
+	assert.Nil(t, err)
+	assert.Equal(t, cm.Data["CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"], "openshift-v4")
 
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCR,
-		ClusterAPI: clusterAPI,
-	}
-
-	if err = r.client.Get(context.TODO(), types.NamespacedName{Name: cheCR.Name, Namespace: cheCR.Namespace}, cheCR); err != nil {
-		t.Errorf("Failed to get the Che custom resource %s: %s", cheCR.Name, err)
-	}
-	if _, err = identity_provider.SyncOpenShiftIdentityProviderItems(deployContext); err != nil {
-		t.Errorf("Failed to create the items for the identity provider: %s", err)
-	}
-	oAuthClientName := cheCR.Spec.Auth.OAuthClientName
-	oauthSecret := cheCR.Spec.Auth.OAuthSecret
-	oAuthClient := &oauthv1.OAuthClient{}
-	if err = r.client.Get(context.TODO(), types.NamespacedName{Name: oAuthClientName, Namespace: ""}, oAuthClient); err != nil {
-		t.Errorf("Failed to Get oAuthClient %s: %s", oAuthClient.Name, err)
-	}
-	if oAuthClient.Secret != oauthSecret {
-		t.Errorf("Secrets do not match. Expecting %s, got %s", oauthSecret, oAuthClient.Secret)
-	}
+	// reread checluster
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
+	assert.Nil(t, err)
+	assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: checluster.Spec.Auth.OAuthClientName}, &oauthv1.OAuthClient{}))
 
 	// check if a new Postgres deployment is not created when spec.Database.ExternalDB is true
-	cheCR.Spec.Database.ExternalDb = true
-	if err := cl.Update(context.TODO(), cheCR); err != nil {
-		t.Error("Failed to update CheCluster custom resource")
-	}
+	checluster.Spec.Database.ExternalDb = true
+	err = cl.Update(context.TODO(), checluster)
+	assert.Nil(t, err)
+
 	postgresDeployment := &appsv1.Deployment{}
-	err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: cheCR.Namespace}, postgresDeployment)
+	err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: checluster.Namespace}, postgresDeployment)
+	assert.Nil(t, err)
+
 	err = r.client.Delete(context.TODO(), postgresDeployment)
+	assert.Nil(t, err)
+
 	_, err = r.Reconcile(context.TODO(), req)
-	if err != nil {
-		t.Fatalf("reconcile: (%v)", err)
-	}
-	err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: cheCR.Namespace}, postgresDeployment)
-	if err == nil {
-		t.Fatalf("Deployment postgres shoud not exist")
-	}
+	assert.Nil(t, err)
+
+	assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.PostgresName, Namespace: checluster.Namespace}, &appsv1.Deployment{}))
 
 	// check of storageClassName ends up in pvc spec
 	fakeStorageClassName := "fake-storage-class-name"
-	cheCR.Spec.Storage.PostgresPVCStorageClassName = fakeStorageClassName
-	cheCR.Spec.Database.ExternalDb = false
-	if err := r.client.Update(context.TODO(), cheCR); err != nil {
-		t.Fatalf("Failed to update %s CR: %s", cheCR.Name, err)
-	}
+	checluster.Spec.Storage.PostgresPVCStorageClassName = fakeStorageClassName
+	checluster.Spec.Database.ExternalDb = false
+	err = r.client.Update(context.TODO(), checluster)
+	assert.Nil(t, err)
+
 	pvc := &corev1.PersistentVolumeClaim{}
-	if err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: cheCR.Namespace}, pvc); err != nil {
-		t.Fatalf("Failed to get PVC: %s", err)
-	}
-	if err = r.client.Delete(context.TODO(), pvc); err != nil {
-		t.Fatalf("Failed to delete PVC %s: %s", pvc.Name, err)
-	}
+	err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: checluster.Namespace}, pvc)
+	assert.Nil(t, err)
+
+	err = r.client.Delete(context.TODO(), pvc)
+	assert.Nil(t, err)
+
 	_, err = r.Reconcile(context.TODO(), req)
-	if err != nil {
-		t.Fatalf("reconcile: (%v)", err)
-	}
+	assert.Nil(t, err)
+
 	pvc = &corev1.PersistentVolumeClaim{}
-	if err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: cheCR.Namespace}, pvc); err != nil {
-		t.Fatalf("Failed to get PVC: %s", err)
-	}
-	actualStorageClassName := pvc.Spec.StorageClassName
-	if len(*actualStorageClassName) != len(fakeStorageClassName) {
-		t.Fatalf("Expecting %s storageClassName, got %s", fakeStorageClassName, *actualStorageClassName)
-	}
+	err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: checluster.Namespace}, pvc)
+	assert.Nil(t, err)
+	assert.Equal(t, fakeStorageClassName, *pvc.Spec.StorageClassName)
 
-	// Get CheCR one more time to get it with newer Che url in the status.
-	r.client.Get(context.TODO(), types.NamespacedName{Name: cheCR.GetName(), Namespace: cheCR.GetNamespace()}, cheCR)
-	if err != nil {
-		t.Fatalf("Failed to get custom resource Eclipse Che: %s", err.Error())
-	}
-	if cheCR.Status.CheURL != "https://eclipse.org" {
-		t.Fatalf("Expected che host url in the custom resource status: %s, but got %s", "https://eclipse.org", cheCR.Status.CheURL)
-	}
+	// reread checluster
+	err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
+	assert.Nil(t, err)
+	assert.Equal(t, "https://eclipse.org", checluster.Status.CheURL)
 
 	// check if oAuthClient is deleted after CR is deleted (finalizer logic)
 	// since fake api does not set deletion timestamp, CR is updated in tests rather than deleted
-	logrus.Info("Updating CR with deletion timestamp")
 	deletionTimestamp := &metav1.Time{Time: time.Now()}
-	cheCR.DeletionTimestamp = deletionTimestamp
-	if err := r.client.Update(context.TODO(), cheCR); err != nil {
-		t.Fatalf("Failed to update CR: %s", err)
-	}
-	if err := deploy.ReconcileOAuthClientFinalizer(deployContext); err != nil {
-		t.Fatal("Failed to reconcile oAuthClient")
-	}
-	oauthClientName := cheCR.Spec.Auth.OAuthClientName
-	oauthClient := &oauthv1.OAuthClient{}
-	err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Name: oAuthClientName}, oauthClient)
-	if err == nil {
-		t.Fatalf("OauthClient %s has not been deleted", oauthClientName)
-	}
-	logrus.Infof("Disregard the error above. OauthClient %s has been deleted", oauthClientName)
-}
-
-func TestConfiguringLabelsForRoutes(t *testing.T) {
-	util.IsOpenShift = true
-	// Set the logger to development mode for verbose logs.
-	logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-
-	cl, dc, scheme := Init()
-
-	// Create a ReconcileChe object with the scheme and fake client
-	r := NewReconciler(cl, cl, dc, &scheme, "")
-	r.tests = true
-
-	// get CR
-	cheCR := &orgv1.CheCluster{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cheCR); err != nil {
-		t.Errorf("CR not found")
-	}
+	checluster.DeletionTimestamp = deletionTimestamp
+	err = r.client.Update(context.TODO(), checluster)
+	assert.Nil(t, err)
 
-	// Mock request to simulate Reconcile() being called on an event for a
-	// watched resource .
-	req := reconcile.Request{
-		NamespacedName: types.NamespacedName{
-			Name:      os.Getenv("CHE_FLAVOR"),
-			Namespace: namespace,
-		},
-	}
-
-	// reconcile
-	_, err := r.Reconcile(context.TODO(), req)
-	if err != nil {
-		t.Fatalf("reconcile: (%v)", err)
-	}
-
-	if err := r.client.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, cheCR); err != nil {
-		t.Errorf("CR not found")
-	}
-
-	cheCR.Spec.Server.CheServerRoute.Labels = "route=one"
-	if err := cl.Update(context.TODO(), cheCR); err != nil {
-		t.Error("Failed to update CheCluster custom resource")
-	}
-
-	// reconcile again
 	_, err = r.Reconcile(context.TODO(), req)
-	if err != nil {
-		t.Fatalf("reconcile: (%v)", err)
-	}
+	assert.Nil(t, err)
 
-	// get route
-	route := &routev1.Route{}
-	if err := cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultCheFlavor(cheCR), Namespace: cheCR.Namespace}, route); err != nil {
-		t.Errorf("Route %s not found: %s", route.Name, err)
-	}
-
-	if route.ObjectMeta.Labels["route"] != "one" {
-		t.Fatalf("Route '%s' does not have label '%s'", route.Name, route)
-	}
+	assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: checluster.Spec.Auth.OAuthClientName}, &oauthv1.OAuthClient{}))
 }
 
 func Init() (client.Client, discovery.DiscoveryInterface, runtime.Scheme) {
@@ -721,7 +235,18 @@ func createAPIObjects() ([]runtime.Object, discovery.DiscoveryInterface, runtime
 	}
 
 	// A CheCluster custom resource with metadata and spec
-	cheCR := InitCheWithSimpleCR()
+	cheCR := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      os.Getenv("CHE_FLAVOR"),
+			Namespace: namespace,
+		},
+		Spec: orgv1.CheClusterSpec{
+			Server: orgv1.CheClusterSpecServer{
+				CheHost:                 "eclipse.org",
+				CheWorkspaceClusterRole: "cluster-admin",
+			},
+		},
+	}
 
 	route := &routev1.Route{
 		ObjectMeta: metav1.ObjectMeta{
@@ -762,20 +287,3 @@ func createAPIObjects() ([]runtime.Object, discovery.DiscoveryInterface, runtime
 	// Create a fake client to mock API calls
 	return objs, fakeDiscovery, *scheme
 }
-
-func InitCheWithSimpleCR() *orgv1.CheCluster {
-	return &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      os.Getenv("CHE_FLAVOR"),
-			Namespace: namespace,
-		},
-		Spec: orgv1.CheClusterSpec{
-			Server: orgv1.CheClusterSpecServer{
-				CheWorkspaceClusterRole: "cluster-admin",
-			},
-			Auth: orgv1.CheClusterSpecAuth{
-				OpenShiftoAuth: pointer.BoolPtr(false),
-			},
-		},
-	}
-}
diff --git a/controllers/che/create.go b/controllers/che/create.go
deleted file mode 100644
index 1f145d1d7a..0000000000
--- a/controllers/che/create.go
+++ /dev/null
@@ -1,261 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-
-package che
-
-import (
-	"strconv"
-
-	"github.com/eclipse-che/che-operator/pkg/deploy"
-	"github.com/eclipse-che/che-operator/pkg/util"
-	"github.com/sirupsen/logrus"
-	appsv1 "k8s.io/api/apps/v1"
-)
-
-func (r *CheClusterReconciler) GenerateAndSaveFields(deployContext *deploy.DeployContext) (err error) {
-	cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster)
-	cheNamespace := deployContext.CheCluster.Namespace
-	if len(deployContext.CheCluster.Spec.Server.CheFlavor) < 1 {
-		deployContext.CheCluster.Spec.Server.CheFlavor = cheFlavor
-		if err := deploy.UpdateCheCRSpec(deployContext, "installation flavor", cheFlavor); err != nil {
-			return err
-		}
-	}
-
-	if len(deployContext.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
-		if len(deployContext.CheCluster.Spec.Database.ChePostgresUser) < 1 || len(deployContext.CheCluster.Spec.Database.ChePostgresPassword) < 1 {
-			chePostgresSecret := deploy.DefaultChePostgresSecret()
-			_, err := deploy.SyncSecretToCluster(deployContext, chePostgresSecret, cheNamespace, map[string][]byte{"user": []byte(deploy.DefaultChePostgresUser), "password": []byte(util.GeneratePasswd(12))})
-			if err != nil {
-				return err
-			}
-			deployContext.CheCluster.Spec.Database.ChePostgresSecret = chePostgresSecret
-			if err := deploy.UpdateCheCRSpec(deployContext, "Postgres Secret", chePostgresSecret); err != nil {
-				return err
-			}
-		} else {
-			if len(deployContext.CheCluster.Spec.Database.ChePostgresUser) < 1 {
-				deployContext.CheCluster.Spec.Database.ChePostgresUser = deploy.DefaultChePostgresUser
-				if err := deploy.UpdateCheCRSpec(deployContext, "Postgres User", deployContext.CheCluster.Spec.Database.ChePostgresUser); err != nil {
-					return err
-				}
-			}
-			if len(deployContext.CheCluster.Spec.Database.ChePostgresPassword) < 1 {
-				deployContext.CheCluster.Spec.Database.ChePostgresPassword = util.GeneratePasswd(12)
-				if err := deploy.UpdateCheCRSpec(deployContext, "auto-generated CheCluster DB password", "password-hidden"); err != nil {
-					return err
-				}
-			}
-		}
-	}
-	if len(deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret) < 1 {
-		keycloakPostgresPassword := util.GeneratePasswd(12)
-		keycloakDeployment := &appsv1.Deployment{}
-		exists, err := deploy.GetNamespacedObject(deployContext, deploy.IdentityProviderName, keycloakDeployment)
-		if err != nil {
-			logrus.Error(err)
-		}
-		if exists {
-			keycloakPostgresPassword = util.GetDeploymentEnv(keycloakDeployment, "DB_PASSWORD")
-		}
-
-		if len(deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresPassword) < 1 {
-			identityPostgresSecret := deploy.DefaultCheIdentityPostgresSecret()
-			_, err := deploy.SyncSecretToCluster(deployContext, identityPostgresSecret, cheNamespace, map[string][]byte{"password": []byte(keycloakPostgresPassword)})
-			if err != nil {
-				return err
-			}
-			deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret = identityPostgresSecret
-			if err := deploy.UpdateCheCRSpec(deployContext, "Identity Provider Postgres Secret", identityPostgresSecret); err != nil {
-				return err
-			}
-		}
-	}
-
-	if len(deployContext.CheCluster.Spec.Auth.IdentityProviderSecret) < 1 {
-		keycloakAdminUserName := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName, "admin")
-		keycloakAdminPassword := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderPassword, util.GeneratePasswd(12))
-
-		keycloakDeployment := &appsv1.Deployment{}
-		exists, _ := deploy.GetNamespacedObject(deployContext, deploy.IdentityProviderName, keycloakDeployment)
-		if exists {
-			keycloakAdminUserName = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_USERNAME")
-			keycloakAdminPassword = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_PASSWORD")
-		}
-
-		if len(deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 || len(deployContext.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
-			identityProviderSecret := deploy.DefaultCheIdentitySecret()
-			_, err = deploy.SyncSecretToCluster(deployContext, identityProviderSecret, cheNamespace, map[string][]byte{"user": []byte(keycloakAdminUserName), "password": []byte(keycloakAdminPassword)})
-			if err != nil {
-				return err
-			}
-			deployContext.CheCluster.Spec.Auth.IdentityProviderSecret = identityProviderSecret
-			if err := deploy.UpdateCheCRSpec(deployContext, "Identity Provider Secret", identityProviderSecret); err != nil {
-				return err
-			}
-		} else {
-			if len(deployContext.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
-				deployContext.CheCluster.Spec.Auth.IdentityProviderPassword = keycloakAdminPassword
-				if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak admin password", "password hidden"); err != nil {
-					return err
-				}
-			}
-			if len(deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 {
-				deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName = keycloakAdminUserName
-				if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak admin username", keycloakAdminUserName); err != nil {
-					return err
-				}
-			}
-		}
-	}
-
-	chePostgresDb := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresDb, "dbche")
-	if len(deployContext.CheCluster.Spec.Database.ChePostgresDb) < 1 {
-		deployContext.CheCluster.Spec.Database.ChePostgresDb = chePostgresDb
-		if err := deploy.UpdateCheCRSpec(deployContext, "Postgres DB", chePostgresDb); err != nil {
-			return err
-		}
-	}
-	chePostgresHostName := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName)
-	if len(deployContext.CheCluster.Spec.Database.ChePostgresHostName) < 1 {
-		deployContext.CheCluster.Spec.Database.ChePostgresHostName = chePostgresHostName
-		if err := deploy.UpdateCheCRSpec(deployContext, "Postgres hostname", chePostgresHostName); err != nil {
-			return err
-		}
-	}
-	chePostgresPort := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort)
-	if len(deployContext.CheCluster.Spec.Database.ChePostgresPort) < 1 {
-		deployContext.CheCluster.Spec.Database.ChePostgresPort = chePostgresPort
-		if err := deploy.UpdateCheCRSpec(deployContext, "Postgres port", chePostgresPort); err != nil {
-			return err
-		}
-	}
-
-	if !deployContext.CheCluster.IsNativeUserModeEnabled() {
-		keycloakRealm := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
-		if len(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm) < 1 {
-			deployContext.CheCluster.Spec.Auth.IdentityProviderRealm = keycloakRealm
-			if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak realm", keycloakRealm); err != nil {
-				return err
-			}
-		}
-		keycloakClientId := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
-		if len(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 {
-			deployContext.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId
-
-			if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak client ID", keycloakClientId); err != nil {
-				return err
-			}
-		}
-	}
-
-	cheLogLevel := util.GetValue(deployContext.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
-	if len(deployContext.CheCluster.Spec.Server.CheLogLevel) < 1 {
-		deployContext.CheCluster.Spec.Server.CheLogLevel = cheLogLevel
-		if err := deploy.UpdateCheCRSpec(deployContext, "log level", cheLogLevel); err != nil {
-			return err
-		}
-	}
-	cheDebug := util.GetValue(deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
-	if len(deployContext.CheCluster.Spec.Server.CheDebug) < 1 {
-		deployContext.CheCluster.Spec.Server.CheDebug = cheDebug
-		if err := deploy.UpdateCheCRSpec(deployContext, "debug", cheDebug); err != nil {
-			return err
-		}
-	}
-	pvcStrategy := util.GetValue(deployContext.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy)
-	if len(deployContext.CheCluster.Spec.Storage.PvcStrategy) < 1 {
-		deployContext.CheCluster.Spec.Storage.PvcStrategy = pvcStrategy
-		if err := deploy.UpdateCheCRSpec(deployContext, "pvc strategy", pvcStrategy); err != nil {
-			return err
-		}
-	}
-	pvcClaimSize := util.GetValue(deployContext.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize)
-	if len(deployContext.CheCluster.Spec.Storage.PvcClaimSize) < 1 {
-		deployContext.CheCluster.Spec.Storage.PvcClaimSize = pvcClaimSize
-		if err := deploy.UpdateCheCRSpec(deployContext, "pvc claim size", pvcClaimSize); err != nil {
-			return err
-		}
-	}
-
-	// This is only to correctly  manage defaults during the transition
-	// from Upstream 7.0.0 GA to the next
-	// version that should fixed bug https://github.com/eclipse/che/issues/13714
-	// Or for the transition from CRW 1.2 to 2.0
-
-	if deployContext.CheCluster.Spec.Storage.PvcJobsImage == deploy.OldDefaultPvcJobsUpstreamImageToDetect ||
-		(deploy.MigratingToCRW2_0(deployContext.CheCluster) && deployContext.CheCluster.Spec.Storage.PvcJobsImage != "") {
-		deployContext.CheCluster.Spec.Storage.PvcJobsImage = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "pvc jobs image", deployContext.CheCluster.Spec.Storage.PvcJobsImage); err != nil {
-			return err
-		}
-	}
-
-	if deployContext.CheCluster.Spec.Database.PostgresImage == deploy.OldDefaultPostgresUpstreamImageToDetect ||
-		(deploy.MigratingToCRW2_0(deployContext.CheCluster) && deployContext.CheCluster.Spec.Database.PostgresImage != "") {
-		deployContext.CheCluster.Spec.Database.PostgresImage = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "postgres image", deployContext.CheCluster.Spec.Database.PostgresImage); err != nil {
-			return err
-		}
-	}
-
-	if deployContext.CheCluster.Spec.Auth.IdentityProviderImage == deploy.OldDefaultKeycloakUpstreamImageToDetect ||
-		(deploy.MigratingToCRW2_0(deployContext.CheCluster) && deployContext.CheCluster.Spec.Auth.IdentityProviderImage != "") {
-		deployContext.CheCluster.Spec.Auth.IdentityProviderImage = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "keycloak image", deployContext.CheCluster.Spec.Auth.IdentityProviderImage); err != nil {
-			return err
-		}
-	}
-
-	if deploy.MigratingToCRW2_0(deployContext.CheCluster) &&
-		!deployContext.CheCluster.Spec.Server.ExternalPluginRegistry &&
-		deployContext.CheCluster.Spec.Server.PluginRegistryUrl == deploy.OldCrwPluginRegistryUrl {
-		deployContext.CheCluster.Spec.Server.PluginRegistryUrl = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "plugin registry url", deployContext.CheCluster.Spec.Server.PluginRegistryUrl); err != nil {
-			return err
-		}
-	}
-
-	if deploy.MigratingToCRW2_0(deployContext.CheCluster) &&
-		deployContext.CheCluster.Spec.Server.CheImage == deploy.OldDefaultCodeReadyServerImageRepo {
-		deployContext.CheCluster.Spec.Server.CheImage = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "che image repo", deployContext.CheCluster.Spec.Server.CheImage); err != nil {
-			return err
-		}
-	}
-
-	if deploy.MigratingToCRW2_0(deployContext.CheCluster) &&
-		deployContext.CheCluster.Spec.Server.CheImageTag == deploy.OldDefaultCodeReadyServerImageTag {
-		deployContext.CheCluster.Spec.Server.CheImageTag = ""
-		if err := deploy.UpdateCheCRSpec(deployContext, "che image tag", deployContext.CheCluster.Spec.Server.CheImageTag); err != nil {
-			return err
-		}
-	}
-
-	if deployContext.CheCluster.Spec.Server.ServerExposureStrategy == "" && deployContext.CheCluster.Spec.K8s.IngressStrategy == "" {
-		strategy := util.GetServerExposureStrategy(deployContext.CheCluster)
-		deployContext.CheCluster.Spec.Server.ServerExposureStrategy = strategy
-		if err := deploy.UpdateCheCRSpec(deployContext, "serverExposureStrategy", strategy); err != nil {
-			return err
-		}
-	}
-
-	if deployContext.CheCluster.Spec.DevWorkspace.Enable && deployContext.CheCluster.Spec.Auth.NativeUserMode == nil {
-		newNativeUserModeValue := util.NewBoolPointer(true)
-		deployContext.CheCluster.Spec.Auth.NativeUserMode = newNativeUserModeValue
-		if err := deploy.UpdateCheCRSpec(deployContext, "nativeUserMode", strconv.FormatBool(*newNativeUserModeValue)); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/mocks/permission_checker_mock.go b/mocks/permission_checker_mock.go
deleted file mode 100644
index fe12700d19..0000000000
--- a/mocks/permission_checker_mock.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: pkg/controller/che/permission_checker.go
-
-// Package mock_che is a generated GoMock package.
-package mock_che
-
-import (
-	reflect "reflect"
-
-	gomock "github.com/golang/mock/gomock"
-	v1 "k8s.io/api/rbac/v1"
-)
-
-// MockPermissionChecker is a mock of PermissionChecker interface
-type MockPermissionChecker struct {
-	ctrl     *gomock.Controller
-	recorder *MockPermissionCheckerMockRecorder
-}
-
-// MockPermissionCheckerMockRecorder is the mock recorder for MockPermissionChecker
-type MockPermissionCheckerMockRecorder struct {
-	mock *MockPermissionChecker
-}
-
-// NewMockPermissionChecker creates a new mock instance
-func NewMockPermissionChecker(ctrl *gomock.Controller) *MockPermissionChecker {
-	mock := &MockPermissionChecker{ctrl: ctrl}
-	mock.recorder = &MockPermissionCheckerMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockPermissionChecker) EXPECT() *MockPermissionCheckerMockRecorder {
-	return m.recorder
-}
-
-// GetNotPermittedPolicyRules mocks base method
-func (m *MockPermissionChecker) GetNotPermittedPolicyRules(policies []v1.PolicyRule, namespace string) ([]v1.PolicyRule, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetNotPermittedPolicyRules", policies, namespace)
-	ret0, _ := ret[0].([]v1.PolicyRule)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetNotPermittedPolicyRules indicates an expected call of GetNotPermittedPolicyRules
-func (mr *MockPermissionCheckerMockRecorder) GetNotPermittedPolicyRules(policies, namespace interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotPermittedPolicyRules", reflect.TypeOf((*MockPermissionChecker)(nil).GetNotPermittedPolicyRules), policies, namespace)
-}
diff --git a/pkg/deploy/consolelink.go b/pkg/deploy/consolelink.go
deleted file mode 100644
index eaf3f67353..0000000000
--- a/pkg/deploy/consolelink.go
+++ /dev/null
@@ -1,104 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package deploy
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/eclipse-che/che-operator/pkg/util"
-	consolev1 "github.com/openshift/api/console/v1"
-	"github.com/sirupsen/logrus"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-const (
-	ConsoleLinkFinalizerName = "consolelink.finalizers.che.eclipse.org"
-	ConsoleLinksResourceName = "consolelinks"
-)
-
-func ReconcileConsoleLink(deployContext *DeployContext) (bool, error) {
-	if !util.IsOpenShift4 || !util.HasK8SResourceObject(deployContext.ClusterAPI.DiscoveryClient, ConsoleLinksResourceName) {
-		// console link is supported only on OpenShift >= 4.2
-		logrus.Debug("Console link won't be created. Consolelinks is not supported by OpenShift cluster.")
-		return true, nil
-	}
-
-	if !deployContext.CheCluster.Spec.Server.TlsSupport {
-		// console link is supported only with https
-		logrus.Debug("Console link won't be created. HTTP protocol is not supported.")
-		return true, nil
-	}
-
-	if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
-		return createConsoleLink(deployContext)
-	}
-	return true, nil
-}
-
-func ReconcileConsoleLinkFinalizer(deployContext *DeployContext) error {
-	if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
-		return DeleteObjectWithFinalizer(deployContext, client.ObjectKey{Name: DefaultConsoleLinkName()}, &consolev1.ConsoleLink{}, ConsoleLinkFinalizerName)
-	}
-	return nil
-}
-
-func createConsoleLink(deployContext *DeployContext) (bool, error) {
-	consoleLinkSpec := getConsoleLinkSpec(deployContext)
-	_, err := CreateIfNotExists(deployContext, consoleLinkSpec)
-	if err != nil {
-		return false, err
-	}
-
-	consoleLink := &consolev1.ConsoleLink{}
-	exists, err := Get(deployContext, client.ObjectKey{Name: DefaultConsoleLinkName()}, consoleLink)
-	if !exists || err != nil {
-		return false, err
-	}
-
-	// consolelink is for this specific instance of Eclipse Che
-	if strings.Index(consoleLink.Spec.Link.Href, deployContext.CheCluster.Spec.Server.CheHost) != -1 {
-		err = AppendFinalizer(deployContext, ConsoleLinkFinalizerName)
-		return err == nil, err
-	}
-
-	return true, nil
-}
-
-func getConsoleLinkSpec(deployContext *DeployContext) *consolev1.ConsoleLink {
-	cheHost := deployContext.CheCluster.Spec.Server.CheHost
-	consoleLink := &consolev1.ConsoleLink{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "ConsoleLink",
-			APIVersion: consolev1.SchemeGroupVersion.String(),
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name: DefaultConsoleLinkName(),
-			Annotations: map[string]string{
-				CheEclipseOrgNamespace: deployContext.CheCluster.Namespace,
-			},
-		},
-		Spec: consolev1.ConsoleLinkSpec{
-			Link: consolev1.Link{
-				Href: "https://" + cheHost,
-				Text: DefaultConsoleLinkDisplayName()},
-			Location: consolev1.ApplicationMenu,
-			ApplicationMenu: &consolev1.ApplicationMenuSpec{
-				Section:  DefaultConsoleLinkSection(),
-				ImageURL: fmt.Sprintf("https://%s%s", cheHost, DefaultConsoleLinkImage()),
-			},
-		},
-	}
-
-	return consoleLink
-}
diff --git a/pkg/deploy/consolelink_test.go b/pkg/deploy/consolelink_test.go
deleted file mode 100644
index 832f092e23..0000000000
--- a/pkg/deploy/consolelink_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package deploy
-
-import (
-	"context"
-	"time"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
-	"github.com/eclipse-che/che-operator/pkg/util"
-	console "github.com/openshift/api/console/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	fakeDiscovery "k8s.io/client-go/discovery/fake"
-	fakeclientset "k8s.io/client-go/kubernetes/fake"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-
-	"testing"
-)
-
-func TestReconcileConsoleLink(t *testing.T) {
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      "eclipse-che",
-		},
-		Spec: orgv1.CheClusterSpec{
-			Server: orgv1.CheClusterSpecServer{
-				TlsSupport: true,
-			},
-		},
-	}
-
-	scheme := scheme.Scheme
-	scheme.AddKnownTypes(orgv1.SchemeBuilder.GroupVersion, &orgv1.CheCluster{})
-	scheme.AddKnownTypes(console.GroupVersion, &console.ConsoleLink{})
-	cli := fake.NewFakeClientWithScheme(scheme, cheCluster)
-	clientSet := fakeclientset.NewSimpleClientset()
-	fakeDiscovery, _ := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
-	fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{
-		{
-			APIResources: []metav1.APIResource{
-				{Name: ConsoleLinksResourceName},
-			},
-		},
-	}
-
-	util.IsOpenShift4 = true
-	deployContext := &DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme,
-			DiscoveryClient:  fakeDiscovery,
-		},
-	}
-
-	done, err := ReconcileConsoleLink(deployContext)
-	if !done || err != nil {
-		t.Fatalf("Failed to reconcile consolelink: %v", err)
-	}
-
-	// check consolelink object existence
-	consoleLink := &console.ConsoleLink{}
-	exists, err := Get(deployContext, types.NamespacedName{Name: DefaultConsoleLinkName()}, consoleLink)
-	if !exists || err != nil {
-		t.Fatalf("Failed to get consolelink: %v", err)
-	}
-
-	// check finalizer
-	c := &orgv1.CheCluster{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Namespace: "eclipse-che", Name: "eclipse-che"}, c)
-	if err != nil {
-		t.Fatalf("Failed to get checluster: %v", err)
-	}
-	if !util.ContainsString(c.ObjectMeta.Finalizers, ConsoleLinkFinalizerName) {
-		t.Fatalf("Failed to add finalizer")
-	}
-
-	// Initialize DeletionTimestamp => checluster is being deleted
-	cheCluster.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()}
-	err = ReconcileConsoleLinkFinalizer(deployContext)
-	if err != nil {
-		t.Fatalf("Failed to reconcile consolelink: %v", err)
-	}
-
-	// check consolelink object existence
-	exists, err = Get(deployContext, types.NamespacedName{Name: DefaultConsoleLinkName()}, consoleLink)
-	if exists || err != nil {
-		t.Fatalf("Failed to remove consolelink")
-	}
-
-	// check finalizer
-	c = &orgv1.CheCluster{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Namespace: "eclipse-che", Name: "eclipse-che"}, c)
-	if !errors.IsNotFound(err) {
-		t.Fatalf("Failed to get checluster: %v", err)
-	}
-}
diff --git a/pkg/deploy/dashboard/dashboard.go b/pkg/deploy/dashboard/dashboard.go
index 394fd957d1..203972090c 100644
--- a/pkg/deploy/dashboard/dashboard.go
+++ b/pkg/deploy/dashboard/dashboard.go
@@ -16,6 +16,7 @@ import (
 	"fmt"
 
 	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/deploy/expose"
@@ -33,97 +34,98 @@ var (
 	log = ctrl.Log.WithName("dashboard")
 )
 
-type Dashboard struct {
-	deployContext *deploy.DeployContext
-	component     string
+type DashboardReconciler struct {
+	deploy.Reconcilable
 }
 
-func NewDashboard(deployContext *deploy.DeployContext) *Dashboard {
-	return &Dashboard{
-		deployContext: deployContext,
-		component:     deploy.DefaultCheFlavor(deployContext.CheCluster) + "-dashboard",
-	}
+func NewDashboardReconciler() *DashboardReconciler {
+	return &DashboardReconciler{}
 }
 
-func (d *Dashboard) GetComponentName() string {
-	return d.component
+func (d *DashboardReconciler) getComponentName(ctx *deploy.DeployContext) string {
+	return deploy.DefaultCheFlavor(ctx.CheCluster) + "-dashboard"
 }
 
-func (d *Dashboard) Reconcile() (done bool, err error) {
+func (d *DashboardReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
 	// Create a new dashboard service
-	done, err = deploy.SyncServiceToCluster(d.deployContext, d.component, []string{"http"}, []int32{8080}, d.component)
+	done, err := deploy.SyncServiceToCluster(ctx, d.getComponentName(ctx), []string{"http"}, []int32{8080}, d.getComponentName(ctx))
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
 	// Expose dashboard service with route or ingress
-	_, done, err = expose.ExposeWithHostPath(d.deployContext, d.component, d.deployContext.CheCluster.Spec.Server.CheHost,
+	_, done, err = expose.ExposeWithHostPath(ctx, d.getComponentName(ctx), ctx.CheCluster.Spec.Server.CheHost,
 		exposePath,
-		d.deployContext.CheCluster.Spec.Server.DashboardRoute,
-		d.deployContext.CheCluster.Spec.Server.DashboardIngress,
-		d.createGatewayConfig(),
+		ctx.CheCluster.Spec.Server.DashboardRoute,
+		ctx.CheCluster.Spec.Server.DashboardIngress,
+		d.createGatewayConfig(ctx),
 	)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
 	// we create dashboard SA in any case to keep a track on resources we access withing it
-	done, err = deploy.SyncServiceAccountToCluster(d.deployContext, DashboardSA)
+	done, err = deploy.SyncServiceAccountToCluster(ctx, DashboardSA)
 	if !done {
-		return done, err
+		return reconcile.Result{}, false, err
 	}
 
 	// on Kubernetes Dashboard needs privileged SA to work with user's objects
 	// for time being until Kubernetes did not get authentication
 	if !util.IsOpenShift {
-		done, err = deploy.SyncClusterRoleToCluster(d.deployContext, d.getClusterRoleName(), GetPrivilegedPoliciesRulesForKubernetes())
+		done, err = deploy.SyncClusterRoleToCluster(ctx, d.getClusterRoleName(ctx), GetPrivilegedPoliciesRulesForKubernetes())
 		if !done {
-			return false, err
+			return reconcile.Result{}, false, err
 		}
 
-		done, err = deploy.SyncClusterRoleBindingToCluster(d.deployContext, d.getClusterRoleBindingName(), DashboardSA, d.getClusterRoleName())
+		done, err = deploy.SyncClusterRoleBindingToCluster(ctx, d.getClusterRoleBindingName(ctx), DashboardSA, d.getClusterRoleName(ctx))
 		if !done {
-			return false, err
+			return reconcile.Result{}, false, err
 		}
 
-		err = deploy.AppendFinalizer(d.deployContext, ClusterPermissionsDashboardFinalizer)
+		err = deploy.AppendFinalizer(ctx, ClusterPermissionsDashboardFinalizer)
 		if err != nil {
-			return false, err
+			return reconcile.Result{}, false, err
 		}
 	}
 
 	// Deploy dashboard
-	spec, err := d.getDashboardDeploymentSpec()
+	spec, err := d.getDashboardDeploymentSpec(ctx)
 	if err != nil {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
-	return deploy.SyncDeploymentSpecToCluster(d.deployContext, spec, deploy.DefaultDeploymentDiffOpts)
+
+	done, err = deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	return reconcile.Result{}, true, nil
 }
 
-func (d *Dashboard) Finalize() (done bool, err error) {
-	done, err = deploy.Delete(d.deployContext, types.NamespacedName{Name: d.getClusterRoleName()}, &rbacv1.ClusterRole{})
+func (d *DashboardReconciler) Finalize(ctx *deploy.DeployContext) error {
+	done, err := deploy.Delete(ctx, types.NamespacedName{Name: d.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{})
 	if !done {
-		return false, err
+		return err
 	}
 
-	done, err = deploy.Delete(d.deployContext, types.NamespacedName{Name: d.getClusterRoleBindingName()}, &rbacv1.ClusterRoleBinding{})
+	done, err = deploy.Delete(ctx, types.NamespacedName{Name: d.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{})
 	if !done {
-		return false, err
+		return err
 	}
 
-	err = deploy.DeleteFinalizer(d.deployContext, ClusterPermissionsDashboardFinalizer)
-	return err == nil, err
+	return deploy.DeleteFinalizer(ctx, ClusterPermissionsDashboardFinalizer)
 }
 
-func (d *Dashboard) createGatewayConfig() *gateway.TraefikConfig {
+func (d *DashboardReconciler) createGatewayConfig(ctx *deploy.DeployContext) *gateway.TraefikConfig {
 	cfg := gateway.CreateCommonTraefikConfig(
-		d.component,
+		d.getComponentName(ctx),
 		fmt.Sprintf("PathPrefix(`%s`)", exposePath),
 		10,
-		"http://"+d.component+":8080",
+		"http://"+d.getComponentName(ctx)+":8080",
 		[]string{})
-	if util.IsOpenShift && d.deployContext.CheCluster.IsNativeUserModeEnabled() {
-		cfg.AddAuthHeaderRewrite(d.component)
+	if util.IsOpenShift && ctx.CheCluster.IsNativeUserModeEnabled() {
+		cfg.AddAuthHeaderRewrite(d.getComponentName(ctx))
 	}
 	return cfg
 }
diff --git a/pkg/deploy/dashboard/dashboard_deployment_test.go b/pkg/deploy/dashboard/dashboard_deployment_test.go
index 736c7c5d84..5538a7b868 100644
--- a/pkg/deploy/dashboard/dashboard_deployment_test.go
+++ b/pkg/deploy/dashboard/dashboard_deployment_test.go
@@ -16,6 +16,7 @@ import (
 	"os"
 
 	configv1 "github.com/openshift/api/config/v1"
+	"github.com/stretchr/testify/assert"
 
 	"github.com/google/go-cmp/cmp"
 	corev1 "k8s.io/api/core/v1"
@@ -27,8 +28,6 @@ import (
 	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -37,35 +36,12 @@ import (
 )
 
 func TestDashboardDeploymentSecurityContext(t *testing.T) {
-	logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
 
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme)
-
-	deployContext := &deploy.DeployContext{
-		CheCluster: &orgv1.CheCluster{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "eclipse-che",
-			},
-			Spec: orgv1.CheClusterSpec{
-				Server: orgv1.CheClusterSpecServer{},
-			},
-		},
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-		Proxy: &deploy.Proxy{},
-	}
-	deployContext.ClusterAPI.Scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Console{})
-
-	dashboard := NewDashboard(deployContext)
-	deployment, err := dashboard.getDashboardDeploymentSpec()
-	if err != nil {
-		t.Fatalf("Failed to evaluate dashboard deployment spec: %v", err)
-	}
+	dashboard := NewDashboardReconciler()
+	deployment, err := dashboard.getDashboardDeploymentSpec(ctx)
 
+	assert.Nil(t, err)
 	util.ValidateSecurityContext(deployment, t)
 }
 
@@ -91,6 +67,7 @@ func TestDashboardDeploymentResources(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -104,6 +81,7 @@ func TestDashboardDeploymentResources(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Server: orgv1.CheClusterSpecServer{
@@ -120,27 +98,11 @@ func TestDashboardDeploymentResources(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
-
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.cheCluster,
-				ClusterAPI: deploy.ClusterAPI{
-					Client:           cli,
-					NonCachingClient: cli,
-					Scheme:           scheme.Scheme,
-				},
-				Proxy: &deploy.Proxy{},
-			}
-			deployContext.ClusterAPI.Scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Console{})
-
-			dashboard := NewDashboard(deployContext)
-			deployment, err := dashboard.getDashboardDeploymentSpec()
-			if err != nil {
-				t.Fatalf("Failed to evaluate dashboard deployment spec: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
+			dashboard := NewDashboardReconciler()
+			deployment, err := dashboard.getDashboardDeploymentSpec(ctx)
+			assert.Nil(t, err)
 			util.CompareResources(deployment,
 				util.TestExpectedResources{
 					MemoryLimit:   testCase.memoryLimit,
@@ -185,6 +147,7 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Server: orgv1.CheClusterSpecServer{
@@ -213,6 +176,7 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Server: orgv1.CheClusterSpecServer{
@@ -256,6 +220,7 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Server: orgv1.CheClusterSpecServer{
@@ -269,36 +234,14 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
-
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.cheCluster,
-				ClusterAPI: deploy.ClusterAPI{
-					Client:           cli,
-					NonCachingClient: cli,
-					Scheme:           scheme.Scheme,
-				},
-				Proxy: &deploy.Proxy{},
-			}
-			deployContext.ClusterAPI.Scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Console{})
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
-			dashboard := NewDashboard(deployContext)
-			deployment, err := dashboard.getDashboardDeploymentSpec()
-			if err != nil {
-				t.Fatalf("Failed to evaluate dashboard deployment spec: %v", err)
-			}
-			containers := deployment.Spec.Template.Spec.Containers
-			if len(containers) != 1 {
-				t.Fatalf("Dashboard deployment is expected to have only one container but is has %v", len(containers))
-			}
+			dashboard := NewDashboardReconciler()
+			deployment, err := dashboard.getDashboardDeploymentSpec(ctx)
 
-			dashboardContainer := containers[0]
-			diff := cmp.Diff(testCase.envVars, dashboardContainer.Env)
-			if diff != "" {
-				t.Fatalf("Container env var does not match expected. Diff: %s", diff)
-			}
+			assert.Nil(t, err)
+			assert.Equal(t, len(deployment.Spec.Template.Spec.Containers), 1)
+			assert.Empty(t, cmp.Diff(testCase.envVars, deployment.Spec.Template.Spec.Containers[0].Env))
 		})
 	}
 }
@@ -334,6 +277,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -379,6 +323,7 @@ func TestDashboardDeploymentVolumes(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -387,40 +332,14 @@ func TestDashboardDeploymentVolumes(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
-
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.cheCluster,
-				ClusterAPI: deploy.ClusterAPI{
-					Client:           cli,
-					NonCachingClient: cli,
-					Scheme:           scheme.Scheme,
-				},
-				Proxy: &deploy.Proxy{},
-			}
-
-			dashboard := NewDashboard(deployContext)
-			deployment, err := dashboard.getDashboardDeploymentSpec()
-			if err != nil {
-				t.Fatalf("Failed to evaluate dashboard deployment spec: %v", err)
-			}
-			containers := deployment.Spec.Template.Spec.Containers
-			if len(containers) != 1 {
-				t.Fatalf("Dashboard deployment is expected to have only one container but is has %v", len(containers))
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
-			diff := cmp.Diff(testCase.volumes, deployment.Spec.Template.Spec.Volumes)
-			if diff != "" {
-				t.Fatalf("Dashboard deployment volume not match expected. Diff: %s", diff)
-			}
+			dashboard := NewDashboardReconciler()
+			deployment, err := dashboard.getDashboardDeploymentSpec(ctx)
 
-			dashboardContainer := containers[0]
-			diff = cmp.Diff(testCase.volumeMounts, dashboardContainer.VolumeMounts)
-			if diff != "" {
-				t.Fatalf("Dashboard deployment volume not match expected. Diff: %s", diff)
-			}
+			assert.Nil(t, err)
+			assert.Equal(t, len(deployment.Spec.Template.Spec.Containers), 1)
+			assert.Empty(t, cmp.Diff(testCase.volumeMounts, deployment.Spec.Template.Spec.Containers[0].VolumeMounts))
 		})
 	}
 }
diff --git a/pkg/deploy/dashboard/dashboard_test.go b/pkg/deploy/dashboard/dashboard_test.go
index 1fc307bca8..62f66136e8 100644
--- a/pkg/deploy/dashboard/dashboard_test.go
+++ b/pkg/deploy/dashboard/dashboard_test.go
@@ -13,24 +13,17 @@
 package dashboard
 
 import (
-	"context"
-	"fmt"
-
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/stretchr/testify/assert"
 	networkingv1 "k8s.io/api/networking/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	"sigs.k8s.io/controller-runtime/pkg/client"
+	"k8s.io/apimachinery/pkg/runtime"
 
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	routev1 "github.com/openshift/api/route/v1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	"testing"
 )
@@ -38,215 +31,61 @@ import (
 const Namespace = "eclipse-che"
 
 func TestDashboardOpenShift(t *testing.T) {
-	//given
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: Namespace,
-			Name:      "eclipse-che",
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	//when
 	util.IsOpenShift = true
-	dashboard := NewDashboard(deployContext)
-	done, err := dashboard.Reconcile()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Dashboard: %v", err)
-	}
-
-	//then
-	verifyDashboardServiceExist(t, cli, dashboard)
-	verifyDashboardRouteExist(t, cli, dashboard)
-	verifyDashboardDeploymentExists(t, cli, dashboard)
-	verifyDashboardServiceAccountExists(t, cli)
-	verifyClusterRoleDoesNotExist(t, cli)
-	verifyClusterRoleBindingDoesNotExist(t, cli)
-	verifyFinalizerIsNotSet(t, cheCluster)
+
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
+	dashboard := NewDashboardReconciler()
+	_, done, err := dashboard.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.ServiceAccount{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx), Namespace: "eclipse-che"}, &rbacv1.ClusterRoleBinding{}))
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx), Namespace: "eclipse-che"}, &rbacv1.ClusterRole{}))
+	assert.False(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer))
 }
 
 func TestDashboardKubernetes(t *testing.T) {
-	//given
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: Namespace,
-			Name:      "eclipse-che",
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	//when
 	util.IsOpenShift = false
-	dashboard := NewDashboard(deployContext)
-	done, err := dashboard.Reconcile()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Dashboard: %v", err)
-	}
-
-	//then
-	verifyDashboardDeploymentExists(t, cli, dashboard)
-	verifyDashboardServiceExist(t, cli, dashboard)
-	verifyDashboardIngressExist(t, cli, dashboard)
-	verifyDashboardServiceAccountExists(t, cli)
-	verifyDashboardClusterRoleExists(t, cli)
-	verifyDashboardClusterRoleBindingExists(t, cli)
-	verifyFinalizerIsSet(t, cheCluster)
+
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
+	dashboard := NewDashboardReconciler()
+	_, done, err := dashboard.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &networkingv1.Ingress{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.ServiceAccount{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{}))
+	assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer))
 }
 
 func TestDashboardClusterRBACFinalizerOnKubernetes(t *testing.T) {
-	//given
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: Namespace,
-			Name:      "eclipse-che",
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	//when
 	util.IsOpenShift = false
-	dashboard := NewDashboard(deployContext)
-	done, err := dashboard.Reconcile()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Dashboard: %v", err)
-	}
-	verifyDashboardClusterRoleExists(t, cli)
-	verifyDashboardClusterRoleBindingExists(t, cli)
-	verifyFinalizerIsSet(t, cheCluster)
-	done, err = dashboard.Finalize()
-	if err != nil {
-		t.Fatalf("Can't finalize dashboard %v", err)
-	}
-
-	//then
-	verifyClusterRoleDoesNotExist(t, cli)
-	verifyClusterRoleBindingDoesNotExist(t, cli)
-	verifyFinalizerIsNotSet(t, cheCluster)
-}
-
-func verifyFinalizerIsSet(t *testing.T, cheCluster *orgv1.CheCluster) {
-	if !hasFinalizer(ClusterPermissionsDashboardFinalizer, cheCluster) {
-		t.Fatal("CheCluster did not get Dashboard Cluster Permissions finalizer on Kubernetes")
-	}
-}
 
-func verifyDashboardClusterRoleBindingExists(t *testing.T, cli client.Client) {
-	clusterRoleBinding := &rbacv1.ClusterRoleBinding{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf(DashboardSAClusterRoleBindingTemplate, Namespace)}, clusterRoleBinding)
-	if err != nil {
-		t.Fatalf("ClusterRoleBinding is not found on k8s: %v", err)
-	}
-}
-
-func verifyDashboardClusterRoleExists(t *testing.T, cli client.Client) {
-	clusterRole := &rbacv1.ClusterRole{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf(DashboardSAClusterRoleTemplate, Namespace)}, clusterRole)
-	if err != nil {
-		t.Fatalf("ClusterRole is not found on K8s: %v", err)
-	}
-}
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
+	dashboard := NewDashboardReconciler()
+	_, done, err := dashboard.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
 
-func verifyDashboardIngressExist(t *testing.T, cli client.Client, dashboard *Dashboard) {
-	ingress := &networkingv1.Ingress{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: dashboard.component, Namespace: "eclipse-che"}, ingress)
-	if err != nil {
-		t.Fatalf("Ingress not found: %v", err)
-	}
-}
-
-func verifyFinalizerIsNotSet(t *testing.T, cheCluster *orgv1.CheCluster) {
-	if hasFinalizer(ClusterPermissionsDashboardFinalizer, cheCluster) {
-		t.Fatal("CheCluster got Dashboard Cluster Permissions finalizer but not expected")
-	}
-}
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{}))
+	assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer))
 
-func verifyClusterRoleBindingDoesNotExist(t *testing.T, cli client.Client) {
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf(DashboardSAClusterRoleBindingTemplate, Namespace)}, &rbacv1.ClusterRoleBinding{})
-	if err == nil || !errors.IsNotFound(err) {
-		t.Fatalf("ClusterRoleBinding is created or failed to check on OpenShift: %v", err)
-	}
-}
-
-func verifyClusterRoleDoesNotExist(t *testing.T, cli client.Client) {
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf(DashboardSAClusterRoleTemplate, Namespace)}, &rbacv1.ClusterRole{})
-	if err == nil || !errors.IsNotFound(err) {
-		t.Fatalf("ClusterRole is created or failed to check on OpenShift: %v", err)
-	}
-}
-
-func verifyDashboardServiceAccountExists(t *testing.T, cli client.Client) {
-	sa := &corev1.ServiceAccount{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, sa)
-	if err != nil {
-		t.Fatalf("Service account not found: %v", err)
-	}
-}
-
-func verifyDashboardDeploymentExists(t *testing.T, cli client.Client, dashboard *Dashboard) {
-	deployment := &appsv1.Deployment{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: dashboard.component, Namespace: "eclipse-che"}, deployment)
-	if err != nil {
-		t.Fatalf("Deployment not found: %v", err)
-	}
-}
-
-func verifyDashboardRouteExist(t *testing.T, cli client.Client, dashboard *Dashboard) {
-	route := &routev1.Route{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: dashboard.component, Namespace: "eclipse-che"}, route)
-	if err != nil {
-		t.Fatalf("Route not found: %v", err)
-	}
-}
-
-func verifyDashboardServiceExist(t *testing.T, cli client.Client, dashboard *Dashboard) {
-	service := &corev1.Service{}
-	err := cli.Get(context.TODO(), types.NamespacedName{Name: dashboard.component, Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Service not found: %v", err)
-	}
-}
+	err = dashboard.Finalize(ctx)
+	assert.Nil(t, err)
 
-func hasFinalizer(name string, cheCluster *orgv1.CheCluster) bool {
-	for _, finalizer := range cheCluster.ObjectMeta.Finalizers {
-		if finalizer == name {
-			return true
-		}
-	}
-	return false
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleBindingName(ctx)}, &rbacv1.ClusterRoleBinding{}))
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getClusterRoleName(ctx)}, &rbacv1.ClusterRole{}))
+	assert.False(t, util.ContainsString(ctx.CheCluster.Finalizers, ClusterPermissionsDashboardFinalizer))
 }
diff --git a/pkg/deploy/dashboard/deployment_dashboard.go b/pkg/deploy/dashboard/deployment_dashboard.go
index 4b8678e228..87b8af508d 100644
--- a/pkg/deploy/dashboard/deployment_dashboard.go
+++ b/pkg/deploy/dashboard/deployment_dashboard.go
@@ -32,14 +32,14 @@ import (
 const CHE_SELF_SIGNED_MOUNT_PATH = "/public-certs/che-self-signed"
 const CHE_CUSTOM_CERTS_MOUNT_PATH = "/public-certs/custom"
 
-func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
+func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) {
 	var volumes []corev1.Volume
 	var volumeMounts []corev1.VolumeMount
 	var envVars []corev1.EnvVar
 
 	volumes, volumeMounts = d.provisionCustomPublicCA(volumes, volumeMounts)
 
-	selfSignedCertSecretExist, err := tls.IsSelfSignedCASecretExists(d.deployContext)
+	selfSignedCertSecretExist, err := tls.IsSelfSignedCASecretExists(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -52,17 +52,17 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 		// CHE_HOST is here for backward compatibility. Replaced with CHE_URL
 		corev1.EnvVar{
 			Name:  "CHE_HOST",
-			Value: util.GetCheURL(d.deployContext.CheCluster)},
+			Value: util.GetCheURL(ctx.CheCluster)},
 		corev1.EnvVar{
 			Name:  "CHE_URL",
-			Value: util.GetCheURL(d.deployContext.CheCluster)},
+			Value: util.GetCheURL(ctx.CheCluster)},
 	)
 
-	if d.deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() {
+	if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() {
 		envVars = append(envVars,
 			corev1.EnvVar{
 				Name:  "CHE_INTERNAL_URL",
-				Value: fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, d.deployContext.CheCluster.Namespace)},
+				Value: fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)},
 		)
 	}
 
@@ -70,14 +70,14 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 		envVars = append(envVars,
 			corev1.EnvVar{
 				Name:  "OPENSHIFT_CONSOLE_URL",
-				Value: d.evaluateOpenShiftConsoleURL()})
+				Value: d.evaluateOpenShiftConsoleURL(ctx)})
 	}
 
 	terminationGracePeriodSeconds := int64(30)
-	labels, labelsSelector := deploy.GetLabelsAndSelector(d.deployContext.CheCluster, d.component)
+	labels, labelsSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, d.getComponentName(ctx))
 
-	dashboardImageAndTag := util.GetValue(d.deployContext.CheCluster.Spec.Server.DashboardImage, deploy.DefaultDashboardImage(d.deployContext.CheCluster))
-	pullPolicy := corev1.PullPolicy(util.GetValue(d.deployContext.CheCluster.Spec.Server.DashboardImagePullPolicy, deploy.DefaultPullPolicyFromDockerImage(dashboardImageAndTag)))
+	dashboardImageAndTag := util.GetValue(ctx.CheCluster.Spec.Server.DashboardImage, deploy.DefaultDashboardImage(ctx.CheCluster))
+	pullPolicy := corev1.PullPolicy(util.GetValue(ctx.CheCluster.Spec.Server.DashboardImagePullPolicy, deploy.DefaultPullPolicyFromDockerImage(dashboardImageAndTag)))
 
 	deployment := &appsv1.Deployment{
 		TypeMeta: metav1.TypeMeta{
@@ -85,8 +85,8 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      d.component,
-			Namespace: d.deployContext.CheCluster.Namespace,
+			Name:      d.getComponentName(ctx),
+			Namespace: ctx.CheCluster.Namespace,
 			Labels:    labels,
 		},
 		Spec: appsv1.DeploymentSpec{
@@ -102,7 +102,7 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 					ServiceAccountName: DashboardSA,
 					Containers: []corev1.Container{
 						{
-							Name:            d.component,
+							Name:            d.getComponentName(ctx),
 							ImagePullPolicy: pullPolicy,
 							Image:           dashboardImageAndTag,
 							Ports: []corev1.ContainerPort{
@@ -115,18 +115,18 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 							Resources: corev1.ResourceRequirements{
 								Requests: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										d.deployContext.CheCluster.Spec.Server.DashboardMemoryRequest,
+										ctx.CheCluster.Spec.Server.DashboardMemoryRequest,
 										deploy.DefaultDashboardMemoryRequest),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										d.deployContext.CheCluster.Spec.Server.DashboardCpuRequest,
+										ctx.CheCluster.Spec.Server.DashboardCpuRequest,
 										deploy.DefaultDashboardCpuRequest),
 								},
 								Limits: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										d.deployContext.CheCluster.Spec.Server.DashboardMemoryLimit,
+										ctx.CheCluster.Spec.Server.DashboardMemoryLimit,
 										deploy.DefaultDashboardMemoryLimit),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										d.deployContext.CheCluster.Spec.Server.DashboardCpuLimit,
+										ctx.CheCluster.Spec.Server.DashboardCpuLimit,
 										deploy.DefaultDashboardCpuLimit),
 								},
 							},
@@ -182,11 +182,11 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 	}
 
 	if !util.IsOpenShift {
-		runAsUser, err := strconv.ParseInt(util.GetValue(d.deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64)
+		runAsUser, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64)
 		if err != nil {
 			return nil, err
 		}
-		fsGroup, err := strconv.ParseInt(util.GetValue(d.deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64)
+		fsGroup, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64)
 		if err != nil {
 			return nil, err
 		}
@@ -199,10 +199,10 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
 	return deployment, nil
 }
 
-func (d *Dashboard) evaluateOpenShiftConsoleURL() string {
+func (d *DashboardReconciler) evaluateOpenShiftConsoleURL(ctx *deploy.DeployContext) string {
 	console := &configv1.Console{}
 
-	err := d.deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{
+	err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{
 		Name:      "cluster",
 		Namespace: "openshift-console",
 	}, console)
@@ -215,7 +215,7 @@ func (d *Dashboard) evaluateOpenShiftConsoleURL() string {
 	return console.Status.ConsoleURL
 }
 
-func (d *Dashboard) provisionCheSelfSignedCA(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) ([]corev1.Volume, []corev1.VolumeMount) {
+func (d *DashboardReconciler) provisionCheSelfSignedCA(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) ([]corev1.Volume, []corev1.VolumeMount) {
 	cheSelfSigned := corev1.Volume{
 		Name: "che-self-signed-ca",
 		VolumeSource: corev1.VolumeSource{
@@ -238,7 +238,7 @@ func (d *Dashboard) provisionCheSelfSignedCA(volumes []corev1.Volume, volumeMoun
 	return volumes, volumeMounts
 }
 
-func (d *Dashboard) provisionCustomPublicCA(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) ([]corev1.Volume, []corev1.VolumeMount) {
+func (d *DashboardReconciler) provisionCustomPublicCA(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) ([]corev1.Volume, []corev1.VolumeMount) {
 	customPublicCertsVolume := corev1.Volume{
 		Name: "che-custom-ca",
 		VolumeSource: corev1.VolumeSource{
diff --git a/pkg/deploy/dashboard/rbac.go b/pkg/deploy/dashboard/rbac.go
index 36b510ea3d..9dfdc0c2ff 100644
--- a/pkg/deploy/dashboard/rbac.go
+++ b/pkg/deploy/dashboard/rbac.go
@@ -15,6 +15,7 @@ package dashboard
 import (
 	"fmt"
 
+	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/util"
 
 	rbacv1 "k8s.io/api/rbac/v1"
@@ -59,10 +60,10 @@ func GetPrivilegedPoliciesRulesForKubernetes() []rbacv1.PolicyRule {
 	return rules
 }
 
-func (d *Dashboard) getClusterRoleName() string {
-	return fmt.Sprintf(DashboardSAClusterRoleTemplate, d.deployContext.CheCluster.Namespace)
+func (d *DashboardReconciler) getClusterRoleName(ctx *deploy.DeployContext) string {
+	return fmt.Sprintf(DashboardSAClusterRoleTemplate, ctx.CheCluster.Namespace)
 }
 
-func (d *Dashboard) getClusterRoleBindingName() string {
-	return fmt.Sprintf(DashboardSAClusterRoleBindingTemplate, d.deployContext.CheCluster.Namespace)
+func (d *DashboardReconciler) getClusterRoleBindingName(ctx *deploy.DeployContext) string {
+	return fmt.Sprintf(DashboardSAClusterRoleBindingTemplate, ctx.CheCluster.Namespace)
 }
diff --git a/pkg/deploy/devfileregistry/devfileregistry.go b/pkg/deploy/devfileregistry/devfileregistry.go
index dd00c217c0..c443a89067 100644
--- a/pkg/deploy/devfileregistry/devfileregistry.go
+++ b/pkg/deploy/devfileregistry/devfileregistry.go
@@ -17,84 +17,91 @@ import (
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/deploy/expose"
 	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-type DevfileRegistry struct {
-	deployContext *deploy.DeployContext
+type DevfileRegistryReconciler struct {
+	deploy.Reconcilable
 }
 
-func NewDevfileRegistry(deployContext *deploy.DeployContext) *DevfileRegistry {
-	return &DevfileRegistry{
-		deployContext: deployContext,
-	}
+func NewDevfileRegistryReconciler() *DevfileRegistryReconciler {
+	return &DevfileRegistryReconciler{}
 }
 
-func (p *DevfileRegistry) SyncAll() (bool, error) {
-	done, err := p.SyncService()
+func (d *DevfileRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	if ctx.CheCluster.Spec.Server.ExternalDevfileRegistry {
+		return reconcile.Result{}, true, nil
+	}
+
+	done, err := d.syncService(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	endpoint, done, err := p.ExposeEndpoint()
+	endpoint, done, err := d.exposeEndpoint(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.UpdateStatus(endpoint)
+	done, err = d.updateStatus(endpoint, ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncConfigMap()
+	done, err = d.syncConfigMap(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncDeployment()
+	done, err = d.syncDeployment(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	return true, nil
+	return reconcile.Result{}, true, nil
+}
+
+func (d *DevfileRegistryReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
 }
 
-func (p *DevfileRegistry) SyncService() (bool, error) {
+func (d *DevfileRegistryReconciler) syncService(ctx *deploy.DeployContext) (bool, error) {
 	return deploy.SyncServiceToCluster(
-		p.deployContext,
+		ctx,
 		deploy.DevfileRegistryName,
 		[]string{"http"},
 		[]int32{8080},
 		deploy.DevfileRegistryName)
 }
 
-func (p *DevfileRegistry) SyncConfigMap() (bool, error) {
-	data, err := p.GetConfigMapData()
+func (d *DevfileRegistryReconciler) syncConfigMap(ctx *deploy.DeployContext) (bool, error) {
+	data, err := d.getConfigMapData(ctx)
 	if err != nil {
 		return false, err
 	}
-	return deploy.SyncConfigMapDataToCluster(p.deployContext, deploy.DevfileRegistryName, data, deploy.DevfileRegistryName)
+	return deploy.SyncConfigMapDataToCluster(ctx, deploy.DevfileRegistryName, data, deploy.DevfileRegistryName)
 }
 
-func (p *DevfileRegistry) ExposeEndpoint() (string, bool, error) {
+func (d *DevfileRegistryReconciler) exposeEndpoint(ctx *deploy.DeployContext) (string, bool, error) {
 	return expose.Expose(
-		p.deployContext,
+		ctx,
 		deploy.DevfileRegistryName,
-		p.deployContext.CheCluster.Spec.Server.DevfileRegistryRoute,
-		p.deployContext.CheCluster.Spec.Server.DevfileRegistryIngress,
-		p.createGatewayConfig())
+		ctx.CheCluster.Spec.Server.DevfileRegistryRoute,
+		ctx.CheCluster.Spec.Server.DevfileRegistryIngress,
+		d.createGatewayConfig())
 }
 
-func (p *DevfileRegistry) UpdateStatus(endpoint string) (bool, error) {
+func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) {
 	var devfileRegistryURL string
-	if p.deployContext.CheCluster.Spec.Server.TlsSupport {
+	if ctx.CheCluster.Spec.Server.TlsSupport {
 		devfileRegistryURL = "https://" + endpoint
 	} else {
 		devfileRegistryURL = "http://" + endpoint
 	}
 
-	if devfileRegistryURL != p.deployContext.CheCluster.Status.DevfileRegistryURL {
-		p.deployContext.CheCluster.Status.DevfileRegistryURL = devfileRegistryURL
-		if err := deploy.UpdateCheCRStatus(p.deployContext, "status: Devfile Registry URL", devfileRegistryURL); err != nil {
+	if devfileRegistryURL != ctx.CheCluster.Status.DevfileRegistryURL {
+		ctx.CheCluster.Status.DevfileRegistryURL = devfileRegistryURL
+		if err := deploy.UpdateCheCRStatus(ctx, "status: Devfile Registry URL", devfileRegistryURL); err != nil {
 			return false, err
 		}
 	}
@@ -102,12 +109,12 @@ func (p *DevfileRegistry) UpdateStatus(endpoint string) (bool, error) {
 	return true, nil
 }
 
-func (p *DevfileRegistry) SyncDeployment() (bool, error) {
-	spec := p.GetDevfileRegistryDeploymentSpec()
-	return deploy.SyncDeploymentSpecToCluster(p.deployContext, spec, deploy.DefaultDeploymentDiffOpts)
+func (d *DevfileRegistryReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
+	spec := d.getDevfileRegistryDeploymentSpec(ctx)
+	return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts)
 }
 
-func (p *DevfileRegistry) createGatewayConfig() *gateway.TraefikConfig {
+func (d *DevfileRegistryReconciler) createGatewayConfig() *gateway.TraefikConfig {
 	pathPrefix := "/" + deploy.DevfileRegistryName
 	cfg := gateway.CreateCommonTraefikConfig(
 		deploy.DevfileRegistryName,
diff --git a/pkg/deploy/devfileregistry/devfileregistry_configmap.go b/pkg/deploy/devfileregistry/devfileregistry_configmap.go
index fcc2e8f607..94df134447 100644
--- a/pkg/deploy/devfileregistry/devfileregistry_configmap.go
+++ b/pkg/deploy/devfileregistry/devfileregistry_configmap.go
@@ -13,6 +13,8 @@ package devfileregistry
 
 import (
 	"encoding/json"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
 )
 
 type DevFileRegistryConfigMap struct {
@@ -21,12 +23,12 @@ type DevFileRegistryConfigMap struct {
 	CheDevfileRegistryURL                string `json:"CHE_DEVFILE_REGISTRY_URL"`
 }
 
-func (p *DevfileRegistry) GetConfigMapData() (map[string]string, error) {
+func (d *DevfileRegistryReconciler) getConfigMapData(ctx *deploy.DeployContext) (map[string]string, error) {
 	devfileRegistryEnv := make(map[string]string)
 	data := &DevFileRegistryConfigMap{
-		CheDevfileImagesRegistryURL:          p.deployContext.CheCluster.Spec.Server.AirGapContainerRegistryHostname,
-		CheDevfileImagesRegistryOrganization: p.deployContext.CheCluster.Spec.Server.AirGapContainerRegistryOrganization,
-		CheDevfileRegistryURL:                p.deployContext.CheCluster.Status.DevfileRegistryURL,
+		CheDevfileImagesRegistryURL:          ctx.CheCluster.Spec.Server.AirGapContainerRegistryHostname,
+		CheDevfileImagesRegistryOrganization: ctx.CheCluster.Spec.Server.AirGapContainerRegistryOrganization,
+		CheDevfileRegistryURL:                ctx.CheCluster.Status.DevfileRegistryURL,
 	}
 
 	out, err := json.Marshal(data)
diff --git a/pkg/deploy/devfileregistry/devfileregistry_deployment.go b/pkg/deploy/devfileregistry/devfileregistry_deployment.go
index 4485538491..9531283406 100644
--- a/pkg/deploy/devfileregistry/devfileregistry_deployment.go
+++ b/pkg/deploy/devfileregistry/devfileregistry_deployment.go
@@ -19,34 +19,34 @@ import (
 	v1 "k8s.io/api/core/v1"
 )
 
-func (p *DevfileRegistry) GetDevfileRegistryDeploymentSpec() *appsv1.Deployment {
+func (p *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *deploy.DeployContext) *appsv1.Deployment {
 	registryType := "devfile"
-	registryImage := util.GetValue(p.deployContext.CheCluster.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(p.deployContext.CheCluster))
-	registryImagePullPolicy := v1.PullPolicy(util.GetValue(string(p.deployContext.CheCluster.Spec.Server.DevfileRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
+	registryImage := util.GetValue(ctx.CheCluster.Spec.Server.DevfileRegistryImage, deploy.DefaultDevfileRegistryImage(ctx.CheCluster))
+	registryImagePullPolicy := v1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.DevfileRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
 	probePath := "/devfiles/"
 	devfileImagesEnv := util.GetEnvByRegExp("^.*devfile_registry_image.*$")
 
 	resources := v1.ResourceRequirements{
 		Requests: v1.ResourceList{
 			v1.ResourceMemory: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.DevfileRegistryMemoryRequest,
+				ctx.CheCluster.Spec.Server.DevfileRegistryMemoryRequest,
 				deploy.DefaultDevfileRegistryMemoryRequest),
 			v1.ResourceCPU: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.DevfileRegistryCpuRequest,
+				ctx.CheCluster.Spec.Server.DevfileRegistryCpuRequest,
 				deploy.DefaultDevfileRegistryCpuRequest),
 		},
 		Limits: v1.ResourceList{
 			v1.ResourceMemory: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.DevfileRegistryMemoryLimit,
+				ctx.CheCluster.Spec.Server.DevfileRegistryMemoryLimit,
 				deploy.DefaultDevfileRegistryMemoryLimit),
 			v1.ResourceCPU: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.DevfileRegistryCpuLimit,
+				ctx.CheCluster.Spec.Server.DevfileRegistryCpuLimit,
 				deploy.DefaultDevfileRegistryCpuLimit),
 		},
 	}
 
 	return registry.GetSpecRegistryDeployment(
-		p.deployContext,
+		ctx,
 		registryType,
 		registryImage,
 		devfileImagesEnv,
diff --git a/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go b/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go
index ccf37a7c28..14fccae8aa 100644
--- a/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go
+++ b/pkg/deploy/devfileregistry/devfileregistry_deployment_test.go
@@ -21,8 +21,6 @@ import (
 	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
@@ -51,6 +49,7 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -63,6 +62,7 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) {
 			memoryRequest: "150Mi",
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
+					Name:      "eclipse-che",
 					Namespace: "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
@@ -80,21 +80,10 @@ func TestGetDevfileRegistryDeploymentSpec(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			deployContext := &deploy.DeployContext{
-				ClusterAPI: deploy.ClusterAPI{
-					Client: cli,
-					Scheme: scheme.Scheme,
-				},
-				Proxy:      &deploy.Proxy{},
-				CheCluster: testCase.cheCluster,
-			}
-
-			devfileregistry := NewDevfileRegistry(deployContext)
-			deployment := devfileregistry.GetDevfileRegistryDeploymentSpec()
+			devfileregistry := NewDevfileRegistryReconciler()
+			deployment := devfileregistry.getDevfileRegistryDeploymentSpec(ctx)
 
 			util.CompareResources(deployment,
 				util.TestExpectedResources{
diff --git a/pkg/deploy/devfileregistry/devfileregistry_test.go b/pkg/deploy/devfileregistry/devfileregistry_test.go
index ad8f32f185..bc6e07f354 100644
--- a/pkg/deploy/devfileregistry/devfileregistry_test.go
+++ b/pkg/deploy/devfileregistry/devfileregistry_test.go
@@ -12,81 +12,127 @@
 package devfileregistry
 
 import (
-	"context"
+	"os"
 
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/util"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	routev1 "github.com/openshift/api/route/v1"
+	"github.com/stretchr/testify/assert"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	"testing"
 )
 
-func TestDevfileRegistrySyncAll(t *testing.T) {
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      "eclipse-che",
-		},
-	}
+func TestDevfileRegistryReconcile(t *testing.T) {
+	util.IsOpenShift = true
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
 
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
+	devfileregistry := NewDevfileRegistryReconciler()
+	_, done, err := devfileregistry.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
 
-	util.IsOpenShift = true
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.Service{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &routev1.Route{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.NotEmpty(t, ctx.CheCluster.Status.DevfileRegistryURL)
+}
 
-	devfileregistry := NewDevfileRegistry(deployContext)
-	done, err := devfileregistry.SyncAll()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Devfile Registry: %v", err)
+func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
+	type testCase struct {
+		name                       string
+		isOpenShift                bool
+		isOpenShift4               bool
+		initObjects                []runtime.Object
+		cheCluster                 *orgv1.CheCluster
+		expectedDevfileRegistryURL string
 	}
 
-	// check service
-	service := &corev1.Service{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Service not found: %v", err)
+	testCases := []testCase{
+		{
+			name: "Test Status.DevfileRegistryURL #1",
+			cheCluster: &orgv1.CheCluster{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "CheCluster",
+					APIVersion: "org.eclipse.che/v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "eclipse-che",
+					Name:      os.Getenv("CHE_FLAVOR"),
+				},
+				Spec: orgv1.CheClusterSpec{
+					Server: orgv1.CheClusterSpecServer{
+						ExternalDevfileRegistry: false,
+					},
+				},
+			},
+			expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./",
+		},
+		{
+			name: "Test Status.DevfileRegistryURL #2",
+			cheCluster: &orgv1.CheCluster{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "CheCluster",
+					APIVersion: "org.eclipse.che/v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "eclipse-che",
+					Name:      os.Getenv("CHE_FLAVOR"),
+				},
+				Spec: orgv1.CheClusterSpec{
+					Server: orgv1.CheClusterSpecServer{
+						ExternalDevfileRegistry: false,
+						DevfileRegistryUrl:      "https://devfile-registry.external.1",
+						ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
+							{Url: "https://devfile-registry.external.2"},
+						},
+					},
+				},
+			},
+			expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./",
+		},
+		{
+			name: "Test Status.DevfileRegistryURL #2",
+			cheCluster: &orgv1.CheCluster{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "CheCluster",
+					APIVersion: "org.eclipse.che/v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "eclipse-che",
+					Name:      os.Getenv("CHE_FLAVOR"),
+				},
+				Spec: orgv1.CheClusterSpec{
+					Server: orgv1.CheClusterSpecServer{
+						ExternalDevfileRegistry: true,
+						DevfileRegistryUrl:      "https://devfile-registry.external.1",
+						ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
+							{Url: "https://devfile-registry.external.2"},
+						},
+					},
+				},
+			},
+			expectedDevfileRegistryURL: "",
+		},
 	}
 
-	// check endpoint
-	route := &routev1.Route{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, route)
-	if err != nil {
-		t.Fatalf("Route not found: %v", err)
-	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-	// check configmap
-	cm := &corev1.ConfigMap{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, cm)
-	if err != nil {
-		t.Fatalf("Config Map not found: %v", err)
-	}
+			util.IsOpenShift = testCase.isOpenShift
+			util.IsOpenShift4 = testCase.isOpenShift4
 
-	// check deployment
-	deployment := &appsv1.Deployment{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, deployment)
-	if err != nil {
-		t.Fatalf("Deployment not found: %v", err)
-	}
+			devfileregistry := NewDevfileRegistryReconciler()
+			devfileregistry.Reconcile(ctx)
 
-	if cheCluster.Status.DevfileRegistryURL == "" {
-		t.Fatalf("Status wasn't updated")
+			assert.Equal(t, ctx.CheCluster.Status.DevfileRegistryURL, testCase.expectedDevfileRegistryURL)
+		})
 	}
 }
diff --git a/pkg/deploy/gateway/gateway.go b/pkg/deploy/gateway/gateway.go
index 2ee81222d6..1dd1e730f5 100644
--- a/pkg/deploy/gateway/gateway.go
+++ b/pkg/deploy/gateway/gateway.go
@@ -36,6 +36,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
 const (
@@ -58,6 +59,27 @@ var (
 	secretDiffOpts         = cmpopts.IgnoreFields(corev1.Secret{}, "TypeMeta", "ObjectMeta")
 )
 
+type GatewayReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewGatewayReconciler() *GatewayReconciler {
+	return &GatewayReconciler{}
+}
+
+func (p *GatewayReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	err := SyncGatewayToCluster(ctx)
+	if err != nil {
+		return reconcile.Result{}, false, err
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (p *GatewayReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
+}
+
 // SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration
 func SyncGatewayToCluster(deployContext *deploy.DeployContext) error {
 	if (util.GetServerExposureStrategy(deployContext.CheCluster) == "single-host" &&
diff --git a/pkg/deploy/identity-provider/deployment_keycloak.go b/pkg/deploy/identity-provider/deployment_keycloak.go
index 752ea209fd..87237257d9 100644
--- a/pkg/deploy/identity-provider/deployment_keycloak.go
+++ b/pkg/deploy/identity-provider/deployment_keycloak.go
@@ -9,7 +9,7 @@
 // Contributors:
 //   Red Hat, Inc. - initial API and implementation
 //
-package identity_provider
+package identityprovider
 
 import (
 	"context"
diff --git a/pkg/deploy/identity-provider/deployment_keycloak_test.go b/pkg/deploy/identity-provider/deployment_keycloak_test.go
index 8f7bc3e1ee..1dad62b11c 100644
--- a/pkg/deploy/identity-provider/deployment_keycloak_test.go
+++ b/pkg/deploy/identity-provider/deployment_keycloak_test.go
@@ -9,7 +9,7 @@
 // Contributors:
 //   Red Hat, Inc. - initial API and implementation
 //
-package identity_provider
+package identityprovider
 
 import (
 	"context"
diff --git a/pkg/deploy/identity-provider/exec.go b/pkg/deploy/identity-provider/exec.go
index 52c2347b3e..6598572eb1 100644
--- a/pkg/deploy/identity-provider/exec.go
+++ b/pkg/deploy/identity-provider/exec.go
@@ -9,7 +9,7 @@
 // Contributors:
 //   Red Hat, Inc. - initial API and implementation
 //
-package identity_provider
+package identityprovider
 
 import (
 	"bytes"
diff --git a/pkg/deploy/identity-provider/identity_provider.go b/pkg/deploy/identity-provider/identity_provider.go
deleted file mode 100644
index 3ff313a00c..0000000000
--- a/pkg/deploy/identity-provider/identity_provider.go
+++ /dev/null
@@ -1,377 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package identity_provider
-
-import (
-	"context"
-	"errors"
-	"strings"
-
-	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
-	"github.com/eclipse-che/che-operator/pkg/deploy"
-	"github.com/eclipse-che/che-operator/pkg/deploy/expose"
-	"github.com/eclipse-che/che-operator/pkg/util"
-	"github.com/google/go-cmp/cmp/cmpopts"
-	oauth "github.com/openshift/api/oauth/v1"
-	"github.com/sirupsen/logrus"
-	appsv1 "k8s.io/api/apps/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-var (
-	oAuthClientDiffOpts = cmpopts.IgnoreFields(oauth.OAuthClient{}, "TypeMeta", "ObjectMeta")
-	syncItems           = []func(*deploy.DeployContext) (bool, error){
-		syncService,
-		syncExposure,
-		SyncKeycloakDeploymentToCluster,
-		syncKeycloakResources,
-		syncOpenShiftIdentityProvider,
-		SyncGitHubOAuth,
-	}
-
-	keycloakUpdated = false
-	keycloakCheHost = ""
-)
-
-// SyncIdentityProviderToCluster instantiates the identity provider (Keycloak) in the cluster. Returns true if
-// the provisioning is complete, false if requeue of the reconcile request is needed.
-func SyncIdentityProviderToCluster(deployContext *deploy.DeployContext) (bool, error) {
-	cr := deployContext.CheCluster
-	if deployContext.CheCluster.IsNativeUserModeEnabled() {
-		return syncNativeIdentityProviderItems(deployContext)
-	} else if cr.Spec.Auth.ExternalIdentityProvider {
-		return true, nil
-	}
-
-	for _, syncItem := range syncItems {
-		provisioned, err := syncItem(deployContext)
-		if !util.IsTestMode() {
-			if !provisioned {
-				return false, err
-			}
-		}
-	}
-
-	return true, nil
-}
-
-func syncService(deployContext *deploy.DeployContext) (bool, error) {
-	return deploy.SyncServiceToCluster(
-		deployContext,
-		deploy.IdentityProviderName,
-		[]string{"http"},
-		[]int32{8080},
-		deploy.IdentityProviderName)
-}
-
-func syncExposure(deployContext *deploy.DeployContext) (bool, error) {
-	cr := deployContext.CheCluster
-
-	protocol := (map[bool]string{
-		true:  "https",
-		false: "http"})[cr.Spec.Server.TlsSupport]
-	endpoint, done, err := expose.Expose(
-		deployContext,
-		deploy.IdentityProviderName,
-		cr.Spec.Auth.IdentityProviderRoute,
-		cr.Spec.Auth.IdentityProviderIngress,
-		createGatewayConfig(deployContext.CheCluster))
-	if !done {
-		return false, err
-	}
-
-	keycloakURL := protocol + "://" + endpoint
-	if cr.Spec.Auth.IdentityProviderURL != keycloakURL {
-		cr.Spec.Auth.IdentityProviderURL = keycloakURL
-		if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak URL", keycloakURL); err != nil {
-			return false, err
-		}
-
-		cr.Status.KeycloakURL = keycloakURL
-		if err := deploy.UpdateCheCRStatus(deployContext, "Keycloak URL", keycloakURL); err != nil {
-			return false, err
-		}
-	}
-
-	return true, nil
-}
-
-func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) {
-	if !util.IsTestMode() {
-		cr := deployContext.CheCluster
-		if !cr.Status.KeycloakProvisoned {
-			if err := ProvisionKeycloakResources(deployContext); err != nil {
-				return false, err
-			}
-
-			for {
-				cr.Status.KeycloakProvisoned = true
-				if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil &&
-					apierrors.IsConflict(err) {
-
-					deploy.ReloadCheClusterCR(deployContext)
-					continue
-				}
-				break
-			}
-		}
-
-		// Updates keycloak if chehost has been changed
-		if !keycloakUpdated || keycloakCheHost != deployContext.CheCluster.Spec.Server.CheHost {
-			if _, err := util.K8sclient.ExecIntoPod(
-				deployContext.CheCluster,
-				deploy.IdentityProviderName,
-				GetKeycloakUpdateCommand,
-				"Update redirect URI-s and webOrigins"); err != nil {
-				return false, err
-			} else {
-				keycloakUpdated = true
-				keycloakCheHost = deployContext.CheCluster.Spec.Server.CheHost
-			}
-		}
-	}
-
-	return true, nil
-}
-
-func syncOpenShiftIdentityProvider(deployContext *deploy.DeployContext) (bool, error) {
-	cr := deployContext.CheCluster
-	if util.IsOpenShift && cr.IsOpenShiftOAuthEnabled() {
-		return SyncOpenShiftIdentityProviderItems(deployContext)
-	}
-	return true, nil
-}
-
-func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
-	cr := deployContext.CheCluster
-
-	if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
-		return false, err
-	}
-	if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
-		return false, err
-	}
-
-	if util.IsOpenShift {
-		redirectURIs := []string{"https://" + cr.Spec.Server.CheHost + "/oauth/callback"}
-		oAuthClient := deploy.GetOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, redirectURIs)
-		provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
-		if !provisioned {
-			return false, err
-		}
-	}
-
-	return true, nil
-}
-
-func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
-	cr := deployContext.CheCluster
-
-	if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
-		return false, err
-	}
-	if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
-		return false, err
-	}
-
-	keycloakURL := cr.Spec.Auth.IdentityProviderURL
-	cheFlavor := deploy.DefaultCheFlavor(cr)
-	keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
-	oAuthClient := deploy.GetKeycloakOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
-	provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
-	if !provisioned {
-		return false, err
-	}
-
-	if !util.IsTestMode() {
-		if !cr.Status.OpenShiftoAuthProvisioned {
-			// note that this uses the instance.Spec.Auth.IdentityProviderRealm and instance.Spec.Auth.IdentityProviderClientId.
-			// because we're not doing much of a change detection on those fields, we can't react on them changing here.
-			_, err := util.K8sclient.ExecIntoPod(
-				cr,
-				deploy.IdentityProviderName,
-				func(cr *orgv1.CheCluster) (string, error) {
-					return GetOpenShiftIdentityProviderProvisionCommand(cr, cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret)
-				},
-				"Create OpenShift identity provider")
-			if err != nil {
-				return false, err
-			}
-
-			for {
-				cr.Status.OpenShiftoAuthProvisioned = true
-				if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil &&
-					apierrors.IsConflict(err) {
-
-					deploy.ReloadCheClusterCR(deployContext)
-					continue
-				}
-				break
-			}
-		}
-	}
-	return true, nil
-}
-
-func resolveOpenshiftOAuthClientName(deployContext *deploy.DeployContext) error {
-	cr := deployContext.CheCluster
-	oAuthClientName := cr.Spec.Auth.OAuthClientName
-	if len(oAuthClientName) < 1 {
-		oAuthClientName = cr.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
-		cr.Spec.Auth.OAuthClientName = oAuthClientName
-		if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func resolveOpenshiftOAuthClientSecret(deployContext *deploy.DeployContext) error {
-	cr := deployContext.CheCluster
-	oauthSecret := cr.Spec.Auth.OAuthSecret
-	if len(oauthSecret) < 1 {
-		oauthSecret = util.GeneratePasswd(12)
-		cr.Spec.Auth.OAuthSecret = oauthSecret
-		if err := deploy.UpdateCheCRSpec(deployContext, "oAuth secret name", oauthSecret); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// SyncGitHubOAuth provisions GitHub OAuth if secret with annotation
-// `che.eclipse.org/github-oauth-credentials=true` or `che.eclipse.org/oauth-scm-configuration=github`
-// is mounted into a container
-func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) {
-	// get legacy secret
-	legacySecrets, err := deploy.GetSecrets(deployContext, map[string]string{
-		deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
-		deploy.KubernetesComponentLabelKey: deploy.IdentityProviderName + "-secret",
-	}, map[string]string{
-		deploy.CheEclipseOrgGithubOAuthCredentials: "true",
-	})
-	if err != nil {
-		return false, err
-	}
-
-	secrets, err := deploy.GetSecrets(deployContext, map[string]string{
-		deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
-		deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration,
-	}, map[string]string{
-		deploy.CheEclipseOrgOAuthScmServer: "github",
-	})
-
-	if err != nil {
-		return false, err
-	} else if len(secrets)+len(legacySecrets) > 1 {
-		return false, errors.New("More than 1 GitHub OAuth configuration secrets found")
-	}
-
-	isGitHubOAuthCredentialsExists := len(secrets) == 1 || len(legacySecrets) == 1
-	cr := deployContext.CheCluster
-
-	if isGitHubOAuthCredentialsExists {
-		if !cr.Status.GitHubOAuthProvisioned {
-			if !util.IsTestMode() {
-				_, err := util.K8sclient.ExecIntoPod(
-					cr,
-					deploy.IdentityProviderName,
-					func(cr *orgv1.CheCluster) (string, error) {
-						return GetGitHubIdentityProviderCreateCommand(deployContext)
-					},
-					"Create GitHub OAuth")
-				if err != nil {
-					return false, err
-				}
-			}
-
-			cr.Status.GitHubOAuthProvisioned = true
-			if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "true"); err != nil {
-				return false, err
-			}
-		}
-	} else {
-		if cr.Status.GitHubOAuthProvisioned {
-			if !util.IsTestMode() {
-				_, err := util.K8sclient.ExecIntoPod(
-					cr,
-					deploy.IdentityProviderName,
-					func(cr *orgv1.CheCluster) (string, error) {
-						return GetIdentityProviderDeleteCommand(cr, "github")
-					},
-					"Delete GitHub OAuth")
-				if err != nil {
-					return false, err
-				}
-			}
-
-			cr.Status.GitHubOAuthProvisioned = false
-			if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "false"); err != nil {
-				return false, err
-			}
-		}
-	}
-
-	return true, nil
-}
-
-func ReconcileIdentityProvider(deployContext *deploy.DeployContext) (deleted bool, err error) {
-	if !deployContext.CheCluster.IsOpenShiftOAuthEnabled() && deployContext.CheCluster.Status.OpenShiftoAuthProvisioned == true {
-		keycloakDeployment := &appsv1.Deployment{}
-		if err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: deployContext.CheCluster.Namespace}, keycloakDeployment); err != nil {
-			logrus.Errorf("Deployment %s not found: %s", keycloakDeployment.Name, err.Error())
-		}
-
-		providerName := "openshift-v3"
-		if util.IsOpenShift4 {
-			providerName = "openshift-v4"
-		}
-		_, err := util.K8sclient.ExecIntoPod(
-			deployContext.CheCluster,
-			keycloakDeployment.Name,
-			func(cr *orgv1.CheCluster) (string, error) {
-				return GetIdentityProviderDeleteCommand(deployContext.CheCluster, providerName)
-			},
-			"delete OpenShift identity provider")
-		if err == nil {
-			oAuthClient := &oauth.OAuthClient{}
-			oAuthClientName := deployContext.CheCluster.Spec.Auth.OAuthClientName
-			if err := deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Name: oAuthClientName, Namespace: ""}, oAuthClient); err != nil {
-				logrus.Errorf("OAuthClient %s not found: %s", oAuthClient.Name, err.Error())
-			}
-			if err := deployContext.ClusterAPI.NonCachingClient.Delete(context.TODO(), oAuthClient); err != nil {
-				logrus.Errorf("Failed to delete %s %s: %s", oAuthClient.Kind, oAuthClient.Name, err.Error())
-			}
-			return true, nil
-		}
-		return false, err
-	}
-	return false, nil
-}
-
-func createGatewayConfig(cheCluster *orgv1.CheCluster) *gateway.TraefikConfig {
-	cfg := gateway.CreateCommonTraefikConfig(
-		deploy.IdentityProviderName,
-		"PathPrefix(`/auth`)",
-		10,
-		"http://"+deploy.IdentityProviderName+":8080",
-		[]string{})
-
-	if util.IsOpenShift && cheCluster.IsNativeUserModeEnabled() {
-		cfg.AddAuthHeaderRewrite(deploy.IdentityProviderName)
-	}
-
-	return cfg
-}
diff --git a/pkg/deploy/identity-provider/identity_provider_test.go b/pkg/deploy/identity-provider/identity_provider_test.go
deleted file mode 100644
index 271b70bf75..0000000000
--- a/pkg/deploy/identity-provider/identity_provider_test.go
+++ /dev/null
@@ -1,211 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package identity_provider
-
-import (
-	"os"
-	"reflect"
-
-	"github.com/eclipse-che/che-operator/pkg/deploy"
-
-	"github.com/google/go-cmp/cmp"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
-
-	"testing"
-)
-
-func TestSyncGitHubOAuth(t *testing.T) {
-	type testCase struct {
-		name        string
-		initCR      *orgv1.CheCluster
-		expectedCR  *orgv1.CheCluster
-		initObjects []runtime.Object
-	}
-
-	testCases := []testCase{
-		{
-			name: "Should provision GitHub OAuth with legacy secret",
-			initCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "0",
-				},
-			},
-			expectedCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "1",
-				},
-				Status: orgv1.CheClusterStatus{
-					GitHubOAuthProvisioned: true,
-				},
-			},
-			initObjects: []runtime.Object{
-				&corev1.Secret{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "github-credentials",
-						Namespace: "eclipse-che",
-						Labels: map[string]string{
-							deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
-							deploy.KubernetesComponentLabelKey: "keycloak-secret",
-						},
-						Annotations: map[string]string{
-							deploy.CheEclipseOrgGithubOAuthCredentials: "true",
-						},
-					},
-					Data: map[string][]byte{
-						"key": []byte("key-data"),
-					},
-				},
-			},
-		},
-		{
-			name: "Should provision GitHub OAuth",
-			initCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "0",
-				},
-			},
-			expectedCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "1",
-				},
-				Status: orgv1.CheClusterStatus{
-					GitHubOAuthProvisioned: true,
-				},
-			},
-			initObjects: []runtime.Object{
-				&corev1.Secret{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "github-oauth-config",
-						Namespace: "eclipse-che",
-						Labels: map[string]string{
-							"app.kubernetes.io/part-of":   "che.eclipse.org",
-							"app.kubernetes.io/component": "oauth-scm-configuration",
-						},
-						Annotations: map[string]string{
-							"che.eclipse.org/oauth-scm-server": "github",
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "Should not provision GitHub OAuth",
-			initCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "0",
-				},
-			},
-			expectedCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "0",
-				},
-			},
-			initObjects: []runtime.Object{
-				&corev1.Secret{
-					TypeMeta: metav1.TypeMeta{
-						Kind:       "Secret",
-						APIVersion: "v1",
-					},
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "github-credentials",
-						Namespace: "eclipse-che",
-						Labels: map[string]string{
-							deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
-							deploy.KubernetesComponentLabelKey: "keycloak-secret",
-						},
-					},
-					Data: map[string][]byte{
-						"key": []byte("key-data"),
-					},
-				},
-			},
-		},
-		{
-			name: "Should delete GitHub OAuth",
-			initCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "0",
-				},
-				Status: orgv1.CheClusterStatus{
-					GitHubOAuthProvisioned: true,
-				},
-			},
-			expectedCR: &orgv1.CheCluster{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:            "che-cluster",
-					Namespace:       "eclipse-che",
-					ResourceVersion: "1",
-				},
-				Status: orgv1.CheClusterStatus{
-					GitHubOAuthProvisioned: false,
-				},
-			},
-			initObjects: []runtime.Object{},
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects, testCase.initCR)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
-
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.initCR,
-				ClusterAPI: deploy.ClusterAPI{
-					Client: cli,
-					Scheme: scheme.Scheme,
-				},
-			}
-
-			_, err := SyncGitHubOAuth(deployContext)
-			if err != nil {
-				t.Fatalf("Error mounting secret: %v", err)
-			}
-
-			if !reflect.DeepEqual(testCase.expectedCR, testCase.initCR) {
-				t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedCR, testCase.initCR))
-			}
-		})
-	}
-}
diff --git a/pkg/deploy/identity-provider/init_test.go b/pkg/deploy/identity-provider/init_test.go
index 67d990dbc4..b836a16fac 100644
--- a/pkg/deploy/identity-provider/init_test.go
+++ b/pkg/deploy/identity-provider/init_test.go
@@ -9,7 +9,7 @@
 // Contributors:
 //   Red Hat, Inc. - initial API and implementation
 //
-package identity_provider
+package identityprovider
 
 import "github.com/eclipse-che/che-operator/pkg/deploy"
 
diff --git a/pkg/deploy/identity-provider/keycloak_readiness.go b/pkg/deploy/identity-provider/keycloak_readiness.go
index c70a756685..a34e840b86 100644
--- a/pkg/deploy/identity-provider/keycloak_readiness.go
+++ b/pkg/deploy/identity-provider/keycloak_readiness.go
@@ -9,7 +9,7 @@
 // Contributors:
 //   Red Hat, Inc. - initial API and implementation
 //
-package identity_provider
+package identityprovider
 
 import (
 	"fmt"
diff --git a/pkg/deploy/oauthclient.go b/pkg/deploy/oauthclient.go
deleted file mode 100644
index b5525a3652..0000000000
--- a/pkg/deploy/oauthclient.go
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package deploy
-
-import (
-	"strings"
-
-	oauth "github.com/openshift/api/oauth/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-const (
-	OAuthFinalizerName = "oauthclients.finalizers.che.eclipse.org"
-)
-
-func GetKeycloakOAuthClientSpec(name string, oauthSecret string, keycloakURL string, keycloakRealm string, isOpenShift4 bool) *oauth.OAuthClient {
-	providerName := "openshift-v3"
-	if isOpenShift4 {
-		providerName = "openshift-v4"
-	}
-
-	redirectURLSuffix := "/realms/" + keycloakRealm + "/broker/" + providerName + "/endpoint"
-	redirectURIs := []string{
-		keycloakURL + redirectURLSuffix,
-	}
-
-	keycloakURL = strings.NewReplacer("https://", "", "http://", "").Replace(keycloakURL)
-	if !strings.Contains(keycloakURL, "://") {
-		redirectURIs = []string{
-			"http://" + keycloakURL + redirectURLSuffix,
-			"https://" + keycloakURL + redirectURLSuffix,
-		}
-	}
-	return GetOAuthClientSpec(name, oauthSecret, redirectURIs)
-}
-
-func GetOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) *oauth.OAuthClient {
-	return &oauth.OAuthClient{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "OAuthClient",
-			APIVersion: oauth.SchemeGroupVersion.String(),
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:   name,
-			Labels: map[string]string{"app": "che"},
-		},
-
-		Secret:       oauthSecret,
-		RedirectURIs: redirectURIs,
-		GrantMethod:  oauth.GrantHandlerPrompt,
-	}
-}
-
-func ReconcileOAuthClientFinalizer(deployContext *DeployContext) (err error) {
-	cheCluster := deployContext.CheCluster
-	if deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
-		return AppendFinalizer(deployContext, OAuthFinalizerName)
-	} else {
-		oAuthClientName := cheCluster.Spec.Auth.OAuthClientName
-		return DeleteObjectWithFinalizer(deployContext, types.NamespacedName{Name: oAuthClientName}, &oauth.OAuthClient{}, OAuthFinalizerName)
-	}
-}
diff --git a/pkg/deploy/pluginregistry/pluginregistry.go b/pkg/deploy/pluginregistry/pluginregistry.go
index 61940720fd..a8ffda0427 100644
--- a/pkg/deploy/pluginregistry/pluginregistry.go
+++ b/pkg/deploy/pluginregistry/pluginregistry.go
@@ -16,79 +16,93 @@ import (
 	"strings"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/deploy/expose"
 )
 
-type PluginRegistry struct {
-	deployContext *deploy.DeployContext
+type PluginRegistryReconciler struct {
+	deploy.Reconcilable
 }
 
-func NewPluginRegistry(deployContext *deploy.DeployContext) *PluginRegistry {
-	return &PluginRegistry{
-		deployContext: deployContext,
-	}
+func NewPluginRegistryReconciler() *PluginRegistryReconciler {
+	return &PluginRegistryReconciler{}
 }
 
-func (p *PluginRegistry) SyncAll() (bool, error) {
-	done, err := p.SyncService()
+func (p *PluginRegistryReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	if ctx.CheCluster.Spec.Server.ExternalPluginRegistry {
+		if ctx.CheCluster.Spec.Server.PluginRegistryUrl != ctx.CheCluster.Status.PluginRegistryURL {
+			ctx.CheCluster.Status.PluginRegistryURL = ctx.CheCluster.Spec.Server.PluginRegistryUrl
+			if err := deploy.UpdateCheCRStatus(ctx, "PluginRegistryUrl", ctx.CheCluster.Spec.Server.PluginRegistryUrl); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		}
+
+		return reconcile.Result{}, true, nil
+	}
+
+	done, err := p.syncService(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	endpoint, done, err := p.ExposeEndpoint()
+	endpoint, done, err := p.ExposeEndpoint(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.UpdateStatus(endpoint)
+	done, err = p.updateStatus(endpoint, ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncConfigMap()
+	done, err = p.syncConfigMap(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncDeployment()
+	done, err = p.syncDeployment(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	return true, nil
+	return reconcile.Result{}, true, nil
+}
+
+func (p *PluginRegistryReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
 }
 
-func (p *PluginRegistry) SyncService() (bool, error) {
+func (p *PluginRegistryReconciler) syncService(ctx *deploy.DeployContext) (bool, error) {
 	return deploy.SyncServiceToCluster(
-		p.deployContext,
+		ctx,
 		deploy.PluginRegistryName,
 		[]string{"http"},
 		[]int32{8080},
 		deploy.PluginRegistryName)
 }
 
-func (p *PluginRegistry) SyncConfigMap() (bool, error) {
-	data, err := p.GetConfigMapData()
+func (p *PluginRegistryReconciler) syncConfigMap(ctx *deploy.DeployContext) (bool, error) {
+	data, err := p.getConfigMapData(ctx)
 	if err != nil {
 		return false, err
 	}
-	return deploy.SyncConfigMapDataToCluster(p.deployContext, deploy.PluginRegistryName, data, deploy.PluginRegistryName)
+	return deploy.SyncConfigMapDataToCluster(ctx, deploy.PluginRegistryName, data, deploy.PluginRegistryName)
 }
 
-func (p *PluginRegistry) ExposeEndpoint() (string, bool, error) {
+func (p *PluginRegistryReconciler) ExposeEndpoint(ctx *deploy.DeployContext) (string, bool, error) {
 	return expose.Expose(
-		p.deployContext,
+		ctx,
 		deploy.PluginRegistryName,
-		p.deployContext.CheCluster.Spec.Server.PluginRegistryRoute,
-		p.deployContext.CheCluster.Spec.Server.PluginRegistryIngress,
-		p.createGatewayConfig())
+		ctx.CheCluster.Spec.Server.PluginRegistryRoute,
+		ctx.CheCluster.Spec.Server.PluginRegistryIngress,
+		p.createGatewayConfig(ctx))
 }
 
-func (p *PluginRegistry) UpdateStatus(endpoint string) (bool, error) {
+func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) {
 	var pluginRegistryURL string
-	if p.deployContext.CheCluster.Spec.Server.TlsSupport {
+	if ctx.CheCluster.Spec.Server.TlsSupport {
 		pluginRegistryURL = "https://" + endpoint
 	} else {
 		pluginRegistryURL = "http://" + endpoint
@@ -101,9 +115,9 @@ func (p *PluginRegistry) UpdateStatus(endpoint string) (bool, error) {
 		pluginRegistryURL = pluginRegistryURL + "v3"
 	}
 
-	if pluginRegistryURL != p.deployContext.CheCluster.Status.PluginRegistryURL {
-		p.deployContext.CheCluster.Status.PluginRegistryURL = pluginRegistryURL
-		if err := deploy.UpdateCheCRStatus(p.deployContext, "status: Plugin Registry URL", pluginRegistryURL); err != nil {
+	if pluginRegistryURL != ctx.CheCluster.Status.PluginRegistryURL {
+		ctx.CheCluster.Status.PluginRegistryURL = pluginRegistryURL
+		if err := deploy.UpdateCheCRStatus(ctx, "status: Plugin Registry URL", pluginRegistryURL); err != nil {
 			return false, err
 		}
 	}
@@ -111,12 +125,12 @@ func (p *PluginRegistry) UpdateStatus(endpoint string) (bool, error) {
 	return true, nil
 }
 
-func (p *PluginRegistry) SyncDeployment() (bool, error) {
-	spec := p.GetPluginRegistryDeploymentSpec()
-	return deploy.SyncDeploymentSpecToCluster(p.deployContext, spec, deploy.DefaultDeploymentDiffOpts)
+func (p *PluginRegistryReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
+	spec := p.getPluginRegistryDeploymentSpec(ctx)
+	return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts)
 }
 
-func (p *PluginRegistry) createGatewayConfig() *gateway.TraefikConfig {
+func (p *PluginRegistryReconciler) createGatewayConfig(ctx *deploy.DeployContext) *gateway.TraefikConfig {
 	pathPrefix := "/" + deploy.PluginRegistryName
 	cfg := gateway.CreateCommonTraefikConfig(
 		deploy.PluginRegistryName,
diff --git a/pkg/deploy/pluginregistry/pluginregistry_configmap.go b/pkg/deploy/pluginregistry/pluginregistry_configmap.go
index dd5635255e..d7b6ace49e 100644
--- a/pkg/deploy/pluginregistry/pluginregistry_configmap.go
+++ b/pkg/deploy/pluginregistry/pluginregistry_configmap.go
@@ -13,6 +13,8 @@ package pluginregistry
 
 import (
 	"encoding/json"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
 )
 
 type PluginRegistryConfigMap struct {
@@ -21,12 +23,12 @@ type PluginRegistryConfigMap struct {
 	ChePluginRegistryURL                     string `json:"CHE_PLUGIN_REGISTRY_URL"`
 }
 
-func (p *PluginRegistry) GetConfigMapData() (map[string]string, error) {
+func (p *PluginRegistryReconciler) getConfigMapData(ctx *deploy.DeployContext) (map[string]string, error) {
 	pluginRegistryEnv := make(map[string]string)
 	data := &PluginRegistryConfigMap{
-		CheSidecarContainersRegistryURL:          p.deployContext.CheCluster.Spec.Server.AirGapContainerRegistryHostname,
-		CheSidecarContainersRegistryOrganization: p.deployContext.CheCluster.Spec.Server.AirGapContainerRegistryOrganization,
-		ChePluginRegistryURL:                     p.deployContext.CheCluster.Status.PluginRegistryURL,
+		CheSidecarContainersRegistryURL:          ctx.CheCluster.Spec.Server.AirGapContainerRegistryHostname,
+		CheSidecarContainersRegistryOrganization: ctx.CheCluster.Spec.Server.AirGapContainerRegistryOrganization,
+		ChePluginRegistryURL:                     ctx.CheCluster.Status.PluginRegistryURL,
 	}
 
 	out, err := json.Marshal(data)
diff --git a/pkg/deploy/pluginregistry/pluginregistry_deployment.go b/pkg/deploy/pluginregistry/pluginregistry_deployment.go
index 097160b14e..dc91b680b0 100644
--- a/pkg/deploy/pluginregistry/pluginregistry_deployment.go
+++ b/pkg/deploy/pluginregistry/pluginregistry_deployment.go
@@ -19,34 +19,34 @@ import (
 	corev1 "k8s.io/api/core/v1"
 )
 
-func (p *PluginRegistry) GetPluginRegistryDeploymentSpec() *appsv1.Deployment {
+func (p *PluginRegistryReconciler) getPluginRegistryDeploymentSpec(ctx *deploy.DeployContext) *appsv1.Deployment {
 	registryType := "plugin"
-	registryImage := util.GetValue(p.deployContext.CheCluster.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(p.deployContext.CheCluster))
-	registryImagePullPolicy := corev1.PullPolicy(util.GetValue(string(p.deployContext.CheCluster.Spec.Server.PluginRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
+	registryImage := util.GetValue(ctx.CheCluster.Spec.Server.PluginRegistryImage, deploy.DefaultPluginRegistryImage(ctx.CheCluster))
+	registryImagePullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.PluginRegistryPullPolicy), deploy.DefaultPullPolicyFromDockerImage(registryImage)))
 	probePath := "/v3/plugins/"
 	pluginImagesEnv := util.GetEnvByRegExp("^.*plugin_registry_image.*$")
 
 	resources := corev1.ResourceRequirements{
 		Requests: corev1.ResourceList{
 			corev1.ResourceMemory: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.PluginRegistryMemoryRequest,
+				ctx.CheCluster.Spec.Server.PluginRegistryMemoryRequest,
 				deploy.DefaultPluginRegistryMemoryRequest),
 			corev1.ResourceCPU: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.PluginRegistryCpuRequest,
+				ctx.CheCluster.Spec.Server.PluginRegistryCpuRequest,
 				deploy.DefaultPluginRegistryCpuRequest),
 		},
 		Limits: corev1.ResourceList{
 			corev1.ResourceMemory: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.PluginRegistryMemoryLimit,
+				ctx.CheCluster.Spec.Server.PluginRegistryMemoryLimit,
 				deploy.DefaultPluginRegistryMemoryLimit),
 			corev1.ResourceCPU: util.GetResourceQuantity(
-				p.deployContext.CheCluster.Spec.Server.PluginRegistryCpuLimit,
+				ctx.CheCluster.Spec.Server.PluginRegistryCpuLimit,
 				deploy.DefaultPluginRegistryCpuLimit),
 		},
 	}
 
 	return registry.GetSpecRegistryDeployment(
-		p.deployContext,
+		ctx,
 		registryType,
 		registryImage,
 		pluginImagesEnv,
diff --git a/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go b/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go
index a5c607933a..fb48eb8aea 100644
--- a/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go
+++ b/pkg/deploy/pluginregistry/pluginregistry_deployment_test.go
@@ -12,8 +12,6 @@
 package pluginregistry
 
 import (
-	"os"
-
 	"github.com/eclipse-che/che-operator/pkg/util"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
@@ -21,10 +19,6 @@ import (
 	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
 	"testing"
 )
@@ -51,6 +45,7 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -64,6 +59,7 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Server: orgv1.CheClusterSpecServer{
@@ -79,22 +75,10 @@ func TestGetPluginRegistryDeploymentSpec(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
-
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.cheCluster,
-				ClusterAPI: deploy.ClusterAPI{
-					Client: cli,
-					Scheme: scheme.Scheme,
-				},
-				Proxy: &deploy.Proxy{},
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			pluginregistry := NewPluginRegistry(deployContext)
-			deployment := pluginregistry.GetPluginRegistryDeploymentSpec()
+			pluginregistry := NewPluginRegistryReconciler()
+			deployment := pluginregistry.getPluginRegistryDeploymentSpec(ctx)
 
 			util.CompareResources(deployment,
 				util.TestExpectedResources{
diff --git a/pkg/deploy/pluginregistry/pluginregistry_test.go b/pkg/deploy/pluginregistry/pluginregistry_test.go
index ba146848f8..9e0beac9cf 100644
--- a/pkg/deploy/pluginregistry/pluginregistry_test.go
+++ b/pkg/deploy/pluginregistry/pluginregistry_test.go
@@ -12,81 +12,31 @@
 package pluginregistry
 
 import (
-	"context"
-
 	"github.com/eclipse-che/che-operator/pkg/deploy"
 	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/stretchr/testify/assert"
 
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
 	routev1 "github.com/openshift/api/route/v1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	"testing"
 )
 
-func TestPluginRegistrySyncAll(t *testing.T) {
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      "eclipse-che",
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
+func TestPluginRegistryReconcile(t *testing.T) {
 	util.IsOpenShift = true
-
-	pluginregistry := NewPluginRegistry(deployContext)
-	done, err := pluginregistry.SyncAll()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Plugin Registry: %v", err)
-	}
-
-	// check service
-	service := &corev1.Service{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Service not found: %v", err)
-	}
-
-	// check endpoint
-	route := &routev1.Route{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, route)
-	if err != nil {
-		t.Fatalf("Route not found: %v", err)
-	}
-
-	// check configmap
-	cm := &corev1.ConfigMap{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, cm)
-	if err != nil {
-		t.Fatalf("Config Map not found: %v", err)
-	}
-
-	// check deployment
-	deployment := &appsv1.Deployment{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, deployment)
-	if err != nil {
-		t.Fatalf("Deployment not found: %v", err)
-	}
-
-	if cheCluster.Status.PluginRegistryURL == "" {
-		t.Fatalf("Status wasn't updated")
-	}
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
+
+	pluginregistry := NewPluginRegistryReconciler()
+	_, done, err := pluginregistry.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.Service{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &routev1.Route{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.NotEmpty(t, ctx.CheCluster.Status.PluginRegistryURL)
 }
diff --git a/pkg/deploy/postgres/postgres.go b/pkg/deploy/postgres/postgres.go
index 64c4194795..a67465b3d0 100644
--- a/pkg/deploy/postgres/postgres.go
+++ b/pkg/deploy/postgres/postgres.go
@@ -21,62 +21,69 @@ import (
 	"github.com/sirupsen/logrus"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-type Postgres struct {
-	deployContext *deploy.DeployContext
+type PostgresReconciler struct {
+	deploy.Reconcilable
 }
 
-func NewPostgres(deployContext *deploy.DeployContext) *Postgres {
-	return &Postgres{
-		deployContext: deployContext,
-	}
+func NewPostgresReconciler() *PostgresReconciler {
+	return &PostgresReconciler{}
 }
 
-func (p *Postgres) SyncAll() (bool, error) {
-	done, err := p.SyncService()
+func (p *PostgresReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	if ctx.CheCluster.Spec.Database.ExternalDb {
+		return reconcile.Result{}, true, nil
+	}
+
+	done, err := p.syncService(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncPVC()
+	done, err = p.syncPVC(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	done, err = p.SyncDeployment()
+	done, err = p.syncDeployment(ctx)
 	if !done {
-		return false, err
+		return reconcile.Result{}, false, err
 	}
 
-	if !p.deployContext.CheCluster.Status.DbProvisoned {
+	if !ctx.CheCluster.Status.DbProvisoned {
 		if !util.IsTestMode() { // ignore in tests
-			done, err = p.ProvisionDB()
+			done, err = p.provisionDB(ctx)
 			if !done {
-				return false, err
+				return reconcile.Result{}, false, err
 			}
 		}
 	}
 
-	if p.deployContext.CheCluster.Spec.Database.PostgresVersion == "" {
+	if ctx.CheCluster.Spec.Database.PostgresVersion == "" {
 		if !util.IsTestMode() { // ignore in tests
-			done, err := p.setDbVersion()
+			done, err := p.setDbVersion(ctx)
 			if !done {
-				return false, err
+				return reconcile.Result{}, false, err
 			}
 		}
 	}
 
-	return true, nil
+	return reconcile.Result{}, true, nil
+}
+
+func (p *PostgresReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
 }
 
-func (p *Postgres) SyncService() (bool, error) {
-	return deploy.SyncServiceToCluster(p.deployContext, deploy.PostgresName, []string{deploy.PostgresName}, []int32{5432}, deploy.PostgresName)
+func (p *PostgresReconciler) syncService(ctx *deploy.DeployContext) (bool, error) {
+	return deploy.SyncServiceToCluster(ctx, deploy.PostgresName, []string{deploy.PostgresName}, []int32{5432}, deploy.PostgresName)
 }
 
-func (p *Postgres) SyncPVC() (bool, error) {
-	pvcClaimSize := util.GetValue(p.deployContext.CheCluster.Spec.Database.PvcClaimSize, deploy.DefaultPostgresPvcClaimSize)
-	done, err := deploy.SyncPVCToCluster(p.deployContext, deploy.DefaultPostgresVolumeClaimName, pvcClaimSize, deploy.PostgresName)
+func (p *PostgresReconciler) syncPVC(ctx *deploy.DeployContext) (bool, error) {
+	pvcClaimSize := util.GetValue(ctx.CheCluster.Spec.Database.PvcClaimSize, deploy.DefaultPostgresPvcClaimSize)
+	done, err := deploy.SyncPVCToCluster(ctx, deploy.DefaultPostgresVolumeClaimName, pvcClaimSize, deploy.PostgresName)
 	if !done {
 		if err == nil {
 			logrus.Infof("Waiting on pvc '%s' to be bound. Sometimes PVC can be bound only when the first consumer is created.", deploy.DefaultPostgresVolumeClaimName)
@@ -85,9 +92,9 @@ func (p *Postgres) SyncPVC() (bool, error) {
 	return done, err
 }
 
-func (p *Postgres) SyncDeployment() (bool, error) {
+func (p *PostgresReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
 	clusterDeployment := &appsv1.Deployment{}
-	exists, err := deploy.GetNamespacedObject(p.deployContext, deploy.PostgresName, clusterDeployment)
+	exists, err := deploy.GetNamespacedObject(ctx, deploy.PostgresName, clusterDeployment)
 	if err != nil {
 		return false, err
 	}
@@ -96,20 +103,20 @@ func (p *Postgres) SyncDeployment() (bool, error) {
 		clusterDeployment = nil
 	}
 
-	specDeployment, err := p.GetDeploymentSpec(clusterDeployment)
+	specDeployment, err := p.getDeploymentSpec(clusterDeployment, ctx)
 	if err != nil {
 		return false, err
 	}
 
-	return deploy.SyncDeploymentSpecToCluster(p.deployContext, specDeployment, deploy.DefaultDeploymentDiffOpts)
+	return deploy.SyncDeploymentSpecToCluster(ctx, specDeployment, deploy.DefaultDeploymentDiffOpts)
 }
 
-func (p *Postgres) ProvisionDB() (bool, error) {
-	identityProviderPostgresPassword := p.deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresPassword
-	identityProviderPostgresSecret := p.deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret
+func (p *PostgresReconciler) provisionDB(ctx *deploy.DeployContext) (bool, error) {
+	identityProviderPostgresPassword := ctx.CheCluster.Spec.Auth.IdentityProviderPostgresPassword
+	identityProviderPostgresSecret := ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret
 	if identityProviderPostgresSecret != "" {
 		secret := &corev1.Secret{}
-		exists, err := deploy.GetNamespacedObject(p.deployContext, identityProviderPostgresSecret, secret)
+		exists, err := deploy.GetNamespacedObject(ctx, identityProviderPostgresSecret, secret)
 		if err != nil {
 			return false, err
 		} else if !exists {
@@ -119,7 +126,7 @@ func (p *Postgres) ProvisionDB() (bool, error) {
 	}
 
 	_, err := util.K8sclient.ExecIntoPod(
-		p.deployContext.CheCluster,
+		ctx.CheCluster,
 		deploy.PostgresName,
 		func(cr *orgv1.CheCluster) (string, error) {
 			return getPostgresProvisionCommand(identityProviderPostgresPassword), nil
@@ -129,8 +136,8 @@ func (p *Postgres) ProvisionDB() (bool, error) {
 		return false, err
 	}
 
-	p.deployContext.CheCluster.Status.DbProvisoned = true
-	err = deploy.UpdateCheCRStatus(p.deployContext, "status: provisioned with DB and user", "true")
+	ctx.CheCluster.Status.DbProvisoned = true
+	err = deploy.UpdateCheCRStatus(ctx, "status: provisioned with DB and user", "true")
 	if err != nil {
 		return false, err
 	}
@@ -138,9 +145,9 @@ func (p *Postgres) ProvisionDB() (bool, error) {
 	return true, nil
 }
 
-func (p *Postgres) setDbVersion() (bool, error) {
+func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, error) {
 	postgresVersion, err := util.K8sclient.ExecIntoPod(
-		p.deployContext.CheCluster,
+		ctx.CheCluster,
 		deploy.PostgresName,
 		func(cr *orgv1.CheCluster) (string, error) {
 			// don't take into account bugfix version
@@ -152,8 +159,8 @@ func (p *Postgres) setDbVersion() (bool, error) {
 	}
 
 	postgresVersion = strings.TrimSpace(postgresVersion)
-	p.deployContext.CheCluster.Spec.Database.PostgresVersion = postgresVersion
-	err = deploy.UpdateCheCRSpec(p.deployContext, "database.postgresVersion", postgresVersion)
+	ctx.CheCluster.Spec.Database.PostgresVersion = postgresVersion
+	err = deploy.UpdateCheCRSpec(ctx, "database.postgresVersion", postgresVersion)
 	if err != nil {
 		return false, err
 	}
diff --git a/pkg/deploy/postgres/postgres_deployment.go b/pkg/deploy/postgres/postgres_deployment.go
index 40dc9534a9..334aa7b01d 100644
--- a/pkg/deploy/postgres/postgres_deployment.go
+++ b/pkg/deploy/postgres/postgres_deployment.go
@@ -32,15 +32,15 @@ var (
 	postgresAdminPassword = util.GeneratePasswd(12)
 )
 
-func (p *Postgres) GetDeploymentSpec(clusterDeployment *appsv1.Deployment) (*appsv1.Deployment, error) {
+func (p *PostgresReconciler) getDeploymentSpec(clusterDeployment *appsv1.Deployment, ctx *deploy.DeployContext) (*appsv1.Deployment, error) {
 	terminationGracePeriodSeconds := int64(30)
-	labels, labelSelector := deploy.GetLabelsAndSelector(p.deployContext.CheCluster, deploy.PostgresName)
-	chePostgresDb := util.GetValue(p.deployContext.CheCluster.Spec.Database.ChePostgresDb, "dbche")
-	postgresImage, err := getPostgresImage(clusterDeployment, p.deployContext.CheCluster)
+	labels, labelSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, deploy.PostgresName)
+	chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, "dbche")
+	postgresImage, err := getPostgresImage(clusterDeployment, ctx.CheCluster)
 	if err != nil {
 		return nil, err
 	}
-	pullPolicy := corev1.PullPolicy(util.GetValue(string(p.deployContext.CheCluster.Spec.Database.PostgresImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(postgresImage)))
+	pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Database.PostgresImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(postgresImage)))
 
 	if clusterDeployment != nil {
 		clusterContainer := &clusterDeployment.Spec.Template.Spec.Containers[0]
@@ -57,7 +57,7 @@ func (p *Postgres) GetDeploymentSpec(clusterDeployment *appsv1.Deployment) (*app
 		},
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      deploy.PostgresName,
-			Namespace: p.deployContext.CheCluster.Namespace,
+			Namespace: ctx.CheCluster.Namespace,
 			Labels:    labels,
 		},
 		Spec: appsv1.DeploymentSpec{
@@ -95,18 +95,18 @@ func (p *Postgres) GetDeploymentSpec(clusterDeployment *appsv1.Deployment) (*app
 							Resources: corev1.ResourceRequirements{
 								Requests: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										p.deployContext.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Memory,
+										ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Memory,
 										deploy.DefaultPostgresMemoryRequest),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										p.deployContext.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Cpu,
+										ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Requests.Cpu,
 										deploy.DefaultPostgresCpuRequest),
 								},
 								Limits: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										p.deployContext.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Memory,
+										ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Memory,
 										deploy.DefaultPostgresMemoryLimit),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										p.deployContext.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Cpu,
+										ctx.CheCluster.Spec.Database.ChePostgresContainerResources.Limits.Cpu,
 										deploy.DefaultPostgresCpuLimit),
 								},
 							},
@@ -170,7 +170,7 @@ func (p *Postgres) GetDeploymentSpec(clusterDeployment *appsv1.Deployment) (*app
 
 	container := &deployment.Spec.Template.Spec.Containers[0]
 
-	chePostgresSecret := p.deployContext.CheCluster.Spec.Database.ChePostgresSecret
+	chePostgresSecret := ctx.CheCluster.Spec.Database.ChePostgresSecret
 	if len(chePostgresSecret) > 0 {
 		container.Env = append(container.Env,
 			corev1.EnvVar{
@@ -198,10 +198,10 @@ func (p *Postgres) GetDeploymentSpec(clusterDeployment *appsv1.Deployment) (*app
 		container.Env = append(container.Env,
 			corev1.EnvVar{
 				Name:  "POSTGRESQL_USER",
-				Value: p.deployContext.CheCluster.Spec.Database.ChePostgresUser,
+				Value: ctx.CheCluster.Spec.Database.ChePostgresUser,
 			}, corev1.EnvVar{
 				Name:  "POSTGRESQL_PASSWORD",
-				Value: p.deployContext.CheCluster.Spec.Database.ChePostgresPassword,
+				Value: ctx.CheCluster.Spec.Database.ChePostgresPassword,
 			})
 	}
 
diff --git a/pkg/deploy/postgres/postgres_test.go b/pkg/deploy/postgres/postgres_test.go
index 127b424e73..3fdcad921c 100644
--- a/pkg/deploy/postgres/postgres_test.go
+++ b/pkg/deploy/postgres/postgres_test.go
@@ -12,7 +12,6 @@
 package postgres
 
 import (
-	"context"
 	"fmt"
 	"os"
 
@@ -27,8 +26,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
@@ -57,6 +54,7 @@ func TestDeploymentSpec(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 			},
 		},
@@ -70,6 +68,7 @@ func TestDeploymentSpec(t *testing.T) {
 			cheCluster: &orgv1.CheCluster{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: "eclipse-che",
+					Name:      "eclipse-che",
 				},
 				Spec: orgv1.CheClusterSpec{
 					Database: orgv1.CheClusterSpecDB{
@@ -92,25 +91,12 @@ func TestDeploymentSpec(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
-			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-			testCase.initObjects = append(testCase.initObjects)
-			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
 
-			deployContext := &deploy.DeployContext{
-				CheCluster: testCase.cheCluster,
-				ClusterAPI: deploy.ClusterAPI{
-					Client: cli,
-					Scheme: scheme.Scheme,
-				},
-				Proxy: &deploy.Proxy{},
-			}
-
-			postgres := NewPostgres(deployContext)
-			deployment, err := postgres.GetDeploymentSpec(nil)
-			if err != nil {
-				t.Fatalf("Error creating deployment: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
+			postgres := NewPostgresReconciler()
 
+			deployment, err := postgres.getDeploymentSpec(nil, ctx)
+			assert.Nil(t, err)
 			util.CompareResources(deployment,
 				util.TestExpectedResources{
 					MemoryLimit:   testCase.memoryLimit,
@@ -125,47 +111,18 @@ func TestDeploymentSpec(t *testing.T) {
 	}
 }
 
-func TestSyncAllToCluster(t *testing.T) {
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme)
-	deployContext := &deploy.DeployContext{
-		CheCluster: &orgv1.CheCluster{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "eclipse-che",
-				Name:      "eclipse-che",
-			},
-		},
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	postgres := NewPostgres(deployContext)
-	done, err := postgres.SyncAll()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync PostgreSQL: %v", err)
-	}
+func TestPostgresReconcile(t *testing.T) {
+	util.IsOpenShift = true
+	ctx := deploy.GetTestDeployContext(nil, []runtime.Object{})
 
-	service := &corev1.Service{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Failed to get service: %v", err)
-	}
+	postgres := NewPostgresReconciler()
+	_, done, err := postgres.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
 
-	pvc := &corev1.PersistentVolumeClaim{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: "eclipse-che"}, pvc)
-	if err != nil {
-		t.Fatalf("Failed to get pvc: %v", err)
-	}
-
-	deployment := &appsv1.Deployment{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: "eclipse-che"}, deployment)
-	if err != nil {
-		t.Fatalf("Failed to get deployment: %v", err)
-	}
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &corev1.Service{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres-data", Namespace: "eclipse-che"}, &corev1.PersistentVolumeClaim{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "postgres", Namespace: "eclipse-che"}, &appsv1.Deployment{}))
 }
 
 func TestGetPostgresImage(t *testing.T) {
diff --git a/pkg/deploy/server/server.go b/pkg/deploy/server/server.go
deleted file mode 100644
index 604f59462e..0000000000
--- a/pkg/deploy/server/server.go
+++ /dev/null
@@ -1,334 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package server
-
-import (
-	"context"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
-	"github.com/eclipse-che/che-operator/pkg/deploy"
-	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
-	"github.com/eclipse-che/che-operator/pkg/util"
-	routev1 "github.com/openshift/api/route/v1"
-	"github.com/sirupsen/logrus"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	networking "k8s.io/api/networking/v1"
-)
-
-const (
-	AvailableStatus               = "Available"
-	UnavailableStatus             = "Unavailable"
-	RollingUpdateInProgressStatus = "Available: Rolling update in progress"
-)
-
-type Server struct {
-	deployContext *deploy.DeployContext
-	component     string
-}
-
-func NewServer(deployContext *deploy.DeployContext) *Server {
-	return &Server{
-		deployContext: deployContext,
-		component:     deploy.DefaultCheFlavor(deployContext.CheCluster),
-	}
-}
-
-func (s *Server) ExposeCheServiceAndEndpoint() (bool, error) {
-	done, err := s.DetectDefaultCheHost()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.SyncCheService()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.ExposeCheEndpoint()
-	if !done {
-		return false, err
-	}
-
-	return true, nil
-}
-
-func (s *Server) SyncAll() (bool, error) {
-	done, err := s.SyncLegacyConfigMap()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.SyncCheConfigMap()
-	if !done {
-		return false, err
-	}
-
-	// ensure configmap is created
-	// the version of the object is used in the deployment
-	exists, err := deploy.GetNamespacedObject(s.deployContext, CheConfigMapName, &corev1.ConfigMap{})
-	if !exists {
-		return false, err
-	}
-
-	done, err = s.SyncDeployment()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.UpdateAvailabilityStatus()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.UpdateCheURL()
-	if !done {
-		return false, err
-	}
-
-	done, err = s.UpdateCheVersion()
-	if !done {
-		return false, err
-	}
-
-	return true, nil
-}
-
-func (s *Server) SyncCheService() (bool, error) {
-	portName := []string{"http"}
-	portNumber := []int32{8080}
-
-	if s.deployContext.CheCluster.Spec.Metrics.Enable {
-		portName = append(portName, "metrics")
-		portNumber = append(portNumber, deploy.DefaultCheMetricsPort)
-	}
-
-	if s.deployContext.CheCluster.Spec.Server.CheDebug == "true" {
-		portName = append(portName, "debug")
-		portNumber = append(portNumber, deploy.DefaultCheDebugPort)
-	}
-
-	spec := deploy.GetServiceSpec(s.deployContext, deploy.CheServiceName, portName, portNumber, s.component)
-	return deploy.Sync(s.deployContext, spec, deploy.ServiceDefaultDiffOpts)
-}
-
-func (s Server) ExposeCheEndpoint() (bool, error) {
-	cheHost := ""
-	exposedServiceName := GetServerExposingServiceName(s.deployContext.CheCluster)
-
-	if !util.IsOpenShift {
-		_, done, err := deploy.SyncIngressToCluster(
-			s.deployContext,
-			s.component,
-			s.deployContext.CheCluster.Spec.Server.CheHost,
-			"",
-			exposedServiceName,
-			8080,
-			s.deployContext.CheCluster.Spec.Server.CheServerIngress,
-			s.component)
-		if !done {
-			return false, err
-		}
-
-		ingress := &networking.Ingress{}
-		exists, err := deploy.GetNamespacedObject(s.deployContext, s.component, ingress)
-		if !exists {
-			return false, err
-		}
-
-		cheHost = ingress.Spec.Rules[0].Host
-	} else {
-		customHost := s.deployContext.CheCluster.Spec.Server.CheHost
-		if s.deployContext.DefaultCheHost == customHost {
-			// let OpenShift set a hostname by itself since it requires a routes/custom-host permissions
-			customHost = ""
-		}
-
-		done, err := deploy.SyncRouteToCluster(
-			s.deployContext,
-			s.component,
-			customHost,
-			"/",
-			exposedServiceName,
-			8080,
-			s.deployContext.CheCluster.Spec.Server.CheServerRoute,
-			s.component)
-		if !done {
-			return false, err
-		}
-
-		route := &routev1.Route{}
-		exists, err := deploy.GetNamespacedObject(s.deployContext, s.component, route)
-		if !exists {
-			return false, err
-		}
-
-		if customHost == "" {
-			s.deployContext.DefaultCheHost = route.Spec.Host
-		}
-		cheHost = route.Spec.Host
-	}
-
-	if s.deployContext.CheCluster.Spec.Server.CheHost != cheHost {
-		s.deployContext.CheCluster.Spec.Server.CheHost = cheHost
-		err := deploy.UpdateCheCRSpec(s.deployContext, "CheHost URL", cheHost)
-		return err == nil, err
-	}
-
-	return true, nil
-}
-
-func (s Server) UpdateCheURL() (bool, error) {
-	var cheUrl = util.GetCheURL(s.deployContext.CheCluster)
-	if s.deployContext.CheCluster.Status.CheURL != cheUrl {
-		s.deployContext.CheCluster.Status.CheURL = cheUrl
-		err := deploy.UpdateCheCRStatus(s.deployContext, s.component+" server URL", cheUrl)
-		return err == nil, err
-	}
-
-	return true, nil
-}
-
-func (s *Server) SyncCheConfigMap() (bool, error) {
-	data, err := s.getCheConfigMapData()
-	if err != nil {
-		return false, err
-	}
-
-	return deploy.SyncConfigMapDataToCluster(s.deployContext, CheConfigMapName, data, s.component)
-}
-
-func (s Server) SyncLegacyConfigMap() (bool, error) {
-	// Get custom ConfigMap
-	// if it exists, add the data into CustomCheProperties
-	customConfigMap := &corev1.ConfigMap{}
-	exists, err := deploy.GetNamespacedObject(s.deployContext, "custom", customConfigMap)
-	if err != nil {
-		return false, err
-	} else if exists {
-		logrus.Info("Found legacy custom ConfigMap. Adding those values to CheCluster.Spec.Server.CustomCheProperties")
-
-		if s.deployContext.CheCluster.Spec.Server.CustomCheProperties == nil {
-			s.deployContext.CheCluster.Spec.Server.CustomCheProperties = make(map[string]string)
-		}
-		for k, v := range customConfigMap.Data {
-			s.deployContext.CheCluster.Spec.Server.CustomCheProperties[k] = v
-		}
-
-		err := s.deployContext.ClusterAPI.Client.Update(context.TODO(), s.deployContext.CheCluster)
-		if err != nil {
-			return false, err
-		}
-
-		return deploy.DeleteNamespacedObject(s.deployContext, "custom", &corev1.ConfigMap{})
-	}
-
-	return true, nil
-}
-
-func (s *Server) UpdateAvailabilityStatus() (bool, error) {
-	cheDeployment := &appsv1.Deployment{}
-	exists, err := deploy.GetNamespacedObject(s.deployContext, s.component, cheDeployment)
-	if err != nil {
-		return false, err
-	}
-
-	if exists {
-		if cheDeployment.Status.AvailableReplicas < 1 {
-			if s.deployContext.CheCluster.Status.CheClusterRunning != UnavailableStatus {
-				s.deployContext.CheCluster.Status.CheClusterRunning = UnavailableStatus
-				err := deploy.UpdateCheCRStatus(s.deployContext, "status: Che API", UnavailableStatus)
-				return err == nil, err
-			}
-		} else if cheDeployment.Status.Replicas != 1 {
-			if s.deployContext.CheCluster.Status.CheClusterRunning != RollingUpdateInProgressStatus {
-				s.deployContext.CheCluster.Status.CheClusterRunning = RollingUpdateInProgressStatus
-				err := deploy.UpdateCheCRStatus(s.deployContext, "status: Che API", RollingUpdateInProgressStatus)
-				return err == nil, err
-			}
-		} else {
-			if s.deployContext.CheCluster.Status.CheClusterRunning != AvailableStatus {
-				cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster)
-				name := "Eclipse Che"
-				if cheFlavor == "codeready" {
-					name = "CodeReady Workspaces"
-				}
-
-				logrus.Infof(name+" is now available at: %s", util.GetCheURL(s.deployContext.CheCluster))
-				s.deployContext.CheCluster.Status.CheClusterRunning = AvailableStatus
-				err := deploy.UpdateCheCRStatus(s.deployContext, "status: Che API", AvailableStatus)
-				return err == nil, err
-			}
-		}
-	} else {
-		s.deployContext.CheCluster.Status.CheClusterRunning = UnavailableStatus
-		err := deploy.UpdateCheCRStatus(s.deployContext, "status: Che API", UnavailableStatus)
-		return err == nil, err
-	}
-
-	return true, nil
-}
-
-func (s *Server) SyncDeployment() (bool, error) {
-	spec, err := s.getDeploymentSpec()
-	if err != nil {
-		return false, err
-	}
-
-	return deploy.SyncDeploymentSpecToCluster(s.deployContext, spec, deploy.DefaultDeploymentDiffOpts)
-}
-
-func (s *Server) DetectDefaultCheHost() (bool, error) {
-	// only for OpenShift
-	if !util.IsOpenShift || s.deployContext.DefaultCheHost != "" {
-		return true, nil
-	}
-
-	done, err := deploy.SyncRouteToCluster(
-		s.deployContext,
-		s.component,
-		"",
-		"/",
-		GetServerExposingServiceName(s.deployContext.CheCluster),
-		8080,
-		s.deployContext.CheCluster.Spec.Server.CheServerRoute,
-		s.component)
-	if !done {
-		return false, err
-	}
-
-	route := &routev1.Route{}
-	exists, err := deploy.GetNamespacedObject(s.deployContext, s.component, route)
-	if !exists {
-		return false, err
-	}
-
-	s.deployContext.DefaultCheHost = route.Spec.Host
-	return true, nil
-}
-
-func (s Server) UpdateCheVersion() (bool, error) {
-	cheVersion := deploy.DefaultCheVersion()
-	if s.deployContext.CheCluster.Status.CheVersion != cheVersion {
-		s.deployContext.CheCluster.Status.CheVersion = cheVersion
-		err := deploy.UpdateCheCRStatus(s.deployContext, "version", cheVersion)
-		return err == nil, err
-	}
-	return true, nil
-}
-
-func GetServerExposingServiceName(cr *orgv1.CheCluster) string {
-	if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == deploy.GatewaySingleHostExposureType {
-		return gateway.GatewayServiceName
-	}
-	return deploy.CheServiceName
-}
diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go
index b595a98fb5..cc46e4dab8 100644
--- a/pkg/deploy/server/server_configmap.go
+++ b/pkg/deploy/server/server_configmap.go
@@ -91,14 +91,14 @@ type CheConfigMap struct {
 
 // GetCheConfigMapData gets env values from CR spec and returns a map with key:value
 // which is used in CheCluster ConfigMap to configure CheCluster master behavior
-func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
-	cheHost := s.deployContext.CheCluster.Spec.Server.CheHost
-	identityProviderURL := s.deployContext.CheCluster.Spec.Auth.IdentityProviderURL
+func (s *ServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (cheEnv map[string]string, err error) {
+	cheHost := ctx.CheCluster.Spec.Server.CheHost
+	identityProviderURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL
 
 	// Adds `/auth` for external identity providers.
 	// If identity provide is deployed by operator then `/auth` is already added.
-	if !s.deployContext.CheCluster.IsNativeUserModeEnabled() &&
-		s.deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider &&
+	if !ctx.CheCluster.IsNativeUserModeEnabled() &&
+		ctx.CheCluster.Spec.Auth.ExternalIdentityProvider &&
 		!strings.HasSuffix(identityProviderURL, "/auth") {
 		if strings.HasSuffix(identityProviderURL, "/") {
 			identityProviderURL = identityProviderURL + "auth"
@@ -107,20 +107,20 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 		}
 	}
 
-	cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster)
+	cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
 	infra := "kubernetes"
 	if util.IsOpenShift {
 		infra = "openshift"
 	}
 	tls := "false"
 	openShiftIdentityProviderId := "NULL"
-	if util.IsOpenShift && s.deployContext.CheCluster.IsOpenShiftOAuthEnabled() {
+	if util.IsOpenShift && ctx.CheCluster.IsOpenShiftOAuthEnabled() {
 		openShiftIdentityProviderId = "openshift-v3"
 		if util.IsOpenShift4 {
 			openShiftIdentityProviderId = "openshift-v4"
 		}
 	}
-	tlsSupport := s.deployContext.CheCluster.Spec.Server.TlsSupport
+	tlsSupport := ctx.CheCluster.Spec.Server.TlsSupport
 	protocol := "http"
 	if tlsSupport {
 		protocol = "https"
@@ -128,85 +128,85 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 	}
 
 	proxyJavaOpts := ""
-	cheWorkspaceNoProxy := s.deployContext.Proxy.NoProxy
-	if s.deployContext.Proxy.HttpProxy != "" {
-		if s.deployContext.Proxy.NoProxy == "" {
+	cheWorkspaceNoProxy := ctx.Proxy.NoProxy
+	if ctx.Proxy.HttpProxy != "" {
+		if ctx.Proxy.NoProxy == "" {
 			cheWorkspaceNoProxy = os.Getenv("KUBERNETES_SERVICE_HOST")
 		} else {
 			cheWorkspaceNoProxy = cheWorkspaceNoProxy + "," + os.Getenv("KUBERNETES_SERVICE_HOST")
 		}
-		proxyJavaOpts, err = deploy.GenerateProxyJavaOpts(s.deployContext.Proxy, cheWorkspaceNoProxy)
+		proxyJavaOpts, err = deploy.GenerateProxyJavaOpts(ctx.Proxy, cheWorkspaceNoProxy)
 		if err != nil {
 			logrus.Errorf("Failed to generate java proxy options: %v", err)
 		}
 	}
 
-	ingressDomain := s.deployContext.CheCluster.Spec.K8s.IngressDomain
-	tlsSecretName := s.deployContext.CheCluster.Spec.K8s.TlsSecretName
-	securityContextFsGroup := util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup)
-	securityContextRunAsUser := util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser)
-	pvcStrategy := util.GetValue(s.deployContext.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy)
-	pvcClaimSize := util.GetValue(s.deployContext.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize)
-	workspacePvcStorageClassName := s.deployContext.CheCluster.Spec.Storage.WorkspacePVCStorageClassName
+	ingressDomain := ctx.CheCluster.Spec.K8s.IngressDomain
+	tlsSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName
+	securityContextFsGroup := util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup)
+	securityContextRunAsUser := util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser)
+	pvcStrategy := util.GetValue(ctx.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy)
+	pvcClaimSize := util.GetValue(ctx.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize)
+	workspacePvcStorageClassName := ctx.CheCluster.Spec.Storage.WorkspacePVCStorageClassName
 
-	defaultPVCJobsImage := deploy.DefaultPvcJobsImage(s.deployContext.CheCluster)
-	pvcJobsImage := util.GetValue(s.deployContext.CheCluster.Spec.Storage.PvcJobsImage, defaultPVCJobsImage)
+	defaultPVCJobsImage := deploy.DefaultPvcJobsImage(ctx.CheCluster)
+	pvcJobsImage := util.GetValue(ctx.CheCluster.Spec.Storage.PvcJobsImage, defaultPVCJobsImage)
 	preCreateSubPaths := "true"
-	if !s.deployContext.CheCluster.Spec.Storage.PreCreateSubPaths {
+	if !ctx.CheCluster.Spec.Storage.PreCreateSubPaths {
 		preCreateSubPaths = "false"
 	}
-	chePostgresHostName := util.GetValue(s.deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName)
-	chePostgresPort := util.GetValue(s.deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort)
-	chePostgresDb := util.GetValue(s.deployContext.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb)
-	keycloakRealm := util.GetValue(s.deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
-	keycloakClientId := util.GetValue(s.deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
-	ingressStrategy := util.GetServerExposureStrategy(s.deployContext.CheCluster)
-	ingressClass := util.GetValue(s.deployContext.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass)
+	chePostgresHostName := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName)
+	chePostgresPort := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort)
+	chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb)
+	keycloakRealm := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
+	keycloakClientId := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
+	ingressStrategy := util.GetServerExposureStrategy(ctx.CheCluster)
+	ingressClass := util.GetValue(ctx.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass)
 
 	// grab first the devfile registry url which is deployed by operator
-	devfileRegistryURL := s.deployContext.CheCluster.Status.DevfileRegistryURL
+	devfileRegistryURL := ctx.CheCluster.Status.DevfileRegistryURL
 
 	// `Spec.Server.DevfileRegistryUrl` is deprecated in favor of `Server.ExternalDevfileRegistries`
-	if s.deployContext.CheCluster.Spec.Server.DevfileRegistryUrl != "" {
-		devfileRegistryURL += " " + s.deployContext.CheCluster.Spec.Server.DevfileRegistryUrl
+	if ctx.CheCluster.Spec.Server.DevfileRegistryUrl != "" {
+		devfileRegistryURL += " " + ctx.CheCluster.Spec.Server.DevfileRegistryUrl
 	}
-	for _, r := range s.deployContext.CheCluster.Spec.Server.ExternalDevfileRegistries {
+	for _, r := range ctx.CheCluster.Spec.Server.ExternalDevfileRegistries {
 		if strings.Index(devfileRegistryURL, r.Url) == -1 {
 			devfileRegistryURL += " " + r.Url
 		}
 	}
 	devfileRegistryURL = strings.TrimSpace(devfileRegistryURL)
 
-	pluginRegistryURL := s.deployContext.CheCluster.Status.PluginRegistryURL
-	cheLogLevel := util.GetValue(s.deployContext.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
-	cheDebug := util.GetValue(s.deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
-	cheMetrics := strconv.FormatBool(s.deployContext.CheCluster.Spec.Metrics.Enable)
-	cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(s.deployContext.CheCluster, deploy.DefaultCheFlavor(s.deployContext.CheCluster)))
-	workspaceExposure := deploy.GetSingleHostExposureType(s.deployContext.CheCluster)
-	singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(s.deployContext.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels))
-	workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(s.deployContext.CheCluster)
+	pluginRegistryURL := ctx.CheCluster.Status.PluginRegistryURL
+	cheLogLevel := util.GetValue(ctx.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
+	cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
+	cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Metrics.Enable)
+	cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(ctx.CheCluster, deploy.DefaultCheFlavor(ctx.CheCluster)))
+	workspaceExposure := deploy.GetSingleHostExposureType(ctx.CheCluster)
+	singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels))
+	workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(ctx.CheCluster)
 
 	cheAPI := protocol + "://" + cheHost + "/api"
 	var keycloakInternalURL, pluginRegistryInternalURL, devfileRegistryInternalURL, cheInternalAPI, webSocketInternalEndpoint string
 
-	if !s.deployContext.CheCluster.IsNativeUserModeEnabled() &&
-		s.deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() &&
-		!s.deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider {
-		keycloakInternalURL = fmt.Sprintf("%s://%s.%s.svc:8080/auth", "http", deploy.IdentityProviderName, s.deployContext.CheCluster.Namespace)
+	if !ctx.CheCluster.IsNativeUserModeEnabled() &&
+		ctx.CheCluster.IsInternalClusterSVCNamesEnabled() &&
+		!ctx.CheCluster.Spec.Auth.ExternalIdentityProvider {
+		keycloakInternalURL = fmt.Sprintf("%s://%s.%s.svc:8080/auth", "http", deploy.IdentityProviderName, ctx.CheCluster.Namespace)
 	}
 
 	// If there is a devfile registry deployed by operator
-	if s.deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() && !s.deployContext.CheCluster.Spec.Server.ExternalDevfileRegistry {
-		devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, s.deployContext.CheCluster.Namespace)
+	if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() && !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry {
+		devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, ctx.CheCluster.Namespace)
 	}
 
-	if s.deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() && !s.deployContext.CheCluster.Spec.Server.ExternalPluginRegistry {
-		pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, s.deployContext.CheCluster.Namespace)
+	if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() && !ctx.CheCluster.Spec.Server.ExternalPluginRegistry {
+		pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, ctx.CheCluster.Namespace)
 	}
 
-	if s.deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() {
-		cheInternalAPI = fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, s.deployContext.CheCluster.Namespace)
-		webSocketInternalEndpoint = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, s.deployContext.CheCluster.Namespace)
+	if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() {
+		cheInternalAPI = fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)
+		webSocketInternalEndpoint = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, ctx.CheCluster.Namespace)
 	}
 
 	wsprotocol := "ws"
@@ -217,9 +217,9 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 
 	cheWorkspaceServiceAccount := "che-workspace"
 	cheUserClusterRoleNames := "NULL"
-	if s.deployContext.CheCluster.IsNativeUserModeEnabled() {
+	if ctx.CheCluster.IsNativeUserModeEnabled() {
 		cheWorkspaceServiceAccount = "NULL"
-		cheUserClusterRoleNames = fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", s.deployContext.CheCluster.Namespace, s.deployContext.CheCluster.Namespace)
+		cheUserClusterRoleNames = fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", ctx.CheCluster.Namespace, ctx.CheCluster.Namespace)
 	}
 
 	data := &CheConfigMap{
@@ -248,23 +248,23 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 		WorkspaceJavaOpts:                      deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts,
 		WorkspaceMavenOpts:                     deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts,
 		WorkspaceProxyJavaOpts:                 proxyJavaOpts,
-		WorkspaceHttpProxy:                     s.deployContext.Proxy.HttpProxy,
-		WorkspaceHttpsProxy:                    s.deployContext.Proxy.HttpsProxy,
+		WorkspaceHttpProxy:                     ctx.Proxy.HttpProxy,
+		WorkspaceHttpsProxy:                    ctx.Proxy.HttpsProxy,
 		WorkspaceNoProxy:                       cheWorkspaceNoProxy,
 		PluginRegistryUrl:                      pluginRegistryURL,
 		PluginRegistryInternalUrl:              pluginRegistryInternalURL,
 		DevfileRegistryUrl:                     devfileRegistryURL,
 		DevfileRegistryInternalUrl:             devfileRegistryInternalURL,
-		CheWorkspacePluginBrokerMetadataImage:  deploy.DefaultCheWorkspacePluginBrokerMetadataImage(s.deployContext.CheCluster),
-		CheWorkspacePluginBrokerArtifactsImage: deploy.DefaultCheWorkspacePluginBrokerArtifactsImage(s.deployContext.CheCluster),
-		CheServerSecureExposerJwtProxyImage:    deploy.DefaultCheServerSecureExposerJwtProxyImage(s.deployContext.CheCluster),
+		CheWorkspacePluginBrokerMetadataImage:  deploy.DefaultCheWorkspacePluginBrokerMetadataImage(ctx.CheCluster),
+		CheWorkspacePluginBrokerArtifactsImage: deploy.DefaultCheWorkspacePluginBrokerArtifactsImage(ctx.CheCluster),
+		CheServerSecureExposerJwtProxyImage:    deploy.DefaultCheServerSecureExposerJwtProxyImage(ctx.CheCluster),
 		CheJGroupsKubernetesLabels:             cheLabels,
 		CheMetricsEnabled:                      cheMetrics,
 		CheTrustedCABundlesConfigMap:           deploytls.CheAllCACertsConfigMapName,
 		ServerStrategy:                         ingressStrategy,
 		WorkspaceExposure:                      workspaceExposure,
 		SingleHostGatewayConfigMapLabels:       singleHostGatewayConfigMapLabels,
-		CheDevWorkspacesEnabled:                strconv.FormatBool(s.deployContext.CheCluster.Spec.DevWorkspace.Enable),
+		CheDevWorkspacesEnabled:                strconv.FormatBool(ctx.CheCluster.Spec.DevWorkspace.Enable),
 	}
 
 	data.IdentityProviderUrl = identityProviderURL
@@ -272,9 +272,9 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 	data.KeycloakRealm = keycloakRealm
 	data.KeycloakClientId = keycloakClientId
 	data.DatabaseURL = "jdbc:postgresql://" + chePostgresHostName + ":" + chePostgresPort + "/" + chePostgresDb
-	if len(s.deployContext.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
-		data.DbUserName = s.deployContext.CheCluster.Spec.Database.ChePostgresUser
-		data.DbPassword = s.deployContext.CheCluster.Spec.Database.ChePostgresPassword
+	if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
+		data.DbUserName = ctx.CheCluster.Spec.Database.ChePostgresUser
+		data.DbPassword = ctx.CheCluster.Spec.Database.ChePostgresPassword
 	}
 
 	out, err := json.Marshal(data)
@@ -295,26 +295,26 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 			"CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM":             "%s(.*)",
 		}
 
-		if s.deployContext.CheCluster.Spec.DevWorkspace.Enable {
+		if ctx.CheCluster.Spec.DevWorkspace.Enable {
 			k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true"
 		}
 
 		// Add TLS key and server certificate to properties since user workspaces is created in another
 		// than Che server namespace, from where the Che TLS secret is not accessable
-		if s.deployContext.CheCluster.Spec.K8s.TlsSecretName != "" {
+		if ctx.CheCluster.Spec.K8s.TlsSecretName != "" {
 			cheTLSSecret := &corev1.Secret{}
-			exists, err := deploy.GetNamespacedObject(s.deployContext, s.deployContext.CheCluster.Spec.K8s.TlsSecretName, cheTLSSecret)
+			exists, err := deploy.GetNamespacedObject(ctx, ctx.CheCluster.Spec.K8s.TlsSecretName, cheTLSSecret)
 			if err != nil {
 				return nil, err
 			}
 			if !exists {
-				return nil, fmt.Errorf("%s secret not found", s.deployContext.CheCluster.Spec.K8s.TlsSecretName)
+				return nil, fmt.Errorf("%s secret not found", ctx.CheCluster.Spec.K8s.TlsSecretName)
 			} else {
 				if _, exists := cheTLSSecret.Data["tls.key"]; !exists {
-					return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", s.deployContext.CheCluster.Spec.K8s.TlsSecretName)
+					return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", ctx.CheCluster.Spec.K8s.TlsSecretName)
 				}
 				if _, exists := cheTLSSecret.Data["tls.crt"]; !exists {
-					return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", s.deployContext.CheCluster.Spec.K8s.TlsSecretName)
+					return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", ctx.CheCluster.Spec.K8s.TlsSecretName)
 				}
 				k8sCheEnv["CHE_INFRA_KUBERNETES_TLS__KEY"] = string(cheTLSSecret.Data["tls.key"])
 				k8sCheEnv["CHE_INFRA_KUBERNETES_TLS__CERT"] = string(cheTLSSecret.Data["tls.crt"])
@@ -324,9 +324,9 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
 		addMap(cheEnv, k8sCheEnv)
 	}
 
-	addMap(cheEnv, s.deployContext.CheCluster.Spec.Server.CustomCheProperties)
+	addMap(cheEnv, ctx.CheCluster.Spec.Server.CustomCheProperties)
 
-	err = setBitbucketEndpoints(s.deployContext, cheEnv)
+	err = setBitbucketEndpoints(ctx, cheEnv)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/deploy/server/server_configmap_test.go b/pkg/deploy/server/server_configmap_test.go
index d5b3d61985..0ce56e678a 100644
--- a/pkg/deploy/server/server_configmap_test.go
+++ b/pkg/deploy/server/server_configmap_test.go
@@ -15,6 +15,7 @@ import (
 	"testing"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -69,14 +70,11 @@ func TestNewCheConfigMap(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -225,14 +223,11 @@ func TestConfigMap(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -339,14 +334,11 @@ func TestUpdateBitBucketEndpoints(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -550,14 +542,11 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -686,14 +675,11 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -763,14 +749,11 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
-
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
-			if err != nil {
-				t.Fatalf("Error creating ConfigMap data: %v", err)
-			}
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
+			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
 		})
 	}
@@ -914,10 +897,10 @@ func TestShouldSetUpCorrectlyInternalIdentityProviderServiceURL(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			util.IsOpenShift = testCase.isOpenShift
 			util.IsOpenShift4 = testCase.isOpenShift4
-			deployContext := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
+			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServer(deployContext)
-			actualData, err := server.getCheConfigMapData()
+			server := NewServerReconciler()
+			actualData, err := server.getCheConfigMapData(ctx)
 			if err != nil {
 				t.Fatalf("Error creating ConfigMap data: %v", err)
 			}
diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go
index 6183f6fccc..e802111c8f 100644
--- a/pkg/deploy/server/server_deployment.go
+++ b/pkg/deploy/server/server_deployment.go
@@ -17,7 +17,7 @@ import (
 	"strings"
 
 	"github.com/eclipse-che/che-operator/pkg/deploy"
-	identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
+	identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
 	"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
 	"github.com/eclipse-che/che-operator/pkg/deploy/tls"
 
@@ -29,18 +29,18 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
-func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
-	selfSignedCASecretExists, err := tls.IsSelfSignedCASecretExists(s.deployContext)
+func (s ServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) {
+	selfSignedCASecretExists, err := tls.IsSelfSignedCASecretExists(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	cmResourceVersions := GetCheConfigMapVersion(s.deployContext)
-	cmResourceVersions += "," + tls.GetAdditionalCACertsConfigMapVersion(s.deployContext)
+	cmResourceVersions := GetCheConfigMapVersion(ctx)
+	cmResourceVersions += "," + tls.GetAdditionalCACertsConfigMapVersion(ctx)
 
 	terminationGracePeriodSeconds := int64(30)
-	cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster)
-	labels, labelSelector := deploy.GetLabelsAndSelector(s.deployContext.CheCluster, cheFlavor)
+	cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
+	labels, labelSelector := deploy.GetLabelsAndSelector(ctx.CheCluster, cheFlavor)
 	optionalEnv := true
 	selfSignedCertEnv := corev1.EnvVar{
 		Name:  "CHE_SELF__SIGNED__CERT",
@@ -82,7 +82,7 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 			},
 		}
 	}
-	if s.deployContext.CheCluster.Spec.Server.GitSelfSignedCert {
+	if ctx.CheCluster.Spec.Server.GitSelfSignedCert {
 		gitSelfSignedCertEnv = corev1.EnvVar{
 			Name: "CHE_GIT_SELF__SIGNED__CERT",
 			ValueFrom: &corev1.EnvVarSource{
@@ -114,7 +114,7 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 	cheEnv = append(cheEnv, gitSelfSignedCertEnv)
 	cheEnv = append(cheEnv, gitSelfSignedCertHostEnv)
 
-	identityProviderSecret := s.deployContext.CheCluster.Spec.Auth.IdentityProviderSecret
+	identityProviderSecret := ctx.CheCluster.Spec.Auth.IdentityProviderSecret
 	if len(identityProviderSecret) > 0 {
 		cheEnv = append(cheEnv, corev1.EnvVar{
 			Name: "CHE_KEYCLOAK_ADMIN__PASSWORD",
@@ -141,11 +141,11 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 	} else {
 		cheEnv = append(cheEnv, corev1.EnvVar{
 			Name:  "CHE_KEYCLOAK_ADMIN__PASSWORD",
-			Value: s.deployContext.CheCluster.Spec.Auth.IdentityProviderPassword,
+			Value: ctx.CheCluster.Spec.Auth.IdentityProviderPassword,
 		},
 			corev1.EnvVar{
 				Name:  "CHE_KEYCLOAK_ADMIN__USERNAME",
-				Value: s.deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName,
+				Value: ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName,
 			})
 	}
 
@@ -162,15 +162,15 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 					FieldPath:  "metadata.namespace"}},
 		})
 
-	if s.deployContext.CheCluster.IsNativeUserModeEnabled() {
+	if ctx.CheCluster.IsNativeUserModeEnabled() {
 		cheEnv = append(cheEnv, corev1.EnvVar{
 			Name:  "CHE_AUTH_NATIVEUSER",
 			Value: "true",
 		})
 	}
 
-	cheImageAndTag := GetFullCheServerImageLink(s.deployContext.CheCluster)
-	pullPolicy := corev1.PullPolicy(util.GetValue(string(s.deployContext.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag)))
+	cheImageAndTag := GetFullCheServerImageLink(ctx.CheCluster)
+	pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag)))
 
 	deployment := &appsv1.Deployment{
 		TypeMeta: metav1.TypeMeta{
@@ -179,7 +179,7 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 		},
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      cheFlavor,
-			Namespace: s.deployContext.CheCluster.Namespace,
+			Namespace: ctx.CheCluster.Namespace,
 			Labels:    labels,
 		},
 		Spec: appsv1.DeploymentSpec{
@@ -222,18 +222,18 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 							Resources: corev1.ResourceRequirements{
 								Requests: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										s.deployContext.CheCluster.Spec.Server.ServerMemoryRequest,
+										ctx.CheCluster.Spec.Server.ServerMemoryRequest,
 										deploy.DefaultServerMemoryRequest),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										s.deployContext.CheCluster.Spec.Server.ServerCpuRequest,
+										ctx.CheCluster.Spec.Server.ServerCpuRequest,
 										deploy.DefaultServerCpuRequest),
 								},
 								Limits: corev1.ResourceList{
 									corev1.ResourceMemory: util.GetResourceQuantity(
-										s.deployContext.CheCluster.Spec.Server.ServerMemoryLimit,
+										ctx.CheCluster.Spec.Server.ServerMemoryLimit,
 										deploy.DefaultServerMemoryLimit),
 									corev1.ResourceCPU: util.GetResourceQuantity(
-										s.deployContext.CheCluster.Spec.Server.ServerCpuLimit,
+										ctx.CheCluster.Spec.Server.ServerCpuLimit,
 										deploy.DefaultServerCpuLimit),
 								},
 							},
@@ -262,23 +262,23 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 		},
 	}
 
-	err = MountBitBucketOAuthConfig(s.deployContext, deployment)
+	err = MountBitBucketOAuthConfig(ctx, deployment)
 	if err != nil {
 		return nil, err
 	}
 
-	err = MountGitHubOAuthConfig(s.deployContext, deployment)
+	err = MountGitHubOAuthConfig(ctx, deployment)
 	if err != nil {
 		return nil, err
 	}
 
-	err = MountGitLabOAuthConfig(s.deployContext, deployment)
+	err = MountGitLabOAuthConfig(ctx, deployment)
 	if err != nil {
 		return nil, err
 	}
 
 	container := &deployment.Spec.Template.Spec.Containers[0]
-	chePostgresSecret := s.deployContext.CheCluster.Spec.Database.ChePostgresSecret
+	chePostgresSecret := ctx.CheCluster.Spec.Database.ChePostgresSecret
 	if len(chePostgresSecret) > 0 {
 		container.Env = append(container.Env,
 			corev1.EnvVar{
@@ -305,7 +305,7 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 	}
 
 	// configure probes if debug isn't set
-	cheDebug := util.GetValue(s.deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
+	cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
 	if cheDebug != "true" {
 		container.ReadinessProbe = &corev1.Probe{
 			Handler: corev1.Handler{
@@ -347,11 +347,11 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 	}
 
 	if !util.IsOpenShift {
-		runAsUser, err := strconv.ParseInt(util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64)
+		runAsUser, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64)
 		if err != nil {
 			return nil, err
 		}
-		fsGroup, err := strconv.ParseInt(util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64)
+		fsGroup, err := strconv.ParseInt(util.GetValue(ctx.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64)
 		if err != nil {
 			return nil, err
 		}
@@ -361,17 +361,17 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
 		}
 	}
 
-	if deploy.IsComponentReadinessInitContainersConfigured(s.deployContext.CheCluster) {
-		if !s.deployContext.CheCluster.Spec.Database.ExternalDb {
-			waitForPostgresInitContainer, err := postgres.GetWaitForPostgresInitContainer(s.deployContext)
+	if deploy.IsComponentReadinessInitContainersConfigured(ctx.CheCluster) {
+		if !ctx.CheCluster.Spec.Database.ExternalDb {
+			waitForPostgresInitContainer, err := postgres.GetWaitForPostgresInitContainer(ctx)
 			if err != nil {
 				return nil, err
 			}
 			deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, *waitForPostgresInitContainer)
 		}
 
-		if !s.deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider {
-			waitForKeycloakInitContainer, err := identity_provider.GetWaitForKeycloakInitContainer(s.deployContext)
+		if !ctx.CheCluster.Spec.Auth.ExternalIdentityProvider {
+			waitForKeycloakInitContainer, err := identityprovider.GetWaitForKeycloakInitContainer(ctx)
 			if err != nil {
 				return nil, err
 			}
diff --git a/pkg/deploy/server/server_deployment_test.go b/pkg/deploy/server/server_deployment_test.go
index adfe9f8065..291bf5a500 100644
--- a/pkg/deploy/server/server_deployment_test.go
+++ b/pkg/deploy/server/server_deployment_test.go
@@ -86,7 +86,7 @@ func TestDeployment(t *testing.T) {
 			testCase.initObjects = append(testCase.initObjects)
 			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
 
-			deployContext := &deploy.DeployContext{
+			ctx := &deploy.DeployContext{
 				CheCluster: testCase.cheCluster,
 				ClusterAPI: deploy.ClusterAPI{
 					Client: cli,
@@ -94,12 +94,10 @@ func TestDeployment(t *testing.T) {
 				},
 			}
 
-			server := NewServer(deployContext)
-			deployment, err := server.getDeploymentSpec()
-			if err != nil {
-				t.Fatalf("Error creating deployment: %v", err)
-			}
+			server := NewServerReconciler()
+			deployment, err := server.getDeploymentSpec(ctx)
 
+			assert.Nil(t, err)
 			util.CompareResources(deployment,
 				util.TestExpectedResources{
 					MemoryLimit:   testCase.memoryLimit,
@@ -181,10 +179,10 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects)
+			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServer(deployContext)
-			deployment, err := server.getDeploymentSpec()
+			server := NewServerReconciler()
+			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error occurred %v", err)
 
 			container := &deployment.Spec.Template.Spec.Containers[0]
@@ -279,10 +277,10 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects)
+			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServer(deployContext)
-			deployment, err := server.getDeploymentSpec()
+			server := NewServerReconciler()
+			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error %v", err)
 
 			container := &deployment.Spec.Template.Spec.Containers[0]
@@ -373,10 +371,10 @@ func TestMountGitLabOAuthEnvVar(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			deployContext := deploy.GetTestDeployContext(nil, testCase.initObjects)
+			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServer(deployContext)
-			deployment, err := server.getDeploymentSpec()
+			server := NewServerReconciler()
+			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error %v", err)
 
 			container := &deployment.Spec.Template.Spec.Containers[0]
diff --git a/pkg/deploy/server/server_test.go b/pkg/deploy/server/server_test.go
deleted file mode 100644
index b17e30d53c..0000000000
--- a/pkg/deploy/server/server_test.go
+++ /dev/null
@@ -1,285 +0,0 @@
-//
-// Copyright (c) 2019-2021 Red Hat, Inc.
-// This program and the accompanying materials are made
-// available under the terms of the Eclipse Public License 2.0
-// which is available at https://www.eclipse.org/legal/epl-2.0/
-//
-// SPDX-License-Identifier: EPL-2.0
-//
-// Contributors:
-//   Red Hat, Inc. - initial API and implementation
-//
-package server
-
-import (
-	"context"
-	"os"
-
-	"github.com/eclipse-che/che-operator/pkg/deploy"
-	"github.com/eclipse-che/che-operator/pkg/util"
-	routev1 "github.com/openshift/api/route/v1"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-
-	orgv1 "github.com/eclipse-che/che-operator/api/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-
-	"testing"
-)
-
-func TestSyncService(t *testing.T) {
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme)
-	deployContext := &deploy.DeployContext{
-		CheCluster: &orgv1.CheCluster{
-			ObjectMeta: metav1.ObjectMeta{
-				Namespace: "eclipse-che",
-				Name:      os.Getenv("CHE_FLAVOR"),
-			},
-			Spec: orgv1.CheClusterSpec{
-				Server: orgv1.CheClusterSpecServer{
-					CheDebug: "true",
-				},
-				Metrics: orgv1.CheClusterSpecMetrics{
-					Enable: true,
-				},
-			},
-		},
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	server := NewServer(deployContext)
-	done, err := server.SyncCheService()
-	if !done {
-		if err != nil {
-			t.Fatalf("Failed to sync service, error: %v", err)
-		} else {
-			t.Fatalf("Failed to sync service")
-		}
-	}
-
-	service := &corev1.Service{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Failed to get service, error: %v", err)
-	}
-
-	checkPort(service.Spec.Ports[0], "http", 8080, t)
-	checkPort(service.Spec.Ports[1], "metrics", deploy.DefaultCheMetricsPort, t)
-	checkPort(service.Spec.Ports[2], "debug", deploy.DefaultCheDebugPort, t)
-}
-
-func TestSyncAll(t *testing.T) {
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      os.Getenv("CHE_FLAVOR"),
-		},
-		Spec: orgv1.CheClusterSpec{
-			Server: orgv1.CheClusterSpecServer{
-				TlsSupport: true,
-			},
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-		Proxy: &deploy.Proxy{},
-	}
-
-	util.IsOpenShift = true
-
-	server := NewServer(deployContext)
-	done, err := server.ExposeCheServiceAndEndpoint()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Server: %v", err)
-	}
-
-	done, err = server.SyncAll()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync Server: %v", err)
-	}
-
-	// check service
-	service := &corev1.Service{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, service)
-	if err != nil {
-		t.Fatalf("Service not found: %v", err)
-	}
-
-	// check endpoint
-	route := &routev1.Route{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: server.component, Namespace: "eclipse-che"}, route)
-	if err != nil {
-		t.Fatalf("Route not found: %v", err)
-	}
-
-	// check configmap
-	configMap := &corev1.ConfigMap{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, configMap)
-	if err != nil {
-		t.Fatalf("ConfigMap not found: %v", err)
-	}
-
-	// check deployment
-	deployment := &appsv1.Deployment{}
-	err = cli.Get(context.TODO(), types.NamespacedName{Name: server.component, Namespace: "eclipse-che"}, deployment)
-	if err != nil {
-		t.Fatalf("Deployment not found: %v", err)
-	}
-
-	if cheCluster.Status.CheURL == "" {
-		t.Fatalf("CheURL is not set")
-	}
-
-	if cheCluster.Status.CheClusterRunning == "" {
-		t.Fatalf("CheClusterRunning is not set")
-	}
-
-	if cheCluster.Status.CheVersion == "" {
-		t.Fatalf("CheVersion is not set")
-	}
-}
-
-func TestSyncLegacyConfigMap(t *testing.T) {
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      "eclipse-che",
-		},
-		Spec: orgv1.CheClusterSpec{
-			Server: orgv1.CheClusterSpecServer{
-				TlsSupport: true,
-			},
-		},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-		Proxy: &deploy.Proxy{},
-	}
-
-	legacyConfigMap := deploy.GetConfigMapSpec(deployContext, "custom", map[string]string{"a": "b"}, "test")
-	err := cli.Create(context.TODO(), legacyConfigMap)
-	if err != nil {
-		t.Fatalf("Failed to create config map: %v", err)
-	}
-
-	server := NewServer(deployContext)
-	done, err := server.SyncLegacyConfigMap()
-	if !done || err != nil {
-		t.Fatalf("Failed to sync config map: %v", err)
-	}
-
-	err = cli.Get(context.TODO(), types.NamespacedName{Namespace: "eclipse-che", Name: "custom"}, &corev1.ConfigMap{})
-	if err == nil {
-		t.Fatalf("Legacy configmap must be removed")
-	}
-
-	if cheCluster.Spec.Server.CustomCheProperties["a"] != "b" {
-		t.Fatalf("CheCluster wasn't updated with legacy configmap data")
-	}
-}
-
-func TestUpdateAvailabilityStatus(t *testing.T) {
-	cheDeployment := &appsv1.Deployment{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      os.Getenv("CHE_FLAVOR"),
-			Namespace: "eclipse-che",
-		},
-		Status: appsv1.DeploymentStatus{
-			AvailableReplicas: 1,
-			Replicas:          1,
-		},
-	}
-	cheCluster := &orgv1.CheCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: "eclipse-che",
-			Name:      os.Getenv("CHE_FLAVOR"),
-		},
-		Spec:   orgv1.CheClusterSpec{},
-		Status: orgv1.CheClusterStatus{},
-	}
-
-	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
-	routev1.AddToScheme(scheme.Scheme)
-	cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster)
-	deployContext := &deploy.DeployContext{
-		CheCluster: cheCluster,
-		ClusterAPI: deploy.ClusterAPI{
-			Client:           cli,
-			NonCachingClient: cli,
-			Scheme:           scheme.Scheme,
-		},
-	}
-
-	server := NewServer(deployContext)
-	_, err := server.UpdateAvailabilityStatus()
-	if err != nil {
-		t.Fatalf("Failed to update availability status: %v", err)
-	}
-	if cheCluster.Status.CheClusterRunning != UnavailableStatus {
-		t.Fatalf("Expected status: %s, actual: %s", UnavailableStatus, cheCluster.Status.CheClusterRunning)
-	}
-
-	err = cli.Create(context.TODO(), cheDeployment)
-	if err != nil {
-		t.Fatalf("Deployment not found: %v", err)
-	}
-	_, err = server.UpdateAvailabilityStatus()
-	if err != nil {
-		t.Fatalf("Failed to update availability status: %v", err)
-	}
-
-	if cheCluster.Status.CheClusterRunning != AvailableStatus {
-		t.Fatalf("Expected status: %s, actual: %s", AvailableStatus, cheCluster.Status.CheClusterRunning)
-	}
-
-	cheDeployment.Status.Replicas = 2
-	err = cli.Update(context.TODO(), cheDeployment)
-	if err != nil {
-		t.Fatalf("Failed to update deployment: %v", err)
-	}
-
-	_, err = server.UpdateAvailabilityStatus()
-	if err != nil {
-		t.Fatalf("Failed to update availability status: %v", err)
-	}
-	if cheCluster.Status.CheClusterRunning != RollingUpdateInProgressStatus {
-		t.Fatalf("Expected status: %s, actual: %s", RollingUpdateInProgressStatus, cheCluster.Status.CheClusterRunning)
-	}
-}
-
-func checkPort(actualPort corev1.ServicePort, expectedName string, expectedPort int32, t *testing.T) {
-	if actualPort.Name != expectedName || actualPort.Port != expectedPort {
-		t.Errorf("expected port name:`%s` port:`%d`, actual name:`%s` port:`%d`",
-			expectedName, expectedPort, actualPort.Name, actualPort.Port)
-	}
-}
diff --git a/pkg/deploy/test_util.go b/pkg/deploy/test_util.go
index d5faff39af..d7cf1c4800 100644
--- a/pkg/deploy/test_util.go
+++ b/pkg/deploy/test_util.go
@@ -13,6 +13,7 @@ package deploy
 
 import (
 	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	console "github.com/openshift/api/console/v1"
 	oauthv1 "github.com/openshift/api/oauth/v1"
 	routev1 "github.com/openshift/api/route/v1"
 	userv1 "github.com/openshift/api/user/v1"
@@ -52,9 +53,11 @@ func GetTestDeployContext(cheCluster *orgv1.CheCluster, initObjs []runtime.Objec
 	scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{})
 	scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, &oauthv1.OAuthClient{})
 	scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{}, &userv1.Identity{})
-	scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{})
+	scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{}, &configv1.Console{})
 	scheme.AddKnownTypes(routev1.GroupVersion, &routev1.Route{})
 	scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{})
+	scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{})
+	scheme.AddKnownTypes(console.SchemeGroupVersion, &console.ConsoleLink{})
 
 	initObjs = append(initObjs, cheCluster)
 	cli := fake.NewFakeClientWithScheme(scheme, initObjs...)

From 7aa29904d13712be48b7ebd61a72f5db490cabf0 Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Wed, 8 Dec 2021 12:26:33 +0200
Subject: [PATCH 2/6] chore: Refactoring

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 pkg/deploy/consolelink/consolelink.go         | 113 +++++
 pkg/deploy/consolelink/consolelink_test.go    |  65 +++
 pkg/deploy/consolelink/init_test.go           |  21 +
 .../identity_provider_reconciler.go           | 470 ++++++++++++++++++
 .../identity_provider_reconciler_test.go      | 211 ++++++++
 .../identity_provider_util.go                 |  57 +++
 pkg/deploy/server/chehost_reconciler.go       | 164 ++++++
 pkg/deploy/server/chehost_reconciler_test.go  | 107 ++++
 pkg/deploy/server/default_reconciler.go       | 274 ++++++++++
 pkg/deploy/server/default_reconciler_test.go  | 162 ++++++
 pkg/deploy/server/server_reconciler.go        | 192 +++++++
 pkg/deploy/server/server_reconciler_test.go   | 138 +++++
 pkg/deploy/server/server_util.go              |  30 ++
 13 files changed, 2004 insertions(+)
 create mode 100644 pkg/deploy/consolelink/consolelink.go
 create mode 100644 pkg/deploy/consolelink/consolelink_test.go
 create mode 100644 pkg/deploy/consolelink/init_test.go
 create mode 100644 pkg/deploy/identity-provider/identity_provider_reconciler.go
 create mode 100644 pkg/deploy/identity-provider/identity_provider_reconciler_test.go
 create mode 100644 pkg/deploy/identity-provider/identity_provider_util.go
 create mode 100644 pkg/deploy/server/chehost_reconciler.go
 create mode 100644 pkg/deploy/server/chehost_reconciler_test.go
 create mode 100644 pkg/deploy/server/default_reconciler.go
 create mode 100644 pkg/deploy/server/default_reconciler_test.go
 create mode 100644 pkg/deploy/server/server_reconciler.go
 create mode 100644 pkg/deploy/server/server_reconciler_test.go
 create mode 100644 pkg/deploy/server/server_util.go

diff --git a/pkg/deploy/consolelink/consolelink.go b/pkg/deploy/consolelink/consolelink.go
new file mode 100644
index 0000000000..4e0948da8a
--- /dev/null
+++ b/pkg/deploy/consolelink/consolelink.go
@@ -0,0 +1,113 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package consolelink
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	consolev1 "github.com/openshift/api/console/v1"
+	"github.com/sirupsen/logrus"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+const (
+	ConsoleLinkFinalizerName = "consolelink.finalizers.che.eclipse.org"
+	ConsoleLinksResourceName = "consolelinks"
+)
+
+type ConsoleLinkReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewConsoleLinkReconciler() *ConsoleLinkReconciler {
+	return &ConsoleLinkReconciler{}
+}
+
+func (c *ConsoleLinkReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	if !util.IsOpenShift4 || !util.HasK8SResourceObject(ctx.ClusterAPI.DiscoveryClient, ConsoleLinksResourceName) {
+		// console link is supported only on OpenShift >= 4.2
+		logrus.Debug("Console link won't be created. Consolelinks is not supported by OpenShift cluster.")
+		return reconcile.Result{}, true, nil
+	}
+
+	if !ctx.CheCluster.Spec.Server.TlsSupport {
+		// console link is supported only with https
+		logrus.Debug("Console link won't be created. HTTP protocol is not supported.")
+		return reconcile.Result{}, true, nil
+	}
+
+	done, err := c.createConsoleLink(ctx)
+	if !done {
+		return reconcile.Result{Requeue: true}, false, err
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (c *ConsoleLinkReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return deploy.DeleteObjectWithFinalizer(ctx, client.ObjectKey{Name: deploy.DefaultConsoleLinkName()}, &consolev1.ConsoleLink{}, ConsoleLinkFinalizerName)
+}
+
+func (c *ConsoleLinkReconciler) createConsoleLink(ctx *deploy.DeployContext) (bool, error) {
+	consoleLinkSpec := c.getConsoleLinkSpec(ctx)
+	_, err := deploy.CreateIfNotExists(ctx, consoleLinkSpec)
+	if err != nil {
+		return false, err
+	}
+
+	consoleLink := &consolev1.ConsoleLink{}
+	exists, err := deploy.Get(ctx, client.ObjectKey{Name: deploy.DefaultConsoleLinkName()}, consoleLink)
+	if !exists || err != nil {
+		return false, err
+	}
+
+	// consolelink is for this specific instance of Eclipse Che
+	if strings.Index(consoleLink.Spec.Link.Href, ctx.CheCluster.Spec.Server.CheHost) != -1 {
+		err = deploy.AppendFinalizer(ctx, ConsoleLinkFinalizerName)
+		return err == nil, err
+	}
+
+	return true, nil
+}
+
+func (c *ConsoleLinkReconciler) getConsoleLinkSpec(ctx *deploy.DeployContext) *consolev1.ConsoleLink {
+	cheHost := ctx.CheCluster.Spec.Server.CheHost
+	consoleLink := &consolev1.ConsoleLink{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "ConsoleLink",
+			APIVersion: consolev1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: deploy.DefaultConsoleLinkName(),
+			Annotations: map[string]string{
+				deploy.CheEclipseOrgNamespace: ctx.CheCluster.Namespace,
+			},
+		},
+		Spec: consolev1.ConsoleLinkSpec{
+			Link: consolev1.Link{
+				Href: "https://" + cheHost,
+				Text: deploy.DefaultConsoleLinkDisplayName()},
+			Location: consolev1.ApplicationMenu,
+			ApplicationMenu: &consolev1.ApplicationMenuSpec{
+				Section:  deploy.DefaultConsoleLinkSection(),
+				ImageURL: fmt.Sprintf("https://%s%s", cheHost, deploy.DefaultConsoleLinkImage()),
+			},
+		},
+	}
+
+	return consoleLink
+}
diff --git a/pkg/deploy/consolelink/consolelink_test.go b/pkg/deploy/consolelink/consolelink_test.go
new file mode 100644
index 0000000000..f56779bb40
--- /dev/null
+++ b/pkg/deploy/consolelink/consolelink_test.go
@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package consolelink
+
+import (
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	console "github.com/openshift/api/console/v1"
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	fakeDiscovery "k8s.io/client-go/discovery/fake"
+
+	"testing"
+)
+
+func TestReconcileConsoleLink(t *testing.T) {
+	cheCluster := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "eclipse-che",
+			Name:      "eclipse-che",
+		},
+		Spec: orgv1.CheClusterSpec{
+			Server: orgv1.CheClusterSpecServer{
+				TlsSupport: true,
+			},
+		},
+	}
+
+	util.IsOpenShift4 = true
+	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
+	ctx.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{
+		{
+			APIResources: []metav1.APIResource{
+				{Name: ConsoleLinksResourceName},
+			},
+		},
+	}
+
+	consolelink := NewConsoleLinkReconciler()
+	_, done, err := consolelink.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.DefaultConsoleLinkName()}, &console.ConsoleLink{}))
+	assert.True(t, util.ContainsString(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName))
+
+	// Initialize DeletionTimestamp => checluster is being deleted
+	err = consolelink.Finalize(ctx)
+	assert.Nil(t, err)
+
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.DefaultConsoleLinkName()}, &console.ConsoleLink{}))
+	assert.False(t, util.ContainsString(ctx.CheCluster.Finalizers, ConsoleLinkFinalizerName))
+}
diff --git a/pkg/deploy/consolelink/init_test.go b/pkg/deploy/consolelink/init_test.go
new file mode 100644
index 0000000000..8390ba322a
--- /dev/null
+++ b/pkg/deploy/consolelink/init_test.go
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package consolelink
+
+import "github.com/eclipse-che/che-operator/pkg/deploy"
+
+func init() {
+	err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml")
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/pkg/deploy/identity-provider/identity_provider_reconciler.go b/pkg/deploy/identity-provider/identity_provider_reconciler.go
new file mode 100644
index 0000000000..50012e6899
--- /dev/null
+++ b/pkg/deploy/identity-provider/identity_provider_reconciler.go
@@ -0,0 +1,470 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package identityprovider
+
+import (
+	"context"
+	"errors"
+	"strings"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/deploy/expose"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	oauth "github.com/openshift/api/oauth/v1"
+	"github.com/sirupsen/logrus"
+	appsv1 "k8s.io/api/apps/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+const (
+	OAuthFinalizerName = "oauthclients.finalizers.che.eclipse.org"
+)
+
+var (
+	oAuthClientDiffOpts = cmpopts.IgnoreFields(oauth.OAuthClient{}, "TypeMeta", "ObjectMeta")
+	syncItems           = []func(*deploy.DeployContext) (bool, error){
+		syncService,
+		syncExposure,
+		SyncKeycloakDeploymentToCluster,
+		syncKeycloakResources,
+		syncOpenShiftIdentityProvider,
+		SyncGitHubOAuth,
+	}
+
+	keycloakUpdated = false
+	keycloakCheHost = ""
+)
+
+type IdentityProviderReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewIdentityProviderReconciler() *IdentityProviderReconciler {
+	return &IdentityProviderReconciler{}
+}
+
+func (ip *IdentityProviderReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	if ctx.CheCluster.Spec.Auth.ExternalIdentityProvider {
+		keycloakURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL
+		if ctx.CheCluster.Status.KeycloakURL != keycloakURL {
+			ctx.CheCluster.Status.KeycloakURL = keycloakURL
+			if err := deploy.UpdateCheCRStatus(ctx, "status: Keycloak URL", keycloakURL); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		}
+
+		return reconcile.Result{}, true, nil
+	}
+
+	if ctx.CheCluster.IsNativeUserModeEnabled() {
+		done, err := syncNativeIdentityProviderItems(ctx)
+		if !done {
+			return reconcile.Result{}, false, err
+		}
+		return reconcile.Result{}, true, nil
+	}
+
+	for _, syncItem := range syncItems {
+		done, err := syncItem(ctx)
+		if !util.IsTestMode() {
+			if !done {
+				return reconcile.Result{}, false, err
+			}
+		}
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (ip *IdentityProviderReconciler) Finalize(ctx *deploy.DeployContext) error {
+	oAuthClientName := ctx.CheCluster.Spec.Auth.OAuthClientName
+	if oAuthClientName != "" {
+		return deploy.DeleteObjectWithFinalizer(ctx, types.NamespacedName{Name: oAuthClientName}, &oauth.OAuthClient{}, OAuthFinalizerName)
+	}
+	return nil
+}
+
+func syncService(deployContext *deploy.DeployContext) (bool, error) {
+	return deploy.SyncServiceToCluster(
+		deployContext,
+		deploy.IdentityProviderName,
+		[]string{"http"},
+		[]int32{8080},
+		deploy.IdentityProviderName)
+}
+
+func syncExposure(deployContext *deploy.DeployContext) (bool, error) {
+	cr := deployContext.CheCluster
+
+	protocol := (map[bool]string{
+		true:  "https",
+		false: "http"})[cr.Spec.Server.TlsSupport]
+	endpoint, done, err := expose.Expose(
+		deployContext,
+		deploy.IdentityProviderName,
+		cr.Spec.Auth.IdentityProviderRoute,
+		cr.Spec.Auth.IdentityProviderIngress,
+		createGatewayConfig(deployContext.CheCluster))
+	if !done {
+		return false, err
+	}
+
+	keycloakURL := protocol + "://" + endpoint
+	if cr.Spec.Auth.IdentityProviderURL != keycloakURL {
+		cr.Spec.Auth.IdentityProviderURL = keycloakURL
+		if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak URL", keycloakURL); err != nil {
+			return false, err
+		}
+
+		cr.Status.KeycloakURL = keycloakURL
+		if err := deploy.UpdateCheCRStatus(deployContext, "Keycloak URL", keycloakURL); err != nil {
+			return false, err
+		}
+	}
+
+	return true, nil
+}
+
+func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) {
+	if !util.IsTestMode() {
+		cr := deployContext.CheCluster
+		if !cr.Status.KeycloakProvisoned {
+			if err := ProvisionKeycloakResources(deployContext); err != nil {
+				return false, err
+			}
+
+			for {
+				cr.Status.KeycloakProvisoned = true
+				if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil &&
+					apierrors.IsConflict(err) {
+
+					deploy.ReloadCheClusterCR(deployContext)
+					continue
+				}
+				break
+			}
+		}
+
+		// Updates keycloak if chehost has been changed
+		if !keycloakUpdated || keycloakCheHost != deployContext.CheCluster.Spec.Server.CheHost {
+			if _, err := util.K8sclient.ExecIntoPod(
+				deployContext.CheCluster,
+				deploy.IdentityProviderName,
+				GetKeycloakUpdateCommand,
+				"Update redirect URI-s and webOrigins"); err != nil {
+				return false, err
+			} else {
+				keycloakUpdated = true
+				keycloakCheHost = deployContext.CheCluster.Spec.Server.CheHost
+			}
+		}
+	}
+
+	return true, nil
+}
+
+func syncOpenShiftIdentityProvider(deployContext *deploy.DeployContext) (bool, error) {
+	cr := deployContext.CheCluster
+	if util.IsOpenShift && cr.IsOpenShiftOAuthEnabled() {
+		return SyncOpenShiftIdentityProviderItems(deployContext)
+	}
+	return true, nil
+}
+
+func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
+	cr := deployContext.CheCluster
+
+	if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
+		return false, err
+	}
+	if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
+		return false, err
+	}
+
+	if util.IsOpenShift {
+		redirectURIs := []string{"https://" + cr.Spec.Server.CheHost + "/oauth/callback"}
+		oAuthClient := getOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, redirectURIs)
+		done, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
+		if !done {
+			return false, err
+		}
+
+		err = deploy.AppendFinalizer(deployContext, OAuthFinalizerName)
+		if err != nil {
+			return false, err
+		}
+	}
+
+	return true, nil
+}
+
+func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
+	cr := deployContext.CheCluster
+
+	if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
+		return false, err
+	}
+	if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
+		return false, err
+	}
+
+	keycloakURL := cr.Spec.Auth.IdentityProviderURL
+	cheFlavor := deploy.DefaultCheFlavor(cr)
+	keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
+	oAuthClient := getKeycloakOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
+	provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
+	if !provisioned {
+		return false, err
+	}
+
+	if !util.IsTestMode() {
+		if !cr.Status.OpenShiftoAuthProvisioned {
+			// note that this uses the instance.Spec.Auth.IdentityProviderRealm and instance.Spec.Auth.IdentityProviderClientId.
+			// because we're not doing much of a change detection on those fields, we can't react on them changing here.
+			_, err := util.K8sclient.ExecIntoPod(
+				cr,
+				deploy.IdentityProviderName,
+				func(cr *orgv1.CheCluster) (string, error) {
+					return GetOpenShiftIdentityProviderProvisionCommand(cr, cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret)
+				},
+				"Create OpenShift identity provider")
+			if err != nil {
+				return false, err
+			}
+
+			for {
+				cr.Status.OpenShiftoAuthProvisioned = true
+				if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil &&
+					apierrors.IsConflict(err) {
+
+					deploy.ReloadCheClusterCR(deployContext)
+					continue
+				}
+				break
+			}
+		}
+	}
+	return true, nil
+}
+
+func resolveOpenshiftOAuthClientName(deployContext *deploy.DeployContext) error {
+	cr := deployContext.CheCluster
+	oAuthClientName := cr.Spec.Auth.OAuthClientName
+	if len(oAuthClientName) < 1 {
+		oAuthClientName = cr.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
+		cr.Spec.Auth.OAuthClientName = oAuthClientName
+		if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func resolveOpenshiftOAuthClientSecret(deployContext *deploy.DeployContext) error {
+	cr := deployContext.CheCluster
+	oauthSecret := cr.Spec.Auth.OAuthSecret
+	if len(oauthSecret) < 1 {
+		oauthSecret = util.GeneratePasswd(12)
+		cr.Spec.Auth.OAuthSecret = oauthSecret
+		if err := deploy.UpdateCheCRSpec(deployContext, "oAuth secret name", oauthSecret); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// SyncGitHubOAuth provisions GitHub OAuth if secret with annotation
+// `che.eclipse.org/github-oauth-credentials=true` or `che.eclipse.org/oauth-scm-configuration=github`
+// is mounted into a container
+func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) {
+	// get legacy secret
+	legacySecrets, err := deploy.GetSecrets(deployContext, map[string]string{
+		deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
+		deploy.KubernetesComponentLabelKey: deploy.IdentityProviderName + "-secret",
+	}, map[string]string{
+		deploy.CheEclipseOrgGithubOAuthCredentials: "true",
+	})
+	if err != nil {
+		return false, err
+	}
+
+	secrets, err := deploy.GetSecrets(deployContext, map[string]string{
+		deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
+		deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration,
+	}, map[string]string{
+		deploy.CheEclipseOrgOAuthScmServer: "github",
+	})
+
+	if err != nil {
+		return false, err
+	} else if len(secrets)+len(legacySecrets) > 1 {
+		return false, errors.New("More than 1 GitHub OAuth configuration secrets found")
+	}
+
+	isGitHubOAuthCredentialsExists := len(secrets) == 1 || len(legacySecrets) == 1
+	cr := deployContext.CheCluster
+
+	if isGitHubOAuthCredentialsExists {
+		if !cr.Status.GitHubOAuthProvisioned {
+			if !util.IsTestMode() {
+				_, err := util.K8sclient.ExecIntoPod(
+					cr,
+					deploy.IdentityProviderName,
+					func(cr *orgv1.CheCluster) (string, error) {
+						return GetGitHubIdentityProviderCreateCommand(deployContext)
+					},
+					"Create GitHub OAuth")
+				if err != nil {
+					return false, err
+				}
+			}
+
+			cr.Status.GitHubOAuthProvisioned = true
+			if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "true"); err != nil {
+				return false, err
+			}
+		}
+	} else {
+		if cr.Status.GitHubOAuthProvisioned {
+			if !util.IsTestMode() {
+				_, err := util.K8sclient.ExecIntoPod(
+					cr,
+					deploy.IdentityProviderName,
+					func(cr *orgv1.CheCluster) (string, error) {
+						return GetIdentityProviderDeleteCommand(cr, "github")
+					},
+					"Delete GitHub OAuth")
+				if err != nil {
+					return false, err
+				}
+			}
+
+			cr.Status.GitHubOAuthProvisioned = false
+			if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "false"); err != nil {
+				return false, err
+			}
+		}
+	}
+
+	return true, nil
+}
+
+func deleteIdentityProvider(ctx *deploy.DeployContext) error {
+	if !ctx.CheCluster.IsOpenShiftOAuthEnabled() && ctx.CheCluster.Status.OpenShiftoAuthProvisioned == true {
+		keycloakDeployment := &appsv1.Deployment{}
+		if err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: ctx.CheCluster.Namespace}, keycloakDeployment); err != nil {
+			logrus.Errorf("Deployment %s not found: %s", keycloakDeployment.Name, err.Error())
+		}
+
+		providerName := "openshift-v3"
+		if util.IsOpenShift4 {
+			providerName = "openshift-v4"
+		}
+
+		_, err := util.K8sclient.ExecIntoPod(
+			ctx.CheCluster,
+			keycloakDeployment.Name,
+			func(cr *orgv1.CheCluster) (string, error) {
+				return GetIdentityProviderDeleteCommand(ctx.CheCluster, providerName)
+			},
+			"delete OpenShift identity provider")
+		if err == nil {
+			oAuthClient := &oauth.OAuthClient{}
+			oAuthClientName := ctx.CheCluster.Spec.Auth.OAuthClientName
+			err := deploy.DeleteObjectWithFinalizer(ctx, types.NamespacedName{Name: oAuthClientName}, &oauth.OAuthClient{}, OAuthFinalizerName)
+			if err != nil {
+				logrus.Errorf("Failed to delete %s %s: %s", oAuthClient.Kind, oAuthClient.Name, err.Error())
+			}
+
+			for {
+				ctx.CheCluster.Status.OpenShiftoAuthProvisioned = false
+				if err := deploy.UpdateCheCRStatus(ctx, "OpenShiftoAuthProvisioned", "false"); err != nil {
+					if apierrors.IsConflict(err) {
+						deploy.ReloadCheClusterCR(ctx)
+						continue
+					}
+				}
+				break
+			}
+
+			for {
+				ctx.CheCluster.Spec.Auth.OAuthSecret = ""
+				ctx.CheCluster.Spec.Auth.OAuthClientName = ""
+				updateFields := map[string]string{
+					"oAuthSecret":     "",
+					"oAuthClientName": "",
+				}
+
+				if err := deploy.UpdateCheCRSpecByFields(ctx, updateFields); err != nil {
+					if apierrors.IsConflict(err) {
+						deploy.ReloadCheClusterCR(ctx)
+						continue
+					}
+				}
+				break
+			}
+
+			return nil
+		}
+		return err
+	}
+
+	return nil
+}
+
+// Delete OpenShift identity provider if OpenShift oAuth is false in spec
+// but OpenShiftoAuthProvisioned is true in CR status, e.g. when oAuth has been turned on and then turned off
+// deleted, err := identityprovider.ReconcileIdentityProvider(deployContext)
+// if deleted {
+// 	// ignore error
+// 	deploy.DeleteFinalizer(deployContext, deploy.OAuthFinalizerName)
+// 	for {
+// 		checluster.Status.OpenShiftoAuthProvisioned = false
+// 		if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "false"); err != nil &&
+// 			errors.IsConflict(err) {
+// 			_ = deploy.ReloadCheClusterCR(deployContext)
+// 			continue
+// 		}
+// 		break
+// 	}
+// 	for {
+// 		checluster.Spec.Auth.OAuthSecret = ""
+// 		checluster.Spec.Auth.OAuthClientName = ""
+// 		if err := deploy.UpdateCheCRStatus(deployContext, "clean oAuth secret name and client name", ""); err != nil &&
+// 			errors.IsConflict(err) {
+// 			_ = deploy.ReloadCheClusterCR(deployContext)
+// 			continue
+// 		}
+// 		break
+// 	}
+// }
+
+func createGatewayConfig(cheCluster *orgv1.CheCluster) *gateway.TraefikConfig {
+	cfg := gateway.CreateCommonTraefikConfig(
+		deploy.IdentityProviderName,
+		"PathPrefix(`/auth`)",
+		10,
+		"http://"+deploy.IdentityProviderName+":8080",
+		[]string{})
+
+	if util.IsOpenShift && cheCluster.IsNativeUserModeEnabled() {
+		cfg.AddAuthHeaderRewrite(deploy.IdentityProviderName)
+	}
+
+	return cfg
+}
diff --git a/pkg/deploy/identity-provider/identity_provider_reconciler_test.go b/pkg/deploy/identity-provider/identity_provider_reconciler_test.go
new file mode 100644
index 0000000000..1106850e87
--- /dev/null
+++ b/pkg/deploy/identity-provider/identity_provider_reconciler_test.go
@@ -0,0 +1,211 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package identityprovider
+
+import (
+	"os"
+	"reflect"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+
+	"github.com/google/go-cmp/cmp"
+
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/kubernetes/scheme"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+	"testing"
+)
+
+func TestSyncGitHubOAuth(t *testing.T) {
+	type testCase struct {
+		name        string
+		initCR      *orgv1.CheCluster
+		expectedCR  *orgv1.CheCluster
+		initObjects []runtime.Object
+	}
+
+	testCases := []testCase{
+		{
+			name: "Should provision GitHub OAuth with legacy secret",
+			initCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "0",
+				},
+			},
+			expectedCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "1",
+				},
+				Status: orgv1.CheClusterStatus{
+					GitHubOAuthProvisioned: true,
+				},
+			},
+			initObjects: []runtime.Object{
+				&corev1.Secret{
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Secret",
+						APIVersion: "v1",
+					},
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "github-credentials",
+						Namespace: "eclipse-che",
+						Labels: map[string]string{
+							deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
+							deploy.KubernetesComponentLabelKey: "keycloak-secret",
+						},
+						Annotations: map[string]string{
+							deploy.CheEclipseOrgGithubOAuthCredentials: "true",
+						},
+					},
+					Data: map[string][]byte{
+						"key": []byte("key-data"),
+					},
+				},
+			},
+		},
+		{
+			name: "Should provision GitHub OAuth",
+			initCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "0",
+				},
+			},
+			expectedCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "1",
+				},
+				Status: orgv1.CheClusterStatus{
+					GitHubOAuthProvisioned: true,
+				},
+			},
+			initObjects: []runtime.Object{
+				&corev1.Secret{
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Secret",
+						APIVersion: "v1",
+					},
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "github-oauth-config",
+						Namespace: "eclipse-che",
+						Labels: map[string]string{
+							"app.kubernetes.io/part-of":   "che.eclipse.org",
+							"app.kubernetes.io/component": "oauth-scm-configuration",
+						},
+						Annotations: map[string]string{
+							"che.eclipse.org/oauth-scm-server": "github",
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "Should not provision GitHub OAuth",
+			initCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "0",
+				},
+			},
+			expectedCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "0",
+				},
+			},
+			initObjects: []runtime.Object{
+				&corev1.Secret{
+					TypeMeta: metav1.TypeMeta{
+						Kind:       "Secret",
+						APIVersion: "v1",
+					},
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "github-credentials",
+						Namespace: "eclipse-che",
+						Labels: map[string]string{
+							deploy.KubernetesPartOfLabelKey:    deploy.CheEclipseOrg,
+							deploy.KubernetesComponentLabelKey: "keycloak-secret",
+						},
+					},
+					Data: map[string][]byte{
+						"key": []byte("key-data"),
+					},
+				},
+			},
+		},
+		{
+			name: "Should delete GitHub OAuth",
+			initCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "0",
+				},
+				Status: orgv1.CheClusterStatus{
+					GitHubOAuthProvisioned: true,
+				},
+			},
+			expectedCR: &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "che-cluster",
+					Namespace:       "eclipse-che",
+					ResourceVersion: "1",
+				},
+				Status: orgv1.CheClusterStatus{
+					GitHubOAuthProvisioned: false,
+				},
+			},
+			initObjects: []runtime.Object{},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
+			orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
+			testCase.initObjects = append(testCase.initObjects, testCase.initCR)
+			cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
+
+			deployContext := &deploy.DeployContext{
+				CheCluster: testCase.initCR,
+				ClusterAPI: deploy.ClusterAPI{
+					Client: cli,
+					Scheme: scheme.Scheme,
+				},
+			}
+
+			_, err := SyncGitHubOAuth(deployContext)
+			if err != nil {
+				t.Fatalf("Error mounting secret: %v", err)
+			}
+
+			if !reflect.DeepEqual(testCase.expectedCR, testCase.initCR) {
+				t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedCR, testCase.initCR))
+			}
+		})
+	}
+}
diff --git a/pkg/deploy/identity-provider/identity_provider_util.go b/pkg/deploy/identity-provider/identity_provider_util.go
new file mode 100644
index 0000000000..a3c09cf531
--- /dev/null
+++ b/pkg/deploy/identity-provider/identity_provider_util.go
@@ -0,0 +1,57 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package identityprovider
+
+import (
+	"strings"
+
+	oauth "github.com/openshift/api/oauth/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func getKeycloakOAuthClientSpec(name string, oauthSecret string, keycloakURL string, keycloakRealm string, isOpenShift4 bool) *oauth.OAuthClient {
+	providerName := "openshift-v3"
+	if isOpenShift4 {
+		providerName = "openshift-v4"
+	}
+
+	redirectURLSuffix := "/realms/" + keycloakRealm + "/broker/" + providerName + "/endpoint"
+	redirectURIs := []string{
+		keycloakURL + redirectURLSuffix,
+	}
+
+	keycloakURL = strings.NewReplacer("https://", "", "http://", "").Replace(keycloakURL)
+	if !strings.Contains(keycloakURL, "://") {
+		redirectURIs = []string{
+			"http://" + keycloakURL + redirectURLSuffix,
+			"https://" + keycloakURL + redirectURLSuffix,
+		}
+	}
+	return getOAuthClientSpec(name, oauthSecret, redirectURIs)
+}
+
+func getOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) *oauth.OAuthClient {
+	return &oauth.OAuthClient{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "OAuthClient",
+			APIVersion: oauth.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:   name,
+			Labels: map[string]string{"app": "che"},
+		},
+
+		Secret:       oauthSecret,
+		RedirectURIs: redirectURIs,
+		GrantMethod:  oauth.GrantHandlerPrompt,
+	}
+}
diff --git a/pkg/deploy/server/chehost_reconciler.go b/pkg/deploy/server/chehost_reconciler.go
new file mode 100644
index 0000000000..ac403695c2
--- /dev/null
+++ b/pkg/deploy/server/chehost_reconciler.go
@@ -0,0 +1,164 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	routev1 "github.com/openshift/api/route/v1"
+	networking "k8s.io/api/networking/v1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+type CheHostReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewCheHostReconciler() *CheHostReconciler {
+	return &CheHostReconciler{}
+}
+
+func (s *CheHostReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	done, err := s.detectDefaultCheHost(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.syncCheService(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.exposeCheEndpoint(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (s *CheHostReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
+}
+
+func (s *CheHostReconciler) detectDefaultCheHost(ctx *deploy.DeployContext) (bool, error) {
+	// only for OpenShift
+	if !util.IsOpenShift || ctx.DefaultCheHost != "" {
+		return true, nil
+	}
+
+	done, err := deploy.SyncRouteToCluster(
+		ctx,
+		getComponentName(ctx),
+		"",
+		"/",
+		getServerExposingServiceName(ctx.CheCluster),
+		8080,
+		ctx.CheCluster.Spec.Server.CheServerRoute,
+		getComponentName(ctx))
+	if !done {
+		return false, err
+	}
+
+	route := &routev1.Route{}
+	exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), route)
+	if !exists {
+		return false, err
+	}
+
+	ctx.DefaultCheHost = route.Spec.Host
+	return true, nil
+}
+
+func (s *CheHostReconciler) syncCheService(ctx *deploy.DeployContext) (bool, error) {
+	portName := []string{"http"}
+	portNumber := []int32{8080}
+
+	if ctx.CheCluster.Spec.Metrics.Enable {
+		portName = append(portName, "metrics")
+		portNumber = append(portNumber, deploy.DefaultCheMetricsPort)
+	}
+
+	if ctx.CheCluster.Spec.Server.CheDebug == "true" {
+		portName = append(portName, "debug")
+		portNumber = append(portNumber, deploy.DefaultCheDebugPort)
+	}
+
+	spec := deploy.GetServiceSpec(ctx, deploy.CheServiceName, portName, portNumber, getComponentName(ctx))
+	return deploy.Sync(ctx, spec, deploy.ServiceDefaultDiffOpts)
+}
+
+func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, error) {
+	cheHost := ""
+	exposedServiceName := getServerExposingServiceName(ctx.CheCluster)
+
+	if !util.IsOpenShift {
+		_, done, err := deploy.SyncIngressToCluster(
+			ctx,
+			getComponentName(ctx),
+			ctx.CheCluster.Spec.Server.CheHost,
+			"",
+			exposedServiceName,
+			8080,
+			ctx.CheCluster.Spec.Server.CheServerIngress,
+			getComponentName(ctx))
+		if !done {
+			return false, err
+		}
+
+		ingress := &networking.Ingress{}
+		exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), ingress)
+		if !exists {
+			return false, err
+		}
+
+		cheHost = ingress.Spec.Rules[0].Host
+	} else {
+		customHost := ctx.CheCluster.Spec.Server.CheHost
+		if ctx.DefaultCheHost == customHost {
+			// let OpenShift set a hostname by itself since it requires a routes/custom-host permissions
+			customHost = ""
+		}
+
+		done, err := deploy.SyncRouteToCluster(
+			ctx,
+			getComponentName(ctx),
+			customHost,
+			"/",
+			exposedServiceName,
+			8080,
+			ctx.CheCluster.Spec.Server.CheServerRoute,
+			getComponentName(ctx))
+		if !done {
+			return false, err
+		}
+
+		route := &routev1.Route{}
+		exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), route)
+		if !exists {
+			return false, err
+		}
+
+		if customHost == "" {
+			ctx.DefaultCheHost = route.Spec.Host
+		}
+		cheHost = route.Spec.Host
+	}
+
+	if ctx.CheCluster.Spec.Server.CheHost != cheHost {
+		ctx.CheCluster.Spec.Server.CheHost = cheHost
+		err := deploy.UpdateCheCRSpec(ctx, "CheHost URL", cheHost)
+		return err == nil, err
+	}
+
+	return true, nil
+}
diff --git a/pkg/deploy/server/chehost_reconciler_test.go b/pkg/deploy/server/chehost_reconciler_test.go
new file mode 100644
index 0000000000..56eeb11ab3
--- /dev/null
+++ b/pkg/deploy/server/chehost_reconciler_test.go
@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	"context"
+	"os"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	routev1 "github.com/openshift/api/route/v1"
+	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
+
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/kubernetes/scheme"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	"testing"
+)
+
+func TestSyncService(t *testing.T) {
+	orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
+	corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
+	cli := fake.NewFakeClientWithScheme(scheme.Scheme)
+	ctx := &deploy.DeployContext{
+		CheCluster: &orgv1.CheCluster{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "eclipse-che",
+				Name:      os.Getenv("CHE_FLAVOR"),
+			},
+			Spec: orgv1.CheClusterSpec{
+				Server: orgv1.CheClusterSpecServer{
+					CheDebug: "true",
+				},
+				Metrics: orgv1.CheClusterSpecMetrics{
+					Enable: true,
+				},
+			},
+		},
+		ClusterAPI: deploy.ClusterAPI{
+			Client:           cli,
+			NonCachingClient: cli,
+			Scheme:           scheme.Scheme,
+		},
+	}
+
+	server := NewCheHostReconciler()
+	done, err := server.syncCheService(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	service := &corev1.Service{}
+	done, err = deploy.Get(ctx, types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, service)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.Equal(t, service.Spec.Ports[0].Name, "http")
+	assert.Equal(t, service.Spec.Ports[0].Port, int32(8080))
+	assert.Equal(t, service.Spec.Ports[1].Name, "metrics")
+	assert.Equal(t, service.Spec.Ports[1].Port, deploy.DefaultCheMetricsPort)
+	assert.Equal(t, service.Spec.Ports[2].Name, "debug")
+	assert.Equal(t, service.Spec.Ports[2].Port, deploy.DefaultCheDebugPort)
+}
+
+func TestConfiguringLabelsForRoutes(t *testing.T) {
+	util.IsOpenShift = true
+
+	cheCluster := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "eclipse-che",
+			Name:      os.Getenv("CHE_FLAVOR"),
+		},
+		Spec: orgv1.CheClusterSpec{
+			Server: orgv1.CheClusterSpecServer{
+				CheServerRoute: orgv1.RouteCustomSettings{
+					Labels: "route=one",
+				},
+			},
+		},
+		Status: orgv1.CheClusterStatus{},
+	}
+
+	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
+
+	server := NewCheHostReconciler()
+	done, err := server.exposeCheEndpoint(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	route := &routev1.Route{}
+	err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, route)
+	assert.Nil(t, err)
+	assert.Equal(t, route.ObjectMeta.Labels["route"], "one")
+}
diff --git a/pkg/deploy/server/default_reconciler.go b/pkg/deploy/server/default_reconciler.go
new file mode 100644
index 0000000000..356a27cdc4
--- /dev/null
+++ b/pkg/deploy/server/default_reconciler.go
@@ -0,0 +1,274 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+
+package server
+
+import (
+	"strconv"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/sirupsen/logrus"
+	appsv1 "k8s.io/api/apps/v1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+type DefaultValuesReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewDefaultValuesReconciler() *DefaultValuesReconciler {
+	return &DefaultValuesReconciler{}
+}
+
+func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
+	cheNamespace := ctx.CheCluster.Namespace
+	if len(ctx.CheCluster.Spec.Server.CheFlavor) < 1 {
+		ctx.CheCluster.Spec.Server.CheFlavor = cheFlavor
+		if err := deploy.UpdateCheCRSpec(ctx, "installation flavor", cheFlavor); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
+		if len(ctx.CheCluster.Spec.Database.ChePostgresUser) < 1 || len(ctx.CheCluster.Spec.Database.ChePostgresPassword) < 1 {
+			chePostgresSecret := deploy.DefaultChePostgresSecret()
+			_, err := deploy.SyncSecretToCluster(ctx, chePostgresSecret, cheNamespace, map[string][]byte{"user": []byte(deploy.DefaultChePostgresUser), "password": []byte(util.GeneratePasswd(12))})
+			if err != nil {
+				return reconcile.Result{}, false, err
+			}
+			ctx.CheCluster.Spec.Database.ChePostgresSecret = chePostgresSecret
+			if err := deploy.UpdateCheCRSpec(ctx, "Postgres Secret", chePostgresSecret); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		} else {
+			if len(ctx.CheCluster.Spec.Database.ChePostgresUser) < 1 {
+				ctx.CheCluster.Spec.Database.ChePostgresUser = deploy.DefaultChePostgresUser
+				if err := deploy.UpdateCheCRSpec(ctx, "Postgres User", ctx.CheCluster.Spec.Database.ChePostgresUser); err != nil {
+					return reconcile.Result{}, false, err
+				}
+			}
+			if len(ctx.CheCluster.Spec.Database.ChePostgresPassword) < 1 {
+				ctx.CheCluster.Spec.Database.ChePostgresPassword = util.GeneratePasswd(12)
+				if err := deploy.UpdateCheCRSpec(ctx, "auto-generated CheCluster DB password", "password-hidden"); err != nil {
+					return reconcile.Result{}, false, err
+				}
+			}
+		}
+	}
+	if len(ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret) < 1 {
+		keycloakPostgresPassword := util.GeneratePasswd(12)
+		keycloakDeployment := &appsv1.Deployment{}
+		exists, err := deploy.GetNamespacedObject(ctx, deploy.IdentityProviderName, keycloakDeployment)
+		if err != nil {
+			logrus.Error(err)
+		}
+		if exists {
+			keycloakPostgresPassword = util.GetDeploymentEnv(keycloakDeployment, "DB_PASSWORD")
+		}
+
+		if len(ctx.CheCluster.Spec.Auth.IdentityProviderPostgresPassword) < 1 {
+			identityPostgresSecret := deploy.DefaultCheIdentityPostgresSecret()
+			_, err := deploy.SyncSecretToCluster(ctx, identityPostgresSecret, cheNamespace, map[string][]byte{"password": []byte(keycloakPostgresPassword)})
+			if err != nil {
+				return reconcile.Result{}, false, err
+			}
+			ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret = identityPostgresSecret
+			if err := deploy.UpdateCheCRSpec(ctx, "Identity Provider Postgres Secret", identityPostgresSecret); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		}
+	}
+
+	if len(ctx.CheCluster.Spec.Auth.IdentityProviderSecret) < 1 {
+		keycloakAdminUserName := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName, "admin")
+		keycloakAdminPassword := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderPassword, util.GeneratePasswd(12))
+
+		keycloakDeployment := &appsv1.Deployment{}
+		exists, _ := deploy.GetNamespacedObject(ctx, deploy.IdentityProviderName, keycloakDeployment)
+		if exists {
+			keycloakAdminUserName = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_USERNAME")
+			keycloakAdminPassword = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_PASSWORD")
+		}
+
+		if len(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 || len(ctx.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
+			identityProviderSecret := deploy.DefaultCheIdentitySecret()
+			_, err := deploy.SyncSecretToCluster(ctx, identityProviderSecret, cheNamespace, map[string][]byte{"user": []byte(keycloakAdminUserName), "password": []byte(keycloakAdminPassword)})
+			if err != nil {
+				return reconcile.Result{}, false, err
+			}
+			ctx.CheCluster.Spec.Auth.IdentityProviderSecret = identityProviderSecret
+			if err := deploy.UpdateCheCRSpec(ctx, "Identity Provider Secret", identityProviderSecret); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		} else {
+			if len(ctx.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
+				ctx.CheCluster.Spec.Auth.IdentityProviderPassword = keycloakAdminPassword
+				if err := deploy.UpdateCheCRSpec(ctx, "Keycloak admin password", "password hidden"); err != nil {
+					return reconcile.Result{}, false, err
+				}
+			}
+			if len(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 {
+				ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName = keycloakAdminUserName
+				if err := deploy.UpdateCheCRSpec(ctx, "Keycloak admin username", keycloakAdminUserName); err != nil {
+					return reconcile.Result{}, false, err
+				}
+			}
+		}
+	}
+
+	chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, "dbche")
+	if len(ctx.CheCluster.Spec.Database.ChePostgresDb) < 1 {
+		ctx.CheCluster.Spec.Database.ChePostgresDb = chePostgresDb
+		if err := deploy.UpdateCheCRSpec(ctx, "Postgres DB", chePostgresDb); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+	chePostgresHostName := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName)
+	if len(ctx.CheCluster.Spec.Database.ChePostgresHostName) < 1 {
+		ctx.CheCluster.Spec.Database.ChePostgresHostName = chePostgresHostName
+		if err := deploy.UpdateCheCRSpec(ctx, "Postgres hostname", chePostgresHostName); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+	chePostgresPort := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort)
+	if len(ctx.CheCluster.Spec.Database.ChePostgresPort) < 1 {
+		ctx.CheCluster.Spec.Database.ChePostgresPort = chePostgresPort
+		if err := deploy.UpdateCheCRSpec(ctx, "Postgres port", chePostgresPort); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if !ctx.CheCluster.IsNativeUserModeEnabled() {
+		keycloakRealm := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
+		if len(ctx.CheCluster.Spec.Auth.IdentityProviderRealm) < 1 {
+			ctx.CheCluster.Spec.Auth.IdentityProviderRealm = keycloakRealm
+			if err := deploy.UpdateCheCRSpec(ctx, "Keycloak realm", keycloakRealm); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		}
+		keycloakClientId := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
+		if len(ctx.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 {
+			ctx.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId
+
+			if err := deploy.UpdateCheCRSpec(ctx, "Keycloak client ID", keycloakClientId); err != nil {
+				return reconcile.Result{}, false, err
+			}
+		}
+	}
+
+	cheLogLevel := util.GetValue(ctx.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
+	if len(ctx.CheCluster.Spec.Server.CheLogLevel) < 1 {
+		ctx.CheCluster.Spec.Server.CheLogLevel = cheLogLevel
+		if err := deploy.UpdateCheCRSpec(ctx, "log level", cheLogLevel); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+	cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
+	if len(ctx.CheCluster.Spec.Server.CheDebug) < 1 {
+		ctx.CheCluster.Spec.Server.CheDebug = cheDebug
+		if err := deploy.UpdateCheCRSpec(ctx, "debug", cheDebug); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+	pvcStrategy := util.GetValue(ctx.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy)
+	if len(ctx.CheCluster.Spec.Storage.PvcStrategy) < 1 {
+		ctx.CheCluster.Spec.Storage.PvcStrategy = pvcStrategy
+		if err := deploy.UpdateCheCRSpec(ctx, "pvc strategy", pvcStrategy); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+	pvcClaimSize := util.GetValue(ctx.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize)
+	if len(ctx.CheCluster.Spec.Storage.PvcClaimSize) < 1 {
+		ctx.CheCluster.Spec.Storage.PvcClaimSize = pvcClaimSize
+		if err := deploy.UpdateCheCRSpec(ctx, "pvc claim size", pvcClaimSize); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	// This is only to correctly  manage defaults during the transition
+	// from Upstream 7.0.0 GA to the next
+	// version that should fixed bug https://github.com/eclipse/che/issues/13714
+	// Or for the transition from CRW 1.2 to 2.0
+
+	if ctx.CheCluster.Spec.Storage.PvcJobsImage == deploy.OldDefaultPvcJobsUpstreamImageToDetect ||
+		(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Storage.PvcJobsImage != "") {
+		ctx.CheCluster.Spec.Storage.PvcJobsImage = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "pvc jobs image", ctx.CheCluster.Spec.Storage.PvcJobsImage); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if ctx.CheCluster.Spec.Database.PostgresImage == deploy.OldDefaultPostgresUpstreamImageToDetect ||
+		(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Database.PostgresImage != "") {
+		ctx.CheCluster.Spec.Database.PostgresImage = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "postgres image", ctx.CheCluster.Spec.Database.PostgresImage); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if ctx.CheCluster.Spec.Auth.IdentityProviderImage == deploy.OldDefaultKeycloakUpstreamImageToDetect ||
+		(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Auth.IdentityProviderImage != "") {
+		ctx.CheCluster.Spec.Auth.IdentityProviderImage = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "keycloak image", ctx.CheCluster.Spec.Auth.IdentityProviderImage); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
+		!ctx.CheCluster.Spec.Server.ExternalPluginRegistry &&
+		ctx.CheCluster.Spec.Server.PluginRegistryUrl == deploy.OldCrwPluginRegistryUrl {
+		ctx.CheCluster.Spec.Server.PluginRegistryUrl = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "plugin registry url", ctx.CheCluster.Spec.Server.PluginRegistryUrl); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
+		ctx.CheCluster.Spec.Server.CheImage == deploy.OldDefaultCodeReadyServerImageRepo {
+		ctx.CheCluster.Spec.Server.CheImage = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "che image repo", ctx.CheCluster.Spec.Server.CheImage); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
+		ctx.CheCluster.Spec.Server.CheImageTag == deploy.OldDefaultCodeReadyServerImageTag {
+		ctx.CheCluster.Spec.Server.CheImageTag = ""
+		if err := deploy.UpdateCheCRSpec(ctx, "che image tag", ctx.CheCluster.Spec.Server.CheImageTag); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if ctx.CheCluster.Spec.Server.ServerExposureStrategy == "" && ctx.CheCluster.Spec.K8s.IngressStrategy == "" {
+		strategy := util.GetServerExposureStrategy(ctx.CheCluster)
+		ctx.CheCluster.Spec.Server.ServerExposureStrategy = strategy
+		if err := deploy.UpdateCheCRSpec(ctx, "serverExposureStrategy", strategy); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	if ctx.CheCluster.Spec.DevWorkspace.Enable && ctx.CheCluster.Spec.Auth.NativeUserMode == nil {
+		newNativeUserModeValue := util.NewBoolPointer(true)
+		ctx.CheCluster.Spec.Auth.NativeUserMode = newNativeUserModeValue
+		if err := deploy.UpdateCheCRSpec(ctx, "nativeUserMode", strconv.FormatBool(*newNativeUserModeValue)); err != nil {
+			return reconcile.Result{}, false, err
+		}
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (p *DefaultValuesReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
+}
diff --git a/pkg/deploy/server/default_reconciler_test.go b/pkg/deploy/server/default_reconciler_test.go
new file mode 100644
index 0000000000..12235e5082
--- /dev/null
+++ b/pkg/deploy/server/default_reconciler_test.go
@@ -0,0 +1,162 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	"os"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/stretchr/testify/assert"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/utils/pointer"
+
+	"testing"
+)
+
+func TestEnsureServerExposureStrategy(t *testing.T) {
+	type testCase struct {
+		name                string
+		expectedCr          *orgv1.CheCluster
+		devWorkspaceEnabled bool
+		initObjects         []runtime.Object
+	}
+
+	testCases := []testCase{
+		{
+			name: "Single Host should be enabled if devWorkspace is enabled",
+			expectedCr: &orgv1.CheCluster{
+				Spec: orgv1.CheClusterSpec{
+					Server: orgv1.CheClusterSpecServer{
+						ServerExposureStrategy: "single-host",
+					},
+				},
+			},
+			devWorkspaceEnabled: true,
+		},
+		{
+			name: "Multi Host should be enabled if devWorkspace is not enabled",
+			expectedCr: &orgv1.CheCluster{
+				Spec: orgv1.CheClusterSpec{
+					Server: orgv1.CheClusterSpecServer{
+						ServerExposureStrategy: "multi-host",
+					},
+				},
+			},
+			devWorkspaceEnabled: false,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			checluster := &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "eclipse-che",
+					Namespace: "eclipse-che",
+				},
+			}
+
+			checluster.Spec.DevWorkspace.Enable = testCase.devWorkspaceEnabled
+			ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{})
+
+			defaults := NewDefaultValuesReconciler()
+			_, done, err := defaults.Reconcile(ctx)
+			assert.True(t, done)
+			assert.Nil(t, err)
+			assert.Equal(t, testCase.expectedCr.Spec.Server.ServerExposureStrategy, ctx.CheCluster.Spec.Server.ServerExposureStrategy)
+		})
+	}
+}
+
+func TestNativeUserModeEnabled(t *testing.T) {
+	type testCase struct {
+		name                    string
+		initObjects             []runtime.Object
+		isOpenshift             bool
+		devworkspaceEnabled     bool
+		initialNativeUserValue  *bool
+		expectedNativeUserValue *bool
+	}
+
+	testCases := []testCase{
+		{
+			name:                    "che-operator should use nativeUserMode when devworkspaces on openshift and no initial value in CR for nativeUserMode",
+			isOpenshift:             true,
+			devworkspaceEnabled:     true,
+			initialNativeUserValue:  nil,
+			expectedNativeUserValue: pointer.BoolPtr(true),
+		},
+		{
+			name:                    "che-operator should use nativeUserMode value from initial CR",
+			isOpenshift:             true,
+			devworkspaceEnabled:     true,
+			initialNativeUserValue:  pointer.BoolPtr(false),
+			expectedNativeUserValue: pointer.BoolPtr(false),
+		},
+		{
+			name:                    "che-operator should use nativeUserMode value from initial CR",
+			isOpenshift:             true,
+			devworkspaceEnabled:     true,
+			initialNativeUserValue:  pointer.BoolPtr(true),
+			expectedNativeUserValue: pointer.BoolPtr(true),
+		},
+		{
+			name:                    "che-operator should use nativeUserMode when devworkspaces on kubernetes and no initial value in CR for nativeUserMode",
+			isOpenshift:             false,
+			devworkspaceEnabled:     true,
+			initialNativeUserValue:  nil,
+			expectedNativeUserValue: pointer.BoolPtr(true),
+		},
+		{
+			name:                    "che-operator not modify nativeUserMode when devworkspace not enabled",
+			isOpenshift:             true,
+			devworkspaceEnabled:     false,
+			initialNativeUserValue:  nil,
+			expectedNativeUserValue: nil,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
+
+			checluster := &orgv1.CheCluster{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "eclipse-che",
+					Namespace: "eclipse-che",
+				},
+			}
+
+			// reread templates (workaround after setting IsOpenShift value)
+			util.IsOpenShift = testCase.isOpenshift
+			devworkspace.DevWorkspaceTemplates = devworkspace.DevWorkspaceTemplatesPath()
+			devworkspace.DevWorkspaceIssuerFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml"
+			devworkspace.DevWorkspaceCertificateFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-serving-cert.Certificate.yaml"
+
+			checluster.Spec.DevWorkspace.Enable = testCase.devworkspaceEnabled
+			checluster.Spec.Auth.NativeUserMode = testCase.initialNativeUserValue
+			ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{})
+
+			defaults := NewDefaultValuesReconciler()
+			_, done, err := defaults.Reconcile(ctx)
+			assert.True(t, done)
+			assert.Nil(t, err)
+			assert.Equal(t, testCase.expectedNativeUserValue, ctx.CheCluster.Spec.Auth.NativeUserMode)
+		})
+	}
+}
diff --git a/pkg/deploy/server/server_reconciler.go b/pkg/deploy/server/server_reconciler.go
new file mode 100644
index 0000000000..47e225128d
--- /dev/null
+++ b/pkg/deploy/server/server_reconciler.go
@@ -0,0 +1,192 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	"context"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	"github.com/sirupsen/logrus"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+const (
+	AvailableStatus               = "Available"
+	UnavailableStatus             = "Unavailable"
+	RollingUpdateInProgressStatus = "Available: Rolling update in progress"
+)
+
+type ServerReconciler struct {
+	deploy.Reconcilable
+}
+
+func NewServerReconciler() *ServerReconciler {
+	return &ServerReconciler{}
+}
+
+func (s *ServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+	done, err := s.syncLegacyConfigMap(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.syncCheConfigMap(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	// ensure configmap is created
+	// the version of the object is used in the deployment
+	exists, err := deploy.GetNamespacedObject(ctx, CheConfigMapName, &corev1.ConfigMap{})
+	if !exists {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.syncDeployment(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.updateAvailabilityStatus(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.updateCheURL(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	done, err = s.updateCheVersion(ctx)
+	if !done {
+		return reconcile.Result{}, false, err
+	}
+
+	return reconcile.Result{}, true, nil
+}
+
+func (s *ServerReconciler) Finalize(ctx *deploy.DeployContext) error {
+	return nil
+}
+
+func (s ServerReconciler) updateCheURL(ctx *deploy.DeployContext) (bool, error) {
+	var cheUrl = util.GetCheURL(ctx.CheCluster)
+	if ctx.CheCluster.Status.CheURL != cheUrl {
+		ctx.CheCluster.Status.CheURL = cheUrl
+		err := deploy.UpdateCheCRStatus(ctx, getComponentName(ctx)+" server URL", cheUrl)
+		return err == nil, err
+	}
+
+	return true, nil
+}
+
+func (s *ServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, error) {
+	data, err := s.getCheConfigMapData(ctx)
+	if err != nil {
+		return false, err
+	}
+
+	return deploy.SyncConfigMapDataToCluster(ctx, CheConfigMapName, data, getComponentName(ctx))
+}
+
+func (s ServerReconciler) syncLegacyConfigMap(ctx *deploy.DeployContext) (bool, error) {
+	// Get custom ConfigMap
+	// if it exists, add the data into CustomCheProperties
+	customConfigMap := &corev1.ConfigMap{}
+	exists, err := deploy.GetNamespacedObject(ctx, "custom", customConfigMap)
+	if err != nil {
+		return false, err
+	} else if exists {
+		logrus.Info("Found legacy custom ConfigMap. Adding those values to CheCluster.Spec.Server.CustomCheProperties")
+
+		if ctx.CheCluster.Spec.Server.CustomCheProperties == nil {
+			ctx.CheCluster.Spec.Server.CustomCheProperties = make(map[string]string)
+		}
+		for k, v := range customConfigMap.Data {
+			ctx.CheCluster.Spec.Server.CustomCheProperties[k] = v
+		}
+
+		err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster)
+		if err != nil {
+			return false, err
+		}
+
+		return deploy.DeleteNamespacedObject(ctx, "custom", &corev1.ConfigMap{})
+	}
+
+	return true, nil
+}
+
+func (s *ServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext) (bool, error) {
+	cheDeployment := &appsv1.Deployment{}
+	exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), cheDeployment)
+	if err != nil {
+		return false, err
+	}
+
+	if exists {
+		if cheDeployment.Status.AvailableReplicas < 1 {
+			if ctx.CheCluster.Status.CheClusterRunning != UnavailableStatus {
+				ctx.CheCluster.Status.CheClusterRunning = UnavailableStatus
+				err := deploy.UpdateCheCRStatus(ctx, "status: Che API", UnavailableStatus)
+				return err == nil, err
+			}
+		} else if cheDeployment.Status.Replicas != 1 {
+			if ctx.CheCluster.Status.CheClusterRunning != RollingUpdateInProgressStatus {
+				ctx.CheCluster.Status.CheClusterRunning = RollingUpdateInProgressStatus
+				err := deploy.UpdateCheCRStatus(ctx, "status: Che API", RollingUpdateInProgressStatus)
+				return err == nil, err
+			}
+		} else {
+			if ctx.CheCluster.Status.CheClusterRunning != AvailableStatus {
+				cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
+				name := "Eclipse Che"
+				if cheFlavor == "codeready" {
+					name = "CodeReady Workspaces"
+				}
+
+				logrus.Infof(name+" is now available at: %s", util.GetCheURL(ctx.CheCluster))
+				ctx.CheCluster.Status.CheClusterRunning = AvailableStatus
+				err := deploy.UpdateCheCRStatus(ctx, "status: Che API", AvailableStatus)
+				return err == nil, err
+			}
+		}
+	} else {
+		ctx.CheCluster.Status.CheClusterRunning = UnavailableStatus
+		err := deploy.UpdateCheCRStatus(ctx, "status: Che API", UnavailableStatus)
+		return err == nil, err
+	}
+
+	return true, nil
+}
+
+func (s *ServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
+	spec, err := s.getDeploymentSpec(ctx)
+	if err != nil {
+		return false, err
+	}
+
+	return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts)
+}
+
+func (s ServerReconciler) updateCheVersion(ctx *deploy.DeployContext) (bool, error) {
+	cheVersion := deploy.DefaultCheVersion()
+	if ctx.CheCluster.Status.CheVersion != cheVersion {
+		ctx.CheCluster.Status.CheVersion = cheVersion
+		err := deploy.UpdateCheCRStatus(ctx, "version", cheVersion)
+		return err == nil, err
+	}
+	return true, nil
+}
diff --git a/pkg/deploy/server/server_reconciler_test.go b/pkg/deploy/server/server_reconciler_test.go
new file mode 100644
index 0000000000..336ada4aac
--- /dev/null
+++ b/pkg/deploy/server/server_reconciler_test.go
@@ -0,0 +1,138 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	"context"
+	"os"
+
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/util"
+	routev1 "github.com/openshift/api/route/v1"
+	"github.com/stretchr/testify/assert"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+
+	"testing"
+)
+
+func TestReconcile(t *testing.T) {
+	cheCluster := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "eclipse-che",
+			Name:      os.Getenv("CHE_FLAVOR"),
+		},
+		Spec: orgv1.CheClusterSpec{
+			Server: orgv1.CheClusterSpecServer{
+				TlsSupport: true,
+			},
+		},
+	}
+
+	util.IsOpenShift = true
+	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
+
+	chehost := NewCheHostReconciler()
+	done, err := chehost.exposeCheEndpoint(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	server := NewServerReconciler()
+	_, done, err = server.Reconcile(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
+	assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
+	assert.NotEmpty(t, cheCluster.Status.CheURL)
+	assert.NotEmpty(t, cheCluster.Status.CheClusterRunning)
+	assert.NotEmpty(t, cheCluster.Status.CheVersion)
+}
+
+func TestSyncLegacyConfigMap(t *testing.T) {
+	cheCluster := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "eclipse-che",
+			Name:      "eclipse-che",
+		},
+		Spec: orgv1.CheClusterSpec{
+			Server: orgv1.CheClusterSpecServer{
+				TlsSupport: true,
+			},
+		},
+	}
+	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
+
+	legacyConfigMap := deploy.GetConfigMapSpec(ctx, "custom", map[string]string{"a": "b"}, "test")
+	err := ctx.ClusterAPI.Client.Create(context.TODO(), legacyConfigMap)
+	assert.Nil(t, err)
+
+	server := NewServerReconciler()
+	done, err := server.syncLegacyConfigMap(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+
+	assert.False(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "custom", Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
+	assert.Equal(t, cheCluster.Spec.Server.CustomCheProperties["a"], "b")
+}
+
+func TestUpdateAvailabilityStatus(t *testing.T) {
+	cheDeployment := &appsv1.Deployment{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      os.Getenv("CHE_FLAVOR"),
+			Namespace: "eclipse-che",
+		},
+		Status: appsv1.DeploymentStatus{
+			AvailableReplicas: 1,
+			Replicas:          1,
+		},
+	}
+	cheCluster := &orgv1.CheCluster{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "eclipse-che",
+			Name:      os.Getenv("CHE_FLAVOR"),
+		},
+		Spec:   orgv1.CheClusterSpec{},
+		Status: orgv1.CheClusterStatus{},
+	}
+
+	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
+
+	server := NewServerReconciler()
+	done, err := server.updateAvailabilityStatus(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+	assert.Equal(t, cheCluster.Status.CheClusterRunning, UnavailableStatus)
+
+	err = ctx.ClusterAPI.Client.Create(context.TODO(), cheDeployment)
+	assert.Nil(t, err)
+
+	done, err = server.updateAvailabilityStatus(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+	assert.Equal(t, cheCluster.Status.CheClusterRunning, AvailableStatus)
+
+	cheDeployment.Status.Replicas = 2
+	err = ctx.ClusterAPI.Client.Update(context.TODO(), cheDeployment)
+	assert.Nil(t, err)
+
+	done, err = server.updateAvailabilityStatus(ctx)
+	assert.True(t, done)
+	assert.Nil(t, err)
+	assert.Equal(t, cheCluster.Status.CheClusterRunning, RollingUpdateInProgressStatus)
+}
diff --git a/pkg/deploy/server/server_util.go b/pkg/deploy/server/server_util.go
new file mode 100644
index 0000000000..c687b2fc6f
--- /dev/null
+++ b/pkg/deploy/server/server_util.go
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2019-2021 Red Hat, Inc.
+// This program and the accompanying materials are made
+// available under the terms of the Eclipse Public License 2.0
+// which is available at https://www.eclipse.org/legal/epl-2.0/
+//
+// SPDX-License-Identifier: EPL-2.0
+//
+// Contributors:
+//   Red Hat, Inc. - initial API and implementation
+//
+package server
+
+import (
+	orgv1 "github.com/eclipse-che/che-operator/api/v1"
+	"github.com/eclipse-che/che-operator/pkg/deploy"
+	"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
+	"github.com/eclipse-che/che-operator/pkg/util"
+)
+
+func getComponentName(ctx *deploy.DeployContext) string {
+	return deploy.DefaultCheFlavor(ctx.CheCluster)
+}
+
+func getServerExposingServiceName(cr *orgv1.CheCluster) string {
+	if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == deploy.GatewaySingleHostExposureType {
+		return gateway.GatewayServiceName
+	}
+	return deploy.CheServiceName
+}

From 8e98d1a963efcf51510057b5fffa75ad42be3300 Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Thu, 9 Dec 2021 10:41:11 +0200
Subject: [PATCH 3/6] Fixes

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 controllers/che/checluster_controller.go | 13 ++++------
 pkg/deploy/data_types.go                 |  1 -
 pkg/deploy/server/chehost_reconciler.go  | 30 +++++++++++-------------
 3 files changed, 19 insertions(+), 25 deletions(-)

diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go
index 27fb97da51..3600c629f0 100644
--- a/controllers/che/checluster_controller.go
+++ b/controllers/che/checluster_controller.go
@@ -253,6 +253,7 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
 
 	if err != nil {
 		if errors.IsNotFound(err) {
+			r.Log.Info("CheCluster Custom Resource not found.")
 			// Request object not found, could have been deleted after reconcile request.
 			// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
 			// Return and don't requeue
@@ -321,12 +322,8 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
 	return ctrl.Result{}, nil
 }
 
-func (r *CheClusterReconciler) GetCR(request ctrl.Request) (instance *orgv1.CheCluster, err error) {
-	instance = &orgv1.CheCluster{}
-	err = r.client.Get(context.TODO(), request.NamespacedName, instance)
-	if err != nil {
-		r.Log.Error(err, "Failed to get %s CR: %s", "Cluster name", instance.Name)
-		return nil, err
-	}
-	return instance, nil
+func (r *CheClusterReconciler) GetCR(request ctrl.Request) (*orgv1.CheCluster, error) {
+	checluster := &orgv1.CheCluster{}
+	err := r.client.Get(context.TODO(), request.NamespacedName, checluster)
+	return checluster, err
 }
diff --git a/pkg/deploy/data_types.go b/pkg/deploy/data_types.go
index 7d8ace3150..f4ef11ea9b 100644
--- a/pkg/deploy/data_types.go
+++ b/pkg/deploy/data_types.go
@@ -29,7 +29,6 @@ type DeployContext struct {
 	CheCluster              *orgv1.CheCluster
 	ClusterAPI              ClusterAPI
 	Proxy                   *Proxy
-	DefaultCheHost          string
 	IsSelfSignedCertificate bool
 }
 
diff --git a/pkg/deploy/server/chehost_reconciler.go b/pkg/deploy/server/chehost_reconciler.go
index ac403695c2..8fc78cc0dd 100644
--- a/pkg/deploy/server/chehost_reconciler.go
+++ b/pkg/deploy/server/chehost_reconciler.go
@@ -21,6 +21,7 @@ import (
 
 type CheHostReconciler struct {
 	deploy.Reconcilable
+	defaultCheHost string
 }
 
 func NewCheHostReconciler() *CheHostReconciler {
@@ -28,12 +29,15 @@ func NewCheHostReconciler() *CheHostReconciler {
 }
 
 func (s *CheHostReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
-	done, err := s.detectDefaultCheHost(ctx)
-	if !done {
-		return reconcile.Result{}, false, err
+	if util.IsOpenShift && s.defaultCheHost == "" {
+		done, defaultCheHost, err := s.getDefaultCheHost(ctx)
+		if !done {
+			return reconcile.Result{}, false, err
+		}
+		s.defaultCheHost = defaultCheHost
 	}
 
-	done, err = s.syncCheService(ctx)
+	done, err := s.syncCheService(ctx)
 	if !done {
 		return reconcile.Result{}, false, err
 	}
@@ -50,12 +54,7 @@ func (s *CheHostReconciler) Finalize(ctx *deploy.DeployContext) error {
 	return nil
 }
 
-func (s *CheHostReconciler) detectDefaultCheHost(ctx *deploy.DeployContext) (bool, error) {
-	// only for OpenShift
-	if !util.IsOpenShift || ctx.DefaultCheHost != "" {
-		return true, nil
-	}
-
+func (s *CheHostReconciler) getDefaultCheHost(ctx *deploy.DeployContext) (bool, string, error) {
 	done, err := deploy.SyncRouteToCluster(
 		ctx,
 		getComponentName(ctx),
@@ -66,17 +65,16 @@ func (s *CheHostReconciler) detectDefaultCheHost(ctx *deploy.DeployContext) (boo
 		ctx.CheCluster.Spec.Server.CheServerRoute,
 		getComponentName(ctx))
 	if !done {
-		return false, err
+		return false, "", err
 	}
 
 	route := &routev1.Route{}
 	exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), route)
 	if !exists {
-		return false, err
+		return false, "", err
 	}
 
-	ctx.DefaultCheHost = route.Spec.Host
-	return true, nil
+	return true, route.Spec.Host, nil
 }
 
 func (s *CheHostReconciler) syncCheService(ctx *deploy.DeployContext) (bool, error) {
@@ -124,7 +122,7 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, e
 		cheHost = ingress.Spec.Rules[0].Host
 	} else {
 		customHost := ctx.CheCluster.Spec.Server.CheHost
-		if ctx.DefaultCheHost == customHost {
+		if s.defaultCheHost == customHost {
 			// let OpenShift set a hostname by itself since it requires a routes/custom-host permissions
 			customHost = ""
 		}
@@ -149,7 +147,7 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, e
 		}
 
 		if customHost == "" {
-			ctx.DefaultCheHost = route.Spec.Host
+			s.defaultCheHost = route.Spec.Host
 		}
 		cheHost = route.Spec.Host
 	}

From c240bd7c5b8561c4fc4aa24f57cc43dd7bbcdfb4 Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Thu, 9 Dec 2021 10:44:53 +0200
Subject: [PATCH 4/6] Fixew

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 controllers/che/checluster_controller.go      |  2 +-
 .../identity_provider_reconciler.go           | 30 ++-----------------
 ...nciler.go => default_values_reconciler.go} |  0
 ...t.go => default_values_reconciler_test.go} |  0
 pkg/deploy/server/server_configmap.go         |  2 +-
 pkg/deploy/server/server_configmap_test.go    | 14 ++++-----
 pkg/deploy/server/server_deployment.go        |  2 +-
 pkg/deploy/server/server_deployment_test.go   |  8 ++---
 pkg/deploy/server/server_reconciler.go        | 22 +++++++-------
 pkg/deploy/server/server_reconciler_test.go   |  6 ++--
 10 files changed, 30 insertions(+), 56 deletions(-)
 rename pkg/deploy/server/{default_reconciler.go => default_values_reconciler.go} (100%)
 rename pkg/deploy/server/{default_reconciler_test.go => default_values_reconciler_test.go} (100%)

diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go
index 3600c629f0..a5b27ee7f4 100644
--- a/controllers/che/checluster_controller.go
+++ b/controllers/che/checluster_controller.go
@@ -113,7 +113,7 @@ func NewReconciler(
 	reconcileManager.RegisterReconciler(pluginregistry.NewPluginRegistryReconciler())
 	reconcileManager.RegisterReconciler(dashboard.NewDashboardReconciler())
 	reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler())
-	reconcileManager.RegisterReconciler(server.NewServerReconciler())
+	reconcileManager.RegisterReconciler(server.NewCheServerReconciler())
 	reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
 
 	return &CheClusterReconciler{
diff --git a/pkg/deploy/identity-provider/identity_provider_reconciler.go b/pkg/deploy/identity-provider/identity_provider_reconciler.go
index 50012e6899..de20e57774 100644
--- a/pkg/deploy/identity-provider/identity_provider_reconciler.go
+++ b/pkg/deploy/identity-provider/identity_provider_reconciler.go
@@ -95,8 +95,9 @@ func (ip *IdentityProviderReconciler) Finalize(ctx *deploy.DeployContext) error
 	oAuthClientName := ctx.CheCluster.Spec.Auth.OAuthClientName
 	if oAuthClientName != "" {
 		return deploy.DeleteObjectWithFinalizer(ctx, types.NamespacedName{Name: oAuthClientName}, &oauth.OAuthClient{}, OAuthFinalizerName)
+	} else {
+		return deploy.DeleteFinalizer(ctx, OAuthFinalizerName)
 	}
-	return nil
 }
 
 func syncService(deployContext *deploy.DeployContext) (bool, error) {
@@ -427,33 +428,6 @@ func deleteIdentityProvider(ctx *deploy.DeployContext) error {
 	return nil
 }
 
-// Delete OpenShift identity provider if OpenShift oAuth is false in spec
-// but OpenShiftoAuthProvisioned is true in CR status, e.g. when oAuth has been turned on and then turned off
-// deleted, err := identityprovider.ReconcileIdentityProvider(deployContext)
-// if deleted {
-// 	// ignore error
-// 	deploy.DeleteFinalizer(deployContext, deploy.OAuthFinalizerName)
-// 	for {
-// 		checluster.Status.OpenShiftoAuthProvisioned = false
-// 		if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "false"); err != nil &&
-// 			errors.IsConflict(err) {
-// 			_ = deploy.ReloadCheClusterCR(deployContext)
-// 			continue
-// 		}
-// 		break
-// 	}
-// 	for {
-// 		checluster.Spec.Auth.OAuthSecret = ""
-// 		checluster.Spec.Auth.OAuthClientName = ""
-// 		if err := deploy.UpdateCheCRStatus(deployContext, "clean oAuth secret name and client name", ""); err != nil &&
-// 			errors.IsConflict(err) {
-// 			_ = deploy.ReloadCheClusterCR(deployContext)
-// 			continue
-// 		}
-// 		break
-// 	}
-// }
-
 func createGatewayConfig(cheCluster *orgv1.CheCluster) *gateway.TraefikConfig {
 	cfg := gateway.CreateCommonTraefikConfig(
 		deploy.IdentityProviderName,
diff --git a/pkg/deploy/server/default_reconciler.go b/pkg/deploy/server/default_values_reconciler.go
similarity index 100%
rename from pkg/deploy/server/default_reconciler.go
rename to pkg/deploy/server/default_values_reconciler.go
diff --git a/pkg/deploy/server/default_reconciler_test.go b/pkg/deploy/server/default_values_reconciler_test.go
similarity index 100%
rename from pkg/deploy/server/default_reconciler_test.go
rename to pkg/deploy/server/default_values_reconciler_test.go
diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go
index cc46e4dab8..f2eec6055c 100644
--- a/pkg/deploy/server/server_configmap.go
+++ b/pkg/deploy/server/server_configmap.go
@@ -91,7 +91,7 @@ type CheConfigMap struct {
 
 // GetCheConfigMapData gets env values from CR spec and returns a map with key:value
 // which is used in CheCluster ConfigMap to configure CheCluster master behavior
-func (s *ServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (cheEnv map[string]string, err error) {
+func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (cheEnv map[string]string, err error) {
 	cheHost := ctx.CheCluster.Spec.Server.CheHost
 	identityProviderURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL
 
diff --git a/pkg/deploy/server/server_configmap_test.go b/pkg/deploy/server/server_configmap_test.go
index 0ce56e678a..038ca90a29 100644
--- a/pkg/deploy/server/server_configmap_test.go
+++ b/pkg/deploy/server/server_configmap_test.go
@@ -72,7 +72,7 @@ func TestNewCheConfigMap(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -225,7 +225,7 @@ func TestConfigMap(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -336,7 +336,7 @@ func TestUpdateBitBucketEndpoints(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, testCase.initObjects)
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -544,7 +544,7 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -677,7 +677,7 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -751,7 +751,7 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			assert.Nil(t, err)
 			util.ValidateContainData(actualData, testCase.expectedData, t)
@@ -899,7 +899,7 @@ func TestShouldSetUpCorrectlyInternalIdentityProviderServiceURL(t *testing.T) {
 			util.IsOpenShift4 = testCase.isOpenShift4
 			ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			actualData, err := server.getCheConfigMapData(ctx)
 			if err != nil {
 				t.Fatalf("Error creating ConfigMap data: %v", err)
diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go
index e802111c8f..fb76fa8ec8 100644
--- a/pkg/deploy/server/server_deployment.go
+++ b/pkg/deploy/server/server_deployment.go
@@ -29,7 +29,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
-func (s ServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) {
+func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*appsv1.Deployment, error) {
 	selfSignedCASecretExists, err := tls.IsSelfSignedCASecretExists(ctx)
 	if err != nil {
 		return nil, err
diff --git a/pkg/deploy/server/server_deployment_test.go b/pkg/deploy/server/server_deployment_test.go
index 291bf5a500..a3098c7219 100644
--- a/pkg/deploy/server/server_deployment_test.go
+++ b/pkg/deploy/server/server_deployment_test.go
@@ -94,7 +94,7 @@ func TestDeployment(t *testing.T) {
 				},
 			}
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			deployment, err := server.getDeploymentSpec(ctx)
 
 			assert.Nil(t, err)
@@ -181,7 +181,7 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error occurred %v", err)
 
@@ -279,7 +279,7 @@ func TestMountGitHubOAuthEnvVar(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error %v", err)
 
@@ -373,7 +373,7 @@ func TestMountGitLabOAuthEnvVar(t *testing.T) {
 		t.Run(testCase.name, func(t *testing.T) {
 			ctx := deploy.GetTestDeployContext(nil, testCase.initObjects)
 
-			server := NewServerReconciler()
+			server := NewCheServerReconciler()
 			deployment, err := server.getDeploymentSpec(ctx)
 			assert.Nil(t, err, "Unexpected error %v", err)
 
diff --git a/pkg/deploy/server/server_reconciler.go b/pkg/deploy/server/server_reconciler.go
index 47e225128d..5d2f6e7a7c 100644
--- a/pkg/deploy/server/server_reconciler.go
+++ b/pkg/deploy/server/server_reconciler.go
@@ -28,15 +28,15 @@ const (
 	RollingUpdateInProgressStatus = "Available: Rolling update in progress"
 )
 
-type ServerReconciler struct {
+type CheServerReconciler struct {
 	deploy.Reconcilable
 }
 
-func NewServerReconciler() *ServerReconciler {
-	return &ServerReconciler{}
+func NewCheServerReconciler() *CheServerReconciler {
+	return &CheServerReconciler{}
 }
 
-func (s *ServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
+func (s *CheServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
 	done, err := s.syncLegacyConfigMap(ctx)
 	if !done {
 		return reconcile.Result{}, false, err
@@ -77,11 +77,11 @@ func (s *ServerReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Resul
 	return reconcile.Result{}, true, nil
 }
 
-func (s *ServerReconciler) Finalize(ctx *deploy.DeployContext) error {
+func (s *CheServerReconciler) Finalize(ctx *deploy.DeployContext) error {
 	return nil
 }
 
-func (s ServerReconciler) updateCheURL(ctx *deploy.DeployContext) (bool, error) {
+func (s CheServerReconciler) updateCheURL(ctx *deploy.DeployContext) (bool, error) {
 	var cheUrl = util.GetCheURL(ctx.CheCluster)
 	if ctx.CheCluster.Status.CheURL != cheUrl {
 		ctx.CheCluster.Status.CheURL = cheUrl
@@ -92,7 +92,7 @@ func (s ServerReconciler) updateCheURL(ctx *deploy.DeployContext) (bool, error)
 	return true, nil
 }
 
-func (s *ServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, error) {
+func (s *CheServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, error) {
 	data, err := s.getCheConfigMapData(ctx)
 	if err != nil {
 		return false, err
@@ -101,7 +101,7 @@ func (s *ServerReconciler) syncCheConfigMap(ctx *deploy.DeployContext) (bool, er
 	return deploy.SyncConfigMapDataToCluster(ctx, CheConfigMapName, data, getComponentName(ctx))
 }
 
-func (s ServerReconciler) syncLegacyConfigMap(ctx *deploy.DeployContext) (bool, error) {
+func (s CheServerReconciler) syncLegacyConfigMap(ctx *deploy.DeployContext) (bool, error) {
 	// Get custom ConfigMap
 	// if it exists, add the data into CustomCheProperties
 	customConfigMap := &corev1.ConfigMap{}
@@ -129,7 +129,7 @@ func (s ServerReconciler) syncLegacyConfigMap(ctx *deploy.DeployContext) (bool,
 	return true, nil
 }
 
-func (s *ServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext) (bool, error) {
+func (s *CheServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext) (bool, error) {
 	cheDeployment := &appsv1.Deployment{}
 	exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), cheDeployment)
 	if err != nil {
@@ -172,7 +172,7 @@ func (s *ServerReconciler) updateAvailabilityStatus(ctx *deploy.DeployContext) (
 	return true, nil
 }
 
-func (s *ServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
+func (s *CheServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, error) {
 	spec, err := s.getDeploymentSpec(ctx)
 	if err != nil {
 		return false, err
@@ -181,7 +181,7 @@ func (s *ServerReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, erro
 	return deploy.SyncDeploymentSpecToCluster(ctx, spec, deploy.DefaultDeploymentDiffOpts)
 }
 
-func (s ServerReconciler) updateCheVersion(ctx *deploy.DeployContext) (bool, error) {
+func (s CheServerReconciler) updateCheVersion(ctx *deploy.DeployContext) (bool, error) {
 	cheVersion := deploy.DefaultCheVersion()
 	if ctx.CheCluster.Status.CheVersion != cheVersion {
 		ctx.CheCluster.Status.CheVersion = cheVersion
diff --git a/pkg/deploy/server/server_reconciler_test.go b/pkg/deploy/server/server_reconciler_test.go
index 336ada4aac..47fe47a020 100644
--- a/pkg/deploy/server/server_reconciler_test.go
+++ b/pkg/deploy/server/server_reconciler_test.go
@@ -51,7 +51,7 @@ func TestReconcile(t *testing.T) {
 	assert.True(t, done)
 	assert.Nil(t, err)
 
-	server := NewServerReconciler()
+	server := NewCheServerReconciler()
 	_, done, err = server.Reconcile(ctx)
 	assert.True(t, done)
 	assert.Nil(t, err)
@@ -82,7 +82,7 @@ func TestSyncLegacyConfigMap(t *testing.T) {
 	err := ctx.ClusterAPI.Client.Create(context.TODO(), legacyConfigMap)
 	assert.Nil(t, err)
 
-	server := NewServerReconciler()
+	server := NewCheServerReconciler()
 	done, err := server.syncLegacyConfigMap(ctx)
 	assert.True(t, done)
 	assert.Nil(t, err)
@@ -113,7 +113,7 @@ func TestUpdateAvailabilityStatus(t *testing.T) {
 
 	ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})
 
-	server := NewServerReconciler()
+	server := NewCheServerReconciler()
 	done, err := server.updateAvailabilityStatus(ctx)
 	assert.True(t, done)
 	assert.Nil(t, err)

From cdd139162565648de45a899fcc4eab53b7935743 Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Thu, 9 Dec 2021 11:18:48 +0200
Subject: [PATCH 5/6] Fixes

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 pkg/deploy/reconcile_manager.go | 5 +----
 pkg/deploy/sync.go              | 6 +++---
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/pkg/deploy/reconcile_manager.go b/pkg/deploy/reconcile_manager.go
index 2215dbd06b..dd7c7f0889 100644
--- a/pkg/deploy/reconcile_manager.go
+++ b/pkg/deploy/reconcile_manager.go
@@ -12,9 +12,6 @@
 package deploy
 
 import (
-	"reflect"
-	"runtime"
-
 	"github.com/sirupsen/logrus"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
@@ -71,7 +68,7 @@ func (manager *ReconcileManager) FinalizeAll(ctx *DeployContext) {
 	for _, reconciler := range manager.reconcilers {
 		err := reconciler.Finalize(ctx)
 		if err != nil {
-			reconcilerName := runtime.FuncForPC(reflect.ValueOf(reconciler).Pointer()).Name()
+			reconcilerName := GetObjectType(reconciler)
 			logrus.Errorf("Finalization failed for reconciler: `%s`, cause: %v", reconcilerName, err)
 		}
 	}
diff --git a/pkg/deploy/sync.go b/pkg/deploy/sync.go
index c06ee15809..e4b4d711b7 100644
--- a/pkg/deploy/sync.go
+++ b/pkg/deploy/sync.go
@@ -337,9 +337,9 @@ func getClientForObject(objectNamespace string, deployContext *DeployContext) cl
 	return deployContext.ClusterAPI.NonCachingClient
 }
 
-func GetObjectType(objectMeta metav1.Object) string {
-	objType := reflect.TypeOf(objectMeta).String()
-	if reflect.TypeOf(objectMeta).Kind().String() == "ptr" {
+func GetObjectType(obj interface{}) string {
+	objType := reflect.TypeOf(obj).String()
+	if reflect.TypeOf(obj).Kind().String() == "ptr" {
 		objType = objType[1:]
 	}
 

From 9c7bec0445f436d2fa17a7386d856ba7918255ce Mon Sep 17 00:00:00 2001
From: Anatolii Bazko <abazko@redhat.com>
Date: Thu, 9 Dec 2021 11:51:53 +0200
Subject: [PATCH 6/6] Fixes

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
---
 controllers/che/checluster_controller.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go
index a5b27ee7f4..c2dd5718ed 100644
--- a/controllers/che/checluster_controller.go
+++ b/controllers/che/checluster_controller.go
@@ -114,7 +114,10 @@ func NewReconciler(
 	reconcileManager.RegisterReconciler(dashboard.NewDashboardReconciler())
 	reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler())
 	reconcileManager.RegisterReconciler(server.NewCheServerReconciler())
-	reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
+
+	if util.IsOpenShift4 {
+		reconcileManager.RegisterReconciler(consolelink.NewConsoleLinkReconciler())
+	}
 
 	return &CheClusterReconciler{
 		Scheme: scheme,