From 17cc64b122c805e25afef38fc13a7609cf7f63fa Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 21 May 2021 09:21:01 +0300 Subject: [PATCH] Reworked server synchronization (#813) * Refactor server provisioning Signed-off-by: Anatolii Bazko --- pkg/controller/che/che_controller.go | 333 ++-------------- pkg/controller/che/che_controller_test.go | 27 +- .../che}/configmap_cert.go | 2 +- .../che}/configmap_cert_test.go | 2 +- pkg/controller/che/create.go | 46 +-- pkg/controller/che/proxy.go | 31 +- pkg/controller/che/proxy_test.go | 11 +- pkg/controller/che/status.go | 96 ----- pkg/controller/che/update.go | 103 ----- pkg/deploy/checluster.go | 78 +++- pkg/deploy/checluster_test.go | 60 +++ pkg/deploy/finalizer.go | 8 +- .../identity-provider/identity_provider.go | 41 +- pkg/deploy/postgres/postgres.go | 4 +- pkg/deploy/server/che_service_test.go | 128 ------ pkg/deploy/server/server.go | 367 ++++++++++++++++++ .../{che_configmap.go => server_configmap.go} | 136 +++---- ...igmap_test.go => server_configmap_test.go} | 21 +- ...deployment_che.go => server_deployment.go} | 61 ++- ..._che_test.go => server_deployment_test.go} | 6 +- pkg/deploy/server/server_test.go | 324 ++++++++++++++++ pkg/deploy/server/service.go | 39 -- pkg/deploy/service.go | 4 +- pkg/util/util.go | 10 - pkg/util/util_test.go | 36 -- 25 files changed, 1074 insertions(+), 900 deletions(-) rename pkg/{deploy/server => controller/che}/configmap_cert.go (99%) rename pkg/{deploy/server => controller/che}/configmap_cert_test.go (99%) delete mode 100644 pkg/controller/che/status.go delete mode 100644 pkg/controller/che/update.go create mode 100644 pkg/deploy/checluster_test.go delete mode 100644 pkg/deploy/server/che_service_test.go create mode 100644 pkg/deploy/server/server.go rename pkg/deploy/server/{che_configmap.go => server_configmap.go} (69%) rename pkg/deploy/server/{che_configmap_test.go => server_configmap_test.go} (97%) rename pkg/deploy/server/{deployment_che.go => server_deployment.go} (84%) rename pkg/deploy/server/{deployment_che_test.go => server_deployment_test.go} (97%) create mode 100644 pkg/deploy/server/server_test.go delete mode 100644 pkg/deploy/server/service.go diff --git a/pkg/controller/che/che_controller.go b/pkg/controller/che/che_controller.go index 270dbc9e4..cf051b940 100644 --- a/pkg/controller/che/che_controller.go +++ b/pkg/controller/che/che_controller.go @@ -91,8 +91,6 @@ func newReconciler(mgr manager.Manager) (reconcile.Reconciler, error) { // add adds a new Controller to mgr with r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { - isOpenShift, _, err := util.DetectOpenShift() - onAllExceptGenericEventsPredicate := predicate.Funcs{ UpdateFunc: func(evt event.UpdateEvent) bool { return true @@ -108,16 +106,13 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { }, } - if err != nil { - logrus.Errorf("An error occurred when detecting current infra: %s", err) - } // Create a new controller c, err := controller.New("che-controller", mgr, controller.Options{Reconciler: r}) if err != nil { return err } // register OpenShift specific types in the scheme - if isOpenShift { + if util.IsOpenShift { if err := routev1.AddToScheme(mgr.GetScheme()); err != nil { logrus.Errorf("Failed to add OpenShift route to scheme: %s", err) } @@ -236,7 +231,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { return err } - if isOpenShift { + if util.IsOpenShift { err = c.Watch(&source.Kind{Type: &routev1.Route{}}, &handler.EnqueueRequestForOwner{ IsController: true, OwnerType: &orgv1.CheCluster{}, @@ -355,39 +350,24 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return imagePullerResult, err } - isOpenShift, isOpenShift4, err := util.DetectOpenShift() - if err != nil { - logrus.Errorf("An error occurred when detecting current infra: %s", err) - } - // Check Che CR correctness if !util.IsTestMode() { if err := ValidateCheCR(instance); err != nil { // Che cannot be deployed with current configuration. // Print error message in logs and wait until the configuration is changed. logrus.Error(err) - if err := r.SetStatusDetails(instance, request, failedValidationReason, err.Error(), ""); err != nil { + if err := deploy.SetStatusDetails(deployContext, failedValidationReason, err.Error(), ""); err != nil { return reconcile.Result{}, err } return reconcile.Result{}, nil } } - if !util.IsTestMode() { - if isOpenShift && deployContext.DefaultCheHost == "" { - host, err := getDefaultCheHost(deployContext) - if host == "" { - return reconcile.Result{RequeueAfter: 1 * time.Second}, err - } - deployContext.DefaultCheHost = host - } - } - - if isOpenShift4 && util.IsDeleteOAuthInitialUser(instance) { + if util.IsOpenShift4 && util.IsDeleteOAuthInitialUser(instance) { if err := r.userHandler.DeleteOAuthInitialUser(deployContext); err != nil { logrus.Errorf("Unable to delete initial OpenShift OAuth user from a cluster. Cause: %s", err.Error()) instance.Spec.Auth.InitialOpenShiftOAuthUser = nil - err := r.UpdateCheCRSpec(instance, "initialOpenShiftOAuthUser", "nil") + err := deploy.UpdateCheCRSpec(deployContext, "initialOpenShiftOAuthUser", "nil") return reconcile.Result{}, err } @@ -398,7 +378,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e "initialOpenShiftOAuthUser": "nil", } - if err := r.UpdateCheCRSpecByFields(instance, updateFields); err != nil { + if err := deploy.UpdateCheCRSpecByFields(deployContext, updateFields); err != nil { return reconcile.Result{}, err } @@ -414,14 +394,14 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{RequeueAfter: time.Second}, err } else if !exists { instance.Status.OpenShiftOAuthUserCredentialsSecret = "" - if err := r.UpdateCheCRStatus(instance, "openShiftOAuthUserCredentialsSecret", ""); err != nil { + if err := deploy.UpdateCheCRStatus(deployContext, "openShiftOAuthUserCredentialsSecret", ""); err != nil { return reconcile.Result{}, err } } } - if isOpenShift && instance.Spec.Auth.OpenShiftoAuth == nil { - if reconcileResult, err := r.autoEnableOAuth(deployContext, request, isOpenShift4); err != nil { + if util.IsOpenShift && instance.Spec.Auth.OpenShiftoAuth == nil { + if reconcileResult, err := r.autoEnableOAuth(deployContext, request, util.IsOpenShift4); err != nil { return reconcileResult, err } } @@ -437,7 +417,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e } // Read proxy configuration - proxy, err := r.getProxyConfiguration(instance) + proxy, err := r.getProxyConfiguration(deployContext) if err != nil { logrus.Errorf("Error on reading proxy configuration: %v", err) return reconcile.Result{}, err @@ -458,9 +438,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e } } - cheFlavor := deploy.DefaultCheFlavor(instance) - cheDeploymentName := cheFlavor - // Detect whether self-signed certificate is used selfSignedCertUsed, err := deploy.IsSelfSignedCertificateUsed(deployContext) if err != nil { @@ -468,28 +445,18 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{}, err } - if isOpenShift { + if util.IsOpenShift { // create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate if selfSignedCertUsed || // To use Openshift v4 OAuth, the OAuth endpoints are served from a namespace // and NOT from the Openshift API Master URL (as in v3) // So we also need the self-signed certificate to access them (same as the Che server) - (isOpenShift4 && util.IsOAuthEnabled(instance) && !instance.Spec.Server.TlsSupport) { + (util.IsOpenShift4 && util.IsOAuthEnabled(instance) && !instance.Spec.Server.TlsSupport) { if err := deploy.CreateTLSSecretFromEndpoint(deployContext, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil { return reconcile.Result{}, err } } - if !tests { - deployment := &appsv1.Deployment{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: cheDeploymentName, Namespace: instance.Namespace}, deployment) - if err != nil && instance.Status.CheClusterRunning != UnavailableStatus { - if err := r.SetCheUnavailableStatus(instance, request); err != nil { - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err - } - } - } - if util.IsOAuthEnabled(instance) { // create a secret with OpenShift API crt to be added to keystore that RH SSO will consume apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls() @@ -537,34 +504,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{}, err } - // Get custom ConfigMap - // if it exists, add the data into CustomCheProperties - customConfigMap := &corev1.ConfigMap{} - err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: instance.Namespace, Name: "custom"}, customConfigMap) - if err != nil && !errors.IsNotFound(err) { - logrus.Errorf("Error getting custom configMap: %v", err) - return reconcile.Result{}, err - } - if err == nil { - logrus.Infof("Found legacy custom ConfigMap. Adding those values to CheCluster.Spec.Server.CustomCheProperties") - if instance.Spec.Server.CustomCheProperties == nil { - instance.Spec.Server.CustomCheProperties = make(map[string]string) - } - for k, v := range customConfigMap.Data { - instance.Spec.Server.CustomCheProperties[k] = v - } - if err := r.client.Update(context.TODO(), instance); err != nil { - logrus.Errorf("Error updating CheCluster: %v", err) - return reconcile.Result{}, err - } - if err = r.client.Delete(context.TODO(), customConfigMap); err != nil { - logrus.Errorf("Error deleting legacy custom ConfigMap: %v", err) - return reconcile.Result{}, err - } - return reconcile.Result{RequeueAfter: 5 * time.Second}, nil - } - - if err := r.SetStatusDetails(instance, request, "", "", ""); err != nil { + if err := deploy.SetStatusDetails(deployContext, "", "", ""); err != nil { return reconcile.Result{}, err } @@ -623,28 +563,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e instance, _ = r.GetCR(request) return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err } - cheMultiUser := deploy.GetCheMultiUser(instance) - - if cheMultiUser == "false" { - claimSize := util.GetValue(instance.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize) - done, err := deploy.SyncPVCToCluster(deployContext, deploy.DefaultCheVolumeClaimName, claimSize, cheFlavor) - if !done { - if err != nil { - logrus.Error(err) - } else { - logrus.Infof("Waiting on pvc '%s' to be bound. Sometimes PVC can be bound only when the first consumer is created.", deploy.DefaultCheVolumeClaimName) - } - return reconcile.Result{}, err - } - } else { - done, err := deploy.DeleteNamespacedObject(deployContext, deploy.DefaultCheVolumeClaimName, &corev1.PersistentVolumeClaim{}) - if !done { - if err != nil { - logrus.Error(err) - } - return reconcile.Result{}, err - } - } if !deployContext.CheCluster.Spec.Database.ExternalDb { postgres := postgres.NewPostgres(deployContext) @@ -657,105 +575,34 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e } } - tlsSupport := instance.Spec.Server.TlsSupport - protocol := "http" - if tlsSupport { - protocol = "https" - } - - // create Che service and route - done, err = server.SyncCheServiceToCluster(deployContext) + // 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 reconcile.Result{}, err } - exposedServiceName := getServerExposingServiceName(instance) - cheHost := "" - if !isOpenShift { - _, done, err := deploy.SyncIngressToCluster( - deployContext, - cheFlavor, - instance.Spec.Server.CheHost, - "", - exposedServiceName, - 8080, - deployContext.CheCluster.Spec.Server.CheServerIngress, - cheFlavor) - if !done { - logrus.Infof("Waiting on ingress '%s' to be ready", cheFlavor) - if err != nil { - logrus.Error(err) - } - - return reconcile.Result{RequeueAfter: time.Second * 1}, err - } - - ingress := &v1beta1.Ingress{} - exists, err := deploy.GetNamespacedObject(deployContext, cheFlavor, ingress) - if !exists { - return reconcile.Result{}, err - } else if err != nil { - logrus.Error(err) - return reconcile.Result{}, err - } - cheHost = ingress.Spec.Rules[0].Host - } else { - customHost := instance.Spec.Server.CheHost - if deployContext.DefaultCheHost == customHost { - // let OpenShift set a hostname by itself since it requires a routes/custom-host permissions - customHost = "" - } - - done, err := deploy.SyncRouteToCluster( - deployContext, - cheFlavor, - customHost, - "/", - exposedServiceName, - 8080, - deployContext.CheCluster.Spec.Server.CheServerRoute, - cheFlavor) - if !done { - if err != nil { - logrus.Error(err) - } - return reconcile.Result{}, err - } - - route := &routev1.Route{} - exists, err := deploy.GetNamespacedObject(deployContext, cheFlavor, route) - if !exists { - if err != nil { - logrus.Error(err) - } - return reconcile.Result{}, err - } - cheHost = route.Spec.Host - if customHost == "" { - deployContext.DefaultCheHost = cheHost - } - } - if instance.Spec.Server.CheHost != cheHost { - instance.Spec.Server.CheHost = cheHost - if err := r.UpdateCheCRSpec(instance, "CheHost URL", cheHost); err != nil { - instance, _ = r.GetCR(request) - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err - } - } - // create and provision Keycloak related objects - provisioned, err := identity_provider.SyncIdentityProviderToCluster(deployContext) - if !tests { + if !instance.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 reconcile.Result{}, err } + } else { + keycloakURL := instance.Spec.Auth.IdentityProviderURL + if instance.Status.KeycloakURL != keycloakURL { + instance.Status.KeycloakURL = keycloakURL + if err := deploy.UpdateCheCRStatus(deployContext, "status: Keycloak URL", keycloakURL); err != nil { + return reconcile.Result{}, err + } + } } if !instance.Spec.Server.ExternalPluginRegistry { @@ -770,7 +617,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e } else { if instance.Spec.Server.PluginRegistryUrl != instance.Status.PluginRegistryURL { instance.Status.PluginRegistryURL = instance.Spec.Server.PluginRegistryUrl - if err := r.UpdateCheCRStatus(instance, "status: Plugin Registry URL", instance.Spec.Server.PluginRegistryUrl); err != nil { + if err := deploy.UpdateCheCRStatus(deployContext, "status: Plugin Registry URL", instance.Spec.Server.PluginRegistryUrl); err != nil { return reconcile.Result{}, err } } @@ -793,7 +640,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e if instance.Spec.Server.DevfileRegistryUrl != instance.Status.DevfileRegistryURL { instance.Status.DevfileRegistryURL = instance.Spec.Server.DevfileRegistryUrl - if err := r.UpdateCheCRStatus(instance, "status: Devfile Registry URL", instance.Spec.Server.DevfileRegistryUrl); err != nil { + if err := deploy.UpdateCheCRStatus(deployContext, "status: Devfile Registry URL", instance.Spec.Server.DevfileRegistryUrl); err != nil { return reconcile.Result{}, err } } @@ -808,73 +655,18 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{}, err } - // create Che ConfigMap which is synced with CR and is not supposed to be manually edited - // controller will reconcile this CM with CR spec - done, err = server.SyncCheConfigMapToCluster(deployContext) - if !tests { - if !done { - logrus.Infof("Waiting on config map '%s' to be created", server.CheConfigMapName) - if err != nil { - logrus.Error(err) - } - return reconcile.Result{}, err - } - } - err = gateway.SyncGatewayToCluster(deployContext) if err != nil { logrus.Errorf("Failed to create the Server Gateway: %s", err) return reconcile.Result{}, err } - // Create a new che deployment - provisioned, err = server.SyncCheDeploymentToCluster(deployContext) - if !tests { - if !provisioned { - logrus.Infof("Waiting on deployment '%s' to be ready", cheFlavor) - if err != nil { - logrus.Error(err) - } - - cheDeployment := &appsv1.Deployment{} - exists, err := deploy.GetNamespacedObject(deployContext, cheFlavor, cheDeployment) - if exists { - if cheDeployment.Status.AvailableReplicas < 1 { - if instance.Status.CheClusterRunning != UnavailableStatus { - if err := r.SetCheUnavailableStatus(instance, request); err != nil { - instance, _ = r.GetCR(request) - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err - } - } - } else if cheDeployment.Status.Replicas != 1 { - if instance.Status.CheClusterRunning != RollingUpdateInProgressStatus { - if err := r.SetCheRollingUpdateStatus(instance, request); err != nil { - instance, _ = r.GetCR(request) - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err - } - } - } - } - return reconcile.Result{}, err - } - } - // Update available status - if instance.Status.CheClusterRunning != AvailableStatus { - cheHost := instance.Spec.Server.CheHost - if err := r.SetCheAvailableStatus(instance, request, protocol, cheHost); err != nil { - instance, _ = r.GetCR(request) - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err - } - } - - // Update Che version status - cheVersion := EvaluateCheServerVersion(instance) - if instance.Status.CheVersion != cheVersion { - instance.Status.CheVersion = cheVersion - if err := r.UpdateCheCRStatus(instance, "version", cheVersion); err != nil { - instance, _ = r.GetCR(request) - return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, 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 @@ -889,13 +681,13 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e // 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 := r.ReconcileIdentityProvider(instance, isOpenShift4) + deleted, err := identity_provider.ReconcileIdentityProvider(deployContext) if deleted { // ignore error deploy.DeleteFinalizer(deployContext, deploy.OAuthFinalizerName) for { instance.Status.OpenShiftoAuthProvisioned = false - if err := r.UpdateCheCRStatus(instance, "status: provisioned with OpenShift identity provider", "false"); err != nil && + if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "false"); err != nil && errors.IsConflict(err) { instance, _ = r.GetCR(request) continue @@ -905,7 +697,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e for { instance.Spec.Auth.OAuthSecret = "" instance.Spec.Auth.OAuthClientName = "" - if err := r.UpdateCheCRSpec(instance, "clean oAuth secret name and client name", ""); err != nil && + if err := deploy.UpdateCheCRStatus(deployContext, "clean oAuth secret name and client name", ""); err != nil && errors.IsConflict(err) { instance, _ = r.GetCR(request) continue @@ -917,49 +709,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{}, nil } -// EvaluateCheServerVersion evaluate che version -// based on Checluster information and image defaults from env variables -func EvaluateCheServerVersion(cr *orgv1.CheCluster) string { - return util.GetValue(cr.Spec.Server.CheImageTag, deploy.DefaultCheVersion()) -} - -func getDefaultCheHost(deployContext *deploy.DeployContext) (string, error) { - cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster) - done, err := deploy.SyncRouteToCluster( - deployContext, - cheFlavor, - "", - "/", - getServerExposingServiceName(deployContext.CheCluster), - 8080, - deployContext.CheCluster.Spec.Server.CheServerRoute, - cheFlavor) - if !done { - if err != nil { - logrus.Error(err) - } - return "", err - } - - route := &routev1.Route{} - exists, err := deploy.GetNamespacedObject(deployContext, cheFlavor, route) - if !exists { - if err != nil { - logrus.Error(err) - } - return "", err - } - - return route.Spec.Host, nil -} - -func getServerExposingServiceName(cr *orgv1.CheCluster) string { - if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == "gateway" { - return gateway.GatewayServiceName - } - return deploy.CheServiceName -} - // isTrustedBundleConfigMap detects whether given config map is the config map with additional CA certificates to be trusted by Che func isTrustedBundleConfigMap(mgr manager.Manager, obj handler.MapObject) (bool, reconcile.Request) { checlusters := &orgv1.CheClusterList{} @@ -1019,7 +768,7 @@ func (r *ReconcileChe) autoEnableOAuth(deployContext *deploy.DeployContext, requ reason = failedNoIdentityProviders // Don't try to create initial user any more, che-operator shouldn't hang on this step. cr.Spec.Auth.InitialOpenShiftOAuthUser = nil - if err := r.UpdateCheCRStatus(cr, "initialOpenShiftOAuthUser", ""); err != nil { + if err := deploy.UpdateCheCRStatus(deployContext, "initialOpenShiftOAuthUser", ""); err != nil { return reconcile.Result{}, err } oauth = false @@ -1030,7 +779,7 @@ func (r *ReconcileChe) autoEnableOAuth(deployContext *deploy.DeployContext, requ oauth = true if deployContext.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret == "" { deployContext.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret = openShiftOAuthUserCredentialsSecret - if err := r.UpdateCheCRStatus(cr, "openShiftOAuthUserCredentialsSecret", openShiftOAuthUserCredentialsSecret); err != nil { + if err := deploy.UpdateCheCRStatus(deployContext, "openShiftOAuthUserCredentialsSecret", openShiftOAuthUserCredentialsSecret); err != nil { return reconcile.Result{}, err } } @@ -1057,13 +806,13 @@ func (r *ReconcileChe) autoEnableOAuth(deployContext *deploy.DeployContext, requ newOAuthValue := util.NewBoolPointer(oauth) if !reflect.DeepEqual(newOAuthValue, cr.Spec.Auth.OpenShiftoAuth) { cr.Spec.Auth.OpenShiftoAuth = newOAuthValue - if err := r.UpdateCheCRSpec(cr, "openShiftoAuth", strconv.FormatBool(oauth)); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "openShiftoAuth", strconv.FormatBool(oauth)); err != nil { return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err } } if message != "" && reason != "" { - if err := r.SetStatusDetails(cr, request, message, reason, ""); err != nil { + if err := deploy.SetStatusDetails(deployContext, message, reason, ""); err != nil { return reconcile.Result{}, err } } diff --git a/pkg/controller/che/che_controller_test.go b/pkg/controller/che/che_controller_test.go index 2c23f2239..6447257cf 100644 --- a/pkg/controller/che/che_controller_test.go +++ b/pkg/controller/che/che_controller_test.go @@ -34,7 +34,7 @@ import ( console "github.com/openshift/api/console/v1" orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" - oauth_config "github.com/openshift/api/config/v1" + configv1 "github.com/openshift/api/config/v1" oauth "github.com/openshift/api/oauth/v1" routev1 "github.com/openshift/api/route/v1" userv1 "github.com/openshift/api/user/v1" @@ -171,19 +171,19 @@ var ( }, } oAuthClient = &oauth.OAuthClient{} - oAuthWithNoIdentityProviders = &oauth_config.OAuth{ + oAuthWithNoIdentityProviders = &configv1.OAuth{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: namespace, }, } - oAuthWithIdentityProvider = &oauth_config.OAuth{ + oAuthWithIdentityProvider = &configv1.OAuth{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: namespace, }, - Spec: oauth_config.OAuthSpec{ - IdentityProviders: []oauth_config.IdentityProvider{ + Spec: configv1.OAuthSpec{ + IdentityProviders: []configv1.IdentityProvider{ { Name: "htpasswd", }, @@ -357,7 +357,7 @@ func TestCaseAutoDetectOAuth(t *testing.T) { orgv1.SchemeBuilder.AddToScheme(scheme) scheme.AddKnownTypes(oauth.SchemeGroupVersion, oAuthClient) scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{}) - scheme.AddKnownTypes(oauth_config.SchemeGroupVersion, &oauth_config.OAuth{}) + scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{}) scheme.AddKnownTypes(routev1.GroupVersion, route) initCR := InitCheWithSimpleCR().DeepCopy() initCR.Spec.Auth.OpenShiftoAuth = testCase.initialOAuthValue @@ -397,7 +397,8 @@ func TestCaseAutoDetectOAuth(t *testing.T) { }, } - os.Setenv("OPENSHIFT_VERSION", testCase.openshiftVersion) + util.IsOpenShift = true + util.IsOpenShift4 = testCase.openshiftVersion == "4" _, err := r.Reconcile(req) if err != nil { @@ -786,7 +787,9 @@ func TestImagePullerConfiguration(t *testing.T) { } func TestCheController(t *testing.T) { - os.Setenv("OPENSHIFT_VERSION", "3") + util.IsOpenShift = true + util.IsOpenShift4 = false + // Set the logger to development mode for verbose logs. logf.SetLogger(logf.ZapLogger(true)) @@ -1007,7 +1010,7 @@ func TestCheController(t *testing.T) { } func TestConfiguringLabelsForRoutes(t *testing.T) { - os.Setenv("OPENSHIFT_VERSION", "3") + util.IsOpenShift = true // Set the logger to development mode for verbose logs. logf.SetLogger(logf.ZapLogger(true)) @@ -1060,7 +1063,8 @@ func TestConfiguringLabelsForRoutes(t *testing.T) { } func TestShouldDelegatePermissionsForCheWorkspaces(t *testing.T) { - os.Setenv("OPENSHIFT_VERSION", "3") + util.IsOpenShift = true + type testCase struct { name string initObjects []runtime.Object @@ -1119,7 +1123,7 @@ func TestShouldDelegatePermissionsForCheWorkspaces(t *testing.T) { orgv1.SchemeBuilder.AddToScheme(scheme) scheme.AddKnownTypes(oauth.SchemeGroupVersion, oAuthClient) scheme.AddKnownTypes(userv1.SchemeGroupVersion, &userv1.UserList{}, &userv1.User{}) - scheme.AddKnownTypes(oauth_config.SchemeGroupVersion, &oauth_config.OAuth{}) + scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.OAuth{}, &configv1.Proxy{}) scheme.AddKnownTypes(routev1.GroupVersion, route) initCR := testCase.checluster @@ -1227,6 +1231,7 @@ func Init() (client.Client, discovery.DiscoveryInterface, runtime.Scheme) { // Register operator types with the runtime scheme scheme.AddKnownTypes(oauth.SchemeGroupVersion, oAuthClient) scheme.AddKnownTypes(userv1.SchemeGroupVersion, users, user) + scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Proxy{}) // Create a fake client to mock API calls return fake.NewFakeClient(objs...), ds, scheme diff --git a/pkg/deploy/server/configmap_cert.go b/pkg/controller/che/configmap_cert.go similarity index 99% rename from pkg/deploy/server/configmap_cert.go rename to pkg/controller/che/configmap_cert.go index 25592d785..2b1f11773 100644 --- a/pkg/deploy/server/configmap_cert.go +++ b/pkg/controller/che/configmap_cert.go @@ -9,7 +9,7 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package server +package che import ( "context" diff --git a/pkg/deploy/server/configmap_cert_test.go b/pkg/controller/che/configmap_cert_test.go similarity index 99% rename from pkg/deploy/server/configmap_cert_test.go rename to pkg/controller/che/configmap_cert_test.go index 3e5871b82..c31bf450f 100644 --- a/pkg/deploy/server/configmap_cert_test.go +++ b/pkg/controller/che/configmap_cert_test.go @@ -9,7 +9,7 @@ // Contributors: // Red Hat, Inc. - initial API and implementation // -package server +package che import ( "context" diff --git a/pkg/controller/che/create.go b/pkg/controller/che/create.go index 12a2750ec..2683c2731 100644 --- a/pkg/controller/che/create.go +++ b/pkg/controller/che/create.go @@ -24,7 +24,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext cheNamespace := deployContext.CheCluster.Namespace if len(deployContext.CheCluster.Spec.Server.CheFlavor) < 1 { deployContext.CheCluster.Spec.Server.CheFlavor = cheFlavor - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "installation flavor", cheFlavor); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "installation flavor", cheFlavor); err != nil { return err } } @@ -39,19 +39,19 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext return err } deployContext.CheCluster.Spec.Database.ChePostgresSecret = chePostgresSecret - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "Postgres Secret", chePostgresSecret); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Postgres User", deployContext.CheCluster.Spec.Database.ChePostgresUser); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "auto-generated CheCluster DB password", "password-hidden"); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "auto-generated CheCluster DB password", "password-hidden"); err != nil { return err } } @@ -75,7 +75,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext return err } deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret = identityPostgresSecret - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "Identity Provider Postgres Secret", identityPostgresSecret); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "Identity Provider Postgres Secret", identityPostgresSecret); err != nil { return err } } @@ -99,19 +99,19 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext return err } deployContext.CheCluster.Spec.Auth.IdentityProviderSecret = identityProviderSecret - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "Identity Provider Secret", identityProviderSecret); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Keycloak admin password", "password hidden"); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Keycloak admin username", keycloakAdminUserName); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak admin username", keycloakAdminUserName); err != nil { return err } } @@ -121,28 +121,28 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Postgres DB", chePostgresDb); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Postgres hostname", chePostgresHostName); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Postgres port", chePostgresPort); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "Postgres port", chePostgresPort); err != nil { return err } } 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "Keycloak realm", keycloakRealm); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak realm", keycloakRealm); err != nil { return err } } @@ -150,7 +150,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext if len(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 { deployContext.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "Keycloak client ID", keycloakClientId); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak client ID", keycloakClientId); err != nil { return err } } @@ -159,28 +159,28 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "log level", cheLogLevel); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "debug", cheDebug); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "pvc strategy", pvcStrategy); err != nil { + 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "pvc claim size", pvcClaimSize); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "pvc claim size", pvcClaimSize); err != nil { return err } } @@ -193,7 +193,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "pvc jobs image", deployContext.CheCluster.Spec.Storage.PvcJobsImage); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "pvc jobs image", deployContext.CheCluster.Spec.Storage.PvcJobsImage); err != nil { return err } } @@ -201,7 +201,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "postgres image", deployContext.CheCluster.Spec.Database.PostgresImage); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "postgres image", deployContext.CheCluster.Spec.Database.PostgresImage); err != nil { return err } } @@ -209,7 +209,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext 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 := r.UpdateCheCRSpec(deployContext.CheCluster, "keycloak image", deployContext.CheCluster.Spec.Auth.IdentityProviderImage); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "keycloak image", deployContext.CheCluster.Spec.Auth.IdentityProviderImage); err != nil { return err } } @@ -218,7 +218,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext !deployContext.CheCluster.Spec.Server.ExternalPluginRegistry && deployContext.CheCluster.Spec.Server.PluginRegistryUrl == deploy.OldCrwPluginRegistryUrl { deployContext.CheCluster.Spec.Server.PluginRegistryUrl = "" - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "plugin registry url", deployContext.CheCluster.Spec.Server.PluginRegistryUrl); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "plugin registry url", deployContext.CheCluster.Spec.Server.PluginRegistryUrl); err != nil { return err } } @@ -226,7 +226,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext if deploy.MigratingToCRW2_0(deployContext.CheCluster) && deployContext.CheCluster.Spec.Server.CheImage == deploy.OldDefaultCodeReadyServerImageRepo { deployContext.CheCluster.Spec.Server.CheImage = "" - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "che image repo", deployContext.CheCluster.Spec.Server.CheImage); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "che image repo", deployContext.CheCluster.Spec.Server.CheImage); err != nil { return err } } @@ -234,7 +234,7 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext if deploy.MigratingToCRW2_0(deployContext.CheCluster) && deployContext.CheCluster.Spec.Server.CheImageTag == deploy.OldDefaultCodeReadyServerImageTag { deployContext.CheCluster.Spec.Server.CheImageTag = "" - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "che image tag", deployContext.CheCluster.Spec.Server.CheImageTag); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "che image tag", deployContext.CheCluster.Spec.Server.CheImageTag); err != nil { return err } } diff --git a/pkg/controller/che/proxy.go b/pkg/controller/che/proxy.go index c8da45672..90ffc9911 100644 --- a/pkg/controller/che/proxy.go +++ b/pkg/controller/che/proxy.go @@ -12,30 +12,29 @@ package che import ( - "context" - - orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/deploy/server" "github.com/eclipse-che/che-operator/pkg/util" configv1 "github.com/openshift/api/config/v1" - "k8s.io/apimachinery/pkg/types" ) -func (r *ReconcileChe) getProxyConfiguration(checluster *orgv1.CheCluster) (*deploy.Proxy, error) { +func (r *ReconcileChe) getProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, error) { // OpenShift 4.x if util.IsOpenShift4 { clusterProxy := &configv1.Proxy{} - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, clusterProxy); err != nil { - return nil, err - } - - clusterWideProxyConf, err := deploy.ReadClusterWideProxyConfiguration(clusterProxy) + exists, err := deploy.GetClusterObject(deployContext, "cluster", clusterProxy) if err != nil { return nil, err } - cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(checluster) + clusterWideProxyConf := &deploy.Proxy{} + if exists { + clusterWideProxyConf, err = deploy.ReadClusterWideProxyConfiguration(clusterProxy) + if err != nil { + return nil, err + } + } + + cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext.CheCluster) if err != nil { return nil, err } @@ -58,11 +57,11 @@ func (r *ReconcileChe) getProxyConfiguration(checluster *orgv1.CheCluster) (*dep } // OpenShift 3.x and k8s - cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(checluster) + cheClusterProxyConf, err := deploy.ReadCheClusterProxyConfiguration(deployContext.CheCluster) if err != nil { return nil, err } - if checluster.Spec.Server.UseInternalClusterSVCNames { + if deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames { cheClusterProxyConf.NoProxy = deploy.MergeNonProxy(cheClusterProxyConf.NoProxy, ".svc") } return cheClusterProxyConf, nil @@ -71,10 +70,10 @@ func (r *ReconcileChe) getProxyConfiguration(checluster *orgv1.CheCluster) (*dep func (r *ReconcileChe) putOpenShiftCertsIntoConfigMap(deployContext *deploy.DeployContext) (bool, error) { if deployContext.CheCluster.Spec.Server.ServerTrustStoreConfigMapName == "" { deployContext.CheCluster.Spec.Server.ServerTrustStoreConfigMapName = deploy.DefaultServerTrustStoreConfigMapName() - if err := r.UpdateCheCRSpec(deployContext.CheCluster, "truststore configmap", deploy.DefaultServerTrustStoreConfigMapName()); err != nil { + if err := deploy.UpdateCheCRSpec(deployContext, "truststore configmap", deploy.DefaultServerTrustStoreConfigMapName()); err != nil { return false, err } } - return server.SyncTrustStoreConfigMapToCluster(deployContext) + return SyncTrustStoreConfigMapToCluster(deployContext) } diff --git a/pkg/controller/che/proxy_test.go b/pkg/controller/che/proxy_test.go index edc77283f..7eea7169b 100644 --- a/pkg/controller/che/proxy_test.go +++ b/pkg/controller/che/proxy_test.go @@ -332,7 +332,16 @@ func TestReadProxyConfiguration(t *testing.T) { scheme: scheme, } - actualProxyConf, err := r.getProxyConfiguration(testCase.cheCluster) + deployContext := &deploy.DeployContext{ + CheCluster: testCase.cheCluster, + ClusterAPI: deploy.ClusterAPI{ + Client: cli, + NonCachedClient: cli, + Scheme: scheme, + }, + } + + actualProxyConf, err := r.getProxyConfiguration(deployContext) if err != nil { t.Fatalf("Error reading proxy configuration: %v", err) } diff --git a/pkg/controller/che/status.go b/pkg/controller/che/status.go deleted file mode 100644 index 1437b6f4e..000000000 --- a/pkg/controller/che/status.go +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright (c) 2012-2019 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 ( - orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" - "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/sirupsen/logrus" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -const ( - AvailableStatus = "Available" - UnavailableStatus = "Unavailable" - RollingUpdateInProgressStatus = "Available: Rolling update in progress" -) - -func (r *ReconcileChe) SetCheAvailableStatus(instance *orgv1.CheCluster, request reconcile.Request, protocol string, cheHost string) (err error) { - cheFlavor := deploy.DefaultCheFlavor(instance) - name := "Eclipse Che" - if cheFlavor == "codeready" { - name = "CodeReady Workspaces" - } - keycloakURL := instance.Spec.Auth.IdentityProviderURL - instance.Status.KeycloakURL = keycloakURL - if err := r.UpdateCheCRStatus(instance, "Keycloak URL status", keycloakURL); err != nil { - instance, _ = r.GetCR(request) - return err - } - instance.Status.CheClusterRunning = AvailableStatus - if err := r.UpdateCheCRStatus(instance, "status: "+name+" server", AvailableStatus); err != nil { - instance, _ = r.GetCR(request) - return err - } - instance.Status.CheURL = protocol + "://" + cheHost - if err := r.UpdateCheCRStatus(instance, name+" server URL", protocol+"://"+cheHost); err != nil { - instance, _ = r.GetCR(request) - return err - } - logrus.Infof(name+" is now available at: %s://%s", protocol, cheHost) - return nil - -} - -func (r *ReconcileChe) SetCheUnavailableStatus(instance *orgv1.CheCluster, request reconcile.Request) (err error) { - instance.Status.CheClusterRunning = UnavailableStatus - if err := r.UpdateCheCRStatus(instance, "status: Che API", UnavailableStatus); err != nil { - instance, _ = r.GetCR(request) - return err - } - return nil -} - -func (r *ReconcileChe) SetStatusDetails(instance *orgv1.CheCluster, request reconcile.Request, reason string, message string, helpLink string) (err error) { - if reason != instance.Status.Reason { - instance.Status.Reason = reason - if err := r.UpdateCheCRStatus(instance, "status: Reason", reason); err != nil { - instance, _ = r.GetCR(request) - return err - } - } - if message != instance.Status.Message { - instance.Status.Message = message - if err := r.UpdateCheCRStatus(instance, "status: Message", message); err != nil { - instance, _ = r.GetCR(request) - return err - } - } - if helpLink != instance.Status.HelpLink { - instance.Status.HelpLink = helpLink - if err := r.UpdateCheCRStatus(instance, "status: HelpLink", message); err != nil { - instance, _ = r.GetCR(request) - return err - } - } - return nil -} - -func (r *ReconcileChe) SetCheRollingUpdateStatus(instance *orgv1.CheCluster, request reconcile.Request) (err error) { - - instance.Status.CheClusterRunning = RollingUpdateInProgressStatus - if err := r.UpdateCheCRStatus(instance, "status", RollingUpdateInProgressStatus); err != nil { - instance, _ = r.GetCR(request) - return err - } - return nil -} diff --git a/pkg/controller/che/update.go b/pkg/controller/che/update.go deleted file mode 100644 index 6bda71078..000000000 --- a/pkg/controller/che/update.go +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2012-2019 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 ( - "context" - "fmt" - "strings" - - "github.com/eclipse-che/che-operator/pkg/deploy" - - orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" - identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider" - "github.com/eclipse-che/che-operator/pkg/util" - oauth "github.com/openshift/api/oauth/v1" - "github.com/sirupsen/logrus" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/types" -) - -func (r *ReconcileChe) UpdateCheCRStatus(instance *orgv1.CheCluster, updatedField string, value string) (err error) { - logrus.Infof("Updating %s CR with %s: %s", instance.Name, updatedField, value) - err = r.client.Status().Update(context.TODO(), instance) - if err != nil { - logrus.Errorf("Failed to update %s CR. Fetching the latest CR version: %s", instance.Name, err.Error()) - return err - } - logrus.Infof("Custom resource %s updated", instance.Name) - return nil -} - -// UpdateCheCRSpecByFields - updates Che CR "spec" fields by field map -func (r *ReconcileChe) UpdateCheCRSpecByFields(instance *orgv1.CheCluster, fields map[string]string) (err error) { - updateInfo := []string{} - for updatedField, value := range fields { - updateInfo = append(updateInfo, fmt.Sprintf("%s: %s", updatedField, value)) - } - logrus.Infof(fmt.Sprintf("Updating multiple CR %s fields: ", instance.Name) + strings.Join(updateInfo, ", ")) - - err = r.client.Update(context.TODO(), instance) - if err != nil { - logrus.Errorf("Failed to update %s CR: %s", instance.Name, err.Error()) - return err - } - logrus.Infof("Custom resource %s updated", instance.Name) - - return nil -} - -// UpdateCheCRSpec - updates Che CR "spec" by field -func (r *ReconcileChe) UpdateCheCRSpec(instance *orgv1.CheCluster, updatedField string, value string) (err error) { - logrus.Infof("Updating %s CR with %s: %s", instance.Name, updatedField, value) - err = r.client.Update(context.TODO(), instance) - if err != nil { - logrus.Errorf("Failed to update %s CR: %s", instance.Name, err.Error()) - return err - } - logrus.Infof("Custom resource %s updated", instance.Name) - return nil -} - -func (r *ReconcileChe) ReconcileIdentityProvider(instance *orgv1.CheCluster, isOpenShift4 bool) (deleted bool, err error) { - if !util.IsOAuthEnabled(instance) && instance.Status.OpenShiftoAuthProvisioned == true { - keycloakDeployment := &appsv1.Deployment{} - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: instance.Namespace}, keycloakDeployment); err != nil { - logrus.Errorf("Deployment %s not found: %s", keycloakDeployment.Name, err.Error()) - } - - providerName := "openshift-v3" - if isOpenShift4 { - providerName = "openshift-v4" - } - _, err := util.K8sclient.ExecIntoPod( - instance, - keycloakDeployment.Name, - func(cr *orgv1.CheCluster) (string, error) { - return identity_provider.GetIdentityProviderDeleteCommand(instance, providerName) - }, - "delete OpenShift identity provider") - if err == nil { - oAuthClient := &oauth.OAuthClient{} - oAuthClientName := instance.Spec.Auth.OAuthClientName - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: oAuthClientName, Namespace: ""}, oAuthClient); err != nil { - logrus.Errorf("OAuthClient %s not found: %s", oAuthClient.Name, err.Error()) - } - if err := r.client.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 -} diff --git a/pkg/deploy/checluster.go b/pkg/deploy/checluster.go index d7881c581..116634a37 100644 --- a/pkg/deploy/checluster.go +++ b/pkg/deploy/checluster.go @@ -13,28 +13,74 @@ package deploy import ( "context" + "fmt" + "strings" "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/types" ) -func UpdateCheCRSpec(deployContext *DeployContext, updatedField string, value string) (err error) { - logrus.Infof("Updating %s CR with %s: %s", deployContext.CheCluster.Name, updatedField, value) - err = deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) - if err != nil { - logrus.Errorf("Failed to update %s CR: %s", deployContext.CheCluster.Name, err) - return err +// UpdateCheCRSpec - updates Che CR "spec" by field +func UpdateCheCRSpec(deployContext *DeployContext, field string, value string) error { + err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) + if err == nil { + logrus.Infof("Custom resource spec %s updated with %s: %s", deployContext.CheCluster.Name, field, value) + return nil + } + return err +} + +// UpdateCheCRSpecByFields - updates Che CR "spec" fields by field map +func UpdateCheCRSpecByFields(deployContext *DeployContext, fields map[string]string) (err error) { + updateInfo := []string{} + for updatedField, value := range fields { + updateInfo = append(updateInfo, fmt.Sprintf("%s: %s", updatedField, value)) + } + + err = deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) + if err == nil { + logrus.Infof(fmt.Sprintf("Custom resource spec %s updated with: ", deployContext.CheCluster.Name) + strings.Join(updateInfo, ", ")) + return nil + } + + return err +} + +func UpdateCheCRStatus(deployContext *DeployContext, field string, value string) (err error) { + err = deployContext.ClusterAPI.Client.Status().Update(context.TODO(), deployContext.CheCluster) + if err == nil { + logrus.Infof("Custom resource status %s updated with %s: %s", deployContext.CheCluster.Name, field, value) + return nil + } + + return err +} + +func SetStatusDetails(deployContext *DeployContext, reason string, message string, helpLink string) (err error) { + if reason != deployContext.CheCluster.Status.Reason { + deployContext.CheCluster.Status.Reason = reason + if err := UpdateCheCRStatus(deployContext, "status: Reason", reason); err != nil { + return err + } + } + if message != deployContext.CheCluster.Status.Message { + deployContext.CheCluster.Status.Message = message + if err := UpdateCheCRStatus(deployContext, "status: Message", message); err != nil { + return err + } + } + if helpLink != deployContext.CheCluster.Status.HelpLink { + deployContext.CheCluster.Status.HelpLink = helpLink + if err := UpdateCheCRStatus(deployContext, "status: HelpLink", message); err != nil { + return err + } } - logrus.Infof("Custom resource %s updated", deployContext.CheCluster.Name) return nil } -func UpdateCheCRStatus(deployContext *DeployContext, updatedField string, value string) (err error) { - logrus.Infof("Updating %s CR with %s: %s", deployContext.CheCluster.Name, updatedField, value) - err = deployContext.ClusterAPI.Client.Status().Update(context.TODO(), deployContext.CheCluster) - if err != nil { - logrus.Errorf("Failed to update %s CR. Fetching the latest CR version: %s", deployContext.CheCluster.Name, err) - return err - } - logrus.Infof("Custom resource %s updated", deployContext.CheCluster.Name) - return nil +func ReloadCheClusterCR(deployContext *DeployContext) error { + return deployContext.ClusterAPI.Client.Get( + context.TODO(), + types.NamespacedName{Name: deployContext.CheCluster.Name, Namespace: deployContext.CheCluster.Namespace}, + deployContext.CheCluster) } diff --git a/pkg/deploy/checluster_test.go b/pkg/deploy/checluster_test.go new file mode 100644 index 000000000..40064d956 --- /dev/null +++ b/pkg/deploy/checluster_test.go @@ -0,0 +1,60 @@ +// +// Copyright (c) 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 ( + orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "testing" +) + +func TestReload(t *testing.T) { + cheCluster := &orgv1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + ResourceVersion: "1", + }, + } + + orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) + cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster) + + cheCluster = &orgv1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + ResourceVersion: "2", + }, + } + + deployContext := &DeployContext{ + CheCluster: cheCluster, + ClusterAPI: ClusterAPI{ + Client: cli, + NonCachedClient: cli, + Scheme: scheme.Scheme, + }, + } + + err := ReloadCheClusterCR(deployContext) + if err != nil { + t.Errorf("Failed to reload checluster, %v", err) + } + + if cheCluster.ObjectMeta.ResourceVersion != "1" { + t.Errorf("Failed to reload checluster") + } +} diff --git a/pkg/deploy/finalizer.go b/pkg/deploy/finalizer.go index cad30e7b0..a0a7724db 100644 --- a/pkg/deploy/finalizer.go +++ b/pkg/deploy/finalizer.go @@ -23,8 +23,8 @@ import ( func AppendFinalizer(deployContext *DeployContext, finalizer string) error { if !util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { - deployContext.CheCluster.ObjectMeta.Finalizers = append(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) for { + deployContext.CheCluster.ObjectMeta.Finalizers = append(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) if err == nil { logrus.Infof("Added finalizer: %s", finalizer) @@ -33,7 +33,7 @@ func AppendFinalizer(deployContext *DeployContext, finalizer string) error { return err } - err = util.ReloadCheCluster(deployContext.ClusterAPI.Client, deployContext.CheCluster) + err = ReloadCheClusterCR(deployContext) if err != nil { return err } @@ -45,8 +45,8 @@ func AppendFinalizer(deployContext *DeployContext, finalizer string) error { func DeleteFinalizer(deployContext *DeployContext, finalizer string) error { if util.ContainsString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) { - deployContext.CheCluster.ObjectMeta.Finalizers = util.DoRemoveString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) for { + deployContext.CheCluster.ObjectMeta.Finalizers = util.DoRemoveString(deployContext.CheCluster.ObjectMeta.Finalizers, finalizer) err := deployContext.ClusterAPI.Client.Update(context.TODO(), deployContext.CheCluster) if err == nil { logrus.Infof("Deleted finalizer: %s", finalizer) @@ -55,7 +55,7 @@ func DeleteFinalizer(deployContext *DeployContext, finalizer string) error { return err } - err = util.ReloadCheCluster(deployContext.ClusterAPI.Client, deployContext.CheCluster) + err = ReloadCheClusterCR(deployContext) if err != nil { return err } diff --git a/pkg/deploy/identity-provider/identity_provider.go b/pkg/deploy/identity-provider/identity_provider.go index c29caec6e..2166c39ba 100644 --- a/pkg/deploy/identity-provider/identity_provider.go +++ b/pkg/deploy/identity-provider/identity_provider.go @@ -12,6 +12,7 @@ package identity_provider import ( + "context" "errors" "strings" @@ -21,8 +22,10 @@ import ( "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 ( @@ -116,7 +119,7 @@ func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) { if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil && apierrors.IsConflict(err) { - util.ReloadCheCluster(deployContext.ClusterAPI.Client, deployContext.CheCluster) + deploy.ReloadCheClusterCR(deployContext) continue } break @@ -196,7 +199,7 @@ func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bo if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil && apierrors.IsConflict(err) { - util.ReloadCheCluster(deployContext.ClusterAPI.Client, deployContext.CheCluster) + deploy.ReloadCheClusterCR(deployContext) continue } break @@ -281,3 +284,37 @@ func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) { return true, nil } + +func ReconcileIdentityProvider(deployContext *deploy.DeployContext) (deleted bool, err error) { + if !util.IsOAuthEnabled(deployContext.CheCluster) && 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.NonCachedClient.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.NonCachedClient.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 +} diff --git a/pkg/deploy/postgres/postgres.go b/pkg/deploy/postgres/postgres.go index 60d98d92a..bbf738779 100644 --- a/pkg/deploy/postgres/postgres.go +++ b/pkg/deploy/postgres/postgres.go @@ -77,7 +77,9 @@ func (p *Postgres) SyncPVC() (bool, error) { done, err := deploy.SyncPVCToCluster(p.deployContext, deploy.DefaultPostgresVolumeClaimName, "1Gi", deploy.PostgresName) if !done { - logrus.Infof("Waiting on pvc '%s' to be bound. Sometimes PVC can be bound only when the first consumer is created.", deploy.DefaultPostgresVolumeClaimName) + 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) + } } return done, err } diff --git a/pkg/deploy/server/che_service_test.go b/pkg/deploy/server/che_service_test.go deleted file mode 100644 index 9d8be2d42..000000000 --- a/pkg/deploy/server/che_service_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// -// Copyright (c) 2012-2019 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 ( - "fmt" - "testing" - - "github.com/eclipse-che/che-operator/pkg/deploy" - - orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" - corev1 "k8s.io/api/core/v1" -) - -type DummyServiceCreator struct { -} - -func (s *DummyServiceCreator) CreateService(cr *orgv1.CheCluster, service *corev1.Service, updateIfExists bool) error { - return nil -} - -type DummyFailingServiceCreator struct { -} - -func (s *DummyFailingServiceCreator) CreateService(cr *orgv1.CheCluster, service *corev1.Service, updateIfExists bool) error { - return fmt.Errorf("dummy error") -} - -func TestCreateCheDefaultService(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{}, - }, - } - deployContext := &deploy.DeployContext{ - CheCluster: cheCluster, - ClusterAPI: deploy.ClusterAPI{}, - } - service := GetSpecCheService(deployContext) - ports := service.Spec.Ports - if len(ports) != 1 { - t.Error("expected 1 default port") - } - checkPort(ports[0], "http", 8080, t) -} - -func TestCreateCheServerDebug(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Server: orgv1.CheClusterSpecServer{ - CheDebug: "true", - }, - }, - } - deployContext := &deploy.DeployContext{ - CheCluster: cheCluster, - ClusterAPI: deploy.ClusterAPI{}, - } - - service := GetSpecCheService(deployContext) - ports := service.Spec.Ports - if len(ports) != 2 { - t.Error("expected 2 default port") - } - checkPort(ports[0], "http", 8080, t) - checkPort(ports[1], "debug", 8000, t) -} - -func TestCreateCheServiceEnableMetrics(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Metrics: orgv1.CheClusterSpecMetrics{ - Enable: false, - }, - }, - } - - deployContext := &deploy.DeployContext{ - CheCluster: cheCluster, - ClusterAPI: deploy.ClusterAPI{}, - } - - service := GetSpecCheService(deployContext) - ports := service.Spec.Ports - if len(ports) != 1 { - t.Error("expected 1 default port") - } - checkPort(ports[0], "http", 8080, t) -} - -func TestCreateCheServiceDisableMetrics(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - Spec: orgv1.CheClusterSpec{ - Metrics: orgv1.CheClusterSpecMetrics{ - Enable: true, - }, - }, - } - - deployContext := &deploy.DeployContext{ - CheCluster: cheCluster, - ClusterAPI: deploy.ClusterAPI{}, - } - - service := GetSpecCheService(deployContext) - ports := service.Spec.Ports - if len(ports) != 2 { - t.Error("expected 2 ports") - } - checkPort(ports[0], "http", 8080, t) - checkPort(ports[1], "metrics", deploy.DefaultCheMetricsPort, t) -} - -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/server/server.go b/pkg/deploy/server/server.go new file mode 100644 index 000000000..bdfa821de --- /dev/null +++ b/pkg/deploy/server/server.go @@ -0,0 +1,367 @@ +// +// Copyright (c) 2012-2019 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/pkg/apis/org/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" + "k8s.io/api/extensions/v1beta1" +) + +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 + } + + done, err = s.UpdateCheURL() + 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.SyncPVC() + 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.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 := &v1beta1.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 string + if s.deployContext.CheCluster.Spec.Server.TlsSupport { + cheUrl = "https://" + s.deployContext.CheCluster.Spec.Server.CheHost + } else { + cheUrl = "http://" + s.deployContext.CheCluster.Spec.Server.CheHost + } + + 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) SyncPVC() (bool, error) { + cheMultiUser := deploy.GetCheMultiUser(s.deployContext.CheCluster) + if cheMultiUser == "false" { + claimSize := util.GetValue(s.deployContext.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize) + done, err := deploy.SyncPVCToCluster(s.deployContext, deploy.DefaultCheVolumeClaimName, claimSize, s.component) + 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.DefaultCheVolumeClaimName) + } + } + return done, err + } else { + return deploy.DeleteNamespacedObject(s.deployContext, deploy.DefaultCheVolumeClaimName, &corev1.PersistentVolumeClaim{}) + } +} + +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", s.deployContext.CheCluster.Status.CheURL) + 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 := s.evaluateCheServerVersion() + 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 +} + +// EvaluateCheServerVersion evaluate che version +// based on Checluster information and image defaults from env variables +func (s Server) evaluateCheServerVersion() string { + return util.GetValue(s.deployContext.CheCluster.Spec.Server.CheImageTag, deploy.DefaultCheVersion()) +} + +func GetServerExposingServiceName(cr *orgv1.CheCluster) string { + if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == "gateway" { + return gateway.GatewayServiceName + } + return deploy.CheServiceName +} diff --git a/pkg/deploy/server/che_configmap.go b/pkg/deploy/server/server_configmap.go similarity index 69% rename from pkg/deploy/server/che_configmap.go rename to pkg/deploy/server/server_configmap.go index 1a4f4f4ae..90c8a7e20 100644 --- a/pkg/deploy/server/che_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -87,23 +87,15 @@ type CheConfigMap struct { CheDevWorkspacesEnabled string `json:"CHE_DEVWORKSPACES_ENABLED"` } -func SyncCheConfigMapToCluster(deployContext *deploy.DeployContext) (bool, error) { - data, err := GetCheConfigMapData(deployContext) - if err != nil { - return false, err - } - return deploy.SyncConfigMapDataToCluster(deployContext, CheConfigMapName, data, deploy.DefaultCheFlavor(deployContext.CheCluster)) -} - // 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 GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string]string, err error) { - cheHost := deployContext.CheCluster.Spec.Server.CheHost - keycloakURL := deployContext.CheCluster.Spec.Auth.IdentityProviderURL +func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) { + cheHost := s.deployContext.CheCluster.Spec.Server.CheHost + keycloakURL := s.deployContext.CheCluster.Spec.Auth.IdentityProviderURL // Adds `/auth` for external identity providers. // If identity provide is deployed by operator then `/auth` is already added. - if deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider && !strings.HasSuffix(keycloakURL, "/auth") { + if s.deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider && !strings.HasSuffix(keycloakURL, "/auth") { if strings.HasSuffix(keycloakURL, "/") { keycloakURL = keycloakURL + "auth" } else { @@ -114,20 +106,20 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string if err != nil { logrus.Errorf("Failed to get current infra: %s", err) } - cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster) + cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster) infra := "kubernetes" if util.IsOpenShift { infra = "openshift" } tls := "false" openShiftIdentityProviderId := "NULL" - if util.IsOpenShift && util.IsOAuthEnabled(deployContext.CheCluster) { + if util.IsOpenShift && util.IsOAuthEnabled(s.deployContext.CheCluster) { openShiftIdentityProviderId = "openshift-v3" if util.IsOpenShift4 { openShiftIdentityProviderId = "openshift-v4" } } - tlsSupport := deployContext.CheCluster.Spec.Server.TlsSupport + tlsSupport := s.deployContext.CheCluster.Spec.Server.TlsSupport protocol := "http" if tlsSupport { protocol = "https" @@ -135,76 +127,76 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string } proxyJavaOpts := "" - cheWorkspaceNoProxy := deployContext.Proxy.NoProxy - if deployContext.Proxy.HttpProxy != "" { - if deployContext.Proxy.NoProxy == "" { + cheWorkspaceNoProxy := s.deployContext.Proxy.NoProxy + if s.deployContext.Proxy.HttpProxy != "" { + if s.deployContext.Proxy.NoProxy == "" { cheWorkspaceNoProxy = os.Getenv("KUBERNETES_SERVICE_HOST") } else { cheWorkspaceNoProxy = cheWorkspaceNoProxy + "," + os.Getenv("KUBERNETES_SERVICE_HOST") } - proxyJavaOpts, err = deploy.GenerateProxyJavaOpts(deployContext.Proxy, cheWorkspaceNoProxy) + proxyJavaOpts, err = deploy.GenerateProxyJavaOpts(s.deployContext.Proxy, cheWorkspaceNoProxy) if err != nil { logrus.Errorf("Failed to generate java proxy options: %v", err) } } - ingressDomain := deployContext.CheCluster.Spec.K8s.IngressDomain - tlsSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName - securityContextFsGroup := util.GetValue(deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup) - securityContextRunAsUser := util.GetValue(deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser) - pvcStrategy := util.GetValue(deployContext.CheCluster.Spec.Storage.PvcStrategy, deploy.DefaultPvcStrategy) - pvcClaimSize := util.GetValue(deployContext.CheCluster.Spec.Storage.PvcClaimSize, deploy.DefaultPvcClaimSize) - workspacePvcStorageClassName := deployContext.CheCluster.Spec.Storage.WorkspacePVCStorageClassName + 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 - defaultPVCJobsImage := deploy.DefaultPvcJobsImage(deployContext.CheCluster) - pvcJobsImage := util.GetValue(deployContext.CheCluster.Spec.Storage.PvcJobsImage, defaultPVCJobsImage) + defaultPVCJobsImage := deploy.DefaultPvcJobsImage(s.deployContext.CheCluster) + pvcJobsImage := util.GetValue(s.deployContext.CheCluster.Spec.Storage.PvcJobsImage, defaultPVCJobsImage) preCreateSubPaths := "true" - if !deployContext.CheCluster.Spec.Storage.PreCreateSubPaths { + if !s.deployContext.CheCluster.Spec.Storage.PreCreateSubPaths { preCreateSubPaths = "false" } - chePostgresHostName := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName) - chePostgresPort := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort) - chePostgresDb := util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb) - keycloakRealm := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor) - keycloakClientId := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public") - ingressStrategy := util.GetServerExposureStrategy(deployContext.CheCluster) - ingressClass := util.GetValue(deployContext.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass) - devfileRegistryURL := deployContext.CheCluster.Status.DevfileRegistryURL - pluginRegistryURL := deployContext.CheCluster.Status.PluginRegistryURL - cheLogLevel := util.GetValue(deployContext.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel) - cheDebug := util.GetValue(deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) - cheMetrics := strconv.FormatBool(deployContext.CheCluster.Spec.Metrics.Enable) - cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(deployContext.CheCluster, deploy.DefaultCheFlavor(deployContext.CheCluster))) - cheMultiUser := deploy.GetCheMultiUser(deployContext.CheCluster) - workspaceExposure := deploy.GetSingleHostExposureType(deployContext.CheCluster) - singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(deployContext.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)) - workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(deployContext.CheCluster) + 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) + devfileRegistryURL := s.deployContext.CheCluster.Status.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))) + cheMultiUser := deploy.GetCheMultiUser(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) cheAPI := protocol + "://" + cheHost + "/api" var keycloakInternalURL, pluginRegistryInternalURL, devfileRegistryInternalURL, cheInternalAPI, webSocketEndpoint, webSocketEndpointMinor string - if deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider { - keycloakInternalURL = fmt.Sprintf("%s://%s.%s.svc:8080/auth", "http", deploy.IdentityProviderName, deployContext.CheCluster.Namespace) + if s.deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !s.deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider { + keycloakInternalURL = fmt.Sprintf("%s://%s.%s.svc:8080/auth", "http", deploy.IdentityProviderName, s.deployContext.CheCluster.Namespace) } else { keycloakInternalURL = keycloakURL } - if deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !deployContext.CheCluster.Spec.Server.ExternalDevfileRegistry { - devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, deployContext.CheCluster.Namespace) + if s.deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !s.deployContext.CheCluster.Spec.Server.ExternalDevfileRegistry { + devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, s.deployContext.CheCluster.Namespace) } else { devfileRegistryInternalURL = devfileRegistryURL } - if deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !deployContext.CheCluster.Spec.Server.ExternalPluginRegistry { - pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, deployContext.CheCluster.Namespace) + if s.deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames && !s.deployContext.CheCluster.Spec.Server.ExternalPluginRegistry { + pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, s.deployContext.CheCluster.Namespace) } else { pluginRegistryInternalURL = pluginRegistryURL } - if deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames { - cheInternalAPI = fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, deployContext.CheCluster.Namespace) - webSocketEndpoint = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, deployContext.CheCluster.Namespace) - webSocketEndpointMinor = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket-minor", deploy.CheServiceName, deployContext.CheCluster.Namespace) + if s.deployContext.CheCluster.Spec.Server.UseInternalClusterSVCNames { + cheInternalAPI = fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, s.deployContext.CheCluster.Namespace) + webSocketEndpoint = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, s.deployContext.CheCluster.Namespace) + webSocketEndpointMinor = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket-minor", deploy.CheServiceName, s.deployContext.CheCluster.Namespace) } else { cheInternalAPI = cheAPI @@ -243,23 +235,23 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string WorkspaceJavaOpts: deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, WorkspaceMavenOpts: deploy.DefaultWorkspaceJavaOpts + " " + proxyJavaOpts, WorkspaceProxyJavaOpts: proxyJavaOpts, - WorkspaceHttpProxy: deployContext.Proxy.HttpProxy, - WorkspaceHttpsProxy: deployContext.Proxy.HttpsProxy, + WorkspaceHttpProxy: s.deployContext.Proxy.HttpProxy, + WorkspaceHttpsProxy: s.deployContext.Proxy.HttpsProxy, WorkspaceNoProxy: cheWorkspaceNoProxy, PluginRegistryUrl: pluginRegistryURL, PluginRegistryInternalUrl: pluginRegistryInternalURL, DevfileRegistryUrl: devfileRegistryURL, DevfileRegistryInternalUrl: devfileRegistryInternalURL, - CheWorkspacePluginBrokerMetadataImage: deploy.DefaultCheWorkspacePluginBrokerMetadataImage(deployContext.CheCluster), - CheWorkspacePluginBrokerArtifactsImage: deploy.DefaultCheWorkspacePluginBrokerArtifactsImage(deployContext.CheCluster), - CheServerSecureExposerJwtProxyImage: deploy.DefaultCheServerSecureExposerJwtProxyImage(deployContext.CheCluster), + CheWorkspacePluginBrokerMetadataImage: deploy.DefaultCheWorkspacePluginBrokerMetadataImage(s.deployContext.CheCluster), + CheWorkspacePluginBrokerArtifactsImage: deploy.DefaultCheWorkspacePluginBrokerArtifactsImage(s.deployContext.CheCluster), + CheServerSecureExposerJwtProxyImage: deploy.DefaultCheServerSecureExposerJwtProxyImage(s.deployContext.CheCluster), CheJGroupsKubernetesLabels: cheLabels, CheMetricsEnabled: cheMetrics, CheTrustedCABundlesConfigMap: deploy.CheAllCACertsConfigMapName, ServerStrategy: ingressStrategy, WorkspaceExposure: workspaceExposure, SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels, - CheDevWorkspacesEnabled: strconv.FormatBool(deployContext.CheCluster.Spec.DevWorkspace.Enable), + CheDevWorkspacesEnabled: strconv.FormatBool(s.deployContext.CheCluster.Spec.DevWorkspace.Enable), } if cheMultiUser == "true" { @@ -268,9 +260,9 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string data.KeycloakRealm = keycloakRealm data.KeycloakClientId = keycloakClientId data.DatabaseURL = "jdbc:postgresql://" + chePostgresHostName + ":" + chePostgresPort + "/" + chePostgresDb - if len(deployContext.CheCluster.Spec.Database.ChePostgresSecret) < 1 { - data.DbUserName = deployContext.CheCluster.Spec.Database.ChePostgresUser - data.DbPassword = deployContext.CheCluster.Spec.Database.ChePostgresPassword + 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 } } @@ -294,20 +286,20 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string // 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 deployContext.CheCluster.Spec.K8s.TlsSecretName != "" { + if s.deployContext.CheCluster.Spec.K8s.TlsSecretName != "" { cheTLSSecret := &corev1.Secret{} - exists, err := deploy.GetNamespacedObject(deployContext, deployContext.CheCluster.Spec.K8s.TlsSecretName, cheTLSSecret) + exists, err := deploy.GetNamespacedObject(s.deployContext, s.deployContext.CheCluster.Spec.K8s.TlsSecretName, cheTLSSecret) if err != nil { return nil, err } if !exists { - return nil, fmt.Errorf("%s secret not found", deployContext.CheCluster.Spec.K8s.TlsSecretName) + return nil, fmt.Errorf("%s secret not found", s.deployContext.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", deployContext.CheCluster.Spec.K8s.TlsSecretName) + return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", s.deployContext.CheCluster.Spec.K8s.TlsSecretName) } if _, exists := cheTLSSecret.Data["tls.crt"]; !exists { - return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", deployContext.CheCluster.Spec.K8s.TlsSecretName) + return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", s.deployContext.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"]) @@ -317,10 +309,10 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string addMap(cheEnv, k8sCheEnv) } - addMap(cheEnv, deployContext.CheCluster.Spec.Server.CustomCheProperties) + addMap(cheEnv, s.deployContext.CheCluster.Spec.Server.CustomCheProperties) // Update BitBucket endpoints - secrets, err := deploy.GetSecrets(deployContext, map[string]string{ + secrets, err := deploy.GetSecrets(s.deployContext, map[string]string{ deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration, }, map[string]string{ diff --git a/pkg/deploy/server/che_configmap_test.go b/pkg/deploy/server/server_configmap_test.go similarity index 97% rename from pkg/deploy/server/che_configmap_test.go rename to pkg/deploy/server/server_configmap_test.go index f7aba3cba..3d1330e53 100644 --- a/pkg/deploy/server/che_configmap_test.go +++ b/pkg/deploy/server/server_configmap_test.go @@ -90,7 +90,8 @@ func TestNewCheConfigMap(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -226,7 +227,8 @@ func TestConfigMap(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -353,7 +355,8 @@ func TestUpdateBitBucketEndpoints(t *testing.T) { Proxy: &deploy.Proxy{}, } - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -525,7 +528,8 @@ func TestShouldSetUpCorrectlyInternalDevfileRegistryServiceURL(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -677,7 +681,8 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -769,7 +774,8 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } @@ -943,7 +949,8 @@ func TestShouldSetUpCorrectlyInternalIdentityProviderServiceURL(t *testing.T) { util.IsOpenShift = testCase.isOpenShift util.IsOpenShift4 = testCase.isOpenShift4 - actualData, err := GetCheConfigMapData(deployContext) + server := NewServer(deployContext) + actualData, err := server.getCheConfigMapData() if err != nil { t.Fatalf("Error creating ConfigMap data: %v", err) } diff --git a/pkg/deploy/server/deployment_che.go b/pkg/deploy/server/server_deployment.go similarity index 84% rename from pkg/deploy/server/deployment_che.go rename to pkg/deploy/server/server_deployment.go index 80fd8ee9b..cfef69fd4 100644 --- a/pkg/deploy/server/deployment_che.go +++ b/pkg/deploy/server/server_deployment.go @@ -26,31 +26,18 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func SyncCheDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) { - specDeployment, err := GetSpecCheDeployment(deployContext) - if err != nil { - return false, err - } - return deploy.SyncDeploymentSpecToCluster(deployContext, specDeployment, deploy.DefaultDeploymentDiffOpts) -} - -func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployment, error) { - isOpenShift, _, err := util.DetectOpenShift() +func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) { + selfSignedCertUsed, err := deploy.IsSelfSignedCertificateUsed(s.deployContext) if err != nil { return nil, err } - selfSignedCertUsed, err := deploy.IsSelfSignedCertificateUsed(deployContext) - if err != nil { - return nil, err - } - - cmResourceVersions := GetCheConfigMapVersion(deployContext) - cmResourceVersions += "," + deploy.GetAdditionalCACertsConfigMapVersion(deployContext) + cmResourceVersions := GetCheConfigMapVersion(s.deployContext) + cmResourceVersions += "," + deploy.GetAdditionalCACertsConfigMapVersion(s.deployContext) terminationGracePeriodSeconds := int64(30) - cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster) - labels, labelSelector := deploy.GetLabelsAndSelector(deployContext.CheCluster, cheFlavor) + cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster) + labels, labelSelector := deploy.GetLabelsAndSelector(s.deployContext.CheCluster, cheFlavor) optionalEnv := true selfSignedCertEnv := corev1.EnvVar{ Name: "CHE_SELF__SIGNED__CERT", @@ -94,7 +81,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme }, } } - if deployContext.CheCluster.Spec.Server.GitSelfSignedCert { + if s.deployContext.CheCluster.Spec.Server.GitSelfSignedCert { gitSelfSignedCertEnv = corev1.EnvVar{ Name: "CHE_GIT_SELF__SIGNED__CERT", ValueFrom: &corev1.EnvVarSource{ @@ -126,7 +113,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme cheEnv = append(cheEnv, gitSelfSignedCertEnv) cheEnv = append(cheEnv, gitSelfSignedCertHostEnv) - identityProviderSecret := deployContext.CheCluster.Spec.Auth.IdentityProviderSecret + identityProviderSecret := s.deployContext.CheCluster.Spec.Auth.IdentityProviderSecret if len(identityProviderSecret) > 0 { cheEnv = append(cheEnv, corev1.EnvVar{ Name: "CHE_KEYCLOAK_ADMIN__PASSWORD", @@ -153,11 +140,11 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme } else { cheEnv = append(cheEnv, corev1.EnvVar{ Name: "CHE_KEYCLOAK_ADMIN__PASSWORD", - Value: deployContext.CheCluster.Spec.Auth.IdentityProviderPassword, + Value: s.deployContext.CheCluster.Spec.Auth.IdentityProviderPassword, }, corev1.EnvVar{ Name: "CHE_KEYCLOAK_ADMIN__USERNAME", - Value: deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName, + Value: s.deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName, }) } @@ -174,8 +161,8 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme FieldPath: "metadata.namespace"}}, }) - cheImageAndTag := GetFullCheServerImageLink(deployContext.CheCluster) - pullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag))) + cheImageAndTag := GetFullCheServerImageLink(s.deployContext.CheCluster) + pullPolicy := corev1.PullPolicy(util.GetValue(string(s.deployContext.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag))) deployment := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ @@ -184,7 +171,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme }, ObjectMeta: metav1.ObjectMeta{ Name: cheFlavor, - Namespace: deployContext.CheCluster.Namespace, + Namespace: s.deployContext.CheCluster.Namespace, Labels: labels, }, Spec: appsv1.DeploymentSpec{ @@ -227,18 +214,18 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceMemory: util.GetResourceQuantity( - deployContext.CheCluster.Spec.Server.ServerMemoryRequest, + s.deployContext.CheCluster.Spec.Server.ServerMemoryRequest, deploy.DefaultServerMemoryRequest), corev1.ResourceCPU: util.GetResourceQuantity( - deployContext.CheCluster.Spec.Server.ServerCpuRequest, + s.deployContext.CheCluster.Spec.Server.ServerCpuRequest, deploy.DefaultServerCpuRequest), }, Limits: corev1.ResourceList{ corev1.ResourceMemory: util.GetResourceQuantity( - deployContext.CheCluster.Spec.Server.ServerMemoryLimit, + s.deployContext.CheCluster.Spec.Server.ServerMemoryLimit, deploy.DefaultServerMemoryLimit), corev1.ResourceCPU: util.GetResourceQuantity( - deployContext.CheCluster.Spec.Server.ServerCpuLimit, + s.deployContext.CheCluster.Spec.Server.ServerCpuLimit, deploy.DefaultServerCpuLimit), }, }, @@ -267,15 +254,15 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme }, } - err = MountBitBucketOAuthConfig(deployContext, deployment) + err = MountBitBucketOAuthConfig(s.deployContext, deployment) if err != nil { return nil, err } container := &deployment.Spec.Template.Spec.Containers[0] - cheMultiUser := deploy.GetCheMultiUser(deployContext.CheCluster) + cheMultiUser := deploy.GetCheMultiUser(s.deployContext.CheCluster) if cheMultiUser == "true" { - chePostgresSecret := deployContext.CheCluster.Spec.Database.ChePostgresSecret + chePostgresSecret := s.deployContext.CheCluster.Spec.Database.ChePostgresSecret if len(chePostgresSecret) > 0 { container.Env = append(container.Env, corev1.EnvVar{ @@ -319,7 +306,7 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme } // configure probes if debug isn't set - cheDebug := util.GetValue(deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) + cheDebug := util.GetValue(s.deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) if cheDebug != "true" { container.ReadinessProbe = &corev1.Probe{ Handler: corev1.Handler{ @@ -360,12 +347,12 @@ func GetSpecCheDeployment(deployContext *deploy.DeployContext) (*appsv1.Deployme } } - if !isOpenShift { - runAsUser, err := strconv.ParseInt(util.GetValue(deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64) + if !util.IsOpenShift { + runAsUser, err := strconv.ParseInt(util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextRunAsUser, deploy.DefaultSecurityContextRunAsUser), 10, 64) if err != nil { return nil, err } - fsGroup, err := strconv.ParseInt(util.GetValue(deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64) + fsGroup, err := strconv.ParseInt(util.GetValue(s.deployContext.CheCluster.Spec.K8s.SecurityContextFsGroup, deploy.DefaultSecurityContextFsGroup), 10, 64) if err != nil { return nil, err } diff --git a/pkg/deploy/server/deployment_che_test.go b/pkg/deploy/server/server_deployment_test.go similarity index 97% rename from pkg/deploy/server/deployment_che_test.go rename to pkg/deploy/server/server_deployment_test.go index c23d8fffe..6fbc736f2 100644 --- a/pkg/deploy/server/deployment_che_test.go +++ b/pkg/deploy/server/server_deployment_test.go @@ -95,7 +95,8 @@ func TestDeployment(t *testing.T) { }, } - deployment, err := GetSpecCheDeployment(deployContext) + server := NewServer(deployContext) + deployment, err := server.getDeploymentSpec() if err != nil { t.Fatalf("Error creating deployment: %v", err) } @@ -201,7 +202,8 @@ func TestMountBitBucketOAuthEnvVar(t *testing.T) { Proxy: &deploy.Proxy{}, } - deployment, err := GetSpecCheDeployment(deployContext) + server := NewServer(deployContext) + deployment, err := server.getDeploymentSpec() if err != nil { t.Fatalf("Error creating deployment: %v", err) } diff --git a/pkg/deploy/server/server_test.go b/pkg/deploy/server/server_test.go new file mode 100644 index 000000000..29a8e0cda --- /dev/null +++ b/pkg/deploy/server/server_test.go @@ -0,0 +1,324 @@ +// +// Copyright (c) 2012-2019 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" + 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/pkg/apis/org/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: "eclipse-che", + }, + Spec: orgv1.CheClusterSpec{ + Server: orgv1.CheClusterSpecServer{ + CheDebug: "true", + }, + Metrics: orgv1.CheClusterSpecMetrics{ + Enable: true, + }, + }, + }, + ClusterAPI: deploy.ClusterAPI{ + Client: cli, + NonCachedClient: 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: "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, + NonCachedClient: 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: server.component, 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, + NonCachedClient: 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 TestSyncPVC(t *testing.T) { + cheCluster := &orgv1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + Spec: orgv1.CheClusterSpec{ + Server: orgv1.CheClusterSpecServer{ + CustomCheProperties: map[string]string{ + "CHE_MULTIUSER": "false", + }, + }, + }, + } + + 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, + NonCachedClient: cli, + Scheme: scheme.Scheme, + }, + } + + server := NewServer(deployContext) + done, err := server.SyncPVC() + if !done || err != nil { + t.Fatalf("Failed to sync PVC: %v", err) + } + + err = cli.Get(context.TODO(), types.NamespacedName{Namespace: "eclipse-che", Name: "custom"}, &corev1.PersistentVolumeClaim{}) + if err == nil { + t.Fatalf("PVC not found") + } +} + +func TestUpdateAvailabilityStatus(t *testing.T) { + cheDeployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "che", + Namespace: "eclipse-che", + }, + Status: appsv1.DeploymentStatus{ + AvailableReplicas: 1, + Replicas: 1, + }, + } + cheCluster := &orgv1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "eclipse-che", + Name: "eclipse-che", + }, + 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, + NonCachedClient: 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/server/service.go b/pkg/deploy/server/service.go deleted file mode 100644 index eac039001..000000000 --- a/pkg/deploy/server/service.go +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright (c) 2020-2020 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" - v1 "k8s.io/api/core/v1" -) - -func GetSpecCheService(deployContext *deploy.DeployContext) *v1.Service { - portName := []string{"http"} - portNumber := []int32{8080} - - if deployContext.CheCluster.Spec.Metrics.Enable { - portName = append(portName, "metrics") - portNumber = append(portNumber, deploy.DefaultCheMetricsPort) - } - - if deployContext.CheCluster.Spec.Server.CheDebug == "true" { - portName = append(portName, "debug") - portNumber = append(portNumber, deploy.DefaultCheDebugPort) - } - - return deploy.GetServiceSpec(deployContext, deploy.CheServiceName, portName, portNumber, deploy.DefaultCheFlavor(deployContext.CheCluster)) -} - -func SyncCheServiceToCluster(deployContext *deploy.DeployContext) (bool, error) { - specService := GetSpecCheService(deployContext) - return deploy.SyncServiceSpecToCluster(deployContext, specService) -} diff --git a/pkg/deploy/service.go b/pkg/deploy/service.go index d1c2ee02d..a4bf46b57 100644 --- a/pkg/deploy/service.go +++ b/pkg/deploy/service.go @@ -25,7 +25,7 @@ const ( CheServiceName = "che-host" ) -var serviceDiffOpts = cmp.Options{ +var ServiceDefaultDiffOpts = cmp.Options{ cmpopts.IgnoreFields(corev1.Service{}, "TypeMeta", "ObjectMeta"), cmp.Comparer(func(x, y corev1.ServiceSpec) bool { return cmp.Equal(x.Ports, y.Ports, cmpopts.IgnoreFields(corev1.ServicePort{}, "TargetPort", "NodePort")) && @@ -45,7 +45,7 @@ func SyncServiceToCluster( } func SyncServiceSpecToCluster(deployContext *DeployContext, serviceSpec *corev1.Service) (bool, error) { - return Sync(deployContext, serviceSpec, serviceDiffOpts) + return Sync(deployContext, serviceSpec, ServiceDefaultDiffOpts) } func GetServiceSpec( diff --git a/pkg/util/util.go b/pkg/util/util.go index e0f8b8fb2..1a1f41a10 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -13,7 +13,6 @@ package util import ( "bytes" - "context" "crypto/sha256" "crypto/tls" "encoding/base64" @@ -38,9 +37,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" "sigs.k8s.io/yaml" ) @@ -445,10 +442,3 @@ func ComputeHash256(yamlFile string) (string, error) { sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) return sha, nil } - -func ReloadCheCluster(client client.Client, cheCluster *orgv1.CheCluster) error { - return client.Get( - context.TODO(), - types.NamespacedName{Name: cheCluster.Name, Namespace: cheCluster.Namespace}, - cheCluster) -} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index ca9eac54f..989b230ea 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -14,12 +14,6 @@ package util import ( "reflect" "testing" - - "k8s.io/client-go/kubernetes/scheme" - - orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client/fake" ) func TestGeneratePasswd(t *testing.T) { @@ -51,33 +45,3 @@ func TestGetValue(t *testing.T) { t.Errorf("Test failed. Expected '%s', but got '%s'", var2, defaultValue) } } - -func TestReload(t *testing.T) { - cheCluster := &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - ResourceVersion: "1", - }, - } - - orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) - cli := fake.NewFakeClientWithScheme(scheme.Scheme, cheCluster) - - cheCluster = &orgv1.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - ResourceVersion: "2", - }, - } - - err := ReloadCheCluster(cli, cheCluster) - if err != nil { - t.Errorf("Failed to reload checluster, %v", err) - } - - if cheCluster.ObjectMeta.ResourceVersion != "1" { - t.Errorf("Failed to reload checluster") - } -}