From 78b6367688dd40f722dc3799a673b7254a43b0f8 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Wed, 1 Sep 2021 09:53:57 +0200 Subject: [PATCH 1/6] feat: devworkspace services auth Signed-off-by: Michal Vala --- api/conversion.go | 3 +- controllers/devworkspace/defaults/defaults.go | 4 +- .../devworkspace/solver/che_routing.go | 544 +++++++++++------- .../devworkspace/solver/che_routing_test.go | 191 ++++-- .../devworkspace/solver/endpoint_exposer.go | 2 +- controllers/devworkspace/solver/solver.go | 2 +- pkg/deploy/defaults.go | 2 +- pkg/deploy/gateway/gateway.go | 27 +- 8 files changed, 508 insertions(+), 267 deletions(-) diff --git a/api/conversion.go b/api/conversion.go index d1eb32e3a2..33fbe4e6ed 100644 --- a/api/conversion.go +++ b/api/conversion.go @@ -3,6 +3,7 @@ package org import ( v1 "github.com/eclipse-che/che-operator/api/v1" "github.com/eclipse-che/che-operator/api/v2alpha1" + "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/util" "sigs.k8s.io/yaml" ) @@ -145,7 +146,7 @@ func v1ToV2alpha1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { } func v1ToV2alpha1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { - v2.Spec.Gateway.Image = v1.Spec.Server.SingleHostGatewayImage + v2.Spec.Gateway.Image = util.GetValue(v1.Spec.Server.SingleHostGatewayImage, deploy.DefaultSingleHostGatewayImage(v1)) } func v1ToV2alpha1_GatewayConfigurerImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { diff --git a/controllers/devworkspace/defaults/defaults.go b/controllers/devworkspace/defaults/defaults.go index 9b778ad16c..aa8a4c9cc0 100644 --- a/controllers/devworkspace/defaults/defaults.go +++ b/controllers/devworkspace/defaults/defaults.go @@ -43,8 +43,8 @@ var ( } ) -func GetGatewayWorkpaceConfigMapName(workspaceID string) string { - return workspaceID +func GetGatewayWorkspaceConfigMapName(workspaceID string) string { + return workspaceID + "-route" } func GetLabelsForComponent(cluster *v2alpha1.CheCluster, component string) map[string]string { diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index e445e8210f..cd7a0a83a6 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -18,6 +18,9 @@ import ( "path" "strings" + "github.com/eclipse-che/che-operator/pkg/deploy/gateway" + "k8s.io/apimachinery/pkg/util/intstr" + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting/solvers" @@ -43,35 +46,63 @@ const ( endpointURLPrefixPattern = "/%s/%s/%d" // note - che-theia DEPENDS on this format - we should not change this unless crosschecked with the che-theia impl uniqueEndpointURLPrefixPattern = "/%s/%s/%s" + wsGatewayPort = 3030 + wsGatewayName = "che-gateway" ) var ( configMapDiffOpts = cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta", "ObjectMeta") ) -// keys are port numbers, values are maps where keys are endpoint names (in case we need more than 1 endpoint for a single port) and values -// contain info about the intended endpoint scheme and the order in which the port is defined (used for unique naming) -type portMapping map[int32]map[string]portMappingValue -type portMappingValue struct { - endpointScheme string - order int -} - -func (c *CheRoutingSolver) cheSpecObjects(cheManager *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) (solvers.RoutingObjects, error) { +func (c *CheRoutingSolver) cheSpecObjects(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) (solvers.RoutingObjects, error) { objs := solvers.RoutingObjects{} + if err := c.provisionServices(&objs, cheCluster, routing, workspaceMeta); err != nil { + return solvers.RoutingObjects{}, err + } + + if err := c.provisionRouting(&objs, cheCluster, routing, workspaceMeta); err != nil { + return solvers.RoutingObjects{}, err + } + + if err := c.provisionPodAdditions(&objs, cheCluster, routing); err != nil { + return solvers.RoutingObjects{}, err + } + + return objs, nil +} + +func (c *CheRoutingSolver) provisionServices(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { objs.Services = solvers.GetDiscoverableServicesForEndpoints(routing.Spec.Endpoints, workspaceMeta) - commonService := solvers.GetServiceForEndpoints(routing.Spec.Endpoints, workspaceMeta, false, dw.PublicEndpointExposure, dw.InternalEndpointExposure) - if commonService != nil { - objs.Services = append(objs.Services, *commonService) + commonService := &corev1.Service{ + ObjectMeta: v1.ObjectMeta{ + Name: common.ServiceName(routing.Spec.DevWorkspaceId), + Namespace: routing.Namespace, + Labels: map[string]string{ + constants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, + }, + }, + Spec: corev1.ServiceSpec{ + Selector: routing.Spec.PodSelector, + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Name: common.EndpointName("ws-route"), + Protocol: corev1.ProtocolTCP, + Port: int32(wsGatewayPort), + TargetPort: intstr.FromInt(wsGatewayPort), + }, + }, + }, } + objs.Services = append(objs.Services, *commonService) annos := map[string]string{} - annos[defaults.ConfigAnnotationCheManagerName] = cheManager.Name - annos[defaults.ConfigAnnotationCheManagerNamespace] = cheManager.Namespace + annos[defaults.ConfigAnnotationCheManagerName] = cheCluster.Name + annos[defaults.ConfigAnnotationCheManagerNamespace] = cheCluster.Namespace - additionalLabels := defaults.GetLabelsForComponent(cheManager, "exposure") + additionalLabels := defaults.GetLabelsForComponent(cheCluster, "exposure") for i := range objs.Services { // need to use a ref otherwise s would be a copy @@ -100,42 +131,77 @@ func (c *CheRoutingSolver) cheSpecObjects(cheManager *v2alpha1.CheCluster, routi } } + return nil +} + +func (c *CheRoutingSolver) provisionRouting(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, workspaceMeta solvers.DevWorkspaceMetadata) error { // k, now we have to create our own objects for configuring the gateway - configMaps, err := c.getGatewayConfigsAndFillRoutingObjects(cheManager, workspaceMeta.DevWorkspaceId, routing, &objs) + configMaps, err := c.getGatewayConfigsAndFillRoutingObjects(cheCluster, workspaceMeta.DevWorkspaceId, routing, objs) if err != nil { - return solvers.RoutingObjects{}, err + return err } + // solvers.RoutingObjects does not currently support ConfigMaps, so we have to actually create it in cluster syncer := sync.New(c.client, c.scheme) - for _, cm := range configMaps { _, _, err := syncer.Sync(context.TODO(), nil, &cm, configMapDiffOpts) if err != nil { - return solvers.RoutingObjects{}, err + return err } + } + + return nil +} +func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) error { + objs.PodAdditions = &dwo.PodAdditions{ + Containers: []corev1.Container{}, + Volumes: []corev1.Volume{}, } + objs.PodAdditions.Containers = append(objs.PodAdditions.Containers, corev1.Container{ + Name: wsGatewayName, + Image: cheCluster.Spec.Gateway.Image, + ImagePullPolicy: corev1.PullAlways, + VolumeMounts: []corev1.VolumeMount{ + { + Name: wsGatewayName, + MountPath: "/etc/traefik", + }, + }, + }) + + defaultMode := int32(420) + objs.PodAdditions.Volumes = append(objs.PodAdditions.Volumes, corev1.Volume{ + Name: wsGatewayName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: defaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), + }, + DefaultMode: &defaultMode, + }, + }, + }) - return objs, nil + return nil } -func (c *CheRoutingSolver) cheExposedEndpoints(manager *v2alpha1.CheCluster, workspaceID string, endpoints map[string]dwo.EndpointList, routingObj solvers.RoutingObjects) (exposedEndpoints map[string]dwo.ExposedEndpointList, ready bool, err error) { - if manager.Status.GatewayPhase == v2alpha1.GatewayPhaseInitializing { +func (c *CheRoutingSolver) cheExposedEndpoints(cheCluster *v2alpha1.CheCluster, workspaceID string, componentEndpoints map[string]dwo.EndpointList, routingObj solvers.RoutingObjects) (exposedEndpoints map[string]dwo.ExposedEndpointList, ready bool, err error) { + if cheCluster.Status.GatewayPhase == v2alpha1.GatewayPhaseInitializing { return nil, false, nil } - gatewayHost := manager.Status.GatewayHost + exposedEndpoints = map[string]dwo.ExposedEndpointList{} - exposed := map[string]dwo.ExposedEndpointList{} + gatewayHost := cheCluster.Status.GatewayHost - for machineName, endpoints := range endpoints { - exposedEndpoints := dwo.ExposedEndpointList{} + for component, endpoints := range componentEndpoints { for _, endpoint := range endpoints { if endpoint.Exposure != dw.PublicEndpointExposure { continue } - scheme := determineEndpointScheme(manager.Spec.Gateway.IsEnabled(), endpoint) + scheme := determineEndpointScheme(endpoint) if !isExposableScheme(scheme) { // we cannot expose non-http endpoints publicly, because ingresses/routes only support http(s) @@ -146,28 +212,24 @@ func (c *CheRoutingSolver) cheExposedEndpoints(manager *v2alpha1.CheCluster, wor // otherwise it is exposed through the gateway var endpointURL string if infrastructure.IsOpenShift() { - route := findRouteForEndpoint(machineName, endpoint, &routingObj) + route := findRouteForEndpoint(component, endpoint, &routingObj, workspaceID) if route != nil { endpointURL = path.Join(route.Spec.Host, endpoint.Path) } } else { - ingress := findIngressForEndpoint(machineName, endpoint, &routingObj) + ingress := findIngressForEndpoint(component, endpoint, &routingObj) if ingress != nil { endpointURL = path.Join(ingress.Spec.Rules[0].Host, endpoint.Path) } } if endpointURL == "" { - if !manager.Spec.Gateway.IsEnabled() { - return map[string]dwo.ExposedEndpointList{}, false, fmt.Errorf("couldn't find an ingress/route for an endpoint `%s` in workspace `%s`, this is a bug", endpoint.Name, workspaceID) - } - if gatewayHost == "" { // the gateway has not yet established the host return map[string]dwo.ExposedEndpointList{}, false, nil } - publicURLPrefix := getPublicURLPrefixForEndpoint(workspaceID, machineName, endpoint) + publicURLPrefix := getPublicURLPrefixForEndpoint(workspaceID, component, endpoint) endpointURL = path.Join(gatewayHost, publicURLPrefix, endpoint.Path) } @@ -178,16 +240,15 @@ func (c *CheRoutingSolver) cheExposedEndpoints(manager *v2alpha1.CheCluster, wor publicURL = publicURL + "/" } - exposedEndpoints = append(exposedEndpoints, dwo.ExposedEndpoint{ + exposedEndpoints[component] = append(exposedEndpoints[component], dwo.ExposedEndpoint{ Name: endpoint.Name, Url: publicURL, Attributes: endpoint.Attributes, }) } - exposed[machineName] = exposedEndpoints } - return exposed, true, nil + return exposedEndpoints, true, nil } func isExposableScheme(scheme string) bool { @@ -208,212 +269,280 @@ func isSecureScheme(scheme string) bool { return scheme == "https" || scheme == "wss" } -func (c *CheRoutingSolver) getGatewayConfigsAndFillRoutingObjects(cheManager *v2alpha1.CheCluster, workspaceID string, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) ([]corev1.ConfigMap, error) { +func (c *CheRoutingSolver) getGatewayConfigsAndFillRoutingObjects(cheCluster *v2alpha1.CheCluster, workspaceID string, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) ([]corev1.ConfigMap, error) { restrictedAnno, setRestrictedAnno := routing.Annotations[constants.DevWorkspaceRestrictedAccessAnnotation] - labels := defaults.AddStandardLabelsForComponent(cheManager, "gateway-config", defaults.GetGatewayWorkspaceConfigMapLabels(cheManager)) - labels[constants.DevWorkspaceIDLabel] = workspaceID + cmLabels := defaults.AddStandardLabelsForComponent(cheCluster, "gateway-config", defaults.GetGatewayWorkspaceConfigMapLabels(cheCluster)) + cmLabels[constants.DevWorkspaceIDLabel] = workspaceID if setRestrictedAnno { - labels[constants.DevWorkspaceRestrictedAccessAnnotation] = restrictedAnno + cmLabels[constants.DevWorkspaceRestrictedAccessAnnotation] = restrictedAnno } - configMap := corev1.ConfigMap{ - ObjectMeta: v1.ObjectMeta{ - Name: defaults.GetGatewayWorkpaceConfigMapName(workspaceID), - Namespace: cheManager.Namespace, - Labels: labels, - Annotations: map[string]string{ - defaults.ConfigAnnotationDevWorkspaceRoutingName: routing.Name, - defaults.ConfigAnnotationDevWorkspaceRoutingNamespace: routing.Namespace, - }, - }, - Data: map[string]string{}, + configs := make([]corev1.ConfigMap, 0) + + // first do routing from main che-gateway into workspace service + if masterConfig := provisionMasterRouting(cheCluster, routing, cmLabels); masterConfig != nil { + configs = append(configs, *masterConfig) } - config := traefikConfig{ - HTTP: traefikConfigHTTP{ - Routers: map[string]traefikConfigRouter{}, - Services: map[string]traefikConfigService{}, - Middlewares: map[string]traefikConfigMiddleware{}, - }, + // then expose the endpoints + if infraExposer, err := c.getInfraSpecificExposer(cheCluster, routing, objs); err != nil { + return nil, err + } else { + if workspaceConfig := exposeAllEndpoints(cheCluster, routing, objs, infraExposer); workspaceConfig != nil { + configs = append(configs, *workspaceConfig) + } } - // we just need something to make the route names unique.. We also need to make the names as short as possible while - // being relatable to the workspaceID by mere human inspection. So let's just suffix the workspaceID with a "unique" - // suffix, the easiest of which is the iteration order in the map. - // Note that this means that the endpoints might get a different route/ingress name on each workspace start because - // the iteration order is not guaranteed in Go maps. If we want stable ingress/route names for the endpoints, we need - // to devise a different algorithm to produce them. Some kind of hash of workspaceID, component name, endpoint name and port - // might work but will not be relatable to the workspace ID just by looking at it anymore. - order := 0 + return configs, nil +} + +func (c *CheRoutingSolver) getInfraSpecificExposer(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects) (func(info *EndpointInfo), error) { if infrastructure.IsOpenShift() { exposer := &RouteExposer{} - if err := exposer.initFrom(context.TODO(), c.client, cheManager, routing); err != nil { - return []corev1.ConfigMap{}, err + if err := exposer.initFrom(context.TODO(), c.client, cheCluster, routing); err != nil { + return nil, err } - - exposeAllEndpoints(&order, cheManager, routing, &config, objs, func(info *EndpointInfo) { + return func(info *EndpointInfo) { route := exposer.getRouteForService(info) objs.Routes = append(objs.Routes, route) - }) + }, nil } else { exposer := &IngressExposer{} - if err := exposer.initFrom(context.TODO(), c.client, cheManager, routing, defaults.GetIngressAnnotations(cheManager)); err != nil { - return []corev1.ConfigMap{}, err + if err := exposer.initFrom(context.TODO(), c.client, cheCluster, routing, defaults.GetIngressAnnotations(cheCluster)); err != nil { + return nil, err } - - exposeAllEndpoints(&order, cheManager, routing, &config, objs, func(info *EndpointInfo) { + return func(info *EndpointInfo) { ingress := exposer.getIngressForService(info) objs.Ingresses = append(objs.Ingresses, ingress) - }) + }, nil } +} - if len(config.HTTP.Routers) > 0 { - contents, err := yaml.Marshal(config) - if err != nil { - return []corev1.ConfigMap{}, err +func getCommonService(objs *solvers.RoutingObjects, dwId string) *corev1.Service { + for i, svc := range objs.Services { + if svc.Name == common.ServiceName(dwId) { + return &objs.Services[i] } + } + return nil +} - configMap.Data[workspaceID+".yml"] = string(contents) - - return []corev1.ConfigMap{configMap}, nil +func exposeAllEndpoints(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, objs *solvers.RoutingObjects, ingressExpose func(*EndpointInfo)) *corev1.ConfigMap { + wsRouteConfig := traefikConfig{ + HTTP: traefikConfigHTTP{ + Routers: map[string]traefikConfigRouter{}, + Services: map[string]traefikConfigService{}, + Middlewares: map[string]traefikConfigMiddleware{}, + }, } - return []corev1.ConfigMap{}, nil -} + commonService := getCommonService(objs, routing.Spec.DevWorkspaceId) + if commonService == nil { + return nil + } -func exposeAllEndpoints(order *int, cheManager *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, config *traefikConfig, objs *solvers.RoutingObjects, ingressExpose func(*EndpointInfo)) { - info := &EndpointInfo{} + order := 1 for componentName, endpoints := range routing.Spec.Endpoints { - info.componentName = componentName - singlehostPorts, multihostPorts := classifyEndpoints(cheManager.Spec.Gateway.IsEnabled(), order, &endpoints) - - addToTraefikConfig(routing.Namespace, routing.Spec.DevWorkspaceId, componentName, singlehostPorts, config) + for _, e := range endpoints { + if e.Exposure != dw.PublicEndpointExposure { + continue + } - for port, names := range multihostPorts { - backingService := findServiceForPort(port, objs) - for endpointName, val := range names { - info.endpointName = endpointName - info.order = val.order - info.port = port - info.scheme = val.endpointScheme - info.service = backingService + if e.Attributes.GetString(urlRewriteSupportedEndpointAttributeName, nil) == "true" { + addEndpointToTraefikConfig(componentName, e, &wsRouteConfig, cheCluster, routing) + } else { + if !containPort(commonService, int32(e.TargetPort)) { + commonService.Spec.Ports = append(commonService.Spec.Ports, corev1.ServicePort{ + Name: common.EndpointName(e.Name), + Protocol: corev1.ProtocolTCP, + Port: int32(e.TargetPort), + TargetPort: intstr.FromInt(e.TargetPort), + }) + } - ingressExpose(info) + ingressExpose(&EndpointInfo{ + order: order, + componentName: componentName, + endpointName: e.Name, + port: int32(e.TargetPort), + scheme: determineEndpointScheme(e), + service: commonService, + }) + order = order + 1 } } } -} -func getTrackedEndpointName(endpoint *dw.Endpoint) string { - name := "" - if endpoint.Attributes.GetString(uniqueEndpointAttributeName, nil) == "true" { - name = endpoint.Name + contents, err := yaml.Marshal(wsRouteConfig) + if err != nil { + logger.Error(err, "can't serialize traefik config") + } + + wsConfigMap := &corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Name: defaults.GetGatewayWorkspaceConfigMapName(routing.Spec.DevWorkspaceId), + Namespace: routing.Namespace, + Labels: map[string]string{ + constants.DevWorkspaceIDLabel: routing.Spec.DevWorkspaceId, + }, + }, + Data: map[string]string{}, } - return name + wsConfigMap.Data["workspace.yml"] = string(contents) + wsConfigMap.Data["traefik.yml"] = fmt.Sprintf(` +entrypoints: + http: + address: ":%d" + forwardedHeaders: + insecure: true +global: + checkNewVersion: false + sendAnonymousUsage: false +providers: + file: + filename: "/etc/traefik/workspace.yml" + watch: false +log: + level: "INFO"`, wsGatewayPort) + + return wsConfigMap } -// we need to support unique endpoints - so 1 port can actually be accessible -// multiple times, each time using a different resulting external URL. -// non-unique endpoints are all represented using a single external URL -func classifyEndpoints(gatewayEnabled bool, order *int, endpoints *dwo.EndpointList) (singlehostPorts portMapping, multihostPorts portMapping) { - singlehostPorts = portMapping{} - multihostPorts = portMapping{} - for _, e := range *endpoints { - if e.Exposure != dw.PublicEndpointExposure { - continue +func containPort(service *corev1.Service, port int32) bool { + for _, p := range service.Spec.Ports { + if p.Port == port { + return true } + } + return false +} + +func provisionMasterRouting(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting, cmLabels map[string]string) *corev1.ConfigMap { + cfg := &traefikConfig{ + HTTP: traefikConfigHTTP{ + Routers: map[string]traefikConfigRouter{}, + Services: map[string]traefikConfigService{}, + Middlewares: map[string]traefikConfigMiddleware{}, + }, + } + + rtrs := cfg.HTTP.Routers + srvcs := cfg.HTTP.Services + mdls := cfg.HTTP.Middlewares + + dwId := routing.Spec.DevWorkspaceId + dwNamespace := routing.Namespace - i := int32(e.TargetPort) + rtrs[dwId] = traefikConfigRouter{ + Rule: fmt.Sprintf("PathPrefix(`/%s`)", dwId), + Service: dwId, + Middlewares: calculateMiddlewares(dwId, true), + Priority: 100, + } + + srvcs[dwId] = traefikConfigService{ + LoadBalancer: traefikConfigLoadbalancer{ + Servers: []traefikConfigLoadbalancerServer{ + { + URL: getServiceURL(wsGatewayPort, dwId, dwNamespace), + }, + }, + }, + } - name := "" - if e.Attributes.GetString(uniqueEndpointAttributeName, nil) == "true" { - name = e.Name + mdls[dwId+"-prefix"] = traefikConfigMiddleware{ + StripPrefix: &traefikConfigStripPrefix{ + Prefixes: []string{"/" + dwId}, + }, + } + + if infrastructure.IsOpenShift() { + mdls[dwId+"-auth"] = traefikConfigMiddleware{ + ForwardAuth: &traefikConfigForwardAuth{ + Address: "http://127.0.0.1:8089?namespace=" + dwNamespace, + }, } - ports := multihostPorts - if gatewayEnabled && e.Attributes.GetString(urlRewriteSupportedEndpointAttributeName, nil) == "true" { - ports = singlehostPorts + mdls[dwId+"-header"] = traefikConfigMiddleware{ + Plugin: &traefikPlugin{ + HeaderRewrite: &traefikPluginHeaderRewrite{ + From: "X-Forwarded-Access-Token", + To: "Authorization", + Prefix: "Bearer ", + }, + }, } + } - if ports[i] == nil { - ports[i] = map[string]portMappingValue{} + if len(cfg.HTTP.Routers) > 0 { + contents, err := yaml.Marshal(cfg) + if err != nil { + logger.Error(err, "can't serialize traefik config") + return nil } - if _, ok := ports[i][name]; !ok { - ports[i][name] = portMappingValue{ - order: *order, - endpointScheme: determineEndpointScheme(gatewayEnabled, e), - } - *order = *order + 1 + configMap := &corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Name: defaults.GetGatewayWorkspaceConfigMapName(dwId), + Namespace: cheCluster.Namespace, + Labels: cmLabels, + Annotations: map[string]string{ + defaults.ConfigAnnotationDevWorkspaceRoutingName: routing.Name, + defaults.ConfigAnnotationDevWorkspaceRoutingNamespace: routing.Namespace, + }, + }, + Data: map[string]string{}, } + configMap.Data[dwId+".yml"] = string(contents) + return configMap } - - return + return nil } -func addToTraefikConfig(namespace string, workspaceID string, machineName string, portMapping portMapping, cfg *traefikConfig) { - rtrs := cfg.HTTP.Routers - srvcs := cfg.HTTP.Services - mdls := cfg.HTTP.Middlewares - - for port, names := range portMapping { - for endpointName := range names { - name := getEndpointExposingObjectName(machineName, workspaceID, port, endpointName) - var prefix string - var serviceURL string - - prefix = getPublicURLPrefix(workspaceID, machineName, port, endpointName) - serviceURL = getServiceURL(port, workspaceID, namespace) +func addEndpointToTraefikConfig(component string, e dw.Endpoint, cfg *traefikConfig, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) { + prefix := getLocalURLPrefix(component, e) - rtrs[name] = traefikConfigRouter{ - Rule: fmt.Sprintf("PathPrefix(`%s`)", prefix), - Service: name, - Middlewares: calculateMiddlewares(name), - Priority: 100, - } - - srvcs[name] = traefikConfigService{ - LoadBalancer: traefikConfigLoadbalancer{ - Servers: []traefikConfigLoadbalancerServer{ - { - URL: serviceURL, - }, - }, - }, - } + cfg.HTTP.Routers[e.Name] = traefikConfigRouter{ + Rule: fmt.Sprintf("PathPrefix(`%s`)", prefix), + Service: e.Name, + Middlewares: calculateMiddlewares(e.Name, false), + Priority: 100, + } - mdls[name+"-prefix"] = traefikConfigMiddleware{ - StripPrefix: &traefikConfigStripPrefix{ - Prefixes: []string{prefix}, + cfg.HTTP.Services[e.Name] = traefikConfigService{ + LoadBalancer: traefikConfigLoadbalancer{ + Servers: []traefikConfigLoadbalancerServer{ + { + URL: fmt.Sprintf("http://127.0.0.1:%d", e.TargetPort), }, - } + }, + }, + } - if infrastructure.IsOpenShift() { - mdls[name+"-auth"] = traefikConfigMiddleware{ - ForwardAuth: &traefikConfigForwardAuth{ - Address: "http://127.0.0.1:8089?namespace=" + namespace, - }, - } + cfg.HTTP.Middlewares[e.Name+"-prefix"] = traefikConfigMiddleware{ + StripPrefix: &traefikConfigStripPrefix{ + Prefixes: []string{prefix}, + }, + } - mdls[name+"-header"] = traefikConfigMiddleware{ - Plugin: &traefikPlugin{ - HeaderRewrite: &traefikPluginHeaderRewrite{ - From: "X-Forwarded-Access-Token", - To: "Authorization", - Prefix: "Bearer ", - }, - }, - } - } + if infrastructure.IsOpenShift() { + cfg.HTTP.Middlewares[e.Name+"-auth"] = traefikConfigMiddleware{ + ForwardAuth: &traefikConfigForwardAuth{ + Address: fmt.Sprintf("http://%s.%s:8089?namespace=%s", gateway.GatewayServiceName, cheCluster.Namespace, routing.Namespace), + }, } } } -func calculateMiddlewares(name string) []string { +func calculateMiddlewares(name string, header bool) []string { if infrastructure.IsOpenShift() { - return []string{name + "-header", name + "-prefix", name + "-auth"} + if header { + return []string{name + "-header", name + "-prefix", name + "-auth"} + } else { + return []string{name + "-prefix", name + "-auth"} + } } else { return []string{name + "-prefix"} } @@ -437,7 +566,7 @@ func findIngressForEndpoint(machineName string, endpoint dw.Endpoint, objs *solv ingress := &objs.Ingresses[i] if ingress.Annotations[defaults.ConfigAnnotationComponentName] != machineName || - ingress.Annotations[defaults.ConfigAnnotationEndpointName] != getTrackedEndpointName(&endpoint) { + ingress.Annotations[defaults.ConfigAnnotationEndpointName] != endpoint.Name { continue } @@ -455,13 +584,19 @@ func findIngressForEndpoint(machineName string, endpoint dw.Endpoint, objs *solv return nil } -func findRouteForEndpoint(machineName string, endpoint dw.Endpoint, objs *solvers.RoutingObjects) *routeV1.Route { +func findRouteForEndpoint(componentName string, endpoint dw.Endpoint, objs *solvers.RoutingObjects, dwId string) *routeV1.Route { service := findServiceForPort(int32(endpoint.TargetPort), objs) + if service == nil { + service = getCommonService(objs, dwId) + } + if service == nil { + return nil + } for r := range objs.Routes { route := &objs.Routes[r] - if route.Annotations[defaults.ConfigAnnotationComponentName] == machineName && - route.Annotations[defaults.ConfigAnnotationEndpointName] == getTrackedEndpointName(&endpoint) && + if route.Annotations[defaults.ConfigAnnotationComponentName] == componentName && + route.Annotations[defaults.ConfigAnnotationEndpointName] == endpoint.Name && route.Spec.To.Kind == "Service" && route.Spec.To.Name == service.Name && route.Spec.Port.TargetPort.IntValue() == endpoint.TargetPort { @@ -473,19 +608,33 @@ func findRouteForEndpoint(machineName string, endpoint dw.Endpoint, objs *solver } func (c *CheRoutingSolver) cheRoutingFinalize(cheManager *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) error { - configs := &corev1.ConfigMapList{} - selector, err := labels.Parse(fmt.Sprintf("%s=%s", constants.DevWorkspaceIDLabel, routing.Spec.DevWorkspaceId)) if err != nil { return err } - listOpts := &client.ListOptions{ + // delete configs from che namespace + if deleteErr := c.deleteConfigs(&client.ListOptions{ Namespace: cheManager.Namespace, LabelSelector: selector, + }); deleteErr != nil { + return deleteErr + } + + // delete configs from workspace namespace + if deleteErr := c.deleteConfigs(&client.ListOptions{ + Namespace: routing.Namespace, + LabelSelector: selector, + }); deleteErr != nil { + return deleteErr } - err = c.client.List(context.TODO(), configs, listOpts) + return nil +} + +func (c *CheRoutingSolver) deleteConfigs(listOpts *client.ListOptions) error { + configs := &corev1.ConfigMapList{} + err := c.client.List(context.TODO(), configs, listOpts) if err != nil { return err } @@ -496,7 +645,6 @@ func (c *CheRoutingSolver) cheRoutingFinalize(cheManager *v2alpha1.CheCluster, r return err } } - return nil } @@ -522,7 +670,15 @@ func getPublicURLPrefix(workspaceID string, machineName string, port int32, uniq return fmt.Sprintf(uniqueEndpointURLPrefixPattern, workspaceID, machineName, uniqueEndpointName) } -func determineEndpointScheme(gatewayEnabled bool, e dw.Endpoint) string { +func getLocalURLPrefix(componentName string, e dw.Endpoint) string { + if e.Attributes.GetString(uniqueEndpointAttributeName, nil) == "true" { + return fmt.Sprintf("/%s/%s", componentName, e.Name) + } else { + return fmt.Sprintf("/%s/%d", componentName, e.TargetPort) + } +} + +func determineEndpointScheme(e dw.Endpoint) string { var scheme string if e.Protocol == "" { scheme = "http" @@ -533,7 +689,7 @@ func determineEndpointScheme(gatewayEnabled bool, e dw.Endpoint) string { upgradeToSecure := e.Secure // gateway is always on HTTPS, so if the endpoint is served through the gateway, we need to use the TLS'd variant. - if gatewayEnabled && e.Attributes.GetString(urlRewriteSupportedEndpointAttributeName, nil) == "true" { + if e.Attributes.GetString(urlRewriteSupportedEndpointAttributeName, nil) == "true" { upgradeToSecure = true } diff --git a/controllers/devworkspace/solver/che_routing_test.go b/controllers/devworkspace/solver/che_routing_test.go index ce54fde5c9..8b749e0d6f 100644 --- a/controllers/devworkspace/solver/che_routing_test.go +++ b/controllers/devworkspace/solver/che_routing_test.go @@ -202,9 +202,12 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { } }) - t.Run("noPodAdditions", func(t *testing.T) { - if objs.PodAdditions != nil { - t.Error() + t.Run("testPodAdditions", func(t *testing.T) { + if len(objs.PodAdditions.Containers) != 1 || objs.PodAdditions.Containers[0].Name != wsGatewayName { + t.Error("expected Container pod addition with Workspace Gateway. Got ", objs.PodAdditions) + } + if len(objs.PodAdditions.Volumes) != 1 || objs.PodAdditions.Volumes[0].Name != wsGatewayName { + t.Error("expected Volume pod addition for workspace gateway. Got ", objs.PodAdditions) } }) @@ -229,24 +232,32 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { cms := &corev1.ConfigMapList{} cl.List(context.TODO(), cms) - if len(cms.Items) != 1 { - t.Errorf("there should be 1 configmap created for the gateway config of the workspace but there were: %d", len(cms.Items)) + if len(cms.Items) != 2 { + t.Errorf("there should be 2 configmaps created for the gateway config of the workspace but there were: %d", len(cms.Items)) } + var workspaceMainCfg *corev1.ConfigMap var workspaceCfg *corev1.ConfigMap - for _, cfg := range cms.Items { - if cfg.Name == "wsid" { - workspaceCfg = &cfg + if cfg.Name == "wsid-route" && cfg.Namespace == "ns" { + workspaceMainCfg = cfg.DeepCopy() + } + if cfg.Name == "wsid-route" && cfg.Namespace == "ws" { + workspaceCfg = cfg.DeepCopy() } } - if workspaceCfg == nil { + if workspaceMainCfg == nil { t.Fatalf("traefik configuration for the workspace not found") } - traefikWorkspaceConfig := workspaceCfg.Data["wsid.yml"] + traefikMainWorkspaceConfig := workspaceMainCfg.Data["wsid.yml"] + if len(traefikMainWorkspaceConfig) == 0 { + t.Fatal("No traefik config file found in the main workspace config configmap") + } + + traefikWorkspaceConfig := workspaceCfg.Data["workspace.yml"] if len(traefikWorkspaceConfig) == 0 { t.Fatal("No traefik config file found in the workspace config configmap") } @@ -256,30 +267,30 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { t.Fatal(err) } - if len(workspaceConfig.HTTP.Routers) != 1 { - t.Fatalf("Expected exactly one traefik router but got %d", len(workspaceConfig.HTTP.Routers)) + if len(workspaceConfig.HTTP.Routers) != 3 { + t.Fatalf("Expected 3 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) } - wsid := "wsid-m1-9999" - if _, ok := workspaceConfig.HTTP.Routers[wsid]; !ok { + endpointName := "e1" + if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } - if len(workspaceConfig.HTTP.Routers[wsid].Middlewares) != 1 { - t.Fatalf("Expected 1 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[wsid].Middlewares)) + if len(workspaceConfig.HTTP.Routers[endpointName].Middlewares) != 1 { + t.Fatalf("Expected 1 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[endpointName].Middlewares)) } - if len(workspaceConfig.HTTP.Middlewares) != 1 { - t.Fatalf("Expected 1 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) + if len(workspaceConfig.HTTP.Middlewares) != 3 { + t.Fatalf("Expected 3 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) } - mwares := []string{wsid + "-prefix"} + mwares := []string{endpointName + "-prefix"} for _, mware := range mwares { if _, ok := workspaceConfig.HTTP.Middlewares[mware]; !ok { t.Fatalf("traefik config doesn't set middleware '%s'", mware) } found := false - for _, r := range workspaceConfig.HTTP.Routers[wsid].Middlewares { + for _, r := range workspaceConfig.HTTP.Routers[endpointName].Middlewares { if r == mware { found = true } @@ -308,9 +319,12 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { } }) - t.Run("noPodAdditions", func(t *testing.T) { - if objs.PodAdditions != nil { - t.Error() + t.Run("testPodAdditions", func(t *testing.T) { + if len(objs.PodAdditions.Containers) != 1 || objs.PodAdditions.Containers[0].Name != wsGatewayName { + t.Error("expected Container pod addition with Workspace Gateway. Got ", objs.PodAdditions) + } + if len(objs.PodAdditions.Volumes) != 1 || objs.PodAdditions.Volumes[0].Name != wsGatewayName { + t.Error("expected Volume pod addition for workspace gateway. Got ", objs.PodAdditions) } }) @@ -318,24 +332,32 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { cms := &corev1.ConfigMapList{} cl.List(context.TODO(), cms) - if len(cms.Items) != 1 { - t.Errorf("there should be 1 configmap created for the gateway config of the workspace but there were: %d", len(cms.Items)) + if len(cms.Items) != 2 { + t.Errorf("there should be 2 configmaps created for the gateway config of the workspace but there were: %d", len(cms.Items)) } + var workspaceMainCfg *corev1.ConfigMap var workspaceCfg *corev1.ConfigMap - for _, cfg := range cms.Items { - if cfg.Name == "wsid" { - workspaceCfg = &cfg + if cfg.Name == "wsid-route" && cfg.Namespace == "ns" { + workspaceMainCfg = cfg.DeepCopy() + } + if cfg.Name == "wsid-route" && cfg.Namespace == "ws" { + workspaceCfg = cfg.DeepCopy() } } - if workspaceCfg == nil { + if workspaceMainCfg == nil { t.Fatalf("traefik configuration for the workspace not found") } - traefikWorkspaceConfig := workspaceCfg.Data["wsid.yml"] + traefikMainWorkspaceConfig := workspaceMainCfg.Data["wsid.yml"] + if len(traefikMainWorkspaceConfig) == 0 { + t.Fatal("No traefik config file found in the main workspace config configmap") + } + + traefikWorkspaceConfig := workspaceCfg.Data["workspace.yml"] if len(traefikWorkspaceConfig) == 0 { t.Fatal("No traefik config file found in the workspace config configmap") } @@ -345,30 +367,40 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { t.Fatal(err) } - if len(workspaceConfig.HTTP.Routers) != 1 { - t.Fatalf("Expected exactly one traefik router but got %d", len(workspaceConfig.HTTP.Routers)) + if len(workspaceConfig.HTTP.Routers) != 3 { + t.Fatalf("Expected 3 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) } - wsid := "wsid-m1-9999" - if _, ok := workspaceConfig.HTTP.Routers[wsid]; !ok { + endpointName := "e1" + if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } - if len(workspaceConfig.HTTP.Routers[wsid].Middlewares) != 3 { - t.Fatalf("Expected 3 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[wsid].Middlewares)) + if len(workspaceConfig.HTTP.Routers[endpointName].Middlewares) != 2 { + t.Fatalf("Expected 2 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[endpointName].Middlewares)) } - if len(workspaceConfig.HTTP.Middlewares) != 3 { + workspaceMainConfig := traefikConfig{} + if err := yaml.Unmarshal([]byte(traefikMainWorkspaceConfig), &workspaceMainConfig); err != nil { + t.Fatal(err) + } + + if len(workspaceMainConfig.HTTP.Routers) != 1 { + t.Fatalf("Expected one route in main route config but got '%d'", len(workspaceMainConfig.HTTP.Routers)) + } + + if len(workspaceMainConfig.HTTP.Middlewares) != 3 { t.Fatalf("Expected 3 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) } - mwares := []string{wsid + "-auth", wsid + "-prefix", wsid + "-header"} + endpointName = "wsid" + mwares := []string{endpointName + "-auth", endpointName + "-prefix", endpointName + "-header"} for _, mware := range mwares { - if _, ok := workspaceConfig.HTTP.Middlewares[mware]; !ok { + if _, ok := workspaceMainConfig.HTTP.Middlewares[mware]; !ok { t.Fatalf("traefik config doesn't set middleware '%s'", mware) } found := false - for _, r := range workspaceConfig.HTTP.Routers[wsid].Middlewares { + for _, r := range workspaceMainConfig.HTTP.Routers[endpointName].Middlewares { if r == mware { found = true } @@ -387,9 +419,12 @@ func TestCreateSubDomainObjects(t *testing.T) { cl, _, objs := getSpecObjects(t, subdomainDevWorkspaceRouting()) - t.Run("noPodAdditions", func(t *testing.T) { - if objs.PodAdditions != nil { - t.Error() + t.Run("testPodAdditions", func(t *testing.T) { + if len(objs.PodAdditions.Containers) != 1 || objs.PodAdditions.Containers[0].Name != wsGatewayName { + t.Error("expected Container pod addition with Workspace Gateway. Got ", objs.PodAdditions) + } + if len(objs.PodAdditions.Volumes) != 1 || objs.PodAdditions.Volumes[0].Name != wsGatewayName { + t.Error("expected Volume pod addition for workspace gateway. Got ", objs.PodAdditions) } }) @@ -414,8 +449,8 @@ func TestCreateSubDomainObjects(t *testing.T) { cms := &corev1.ConfigMapList{} cl.List(context.TODO(), cms) - if len(cms.Items) != 0 { - t.Errorf("there should be 0 configmaps created but there were: %d", len(cms.Items)) + if len(cms.Items) != 2 { + t.Errorf("there should be 2 configmaps create but found: %d", len(cms.Items)) } }) @@ -424,21 +459,21 @@ func TestCreateSubDomainObjects(t *testing.T) { t.Run("expectedIngresses", func(t *testing.T) { objs := testCommon(infrastructure.Kubernetes) - if len(objs.Ingresses) != 1 { - t.Error() + if len(objs.Ingresses) != 3 { + t.Error("Expected 3 ingress, found ", len(objs.Ingresses)) } if objs.Ingresses[0].Spec.Rules[0].Host != "wsid-1.down.on.earth" { - t.Error() + t.Error("Expected Ingress host 'wsid-1.down.on.earth', but got ", objs.Ingresses[0].Spec.Rules[0].Host) } }) t.Run("expectedRoutes", func(t *testing.T) { objs := testCommon(infrastructure.OpenShiftv4) - if len(objs.Routes) != 1 { - t.Error() + if len(objs.Routes) != 3 { + t.Error("Expected 3 Routes, found ", len(objs.Routes)) } if objs.Routes[0].Spec.Host != "wsid-1.down.on.earth" { - t.Error() + t.Error("Expected Route host 'wsid-1.down.on.earth', but got ", objs.Routes[0].Spec.Host) } }) } @@ -534,16 +569,16 @@ func TestReportSubdomainExposedEndpoints(t *testing.T) { if e2.Name != "e2" { t.Errorf("The second endpoint should have been e2 but is %s", e1.Name) } - if e2.Url != "https://wsid-1.down.on.earth/2.js" { - t.Errorf("The e2 endpoint should have the following URL: '%s' but has '%s'.", "https://wsid-1.down.on.earth/2.js", e2.Url) + if e2.Url != "https://wsid-2.down.on.earth/2.js" { + t.Errorf("The e2 endpoint should have the following URL: '%s' but has '%s'.", "https://wsid-2.down.on.earth/2.js", e2.Url) } e3 := m1[2] if e3.Name != "e3" { t.Errorf("The third endpoint should have been e3 but is %s", e1.Name) } - if e3.Url != "http://wsid-1.down.on.earth/" { - t.Errorf("The e3 endpoint should have the following URL: '%s' but has '%s'.", "https://wsid-1.down.on.earth/", e3.Url) + if e3.Url != "http://wsid-3.down.on.earth/" { + t.Errorf("The e3 endpoint should have the following URL: '%s' but has '%s'.", "https://wsid-3.down.on.earth/", e3.Url) } } @@ -617,7 +652,7 @@ func TestUsesIngressAnnotationsForWorkspaceEndpointIngresses(t *testing.T) { _, _, objs := getSpecObjectsForManager(t, mgr, subdomainDevWorkspaceRouting()) - if len(objs.Ingresses) != 1 { + if len(objs.Ingresses) != 3 { t.Fatalf("Unexpected number of generated ingresses: %d", len(objs.Ingresses)) } @@ -663,7 +698,7 @@ func TestUsesCustomCertificateForWorkspaceEndpointIngresses(t *testing.T) { }, }) - if len(objs.Ingresses) != 1 { + if len(objs.Ingresses) != 3 { t.Fatalf("Unexpected number of generated ingresses: %d", len(objs.Ingresses)) } @@ -684,6 +719,30 @@ func TestUsesCustomCertificateForWorkspaceEndpointIngresses(t *testing.T) { if ingress.Spec.TLS[0].Hosts[0] != "wsid-1.almost.trivial" { t.Errorf("Unexpected host name of the TLS spec: %s", ingress.Spec.TLS[0].Hosts[0]) } + + ingress = objs.Ingresses[1] + + if len(ingress.Spec.TLS) != 1 { + t.Fatalf("Unexpected number of TLS records on the ingress: %d", len(ingress.Spec.TLS)) + } + + if ingress.Spec.TLS[0].SecretName != "wsid-endpoints" { + t.Errorf("Unexpected name of the TLS secret on the ingress: %s", ingress.Spec.TLS[0].SecretName) + } + + if len(ingress.Spec.TLS[0].Hosts) != 1 { + t.Fatalf("Unexpected number of host records on the TLS spec: %d", len(ingress.Spec.TLS[0].Hosts)) + } + + if ingress.Spec.TLS[0].Hosts[0] != "wsid-2.almost.trivial" { + t.Errorf("Unexpected host name of the TLS spec: %s", ingress.Spec.TLS[0].Hosts[0]) + } + + ingress = objs.Ingresses[2] + + if len(ingress.Spec.TLS) != 0 { + t.Fatalf("Unexpected number of TLS records on the ingress: %d", len(ingress.Spec.TLS)) + } } func TestUsesCustomCertificateForWorkspaceEndpointRoutes(t *testing.T) { @@ -717,7 +776,7 @@ func TestUsesCustomCertificateForWorkspaceEndpointRoutes(t *testing.T) { }, }) - if len(objs.Routes) != 1 { + if len(objs.Routes) != 3 { t.Fatalf("Unexpected number of generated routes: %d", len(objs.Routes)) } @@ -730,6 +789,22 @@ func TestUsesCustomCertificateForWorkspaceEndpointRoutes(t *testing.T) { if route.Spec.TLS.Key != "asdf" { t.Errorf("Unexpected key of TLS spec: %s", route.Spec.TLS.Key) } + + route = objs.Routes[1] + + if route.Spec.TLS.Certificate != "qwer" { + t.Errorf("Unexpected name of the TLS certificate on the route: %s", route.Spec.TLS.Certificate) + } + + if route.Spec.TLS.Key != "asdf" { + t.Errorf("Unexpected key of TLS spec: %s", route.Spec.TLS.Key) + } + + route = objs.Routes[2] + + if route.Spec.TLS != nil { + t.Errorf("Unexpected TLS on the route: %s", route.Spec.TLS) + } } func asV1(v2Obj *v2alpha1.CheCluster) *v1.CheCluster { diff --git a/controllers/devworkspace/solver/endpoint_exposer.go b/controllers/devworkspace/solver/endpoint_exposer.go index 4f2235152a..5ba8c5b797 100644 --- a/controllers/devworkspace/solver/endpoint_exposer.go +++ b/controllers/devworkspace/solver/endpoint_exposer.go @@ -220,7 +220,7 @@ func (e *IngressExposer) getIngressForService(endpoint *EndpointInfo) networking } func hostName(order int, workspaceID string, baseDomain string) string { - return fmt.Sprintf("%s-%d.%s", workspaceID, order+1, baseDomain) + return fmt.Sprintf("%s-%d.%s", workspaceID, order, baseDomain) } func routeAnnotations(machineName string, endpointName string) map[string]string { diff --git a/controllers/devworkspace/solver/solver.go b/controllers/devworkspace/solver/solver.go index 8dcbca9c6e..69fe11c232 100644 --- a/controllers/devworkspace/solver/solver.go +++ b/controllers/devworkspace/solver/solver.go @@ -101,7 +101,7 @@ func isGatewayWorkspaceConfig(obj metav1.Object) (bool, types.NamespacedName) { objectName := obj.GetName() // bail out quickly if we're not dealing with a configmap with an expected name - if objectName != defaults.GetGatewayWorkpaceConfigMapName(workspaceID) { + if objectName != defaults.GetGatewayWorkspaceConfigMapName(workspaceID) { return false, types.NamespacedName{} } diff --git a/pkg/deploy/defaults.go b/pkg/deploy/defaults.go index 81810443a5..65318916d3 100644 --- a/pkg/deploy/defaults.go +++ b/pkg/deploy/defaults.go @@ -360,7 +360,7 @@ func DefaultPullPolicyFromDockerImage(dockerImage string) string { } func GetSingleHostExposureType(cr *orgv1.CheCluster) string { - if util.IsOpenShift { + if util.IsOpenShift || cr.Spec.DevWorkspace.Enable { return DefaultOpenShiftSingleHostExposureType } diff --git a/pkg/deploy/gateway/gateway.go b/pkg/deploy/gateway/gateway.go index acb7f42552..4cce9e766b 100644 --- a/pkg/deploy/gateway/gateway.go +++ b/pkg/deploy/gateway/gateway.go @@ -40,8 +40,10 @@ import ( const ( // GatewayServiceName is the name of the service which through which the gateway can be accessed GatewayServiceName = "che-gateway" + GatewayServicePort = 8080 gatewayServerConfigName = "che-gateway-route-server" + gatewayKubeAuthConfigName = "che-gateway-route-kube-auth" gatewayConfigComponentName = "che-gateway-config" gatewayOauthSecretName = "che-gateway-oauth-secret" ) @@ -56,8 +58,9 @@ var ( // SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration func SyncGatewayToCluster(deployContext *deploy.DeployContext) error { - if util.GetServerExposureStrategy(deployContext.CheCluster) == "single-host" && - (deploy.GetSingleHostExposureType(deployContext.CheCluster) == "gateway") { + if (util.GetServerExposureStrategy(deployContext.CheCluster) == "single-host" && + deploy.GetSingleHostExposureType(deployContext.CheCluster) == "gateway") || + deployContext.CheCluster.Spec.DevWorkspace.Enable { return syncAll(deployContext) } @@ -408,7 +411,7 @@ func getGatewayOauthProxyConfigSpec(instance *orgv1.CheCluster, cookieSecret str }, Data: map[string]string{ "oauth-proxy.cfg": fmt.Sprintf(` -http_address = ":8080" +http_address = ":%d" https_address = "" provider = "openshift" redirect_url = "https://%s/oauth/callback" @@ -424,7 +427,7 @@ cookie_expire = "24h0m0s" email_domains = "*" cookie_httponly = false pass_access_token = true -skip_provider_button = true`, instance.Spec.Server.CheHost, instance.Spec.Auth.OAuthClientName, instance.Spec.Auth.OAuthSecret, GatewayServiceName, cookieSecret), +skip_provider_button = true`, GatewayServicePort, instance.Spec.Server.CheHost, instance.Spec.Auth.OAuthClientName, instance.Spec.Auth.OAuthSecret, GatewayServiceName, cookieSecret), }, } } @@ -490,7 +493,7 @@ func getGatewayHeaderRewritePluginConfigSpec(instance *orgv1.CheCluster) (*corev } func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap { - traefikPort := 8080 + traefikPort := GatewayServicePort if util.IsNativeUserModeEnabled(instance) { traefikPort = 8081 } @@ -639,7 +642,7 @@ func getContainersSpec(instance *orgv1.CheCluster) []corev1.Container { }, }, Ports: []corev1.ContainerPort{ - {ContainerPort: 8080, Protocol: "TCP"}, + {ContainerPort: GatewayServicePort, Protocol: "TCP"}, }, }, corev1.Container{ @@ -647,7 +650,7 @@ func getContainersSpec(instance *orgv1.CheCluster) []corev1.Container { Image: authzImage, ImagePullPolicy: corev1.PullAlways, Args: []string{ - "--insecure-listen-address=127.0.0.1:8089", + "--insecure-listen-address=0.0.0.0:8089", "--upstream=http://127.0.0.1:8090/ping", "--logtostderr=true", "--config-file=/etc/kube-rbac-proxy/authorization-config.yaml", @@ -761,9 +764,15 @@ func getGatewayServiceSpec(instance *orgv1.CheCluster) corev1.Service { Ports: []corev1.ServicePort{ { Name: "gateway-http", - Port: 8080, + Port: GatewayServicePort, Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromInt(8080), + TargetPort: intstr.FromInt(GatewayServicePort), + }, + { + Name: "gateway-kube-authz", + Port: 8089, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt(8089), }, }, }, From 31e6f044d9e10baa582a44a01a6216f78f991b26 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Wed, 15 Sep 2021 10:03:10 +0200 Subject: [PATCH 2/6] don't expose same path multiple times Signed-off-by: Michal Vala --- .../devworkspace/solver/che_routing.go | 26 +++++++++++++------ .../devworkspace/solver/che_routing_test.go | 16 ++++++------ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index cd7a0a83a6..1a02a955c2 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -16,6 +16,7 @@ import ( "context" "fmt" "path" + "strconv" "strings" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" @@ -501,17 +502,26 @@ func provisionMasterRouting(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWor return nil } -func addEndpointToTraefikConfig(component string, e dw.Endpoint, cfg *traefikConfig, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) { - prefix := getLocalURLPrefix(component, e) +func addEndpointToTraefikConfig(componentName string, e dw.Endpoint, cfg *traefikConfig, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) { + prefix := getLocalURLPrefix(componentName, e) + rulePrefix := fmt.Sprintf("PathPrefix(`%s`)", prefix) - cfg.HTTP.Routers[e.Name] = traefikConfigRouter{ - Rule: fmt.Sprintf("PathPrefix(`%s`)", prefix), + // skip if exact same route is already exposed + for _, r := range cfg.HTTP.Routers { + if r.Rule == rulePrefix { + return + } + } + + name := fmt.Sprintf("%s-%s-%s", routing.Spec.DevWorkspaceId, componentName, strconv.Itoa(e.TargetPort)) + cfg.HTTP.Routers[name] = traefikConfigRouter{ + Rule: rulePrefix, Service: e.Name, - Middlewares: calculateMiddlewares(e.Name, false), + Middlewares: calculateMiddlewares(name, false), Priority: 100, } - cfg.HTTP.Services[e.Name] = traefikConfigService{ + cfg.HTTP.Services[name] = traefikConfigService{ LoadBalancer: traefikConfigLoadbalancer{ Servers: []traefikConfigLoadbalancerServer{ { @@ -521,14 +531,14 @@ func addEndpointToTraefikConfig(component string, e dw.Endpoint, cfg *traefikCon }, } - cfg.HTTP.Middlewares[e.Name+"-prefix"] = traefikConfigMiddleware{ + cfg.HTTP.Middlewares[name+"-prefix"] = traefikConfigMiddleware{ StripPrefix: &traefikConfigStripPrefix{ Prefixes: []string{prefix}, }, } if infrastructure.IsOpenShift() { - cfg.HTTP.Middlewares[e.Name+"-auth"] = traefikConfigMiddleware{ + cfg.HTTP.Middlewares[name+"-auth"] = traefikConfigMiddleware{ ForwardAuth: &traefikConfigForwardAuth{ Address: fmt.Sprintf("http://%s.%s:8089?namespace=%s", gateway.GatewayServiceName, cheCluster.Namespace, routing.Namespace), }, diff --git a/controllers/devworkspace/solver/che_routing_test.go b/controllers/devworkspace/solver/che_routing_test.go index 8b749e0d6f..26865cdda4 100644 --- a/controllers/devworkspace/solver/che_routing_test.go +++ b/controllers/devworkspace/solver/che_routing_test.go @@ -267,11 +267,11 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { t.Fatal(err) } - if len(workspaceConfig.HTTP.Routers) != 3 { - t.Fatalf("Expected 3 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) + if len(workspaceConfig.HTTP.Routers) != 1 { + t.Fatalf("Expected 1 traefik router but got %d", len(workspaceConfig.HTTP.Routers)) } - endpointName := "e1" + endpointName := "wsid-m1-9999" if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } @@ -280,8 +280,8 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { t.Fatalf("Expected 1 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[endpointName].Middlewares)) } - if len(workspaceConfig.HTTP.Middlewares) != 3 { - t.Fatalf("Expected 3 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) + if len(workspaceConfig.HTTP.Middlewares) != 1 { + t.Fatalf("Expected 1 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) } mwares := []string{endpointName + "-prefix"} @@ -367,11 +367,11 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { t.Fatal(err) } - if len(workspaceConfig.HTTP.Routers) != 3 { - t.Fatalf("Expected 3 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) + if len(workspaceConfig.HTTP.Routers) != 1 { + t.Fatalf("Expected 1 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) } - endpointName := "e1" + endpointName := "wsid-m1-9999" if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } From 60cf8382907cdcba228473b29a7532c6f9abe042 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Wed, 15 Sep 2021 10:24:16 +0200 Subject: [PATCH 3/6] cleanup Signed-off-by: Michal Vala --- .../devworkspace/solver/che_routing.go | 4 +-- .../devworkspace/solver/che_routing_test.go | 32 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index 1a02a955c2..dca038b729 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -571,11 +571,11 @@ func findServiceForPort(port int32, objs *solvers.RoutingObjects) *corev1.Servic return nil } -func findIngressForEndpoint(machineName string, endpoint dw.Endpoint, objs *solvers.RoutingObjects) *networkingv1.Ingress { +func findIngressForEndpoint(componentName string, endpoint dw.Endpoint, objs *solvers.RoutingObjects) *networkingv1.Ingress { for i := range objs.Ingresses { ingress := &objs.Ingresses[i] - if ingress.Annotations[defaults.ConfigAnnotationComponentName] != machineName || + if ingress.Annotations[defaults.ConfigAnnotationComponentName] != componentName || ingress.Annotations[defaults.ConfigAnnotationEndpointName] != endpoint.Name { continue } diff --git a/controllers/devworkspace/solver/che_routing_test.go b/controllers/devworkspace/solver/che_routing_test.go index 26865cdda4..663d271175 100644 --- a/controllers/devworkspace/solver/che_routing_test.go +++ b/controllers/devworkspace/solver/che_routing_test.go @@ -271,26 +271,26 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) { t.Fatalf("Expected 1 traefik router but got %d", len(workspaceConfig.HTTP.Routers)) } - endpointName := "wsid-m1-9999" - if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { + wsid := "wsid-m1-9999" + if _, ok := workspaceConfig.HTTP.Routers[wsid]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } - if len(workspaceConfig.HTTP.Routers[endpointName].Middlewares) != 1 { - t.Fatalf("Expected 1 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[endpointName].Middlewares)) + if len(workspaceConfig.HTTP.Routers[wsid].Middlewares) != 1 { + t.Fatalf("Expected 1 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[wsid].Middlewares)) } if len(workspaceConfig.HTTP.Middlewares) != 1 { t.Fatalf("Expected 1 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) } - mwares := []string{endpointName + "-prefix"} + mwares := []string{wsid + "-prefix"} for _, mware := range mwares { if _, ok := workspaceConfig.HTTP.Middlewares[mware]; !ok { t.Fatalf("traefik config doesn't set middleware '%s'", mware) } found := false - for _, r := range workspaceConfig.HTTP.Routers[endpointName].Middlewares { + for _, r := range workspaceConfig.HTTP.Routers[wsid].Middlewares { if r == mware { found = true } @@ -371,13 +371,13 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { t.Fatalf("Expected 1 traefik routers but got %d", len(workspaceConfig.HTTP.Routers)) } - endpointName := "wsid-m1-9999" - if _, ok := workspaceConfig.HTTP.Routers[endpointName]; !ok { + wsid := "wsid-m1-9999" + if _, ok := workspaceConfig.HTTP.Routers[wsid]; !ok { t.Fatal("traefik config doesn't contain expected workspace configuration") } - if len(workspaceConfig.HTTP.Routers[endpointName].Middlewares) != 2 { - t.Fatalf("Expected 2 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[endpointName].Middlewares)) + if len(workspaceConfig.HTTP.Routers[wsid].Middlewares) != 2 { + t.Fatalf("Expected 2 middlewares in router but got '%d'", len(workspaceConfig.HTTP.Routers[wsid].Middlewares)) } workspaceMainConfig := traefikConfig{} @@ -393,14 +393,14 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) { t.Fatalf("Expected 3 middlewares set but got '%d'", len(workspaceConfig.HTTP.Middlewares)) } - endpointName = "wsid" - mwares := []string{endpointName + "-auth", endpointName + "-prefix", endpointName + "-header"} + wsid = "wsid" + mwares := []string{wsid + "-auth", wsid + "-prefix", wsid + "-header"} for _, mware := range mwares { if _, ok := workspaceMainConfig.HTTP.Middlewares[mware]; !ok { t.Fatalf("traefik config doesn't set middleware '%s'", mware) } found := false - for _, r := range workspaceMainConfig.HTTP.Routers[endpointName].Middlewares { + for _, r := range workspaceMainConfig.HTTP.Routers[wsid].Middlewares { if r == mware { found = true } @@ -465,6 +465,12 @@ func TestCreateSubDomainObjects(t *testing.T) { if objs.Ingresses[0].Spec.Rules[0].Host != "wsid-1.down.on.earth" { t.Error("Expected Ingress host 'wsid-1.down.on.earth', but got ", objs.Ingresses[0].Spec.Rules[0].Host) } + if objs.Ingresses[1].Spec.Rules[0].Host != "wsid-2.down.on.earth" { + t.Error("Expected Ingress host 'wsid-2.down.on.earth', but got ", objs.Ingresses[1].Spec.Rules[0].Host) + } + if objs.Ingresses[2].Spec.Rules[0].Host != "wsid-3.down.on.earth" { + t.Error("Expected Ingress host 'wsid-3.down.on.earth', but got ", objs.Ingresses[2].Spec.Rules[0].Host) + } }) t.Run("expectedRoutes", func(t *testing.T) { From bb0bae53bf03d60920a4c4bff3ebb92b76c085c0 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Wed, 15 Sep 2021 10:47:41 +0200 Subject: [PATCH 4/6] fix multiple routes on same port Signed-off-by: Michal Vala --- .../devworkspace/solver/che_routing.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index dca038b729..ab69018f3f 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -503,7 +503,15 @@ func provisionMasterRouting(cheCluster *v2alpha1.CheCluster, routing *dwo.DevWor } func addEndpointToTraefikConfig(componentName string, e dw.Endpoint, cfg *traefikConfig, cheCluster *v2alpha1.CheCluster, routing *dwo.DevWorkspaceRouting) { - prefix := getLocalURLPrefix(componentName, e) + var routeName string + if e.Attributes.GetString(uniqueEndpointAttributeName, nil) == "true" { + // if endpoint is unique, we're exposing on /componentName/ + routeName = e.Name + } else { + // if endpoint is NOT unique, we're exposing on /componentName/ + routeName = strconv.Itoa(e.TargetPort) + } + prefix := fmt.Sprintf("/%s/%s", componentName, routeName) rulePrefix := fmt.Sprintf("PathPrefix(`%s`)", prefix) // skip if exact same route is already exposed @@ -513,10 +521,10 @@ func addEndpointToTraefikConfig(componentName string, e dw.Endpoint, cfg *traefi } } - name := fmt.Sprintf("%s-%s-%s", routing.Spec.DevWorkspaceId, componentName, strconv.Itoa(e.TargetPort)) + name := fmt.Sprintf("%s-%s-%s", routing.Spec.DevWorkspaceId, componentName, routeName) cfg.HTTP.Routers[name] = traefikConfigRouter{ Rule: rulePrefix, - Service: e.Name, + Service: name, Middlewares: calculateMiddlewares(name, false), Priority: 100, } @@ -680,14 +688,6 @@ func getPublicURLPrefix(workspaceID string, machineName string, port int32, uniq return fmt.Sprintf(uniqueEndpointURLPrefixPattern, workspaceID, machineName, uniqueEndpointName) } -func getLocalURLPrefix(componentName string, e dw.Endpoint) string { - if e.Attributes.GetString(uniqueEndpointAttributeName, nil) == "true" { - return fmt.Sprintf("/%s/%s", componentName, e.Name) - } else { - return fmt.Sprintf("/%s/%d", componentName, e.TargetPort) - } -} - func determineEndpointScheme(e dw.Endpoint) string { var scheme string if e.Protocol == "" { From 1ecbbb6a7a0eb25aedc2b062cf83126ad29a5936 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Thu, 16 Sep 2021 09:29:15 +0200 Subject: [PATCH 5/6] cleanup Signed-off-by: Michal Vala --- controllers/devworkspace/solver/che_routing.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/controllers/devworkspace/solver/che_routing.go b/controllers/devworkspace/solver/che_routing.go index ab69018f3f..53ee15b200 100644 --- a/controllers/devworkspace/solver/che_routing.go +++ b/controllers/devworkspace/solver/che_routing.go @@ -171,6 +171,8 @@ func (c *CheRoutingSolver) provisionPodAdditions(objs *solvers.RoutingObjects, c }, }) + // Even though DefaultMode is optional in Kubernetes, DevWorkspace Controller needs it to be explicitly defined. + // 420 = 0644 = '-rw-r--r--' defaultMode := int32(420) objs.PodAdditions.Volumes = append(objs.PodAdditions.Volumes, corev1.Volume{ Name: wsGatewayName, @@ -321,8 +323,9 @@ func (c *CheRoutingSolver) getInfraSpecificExposer(cheCluster *v2alpha1.CheClust } func getCommonService(objs *solvers.RoutingObjects, dwId string) *corev1.Service { + commonServiceName := common.ServiceName(dwId) for i, svc := range objs.Services { - if svc.Name == common.ServiceName(dwId) { + if svc.Name == commonServiceName { return &objs.Services[i] } } From 9966ab0f56c2e643d810cbdce710a710fa4709c2 Mon Sep 17 00:00:00 2001 From: Michal Vala Date: Thu, 16 Sep 2021 11:13:02 +0200 Subject: [PATCH 6/6] cleanup Signed-off-by: Michal Vala --- pkg/deploy/defaults.go | 10 +++++----- pkg/deploy/expose/expose.go | 2 +- pkg/deploy/gateway/gateway.go | 2 +- pkg/deploy/server/server.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/deploy/defaults.go b/pkg/deploy/defaults.go index 65318916d3..43741d27c7 100644 --- a/pkg/deploy/defaults.go +++ b/pkg/deploy/defaults.go @@ -79,9 +79,9 @@ const ( KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.4" - DefaultServerExposureStrategy = "multi-host" - DefaultKubernetesSingleHostExposureType = "native" - DefaultOpenShiftSingleHostExposureType = "gateway" + DefaultServerExposureStrategy = "multi-host" + NativeSingleHostExposureType = "native" + GatewaySingleHostExposureType = "gateway" // This is only to correctly manage defaults during the transition // from Upstream 7.0.0 GA to the next version @@ -361,10 +361,10 @@ func DefaultPullPolicyFromDockerImage(dockerImage string) string { func GetSingleHostExposureType(cr *orgv1.CheCluster) string { if util.IsOpenShift || cr.Spec.DevWorkspace.Enable { - return DefaultOpenShiftSingleHostExposureType + return GatewaySingleHostExposureType } - return util.GetValue(cr.Spec.K8s.SingleHostExposureType, DefaultKubernetesSingleHostExposureType) + return util.GetValue(cr.Spec.K8s.SingleHostExposureType, NativeSingleHostExposureType) } func patchDefaultImageName(cr *orgv1.CheCluster, imageName string) string { diff --git a/pkg/deploy/expose/expose.go b/pkg/deploy/expose/expose.go index e025d4fb1f..469e4c3c82 100644 --- a/pkg/deploy/expose/expose.go +++ b/pkg/deploy/expose/expose.go @@ -52,7 +52,7 @@ func ExposeWithHostPath( } singleHostExposureType := deploy.GetSingleHostExposureType(deployContext.CheCluster) - useGateway := exposureStrategy == "single-host" && (util.IsOpenShift || singleHostExposureType == "gateway") + useGateway := exposureStrategy == "single-host" && (util.IsOpenShift || singleHostExposureType == deploy.GatewaySingleHostExposureType) gatewayConfig := "che-gateway-route-" + component if !util.IsOpenShift { if useGateway { diff --git a/pkg/deploy/gateway/gateway.go b/pkg/deploy/gateway/gateway.go index 4cce9e766b..05144349d1 100644 --- a/pkg/deploy/gateway/gateway.go +++ b/pkg/deploy/gateway/gateway.go @@ -59,7 +59,7 @@ var ( // SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration func SyncGatewayToCluster(deployContext *deploy.DeployContext) error { if (util.GetServerExposureStrategy(deployContext.CheCluster) == "single-host" && - deploy.GetSingleHostExposureType(deployContext.CheCluster) == "gateway") || + deploy.GetSingleHostExposureType(deployContext.CheCluster) == deploy.GatewaySingleHostExposureType) || deployContext.CheCluster.Spec.DevWorkspace.Enable { return syncAll(deployContext) } diff --git a/pkg/deploy/server/server.go b/pkg/deploy/server/server.go index 3852706e40..cbda968d8e 100644 --- a/pkg/deploy/server/server.go +++ b/pkg/deploy/server/server.go @@ -333,7 +333,7 @@ func (s Server) evaluateCheServerVersion() string { } func GetServerExposingServiceName(cr *orgv1.CheCluster) string { - if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == "gateway" { + if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == deploy.GatewaySingleHostExposureType { return gateway.GatewayServiceName } return deploy.CheServiceName