368 lines
10 KiB
Go
368 lines
10 KiB
Go
//
|
|
// 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
|
|
}
|