From c9b6d1a6f2a890c072c13e7bfb3297827851e00b Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Wed, 11 Feb 2026 13:47:29 +0100 Subject: [PATCH 1/2] chore: Remove unnecessary entries from che configmap Signed-off-by: Anatolii Bazko --- pkg/common/constants/constants.go | 5 +- pkg/deploy/ingress.go | 6 +- pkg/deploy/proxy.go | 12 +- pkg/deploy/proxy_test.go | 8 +- pkg/deploy/server/chehost_reconciler.go | 22 +- pkg/deploy/server/chehost_reconciler_test.go | 6 +- .../{server_util.go => server_common.go} | 24 +- pkg/deploy/server/server_configmap.go | 404 ++++++++---------- pkg/deploy/server/server_configmap_test.go | 394 +++++++---------- pkg/deploy/server/server_deployment.go | 19 +- pkg/deploy/server/server_reconciler.go | 36 +- pkg/deploy/server/server_reconciler_test.go | 6 +- 12 files changed, 404 insertions(+), 538 deletions(-) rename pkg/deploy/server/{server_util.go => server_common.go} (60%) diff --git a/pkg/common/constants/constants.go b/pkg/common/constants/constants.go index dc1e93529..2572fc859 100644 --- a/pkg/common/constants/constants.go +++ b/pkg/common/constants/constants.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -42,8 +42,10 @@ const ( DefaultServerCpuLimit = "1" DefaultServerCpuRequest = "100m" DefaultServerLogLevel = "INFO" + DefaultServerDebug = false DefaultServerMetricsPort = int32(8087) DefaultServerDebugPort = int32(8000) + DefaultServerPort = int32(8080) DefaultCaBundleCertsCMName = "ca-certs" DefaultProxyCredentialsSecret = "proxy-credentials" DefaultGitSelfSignedCertsConfigMapName = "che-git-self-signed-cert" @@ -74,6 +76,7 @@ const ( AccessToken = "access_token" IdToken = "id_token" OpenShiftOAuthScope = "user:full" + BitbucketOAuth = "bitbucket" // Labels KubernetesComponentLabelKey = "app.kubernetes.io/component" diff --git a/pkg/deploy/ingress.go b/pkg/deploy/ingress.go index 46d03f4b1..d20f04d87 100644 --- a/pkg/deploy/ingress.go +++ b/pkg/deploy/ingress.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -53,7 +53,7 @@ func SyncIngressToCluster( name string, path string, serviceName string, - servicePort int, + servicePort int32, component string) (endpointUrl string, done bool, err error) { ingressUrl, ingressSpec := GetIngressSpec(deployContext, name, path, serviceName, servicePort, component) @@ -68,7 +68,7 @@ func GetIngressSpec( name string, path string, serviceName string, - servicePort int, + servicePort int32, component string) (ingressUrl string, i *networking.Ingress) { ingressDomain := deployContext.CheCluster.Spec.Networking.Domain diff --git a/pkg/deploy/proxy.go b/pkg/deploy/proxy.go index cda633003..3b8263dd1 100644 --- a/pkg/deploy/proxy.go +++ b/pkg/deploy/proxy.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -132,7 +132,7 @@ func MergeNonProxy(noProxy1 string, noProxy2 string) string { } // GenerateProxyJavaOpts converts given proxy configuration into Java format. -func GenerateProxyJavaOpts(proxy *chetypes.Proxy, noProxy string) (javaOpts string, err error) { +func GenerateProxyJavaOpts(proxy *chetypes.Proxy, noProxy string) (javaOpts string) { if noProxy == "" { noProxy = proxy.NoProxy } @@ -153,11 +153,9 @@ func GenerateProxyJavaOpts(proxy *chetypes.Proxy, noProxy string) (javaOpts stri " -Djdk.http.auth.tunneling.disabledSchemes= -Djdk.http.auth.proxying.disabledSchemes=" } - javaOpts = - " -Dhttp.proxyHost=" + removeProtocolPrefix(proxy.HttpHost) + " -Dhttp.proxyPort=" + proxy.HttpPort + - " -Dhttps.proxyHost=" + removeProtocolPrefix(proxy.HttpsHost) + " -Dhttps.proxyPort=" + proxy.HttpsPort + - " -Dhttp.nonProxyHosts='" + noProxy + "'" + proxyUserPassword - return javaOpts, nil + return " -Dhttp.proxyHost=" + removeProtocolPrefix(proxy.HttpHost) + " -Dhttp.proxyPort=" + proxy.HttpPort + + " -Dhttps.proxyHost=" + removeProtocolPrefix(proxy.HttpsHost) + " -Dhttps.proxyPort=" + proxy.HttpsPort + + " -Dhttp.nonProxyHosts='" + noProxy + "'" + proxyUserPassword } func removeProtocolPrefix(url string) string { diff --git a/pkg/deploy/proxy_test.go b/pkg/deploy/proxy_test.go index 78f0e1007..0c0fee06d 100644 --- a/pkg/deploy/proxy_test.go +++ b/pkg/deploy/proxy_test.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -48,7 +48,7 @@ func TestGenerateProxyJavaOptsWithUsernameAndPassword(t *testing.T) { logrus.Errorf("Failed to set env %s", err) } - javaOpts, _ := GenerateProxyJavaOpts(proxy, "") + javaOpts := GenerateProxyJavaOpts(proxy, "") expectedJavaOpts := " -Dhttp.proxyHost=myproxy.com -Dhttp.proxyPort=1234 -Dhttps.proxyHost=myproxy.com " + "-Dhttps.proxyPort=1234 -Dhttp.nonProxyHosts='localhost|myhost.com' -Dhttp.proxyUser=user " + "-Dhttp.proxyPassword=password -Dhttps.proxyUser=user -Dhttps.proxyPassword=password " + @@ -71,7 +71,7 @@ func TestGenerateProxyJavaOptsWithoutAuthentication(t *testing.T) { NoProxy: "localhost,myhost.com", } - javaOpts, _ := GenerateProxyJavaOpts(proxy, "test-no-proxy.com") + javaOpts := GenerateProxyJavaOpts(proxy, "test-no-proxy.com") expectedJavaOptsWithoutUsernamePassword := " -Dhttp.proxyHost=myproxy.com -Dhttp.proxyPort=1234 -Dhttps.proxyHost=myproxy.com " + "-Dhttps.proxyPort=1234 -Dhttp.nonProxyHosts='test-no-proxy.com'" @@ -92,7 +92,7 @@ func TestGenerateProxyJavaOptsWildcardInNonProxyHosts(t *testing.T) { NoProxy: ".example.com,localhost, *.wildcard.domain.com ,myhost.com , .wildcard.net , 127.* ", } - javaOpts, _ := GenerateProxyJavaOpts(proxy, "") + javaOpts := GenerateProxyJavaOpts(proxy, "") expectedJavaOptsWithoutUsernamePassword := " -Dhttp.proxyHost=myproxy.com -Dhttp.proxyPort=1234 -Dhttps.proxyHost=myproxy.com " + "-Dhttps.proxyPort=1234 -Dhttp.nonProxyHosts='*.example.com|localhost|*.wildcard.domain.com|myhost.com|*.wildcard.net|127.*'" diff --git a/pkg/deploy/server/chehost_reconciler.go b/pkg/deploy/server/chehost_reconciler.go index 8032b7661..2b97fcbc4 100644 --- a/pkg/deploy/server/chehost_reconciler.go +++ b/pkg/deploy/server/chehost_reconciler.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -52,7 +52,7 @@ func (s *CheHostReconciler) Finalize(ctx *chetypes.DeployContext) bool { func (s *CheHostReconciler) syncCheService(ctx *chetypes.DeployContext) (bool, error) { portName := []string{"http"} - portNumber := []int32{8080} + portNumber := []int32{constants.DefaultServerPort} if ctx.CheCluster.Spec.Components.Metrics.Enable { portName = append(portName, "metrics") @@ -64,7 +64,7 @@ func (s *CheHostReconciler) syncCheService(ctx *chetypes.DeployContext) (bool, e portNumber = append(portNumber, constants.DefaultServerDebugPort) } - spec := deploy.GetServiceSpec(ctx, deploy.CheServiceName, portName, portNumber, getComponentName(ctx)) + spec := deploy.GetServiceSpec(ctx, deploy.CheServiceName, portName, portNumber, getComponentName()) return deploy.Sync(ctx, spec, deploy.ServiceDefaultDiffOpts) } @@ -72,17 +72,17 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *chetypes.DeployContext) (strin if !infrastructure.IsOpenShift() { _, done, err := deploy.SyncIngressToCluster( ctx, - getComponentName(ctx), + getComponentName(), "", gateway.GatewayServiceName, - 8080, - getComponentName(ctx)) + constants.DefaultServerPort, + getComponentName()) if !done { return "", false, err } ingress := &networking.Ingress{} - exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), ingress) + exists, err := deploy.GetNamespacedObject(ctx, getComponentName(), ingress) if !exists { return "", false, err } @@ -92,17 +92,17 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *chetypes.DeployContext) (strin done, err := deploy.SyncRouteToCluster( ctx, - getComponentName(ctx), + getComponentName(), "/", gateway.GatewayServiceName, - 8080, - getComponentName(ctx)) + constants.DefaultServerPort, + getComponentName()) if !done { return "", false, err } route := &routev1.Route{} - exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), route) + exists, err := deploy.GetNamespacedObject(ctx, getComponentName(), route) if !exists { return "", false, err } diff --git a/pkg/deploy/server/chehost_reconciler_test.go b/pkg/deploy/server/chehost_reconciler_test.go index 741773f18..d660fb06f 100644 --- a/pkg/deploy/server/chehost_reconciler_test.go +++ b/pkg/deploy/server/chehost_reconciler_test.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -87,7 +87,7 @@ func TestConfiguringLabelsForRoutes(t *testing.T) { assert.Nil(t, err) route := &routev1.Route{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, route) + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: getComponentName(), Namespace: "eclipse-che"}, route) assert.Nil(t, err) assert.Equal(t, route.ObjectMeta.Labels["route"], "one") } @@ -98,6 +98,6 @@ func TestCheHostReconciler(t *testing.T) { cheHostReconciler := NewCheHostReconciler() test.EnsureReconcile(t, ctx, cheHostReconciler.Reconcile) - assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(), Namespace: "eclipse-che"}, &routev1.Route{})) assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: deploy.CheServiceName, Namespace: "eclipse-che"}, &corev1.Service{})) } diff --git a/pkg/deploy/server/server_util.go b/pkg/deploy/server/server_common.go similarity index 60% rename from pkg/deploy/server/server_util.go rename to pkg/deploy/server/server_common.go index 8d80f5199..365aa3f90 100644 --- a/pkg/deploy/server/server_util.go +++ b/pkg/deploy/server/server_common.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -22,24 +22,28 @@ import ( corev1 "k8s.io/api/core/v1" ) -func getComponentName(ctx *chetypes.DeployContext) string { +func getComponentName() string { return defaults.GetCheFlavor() } -func getOAuthConfig(ctx *chetypes.DeployContext, oauthProvider string) (*corev1.Secret, error) { - secrets, err := deploy.GetSecrets(ctx, map[string]string{ - constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, - constants.KubernetesComponentLabelKey: constants.OAuthScmConfiguration, - }, map[string]string{ - constants.CheEclipseOrgOAuthScmServer: oauthProvider, - }) +func getOAuthConfigSecret(ctx *chetypes.DeployContext, oauthProvider string) (*corev1.Secret, error) { + secrets, err := deploy.GetSecrets( + ctx, + map[string]string{ + constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg, + constants.KubernetesComponentLabelKey: constants.OAuthScmConfiguration, + }, + map[string]string{ + constants.CheEclipseOrgOAuthScmServer: oauthProvider, + }, + ) if err != nil { return nil, err } else if len(secrets) == 0 { return nil, nil } else if len(secrets) > 1 { - return nil, fmt.Errorf("More than 1 OAuth %s configuration secrets found", oauthProvider) + return nil, fmt.Errorf("more than 1 OAuth %s configuration secrets found", oauthProvider) } return &secrets[0], nil diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go index 710121e84..f0d12b445 100644 --- a/pkg/deploy/server/server_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -13,171 +13,141 @@ package server import ( + "context" "encoding/json" "fmt" + "maps" + "slices" + "sort" "strconv" "strings" "github.com/devfile/devworkspace-operator/pkg/infrastructure" "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/common/constants" + "github.com/eclipse-che/che-operator/pkg/common/diffs" + k8sclient "github.com/eclipse-che/che-operator/pkg/common/k8s-client" defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/common/utils" "github.com/eclipse-che/che-operator/pkg/deploy" - deploytls "github.com/eclipse-che/che-operator/pkg/deploy/tls" - - "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" -) - -const ( - CheConfigMapName = "che" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" ) type CheConfigMap struct { - CheHost string `json:"CHE_HOST"` - CheMultiUser string `json:"CHE_MULTIUSER"` - ChePort string `json:"CHE_PORT"` - CheApi string `json:"CHE_API"` - CheApiInternal string `json:"CHE_API_INTERNAL"` - CheWebSocketEndpoint string `json:"CHE_WEBSOCKET_ENDPOINT"` - CheWebSocketInternalEndpoint string `json:"CHE_WEBSOCKET_INTERNAL_ENDPOINT"` - CheDebugServer string `json:"CHE_DEBUG_SERVER"` - CheMetricsEnabled string `json:"CHE_METRICS_ENABLED"` - CheInfrastructureActive string `json:"CHE_INFRASTRUCTURE_ACTIVE"` - CheInfraKubernetesServiceAccountName string `json:"CHE_INFRA_KUBERNETES_SERVICE__ACCOUNT__NAME"` - CheInfraKubernetesUserClusterRoles string `json:"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"` - DefaultTargetNamespace string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"` - NamespaceCreationAllowed string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_CREATION__ALLOWED"` - PvcStrategy string `json:"CHE_INFRA_KUBERNETES_PVC_STRATEGY"` - PvcClaimSize string `json:"CHE_INFRA_KUBERNETES_PVC_QUANTITY"` - WorkspacePvcStorageClassName string `json:"CHE_INFRA_KUBERNETES_PVC_STORAGE__CLASS__NAME"` - TlsSupport string `json:"CHE_INFRA_OPENSHIFT_TLS__ENABLED"` - K8STrustCerts string `json:"CHE_INFRA_KUBERNETES_TRUST__CERTS"` - CheLogLevel string `json:"CHE_LOG_LEVEL"` - IdentityProviderUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"` - IdentityProviderInternalURL string `json:"CHE_OIDC_AUTH__INTERNAL__SERVER__URL,omitempty"` - OpenShiftIdentityProvider string `json:"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"` - JavaOpts string `json:"JAVA_OPTS"` - PluginRegistryUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__URL,omitempty"` - PluginRegistryInternalUrl string `json:"CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL,omitempty"` - CheJGroupsKubernetesLabels string `json:"KUBERNETES_LABELS,omitempty"` - CheTrustedCABundlesConfigMap string `json:"CHE_TRUSTED__CA__BUNDLES__CONFIGMAP,omitempty"` - ServerStrategy string `json:"CHE_INFRA_KUBERNETES_SERVER__STRATEGY"` - WorkspaceExposure string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_WORKSPACE_EXPOSURE"` - SingleHostGatewayConfigMapLabels string `json:"CHE_INFRA_KUBERNETES_SINGLEHOST_GATEWAY_CONFIGMAP__LABELS"` - CheDevWorkspacesEnabled string `json:"CHE_DEVWORKSPACES_ENABLED"` - Http2Disable string `json:"HTTP2_DISABLE"` + JavaOpts string `json:"JAVA_OPTS"` + CheHost string `json:"CHE_HOST"` + ChePort string `json:"CHE_PORT"` + CheDebugServer string `json:"CHE_DEBUG_SERVER"` + CheLogLevel string `json:"CHE_LOG_LEVEL"` + CheMetricsEnabled string `json:"CHE_METRICS_ENABLED"` + CheInfrastructure string `json:"CHE_INFRASTRUCTURE_ACTIVE"` + UserClusterRoles string `json:"CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"` + NamespaceDefault string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"` + NamespaceCreationAllowed string `json:"CHE_INFRA_KUBERNETES_NAMESPACE_CREATION__ALLOWED"` + Http2Disable string `json:"HTTP2_DISABLE"` + KubernetesLabels string `json:"KUBERNETES_LABELS"` + + // TODO remove when keycloak codebase is removed from che-server component + CheOIDCAuthServerUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"` } -// GetCheConfigMapData gets env values from CR spec and returns a map with key:value -// which is used in CheCluster ConfigMap to configure CheCluster master behavior -func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (cheEnv map[string]string, err error) { - identityProviderURL := ctx.CheCluster.Spec.Networking.Auth.IdentityProviderURL - - infra := "kubernetes" - openShiftIdentityProviderId := "NULL" - if infrastructure.IsOpenShift() { - infra = "openshift" - openShiftIdentityProviderId = "openshift-v4" +func (s *CheServerReconciler) syncConfigMap(ctx *chetypes.DeployContext) (bool, error) { + data, err := s.getConfigMapData(ctx) + if err != nil { + return false, err } - proxyJavaOpts := "" - cheWorkspaceNoProxy := ctx.Proxy.NoProxy - if ctx.Proxy.HttpProxy != "" { - proxyJavaOpts, err = deploy.GenerateProxyJavaOpts(ctx.Proxy, cheWorkspaceNoProxy) - if err != nil { - logrus.Errorf("Failed to generate java proxy options: %v", err) - } + cm := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: ctx.CheCluster.Namespace, + Labels: deploy.GetLabels(getComponentName()), + }, + Data: data, } - ingressDomain := ctx.CheCluster.Spec.Networking.Domain - tlsSecretName := ctx.CheCluster.Spec.Networking.TlsSecretName + err = ctx.ClusterAPI.ClientWrapper.Sync( + context.TODO(), + cm, + &k8sclient.SyncOptions{DiffOpts: diffs.ConfigMapAllLabels}, + ) - securityContextFsGroup := strconv.FormatInt(constants.DefaultSecurityContextFsGroup, 10) - securityContextRunAsUser := strconv.FormatInt(constants.DefaultSecurityContextRunAsUser, 10) - if ctx.CheCluster.Spec.Components.CheServer.Deployment != nil { - if ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext != nil { - if ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup != nil { - securityContextFsGroup = strconv.FormatInt(*ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.FsGroup, 10) - } - if ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser != nil { - securityContextRunAsUser = strconv.FormatInt(*ctx.CheCluster.Spec.Components.CheServer.Deployment.SecurityContext.RunAsUser, 10) - } - } + return err == nil, err +} + +func (s *CheServerReconciler) getConfigMapRevision(ctx *chetypes.DeployContext) (string, error) { + cm := &corev1.ConfigMap{} + + if exist, err := ctx.ClusterAPI.ClientWrapper.GetIgnoreNotFound( + context.TODO(), + types.NamespacedName{ + Namespace: ctx.CheCluster.Namespace, + Name: configMapName, + }, + cm, + ); !exist { + return "", err } - ingressClass := utils.GetValue(ctx.CheCluster.Spec.Networking.Annotations["kubernetes.io/ingress.class"], constants.DefaultIngressClass) + return cm.ResourceVersion, nil +} - pluginRegistryURL := ctx.CheCluster.Status.PluginRegistryURL - for _, r := range ctx.CheCluster.Spec.Components.PluginRegistry.ExternalPluginRegistries { - if strings.Index(pluginRegistryURL, r.Url) == -1 { - pluginRegistryURL += " " + r.Url - } +func (s *CheServerReconciler) getConfigMapData(ctx *chetypes.DeployContext) (cheEnv map[string]string, err error) { + var cheInfrastructure string + if infrastructure.IsOpenShift() { + cheInfrastructure = "openshift" + } else { + cheInfrastructure = "kubernetes" } - pluginRegistryURL = strings.TrimSpace(pluginRegistryURL) - cheLogLevel := utils.GetValue(ctx.CheCluster.Spec.Components.CheServer.LogLevel, constants.DefaultServerLogLevel) - cheDebug := "false" - if ctx.CheCluster.Spec.Components.CheServer.Debug != nil { - cheDebug = strconv.FormatBool(*ctx.CheCluster.Spec.Components.CheServer.Debug) + javaOpts := constants.DefaultJavaOpts + if ctx.Proxy.HttpProxy != "" { + javaOpts += deploy.GenerateProxyJavaOpts(ctx.Proxy, ctx.Proxy.NoProxy) } - cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Components.Metrics.Enable) - cheLabels := labels.FormatLabels(deploy.GetLabels(defaults.GetCheFlavor())) - singleHostGatewayConfigMapLabels := "" - if len(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels) != 0 { - singleHostGatewayConfigMapLabels = labels.FormatLabels(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels) - } else { - singleHostGatewayConfigMapLabels = labels.FormatLabels(constants.DefaultSingleHostGatewayConfigMapLabels) + cheLogLevel := utils.GetValue( + ctx.CheCluster.Spec.Components.CheServer.LogLevel, + constants.DefaultServerLogLevel, + ) - } - workspaceNamespaceDefault := ctx.CheCluster.GetDefaultNamespace() - namespaceCreationAllowed := strconv.FormatBool(constants.DefaultAutoProvision) - if ctx.CheCluster.Spec.DevEnvironments.DefaultNamespace.AutoProvision != nil { - namespaceCreationAllowed = strconv.FormatBool(*ctx.CheCluster.Spec.DevEnvironments.DefaultNamespace.AutoProvision) - } + cheDebugServer := strconv.FormatBool( + pointer.BoolDeref( + ctx.CheCluster.Spec.Components.CheServer.Debug, + constants.DefaultServerDebug, + )) - cheAPI := "https://" + ctx.CheHost + "/api" - var pluginRegistryInternalURL string + chePort := strconv.Itoa(int(constants.DefaultServerPort)) + cheMetricsEnabled := strconv.FormatBool(ctx.CheCluster.Spec.Components.Metrics.Enable) - if !ctx.CheCluster.IsInternalPluginRegistryDisabled() { - pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", constants.PluginRegistryName, ctx.CheCluster.Namespace) - } + namespaceDefault := ctx.CheCluster.GetDefaultNamespace() + namespaceCreationAllowed := strconv.FormatBool( + pointer.BoolDeref( + ctx.CheCluster.Spec.DevEnvironments.DefaultNamespace.AutoProvision, + constants.DefaultAutoProvision, + )) - cheInternalAPI := fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace) - webSocketInternalEndpoint := fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, ctx.CheCluster.Namespace) - webSocketEndpoint := "wss://" + ctx.CheHost + "/api/websocket" - cheWorkspaceServiceAccount := "NULL" + kubernetesLabels := labels.FormatLabels(deploy.GetLabels(defaults.GetCheFlavor())) data := &CheConfigMap{ - CheMultiUser: "true", - CheHost: ctx.CheHost, - ChePort: "8080", - CheApi: cheAPI, - CheApiInternal: cheInternalAPI, - CheWebSocketEndpoint: webSocketEndpoint, - CheWebSocketInternalEndpoint: webSocketInternalEndpoint, - CheDebugServer: cheDebug, - CheInfrastructureActive: infra, - CheInfraKubernetesServiceAccountName: cheWorkspaceServiceAccount, - DefaultTargetNamespace: workspaceNamespaceDefault, - NamespaceCreationAllowed: namespaceCreationAllowed, - TlsSupport: "true", - K8STrustCerts: "true", - CheLogLevel: cheLogLevel, - OpenShiftIdentityProvider: openShiftIdentityProviderId, - JavaOpts: constants.DefaultJavaOpts + " " + proxyJavaOpts, - PluginRegistryUrl: pluginRegistryURL, - PluginRegistryInternalUrl: pluginRegistryInternalURL, - CheJGroupsKubernetesLabels: cheLabels, - CheMetricsEnabled: cheMetrics, - CheTrustedCABundlesConfigMap: deploytls.CheMergedCABundleCertsCMName, - ServerStrategy: "single-host", - WorkspaceExposure: "gateway", - SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels, - CheDevWorkspacesEnabled: strconv.FormatBool(true), + JavaOpts: javaOpts, + CheHost: ctx.CheHost, + ChePort: chePort, + CheDebugServer: cheDebugServer, + CheLogLevel: cheLogLevel, + CheMetricsEnabled: cheMetricsEnabled, + CheInfrastructure: cheInfrastructure, + CheOIDCAuthServerUrl: ctx.CheCluster.Spec.Networking.Auth.IdentityProviderURL, + NamespaceDefault: namespaceDefault, + NamespaceCreationAllowed: namespaceCreationAllowed, + KubernetesLabels: kubernetesLabels, // Disable HTTP2 protocol. // Fix issue with creating config maps on the cluster https://issues.redhat.com/browse/CRW-2677 // The root cause is in the HTTP2 protocol support of the okttp3 library that is used by fabric8.kubernetes-client that is used by che-server @@ -185,119 +155,125 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) ( Http2Disable: strconv.FormatBool(true), } - data.IdentityProviderUrl = identityProviderURL - out, err := json.Marshal(data) if err != nil { - fmt.Println(err) + return nil, err } - err = json.Unmarshal(out, &cheEnv) - - // Advanced authorization - if ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization != nil { - cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_ALLOW__USERS"] = strings.Join(ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.AllowUsers, ",") - cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_ALLOW__GROUPS"] = strings.Join(ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.AllowGroups, ",") - cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_DENY__USERS"] = strings.Join(ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.DenyUsers, ",") - cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_DENY__GROUPS"] = strings.Join(ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.DenyGroups, ",") - } - // k8s specific envs - if !infrastructure.IsOpenShift() { - k8sCheEnv := map[string]string{ - "CHE_INFRA_KUBERNETES_POD_SECURITY__CONTEXT_FS__GROUP": securityContextFsGroup, - "CHE_INFRA_KUBERNETES_POD_SECURITY__CONTEXT_RUN__AS__USER": securityContextRunAsUser, - "CHE_INFRA_KUBERNETES_INGRESS_DOMAIN": ingressDomain, - "CHE_INFRA_KUBERNETES_TLS__SECRET": tlsSecretName, - "CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON": "{\"kubernetes.io/ingress.class\": " + ingressClass + ", \"nginx.ingress.kubernetes.io/rewrite-target\": \"/$1\",\"nginx.ingress.kubernetes.io/ssl-redirect\": \"true\",\"nginx.ingress.kubernetes.io/proxy-connect-timeout\": \"3600\",\"nginx.ingress.kubernetes.io/proxy-read-timeout\": \"3600\"}", - "CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM": "%s(.*)", - } - k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true" - utils.AddMap(cheEnv, k8sCheEnv) + err = json.Unmarshal(out, &cheEnv) + if err != nil { + return nil, err } - // 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 tlsSecretName != "" { - cheTLSSecret := &corev1.Secret{} - exists, err := deploy.GetNamespacedObject(ctx, tlsSecretName, cheTLSSecret) - if err != nil { - return nil, err - } - if !exists { - return nil, fmt.Errorf("%s secret not found", tlsSecretName) - } else { - if _, exists := cheTLSSecret.Data["tls.key"]; !exists { - return nil, fmt.Errorf("%s secret has no 'tls.key' key in data", tlsSecretName) - } - if _, exists := cheTLSSecret.Data["tls.crt"]; !exists { - return nil, fmt.Errorf("%s secret has no 'tls.crt' key in data", tlsSecretName) - } - cheEnv["CHE_INFRA_KUBERNETES_TLS__KEY"] = string(cheTLSSecret.Data["tls.key"]) - cheEnv["CHE_INFRA_KUBERNETES_TLS__CERT"] = string(cheTLSSecret.Data["tls.crt"]) - } - } + // override envs by extra properties + maps.Copy(cheEnv, ctx.CheCluster.Spec.Components.CheServer.ExtraProperties) - utils.AddMap(cheEnv, ctx.CheCluster.Spec.Components.CheServer.ExtraProperties) + // Updates `CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION__<...>` + s.updateAdvancedAuthorizationEnv(ctx, cheEnv) - s.updateUserClusterRoles(ctx, cheEnv) + // Updates `CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES` + s.updateUserClusterRoleEnv(ctx, cheEnv) - for _, oauthProvider := range []string{"bitbucket", constants.AzureDevOpsOAuth} { - err := s.updateIntegrationServerEndpoints(ctx, cheEnv, oauthProvider) - if err != nil { - return nil, err - } + // Update `CHE_INTEGRATION_<...>_SERVER__ENDPOINTS` + if err := s.updateServerEndpointsEnv(ctx, cheEnv); err != nil { + return nil, err } return cheEnv, nil } -func (s *CheServerReconciler) updateIntegrationServerEndpoints(ctx *chetypes.DeployContext, cheEnv map[string]string, oauthProvider string) error { - secret, err := getOAuthConfig(ctx, oauthProvider) - if secret == nil { - return err +func (s *CheServerReconciler) updateAdvancedAuthorizationEnv(ctx *chetypes.DeployContext, cheEnv map[string]string) { + if ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization != nil { + cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_ALLOW__USERS"] = strings.Join( + ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.AllowUsers, + ",", + ) + cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_DENY__USERS"] = strings.Join( + ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.DenyUsers, + ",", + ) + cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_ALLOW__GROUPS"] = strings.Join( + ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.AllowGroups, + ",", + ) + cheEnv["CHE_INFRA_KUBERNETES_ADVANCED__AUTHORIZATION_DENY__GROUPS"] = strings.Join( + ctx.CheCluster.Spec.Networking.Auth.AdvancedAuthorization.DenyGroups, + ",", + ) } +} - envName := fmt.Sprintf("CHE_INTEGRATION_%s_SERVER__ENDPOINTS", strings.ReplaceAll(strings.ToUpper(oauthProvider), "-", "_")) - if err != nil { - return err +func (s *CheServerReconciler) updateUserClusterRoleEnv(ctx *chetypes.DeployContext, cheEnv map[string]string) { + userClusterRolesSet := map[string]bool{} + + for _, role := range s.getDefaultUserClusterRoles(ctx) { + userClusterRolesSet[role] = true } - if cheEnv[envName] != "" { - cheEnv[envName] = secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] + "," + cheEnv[envName] - } else { - cheEnv[envName] = secret.Annotations[constants.CheEclipseOrgScmServerEndpoint] + for _, role := range strings.Split(cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"], ",") { + role = strings.TrimSpace(role) + if role != "" { + userClusterRolesSet[strings.TrimSpace(role)] = true + } } - return nil -} -func GetCheConfigMapVersion(deployContext *chetypes.DeployContext) string { - cheConfigMap := &corev1.ConfigMap{} - exists, _ := deploy.GetNamespacedObject(deployContext, CheConfigMapName, cheConfigMap) - if exists { - return cheConfigMap.ResourceVersion + if ctx.CheCluster.Spec.DevEnvironments.User != nil { + for _, role := range ctx.CheCluster.Spec.DevEnvironments.User.ClusterRoles { + role = strings.TrimSpace(role) + if role != "" { + userClusterRolesSet[strings.TrimSpace(role)] = true + } + } } - return "" + + userClusterRoles := slices.Collect(maps.Keys(userClusterRolesSet)) + sort.Strings(userClusterRoles) + + cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"] = strings.Join(userClusterRoles, ",") } -func (s *CheServerReconciler) updateUserClusterRoles(ctx *chetypes.DeployContext, cheEnv map[string]string) { - userClusterRoles := strings.Join(s.getDefaultUserClusterRoles(ctx), ", ") +func (s *CheServerReconciler) updateServerEndpointsEnv(ctx *chetypes.DeployContext, cheEnv map[string]string) error { + // https://github.com/eclipse-che/che-operator/pull/1250 + oAuthProviders := []string{constants.BitbucketOAuth, constants.AzureDevOpsOAuth} - for _, role := range strings.Split(cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"], ",") { - role := strings.TrimSpace(role) - if !strings.Contains(userClusterRoles, role) { - userClusterRoles = userClusterRoles + ", " + role + for _, oauthProvider := range oAuthProviders { + secret, err := getOAuthConfigSecret(ctx, oauthProvider) + if err != nil { + return err + } else if secret == nil { + continue } - } - if ctx.CheCluster.Spec.DevEnvironments.User != nil { - for _, role := range ctx.CheCluster.Spec.DevEnvironments.User.ClusterRoles { - role := strings.TrimSpace(role) - if !strings.Contains(userClusterRoles, role) { - userClusterRoles = userClusterRoles + ", " + role + envName := fmt.Sprintf( + "CHE_INTEGRATION_%s_SERVER__ENDPOINTS", + strings.ReplaceAll( + strings.ToUpper(oauthProvider), + "-", + "_", + ), + ) + + // make endpoints uniq and sorted + endpointsSet := map[string]bool{} + + for _, endpointsStr := range []string{ + secret.Annotations[constants.CheEclipseOrgScmServerEndpoint], + cheEnv[envName], + } { + for _, endpoint := range strings.Split(endpointsStr, ",") { + endpoint = strings.TrimSpace(endpoint) + if endpoint != "" { + endpointsSet[strings.TrimSpace(endpoint)] = true + } } } + + endpoints := slices.Collect(maps.Keys(endpointsSet)) + sort.Strings(endpoints) + + cheEnv[envName] = strings.Join(endpoints, ",") } - cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"] = userClusterRoles + return nil } diff --git a/pkg/deploy/server/server_configmap_test.go b/pkg/deploy/server/server_configmap_test.go index edc7d17d5..54b2ebf0a 100644 --- a/pkg/deploy/server/server_configmap_test.go +++ b/pkg/deploy/server/server_configmap_test.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -15,10 +15,11 @@ package server import ( "testing" + defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" + "github.com/eclipse-che/che-operator/pkg/deploy" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" - "k8s.io/utils/pointer" - "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -28,39 +29,60 @@ import ( chev2 "github.com/eclipse-che/che-operator/api/v2" ) -func TestNewCheConfigMap(t *testing.T) { +func TestGetConfigMapData(t *testing.T) { type testCase struct { name string - initObjects []runtime.Object cheCluster *chev2.CheCluster + initObjects []runtime.Object expectedData map[string]string } testCases := []testCase{ { - name: "Test", + name: "Test defaults", initObjects: []runtime.Object{}, cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", + Name: "eclipse-che", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ CheServer: chev2.CheServer{ ExtraProperties: map[string]string{ - "CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com", + "EXTRA_PROPERTY": "extra-value", }, + Proxy: &chev2.Proxy{ + Url: "http://127.0.0.1", + Port: "8080", + }, + }, + }, + Networking: chev2.CheClusterSpecNetworking{ + Auth: chev2.Auth{ + IdentityProviderURL: "http://identity-provider", }, }, }, Status: chev2.CheClusterStatus{ - CheURL: "https://che-host", + CheURL: "https://che.che", }, }, expectedData: map[string]string{ - "CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER": "openshift-v4", - "CHE_API": "https://che-host/api", - "CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com", + "EXTRA_PROPERTY": "extra-value", + "JAVA_OPTS": "-XX:MaxRAMPercentage=85.0 -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=8080 -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=''", + "CHE_HOST": "che.che", + "CHE_PORT": "8080", + "CHE_DEBUG_SERVER": "false", + "CHE_LOG_LEVEL": "INFO", + "CHE_METRICS_ENABLED": "false", + "CHE_INFRASTRUCTURE_ACTIVE": "openshift", + "CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES": "eclipse-che-cheworkspaces-clusterrole,eclipse-che-cheworkspaces-devworkspace-clusterrole", + "CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "-" + defaults.GetCheFlavor(), + "CHE_INFRA_KUBERNETES_NAMESPACE_CREATION__ALLOWED": "true", + "KUBERNETES_LABELS": labels.FormatLabels(deploy.GetLabels(defaults.GetCheFlavor())), + "HTTP2_DISABLE": "true", + "CHE_OIDC_AUTH__SERVER__URL": "http://identity-provider", }, }, } @@ -68,16 +90,23 @@ func TestNewCheConfigMap(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).Build() + ctx.Proxy.HttpProxy = "http://127.0.0.1:8080" + ctx.Proxy.HttpHost = "127.0.0.1" + ctx.Proxy.HttpPort = "8080" + ctx.Proxy.HttpsProxy = "http://127.0.0.1:8080" + ctx.Proxy.HttpsHost = "127.0.0.1" + ctx.Proxy.HttpsPort = "8080" - server := NewCheServerReconciler() - actualData, err := server.getCheConfigMapData(ctx) - assert.Nil(t, err) - test.ValidateContainData(actualData, testCase.expectedData, t) + serverReconciler := NewCheServerReconciler() + actualData, err := serverReconciler.getConfigMapData(ctx) + + assert.NoError(t, err) + assert.Equal(t, testCase.expectedData, actualData) }) } } -func TestConfigMap(t *testing.T) { +func TestGetConfigMapDataWithServerEndpoints(t *testing.T) { type testCase struct { name string initObjects []client.Object @@ -87,126 +116,81 @@ func TestConfigMap(t *testing.T) { testCases := []testCase{ { - name: "Test k8s data, no tls secret", - initObjects: []client.Object{}, - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: chev2.CheClusterSpec{ - DevEnvironments: chev2.CheClusterDevEnvironments{ - DefaultNamespace: chev2.DefaultNamespace{ - Template: "-che", - }, - }, - }, - }, - expectedData: map[string]string{ - "CHE_INFRA_KUBERNETES_TLS__CERT": "", - "CHE_INFRA_KUBERNETES_TLS__KEY": "", - }, - }, - { - name: "Test k8s data, with tls secret", + name: "Test use endpoint from secret", initObjects: []client.Object{ &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, ObjectMeta: metav1.ObjectMeta{ - Name: "che-tls", + Name: "bitbucket-oauth-config", Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "bitbucket", + "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint", + }, }, - Data: map[string][]byte{ - "tls.crt": []byte("CRT"), - "tls.key": []byte("KEY"), - }, - }, - }, - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", }, - Spec: chev2.CheClusterSpec{ - DevEnvironments: chev2.CheClusterDevEnvironments{ - DefaultNamespace: chev2.DefaultNamespace{ - Template: "-che", - }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", }, - Networking: chev2.CheClusterSpecNetworking{ - TlsSecretName: "che-tls", + ObjectMeta: metav1.ObjectMeta{ + Name: "azure-devops-oauth-config", + Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "azure-devops", + "che.eclipse.org/scm-server-endpoint": "azure-devops_endpoint", + }, }, }, }, - expectedData: map[string]string{ - "CHE_INFRA_KUBERNETES_TLS__CERT": "CRT", - "CHE_INFRA_KUBERNETES_TLS__KEY": "KEY", - }, - }, - { - name: "Test k8s data, check public url when internal network enabled.", cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: "eclipse-che", Namespace: "eclipse-che", - }, - Status: chev2.CheClusterStatus{ - CheURL: "https://che-host", - }, - }, - expectedData: map[string]string{ - "CHE_WEBSOCKET_ENDPOINT": "wss://che-host/api/websocket", - }, - }, - { - name: "Test k8s data, with internal cluster svc names", - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", - Namespace: "eclipse-che", }, }, expectedData: map[string]string{ - "CHE_WEBSOCKET_INTERNAL_ENDPOINT": "ws://che-host.eclipse-che.svc:8080/api/websocket", + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint", }, }, { - name: "Test k8s data, without internal cluster svc names", + name: "Test use endpoint from extra properties", + initObjects: []client.Object{}, cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: "eclipse-che", Namespace: "eclipse-che", }, - Status: chev2.CheClusterStatus{ - CheURL: "https://che-host", + Spec: chev2.CheClusterSpec{ + Components: chev2.CheClusterComponents{ + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint", + }, + }, + }, }, }, expectedData: map[string]string{ - "CHE_WEBSOCKET_ENDPOINT": "wss://che-host/api/websocket", + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint", }, }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).WithObjects(testCase.initObjects...).Build() - - server := NewCheServerReconciler() - actualData, err := server.getCheConfigMapData(ctx) - assert.Nil(t, err) - test.ValidateContainData(actualData, testCase.expectedData, t) - }) - } -} - -func TestUpdateIntegrationServerEndpoints(t *testing.T) { - type testCase struct { - name string - initObjects []client.Object - cheCluster *chev2.CheCluster - expectedData map[string]string - } - - testCases := []testCase{ { - name: "Test set BitBucket endpoints from secret", + name: "Test duplicate endpoints", initObjects: []client.Object{ &corev1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -214,7 +198,7 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "github-oauth-config", + Name: "bitbucket-oauth-config", Namespace: "eclipse-che", Labels: map[string]string{ "app.kubernetes.io/part-of": "che.eclipse.org", @@ -222,39 +206,25 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { }, Annotations: map[string]string{ "che.eclipse.org/oauth-scm-server": "bitbucket", - "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint_2", + "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint", }, }, }, - }, - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - }, - }, - expectedData: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_2", - }, - }, - { - name: "Test update BitBucket endpoints", - initObjects: []client.Object{ &corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "github-oauth-config", + Name: "azure-devops-oauth-config", Namespace: "eclipse-che", Labels: map[string]string{ "app.kubernetes.io/part-of": "che.eclipse.org", "app.kubernetes.io/component": "oauth-scm-configuration", }, Annotations: map[string]string{ - "che.eclipse.org/oauth-scm-server": "bitbucket", - "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint_2", + "che.eclipse.org/oauth-scm-server": "azure-devops", + "che.eclipse.org/scm-server-endpoint": "azure-devops_endpoint", }, }, }, @@ -268,103 +238,77 @@ func TestUpdateIntegrationServerEndpoints(t *testing.T) { Components: chev2.CheClusterComponents{ CheServer: chev2.CheServer{ ExtraProperties: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint", }, }, }, }, }, expectedData: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_2,bitbucket_endpoint_1", + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint", }, }, { - name: "Test don't update BitBucket endpoints", - initObjects: []client.Object{}, - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: chev2.CheClusterSpec{ - Components: chev2.CheClusterComponents{ - CheServer: chev2.CheServer{ - ExtraProperties: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", - }, + name: "Test update endpoints", + initObjects: []client.Object{ + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bitbucket-oauth-config", + Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "bitbucket", + "che.eclipse.org/scm-server-endpoint": "bitbucket_endpoint_1", }, }, }, - }, - expectedData: map[string]string{ - "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1", - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).WithObjects(testCase.initObjects...).Build() - - server := NewCheServerReconciler() - actualData, err := server.getCheConfigMapData(ctx) - assert.Nil(t, err) - test.ValidateContainData(actualData, testCase.expectedData, t) - }) - } -} - -func TestShouldSetUpCorrectlyPluginRegistryURL(t *testing.T) { - type testCase struct { - name string - initObjects []client.Object - cheCluster *chev2.CheCluster - expectedData map[string]string - } - - testCases := []testCase{ - { - name: "Test #1", - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: chev2.CheClusterSpec{ - Components: chev2.CheClusterComponents{ - PluginRegistry: chev2.PluginRegistry{ - DisableInternalRegistry: true, - ExternalPluginRegistries: []chev2.ExternalPluginRegistry{ - {Url: "external-plugin-registry"}, - }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "azure-devops-oauth-config", + Namespace: "eclipse-che", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "che.eclipse.org", + "app.kubernetes.io/component": "oauth-scm-configuration", + }, + Annotations: map[string]string{ + "che.eclipse.org/oauth-scm-server": "azure-devops", + "che.eclipse.org/scm-server-endpoint": "azure-devops_endpoint_1", }, }, }, }, - expectedData: map[string]string{ - "CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL": "", - "CHE_WORKSPACE_PLUGIN__REGISTRY__URL": "external-plugin-registry", - }, - }, - { - name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #2", cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: "eclipse-che", + Name: "eclipse-che", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ - PluginRegistry: chev2.PluginRegistry{ - DisableInternalRegistry: false, - OpenVSXURL: pointer.String(""), + CheServer: chev2.CheServer{ + ExtraProperties: map[string]string{ + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_2", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint_2", + }, }, }, }, - Status: chev2.CheClusterStatus{ - PluginRegistryURL: "internal-plugin-registry", - }, }, expectedData: map[string]string{ - "CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL": "http://plugin-registry.eclipse-che.svc:8080/v3", - "CHE_WORKSPACE_PLUGIN__REGISTRY__URL": "internal-plugin-registry", + "CHE_INTEGRATION_BITBUCKET_SERVER__ENDPOINTS": "bitbucket_endpoint_1,bitbucket_endpoint_2", + "CHE_INTEGRATION_AZURE_DEVOPS_SERVER__ENDPOINTS": "azure-devops_endpoint_1,azure-devops_endpoint_2", }, }, } @@ -372,55 +316,17 @@ func TestShouldSetUpCorrectlyPluginRegistryURL(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).WithObjects(testCase.initObjects...).Build() + serverReconciler := NewCheServerReconciler() - server := NewCheServerReconciler() - actualData, err := server.getCheConfigMapData(ctx) - assert.Nil(t, err) - test.ValidateContainData(actualData, testCase.expectedData, t) - }) - } -} - -func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) { - type testCase struct { - name string - initObjects []runtime.Object - cheCluster *chev2.CheCluster - expectedData map[string]string - } - - testCases := []testCase{ - { - name: "Should use internal che-server url, when internal network is enabled", - cheCluster: &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - }, - Spec: chev2.CheClusterSpec{ - Networking: chev2.CheClusterSpecNetworking{ - Hostname: "che-host", - }, - }, - }, - expectedData: map[string]string{ - "CHE_API_INTERNAL": "http://che-host.eclipse-che.svc:8080/api", - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).Build() + actualData, err := serverReconciler.getConfigMapData(ctx) - server := NewCheServerReconciler() - actualData, err := server.getCheConfigMapData(ctx) assert.Nil(t, err) test.ValidateContainData(actualData, testCase.expectedData, t) }) } } -func TestUpdateUserClusterRoles(t *testing.T) { +func TestGetConfigMapDataWithUserClusterRoles(t *testing.T) { type testCase struct { name string cheCluster *chev2.CheCluster @@ -429,17 +335,17 @@ func TestUpdateUserClusterRoles(t *testing.T) { testCases := []testCase{ { - name: "Test #1", + name: "Test defaults roles", cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: "eclipse-che", }, }, - expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole", + expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole,eclipse-che-cheworkspaces-devworkspace-clusterrole", }, { - name: "Test #2", + name: "Test additional roles #1", cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", @@ -455,10 +361,10 @@ func TestUpdateUserClusterRoles(t *testing.T) { }, }, }, - expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole, test-roles-1, test-roles-2", + expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole,eclipse-che-cheworkspaces-devworkspace-clusterrole,test-roles-1,test-roles-2", }, { - name: "Test #3", + name: "Test additional roles #2", cheCluster: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", @@ -481,16 +387,16 @@ func TestUpdateUserClusterRoles(t *testing.T) { }, }, }, - expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole, eclipse-che-cheworkspaces-devworkspace-clusterrole, test-roles-1, test-roles-2, test-roles-3", + expectedUserClusterRoles: "eclipse-che-cheworkspaces-clusterrole,eclipse-che-cheworkspaces-devworkspace-clusterrole,test-roles-1,test-roles-2,test-roles-3", }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { ctx := test.NewCtxBuilder().WithCheCluster(testCase.cheCluster).Build() + serverReconciler := NewCheServerReconciler() - reconciler := NewCheServerReconciler() - cheEnv, err := reconciler.getCheConfigMapData(ctx) + cheEnv, err := serverReconciler.getConfigMapData(ctx) assert.NoError(t, err) assert.Equal(t, testCase.expectedUserClusterRoles, cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"]) diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go index 59a987484..c0b89b97a 100644 --- a/pkg/deploy/server/server_deployment.go +++ b/pkg/deploy/server/server_deployment.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -36,7 +36,10 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap return nil, err } - cmResourceVersions := GetCheConfigMapVersion(ctx) + cmResourceVersions, err := s.getConfigMapRevision(ctx) + if err != nil { + return nil, err + } cmResourceVersions += "," + tls.GetAdditionalCACertsConfigMapVersion(ctx) terminationGracePeriodSeconds := int64(30) @@ -129,12 +132,12 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap Ports: []corev1.ContainerPort{ { Name: "http", - ContainerPort: 8080, + ContainerPort: constants.DefaultServerPort, Protocol: "TCP", }, { Name: "http-debug", - ContainerPort: 8000, + ContainerPort: constants.DefaultServerPort, Protocol: "TCP", }, { @@ -200,7 +203,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap Path: "/api/system/state", Port: intstr.IntOrString{ Type: intstr.Int, - IntVal: int32(8080), + IntVal: constants.DefaultServerPort, }, Scheme: corev1.URISchemeHTTP, }, @@ -219,7 +222,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap Path: "/api/system/state", Port: intstr.IntOrString{ Type: intstr.Int, - IntVal: int32(8080), + IntVal: constants.DefaultServerPort, }, Scheme: corev1.URISchemeHTTP, }, @@ -242,7 +245,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap } func MountBitBucketOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Deployment) error { - secret, err := getOAuthConfig(ctx, "bitbucket") + secret, err := getOAuthConfigSecret(ctx, constants.BitbucketOAuth) if secret == nil { return err } @@ -302,7 +305,7 @@ func MountGitHubOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Depl } func MountAzureDevOpsOAuthConfig(ctx *chetypes.DeployContext, deployment *appsv1.Deployment) error { - secret, err := getOAuthConfig(ctx, constants.AzureDevOpsOAuth) + secret, err := getOAuthConfigSecret(ctx, constants.AzureDevOpsOAuth) if secret == nil { return err } diff --git a/pkg/deploy/server/server_reconciler.go b/pkg/deploy/server/server_reconciler.go index 85acf795a..fe05feeaf 100644 --- a/pkg/deploy/server/server_reconciler.go +++ b/pkg/deploy/server/server_reconciler.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2023 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -18,20 +18,19 @@ import ( chev2 "github.com/eclipse-che/che-operator/api/v2" "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/common/constants" - "github.com/eclipse-che/che-operator/pkg/common/diffs" defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/common/reconciler" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) const ( crb = ".crb." cheCRBFinalizerSuffix = crb + constants.FinalizerSuffix + configMapName = "che" ) type CheServerReconciler struct { @@ -43,14 +42,14 @@ func NewCheServerReconciler() *CheServerReconciler { } func (s *CheServerReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { - done, err := s.syncCheConfigMap(ctx) + done, err := s.syncConfigMap(ctx) if !done { return reconcile.Result{}, false, err } // ensure configmap is created // the version of the object is used in the deployment - exists, err := deploy.GetNamespacedObject(ctx, CheConfigMapName, &corev1.ConfigMap{}) + exists, err := deploy.GetNamespacedObject(ctx, configMapName, &corev1.ConfigMap{}) if !exists { return reconcile.Result{}, false, err } @@ -90,32 +89,9 @@ func (c *CheServerReconciler) Finalize(ctx *chetypes.DeployContext) bool { return c.deletePermissions(ctx) } -func (s *CheServerReconciler) syncCheConfigMap(ctx *chetypes.DeployContext) (bool, error) { - data, err := s.getCheConfigMapData(ctx) - if err != nil { - return false, err - } - - cm := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: CheConfigMapName, - Namespace: ctx.CheCluster.Namespace, - Labels: deploy.GetLabels(getComponentName(ctx)), - Annotations: data, - }, - Data: data, - } - - return deploy.Sync(ctx, cm, diffs.ConfigMapAllLabels) -} - func (s *CheServerReconciler) syncActiveChePhase(ctx *chetypes.DeployContext) (bool, error) { cheDeployment := &appsv1.Deployment{} - exists, err := deploy.GetNamespacedObject(ctx, getComponentName(ctx), cheDeployment) + exists, err := deploy.GetNamespacedObject(ctx, getComponentName(), cheDeployment) if err != nil { return false, err } @@ -184,7 +160,7 @@ func (s CheServerReconciler) syncCheURL(ctx *chetypes.DeployContext) (bool, erro logrus.Infof("%s is now available at: %s", product, cheUrl) ctx.CheCluster.Status.CheURL = cheUrl - err := deploy.UpdateCheCRStatus(ctx, getComponentName(ctx)+" server URL", cheUrl) + err := deploy.UpdateCheCRStatus(ctx, getComponentName()+" server URL", cheUrl) return err == nil, err } diff --git a/pkg/deploy/server/server_reconciler_test.go b/pkg/deploy/server/server_reconciler_test.go index 7aa4ac488..f3721773a 100644 --- a/pkg/deploy/server/server_reconciler_test.go +++ b/pkg/deploy/server/server_reconciler_test.go @@ -1,5 +1,5 @@ // -// Copyright (c) 2019-2025 Red Hat, Inc. +// Copyright (c) 2019-2026 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/ @@ -46,10 +46,10 @@ func TestReconcile(t *testing.T) { server := NewCheServerReconciler() test.EnsureReconcile(t, ctx, server.Reconcile) - assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: CheConfigMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: configMapName, Namespace: "eclipse-che"}, &corev1.ConfigMap{})) assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Namespace: "eclipse-che", Name: "che"}, &corev1.ServiceAccount{})) assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "test-role"}, &rbac.ClusterRoleBinding{})) - assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) + assert.True(t, test.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: getComponentName(), Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.Equal(t, ctx.CheCluster.Status.ChePhase, chev2.CheClusterPhase(chev2.ClusterPhaseInactive)) assert.Equal(t, 1, len(ctx.CheCluster.Finalizers)) From 6a2434b3089d30861e6852097c4841d4c0508893 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Thu, 12 Feb 2026 10:18:15 +0100 Subject: [PATCH 2/2] fixup Signed-off-by: Anatolii Bazko --- pkg/deploy/server/server_configmap.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go index f0d12b445..4ad150e12 100644 --- a/pkg/deploy/server/server_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -214,7 +214,7 @@ func (s *CheServerReconciler) updateUserClusterRoleEnv(ctx *chetypes.DeployConte for _, role := range strings.Split(cheEnv["CHE_INFRA_KUBERNETES_USER__CLUSTER__ROLES"], ",") { role = strings.TrimSpace(role) if role != "" { - userClusterRolesSet[strings.TrimSpace(role)] = true + userClusterRolesSet[role] = true } } @@ -222,7 +222,7 @@ func (s *CheServerReconciler) updateUserClusterRoleEnv(ctx *chetypes.DeployConte for _, role := range ctx.CheCluster.Spec.DevEnvironments.User.ClusterRoles { role = strings.TrimSpace(role) if role != "" { - userClusterRolesSet[strings.TrimSpace(role)] = true + userClusterRolesSet[role] = true } } } @@ -264,7 +264,7 @@ func (s *CheServerReconciler) updateServerEndpointsEnv(ctx *chetypes.DeployConte for _, endpoint := range strings.Split(endpointsStr, ",") { endpoint = strings.TrimSpace(endpoint) if endpoint != "" { - endpointsSet[strings.TrimSpace(endpoint)] = true + endpointsSet[endpoint] = true } } }