chore: Remove obsolete code after switching to Dev Workspace enabled (#1291)

* chore: Remove keycloak deployment

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Get rid of codebase related to spec.devWorkspace.enable field

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to auth.initialOpenShiftOAuthUser field

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to some auth.* fields

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to some auth.openShiftoAuth field

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to auth.nativeUserMode field

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to server.tlsSupport and server.*ClusterSVCNames fields

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Update bundle

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to server expouse strategy fields

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove deprecated code

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Remove code related to server.tlsSupport and server.cheFlavor field

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Update bundle

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Update helm charts

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: Update bundle

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

* chore: remove RELATED_IMAGE_keycloak

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
7.41
Anatolii Bazko 2022-01-24 09:52:15 +02:00 committed by GitHub
parent 228a3350cd
commit 92d24388ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 712 additions and 5883 deletions

View File

@ -254,7 +254,7 @@ vet: ## Run go vet against code.
go vet ./... go vet ./...
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
test: manifests generate fmt vet prepare-templates ## Run tests. test: prepare-templates ## Run tests.
export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out export MOCK_API=true; go test -mod=vendor ./... -coverprofile cover.out
##@ Build ##@ Build
@ -664,8 +664,9 @@ get-next-version-increment:
echo "$${incrementPart}" echo "$${incrementPart}"
update-resources: SHELL := /bin/bash update-resources: SHELL := /bin/bash
update-resources: check-requirements update-resource-images update-roles update-helmcharts update-resources: check-requirements update-resource-images update-roles
$(MAKE) bundle channel=next $(MAKE) bundle channel=next
$(MAKE) update-helmcharts HELM_FOLDER=next
update-helmcharts: SHELL := /bin/bash update-helmcharts: SHELL := /bin/bash
update-helmcharts: add-license-download check-requirements update-helmcharts: add-license-download check-requirements

View File

@ -201,28 +201,6 @@ spec:
```bash ```bash
$ chectl server:update -n <ECLIPSE-CHE-NAMESPACE> --che-operator-cr-patch-yaml <PATH_TO_CR_PATCH_YAML> $ chectl server:update -n <ECLIPSE-CHE-NAMESPACE> --che-operator-cr-patch-yaml <PATH_TO_CR_PATCH_YAML>
### OpenShift OAuth
OpenShift clusters include a built-in OAuth server. Che operator supports this authentication method. It's enabled by default.
To disable OpenShift OAuth use command line:
```bash
$ kubectl patch checluster/eclipse-che -n <ECLIPSE-CHE-NAMESPACE> --type=merge -p '{"spec":{"auth":{"openShiftoAuth": false}}}'
```
or create `cr-patch.yaml` and use it with chectl:
```yaml
spec:
auth:
openShiftoAuth: false
```
```bash
$ chectl server:update -n <ECLIPSE-CHE-NAMESPACE> --che-operator-cr-patch-yaml <PATH_TO_CR_PATCH_YAML>
```
### TLS ### TLS
TLS is enabled by default. TLS is enabled by default.

View File

@ -17,6 +17,7 @@ import (
"github.com/eclipse-che/che-operator/api/v2alpha1" "github.com/eclipse-che/che-operator/api/v2alpha1"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"k8s.io/utils/pointer"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@ -104,7 +105,6 @@ func V2alpha1ToV1(v2 *v2alpha1.CheCluster, v1Obj *v1.CheCluster) error {
v2alpha1ToV1_Enabled(v1Obj, v2) v2alpha1ToV1_Enabled(v1Obj, v2)
v2alpha1ToV1_Host(v1Obj, v2) v2alpha1ToV1_Host(v1Obj, v2)
v2alpha1ToV1_GatewayEnabled(v1Obj, v2)
v2alpha1ToV1_GatewayImage(v1Obj, v2) v2alpha1ToV1_GatewayImage(v1Obj, v2)
v2alpha1ToV1_GatewayConfigurerImage(v1Obj, v2) v2alpha1ToV1_GatewayConfigurerImage(v1Obj, v2)
v2alpha1ToV1_GatewayTlsSecretName(v1Obj, v2) v2alpha1ToV1_GatewayTlsSecretName(v1Obj, v2)
@ -145,7 +145,6 @@ func v1ToV2alpha1_WorkspaceDomainEndpointsTlsSecretName(v1 *v1.CheCluster, v2 *v
} }
func v1ToV2alpha1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { func v1ToV2alpha1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
exposureStrategy := util.GetServerExposureStrategy(v1)
// On Kubernetes, we can have single-host realized using ingresses (that use the same host but different paths). // On Kubernetes, we can have single-host realized using ingresses (that use the same host but different paths).
// This is actually not supported on DWCO where we always use the gateway for that. So here, we actually just // This is actually not supported on DWCO where we always use the gateway for that. So here, we actually just
// ignore the Spec.K8s.SingleHostExposureType, but we need to be aware of it when converting back. // ignore the Spec.K8s.SingleHostExposureType, but we need to be aware of it when converting back.
@ -153,8 +152,7 @@ func v1ToV2alpha1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
// treat it as such for v2. The difference between default-host and single-host is that the default-host uses // treat it as such for v2. The difference between default-host and single-host is that the default-host uses
// the cluster domain itself as the base domain whereas single-host uses a configured domain. In v2 we always // the cluster domain itself as the base domain whereas single-host uses a configured domain. In v2 we always
// need a domain configured. // need a domain configured.
val := exposureStrategy == "single-host" || exposureStrategy == "default-host" v2.Spec.Gateway.Enabled = pointer.BoolPtr(true)
v2.Spec.Gateway.Enabled = &val
} }
func v1ToV2alpha1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { func v1ToV2alpha1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
@ -184,11 +182,6 @@ func v1ToV2alpha1_K8sIngressAnnotations(v1 *v1.CheCluster, v2 *v2alpha1.CheClust
} }
v2.Spec.K8s.IngressAnnotations["kubernetes.io/ingress.class"] = v1.Spec.K8s.IngressClass v2.Spec.K8s.IngressAnnotations["kubernetes.io/ingress.class"] = v1.Spec.K8s.IngressClass
} }
// This is what is applied in the deploy/ingress.go but I don't think it is applicable in our situation
// if ingressStrategy != "multi-host" && (component == DevfileRegistryName || component == PluginRegistryName) {
// annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/$1"
// }
} }
func v2alpha1ToV1_Enabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { func v2alpha1ToV1_Enabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
@ -219,70 +212,6 @@ func v2alpha1ToV1_WorkspaceDomainEndpointsTlsSecretName(v1 *v1.CheCluster, v2 *v
} }
} }
func v2alpha1ToV1_GatewayEnabled(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
v1Strategy := util.GetServerExposureStrategy(v1)
v1IngressStrategy := v1.Spec.K8s.IngressStrategy
var v2Strategy string
if v2.Spec.Gateway.IsEnabled() {
v2Strategy = "single-host"
} else {
v2Strategy = "multi-host"
}
if v1.Spec.Server.ServerExposureStrategy == "" {
// in the original, the server exposure strategy was undefined, so we need to check whether we can leave it that way
if util.IsOpenShift {
if v2Strategy != v1Strategy {
// only update if the v2Strategy doesn't correspond to the default determined from the v1
v1.Spec.Server.ServerExposureStrategy = v2Strategy
}
} else {
// on Kubernetes, the strategy might have been defined by the deprecated Spec.K8s.IngressStrategy
if v1IngressStrategy != "" {
// check for the default host
if v1IngressStrategy == "default-host" {
if v2Strategy != "single-host" {
v1.Spec.K8s.IngressStrategy = v2Strategy
}
} else if v2Strategy != v1Strategy {
// only change the strategy if the determined strategy would differ
v1.Spec.K8s.IngressStrategy = v2Strategy
}
} else {
if v2Strategy != v1Strategy {
// only update if the v2Strategy doesn't correspond to the default determined from the v1
v1.Spec.Server.ServerExposureStrategy = v2Strategy
}
}
}
} else {
// The below table specifies how to convert the v2Strategy back to v1 taking into the account the original state of v1
// from which v2 was converted before (which could also be just the default v1, if v2 was created on its own)
//
// v2Strategy | orig v1Strategy | orig v1ExposureType | resulting v1Strategy | resulting v1ExposureType
// ----------------------------------------------------------------------------------------------------
// single | single | native | single | orig
// single | single | gateway | single | orig
// single | default | NA | default | orig
// single | multi | NA | single | orig
// multi | single | native | multi | orig
// multi | single | gateway | multi | orig
// multi | default | NA | multi | orig
// multi | multi | NA | multi | orig
//
// Notice that we don't ever want to update the singlehost exposure type. This is only used on Kubernetes and dictates how
// we are going to expose the singlehost endpoints - either using ingresses (native) or using the gateway.
// Because this distinction is not made in DWCO, which always uses the gateway, we just keep whatever the value was originally.
//
// The default-host is actually not supported in v2... but it is quite similar to single host in that everything is exposed
// through the cluster hostname and when converting to v2, we convert it to single-host
if v1Strategy != "default-host" || v2Strategy != "single-host" {
v1.Spec.Server.ServerExposureStrategy = v2Strategy
}
}
}
func v2alpha1ToV1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) { func v2alpha1ToV1_GatewayImage(v1 *v1.CheCluster, v2 *v2alpha1.CheCluster) {
v1.Spec.Server.SingleHostGatewayImage = v2.Spec.Gateway.Image v1.Spec.Server.SingleHostGatewayImage = v2.Spec.Gateway.Image
} }

View File

@ -13,7 +13,6 @@
package org package org
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
@ -54,10 +53,9 @@ func TestV1ToV2alpha1(t *testing.T) {
}, },
}, },
K8s: v1.CheClusterSpecK8SOnly{ K8s: v1.CheClusterSpecK8SOnly{
IngressDomain: "ingressDomain", IngressDomain: "ingressDomain",
IngressClass: "traefik", IngressClass: "traefik",
TlsSecretName: "k8sSecret", TlsSecretName: "k8sSecret",
IngressStrategy: "single-host",
}, },
Metrics: v1.CheClusterSpecMetrics{ Metrics: v1.CheClusterSpecMetrics{
Enable: true, Enable: true,
@ -207,8 +205,8 @@ func TestV1ToV2alpha1(t *testing.T) {
t.FailNow() t.FailNow()
} }
if *v2.Spec.Gateway.Enabled { if !*v2.Spec.Gateway.Enabled {
t.Errorf("The default for OpenShift without devworkspace enabled (which is our testing object) is multihost, but we found v2 in singlehost.") t.Errorf("The default for OpenShift is single")
} }
}) })
}) })
@ -418,11 +416,6 @@ func TestV2alpha1ToV1(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if util.GetServerExposureStrategy(v1) != "single-host" {
t.Logf("When gateway.enabled is true in v2, v1 is single-host.")
t.FailNow()
}
}) })
}) })
@ -463,180 +456,6 @@ func TestV2alpha1ToV1(t *testing.T) {
}) })
} }
func TestExposureStrategyConversions(t *testing.T) {
testWithExposure := func(v1ExposureStrategy string, v1IngressStrategy string, v1DevWorkspaceEnabled bool, v2GatewayEnabledChange *bool, test func(*testing.T, *v1.CheCluster)) {
origV1 := &v1.CheCluster{
Spec: v1.CheClusterSpec{
Server: v1.CheClusterSpecServer{
ServerExposureStrategy: v1ExposureStrategy,
},
K8s: v1.CheClusterSpecK8SOnly{
IngressStrategy: v1IngressStrategy,
},
DevWorkspace: v1.CheClusterSpecDevWorkspace{
Enable: v1DevWorkspaceEnabled,
},
},
}
t.Run(fmt.Sprintf("[v1ExposureStrategy=%v/v1IngressStrategy=%v/v1DevworkspaceEnabled=%v/v2GatewayEnabledChange=%v]", v1ExposureStrategy, v1IngressStrategy, v1DevWorkspaceEnabled, v2GatewayEnabledChange), func(t *testing.T) {
v2 := &v2alpha1.CheCluster{}
if err := V1ToV2alpha1(origV1, v2); err != nil {
t.Error(err)
}
if v2GatewayEnabledChange != nil {
v2.Spec.Gateway.Enabled = v2GatewayEnabledChange
}
// now convert back and run the test
v1Tested := &v1.CheCluster{}
if err := V2alpha1ToV1(v2, v1Tested); err != nil {
t.Error(err)
}
test(t, v1Tested)
})
}
testWithExposure("single-host", "", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "single-host" {
t.Errorf("The v1 should have single-host exposure after conversion")
}
})
testWithExposure("single-host", "", true, pointer.BoolPtr(false), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "multi-host" {
t.Errorf("The v1 should have multi-host exposure after conversion")
}
})
testWithExposure("multi-host", "", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "multi-host" {
t.Errorf("The v1 should have multi-host exposure after conversion")
}
})
testWithExposure("multi-host", "", true, pointer.BoolPtr(true), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "single-host" {
t.Errorf("The v1 should have single-host exposure after conversion")
}
})
testWithExposure("default-host", "", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "default-host" {
t.Errorf("The v1 should have default-host exposure after conversion")
}
})
testWithExposure("default-host", "", true, pointer.BoolPtr(true), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "default-host" {
t.Errorf("The v1 should have default-host exposure after conversion")
}
})
testWithExposure("default-host", "", true, pointer.BoolPtr(false), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "multi-host" {
t.Errorf("The v1 should have multi-host exposure after conversion")
}
})
onFakeKubernetes(func() {
testWithExposure("", "single-host", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "single-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
testWithExposure("", "single-host", true, pointer.BoolPtr(false), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "multi-host" {
t.Errorf("The ingress strategy should have been set to multi-host after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
testWithExposure("", "single-host", true, pointer.BoolPtr(true), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "single-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
testWithExposure("", "multi-host", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "multi-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
// the below two tests test that we're leaving the ingress strategy unchanged if it doesn't affect the effective exposure
// strategy
testWithExposure("", "multi-host", true, pointer.BoolPtr(false), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "multi-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
testWithExposure("", "multi-host", true, pointer.BoolPtr(true), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "single-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
testWithExposure("", "default-host", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
if old.Spec.K8s.IngressStrategy != "default-host" {
t.Errorf("The ingress strategy should have been unchanged after conversion but was: %v", old.Spec.K8s.IngressStrategy)
}
})
})
onFakeOpenShift(func() {
testWithExposure("", "", true, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
})
testWithExposure("", "", false, nil, func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
})
testWithExposure("", "", true, pointer.BoolPtr(false), func(t *testing.T, old *v1.CheCluster) {
// default on openshift with devworkspace enabled in v1 is single-host, but we've disabled the gateway in v2. So after the conversion
// v1 should change to an explicit multi-host.
if old.Spec.Server.ServerExposureStrategy != "multi-host" {
t.Errorf("The server exposure strategy should have been set to multi-host after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
})
testWithExposure("", "", true, pointer.BoolPtr(true), func(t *testing.T, old *v1.CheCluster) {
if old.Spec.Server.ServerExposureStrategy != "" {
t.Errorf("The server exposure strategy should have been left empty after conversion but was: %v", old.Spec.Server.ServerExposureStrategy)
}
})
})
}
func TestFullCircleV1(t *testing.T) { func TestFullCircleV1(t *testing.T) {
v1Obj := v1.CheCluster{ v1Obj := v1.CheCluster{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -661,10 +480,9 @@ func TestFullCircleV1(t *testing.T) {
}, },
}, },
K8s: v1.CheClusterSpecK8SOnly{ K8s: v1.CheClusterSpecK8SOnly{
IngressDomain: "ingressDomain", IngressDomain: "ingressDomain",
IngressClass: "traefik", IngressClass: "traefik",
TlsSecretName: "k8sSecret", TlsSecretName: "k8sSecret",
IngressStrategy: "single-host",
}, },
Metrics: v1.CheClusterSpecMetrics{ Metrics: v1.CheClusterSpecMetrics{
Enable: true, Enable: true,

View File

@ -95,6 +95,7 @@ type CheClusterSpecServer struct {
// Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. // Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases.
// +optional // +optional
CheImagePullPolicy corev1.PullPolicy `json:"cheImagePullPolicy,omitempty"` CheImagePullPolicy corev1.PullPolicy `json:"cheImagePullPolicy,omitempty"`
// Deprecated. The value of this flag is ignored.
// Specifies a variation of the installation. The options are `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady Workspaces] installation. // Specifies a variation of the installation. The options are `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady Workspaces] installation.
// Override the default value only on necessary occasions. // Override the default value only on necessary occasions.
// +optional // +optional
@ -158,6 +159,7 @@ type CheClusterSpecServer struct {
// +optional // +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors="urn:alm:descriptor:com.tectonic.ui:hidden" // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors="urn:alm:descriptor:com.tectonic.ui:hidden"
UseInternalClusterSVCNames bool `json:"useInternalClusterSVCNames"` UseInternalClusterSVCNames bool `json:"useInternalClusterSVCNames"`
// Deprecated. The value of this flag is ignored.
// Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues. // Disable internal cluster SVC names usage to communicate between components to speed up the traffic and avoid proxy issues.
// +optional // +optional
DisableInternalClusterSVCNames *bool `json:"disableInternalClusterSVCNames,omitempty"` DisableInternalClusterSVCNames *bool `json:"disableInternalClusterSVCNames,omitempty"`
@ -183,9 +185,11 @@ type CheClusterSpecServer struct {
// In cores. (500m = .5 cores). Default to 100m. // In cores. (500m = .5 cores). Default to 100m.
// +optional // +optional
DashboardCpuRequest string `json:"dashboardCpuRequest,omitempty"` DashboardCpuRequest string `json:"dashboardCpuRequest,omitempty"`
// Deprecated. The value of this flag is ignored.
// Dashboard ingress custom settings. // Dashboard ingress custom settings.
// +optional // +optional
DashboardIngress IngressCustomSettings `json:"dashboardIngress,omitempty"` DashboardIngress IngressCustomSettings `json:"dashboardIngress,omitempty"`
// Deprecated. The value of this flag is ignored.
// Dashboard route custom settings. // Dashboard route custom settings.
// +optional // +optional
DashboardRoute RouteCustomSettings `json:"dashboardRoute,omitempty"` DashboardRoute RouteCustomSettings `json:"dashboardRoute,omitempty"`
@ -215,9 +219,11 @@ type CheClusterSpecServer struct {
// In cores. (500m = .5 cores). Default to 100m. // In cores. (500m = .5 cores). Default to 100m.
// +optional // +optional
DevfileRegistryCpuRequest string `json:"devfileRegistryCpuRequest,omitempty"` DevfileRegistryCpuRequest string `json:"devfileRegistryCpuRequest,omitempty"`
// Deprecated. The value of this flag is ignored.
// The devfile registry ingress custom settings. // The devfile registry ingress custom settings.
// +optional // +optional
DevfileRegistryIngress IngressCustomSettings `json:"devfileRegistryIngress,omitempty"` DevfileRegistryIngress IngressCustomSettings `json:"devfileRegistryIngress,omitempty"`
// Deprecated. The value of this flag is ignored.
// The devfile registry route custom settings. // The devfile registry route custom settings.
// +optional // +optional
DevfileRegistryRoute RouteCustomSettings `json:"devfileRegistryRoute,omitempty"` DevfileRegistryRoute RouteCustomSettings `json:"devfileRegistryRoute,omitempty"`
@ -259,9 +265,11 @@ type CheClusterSpecServer struct {
// In cores. (500m = .5 cores). Default to 100m. // In cores. (500m = .5 cores). Default to 100m.
// +optional // +optional
PluginRegistryCpuRequest string `json:"pluginRegistryCpuRequest,omitempty"` PluginRegistryCpuRequest string `json:"pluginRegistryCpuRequest,omitempty"`
// Deprecated. The value of this flag is ignored.
// Plugin registry ingress custom settings. // Plugin registry ingress custom settings.
// +optional // +optional
PluginRegistryIngress IngressCustomSettings `json:"pluginRegistryIngress,omitempty"` PluginRegistryIngress IngressCustomSettings `json:"pluginRegistryIngress,omitempty"`
// Deprecated. The value of this flag is ignored.
// Plugin registry route custom settings. // Plugin registry route custom settings.
// +optional // +optional
PluginRegistryRoute RouteCustomSettings `json:"pluginRegistryRoute,omitempty"` PluginRegistryRoute RouteCustomSettings `json:"pluginRegistryRoute,omitempty"`
@ -318,6 +326,7 @@ type CheClusterSpecServer struct {
// In cores. (500m = .5 cores). Default to 100m. // In cores. (500m = .5 cores). Default to 100m.
// +optional // +optional
ServerCpuRequest string `json:"serverCpuRequest,omitempty"` ServerCpuRequest string `json:"serverCpuRequest,omitempty"`
// Deprecated. The value of this flag is ignored.
// Sets the server and workspaces exposure type. // Sets the server and workspaces exposure type.
// Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint. // Possible values are `multi-host`, `single-host`, `default-host`. Defaults to `multi-host`, which creates a separate ingress, or OpenShift routes, for every required endpoint.
// `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths. // `single-host` makes Che exposed on a single host name with workspaces exposed on subpaths.
@ -403,6 +412,7 @@ type CheClusterSpecDB struct {
// +k8s:openapi-gen=true // +k8s:openapi-gen=true
// Configuration settings related to the Authentication used by the Che installation. // Configuration settings related to the Authentication used by the Che installation.
type CheClusterSpecAuth struct { type CheClusterSpecAuth struct {
// Deprecated. The value of this flag is ignored.
// For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. // For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used.
// If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. // If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider.
// If the value is false and the user has already been created, then it will be removed. // If the value is false and the user has already been created, then it will be removed.
@ -410,6 +420,7 @@ type CheClusterSpecAuth struct {
// The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. // The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator.
// Note that this solution is Openshift 4 platform-specific. // Note that this solution is Openshift 4 platform-specific.
InitialOpenShiftOAuthUser *bool `json:"initialOpenShiftOAuthUser,omitempty"` InitialOpenShiftOAuthUser *bool `json:"initialOpenShiftOAuthUser,omitempty"`
// Deprecated. The value of this flag is ignored.
// Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance). // Instructs the Operator on whether or not to deploy a dedicated Identity Provider (Keycloak or RH SSO instance).
// Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance). // Instructs the Operator on whether to deploy a dedicated Identity Provider (Keycloak or RH-SSO instance).
// By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`, // By default, a dedicated Identity Provider server is deployed as part of the Che installation. When `externalIdentityProvider` is `true`,
@ -422,14 +433,17 @@ type CheClusterSpecAuth struct {
// See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator. // See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
// +optional // +optional
IdentityProviderURL string `json:"identityProviderURL,omitempty"` IdentityProviderURL string `json:"identityProviderURL,omitempty"`
// Deprecated. The value of this flag is ignored.
// Overrides the name of the Identity Provider administrator user. Defaults to `admin`. // Overrides the name of the Identity Provider administrator user. Defaults to `admin`.
// +optional // +optional
IdentityProviderAdminUserName string `json:"identityProviderAdminUserName,omitempty"` IdentityProviderAdminUserName string `json:"identityProviderAdminUserName,omitempty"`
// Deprecated. The value of this flag is ignored.
// Overrides the password of Keycloak administrator user. // Overrides the password of Keycloak administrator user.
// Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. // Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field.
// When omitted or left blank, it is set to an auto-generated password. // When omitted or left blank, it is set to an auto-generated password.
// +optional // +optional
IdentityProviderPassword string `json:"identityProviderPassword,omitempty"` IdentityProviderPassword string `json:"identityProviderPassword,omitempty"`
// Deprecated. The value of this flag is ignored.
// The secret that contains `user` and `password` for Identity Provider. // The secret that contains `user` and `password` for Identity Provider.
// When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored. // When the secret is defined, the `identityProviderAdminUserName` and `identityProviderPassword` are ignored.
// When the value is omitted or left blank, the one of following scenarios applies: // When the value is omitted or left blank, the one of following scenarios applies:
@ -439,21 +453,25 @@ type CheClusterSpecAuth struct {
// The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. // The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.
// +optional // +optional
IdentityProviderSecret string `json:"identityProviderSecret,omitempty"` IdentityProviderSecret string `json:"identityProviderSecret,omitempty"`
// Deprecated. The value of this flag is ignored.
// Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che. // Name of a Identity provider, Keycloak or RH-SSO, realm that is used for Che.
// Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. // Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field.
// When omitted or left blank, it is set to the value of the `flavour` field. // When omitted or left blank, it is set to the value of the `flavour` field.
// +optional // +optional
IdentityProviderRealm string `json:"identityProviderRealm,omitempty"` IdentityProviderRealm string `json:"identityProviderRealm,omitempty"`
// Deprecated. The value of this flag is ignored.
// Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che. // Name of a Identity provider, Keycloak or RH-SSO, `client-id` that is used for Che.
// Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. // Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field.
// When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`. // When omitted or left blank, it is set to the value of the `flavour` field suffixed with `-public`.
// +optional // +optional
IdentityProviderClientId string `json:"identityProviderClientId,omitempty"` IdentityProviderClientId string `json:"identityProviderClientId,omitempty"`
// Deprecated. The value of this flag is ignored.
// Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database. // Password for a Identity Provider, Keycloak or RH-SSO, to connect to the database.
// Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field. // Override this when an external Identity Provider is in use. See the `externalIdentityProvider` field.
// When omitted or left blank, it is set to an auto-generated password. // When omitted or left blank, it is set to an auto-generated password.
// +optional // +optional
IdentityProviderPostgresPassword string `json:"identityProviderPostgresPassword,omitempty"` IdentityProviderPostgresPassword string `json:"identityProviderPostgresPassword,omitempty"`
// Deprecated. The value of this flag is ignored.
// The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database. // The secret that contains `password` for the Identity Provider, Keycloak or RH-SSO, to connect to the database.
// When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies: // When the secret is defined, the `identityProviderPostgresPassword` is ignored. When the value is omitted or left blank, the one of following scenarios applies:
// 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database. // 1. `identityProviderPostgresPassword` is defined, then it will be used to connect to the database.
@ -461,9 +479,11 @@ type CheClusterSpecAuth struct {
// The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label. // The secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.
// +optional // +optional
IdentityProviderPostgresSecret string `json:"identityProviderPostgresSecret,omitempty"` IdentityProviderPostgresSecret string `json:"identityProviderPostgresSecret,omitempty"`
// Deprecated. The value of this flag is ignored.
// Forces the default `admin` Che user to update password on first login. Defaults to `false`. // Forces the default `admin` Che user to update password on first login. Defaults to `false`.
// +optional // +optional
UpdateAdminPassword bool `json:"updateAdminPassword"` UpdateAdminPassword bool `json:"updateAdminPassword"`
// Deprecated. The value of this flag is ignored.
// Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth. // Enables the integration of the identity provider (Keycloak / RHSSO) with OpenShift OAuth.
// Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login, // Empty value on OpenShift by default. This will allow users to directly login with their OpenShift user through the OpenShift login,
// and have their workspaces created under personal OpenShift namespaces. // and have their workspaces created under personal OpenShift namespaces.
@ -476,23 +496,29 @@ type CheClusterSpecAuth struct {
// Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field. // Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
// +optional // +optional
OAuthSecret string `json:"oAuthSecret,omitempty"` OAuthSecret string `json:"oAuthSecret,omitempty"`
// Deprecated. The value of this flag is ignored.
// Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment. // Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment.
// This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator. // This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator.
// +optional // +optional
IdentityProviderImage string `json:"identityProviderImage,omitempty"` IdentityProviderImage string `json:"identityProviderImage,omitempty"`
// Deprecated. The value of this flag is ignored.
// Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment. // Overrides the image pull policy used in the Identity Provider, Keycloak or RH-SSO, deployment.
// Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases. // Default value is `Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` in other cases.
// +optional // +optional
IdentityProviderImagePullPolicy corev1.PullPolicy `json:"identityProviderImagePullPolicy,omitempty"` IdentityProviderImagePullPolicy corev1.PullPolicy `json:"identityProviderImagePullPolicy,omitempty"`
// Deprecated. The value of this flag is ignored.
// Ingress custom settings. // Ingress custom settings.
// +optional // +optional
IdentityProviderIngress IngressCustomSettings `json:"identityProviderIngress,omitempty"` IdentityProviderIngress IngressCustomSettings `json:"identityProviderIngress,omitempty"`
// Deprecated. The value of this flag is ignored.
// Route custom settings. // Route custom settings.
// +optional // +optional
IdentityProviderRoute RouteCustomSettings `json:"identityProviderRoute,omitempty"` IdentityProviderRoute RouteCustomSettings `json:"identityProviderRoute,omitempty"`
// Deprecated. The value of this flag is ignored.
// Identity provider container custom settings. // Identity provider container custom settings.
// +optional // +optional
IdentityProviderContainerResources ResourcesCustomSettings `json:"identityProviderContainerResources,omitempty"` IdentityProviderContainerResources ResourcesCustomSettings `json:"identityProviderContainerResources,omitempty"`
// Deprecated. The value of this flag is ignored.
// Enables native user mode. Currently works only on OpenShift and DevWorkspace engine. // Enables native user mode. Currently works only on OpenShift and DevWorkspace engine.
// Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak. // Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak.
// +optional // +optional
@ -509,7 +535,7 @@ type CheClusterSpecAuth struct {
// +optional // +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors="urn:alm:descriptor:com.tectonic.ui:hidden" // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors="urn:alm:descriptor:com.tectonic.ui:hidden"
GatewayHeaderRewriteSidecarImage string `json:"gatewayHeaderRewriteSidecarImage,omitempty"` GatewayHeaderRewriteSidecarImage string `json:"gatewayHeaderRewriteSidecarImage,omitempty"`
// Deprecated. The value of this flag is ignored.
// Debug internal identity provider. // Debug internal identity provider.
Debug bool `json:"debug,omitempty"` Debug bool `json:"debug,omitempty"`
} }
@ -597,6 +623,7 @@ type CheClusterSpecStorage struct {
type CheClusterSpecK8SOnly struct { type CheClusterSpecK8SOnly struct {
// Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults. // Global ingress domain for a Kubernetes cluster. This MUST be explicitly specified: there are no defaults.
IngressDomain string `json:"ingressDomain,omitempty"` IngressDomain string `json:"ingressDomain,omitempty"`
// Deprecated. The value of this flag is ignored.
// Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress), // Strategy for ingress creation. Options are: `multi-host` (host is explicitly provided in ingress),
// `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules). // `single-host` (host is provided, path-based rules) and `default-host` (no host is provided, path-based rules).
// Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section, // Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy` in the `server` section,
@ -617,6 +644,7 @@ type CheClusterSpecK8SOnly struct {
// ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`. // ID of the user the Che Pod and workspace Pods containers run as. Default value is `1724`.
// +optional // +optional
SecurityContextRunAsUser string `json:"securityContextRunAsUser,omitempty"` SecurityContextRunAsUser string `json:"securityContextRunAsUser,omitempty"`
// Deprecated. The value of this flag is ignored.
// When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property. // When the serverExposureStrategy is set to `single-host`, the way the server, registries and workspaces are exposed is further configured by this property.
// The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s // The possible values are `native`, which means that the server and workspaces are exposed using ingresses on K8s
// or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. // or `gateway` where the server and workspaces are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik].
@ -746,7 +774,6 @@ type CheClusterStatus struct {
// +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Help link" // +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Help link"
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link" // +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors="urn:alm:descriptor:org.w3:link"
HelpLink string `json:"helpLink,omitempty"` HelpLink string `json:"helpLink,omitempty"`
// The status of the Devworkspace subsystem // The status of the Devworkspace subsystem
// +optional // +optional
DevworkspaceStatus v2alpha1.CheClusterStatusV2Alpha1 `json:"devworkspaceStatus,omitempty"` DevworkspaceStatus v2alpha1.CheClusterStatusV2Alpha1 `json:"devworkspaceStatus,omitempty"`
@ -800,24 +827,3 @@ func (c *CheCluster) IsImagePullerSpecEmpty() bool {
func (c *CheCluster) IsImagePullerImagesEmpty() bool { func (c *CheCluster) IsImagePullerImagesEmpty() bool {
return len(c.Spec.ImagePuller.Spec.Images) == 0 return len(c.Spec.ImagePuller.Spec.Images) == 0
} }
func (c *CheCluster) IsInternalClusterSVCNamesEnabled() bool {
return c.Spec.Server.DisableInternalClusterSVCNames == nil || !*c.Spec.Server.DisableInternalClusterSVCNames
}
// IsInitialOpenShiftOAuthUserEnabled returns true when initial Openshift oAuth user is enabled for CheCluster resource, otherwise false.
func (c *CheCluster) IsOpenShiftOAuthUserConfigured() bool {
return c.Spec.Auth.InitialOpenShiftOAuthUser != nil && *c.Spec.Auth.InitialOpenShiftOAuthUser
}
func (c *CheCluster) IsOpenShiftOAuthUserMustBeDeleted() bool {
return c.Spec.Auth.InitialOpenShiftOAuthUser != nil && !*c.Spec.Auth.InitialOpenShiftOAuthUser
}
func (c *CheCluster) IsOpenShiftOAuthEnabled() bool {
return c.Spec.Auth.OpenShiftoAuth != nil && *c.Spec.Auth.OpenShiftoAuth
}
func (c *CheCluster) IsNativeUserModeEnabled() bool {
return c.Spec.Auth.NativeUserMode != nil && *c.Spec.Auth.NativeUserMode
}

View File

@ -66,7 +66,6 @@ metadata:
"proxyPort": "", "proxyPort": "",
"proxySecret": "", "proxySecret": "",
"proxyURL": "", "proxyURL": "",
"tlsSupport": true,
"workspaceNamespaceDefault": "<username>-che" "workspaceNamespaceDefault": "<username>-che"
}, },
"storage": { "storage": {
@ -114,7 +113,7 @@ metadata:
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/eclipse-che/che-operator repository: https://github.com/eclipse-che/che-operator
support: Eclipse Foundation support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.43.0-408.next name: eclipse-che-preview-openshift.v7.43.0-412.next
namespace: placeholder namespace: placeholder
spec: spec:
apiservicedefinitions: {} apiservicedefinitions: {}
@ -1079,8 +1078,6 @@ spec:
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_postgres_13_3 - name: RELATED_IMAGE_postgres_13_3
value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2
- name: RELATED_IMAGE_keycloak
value: quay.io/eclipse/che-keycloak:next
- name: RELATED_IMAGE_che_workspace_plugin_broker_metadata - name: RELATED_IMAGE_che_workspace_plugin_broker_metadata
value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0 value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0
- name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts - name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts
@ -1406,4 +1403,4 @@ spec:
maturity: stable maturity: stable
provider: provider:
name: Eclipse Foundation name: Eclipse Foundation
version: 7.43.0-408.next version: 7.43.0-412.next

View File

@ -61,14 +61,16 @@ spec:
used by the Che installation. used by the Che installation.
properties: properties:
debug: debug:
description: Debug internal identity provider. description: Deprecated. The value of this flag is ignored.
Debug internal identity provider.
type: boolean type: boolean
externalIdentityProvider: externalIdentityProvider:
description: 'Instructs the Operator on whether or not to deploy description: 'Deprecated. The value of this flag is ignored.
a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether or not to deploy a dedicated
Instructs the Operator on whether to deploy a dedicated Identity Identity Provider (Keycloak or RH SSO instance). Instructs
Provider (Keycloak or RH-SSO instance). By default, a dedicated the Operator on whether to deploy a dedicated Identity Provider
Identity Provider server is deployed as part of the Che installation. (Keycloak or RH-SSO instance). By default, a dedicated Identity
Provider server is deployed as part of the Che installation.
When `externalIdentityProvider` is `true`, no dedicated identity When `externalIdentityProvider` is `true`, no dedicated identity
provider will be deployed by the Operator and you will need provider will be deployed by the Operator and you will need
to provide details about the external identity provider you to provide details about the external identity provider you
@ -90,18 +92,21 @@ spec:
Sidecar functionality is now implemented in Traefik plugin. Sidecar functionality is now implemented in Traefik plugin.
type: string type: string
identityProviderAdminUserName: identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator description: Deprecated. The value of this flag is ignored.
Overrides the name of the Identity Provider administrator
user. Defaults to `admin`. user. Defaults to `admin`.
type: string type: string
identityProviderClientId: identityProviderClientId:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
`client-id` that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, `client-id`
Identity Provider is in use. See the `externalIdentityProvider` that is used for Che. Override this when an external Identity
field. When omitted or left blank, it is set to the value Provider is in use. See the `externalIdentityProvider` field.
of the `flavour` field suffixed with `-public`. When omitted or left blank, it is set to the value of the
`flavour` field suffixed with `-public`.
type: string type: string
identityProviderContainerResources: identityProviderContainerResources:
description: Identity provider container custom settings. description: Deprecated. The value of this flag is ignored.
Identity provider container custom settings.
properties: properties:
limits: limits:
description: Limits describes the maximum amount of compute description: Limits describes the maximum amount of compute
@ -129,19 +134,22 @@ spec:
type: object type: object
type: object type: object
identityProviderImage: identityProviderImage:
description: Overrides the container image used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. This includes the Overrides the container image used in the Identity Provider,
image tag. Omit it or leave it empty to use the default container Keycloak or RH-SSO, deployment. This includes the image tag.
image provided by the Operator. Omit it or leave it empty to use the default container image
provided by the Operator.
type: string type: string
identityProviderImagePullPolicy: identityProviderImagePullPolicy:
description: Overrides the image pull policy used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. Default value is Overrides the image pull policy used in the Identity Provider,
`Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` Keycloak or RH-SSO, deployment. Default value is `Always`
for `nightly`, `next` or `latest` images, and `IfNotPresent`
in other cases. in other cases.
type: string type: string
identityProviderIngress: identityProviderIngress:
description: Ingress custom settings. description: Deprecated. The value of this flag is ignored.
Ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -157,25 +165,27 @@ spec:
type: string type: string
type: object type: object
identityProviderPassword: identityProviderPassword:
description: Overrides the password of Keycloak administrator description: Deprecated. The value of this flag is ignored.
user. Override this when an external Identity Provider is Overrides the password of Keycloak administrator user. Override
in use. See the `externalIdentityProvider` field. When omitted this when an external Identity Provider is in use. See the
or left blank, it is set to an auto-generated password. `externalIdentityProvider` field. When omitted or left blank,
it is set to an auto-generated password.
type: string type: string
identityProviderPostgresPassword: identityProviderPostgresPassword:
description: Password for a Identity Provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
to connect to the database. Override this when an external Password for a Identity Provider, Keycloak or RH-SSO, to connect
Identity Provider is in use. See the `externalIdentityProvider` to the database. Override this when an external Identity Provider
field. When omitted or left blank, it is set to an auto-generated is in use. See the `externalIdentityProvider` field. When
password. omitted or left blank, it is set to an auto-generated password.
type: string type: string
identityProviderPostgresSecret: identityProviderPostgresSecret:
description: 'The secret that contains `password` for the Identity description: 'Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, to connect to the database. The secret that contains `password` for the Identity Provider,
When the secret is defined, the `identityProviderPostgresPassword` Keycloak or RH-SSO, to connect to the database. When the secret
is ignored. When the value is omitted or left blank, the one is defined, the `identityProviderPostgresPassword` is ignored.
of following scenarios applies: 1. `identityProviderPostgresPassword` When the value is omitted or left blank, the one of following
is defined, then it will be used to connect to the database. scenarios applies: 1. `identityProviderPostgresPassword` is
defined, then it will be used to connect to the database.
2. `identityProviderPostgresPassword` is not defined, then 2. `identityProviderPostgresPassword` is not defined, then
a new secret with the name `che-identity-postgres-secret` a new secret with the name `che-identity-postgres-secret`
will be created with an auto-generated value for `password`. will be created with an auto-generated value for `password`.
@ -183,14 +193,16 @@ spec:
label.' label.'
type: string type: string
identityProviderRealm: identityProviderRealm:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
realm that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, realm that
Identity Provider is in use. See the `externalIdentityProvider` is used for Che. Override this when an external Identity Provider
field. When omitted or left blank, it is set to the value is in use. See the `externalIdentityProvider` field. When
of the `flavour` field. omitted or left blank, it is set to the value of the `flavour`
field.
type: string type: string
identityProviderRoute: identityProviderRoute:
description: Route custom settings. description: Deprecated. The value of this flag is ignored.
Route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -212,8 +224,9 @@ spec:
type: string type: string
type: object type: object
identityProviderSecret: identityProviderSecret:
description: 'The secret that contains `user` and `password` description: 'Deprecated. The value of this flag is ignored.
for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` The secret that contains `user` and `password` for Identity
Provider. When the secret is defined, the `identityProviderAdminUserName`
and `identityProviderPassword` are ignored. When the value and `identityProviderPassword` are ignored. When the value
is omitted or left blank, the one of following scenarios applies: is omitted or left blank, the one of following scenarios applies:
1. `identityProviderAdminUserName` and `identityProviderPassword` 1. `identityProviderAdminUserName` and `identityProviderPassword`
@ -232,20 +245,22 @@ spec:
set by the Operator. set by the Operator.
type: string type: string
initialOpenShiftOAuthUser: initialOpenShiftOAuthUser:
description: For operating with the OpenShift OAuth authentication, description: Deprecated. The value of this flag is ignored.
create a new user account since the kubeadmin can not be used. For operating with the OpenShift OAuth authentication, create
If the value is true, then a new OpenShift OAuth user will a new user account since the kubeadmin can not be used. If
be created for the HTPasswd identity provider. If the value the value is true, then a new OpenShift OAuth user will be
is false and the user has already been created, then it will created for the HTPasswd identity provider. If the value is
false and the user has already been created, then it will
be removed. If value is an empty, then do nothing. The user's be removed. If value is an empty, then do nothing. The user's
credentials are stored in the `openshift-oauth-user-credentials` credentials are stored in the `openshift-oauth-user-credentials`
secret in 'openshift-config' namespace by Operator. Note that secret in 'openshift-config' namespace by Operator. Note that
this solution is Openshift 4 platform-specific. this solution is Openshift 4 platform-specific.
type: boolean type: boolean
nativeUserMode: nativeUserMode:
description: Enables native user mode. Currently works only description: Deprecated. The value of this flag is ignored.
on OpenShift and DevWorkspace engine. Native User mode uses Enables native user mode. Currently works only on OpenShift
OpenShift OAuth directly as identity provider, without Keycloak. and DevWorkspace engine. Native User mode uses OpenShift OAuth
directly as identity provider, without Keycloak.
type: boolean type: boolean
oAuthClientName: oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used description: Name of the OpenShift `OAuthClient` resource used
@ -259,17 +274,19 @@ spec:
field. field.
type: string type: string
openShiftoAuth: openShiftoAuth:
description: 'Enables the integration of the identity provider description: 'Deprecated. The value of this flag is ignored.
(Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift Enables the integration of the identity provider (Keycloak
by default. This will allow users to directly login with their / RHSSO) with OpenShift OAuth. Empty value on OpenShift by
default. This will allow users to directly login with their
OpenShift user through the OpenShift login, and have their OpenShift user through the OpenShift login, and have their
workspaces created under personal OpenShift namespaces. WARNING: workspaces created under personal OpenShift namespaces. WARNING:
the `kubeadmin` user is NOT supported, and logging through the `kubeadmin` user is NOT supported, and logging through
it will NOT allow accessing the Che Dashboard.' it will NOT allow accessing the Che Dashboard.'
type: boolean type: boolean
updateAdminPassword: updateAdminPassword:
description: Forces the default `admin` Che user to update password description: Deprecated. The value of this flag is ignored.
on first login. Defaults to `false`. Forces the default `admin` Che user to update password on
first login. Defaults to `false`.
type: boolean type: boolean
type: object type: object
database: database:
@ -456,10 +473,11 @@ spec:
This MUST be explicitly specified: there are no defaults.' This MUST be explicitly specified: there are no defaults.'
type: string type: string
ingressStrategy: ingressStrategy:
description: 'Strategy for ingress creation. Options are: `multi-host` description: 'Deprecated. The value of this flag is ignored.
(host is explicitly provided in ingress), `single-host` (host Strategy for ingress creation. Options are: `multi-host` (host
is provided, path-based rules) and `default-host` (no host is explicitly provided in ingress), `single-host` (host is
is provided, path-based rules). Defaults to `multi-host` Deprecated provided, path-based rules) and `default-host` (no host is
provided, path-based rules). Defaults to `multi-host` Deprecated
in favor of `serverExposureStrategy` in the `server` section, in favor of `serverExposureStrategy` in the `server` section,
which defines this regardless of the cluster type. When both which defines this regardless of the cluster type. When both
are defined, the `serverExposureStrategy` option takes precedence.' are defined, the `serverExposureStrategy` option takes precedence.'
@ -473,15 +491,16 @@ spec:
run as. Default value is `1724`. run as. Default value is `1724`.
type: string type: string
singleHostExposureType: singleHostExposureType:
description: When the serverExposureStrategy is set to `single-host`, description: Deprecated. The value of this flag is ignored.
the way the server, registries and workspaces are exposed When the serverExposureStrategy is set to `single-host`, the
is further configured by this property. The possible values way the server, registries and workspaces are exposed is further
are `native`, which means that the server and workspaces are configured by this property. The possible values are `native`,
exposed using ingresses on K8s or `gateway` where the server which means that the server and workspaces are exposed using
and workspaces are exposed using a custom gateway based on ingresses on K8s or `gateway` where the server and workspaces
link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik].
whether backed by the ingress or gateway `route` always point All the endpoints whether backed by the ingress or gateway
to the subpaths on the same domain. Defaults to `native`. `route` always point to the subpaths on the same domain. Defaults
to `native`.
type: string type: string
tlsSecretName: tlsSecretName:
description: Name of a secret that will be used to setup ingress description: Name of a secret that will be used to setup ingress
@ -535,9 +554,9 @@ spec:
to `false`. to `false`.
type: string type: string
cheFlavor: cheFlavor:
description: Specifies a variation of the installation. The description: Deprecated. The value of this flag is ignored.
options are `che` for upstream Che installations, or `codeready` Specifies a variation of the installation. The options are
for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady
Workspaces] installation. Override the default value only Workspaces] installation. Override the default value only
on necessary occasions. on necessary occasions.
type: string type: string
@ -646,7 +665,8 @@ spec:
or `latest` images, and `IfNotPresent` in other cases. or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
dashboardIngress: dashboardIngress:
description: Dashboard ingress custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -670,7 +690,8 @@ spec:
deployment. Defaults to 16Mi. deployment. Defaults to 16Mi.
type: string type: string
dashboardRoute: dashboardRoute:
description: Dashboard route custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -706,7 +727,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
devfileRegistryIngress: devfileRegistryIngress:
description: The devfile registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -735,7 +757,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
devfileRegistryRoute: devfileRegistryRoute:
description: The devfile registry route custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -761,9 +784,9 @@ spec:
fields. fields.
type: string type: string
disableInternalClusterSVCNames: disableInternalClusterSVCNames:
description: Disable internal cluster SVC names usage to communicate description: Deprecated. The value of this flag is ignored.
between components to speed up the traffic and avoid proxy Disable internal cluster SVC names usage to communicate between
issues. components to speed up the traffic and avoid proxy issues.
type: boolean type: boolean
externalDevfileRegistries: externalDevfileRegistries:
description: External devfile registries, that serves sample, description: External devfile registries, that serves sample,
@ -828,7 +851,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
pluginRegistryIngress: pluginRegistryIngress:
description: Plugin registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -857,7 +881,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
pluginRegistryRoute: pluginRegistryRoute:
description: Plugin registry route custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -933,8 +958,9 @@ spec:
deployment In cores. (500m = .5 cores). Default to 100m. deployment In cores. (500m = .5 cores). Default to 100m.
type: string type: string
serverExposureStrategy: serverExposureStrategy:
description: Sets the server and workspaces exposure type. Possible description: Deprecated. The value of this flag is ignored.
values are `multi-host`, `single-host`, `default-host`. Defaults Sets the server and workspaces exposure type. Possible values
are `multi-host`, `single-host`, `default-host`. Defaults
to `multi-host`, which creates a separate ingress, or OpenShift to `multi-host`, which creates a separate ingress, or OpenShift
routes, for every required endpoint. `single-host` makes Che routes, for every required endpoint. `single-host` makes Che
exposed on a single host name with workspaces exposed on subpaths. exposed on a single host name with workspaces exposed on subpaths.

View File

@ -54,18 +54,20 @@ spec:
by the Che installation. by the Che installation.
properties: properties:
debug: debug:
description: Debug internal identity provider. description: Deprecated. The value of this flag is ignored. Debug
internal identity provider.
type: boolean type: boolean
externalIdentityProvider: externalIdentityProvider:
description: 'Instructs the Operator on whether or not to deploy description: 'Deprecated. The value of this flag is ignored. Instructs
a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether or not to deploy a dedicated Identity
the Operator on whether to deploy a dedicated Identity Provider Provider (Keycloak or RH SSO instance). Instructs the Operator
(Keycloak or RH-SSO instance). By default, a dedicated Identity on whether to deploy a dedicated Identity Provider (Keycloak or
Provider server is deployed as part of the Che installation. When RH-SSO instance). By default, a dedicated Identity Provider server
`externalIdentityProvider` is `true`, no dedicated identity provider is deployed as part of the Che installation. When `externalIdentityProvider`
will be deployed by the Operator and you will need to provide is `true`, no dedicated identity provider will be deployed by
details about the external identity provider you are about to the Operator and you will need to provide details about the external
use. See also all the other fields starting with: `identityProvider`.' identity provider you are about to use. See also all the other
fields starting with: `identityProvider`.'
type: boolean type: boolean
gatewayAuthenticationSidecarImage: gatewayAuthenticationSidecarImage:
description: Gateway sidecar responsible for authentication when description: Gateway sidecar responsible for authentication when
@ -82,18 +84,21 @@ spec:
functionality is now implemented in Traefik plugin. functionality is now implemented in Traefik plugin.
type: string type: string
identityProviderAdminUserName: identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator description: Deprecated. The value of this flag is ignored. Overrides
user. Defaults to `admin`. the name of the Identity Provider administrator user. Defaults
to `admin`.
type: string type: string
identityProviderClientId: identityProviderClientId:
description: Name of a Identity provider, Keycloak or RH-SSO, `client-id` description: Deprecated. The value of this flag is ignored. Name
that is used for Che. Override this when an external Identity of a Identity provider, Keycloak or RH-SSO, `client-id` that is
Provider is in use. See the `externalIdentityProvider` field. used for Che. Override this when an external Identity Provider
When omitted or left blank, it is set to the value of the `flavour` is in use. See the `externalIdentityProvider` field. When omitted
field suffixed with `-public`. or left blank, it is set to the value of the `flavour` field suffixed
with `-public`.
type: string type: string
identityProviderContainerResources: identityProviderContainerResources:
description: Identity provider container custom settings. description: Deprecated. The value of this flag is ignored. Identity
provider container custom settings.
properties: properties:
limits: limits:
description: Limits describes the maximum amount of compute description: Limits describes the maximum amount of compute
@ -121,19 +126,20 @@ spec:
type: object type: object
type: object type: object
identityProviderImage: identityProviderImage:
description: Overrides the container image used in the Identity description: Deprecated. The value of this flag is ignored. Overrides
Provider, Keycloak or RH-SSO, deployment. This includes the image the container image used in the Identity Provider, Keycloak or
tag. Omit it or leave it empty to use the default container image RH-SSO, deployment. This includes the image tag. Omit it or leave
provided by the Operator. it empty to use the default container image provided by the Operator.
type: string type: string
identityProviderImagePullPolicy: identityProviderImagePullPolicy:
description: Overrides the image pull policy used in the Identity description: Deprecated. The value of this flag is ignored. Overrides
Provider, Keycloak or RH-SSO, deployment. Default value is `Always` the image pull policy used in the Identity Provider, Keycloak
for `nightly`, `next` or `latest` images, and `IfNotPresent` in or RH-SSO, deployment. Default value is `Always` for `nightly`,
other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
identityProviderIngress: identityProviderIngress:
description: Ingress custom settings. description: Deprecated. The value of this flag is ignored. Ingress
custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -148,38 +154,41 @@ spec:
type: string type: string
type: object type: object
identityProviderPassword: identityProviderPassword:
description: Overrides the password of Keycloak administrator user. description: Deprecated. The value of this flag is ignored. Overrides
Override this when an external Identity Provider is in use. See the password of Keycloak administrator user. Override this when
the `externalIdentityProvider` field. When omitted or left blank, an external Identity Provider is in use. See the `externalIdentityProvider`
it is set to an auto-generated password. field. When omitted or left blank, it is set to an auto-generated
password.
type: string type: string
identityProviderPostgresPassword: identityProviderPostgresPassword:
description: Password for a Identity Provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored. Password
to connect to the database. Override this when an external Identity for a Identity Provider, Keycloak or RH-SSO, to connect to the
Provider is in use. See the `externalIdentityProvider` field. database. Override this when an external Identity Provider is
When omitted or left blank, it is set to an auto-generated password. in use. See the `externalIdentityProvider` field. When omitted
or left blank, it is set to an auto-generated password.
type: string type: string
identityProviderPostgresSecret: identityProviderPostgresSecret:
description: 'The secret that contains `password` for the Identity description: 'Deprecated. The value of this flag is ignored. The
Provider, Keycloak or RH-SSO, to connect to the database. When secret that contains `password` for the Identity Provider, Keycloak
the secret is defined, the `identityProviderPostgresPassword` or RH-SSO, to connect to the database. When the secret is defined,
is ignored. When the value is omitted or left blank, the one of the `identityProviderPostgresPassword` is ignored. When the value
following scenarios applies: 1. `identityProviderPostgresPassword` is omitted or left blank, the one of following scenarios applies:
is defined, then it will be used to connect to the database. 2. 1. `identityProviderPostgresPassword` is defined, then it will
`identityProviderPostgresPassword` is not defined, then a new be used to connect to the database. 2. `identityProviderPostgresPassword`
secret with the name `che-identity-postgres-secret` will be created is not defined, then a new secret with the name `che-identity-postgres-secret`
with an auto-generated value for `password`. The secret must have will be created with an auto-generated value for `password`. The
`app.kubernetes.io/part-of=che.eclipse.org` label.' secret must have `app.kubernetes.io/part-of=che.eclipse.org` label.'
type: string type: string
identityProviderRealm: identityProviderRealm:
description: Name of a Identity provider, Keycloak or RH-SSO, realm description: Deprecated. The value of this flag is ignored. Name
that is used for Che. Override this when an external Identity of a Identity provider, Keycloak or RH-SSO, realm that is used
Provider is in use. See the `externalIdentityProvider` field. for Che. Override this when an external Identity Provider is in
When omitted or left blank, it is set to the value of the `flavour` use. See the `externalIdentityProvider` field. When omitted or
field. left blank, it is set to the value of the `flavour` field.
type: string type: string
identityProviderRoute: identityProviderRoute:
description: Route custom settings. description: Deprecated. The value of this flag is ignored. Route
custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -200,8 +209,9 @@ spec:
type: string type: string
type: object type: object
identityProviderSecret: identityProviderSecret:
description: 'The secret that contains `user` and `password` for description: 'Deprecated. The value of this flag is ignored. The
Identity Provider. When the secret is defined, the `identityProviderAdminUserName` secret that contains `user` and `password` for Identity Provider.
When the secret is defined, the `identityProviderAdminUserName`
and `identityProviderPassword` are ignored. When the value is and `identityProviderPassword` are ignored. When the value is
omitted or left blank, the one of following scenarios applies: omitted or left blank, the one of following scenarios applies:
1. `identityProviderAdminUserName` and `identityProviderPassword` 1. `identityProviderAdminUserName` and `identityProviderPassword`
@ -220,20 +230,22 @@ spec:
Operator. Operator.
type: string type: string
initialOpenShiftOAuthUser: initialOpenShiftOAuthUser:
description: For operating with the OpenShift OAuth authentication, description: Deprecated. The value of this flag is ignored. For
create a new user account since the kubeadmin can not be used. operating with the OpenShift OAuth authentication, create a new
If the value is true, then a new OpenShift OAuth user will be user account since the kubeadmin can not be used. If the value
created for the HTPasswd identity provider. If the value is false is true, then a new OpenShift OAuth user will be created for the
and the user has already been created, then it will be removed. HTPasswd identity provider. If the value is false and the user
If value is an empty, then do nothing. The user's credentials has already been created, then it will be removed. If value is
are stored in the `openshift-oauth-user-credentials` secret in an empty, then do nothing. The user's credentials are stored in
'openshift-config' namespace by Operator. Note that this solution the `openshift-oauth-user-credentials` secret in 'openshift-config'
is Openshift 4 platform-specific. namespace by Operator. Note that this solution is Openshift 4
platform-specific.
type: boolean type: boolean
nativeUserMode: nativeUserMode:
description: Enables native user mode. Currently works only on OpenShift description: Deprecated. The value of this flag is ignored. Enables
and DevWorkspace engine. Native User mode uses OpenShift OAuth native user mode. Currently works only on OpenShift and DevWorkspace
directly as identity provider, without Keycloak. engine. Native User mode uses OpenShift OAuth directly as identity
provider, without Keycloak.
type: boolean type: boolean
oAuthClientName: oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to description: Name of the OpenShift `OAuthClient` resource used to
@ -247,17 +259,18 @@ spec:
field. field.
type: string type: string
openShiftoAuth: openShiftoAuth:
description: 'Enables the integration of the identity provider (Keycloak description: 'Deprecated. The value of this flag is ignored. Enables
/ RHSSO) with OpenShift OAuth. Empty value on OpenShift by default. the integration of the identity provider (Keycloak / RHSSO) with
This will allow users to directly login with their OpenShift user OpenShift OAuth. Empty value on OpenShift by default. This will
through the OpenShift login, and have their workspaces created allow users to directly login with their OpenShift user through
under personal OpenShift namespaces. WARNING: the `kubeadmin` the OpenShift login, and have their workspaces created under personal
user is NOT supported, and logging through it will NOT allow accessing OpenShift namespaces. WARNING: the `kubeadmin` user is NOT supported,
the Che Dashboard.' and logging through it will NOT allow accessing the Che Dashboard.'
type: boolean type: boolean
updateAdminPassword: updateAdminPassword:
description: Forces the default `admin` Che user to update password description: Deprecated. The value of this flag is ignored. Forces
on first login. Defaults to `false`. the default `admin` Che user to update password on first login.
Defaults to `false`.
type: boolean type: boolean
type: object type: object
database: database:
@ -436,13 +449,14 @@ spec:
MUST be explicitly specified: there are no defaults.' MUST be explicitly specified: there are no defaults.'
type: string type: string
ingressStrategy: ingressStrategy:
description: 'Strategy for ingress creation. Options are: `multi-host` description: 'Deprecated. The value of this flag is ignored. Strategy
(host is explicitly provided in ingress), `single-host` (host for ingress creation. Options are: `multi-host` (host is explicitly
is provided, path-based rules) and `default-host` (no host is provided in ingress), `single-host` (host is provided, path-based
provided, path-based rules). Defaults to `multi-host` Deprecated rules) and `default-host` (no host is provided, path-based rules).
in favor of `serverExposureStrategy` in the `server` section, Defaults to `multi-host` Deprecated in favor of `serverExposureStrategy`
which defines this regardless of the cluster type. When both are in the `server` section, which defines this regardless of the
defined, the `serverExposureStrategy` option takes precedence.' cluster type. When both are defined, the `serverExposureStrategy`
option takes precedence.'
type: string type: string
securityContextFsGroup: securityContextFsGroup:
description: The FSGroup in which the Che Pod and workspace Pods description: The FSGroup in which the Che Pod and workspace Pods
@ -453,12 +467,13 @@ spec:
run as. Default value is `1724`. run as. Default value is `1724`.
type: string type: string
singleHostExposureType: singleHostExposureType:
description: When the serverExposureStrategy is set to `single-host`, description: Deprecated. The value of this flag is ignored. When
the way the server, registries and workspaces are exposed is further the serverExposureStrategy is set to `single-host`, the way the
configured by this property. The possible values are `native`, server, registries and workspaces are exposed is further configured
which means that the server and workspaces are exposed using ingresses by this property. The possible values are `native`, which means
on K8s or `gateway` where the server and workspaces are exposed that the server and workspaces are exposed using ingresses on
using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik]. K8s or `gateway` where the server and workspaces are exposed using
a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik].
All the endpoints whether backed by the ingress or gateway `route` All the endpoints whether backed by the ingress or gateway `route`
always point to the subpaths on the same domain. Defaults to `native`. always point to the subpaths on the same domain. Defaults to `native`.
type: string type: string
@ -514,8 +529,9 @@ spec:
`false`. `false`.
type: string type: string
cheFlavor: cheFlavor:
description: Specifies a variation of the installation. The options description: Deprecated. The value of this flag is ignored. Specifies
are `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady a variation of the installation. The options are `che` for upstream
Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady
Workspaces] installation. Override the default value only on necessary Workspaces] installation. Override the default value only on necessary
occasions. occasions.
type: string type: string
@ -620,7 +636,8 @@ spec:
`latest` images, and `IfNotPresent` in other cases. `latest` images, and `IfNotPresent` in other cases.
type: string type: string
dashboardIngress: dashboardIngress:
description: Dashboard ingress custom settings. description: Deprecated. The value of this flag is ignored. Dashboard
ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -643,7 +660,8 @@ spec:
deployment. Defaults to 16Mi. deployment. Defaults to 16Mi.
type: string type: string
dashboardRoute: dashboardRoute:
description: Dashboard route custom settings. description: Deprecated. The value of this flag is ignored. Dashboard
route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -677,7 +695,8 @@ spec:
to use the default container image provided by the Operator. to use the default container image provided by the Operator.
type: string type: string
devfileRegistryIngress: devfileRegistryIngress:
description: The devfile registry ingress custom settings. description: Deprecated. The value of this flag is ignored. The
devfile registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -705,7 +724,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
devfileRegistryRoute: devfileRegistryRoute:
description: The devfile registry route custom settings. description: Deprecated. The value of this flag is ignored. The
devfile registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -730,8 +750,9 @@ spec:
fields. fields.
type: string type: string
disableInternalClusterSVCNames: disableInternalClusterSVCNames:
description: Disable internal cluster SVC names usage to communicate description: Deprecated. The value of this flag is ignored. Disable
between components to speed up the traffic and avoid proxy issues. internal cluster SVC names usage to communicate between components
to speed up the traffic and avoid proxy issues.
type: boolean type: boolean
externalDevfileRegistries: externalDevfileRegistries:
description: External devfile registries, that serves sample, ready-to-use description: External devfile registries, that serves sample, ready-to-use
@ -795,7 +816,8 @@ spec:
to use the default container image provided by the Operator. to use the default container image provided by the Operator.
type: string type: string
pluginRegistryIngress: pluginRegistryIngress:
description: Plugin registry ingress custom settings. description: Deprecated. The value of this flag is ignored. Plugin
registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -823,7 +845,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
pluginRegistryRoute: pluginRegistryRoute:
description: Plugin registry route custom settings. description: Deprecated. The value of this flag is ignored. Plugin
registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -897,16 +920,17 @@ spec:
In cores. (500m = .5 cores). Default to 100m. In cores. (500m = .5 cores). Default to 100m.
type: string type: string
serverExposureStrategy: serverExposureStrategy:
description: Sets the server and workspaces exposure type. Possible description: Deprecated. The value of this flag is ignored. Sets
values are `multi-host`, `single-host`, `default-host`. Defaults the server and workspaces exposure type. Possible values are `multi-host`,
to `multi-host`, which creates a separate ingress, or OpenShift `single-host`, `default-host`. Defaults to `multi-host`, which
routes, for every required endpoint. `single-host` makes Che exposed creates a separate ingress, or OpenShift routes, for every required
on a single host name with workspaces exposed on subpaths. Read endpoint. `single-host` makes Che exposed on a single host name
the docs to learn about the limitations of this approach. Also with workspaces exposed on subpaths. Read the docs to learn about
consult the `singleHostExposureType` property to further configure the limitations of this approach. Also consult the `singleHostExposureType`
how the Operator and the Che server make that happen on Kubernetes. property to further configure how the Operator and the Che server
`default-host` exposes the Che server on the host of the cluster. make that happen on Kubernetes. `default-host` exposes the Che
Read the docs to learn about the limitations of this approach. server on the host of the cluster. Read the docs to learn about
the limitations of this approach.
type: string type: string
serverMemoryLimit: serverMemoryLimit:
description: Overrides the memory limit used in the Che server deployment. description: Overrides the memory limit used in the Che server deployment.

View File

@ -57,14 +57,16 @@ spec:
used by the Che installation. used by the Che installation.
properties: properties:
debug: debug:
description: Debug internal identity provider. description: Deprecated. The value of this flag is ignored.
Debug internal identity provider.
type: boolean type: boolean
externalIdentityProvider: externalIdentityProvider:
description: 'Instructs the Operator on whether or not to deploy description: 'Deprecated. The value of this flag is ignored.
a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether or not to deploy a dedicated
Instructs the Operator on whether to deploy a dedicated Identity Identity Provider (Keycloak or RH SSO instance). Instructs
Provider (Keycloak or RH-SSO instance). By default, a dedicated the Operator on whether to deploy a dedicated Identity Provider
Identity Provider server is deployed as part of the Che installation. (Keycloak or RH-SSO instance). By default, a dedicated Identity
Provider server is deployed as part of the Che installation.
When `externalIdentityProvider` is `true`, no dedicated identity When `externalIdentityProvider` is `true`, no dedicated identity
provider will be deployed by the Operator and you will need provider will be deployed by the Operator and you will need
to provide details about the external identity provider you to provide details about the external identity provider you
@ -86,18 +88,21 @@ spec:
Sidecar functionality is now implemented in Traefik plugin. Sidecar functionality is now implemented in Traefik plugin.
type: string type: string
identityProviderAdminUserName: identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator description: Deprecated. The value of this flag is ignored.
Overrides the name of the Identity Provider administrator
user. Defaults to `admin`. user. Defaults to `admin`.
type: string type: string
identityProviderClientId: identityProviderClientId:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
`client-id` that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, `client-id`
Identity Provider is in use. See the `externalIdentityProvider` that is used for Che. Override this when an external Identity
field. When omitted or left blank, it is set to the value Provider is in use. See the `externalIdentityProvider` field.
of the `flavour` field suffixed with `-public`. When omitted or left blank, it is set to the value of the
`flavour` field suffixed with `-public`.
type: string type: string
identityProviderContainerResources: identityProviderContainerResources:
description: Identity provider container custom settings. description: Deprecated. The value of this flag is ignored.
Identity provider container custom settings.
properties: properties:
limits: limits:
description: Limits describes the maximum amount of compute description: Limits describes the maximum amount of compute
@ -125,19 +130,22 @@ spec:
type: object type: object
type: object type: object
identityProviderImage: identityProviderImage:
description: Overrides the container image used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. This includes the Overrides the container image used in the Identity Provider,
image tag. Omit it or leave it empty to use the default container Keycloak or RH-SSO, deployment. This includes the image tag.
image provided by the Operator. Omit it or leave it empty to use the default container image
provided by the Operator.
type: string type: string
identityProviderImagePullPolicy: identityProviderImagePullPolicy:
description: Overrides the image pull policy used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. Default value is Overrides the image pull policy used in the Identity Provider,
`Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` Keycloak or RH-SSO, deployment. Default value is `Always`
for `nightly`, `next` or `latest` images, and `IfNotPresent`
in other cases. in other cases.
type: string type: string
identityProviderIngress: identityProviderIngress:
description: Ingress custom settings. description: Deprecated. The value of this flag is ignored.
Ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -153,25 +161,27 @@ spec:
type: string type: string
type: object type: object
identityProviderPassword: identityProviderPassword:
description: Overrides the password of Keycloak administrator description: Deprecated. The value of this flag is ignored.
user. Override this when an external Identity Provider is Overrides the password of Keycloak administrator user. Override
in use. See the `externalIdentityProvider` field. When omitted this when an external Identity Provider is in use. See the
or left blank, it is set to an auto-generated password. `externalIdentityProvider` field. When omitted or left blank,
it is set to an auto-generated password.
type: string type: string
identityProviderPostgresPassword: identityProviderPostgresPassword:
description: Password for a Identity Provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
to connect to the database. Override this when an external Password for a Identity Provider, Keycloak or RH-SSO, to connect
Identity Provider is in use. See the `externalIdentityProvider` to the database. Override this when an external Identity Provider
field. When omitted or left blank, it is set to an auto-generated is in use. See the `externalIdentityProvider` field. When
password. omitted or left blank, it is set to an auto-generated password.
type: string type: string
identityProviderPostgresSecret: identityProviderPostgresSecret:
description: 'The secret that contains `password` for the Identity description: 'Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, to connect to the database. The secret that contains `password` for the Identity Provider,
When the secret is defined, the `identityProviderPostgresPassword` Keycloak or RH-SSO, to connect to the database. When the secret
is ignored. When the value is omitted or left blank, the one is defined, the `identityProviderPostgresPassword` is ignored.
of following scenarios applies: 1. `identityProviderPostgresPassword` When the value is omitted or left blank, the one of following
is defined, then it will be used to connect to the database. scenarios applies: 1. `identityProviderPostgresPassword` is
defined, then it will be used to connect to the database.
2. `identityProviderPostgresPassword` is not defined, then 2. `identityProviderPostgresPassword` is not defined, then
a new secret with the name `che-identity-postgres-secret` a new secret with the name `che-identity-postgres-secret`
will be created with an auto-generated value for `password`. will be created with an auto-generated value for `password`.
@ -179,14 +189,16 @@ spec:
label.' label.'
type: string type: string
identityProviderRealm: identityProviderRealm:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
realm that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, realm that
Identity Provider is in use. See the `externalIdentityProvider` is used for Che. Override this when an external Identity Provider
field. When omitted or left blank, it is set to the value is in use. See the `externalIdentityProvider` field. When
of the `flavour` field. omitted or left blank, it is set to the value of the `flavour`
field.
type: string type: string
identityProviderRoute: identityProviderRoute:
description: Route custom settings. description: Deprecated. The value of this flag is ignored.
Route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -208,8 +220,9 @@ spec:
type: string type: string
type: object type: object
identityProviderSecret: identityProviderSecret:
description: 'The secret that contains `user` and `password` description: 'Deprecated. The value of this flag is ignored.
for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` The secret that contains `user` and `password` for Identity
Provider. When the secret is defined, the `identityProviderAdminUserName`
and `identityProviderPassword` are ignored. When the value and `identityProviderPassword` are ignored. When the value
is omitted or left blank, the one of following scenarios applies: is omitted or left blank, the one of following scenarios applies:
1. `identityProviderAdminUserName` and `identityProviderPassword` 1. `identityProviderAdminUserName` and `identityProviderPassword`
@ -228,20 +241,22 @@ spec:
set by the Operator. set by the Operator.
type: string type: string
initialOpenShiftOAuthUser: initialOpenShiftOAuthUser:
description: For operating with the OpenShift OAuth authentication, description: Deprecated. The value of this flag is ignored.
create a new user account since the kubeadmin can not be used. For operating with the OpenShift OAuth authentication, create
If the value is true, then a new OpenShift OAuth user will a new user account since the kubeadmin can not be used. If
be created for the HTPasswd identity provider. If the value the value is true, then a new OpenShift OAuth user will be
is false and the user has already been created, then it will created for the HTPasswd identity provider. If the value is
false and the user has already been created, then it will
be removed. If value is an empty, then do nothing. The user's be removed. If value is an empty, then do nothing. The user's
credentials are stored in the `openshift-oauth-user-credentials` credentials are stored in the `openshift-oauth-user-credentials`
secret in 'openshift-config' namespace by Operator. Note that secret in 'openshift-config' namespace by Operator. Note that
this solution is Openshift 4 platform-specific. this solution is Openshift 4 platform-specific.
type: boolean type: boolean
nativeUserMode: nativeUserMode:
description: Enables native user mode. Currently works only description: Deprecated. The value of this flag is ignored.
on OpenShift and DevWorkspace engine. Native User mode uses Enables native user mode. Currently works only on OpenShift
OpenShift OAuth directly as identity provider, without Keycloak. and DevWorkspace engine. Native User mode uses OpenShift OAuth
directly as identity provider, without Keycloak.
type: boolean type: boolean
oAuthClientName: oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used description: Name of the OpenShift `OAuthClient` resource used
@ -255,17 +270,19 @@ spec:
field. field.
type: string type: string
openShiftoAuth: openShiftoAuth:
description: 'Enables the integration of the identity provider description: 'Deprecated. The value of this flag is ignored.
(Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift Enables the integration of the identity provider (Keycloak
by default. This will allow users to directly login with their / RHSSO) with OpenShift OAuth. Empty value on OpenShift by
default. This will allow users to directly login with their
OpenShift user through the OpenShift login, and have their OpenShift user through the OpenShift login, and have their
workspaces created under personal OpenShift namespaces. WARNING: workspaces created under personal OpenShift namespaces. WARNING:
the `kubeadmin` user is NOT supported, and logging through the `kubeadmin` user is NOT supported, and logging through
it will NOT allow accessing the Che Dashboard.' it will NOT allow accessing the Che Dashboard.'
type: boolean type: boolean
updateAdminPassword: updateAdminPassword:
description: Forces the default `admin` Che user to update password description: Deprecated. The value of this flag is ignored.
on first login. Defaults to `false`. Forces the default `admin` Che user to update password on
first login. Defaults to `false`.
type: boolean type: boolean
type: object type: object
database: database:
@ -452,10 +469,11 @@ spec:
This MUST be explicitly specified: there are no defaults.' This MUST be explicitly specified: there are no defaults.'
type: string type: string
ingressStrategy: ingressStrategy:
description: 'Strategy for ingress creation. Options are: `multi-host` description: 'Deprecated. The value of this flag is ignored.
(host is explicitly provided in ingress), `single-host` (host Strategy for ingress creation. Options are: `multi-host` (host
is provided, path-based rules) and `default-host` (no host is explicitly provided in ingress), `single-host` (host is
is provided, path-based rules). Defaults to `multi-host` Deprecated provided, path-based rules) and `default-host` (no host is
provided, path-based rules). Defaults to `multi-host` Deprecated
in favor of `serverExposureStrategy` in the `server` section, in favor of `serverExposureStrategy` in the `server` section,
which defines this regardless of the cluster type. When both which defines this regardless of the cluster type. When both
are defined, the `serverExposureStrategy` option takes precedence.' are defined, the `serverExposureStrategy` option takes precedence.'
@ -469,15 +487,16 @@ spec:
run as. Default value is `1724`. run as. Default value is `1724`.
type: string type: string
singleHostExposureType: singleHostExposureType:
description: When the serverExposureStrategy is set to `single-host`, description: Deprecated. The value of this flag is ignored.
the way the server, registries and workspaces are exposed When the serverExposureStrategy is set to `single-host`, the
is further configured by this property. The possible values way the server, registries and workspaces are exposed is further
are `native`, which means that the server and workspaces are configured by this property. The possible values are `native`,
exposed using ingresses on K8s or `gateway` where the server which means that the server and workspaces are exposed using
and workspaces are exposed using a custom gateway based on ingresses on K8s or `gateway` where the server and workspaces
link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik].
whether backed by the ingress or gateway `route` always point All the endpoints whether backed by the ingress or gateway
to the subpaths on the same domain. Defaults to `native`. `route` always point to the subpaths on the same domain. Defaults
to `native`.
type: string type: string
tlsSecretName: tlsSecretName:
description: Name of a secret that will be used to setup ingress description: Name of a secret that will be used to setup ingress
@ -531,9 +550,9 @@ spec:
to `false`. to `false`.
type: string type: string
cheFlavor: cheFlavor:
description: Specifies a variation of the installation. The description: Deprecated. The value of this flag is ignored.
options are `che` for upstream Che installations, or `codeready` Specifies a variation of the installation. The options are
for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady
Workspaces] installation. Override the default value only Workspaces] installation. Override the default value only
on necessary occasions. on necessary occasions.
type: string type: string
@ -642,7 +661,8 @@ spec:
or `latest` images, and `IfNotPresent` in other cases. or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
dashboardIngress: dashboardIngress:
description: Dashboard ingress custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -666,7 +686,8 @@ spec:
deployment. Defaults to 16Mi. deployment. Defaults to 16Mi.
type: string type: string
dashboardRoute: dashboardRoute:
description: Dashboard route custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -702,7 +723,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
devfileRegistryIngress: devfileRegistryIngress:
description: The devfile registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -731,7 +753,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
devfileRegistryRoute: devfileRegistryRoute:
description: The devfile registry route custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -757,9 +780,9 @@ spec:
fields. fields.
type: string type: string
disableInternalClusterSVCNames: disableInternalClusterSVCNames:
description: Disable internal cluster SVC names usage to communicate description: Deprecated. The value of this flag is ignored.
between components to speed up the traffic and avoid proxy Disable internal cluster SVC names usage to communicate between
issues. components to speed up the traffic and avoid proxy issues.
type: boolean type: boolean
externalDevfileRegistries: externalDevfileRegistries:
description: External devfile registries, that serves sample, description: External devfile registries, that serves sample,
@ -824,7 +847,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
pluginRegistryIngress: pluginRegistryIngress:
description: Plugin registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -853,7 +877,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
pluginRegistryRoute: pluginRegistryRoute:
description: Plugin registry route custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -929,8 +954,9 @@ spec:
deployment In cores. (500m = .5 cores). Default to 100m. deployment In cores. (500m = .5 cores). Default to 100m.
type: string type: string
serverExposureStrategy: serverExposureStrategy:
description: Sets the server and workspaces exposure type. Possible description: Deprecated. The value of this flag is ignored.
values are `multi-host`, `single-host`, `default-host`. Defaults Sets the server and workspaces exposure type. Possible values
are `multi-host`, `single-host`, `default-host`. Defaults
to `multi-host`, which creates a separate ingress, or OpenShift to `multi-host`, which creates a separate ingress, or OpenShift
routes, for every required endpoint. `single-host` makes Che routes, for every required endpoint. `single-host` makes Che
exposed on a single host name with workspaces exposed on subpaths. exposed on a single host name with workspaces exposed on subpaths.

View File

@ -77,8 +77,6 @@ spec:
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_postgres_13_3 - name: RELATED_IMAGE_postgres_13_3
value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2
- name: RELATED_IMAGE_keycloak
value: quay.io/eclipse/che-keycloak:next
- name: RELATED_IMAGE_che_workspace_plugin_broker_metadata - name: RELATED_IMAGE_che_workspace_plugin_broker_metadata
value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0 value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0
- name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts - name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts

View File

@ -24,8 +24,6 @@ spec:
cheWorkspaceClusterRole: '' cheWorkspaceClusterRole: ''
# When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git. # When enabled, the certificate from `che-git-self-signed-cert` ConfigMap will be propagated to the Che components and provide particular configuration for Git.
gitSelfSignedCert: false gitSelfSignedCert: false
# Deprecated. Instructs the Operator to deploy Che in TLS mode. Disabling TLS sometimes cause malfunction of some Che components.
tlsSupport: true
# URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables # URL (protocol+host name) of the proxy server. This drives the appropriate changes in the `JAVA_OPTS` and `https(s)_proxy` variables
# in the Che server and workspaces containers. # in the Che server and workspaces containers.
# Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration # Only use when configuring a proxy is required. Operator respects OpenShift cluster wide proxy configuration

View File

@ -25,7 +25,6 @@ import (
identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider" identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
imagepuller "github.com/eclipse-che/che-operator/pkg/deploy/image-puller" imagepuller "github.com/eclipse-che/che-operator/pkg/deploy/image-puller"
"github.com/eclipse-che/che-operator/pkg/deploy/migration" "github.com/eclipse-che/che-operator/pkg/deploy/migration"
openshiftoauth "github.com/eclipse-che/che-operator/pkg/deploy/openshift-oauth"
"github.com/eclipse-che/che-operator/pkg/deploy/pluginregistry" "github.com/eclipse-che/che-operator/pkg/deploy/pluginregistry"
"github.com/eclipse-che/che-operator/pkg/deploy/postgres" "github.com/eclipse-che/che-operator/pkg/deploy/postgres"
"github.com/eclipse-che/che-operator/pkg/deploy/rbac" "github.com/eclipse-che/che-operator/pkg/deploy/rbac"
@ -93,9 +92,6 @@ func NewReconciler(
} }
reconcileManager.RegisterReconciler(imagepuller.NewImagePuller()) reconcileManager.RegisterReconciler(imagepuller.NewImagePuller())
openShiftOAuthUser := openshiftoauth.NewOpenShiftOAuthUser()
reconcileManager.RegisterReconciler(openShiftOAuthUser)
reconcileManager.RegisterReconciler(openshiftoauth.NewOpenShiftOAuth(openShiftOAuthUser))
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler()) reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler()) reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
reconcileManager.RegisterReconciler(devworkspace.NewDevWorkspaceReconciler()) reconcileManager.RegisterReconciler(devworkspace.NewDevWorkspaceReconciler())

View File

@ -1,297 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package che
import (
"context"
"os"
"time"
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
console "github.com/openshift/api/console/v1"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
oauthv1 "github.com/openshift/api/oauth/v1"
routev1 "github.com/openshift/api/route/v1"
userv1 "github.com/openshift/api/user/v1"
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery"
fakeDiscovery "k8s.io/client-go/discovery/fake"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
configv1 "github.com/openshift/api/config/v1"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"testing"
)
var (
namespace = "eclipse-che"
)
func TestCheController(t *testing.T) {
var err error
util.IsOpenShift = true
util.IsOpenShift4 = true
cl, dc, scheme := Init()
// Create a ReconcileChe object with the scheme and fake client
r := NewReconciler(cl, cl, dc, &scheme, "")
// get CR
checluster := &orgv1.CheCluster{}
err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
assert.Nil(t, err)
// Mock request to simulate Reconcile() being called on an event for a
// watched resource .
req := reconcile.Request{
NamespacedName: types.NamespacedName{
Name: os.Getenv("CHE_FLAVOR"),
Namespace: namespace,
},
}
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.DevfileRegistryName, Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.PluginRegistryName, Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
// reade checluster
err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
assert.Nil(t, err)
// update CR and make sure Che configmap has been updated
checluster.Spec.Server.TlsSupport = true
err = cl.Update(context.TODO(), checluster)
assert.Nil(t, err)
// reconcile several times
reconcileLoops := 4
for i := 0; i < reconcileLoops; i++ {
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
}
// get configmap
cm := &corev1.ConfigMap{}
err = cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: checluster.Namespace}, cm)
assert.Nil(t, err)
assert.Equal(t, cm.Data["CHE_INFRA_OPENSHIFT_TLS__ENABLED"], "true")
// Custom ConfigMap should be gone
assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: "custom", Namespace: checluster.Namespace}, &corev1.ConfigMap{}))
// Get the custom role binding that should have been created for the role we passed in
assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: "che-workspace-custom", Namespace: checluster.Namespace}, &rbacv1.RoleBinding{}))
route := &routev1.Route{}
err = cl.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultCheFlavor(checluster), Namespace: checluster.Namespace}, route)
assert.Nil(t, err)
assert.Equal(t, route.Spec.TLS.Termination, routev1.TLSTerminationType("edge"))
// reread checluster
err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
assert.Nil(t, err)
// update CR and make sure Che configmap has been updated
checluster.Spec.Auth.OpenShiftoAuth = util.NewBoolPointer(true)
err = cl.Update(context.TODO(), checluster)
assert.Nil(t, err)
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
// get configmap and check if identity provider name and workspace project name are correctly set
cm = &corev1.ConfigMap{}
err = cl.Get(context.TODO(), types.NamespacedName{Name: "che", Namespace: checluster.Namespace}, cm)
assert.Nil(t, err)
assert.Equal(t, cm.Data["CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"], "openshift-v4")
// reread checluster
err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
assert.Nil(t, err)
assert.True(t, util.IsObjectExists(cl, types.NamespacedName{Name: checluster.Spec.Auth.OAuthClientName}, &oauthv1.OAuthClient{}))
// check if a new Postgres deployment is not created when spec.Database.ExternalDB is true
checluster.Spec.Database.ExternalDb = true
err = cl.Update(context.TODO(), checluster)
assert.Nil(t, err)
postgresDeployment := &appsv1.Deployment{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.PostgresName, Namespace: checluster.Namespace}, postgresDeployment)
assert.Nil(t, err)
err = r.client.Delete(context.TODO(), postgresDeployment)
assert.Nil(t, err)
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: deploy.PostgresName, Namespace: checluster.Namespace}, &appsv1.Deployment{}))
// check of storageClassName ends up in pvc spec
fakeStorageClassName := "fake-storage-class-name"
checluster.Spec.Storage.PostgresPVCStorageClassName = fakeStorageClassName
checluster.Spec.Database.ExternalDb = false
err = r.client.Update(context.TODO(), checluster)
assert.Nil(t, err)
pvc := &corev1.PersistentVolumeClaim{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: checluster.Namespace}, pvc)
assert.Nil(t, err)
err = r.client.Delete(context.TODO(), pvc)
assert.Nil(t, err)
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
pvc = &corev1.PersistentVolumeClaim{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: deploy.DefaultPostgresVolumeClaimName, Namespace: checluster.Namespace}, pvc)
assert.Nil(t, err)
assert.Equal(t, fakeStorageClassName, *pvc.Spec.StorageClassName)
// reread checluster
err = cl.Get(context.TODO(), types.NamespacedName{Name: os.Getenv("CHE_FLAVOR"), Namespace: namespace}, checluster)
assert.Nil(t, err)
assert.Equal(t, "https://eclipse.org", checluster.Status.CheURL)
// check if oAuthClient is deleted after CR is deleted (finalizer logic)
// since fake api does not set deletion timestamp, CR is updated in tests rather than deleted
deletionTimestamp := &metav1.Time{Time: time.Now()}
checluster.DeletionTimestamp = deletionTimestamp
err = r.client.Update(context.TODO(), checluster)
assert.Nil(t, err)
_, err = r.Reconcile(context.TODO(), req)
assert.Nil(t, err)
assert.False(t, util.IsObjectExists(cl, types.NamespacedName{Name: checluster.Spec.Auth.OAuthClientName}, &oauthv1.OAuthClient{}))
}
func Init() (client.Client, discovery.DiscoveryInterface, runtime.Scheme) {
objs, ds, scheme := createAPIObjects()
oAuth := &configv1.OAuth{}
oAuthClient := &oauthv1.OAuthClient{}
users := &userv1.UserList{}
user := &userv1.User{}
// Register operator types with the runtime scheme
scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, oAuth)
scheme.AddKnownTypes(oauthv1.SchemeGroupVersion, oAuthClient)
scheme.AddKnownTypes(userv1.SchemeGroupVersion, users, user)
scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Proxy{})
// Create a fake client to mock API calls
return fake.NewFakeClient(objs...), ds, scheme
}
func createAPIObjects() ([]runtime.Object, discovery.DiscoveryInterface, runtime.Scheme) {
pgPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "fake-pg-pod",
Namespace: "eclipse-che",
Labels: map[string]string{
"component": deploy.PostgresName,
},
},
}
// A CheCluster custom resource with metadata and spec
cheCR := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("CHE_FLAVOR"),
Namespace: namespace,
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CheHost: "eclipse.org",
CheWorkspaceClusterRole: "cluster-admin",
},
},
}
route := &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: deploy.DefaultCheFlavor(cheCR),
Namespace: namespace,
},
}
packageManifest := &packagesv1.PackageManifest{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
}
oAuth := &configv1.OAuth{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
}
// Objects to track in the fake client.
objs := []runtime.Object{
cheCR, pgPod, route, packageManifest, oAuth,
}
// Register operator types with the runtime scheme
scheme := scheme.Scheme
scheme.AddKnownTypes(orgv1.GroupVersion, cheCR)
scheme.AddKnownTypes(routev1.SchemeGroupVersion, route)
scheme.AddKnownTypes(console.GroupVersion, &console.ConsoleLink{})
chev1alpha1.AddToScheme(scheme)
packagesv1.AddToScheme(scheme)
operatorsv1.AddToScheme(scheme)
operatorsv1alpha1.AddToScheme(scheme)
cli := fakeclientset.NewSimpleClientset()
fakeDiscovery, ok := cli.Discovery().(*fakeDiscovery.FakeDiscovery)
if !ok {
logrus.Error("Error creating fake discovery client")
os.Exit(1)
}
// Create a fake client to mock API calls
return objs, fakeDiscovery, *scheme
}

View File

@ -46,7 +46,7 @@ func (v *CheClusterValidator) Reconcile(ctx *deploy.DeployContext) (reconcile.Re
} }
} }
workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(ctx.CheCluster) workspaceNamespaceDefault := deploy.GetWorkspaceNamespaceDefault(ctx.CheCluster)
if strings.Index(workspaceNamespaceDefault, "<username>") == -1 && strings.Index(workspaceNamespaceDefault, "<userid>") == -1 { if strings.Index(workspaceNamespaceDefault, "<username>") == -1 && strings.Index(workspaceNamespaceDefault, "<userid>") == -1 {
return reconcile.Result{}, false, fmt.Errorf(NamespaceStrategyErrorMessage, workspaceNamespaceDefault) return reconcile.Result{}, false, fmt.Errorf(NamespaceStrategyErrorMessage, workspaceNamespaceDefault)
} }

View File

@ -61,8 +61,6 @@ func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() { cheClusterProxyConf.NoProxy = deploy.MergeNonProxy(cheClusterProxyConf.NoProxy, ".svc")
cheClusterProxyConf.NoProxy = deploy.MergeNonProxy(cheClusterProxyConf.NoProxy, ".svc")
}
return cheClusterProxyConf, nil return cheClusterProxyConf, nil
} }

View File

@ -26,7 +26,6 @@ import (
fakeDiscovery "k8s.io/client-go/discovery/fake" fakeDiscovery "k8s.io/client-go/discovery/fake"
fakeclientset "k8s.io/client-go/kubernetes/fake" fakeclientset "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/fake"
logf "sigs.k8s.io/controller-runtime/pkg/log" logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/log/zap"
@ -250,10 +249,9 @@ func TestReadProxyConfiguration(t *testing.T) {
}, },
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true), ProxyURL: "http://proxy",
ProxyURL: "http://proxy", ProxyPort: "3128",
ProxyPort: "3128", NonProxyHosts: "host1",
NonProxyHosts: "host1",
}, },
}, },
}, },
@ -269,7 +267,7 @@ func TestReadProxyConfiguration(t *testing.T) {
HttpsPassword: "", HttpsPassword: "",
HttpsHost: "proxy", HttpsHost: "proxy",
HttpsPort: "3128", HttpsPort: "3128",
NoProxy: "host1", NoProxy: "host1,.svc",
TrustedCAMapName: "", TrustedCAMapName: "",
}, },
}, },

View File

@ -163,9 +163,6 @@ func backupDatabases(bctx *BackupContext, destDir string) (bool, error) {
databasesToBackup := []string{ databasesToBackup := []string{
bctx.cheCR.Spec.Database.ChePostgresDb, bctx.cheCR.Spec.Database.ChePostgresDb,
} }
if !bctx.cheCR.Spec.DevWorkspace.Enable {
databasesToBackup = append(databasesToBackup, "keycloak")
}
k8sClient := util.GetK8Client() k8sClient := util.GetK8Client()
postgresPodName, err := k8sClient.GetDeploymentPod(deploy.PostgresName, bctx.namespace) postgresPodName, err := k8sClient.GetDeploymentPod(deploy.PostgresName, bctx.namespace)
@ -256,12 +253,6 @@ func backupSecrets(bctx *BackupContext, destDir string) (bool, error) {
if bctx.cheCR.Spec.Database.ChePostgresSecret != "" { if bctx.cheCR.Spec.Database.ChePostgresSecret != "" {
secretsNames = append(secretsNames, bctx.cheCR.Spec.Database.ChePostgresSecret) secretsNames = append(secretsNames, bctx.cheCR.Spec.Database.ChePostgresSecret)
} }
if bctx.cheCR.Spec.Auth.IdentityProviderPostgresSecret != "" {
secretsNames = append(secretsNames, bctx.cheCR.Spec.Auth.IdentityProviderPostgresSecret)
}
if bctx.cheCR.Spec.Auth.IdentityProviderSecret != "" {
secretsNames = append(secretsNames, bctx.cheCR.Spec.Auth.IdentityProviderSecret)
}
// Retrieve and save each secret // Retrieve and save each secret
for _, secretName := range secretsNames { for _, secretName := range secretsNames {

View File

@ -111,14 +111,6 @@ func RestoreChe(rctx *RestoreContext, dataDir string) (bool, error) {
return done, err return done, err
} }
if !rctx.cheCR.Spec.DevWorkspace.Enable {
// After Keycloak's database restoring, it is required to restart Keycloak to invalidate its cache.
done, err = deleteKeycloakPod(rctx)
if err != nil || !done {
return done, err
}
}
rctx.state.cheDatabaseRestored = true rctx.state.cheDatabaseRestored = true
if err := rctx.UpdateRestoreStatus(); err != nil { if err := rctx.UpdateRestoreStatus(); err != nil {
return false, err return false, err
@ -238,24 +230,6 @@ func waitPreviousInstallationDeleted(rctx *RestoreContext) (bool, error) {
return true, nil return true, nil
} }
func deleteKeycloakPod(rctx *RestoreContext) (bool, error) {
k8sClient := util.GetK8Client()
keycloakPodName, err := k8sClient.GetDeploymentPod(deploy.IdentityProviderName, rctx.namespace)
if err != nil {
// Keycloak pod is already deleted, just skip it
return true, nil
}
keycloakPodNsN := types.NamespacedName{Name: keycloakPodName, Namespace: rctx.namespace}
keycloakPod := &corev1.Pod{}
if err := rctx.r.nonCachingClient.Get(context.TODO(), keycloakPodNsN, keycloakPod); err != nil {
return false, err
}
if err := rctx.r.nonCachingClient.Delete(context.TODO(), keycloakPod); err != nil {
return false, err
}
return true, nil
}
func restoreCheResources(rctx *RestoreContext, dataDir string) (bool, error) { func restoreCheResources(rctx *RestoreContext, dataDir string) (bool, error) {
partsToRestore := []func(*RestoreContext, string) (bool, error){ partsToRestore := []func(*RestoreContext, string) (bool, error){
restoreConfigMaps, restoreConfigMaps,
@ -406,10 +380,6 @@ func readAndAdaptCheCRFromBackup(rctx *RestoreContext, dataDir string) (*chev1.C
cheCR.Spec.Server.CheHost = "" cheCR.Spec.Server.CheHost = ""
} }
} }
if !cheCR.Spec.Auth.ExternalIdentityProvider {
// Let operator set the URL automatically
cheCR.Spec.Auth.IdentityProviderURL = ""
}
return cheCR, true, nil return cheCR, true, nil
} }
@ -466,7 +436,6 @@ func dropDatabases(rctx *RestoreContext) {
databasesToDrop := []string{ databasesToDrop := []string{
rctx.cheCR.Spec.Database.ChePostgresDb, rctx.cheCR.Spec.Database.ChePostgresDb,
"keycloak",
} }
k8sClient := util.GetK8Client() k8sClient := util.GetK8Client()
@ -525,24 +494,6 @@ func restoreDatabase(rctx *RestoreContext, dataDir string) (bool, error) {
} }
return false, err return false, err
} }
if rctx.cheCR.Spec.Server.ServerExposureStrategy == "multi-host" {
// Some databases contain values bind to cluster and/or namespace
// These values should be adjusted according to new environmant.
pathcDatabaseScript, err := getPatchDatabaseScript(rctx, dbName, dataDir)
if err != nil {
return false, err
}
if pathcDatabaseScript != "" {
execReason := fmt.Sprintf("patching %s database", dbName)
if output, err := k8sClient.DoExecIntoPod(rctx.namespace, postgresPodName, pathcDatabaseScript, execReason); err != nil {
if output != "" {
logrus.Error(output)
}
return false, err
}
}
}
} }
} }
@ -565,8 +516,6 @@ func getDatabaseOwner(rctx *RestoreContext, dbName string) (string, error) {
return "", err return "", err
} }
return string(secret.Data["user"]), nil return string(secret.Data["user"]), nil
case "keycloak":
return "keycloak", nil
default: default:
return "postgres", nil return "postgres", nil
} }
@ -616,14 +565,6 @@ func getPatchDatabaseScript(rctx *RestoreContext, dbName string, dataDir string)
script += "\n" + getReplaceInColumnScript(dbName, "devfile_project", "location", oldAppsSubStr, newAppsSubStr) script += "\n" + getReplaceInColumnScript(dbName, "devfile_project", "location", oldAppsSubStr, newAppsSubStr)
} }
return script, nil return script, nil
case "keycloak":
if !shouldPatchUrls {
// No need to do anything, restoring into the same cluster and the same namespace
break
}
script := getReplaceInColumnScript(dbName, "redirect_uris", "value", oldAppsSubStr, newAppsSubStr) + "\n" +
getReplaceInColumnScript(dbName, "web_origins", "value", oldAppsSubStr, newAppsSubStr)
return script, nil
} }
return "", nil return "", nil
} }

View File

@ -57,14 +57,16 @@ spec:
used by the Che installation. used by the Che installation.
properties: properties:
debug: debug:
description: Debug internal identity provider. description: Deprecated. The value of this flag is ignored.
Debug internal identity provider.
type: boolean type: boolean
externalIdentityProvider: externalIdentityProvider:
description: 'Instructs the Operator on whether or not to deploy description: 'Deprecated. The value of this flag is ignored.
a dedicated Identity Provider (Keycloak or RH SSO instance). Instructs the Operator on whether or not to deploy a dedicated
Instructs the Operator on whether to deploy a dedicated Identity Identity Provider (Keycloak or RH SSO instance). Instructs
Provider (Keycloak or RH-SSO instance). By default, a dedicated the Operator on whether to deploy a dedicated Identity Provider
Identity Provider server is deployed as part of the Che installation. (Keycloak or RH-SSO instance). By default, a dedicated Identity
Provider server is deployed as part of the Che installation.
When `externalIdentityProvider` is `true`, no dedicated identity When `externalIdentityProvider` is `true`, no dedicated identity
provider will be deployed by the Operator and you will need provider will be deployed by the Operator and you will need
to provide details about the external identity provider you to provide details about the external identity provider you
@ -86,18 +88,21 @@ spec:
Sidecar functionality is now implemented in Traefik plugin. Sidecar functionality is now implemented in Traefik plugin.
type: string type: string
identityProviderAdminUserName: identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator description: Deprecated. The value of this flag is ignored.
Overrides the name of the Identity Provider administrator
user. Defaults to `admin`. user. Defaults to `admin`.
type: string type: string
identityProviderClientId: identityProviderClientId:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
`client-id` that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, `client-id`
Identity Provider is in use. See the `externalIdentityProvider` that is used for Che. Override this when an external Identity
field. When omitted or left blank, it is set to the value Provider is in use. See the `externalIdentityProvider` field.
of the `flavour` field suffixed with `-public`. When omitted or left blank, it is set to the value of the
`flavour` field suffixed with `-public`.
type: string type: string
identityProviderContainerResources: identityProviderContainerResources:
description: Identity provider container custom settings. description: Deprecated. The value of this flag is ignored.
Identity provider container custom settings.
properties: properties:
limits: limits:
description: Limits describes the maximum amount of compute description: Limits describes the maximum amount of compute
@ -125,19 +130,22 @@ spec:
type: object type: object
type: object type: object
identityProviderImage: identityProviderImage:
description: Overrides the container image used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. This includes the Overrides the container image used in the Identity Provider,
image tag. Omit it or leave it empty to use the default container Keycloak or RH-SSO, deployment. This includes the image tag.
image provided by the Operator. Omit it or leave it empty to use the default container image
provided by the Operator.
type: string type: string
identityProviderImagePullPolicy: identityProviderImagePullPolicy:
description: Overrides the image pull policy used in the Identity description: Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, deployment. Default value is Overrides the image pull policy used in the Identity Provider,
`Always` for `nightly`, `next` or `latest` images, and `IfNotPresent` Keycloak or RH-SSO, deployment. Default value is `Always`
for `nightly`, `next` or `latest` images, and `IfNotPresent`
in other cases. in other cases.
type: string type: string
identityProviderIngress: identityProviderIngress:
description: Ingress custom settings. description: Deprecated. The value of this flag is ignored.
Ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -153,25 +161,27 @@ spec:
type: string type: string
type: object type: object
identityProviderPassword: identityProviderPassword:
description: Overrides the password of Keycloak administrator description: Deprecated. The value of this flag is ignored.
user. Override this when an external Identity Provider is Overrides the password of Keycloak administrator user. Override
in use. See the `externalIdentityProvider` field. When omitted this when an external Identity Provider is in use. See the
or left blank, it is set to an auto-generated password. `externalIdentityProvider` field. When omitted or left blank,
it is set to an auto-generated password.
type: string type: string
identityProviderPostgresPassword: identityProviderPostgresPassword:
description: Password for a Identity Provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
to connect to the database. Override this when an external Password for a Identity Provider, Keycloak or RH-SSO, to connect
Identity Provider is in use. See the `externalIdentityProvider` to the database. Override this when an external Identity Provider
field. When omitted or left blank, it is set to an auto-generated is in use. See the `externalIdentityProvider` field. When
password. omitted or left blank, it is set to an auto-generated password.
type: string type: string
identityProviderPostgresSecret: identityProviderPostgresSecret:
description: 'The secret that contains `password` for the Identity description: 'Deprecated. The value of this flag is ignored.
Provider, Keycloak or RH-SSO, to connect to the database. The secret that contains `password` for the Identity Provider,
When the secret is defined, the `identityProviderPostgresPassword` Keycloak or RH-SSO, to connect to the database. When the secret
is ignored. When the value is omitted or left blank, the one is defined, the `identityProviderPostgresPassword` is ignored.
of following scenarios applies: 1. `identityProviderPostgresPassword` When the value is omitted or left blank, the one of following
is defined, then it will be used to connect to the database. scenarios applies: 1. `identityProviderPostgresPassword` is
defined, then it will be used to connect to the database.
2. `identityProviderPostgresPassword` is not defined, then 2. `identityProviderPostgresPassword` is not defined, then
a new secret with the name `che-identity-postgres-secret` a new secret with the name `che-identity-postgres-secret`
will be created with an auto-generated value for `password`. will be created with an auto-generated value for `password`.
@ -179,14 +189,16 @@ spec:
label.' label.'
type: string type: string
identityProviderRealm: identityProviderRealm:
description: Name of a Identity provider, Keycloak or RH-SSO, description: Deprecated. The value of this flag is ignored.
realm that is used for Che. Override this when an external Name of a Identity provider, Keycloak or RH-SSO, realm that
Identity Provider is in use. See the `externalIdentityProvider` is used for Che. Override this when an external Identity Provider
field. When omitted or left blank, it is set to the value is in use. See the `externalIdentityProvider` field. When
of the `flavour` field. omitted or left blank, it is set to the value of the `flavour`
field.
type: string type: string
identityProviderRoute: identityProviderRoute:
description: Route custom settings. description: Deprecated. The value of this flag is ignored.
Route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -208,8 +220,9 @@ spec:
type: string type: string
type: object type: object
identityProviderSecret: identityProviderSecret:
description: 'The secret that contains `user` and `password` description: 'Deprecated. The value of this flag is ignored.
for Identity Provider. When the secret is defined, the `identityProviderAdminUserName` The secret that contains `user` and `password` for Identity
Provider. When the secret is defined, the `identityProviderAdminUserName`
and `identityProviderPassword` are ignored. When the value and `identityProviderPassword` are ignored. When the value
is omitted or left blank, the one of following scenarios applies: is omitted or left blank, the one of following scenarios applies:
1. `identityProviderAdminUserName` and `identityProviderPassword` 1. `identityProviderAdminUserName` and `identityProviderPassword`
@ -228,20 +241,22 @@ spec:
set by the Operator. set by the Operator.
type: string type: string
initialOpenShiftOAuthUser: initialOpenShiftOAuthUser:
description: For operating with the OpenShift OAuth authentication, description: Deprecated. The value of this flag is ignored.
create a new user account since the kubeadmin can not be used. For operating with the OpenShift OAuth authentication, create
If the value is true, then a new OpenShift OAuth user will a new user account since the kubeadmin can not be used. If
be created for the HTPasswd identity provider. If the value the value is true, then a new OpenShift OAuth user will be
is false and the user has already been created, then it will created for the HTPasswd identity provider. If the value is
false and the user has already been created, then it will
be removed. If value is an empty, then do nothing. The user's be removed. If value is an empty, then do nothing. The user's
credentials are stored in the `openshift-oauth-user-credentials` credentials are stored in the `openshift-oauth-user-credentials`
secret in 'openshift-config' namespace by Operator. Note that secret in 'openshift-config' namespace by Operator. Note that
this solution is Openshift 4 platform-specific. this solution is Openshift 4 platform-specific.
type: boolean type: boolean
nativeUserMode: nativeUserMode:
description: Enables native user mode. Currently works only description: Deprecated. The value of this flag is ignored.
on OpenShift and DevWorkspace engine. Native User mode uses Enables native user mode. Currently works only on OpenShift
OpenShift OAuth directly as identity provider, without Keycloak. and DevWorkspace engine. Native User mode uses OpenShift OAuth
directly as identity provider, without Keycloak.
type: boolean type: boolean
oAuthClientName: oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used description: Name of the OpenShift `OAuthClient` resource used
@ -255,17 +270,19 @@ spec:
field. field.
type: string type: string
openShiftoAuth: openShiftoAuth:
description: 'Enables the integration of the identity provider description: 'Deprecated. The value of this flag is ignored.
(Keycloak / RHSSO) with OpenShift OAuth. Empty value on OpenShift Enables the integration of the identity provider (Keycloak
by default. This will allow users to directly login with their / RHSSO) with OpenShift OAuth. Empty value on OpenShift by
default. This will allow users to directly login with their
OpenShift user through the OpenShift login, and have their OpenShift user through the OpenShift login, and have their
workspaces created under personal OpenShift namespaces. WARNING: workspaces created under personal OpenShift namespaces. WARNING:
the `kubeadmin` user is NOT supported, and logging through the `kubeadmin` user is NOT supported, and logging through
it will NOT allow accessing the Che Dashboard.' it will NOT allow accessing the Che Dashboard.'
type: boolean type: boolean
updateAdminPassword: updateAdminPassword:
description: Forces the default `admin` Che user to update password description: Deprecated. The value of this flag is ignored.
on first login. Defaults to `false`. Forces the default `admin` Che user to update password on
first login. Defaults to `false`.
type: boolean type: boolean
type: object type: object
database: database:
@ -452,10 +469,11 @@ spec:
This MUST be explicitly specified: there are no defaults.' This MUST be explicitly specified: there are no defaults.'
type: string type: string
ingressStrategy: ingressStrategy:
description: 'Strategy for ingress creation. Options are: `multi-host` description: 'Deprecated. The value of this flag is ignored.
(host is explicitly provided in ingress), `single-host` (host Strategy for ingress creation. Options are: `multi-host` (host
is provided, path-based rules) and `default-host` (no host is explicitly provided in ingress), `single-host` (host is
is provided, path-based rules). Defaults to `multi-host` Deprecated provided, path-based rules) and `default-host` (no host is
provided, path-based rules). Defaults to `multi-host` Deprecated
in favor of `serverExposureStrategy` in the `server` section, in favor of `serverExposureStrategy` in the `server` section,
which defines this regardless of the cluster type. When both which defines this regardless of the cluster type. When both
are defined, the `serverExposureStrategy` option takes precedence.' are defined, the `serverExposureStrategy` option takes precedence.'
@ -469,15 +487,16 @@ spec:
run as. Default value is `1724`. run as. Default value is `1724`.
type: string type: string
singleHostExposureType: singleHostExposureType:
description: When the serverExposureStrategy is set to `single-host`, description: Deprecated. The value of this flag is ignored.
the way the server, registries and workspaces are exposed When the serverExposureStrategy is set to `single-host`, the
is further configured by this property. The possible values way the server, registries and workspaces are exposed is further
are `native`, which means that the server and workspaces are configured by this property. The possible values are `native`,
exposed using ingresses on K8s or `gateway` where the server which means that the server and workspaces are exposed using
and workspaces are exposed using a custom gateway based on ingresses on K8s or `gateway` where the server and workspaces
link:https://doc.traefik.io/traefik/[Traefik]. All the endpoints are exposed using a custom gateway based on link:https://doc.traefik.io/traefik/[Traefik].
whether backed by the ingress or gateway `route` always point All the endpoints whether backed by the ingress or gateway
to the subpaths on the same domain. Defaults to `native`. `route` always point to the subpaths on the same domain. Defaults
to `native`.
type: string type: string
tlsSecretName: tlsSecretName:
description: Name of a secret that will be used to setup ingress description: Name of a secret that will be used to setup ingress
@ -531,9 +550,9 @@ spec:
to `false`. to `false`.
type: string type: string
cheFlavor: cheFlavor:
description: Specifies a variation of the installation. The description: Deprecated. The value of this flag is ignored.
options are `che` for upstream Che installations, or `codeready` Specifies a variation of the installation. The options are
for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady `che` for upstream Che installations, or `codeready` for link:https://developers.redhat.com/products/codeready-workspaces/overview[CodeReady
Workspaces] installation. Override the default value only Workspaces] installation. Override the default value only
on necessary occasions. on necessary occasions.
type: string type: string
@ -642,7 +661,8 @@ spec:
or `latest` images, and `IfNotPresent` in other cases. or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
dashboardIngress: dashboardIngress:
description: Dashboard ingress custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -666,7 +686,8 @@ spec:
deployment. Defaults to 16Mi. deployment. Defaults to 16Mi.
type: string type: string
dashboardRoute: dashboardRoute:
description: Dashboard route custom settings. description: Deprecated. The value of this flag is ignored.
Dashboard route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -702,7 +723,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
devfileRegistryIngress: devfileRegistryIngress:
description: The devfile registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -731,7 +753,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
devfileRegistryRoute: devfileRegistryRoute:
description: The devfile registry route custom settings. description: Deprecated. The value of this flag is ignored.
The devfile registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -757,9 +780,9 @@ spec:
fields. fields.
type: string type: string
disableInternalClusterSVCNames: disableInternalClusterSVCNames:
description: Disable internal cluster SVC names usage to communicate description: Deprecated. The value of this flag is ignored.
between components to speed up the traffic and avoid proxy Disable internal cluster SVC names usage to communicate between
issues. components to speed up the traffic and avoid proxy issues.
type: boolean type: boolean
externalDevfileRegistries: externalDevfileRegistries:
description: External devfile registries, that serves sample, description: External devfile registries, that serves sample,
@ -824,7 +847,8 @@ spec:
by the Operator. by the Operator.
type: string type: string
pluginRegistryIngress: pluginRegistryIngress:
description: Plugin registry ingress custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry ingress custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -853,7 +877,8 @@ spec:
`next` or `latest` images, and `IfNotPresent` in other cases. `next` or `latest` images, and `IfNotPresent` in other cases.
type: string type: string
pluginRegistryRoute: pluginRegistryRoute:
description: Plugin registry route custom settings. description: Deprecated. The value of this flag is ignored.
Plugin registry route custom settings.
properties: properties:
annotations: annotations:
additionalProperties: additionalProperties:
@ -929,8 +954,9 @@ spec:
deployment In cores. (500m = .5 cores). Default to 100m. deployment In cores. (500m = .5 cores). Default to 100m.
type: string type: string
serverExposureStrategy: serverExposureStrategy:
description: Sets the server and workspaces exposure type. Possible description: Deprecated. The value of this flag is ignored.
values are `multi-host`, `single-host`, `default-host`. Defaults Sets the server and workspaces exposure type. Possible values
are `multi-host`, `single-host`, `default-host`. Defaults
to `multi-host`, which creates a separate ingress, or OpenShift to `multi-host`, which creates a separate ingress, or OpenShift
routes, for every required endpoint. `single-host` makes Che routes, for every required endpoint. `single-host` makes Che
exposed on a single host name with workspaces exposed on subpaths. exposed on a single host name with workspaces exposed on subpaths.

View File

@ -77,8 +77,6 @@ spec:
value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392
- name: RELATED_IMAGE_postgres_13_3 - name: RELATED_IMAGE_postgres_13_3
value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2 value: quay.io/eclipse/che--centos--postgresql-13-centos7:1-71b24684d64da46f960682cc4216222a7e4ed8b1a31dd5a865b3e71afdea20d2
- name: RELATED_IMAGE_keycloak
value: quay.io/eclipse/che-keycloak:next
- name: RELATED_IMAGE_che_workspace_plugin_broker_metadata - name: RELATED_IMAGE_che_workspace_plugin_broker_metadata
value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0 value: quay.io/eclipse/che-plugin-metadata-broker:v3.4.0
- name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts - name: RELATED_IMAGE_che_workspace_plugin_broker_artifacts

View File

@ -20,7 +20,6 @@ spec:
cheClusterRoles: '' cheClusterRoles: ''
cheWorkspaceClusterRole: '' cheWorkspaceClusterRole: ''
gitSelfSignedCert: false gitSelfSignedCert: false
tlsSupport: true
proxyURL: '' proxyURL: ''
proxyPort: '' proxyPort: ''
proxySecret: '' proxySecret: ''

View File

@ -44,12 +44,6 @@ func (c *ConsoleLinkReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil
} }
if !ctx.CheCluster.Spec.Server.TlsSupport {
// console link is supported only with https
logrus.Debug("Console link won't be created. HTTP protocol is not supported.")
return reconcile.Result{}, true, nil
}
done, err := c.createConsoleLink(ctx) done, err := c.createConsoleLink(ctx)
if !done { if !done {
return reconcile.Result{Requeue: true}, false, err return reconcile.Result{Requeue: true}, false, err

View File

@ -31,11 +31,7 @@ func TestReconcileConsoleLink(t *testing.T) {
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: "eclipse-che", Name: "eclipse-che",
}, },
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{},
Server: orgv1.CheClusterSpecServer{
TlsSupport: true,
},
},
} }
util.IsOpenShift4 = true util.IsOpenShift4 = true

View File

@ -57,8 +57,6 @@ func (d *DashboardReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Re
// Expose dashboard service with route or ingress // Expose dashboard service with route or ingress
_, done, err = expose.ExposeWithHostPath(ctx, d.getComponentName(ctx), ctx.CheCluster.Spec.Server.CheHost, _, done, err = expose.ExposeWithHostPath(ctx, d.getComponentName(ctx), ctx.CheCluster.Spec.Server.CheHost,
exposePath, exposePath,
ctx.CheCluster.Spec.Server.DashboardRoute,
ctx.CheCluster.Spec.Server.DashboardIngress,
d.createGatewayConfig(ctx), d.createGatewayConfig(ctx),
) )
if !done { if !done {
@ -130,7 +128,7 @@ func (d *DashboardReconciler) createGatewayConfig(ctx *deploy.DeployContext) *ga
10, 10,
"http://"+d.getComponentName(ctx)+":8080", "http://"+d.getComponentName(ctx)+":8080",
[]string{}) []string{})
if util.IsOpenShift && ctx.CheCluster.IsNativeUserModeEnabled() { if util.IsOpenShift {
cfg.AddAuthHeaderRewrite(d.getComponentName(ctx)) cfg.AddAuthHeaderRewrite(d.getComponentName(ctx))
} }
return cfg return cfg

View File

@ -122,19 +122,18 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
envVars []corev1.EnvVar envVars []corev1.EnvVar
cheCluster *orgv1.CheCluster cheCluster *orgv1.CheCluster
} }
trueBool := true
testCases := []resourcesTestCase{ testCases := []resourcesTestCase{
{ {
name: "Test provisioning Che and Keycloak URLs", name: "Test provisioning Che URLs",
initObjects: []runtime.Object{}, initObjects: []runtime.Object{},
envVars: []corev1.EnvVar{ envVars: []corev1.EnvVar{
{ {
Name: "CHE_HOST", Name: "CHE_HOST",
Value: "http://che.com", Value: "https://che.com",
}, },
{ {
Name: "CHE_URL", Name: "CHE_URL",
Value: "http://che.com", Value: "https://che.com",
}, },
{ {
Name: "CHECLUSTER_CR_NAMESPACE", Name: "CHECLUSTER_CR_NAMESPACE",
@ -164,44 +163,6 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
}, },
}, },
}, },
{
name: "Test provisioning Che and Keycloak URLs when internal SVC is disabled",
initObjects: []runtime.Object{},
envVars: []corev1.EnvVar{
{
Name: "CHE_HOST",
Value: "http://che.com",
},
{
Name: "CHE_URL",
Value: "http://che.com",
},
{
Name: "CHECLUSTER_CR_NAMESPACE",
Value: "eclipse-che",
},
{
Name: "CHECLUSTER_CR_NAME",
Value: "eclipse-che",
},
{
Name: "OPENSHIFT_CONSOLE_URL",
},
// the following are not provisioned: CHE_INTERNAL_URL
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: &trueBool,
CheHost: "che.com",
},
},
},
},
{ {
name: "Test provisioning OpenShift Console URL", name: "Test provisioning OpenShift Console URL",
initObjects: []runtime.Object{ initObjects: []runtime.Object{
@ -218,11 +179,11 @@ func TestDashboardDeploymentEnvVars(t *testing.T) {
envVars: []corev1.EnvVar{ envVars: []corev1.EnvVar{
{ {
Name: "CHE_HOST", Name: "CHE_HOST",
Value: "http://che.com", Value: "https://che.com",
}, },
{ {
Name: "CHE_URL", Name: "CHE_URL",
Value: "http://che.com", Value: "https://che.com",
}, },
{ {
Name: "CHECLUSTER_CR_NAMESPACE", Name: "CHECLUSTER_CR_NAMESPACE",

View File

@ -16,11 +16,9 @@ import (
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
routev1 "github.com/openshift/api/route/v1"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -40,7 +38,6 @@ func TestDashboardOpenShift(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &routev1.Route{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
@ -60,7 +57,6 @@ func TestDashboardKubernetes(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &corev1.Service{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &networkingv1.Ingress{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: DashboardSA, Namespace: "eclipse-che"}, &corev1.ServiceAccount{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: dashboard.getComponentName(ctx), Namespace: "eclipse-che"}, &appsv1.Deployment{}))

View File

@ -64,13 +64,11 @@ func (d *DashboardReconciler) getDashboardDeploymentSpec(ctx *deploy.DeployConte
Value: ctx.CheCluster.Name}, Value: ctx.CheCluster.Name},
) )
if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() { envVars = append(envVars,
envVars = append(envVars, corev1.EnvVar{
corev1.EnvVar{ Name: "CHE_INTERNAL_URL",
Name: "CHE_INTERNAL_URL", Value: fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)},
Value: fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)}, )
)
}
if util.IsOpenShift { if util.IsOpenShift {
envVars = append(envVars, envVars = append(envVars,

View File

@ -37,7 +37,6 @@ var (
defaultPvcJobsImage string defaultPvcJobsImage string
defaultPostgresImage string defaultPostgresImage string
defaultPostgres13Image string defaultPostgres13Image string
defaultKeycloakImage string
defaultSingleHostGatewayImage string defaultSingleHostGatewayImage string
defaultSingleHostGatewayConfigSidecarImage string defaultSingleHostGatewayConfigSidecarImage string
defaultInternalRestBackupServerImage string defaultInternalRestBackupServerImage string
@ -63,7 +62,6 @@ const (
DefaultPvcClaimSize = "10Gi" DefaultPvcClaimSize = "10Gi"
DefaultIngressClass = "nginx" DefaultIngressClass = "nginx"
DefaultKeycloakAdminUserName = "admin"
DefaultCheLogLevel = "INFO" DefaultCheLogLevel = "INFO"
DefaultCheDebug = "false" DefaultCheDebug = "false"
DefaultCheMetricsPort = int32(8087) DefaultCheMetricsPort = int32(8087)
@ -81,21 +79,9 @@ const (
KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.9" KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.9"
DefaultServerExposureStrategy = "multi-host" ServerExposureStrategy = "single-host"
NativeSingleHostExposureType = "native"
GatewaySingleHostExposureType = "gateway" GatewaySingleHostExposureType = "gateway"
// This is only to correctly manage defaults during the transition
// from Upstream 7.0.0 GA to the next version
// That fixed bug https://github.com/eclipse/che/issues/13714
OldDefaultKeycloakUpstreamImageToDetect = "eclipse/che-keycloak:7.0.0"
OldDefaultPvcJobsUpstreamImageToDetect = "registry.access.redhat.com/ubi8-minimal:8.0-127"
OldDefaultPostgresUpstreamImageToDetect = "centos/postgresql-96-centos7:9.6"
OldDefaultCodeReadyServerImageRepo = "registry.redhat.io/codeready-workspaces/server-rhel8"
OldDefaultCodeReadyServerImageTag = "1.2"
OldCrwPluginRegistryUrl = "https://che-plugin-registry.openshift.io"
// kubernetes default labels // kubernetes default labels
KubernetesComponentLabelKey = "app.kubernetes.io/component" KubernetesComponentLabelKey = "app.kubernetes.io/component"
KubernetesPartOfLabelKey = "app.kubernetes.io/part-of" KubernetesPartOfLabelKey = "app.kubernetes.io/part-of"
@ -118,10 +104,9 @@ const (
CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest" CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest"
// components // components
IdentityProviderName = "keycloak" DevfileRegistryName = "devfile-registry"
DevfileRegistryName = "devfile-registry" PluginRegistryName = "plugin-registry"
PluginRegistryName = "plugin-registry" PostgresName = "postgres"
PostgresName = "postgres"
// CheServiceAccountName - service account name for che-server. // CheServiceAccountName - service account name for che-server.
CheServiceAccountName = "che" CheServiceAccountName = "che"
@ -198,7 +183,6 @@ func InitDefaultsFromFile(defaultsPath string) {
defaultPvcJobsImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_pvc_jobs")) defaultPvcJobsImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_pvc_jobs"))
defaultPostgresImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres")) defaultPostgresImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres"))
defaultPostgres13Image = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) defaultPostgres13Image = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3"))
defaultKeycloakImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_keycloak"))
defaultSingleHostGatewayImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) defaultSingleHostGatewayImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway"))
defaultSingleHostGatewayConfigSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) defaultSingleHostGatewayConfigSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar"))
defaultGatewayAuthenticationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar")) defaultGatewayAuthenticationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar"))
@ -246,15 +230,6 @@ func getDefaultFromEnv(envName string) string {
return value return value
} }
func MigratingToCRW2_0(cr *orgv1.CheCluster) bool {
if cr.Spec.Server.CheFlavor == "codeready" &&
strings.HasPrefix(cr.Status.CheVersion, "1.2") &&
strings.HasPrefix(defaultCheVersion, "2.0") {
return true
}
return false
}
func IsComponentReadinessInitContainersConfigured(cr *orgv1.CheCluster) bool { func IsComponentReadinessInitContainersConfigured(cr *orgv1.CheCluster) bool {
return os.Getenv("ADD_COMPONENT_READINESS_INIT_CONTAINERS") == "true" return os.Getenv("ADD_COMPONENT_READINESS_INIT_CONTAINERS") == "true"
} }
@ -264,7 +239,7 @@ func DefaultServerTrustStoreConfigMapName() string {
} }
func DefaultCheFlavor(cr *orgv1.CheCluster) string { func DefaultCheFlavor(cr *orgv1.CheCluster) string {
return util.GetValue(cr.Spec.Server.CheFlavor, getDefaultFromEnv("CHE_FLAVOR")) return getDefaultFromEnv("CHE_FLAVOR")
} }
func DefaultConsoleLinkName() string { func DefaultConsoleLinkName() string {
@ -331,10 +306,6 @@ func DefaultDevworkspaceControllerImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultDevworkspaceControllerImage) return patchDefaultImageName(cr, defaultDevworkspaceControllerImage)
} }
func DefaultKeycloakImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultKeycloakImage)
}
func DefaultPluginRegistryImage(cr *orgv1.CheCluster) string { func DefaultPluginRegistryImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultPluginRegistryImage) return patchDefaultImageName(cr, defaultPluginRegistryImage)
} }
@ -395,12 +366,20 @@ func DefaultPullPolicyFromDockerImage(dockerImage string) string {
return "IfNotPresent" return "IfNotPresent"
} }
func GetSingleHostExposureType(cr *orgv1.CheCluster) string { // GetWorkspaceNamespaceDefault - returns workspace namespace default strategy, which points on the namespaces used for workspaces execution.
if util.IsOpenShift || cr.Spec.DevWorkspace.Enable { func GetWorkspaceNamespaceDefault(cr *orgv1.CheCluster) string {
return GatewaySingleHostExposureType if cr.Spec.Server.CustomCheProperties != nil {
k8sNamespaceDefault := cr.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"]
if k8sNamespaceDefault != "" {
return k8sNamespaceDefault
}
} }
return util.GetValue(cr.Spec.K8s.SingleHostExposureType, NativeSingleHostExposureType) workspaceNamespaceDefault := cr.Namespace
if util.IsOpenShift {
workspaceNamespaceDefault = "<username>-" + DefaultCheFlavor(cr)
}
return util.GetValue(cr.Spec.Server.WorkspaceNamespaceDefault, workspaceNamespaceDefault)
} }
func patchDefaultImageName(cr *orgv1.CheCluster, imageName string) string { func patchDefaultImageName(cr *orgv1.CheCluster, imageName string) string {
@ -474,7 +453,6 @@ func InitDefaultsFromEnv() {
// while downstream is not migrated to PostgreSQL 13.3 yet // while downstream is not migrated to PostgreSQL 13.3 yet
defaultPostgres13Image = os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3")) defaultPostgres13Image = os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_postgres_13_3"))
defaultKeycloakImage = os.Getenv(util.GetArchitectureDependentEnv("RELATED_IMAGE_keycloak"))
defaultSingleHostGatewayImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway")) defaultSingleHostGatewayImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway"))
defaultSingleHostGatewayConfigSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar")) defaultSingleHostGatewayConfigSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar"))
defaultInternalRestBackupServerImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_internal_rest_backup_server")) defaultInternalRestBackupServerImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_internal_rest_backup_server"))

View File

@ -21,8 +21,8 @@ if [[ ! -f ${defaultsFile} ]]; then
curl -ssL https://raw.githubusercontent.com/eclipse-che/che-operator/main/pkg/deploy/defaults.go -o ${defaultsFile} curl -ssL https://raw.githubusercontent.com/eclipse-che/che-operator/main/pkg/deploy/defaults.go -o ${defaultsFile}
fi fi
excludes="eclipse/che-keycloak|centos/postgresql-96-centos7" excludes="eclipse|centos/postgresql-96-centos7"
for d in $(cat /tmp/defaults.go | egrep "Keycloak|Postgres|Pvc" | egrep Image | egrep -v "func|return|Old|ToDetect|$excludes" | sed -e "s#.\+= \"\(.\+\)\"#\1#"); do for d in $(cat /tmp/defaults.go | egrep "Postgres|Pvc" | egrep Image | egrep -v "func|return|Old|ToDetect|$excludes" | sed -e "s#.\+= \"\(.\+\)\"#\1#"); do
echo "- ${d}" echo "- ${d}"
echo -n "+ ${d%:*}:"; echo -n "+ ${d%:*}:";
e=$(skopeo inspect docker://${d%:*} | yq .RepoTags | egrep -v "\[|\]|latest" | tr -d ",\" " | sort -V | tail -1) e=$(skopeo inspect docker://${d%:*} | yq .RepoTags | egrep -v "\[|\]|latest" | tr -d ",\" " | sort -V | tail -1)

View File

@ -117,7 +117,6 @@ func TestCorrectAirGapPatchedImage(t *testing.T) {
expectedAirGapPostgresUpstreamImageOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPostgresImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) expectedAirGapPostgresUpstreamImageOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPostgresImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage))
expectedAirGapCRWPluginRegistryOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPluginRegistryImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPluginRegistryImage)) expectedAirGapCRWPluginRegistryOnlyOrgChanged = makeAirGapImagePath(getHostnameFromImage(defaultPluginRegistryImage), airGapRegistryOrganization, getImageNameFromFullImage(defaultPluginRegistryImage))
expectedAirGapCRWPostgresImage = makeAirGapImagePath(airGapRegistryHostname, airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage)) expectedAirGapCRWPostgresImage = makeAirGapImagePath(airGapRegistryHostname, airGapRegistryOrganization, getImageNameFromFullImage(defaultPostgresImage))
expectedAirGapKeyCloakImageOnlyHostnameChanged = makeAirGapImagePath(airGapRegistryHostname, getOrganizationFromImage(defaultKeycloakImage), getImageNameFromFullImage(defaultKeycloakImage))
expectedAirGapCRWDevfileRegistryImageOnlyHostnameChanged = makeAirGapImagePath(airGapRegistryHostname, getOrganizationFromImage(defaultDevfileRegistryImage), getImageNameFromFullImage(defaultDevfileRegistryImage)) expectedAirGapCRWDevfileRegistryImageOnlyHostnameChanged = makeAirGapImagePath(airGapRegistryHostname, getOrganizationFromImage(defaultDevfileRegistryImage), getImageNameFromFullImage(defaultDevfileRegistryImage))
) )
@ -157,13 +156,6 @@ func TestCorrectAirGapPatchedImage(t *testing.T) {
}, },
}, },
} }
upstreamOnlyHostname := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
AirGapContainerRegistryHostname: airGapRegistryHostname,
},
},
}
crwOnlyOrg := &orgv1.CheCluster{ crwOnlyOrg := &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
@ -188,7 +180,6 @@ func TestCorrectAirGapPatchedImage(t *testing.T) {
"codeready plugin registry with only the org changed": {image: defaultPluginRegistryImage, expected: expectedAirGapCRWPluginRegistryOnlyOrgChanged, cr: crwOnlyOrg}, "codeready plugin registry with only the org changed": {image: defaultPluginRegistryImage, expected: expectedAirGapCRWPluginRegistryOnlyOrgChanged, cr: crwOnlyOrg},
"CRW postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: crw}, "CRW postgres": {image: defaultPostgresImage, expected: defaultPostgresImage, cr: crw},
"CRW airgap postgres": {image: defaultPostgresImage, expected: expectedAirGapCRWPostgresImage, cr: airGapCRW}, "CRW airgap postgres": {image: defaultPostgresImage, expected: expectedAirGapCRWPostgresImage, cr: airGapCRW},
"airgap with only hostname defined": {image: defaultKeycloakImage, expected: expectedAirGapKeyCloakImageOnlyHostnameChanged, cr: upstreamOnlyHostname},
"crw airgap with only hostname defined": {image: defaultDevfileRegistryImage, expected: expectedAirGapCRWDevfileRegistryImageOnlyHostnameChanged, cr: crwOnlyHostname}, "crw airgap with only hostname defined": {image: defaultDevfileRegistryImage, expected: expectedAirGapCRWDevfileRegistryImageOnlyHostnameChanged, cr: crwOnlyHostname},
} }
for name, tc := range testCases { for name, tc := range testCases {

View File

@ -63,11 +63,6 @@ func (d *DevWorkspaceReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil
} }
if !ctx.CheCluster.Spec.DevWorkspace.Enable {
// Do nothing if DevWorkspace is disabled
return reconcile.Result{}, true, nil
}
if isDevWorkspaceOperatorCSVExists(ctx) { if isDevWorkspaceOperatorCSVExists(ctx) {
// Do nothing if DevWorkspace has been already deployed via OLM // Do nothing if DevWorkspace has been already deployed via OLM
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil

View File

@ -59,19 +59,13 @@ func TestReconcileDevWorkspace(t *testing.T) {
DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ DevWorkspace: orgv1.CheClusterSpecDevWorkspace{
Enable: true, Enable: true,
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(true),
},
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
}, },
}, },
IsOpenShift: true, IsOpenShift: true,
IsOpenShift4: true, IsOpenShift4: true,
}, },
{ {
name: "Reconcile DevWorkspace on K8S multi-host", name: "Reconcile DevWorkspace on K8S",
cheCluster: &orgv1.CheCluster{ cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che", Namespace: "eclipse-che",
@ -80,37 +74,8 @@ func TestReconcileDevWorkspace(t *testing.T) {
DevWorkspace: orgv1.CheClusterSpecDevWorkspace{ DevWorkspace: orgv1.CheClusterSpecDevWorkspace{
Enable: true, Enable: true,
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(true),
},
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host", CustomCheProperties: map[string]string{"CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S": "true"},
CustomCheProperties: map[string]string{"CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S": "true"},
},
K8s: orgv1.CheClusterSpecK8SOnly{
IngressDomain: "che.domain",
},
},
},
IsOpenShift: false,
IsOpenShift4: false,
},
{
name: "Reconcile DevWorkspace on K8S single-host",
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
DevWorkspace: orgv1.CheClusterSpecDevWorkspace{
Enable: true,
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(true),
},
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
CustomCheProperties: map[string]string{"CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S": "true"},
}, },
K8s: orgv1.CheClusterSpecK8SOnly{ K8s: orgv1.CheClusterSpecK8SOnly{
IngressDomain: "che.domain", IngressDomain: "che.domain",

View File

@ -86,19 +86,11 @@ func (d *DevfileRegistryReconciler) exposeEndpoint(ctx *deploy.DeployContext) (s
return expose.Expose( return expose.Expose(
ctx, ctx,
deploy.DevfileRegistryName, deploy.DevfileRegistryName,
ctx.CheCluster.Spec.Server.DevfileRegistryRoute,
ctx.CheCluster.Spec.Server.DevfileRegistryIngress,
d.createGatewayConfig()) d.createGatewayConfig())
} }
func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) { func (d *DevfileRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) {
var devfileRegistryURL string devfileRegistryURL := "https://" + endpoint
if ctx.CheCluster.Spec.Server.TlsSupport {
devfileRegistryURL = "https://" + endpoint
} else {
devfileRegistryURL = "http://" + endpoint
}
if devfileRegistryURL != ctx.CheCluster.Status.DevfileRegistryURL { if devfileRegistryURL != ctx.CheCluster.Status.DevfileRegistryURL {
ctx.CheCluster.Status.DevfileRegistryURL = devfileRegistryURL ctx.CheCluster.Status.DevfileRegistryURL = devfileRegistryURL
if err := deploy.UpdateCheCRStatus(ctx, "status: Devfile Registry URL", devfileRegistryURL); err != nil { if err := deploy.UpdateCheCRStatus(ctx, "status: Devfile Registry URL", devfileRegistryURL); err != nil {

View File

@ -30,7 +30,7 @@ func (d *DevfileRegistryReconciler) getDevfileRegistryDeploymentSpec(ctx *deploy
devfileImagesEnv := util.GetEnvByRegExp("^.*devfile_registry_image.*$") devfileImagesEnv := util.GetEnvByRegExp("^.*devfile_registry_image.*$")
// If there is a devfile registry deployed by operator // If there is a devfile registry deployed by operator
if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() && !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry { if !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry {
devfileImagesEnv = append(devfileImagesEnv, devfileImagesEnv = append(devfileImagesEnv,
corev1.EnvVar{ corev1.EnvVar{
Name: "CHE_DEVFILE_REGISTRY_INTERNAL_URL", Name: "CHE_DEVFILE_REGISTRY_INTERNAL_URL",

View File

@ -17,7 +17,6 @@ import (
orgv1 "github.com/eclipse-che/che-operator/api/v1" orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
routev1 "github.com/openshift/api/route/v1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -38,7 +37,6 @@ func TestDevfileRegistryReconcile(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.Service{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.Service{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &routev1.Route{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "devfile-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{}))
assert.NotEmpty(t, ctx.CheCluster.Status.DevfileRegistryURL) assert.NotEmpty(t, ctx.CheCluster.Status.DevfileRegistryURL)
@ -72,7 +70,7 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
}, },
}, },
}, },
expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./", expectedDevfileRegistryURL: "https:///devfile-registry",
}, },
{ {
name: "Test Status.DevfileRegistryURL #2", name: "Test Status.DevfileRegistryURL #2",
@ -95,7 +93,7 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
}, },
}, },
}, },
expectedDevfileRegistryURL: "http://devfile-registry-eclipse-che./", expectedDevfileRegistryURL: "https:///devfile-registry",
}, },
{ {
name: "Test Status.DevfileRegistryURL #2", name: "Test Status.DevfileRegistryURL #2",

View File

@ -16,7 +16,6 @@ import (
routev1 "github.com/openshift/api/route/v1" routev1 "github.com/openshift/api/route/v1"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway" "github.com/eclipse-che/che-operator/pkg/deploy/gateway"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
@ -32,11 +31,9 @@ const (
func Expose( func Expose(
deployContext *deploy.DeployContext, deployContext *deploy.DeployContext,
componentName string, componentName string,
routeCustomSettings orgv1.RouteCustomSettings,
ingressCustomSettings orgv1.IngressCustomSettings,
gatewayConfig *gateway.TraefikConfig) (endpointUrl string, done bool, err error) { gatewayConfig *gateway.TraefikConfig) (endpointUrl string, done bool, err error) {
//the host and path are empty and will be evaluated for the specified component + path //the host and path are empty and will be evaluated for the specified component + path
return ExposeWithHostPath(deployContext, componentName, "", "", routeCustomSettings, ingressCustomSettings, gatewayConfig) return ExposeWithHostPath(deployContext, componentName, "", "", gatewayConfig)
} }
//Expose exposes the specified component on the specified host and domain. //Expose exposes the specified component on the specified host and domain.
@ -47,78 +44,24 @@ func ExposeWithHostPath(
component string, component string,
host string, host string,
path string, path string,
routeCustomSettings orgv1.RouteCustomSettings,
ingressCustomSettings orgv1.IngressCustomSettings,
gatewayConfig *gateway.TraefikConfig) (endpointUrl string, done bool, err error) { gatewayConfig *gateway.TraefikConfig) (endpointUrl string, done bool, err error) {
exposureStrategy := util.GetServerExposureStrategy(deployContext.CheCluster)
if path != "" && !strings.HasPrefix(path, "/") { if path != "" && !strings.HasPrefix(path, "/") {
path = "/" + path path = "/" + path
} }
singleHostExposureType := deploy.GetSingleHostExposureType(deployContext.CheCluster)
useGateway := exposureStrategy == "single-host" && (util.IsOpenShift || singleHostExposureType == deploy.GatewaySingleHostExposureType)
if !util.IsOpenShift { if !util.IsOpenShift {
if useGateway { return exposeWithGateway(deployContext, gatewayConfig, component, path, func() {
return exposeWithGateway(deployContext, gatewayConfig, component, path, func() { if _, err = deploy.DeleteNamespacedObject(deployContext, component, &networking.Ingress{}); err != nil {
if _, err = deploy.DeleteNamespacedObject(deployContext, component, &networking.Ingress{}); err != nil {
logrus.Error(err)
}
})
} else {
endpointUrl, done, err = deploy.SyncIngressToCluster(deployContext, component, host, path, component, 8080, ingressCustomSettings, component)
if !done {
logrus.Infof("Waiting on ingress '%s' to be ready", component)
if err != nil {
logrus.Error(err)
}
return "", false, err
}
if err := gateway.DeleteGatewayRouteConfig(component, deployContext); !util.IsTestMode() && err != nil {
logrus.Error(err) logrus.Error(err)
} }
})
return endpointUrl, true, nil
}
} else { } else {
if useGateway { return exposeWithGateway(deployContext, gatewayConfig, component, path, func() {
return exposeWithGateway(deployContext, gatewayConfig, component, path, func() { if _, err := deploy.DeleteNamespacedObject(deployContext, component, &routev1.Route{}); !util.IsTestMode() && err != nil {
if _, err := deploy.DeleteNamespacedObject(deployContext, component, &routev1.Route{}); !util.IsTestMode() && err != nil {
logrus.Error(err)
}
})
} else {
// the empty string for a host is intentional here - we let OpenShift decide on the hostname
done, err := deploy.SyncRouteToCluster(deployContext, component, host, path, component, 8080, routeCustomSettings, component)
if !done {
logrus.Infof("Waiting on route '%s' to be ready", component)
if err != nil {
logrus.Error(err)
}
return "", false, err
}
route := &routev1.Route{}
exists, err := deploy.GetNamespacedObject(deployContext, component, route)
if !exists {
if err != nil {
logrus.Error(err)
}
return "", false, err
}
if err := gateway.DeleteGatewayRouteConfig(component, deployContext); !util.IsTestMode() && err != nil {
logrus.Error(err) logrus.Error(err)
} }
})
// Keycloak needs special rule in multihost. It's exposed on / which redirects to /auth
// clients which does not support redirects needs /auth be explicitely set
if path == "" && component == deploy.IdentityProviderName {
path = "/auth"
}
return route.Spec.Host + path, true, nil
}
} }
} }
@ -143,11 +86,7 @@ func exposeWithGateway(deployContext *deploy.DeployContext,
cleanUpRouting() cleanUpRouting()
if path == "" { if path == "" {
if component == deploy.IdentityProviderName { path = "/" + component
path = "/auth"
} else {
path = "/" + component
}
} }
return deployContext.CheCluster.Spec.Server.CheHost + path, true, err return deployContext.CheCluster.Spec.Server.CheHost + path, true, err
} }

View File

@ -84,13 +84,7 @@ func (p *GatewayReconciler) Finalize(ctx *deploy.DeployContext) bool {
// SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration // SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration
func SyncGatewayToCluster(deployContext *deploy.DeployContext) error { func SyncGatewayToCluster(deployContext *deploy.DeployContext) error {
if (util.GetServerExposureStrategy(deployContext.CheCluster) == "single-host" && return syncAll(deployContext)
deploy.GetSingleHostExposureType(deployContext.CheCluster) == deploy.GatewaySingleHostExposureType) ||
deployContext.CheCluster.Spec.DevWorkspace.Enable {
return syncAll(deployContext)
}
return deleteAll(deployContext)
} }
func syncAll(deployContext *deploy.DeployContext) error { func syncAll(deployContext *deploy.DeployContext) error {
@ -110,33 +104,31 @@ func syncAll(deployContext *deploy.DeployContext) error {
return err return err
} }
if deployContext.CheCluster.IsNativeUserModeEnabled() { if oauthSecret, err := getGatewaySecretSpec(deployContext); err == nil {
if oauthSecret, err := getGatewaySecretSpec(deployContext); err == nil { if _, err := deploy.Sync(deployContext, oauthSecret, secretDiffOpts); err != nil {
if _, err := deploy.Sync(deployContext, oauthSecret, secretDiffOpts); err != nil { return err
return err }
} oauthProxyConfig := getGatewayOauthProxyConfigSpec(instance, string(oauthSecret.Data["cookie_secret"]))
oauthProxyConfig := getGatewayOauthProxyConfigSpec(instance, string(oauthSecret.Data["cookie_secret"])) if _, err := deploy.Sync(deployContext, &oauthProxyConfig, configMapDiffOpts); err != nil {
if _, err := deploy.Sync(deployContext, &oauthProxyConfig, configMapDiffOpts); err != nil { return err
}
} else {
return err
}
kubeRbacProxyConfig := getGatewayKubeRbacProxyConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &kubeRbacProxyConfig, configMapDiffOpts); err != nil {
return err
}
if util.IsOpenShift {
if headerRewritePluginConfig, err := getGatewayHeaderRewritePluginConfigSpec(instance); err == nil {
if _, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); err != nil {
return err return err
} }
} else { } else {
return err return err
} }
kubeRbacProxyConfig := getGatewayKubeRbacProxyConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &kubeRbacProxyConfig, configMapDiffOpts); err != nil {
return err
}
if util.IsOpenShift {
if headerRewritePluginConfig, err := getGatewayHeaderRewritePluginConfigSpec(instance); err == nil {
if _, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); err != nil {
return err
}
} else {
return err
}
}
} }
traefikConfig := getGatewayTraefikConfigSpec(instance) traefikConfig := getGatewayTraefikConfigSpec(instance)
@ -196,73 +188,6 @@ func generateOauthSecretSpec(deployContext *deploy.DeployContext) *corev1.Secret
} }
} }
func deleteAll(deployContext *deploy.DeployContext) error {
instance := deployContext.CheCluster
clusterAPI := deployContext.ClusterAPI
deployment := appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayServiceName,
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &deployment); err != nil {
return err
}
serverConfig := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayConfigMapNamePrefix + serverComponentName,
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &serverConfig); err != nil {
return err
}
traefikConfig := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "che-gateway-config",
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &traefikConfig); err == nil {
return err
}
roleBinding := rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayServiceName,
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &roleBinding); err == nil {
return err
}
role := rbac.Role{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayServiceName,
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &role); err == nil {
return err
}
sa := corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayServiceName,
Namespace: instance.Namespace,
},
}
if err := delete(clusterAPI, &sa); err == nil {
return err
}
return nil
}
func delete(clusterAPI deploy.ClusterAPI, obj metav1.Object) error { func delete(clusterAPI deploy.ClusterAPI, obj metav1.Object) error {
key := client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()} key := client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}
ro := obj.(client.Object) ro := obj.(client.Object)
@ -298,7 +223,7 @@ func getGatewayServerConfigSpec(deployContext *deploy.DeployContext) (corev1.Con
"http://"+deploy.CheServiceName+":8080", "http://"+deploy.CheServiceName+":8080",
[]string{}) []string{})
if util.IsOpenShift && deployContext.CheCluster.IsNativeUserModeEnabled() { if util.IsOpenShift {
cfg.AddAuthHeaderRewrite(serverComponentName) cfg.AddAuthHeaderRewrite(serverComponentName)
// native user mode is currently only available on OpenShift but let's be defensive here so that // native user mode is currently only available on OpenShift but let's be defensive here so that
// this doesn't break once we enable it on Kubernetes, too. Token check will have to work // this doesn't break once we enable it on Kubernetes, too. Token check will have to work
@ -439,10 +364,7 @@ func getGatewayHeaderRewritePluginConfigSpec(instance *orgv1.CheCluster) (*corev
} }
func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap { func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap {
traefikPort := GatewayServicePort traefikPort := 8081
if instance.IsNativeUserModeEnabled() {
traefikPort = 8081
}
data := fmt.Sprintf(` data := fmt.Sprintf(`
entrypoints: entrypoints:
http: http:
@ -463,7 +385,7 @@ providers:
log: log:
level: "INFO"`, traefikPort) level: "INFO"`, traefikPort)
if util.IsOpenShift && instance.IsNativeUserModeEnabled() { if util.IsOpenShift {
data += ` data += `
experimental: experimental:
localPlugins: localPlugins:
@ -590,11 +512,9 @@ func getContainersSpec(instance *orgv1.CheCluster) []corev1.Container {
}, },
} }
if instance.IsNativeUserModeEnabled() { containers = append(containers,
containers = append(containers, getOauthProxyContainerSpec(instance),
getOauthProxyContainerSpec(instance), getKubeRbacProxyContainerSpec(instance))
getKubeRbacProxyContainerSpec(instance))
}
return containers return containers
} }
@ -610,7 +530,7 @@ func getTraefikContainerVolumeMounts(instance *orgv1.CheCluster) []corev1.Volume
MountPath: "/dynamic-config", MountPath: "/dynamic-config",
}, },
} }
if util.IsOpenShift && instance.IsNativeUserModeEnabled() { if util.IsOpenShift {
mounts = append(mounts, corev1.VolumeMount{ mounts = append(mounts, corev1.VolumeMount{
Name: "header-rewrite-traefik-plugin", Name: "header-rewrite-traefik-plugin",
MountPath: "/plugins-local/src/github.com/che-incubator/header-rewrite-traefik-plugin", MountPath: "/plugins-local/src/github.com/che-incubator/header-rewrite-traefik-plugin",
@ -640,23 +560,21 @@ func getVolumesSpec(instance *orgv1.CheCluster) []corev1.Volume {
}, },
} }
if instance.IsNativeUserModeEnabled() { volumes = append(volumes,
volumes = append(volumes, getOauthProxyConfigVolume(),
getOauthProxyConfigVolume(), getKubeRbacProxyConfigVolume())
getKubeRbacProxyConfigVolume())
if util.IsOpenShift { if util.IsOpenShift {
volumes = append(volumes, corev1.Volume{ volumes = append(volumes, corev1.Volume{
Name: "header-rewrite-traefik-plugin", Name: "header-rewrite-traefik-plugin",
VolumeSource: corev1.VolumeSource{ VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{ LocalObjectReference: corev1.LocalObjectReference{
Name: "che-gateway-config-header-rewrite-traefik-plugin", Name: "che-gateway-config-header-rewrite-traefik-plugin",
},
}, },
}, },
}) },
} })
} }
return volumes return volumes

View File

@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
@ -42,11 +41,6 @@ func TestSyncAllToCluster(t *testing.T) {
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: "eclipse-che", Name: "eclipse-che",
}, },
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
},
}, },
ClusterAPI: deploy.ClusterAPI{ ClusterAPI: deploy.ClusterAPI{
Client: cli, Client: cli,
@ -66,8 +60,8 @@ func TestSyncAllToCluster(t *testing.T) {
t.Fatalf("Failed to get deployment: %v", err) t.Fatalf("Failed to get deployment: %v", err)
} }
assert.Lenf(t, deployment.Spec.Template.Spec.Containers, 2, assert.Lenf(t, deployment.Spec.Template.Spec.Containers, 4,
"With classic multi-user, there should be 2 containers in the gateway, traefik and configbump. But it has '%d' containers.", len(deployment.Spec.Template.Spec.Containers)) "There should be 4 containers in the gateway. But it has '%d' containers.", len(deployment.Spec.Template.Spec.Containers))
for _, c := range deployment.Spec.Template.Spec.Containers { for _, c := range deployment.Spec.Template.Spec.Containers {
assert.NotNil(t, c.Resources, "container '%s' has not set resources", c.Name) assert.NotNil(t, c.Resources, "container '%s' has not set resources", c.Name)
} }
@ -84,21 +78,12 @@ func TestNativeUserGateway(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme) orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
corev1.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme) cli := fake.NewFakeClientWithScheme(scheme.Scheme)
nativeUserMode := true
deployContext := &deploy.DeployContext{ deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{ CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: "eclipse-che", Name: "eclipse-che",
}, },
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
NativeUserMode: &nativeUserMode,
},
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
},
}, },
ClusterAPI: deploy.ClusterAPI{ ClusterAPI: deploy.ClusterAPI{
Client: cli, Client: cli,
@ -138,49 +123,6 @@ func TestNativeUserGateway(t *testing.T) {
} }
} }
func TestNoGatewayForMultiHost(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
},
},
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachingClient: cli,
Scheme: scheme.Scheme,
},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)
if err == nil {
t.Fatalf("Failed to get deployment: %v", err)
} else {
if v, ok := err.(errors.APIStatus); ok {
if v.Status().Code != 404 {
t.Fatalf("Deployment should not be found, thus code 404, but got '%d'", v.Status().Code)
}
} else {
t.Fatalf("Wrong error returned.")
}
}
}
func TestRandomCookieSecret(t *testing.T) { func TestRandomCookieSecret(t *testing.T) {
secret := generateRandomCookieSecret() secret := generateRandomCookieSecret()
if len(secret) != 24 { if len(secret) != 24 {
@ -194,6 +136,9 @@ func TestRandomCookieSecret(t *testing.T) {
} }
func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) { func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) {
util.IsOpenShift = true
util.IsOpenShift4 = true
t.Run("no skip auth", func(t *testing.T) { t.Run("no skip auth", func(t *testing.T) {
configmap := getGatewayOauthProxyConfigSpec(&orgv1.CheCluster{ configmap := getGatewayOauthProxyConfigSpec(&orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
@ -202,7 +147,7 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) {
ExternalPluginRegistry: true, ExternalPluginRegistry: true,
}}}, "blabol") }}}, "blabol")
config := configmap.Data["oauth-proxy.cfg"] config := configmap.Data["oauth-proxy.cfg"]
if strings.Contains(config, "skip_auth_regex") { if !strings.Contains(config, "skip_auth_regex = \"/healthz$\"") {
t.Errorf("oauth config shold not contain any skip auth when both registries are external") t.Errorf("oauth config shold not contain any skip auth when both registries are external")
} }
}) })
@ -216,7 +161,7 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) {
ExternalPluginRegistry: true, ExternalPluginRegistry: true,
}}}, "blabol") }}}, "blabol")
config := configmap.Data["oauth-proxy.cfg"] config := configmap.Data["oauth-proxy.cfg"]
if !strings.Contains(config, "skip_auth_regex = \"^/devfile-registry\"") { if !strings.Contains(config, "skip_auth_regex = \"^/devfile-registry|/healthz$\"") {
t.Error("oauth config should skip auth for devfile registry", config) t.Error("oauth config should skip auth for devfile registry", config)
} }
}) })
@ -229,7 +174,7 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) {
ExternalPluginRegistry: false, ExternalPluginRegistry: false,
}}}, "blabol") }}}, "blabol")
config := configmap.Data["oauth-proxy.cfg"] config := configmap.Data["oauth-proxy.cfg"]
if !strings.Contains(config, "skip_auth_regex = \"^/plugin-registry\"") { if !strings.Contains(config, "skip_auth_regex = \"^/plugin-registry|/healthz$\"") {
t.Error("oauth config should skip auth for plugin registry", config) t.Error("oauth config should skip auth for plugin registry", config)
} }
}) })
@ -242,17 +187,14 @@ func TestOauthProxyConfigUnauthorizedPaths(t *testing.T) {
ExternalPluginRegistry: false, ExternalPluginRegistry: false,
}}}, "blabol") }}}, "blabol")
config := configmap.Data["oauth-proxy.cfg"] config := configmap.Data["oauth-proxy.cfg"]
if !strings.Contains(config, "skip_auth_regex = \"^/plugin-registry|^/devfile-registry\"") { if !strings.Contains(config, "skip_auth_regex = \"^/plugin-registry|^/devfile-registry|/healthz$\"") {
t.Error("oauth config should skip auth for plugin and devfile registry.", config) t.Error("oauth config should skip auth for plugin and devfile registry.", config)
} }
}) })
t.Run("skip '/healthz' path", func(t *testing.T) { t.Run("skip '/healthz' path", func(t *testing.T) {
configmap := getGatewayOauthProxyConfigSpec(&orgv1.CheCluster{ configmap := getGatewayOauthProxyConfigSpec(&orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{}}, "blabol")
Auth: orgv1.CheClusterSpecAuth{
NativeUserMode: util.NewBoolPointer(true),
}}}, "blabol")
config := configmap.Data["oauth-proxy.cfg"] config := configmap.Data["oauth-proxy.cfg"]
assert.Contains(t, config, "/healthz$") assert.Contains(t, config, "/healthz$")
}) })
@ -264,13 +206,7 @@ func TestTokenValidityCheckOnOpenShiftNativeUser(t *testing.T) {
corev1.SchemeBuilder.AddToScheme(scheme.Scheme) corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cm, err := getGatewayServerConfigSpec(&deploy.DeployContext{ cm, err := getGatewayServerConfigSpec(&deploy.DeployContext{
CheCluster: &orgv1.CheCluster{ CheCluster: &orgv1.CheCluster{},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
NativeUserMode: util.NewBoolPointer(true),
},
},
},
ClusterAPI: deploy.ClusterAPI{ ClusterAPI: deploy.ClusterAPI{
Scheme: scheme.Scheme, Scheme: scheme.Scheme,
}, },

View File

@ -116,9 +116,7 @@ func skipAuthConfig(instance *orgv1.CheCluster) string {
if !instance.Spec.Server.ExternalDevfileRegistry { if !instance.Spec.Server.ExternalDevfileRegistry {
skipAuthPaths = append(skipAuthPaths, "^/"+deploy.DevfileRegistryName) skipAuthPaths = append(skipAuthPaths, "^/"+deploy.DevfileRegistryName)
} }
if instance.IsNativeUserModeEnabled() { skipAuthPaths = append(skipAuthPaths, "/healthz$")
skipAuthPaths = append(skipAuthPaths, "/healthz$")
}
if len(skipAuthPaths) > 0 { if len(skipAuthPaths) > 0 {
propName := "skip_auth_routes" propName := "skip_auth_routes"
if util.IsOpenShift { if util.IsOpenShift {

View File

@ -1,864 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package identityprovider
import (
"context"
"regexp"
"strconv"
"strings"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
"github.com/eclipse-che/che-operator/pkg/util"
"github.com/google/go-cmp/cmp"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)
const (
selectSslRequiredCommand = "OUT=$(psql keycloak -tAc \"SELECT 1 FROM REALM WHERE id = 'master'\"); " +
"if [ $OUT -eq 1 ]; then psql keycloak -tAc \"SELECT ssl_required FROM REALM WHERE id = 'master'\"; fi"
updateSslRequiredCommand = "psql keycloak -c \"update REALM set ssl_required='NONE' where id = 'master'\""
)
var (
trustpass = util.GeneratePasswd(12)
keycloakCustomDiffOpts = cmp.Options{
deploy.DefaultDeploymentDiffOpts,
cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
return x.Annotations["che.self-signed-certificate.version"] == y.Annotations["che.self-signed-certificate.version"] &&
x.Annotations["che.openshift-api-crt.version"] == y.Annotations["che.openshift-api-crt.version"] &&
x.Annotations["che.keycloak-ssl-required-updated"] == y.Annotations["che.keycloak-ssl-required-updated"]
}),
}
)
func SyncKeycloakDeploymentToCluster(deployContext *deploy.DeployContext) (bool, error) {
actual := &appsv1.Deployment{}
exists, err := deploy.GetNamespacedObject(deployContext, deploy.IdentityProviderName, actual)
if err != nil {
return false, err
}
if !exists {
actual = nil
}
specDeployment, err := GetSpecKeycloakDeployment(deployContext, actual)
if err != nil {
return false, err
}
return deploy.SyncDeploymentSpecToCluster(deployContext, specDeployment, deploy.DefaultDeploymentDiffOpts)
}
func GetSpecKeycloakDeployment(
deployContext *deploy.DeployContext,
clusterDeployment *appsv1.Deployment) (*appsv1.Deployment, error) {
optionalEnv := true
labels, labelSelector := deploy.GetLabelsAndSelector(deployContext.CheCluster, deploy.IdentityProviderName)
cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster)
keycloakImage := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderImage, deploy.DefaultKeycloakImage(deployContext.CheCluster))
pullPolicy := corev1.PullPolicy(util.GetValue(string(deployContext.CheCluster.Spec.Auth.IdentityProviderImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(keycloakImage)))
jbossDir := "/opt/eap"
if cheFlavor == "che" {
// writable dir in the upstream Keycloak image
jbossDir = "/scripts"
}
jbossCli := "/opt/jboss/keycloak/bin/jboss-cli.sh"
if cheFlavor == "codeready" {
jbossCli = "/opt/eap/bin/jboss-cli.sh"
}
if clusterDeployment != nil {
// To be compatible with prev deployments when "TRUSTPASS" env was used
clusterContainer := &clusterDeployment.Spec.Template.Spec.Containers[0]
env := util.FindEnv(clusterContainer.Env, "TRUSTPASS")
if env != nil {
trustpass = env.Value
} else {
env := util.FindEnv(clusterContainer.Env, "SSO_TRUSTSTORE_PASSWORD")
if env != nil {
trustpass = env.Value
}
}
}
cmResourceVersions := tls.GetAdditionalCACertsConfigMapVersion(deployContext)
terminationGracePeriodSeconds := int64(30)
cheCertSecretVersion := getSecretResourceVersion("self-signed-certificate", deployContext.CheCluster.Namespace, deployContext.ClusterAPI)
openshiftApiCertSecretVersion := getSecretResourceVersion("openshift-api-crt", deployContext.CheCluster.Namespace, deployContext.ClusterAPI)
// holds bash functions that should be available when run init commands in shell
bashFunctions := ""
// add various certificates to Java trust store so that Keycloak can connect to OpenShift API
// certificate that OpenShift router uses (for 4.0 only)
addRouterCrt := "if [ ! -z \"${CHE_SELF__SIGNED__CERT}\" ]; then echo \"${CHE_SELF__SIGNED__CERT}\" > " + jbossDir + "/che.crt && " +
"keytool -importcert -alias ROUTERCRT" +
" -keystore " + jbossDir + "/openshift.jks" +
" -file " + jbossDir + "/che.crt -storepass " + trustpass + " -noprompt; fi"
// certificate retrieved from http call to OpenShift API endpoint
addOpenShiftAPICrt := "if [ ! -z \"${OPENSHIFT_SELF__SIGNED__CERT}\" ]; then echo \"${OPENSHIFT_SELF__SIGNED__CERT}\" > " + jbossDir + "/openshift.crt && " +
"keytool -importcert -alias OPENSHIFTAPI" +
" -keystore " + jbossDir + "/openshift.jks" +
" -file " + jbossDir + "/openshift.crt -storepass " + trustpass + " -noprompt; fi"
// certificate mounted into container /var/run/secrets
addMountedCrt := " keytool -importcert -alias MOUNTEDCRT" +
" -keystore " + jbossDir + "/openshift.jks" +
" -file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -storepass " + trustpass + " -noprompt"
addMountedServiceCrt := "if [ -f /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt ]; then " +
"keytool -importcert -alias MOUNTEDSERVICECRT" +
" -keystore " + jbossDir + "/openshift.jks" +
" -file /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt -storepass " + trustpass + " -noprompt; fi"
importJavaCacerts := "keytool -importkeystore -srckeystore /etc/pki/ca-trust/extracted/java/cacerts" +
" -destkeystore " + jbossDir + "/openshift.jks" +
" -srcstorepass changeit -deststorepass " + trustpass
customPublicCertsDir := "/public-certs"
customPublicCertsVolumeSource := corev1.VolumeSource{}
customPublicCertsVolumeSource = corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: tls.CheAllCACertsConfigMapName,
},
},
}
customPublicCertsVolume := corev1.Volume{
Name: "che-public-certs",
VolumeSource: customPublicCertsVolumeSource,
}
customPublicCertsVolumeMount := corev1.VolumeMount{
Name: "che-public-certs",
MountPath: customPublicCertsDir,
}
addCustomPublicCertsCommand := "if [[ -d \"" + customPublicCertsDir + "\" && -n \"$(find " + customPublicCertsDir + " -type f)\" ]]; then " +
"for certfile in " + customPublicCertsDir + "/* ; do " +
"jks_import_ca_bundle $certfile " + jbossDir + "/openshift.jks " + trustpass + " ; " +
"done; fi"
bashFunctions += getImportCABundleScript()
addCertToTrustStoreCommand := addRouterCrt + " && " + addOpenShiftAPICrt + " && " + addMountedCrt + " && " + addMountedServiceCrt + " && " + importJavaCacerts + " && " + addCustomPublicCertsCommand
// upstream Keycloak has a bit different mechanism of adding jks
changeConfigCommand := "echo Installing certificates into Keycloak && " +
"echo -e \"embed-server --server-config=standalone.xml --std-out=echo \n" +
"/subsystem=keycloak-server/spi=truststore/:add \n" +
"/subsystem=keycloak-server/spi=truststore/provider=file/:add(properties={file => " +
"\"" + jbossDir + "/openshift.jks\", password => \"" + trustpass + "\", disabled => \"false\" },enabled=true) \n" +
"stop-embedded-server\" > /scripts/add_openshift_certificate.cli && " +
"/opt/jboss/keycloak/bin/jboss-cli.sh --file=/scripts/add_openshift_certificate.cli"
addProxyCliCommand := ""
applyProxyCliCommand := ""
proxyEnvVars := []corev1.EnvVar{}
if deployContext.Proxy.HttpProxy != "" {
proxyEnvVars = []corev1.EnvVar{
{
Name: "HTTP_PROXY",
Value: deployContext.Proxy.HttpProxy,
},
{
Name: "HTTPS_PROXY",
Value: deployContext.Proxy.HttpsProxy,
},
{
Name: "NO_PROXY",
Value: deployContext.Proxy.NoProxy,
},
}
quotedNoProxy := ""
for _, noProxyHost := range strings.Split(deployContext.Proxy.NoProxy, ",") {
if len(quotedNoProxy) != 0 {
quotedNoProxy += ","
}
var noProxyEntry string
if strings.HasPrefix(noProxyHost, ".") {
noProxyEntry = ".*" + strings.ReplaceAll(regexp.QuoteMeta(noProxyHost), "\\", "\\\\\\")
} else if strings.HasPrefix(noProxyHost, "*.") {
noProxyEntry = strings.TrimPrefix(noProxyHost, "*")
noProxyEntry = ".*" + strings.ReplaceAll(regexp.QuoteMeta(noProxyEntry), "\\", "\\\\\\")
} else {
noProxyEntry = strings.ReplaceAll(regexp.QuoteMeta(noProxyHost), "\\", "\\\\\\")
}
quotedNoProxy += "\"" + noProxyEntry + ";NO_PROXY\""
}
serverConfig := "standalone.xml"
if cheFlavor == "codeready" {
serverConfig = "standalone-openshift.xml"
}
addProxyCliCommand = " && echo Configuring Proxy && " +
"echo -e 'embed-server --server-config=" + serverConfig + " --std-out=echo \n" +
"/subsystem=keycloak-server/spi=connectionsHttpClient/provider=default:write-attribute(name=properties.proxy-mappings,value=[" + quotedNoProxy + ",\".*;" + deployContext.Proxy.HttpProxy + "\"]) \n" +
"stop-embedded-server' > " + jbossDir + "/setup-http-proxy.cli"
applyProxyCliCommand = " && " + jbossCli + " --file=" + jbossDir + "/setup-http-proxy.cli"
if cheFlavor == "codeready" {
applyProxyCliCommand = " && mkdir -p " + jbossDir + "/extensions && echo '#!/bin/bash\n" +
"" + jbossDir + "/bin/jboss-cli.sh --file=" + jbossDir + "/setup-http-proxy.cli' > " + jbossDir + "/extensions/postconfigure.sh && " +
"chmod a+x " + jbossDir + "/extensions/postconfigure.sh "
}
}
keycloakEnv := []corev1.EnvVar{
{
Name: "CM_REVISION",
Value: cmResourceVersions,
},
{
Name: "PROXY_ADDRESS_FORWARDING",
Value: "true",
},
{
Name: "DB_VENDOR",
Value: "POSTGRES",
},
{
Name: "DB_USERNAME",
Value: "keycloak",
},
{
Name: "DB_ADDR",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName),
},
{
Name: "DB_DATABASE",
Value: "keycloak",
},
{
Name: "POSTGRES_PORT_5432_TCP_ADDR",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName),
},
{
Name: "POSTGRES_PORT_5432_TCP_PORT",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort),
},
{
Name: "POSTGRES_PORT",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort),
},
{
Name: "POSTGRES_USER",
Value: "keycloak",
},
{
Name: "SSO_TRUSTSTORE",
Value: "openshift.jks",
},
{
Name: "SSO_TRUSTSTORE_DIR",
Value: jbossDir,
},
{
Name: "SSO_TRUSTSTORE_PASSWORD",
Value: trustpass,
},
{
Name: "CHE_SELF__SIGNED__CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "ca.crt",
LocalObjectReference: corev1.LocalObjectReference{
Name: deploy.CheTLSSelfSignedCertificateSecretName,
},
Optional: &optionalEnv,
},
},
},
{
Name: "OPENSHIFT_SELF__SIGNED__CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "ca.crt",
LocalObjectReference: corev1.LocalObjectReference{
Name: "openshift-api-crt",
},
Optional: &optionalEnv,
},
},
},
}
identityProviderPostgresSecret := deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret
if len(identityProviderPostgresSecret) > 0 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "DB_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "password",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderPostgresSecret,
},
},
},
})
} else {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "DB_PASSWORD",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresPassword,
})
}
identityProviderSecret := deployContext.CheCluster.Spec.Auth.IdentityProviderSecret
if len(identityProviderSecret) > 0 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "KEYCLOAK_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "password",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
},
corev1.EnvVar{
Name: "KEYCLOAK_USER",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "user",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
})
} else {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "KEYCLOAK_PASSWORD",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderPassword,
},
corev1.EnvVar{
Name: "KEYCLOAK_USER",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName,
})
}
if cheFlavor == "codeready" {
keycloakEnv = []corev1.EnvVar{
{
Name: "CM_REVISION",
Value: cmResourceVersions,
},
{
Name: "PROXY_ADDRESS_FORWARDING",
Value: "true",
},
{
Name: "DB_SERVICE_PREFIX_MAPPING",
Value: "keycloak-postgresql=DB",
},
{
Name: "KEYCLOAK_POSTGRESQL_SERVICE_HOST",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName),
},
{
Name: "KEYCLOAK_POSTGRESQL_SERVICE_PORT",
Value: util.GetValue(deployContext.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort),
},
{
Name: "DB_DATABASE",
Value: "keycloak",
},
{
Name: "DB_USERNAME",
Value: "keycloak",
},
{
Name: "DB_VENDOR",
Value: "POSTGRES",
},
{
Name: "SSO_TRUSTSTORE",
Value: "openshift.jks",
},
{
Name: "SSO_TRUSTSTORE_DIR",
Value: jbossDir,
},
{
Name: "SSO_TRUSTSTORE_PASSWORD",
Value: trustpass,
},
{
Name: "CHE_SELF__SIGNED__CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "ca.crt",
LocalObjectReference: corev1.LocalObjectReference{
Name: deploy.CheTLSSelfSignedCertificateSecretName,
},
Optional: &optionalEnv,
},
},
},
{
Name: "OPENSHIFT_SELF__SIGNED__CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "ca.crt",
LocalObjectReference: corev1.LocalObjectReference{
Name: "openshift-api-crt",
},
Optional: &optionalEnv,
},
},
},
}
identityProviderPostgresSecret := deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresSecret
if len(identityProviderPostgresSecret) > 0 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "DB_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "password",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderPostgresSecret,
},
},
},
})
} else {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "DB_PASSWORD",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderPostgresPassword,
})
}
identityProviderSecret := deployContext.CheCluster.Spec.Auth.IdentityProviderSecret
if len(identityProviderSecret) > 0 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "SSO_ADMIN_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "password",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
},
corev1.EnvVar{
Name: "SSO_ADMIN_USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "user",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
})
} else {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "SSO_ADMIN_PASSWORD",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderPassword,
},
corev1.EnvVar{
Name: "SSO_ADMIN_USERNAME",
Value: deployContext.CheCluster.Spec.Auth.IdentityProviderAdminUserName,
})
}
}
// Mount GITHUB_CLIENT_ID and GITHUB_SECRET to keycloak container
secrets, err := deploy.GetSecrets(deployContext, map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration,
}, map[string]string{
deploy.CheEclipseOrgOAuthScmServer: "github",
})
if err != nil {
return nil, err
} else if len(secrets) == 1 {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "GITHUB_CLIENT_ID",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "id",
LocalObjectReference: corev1.LocalObjectReference{
Name: secrets[0].Name,
},
},
},
}, corev1.EnvVar{
Name: "GITHUB_SECRET",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "secret",
LocalObjectReference: corev1.LocalObjectReference{
Name: secrets[0].Name,
},
},
},
})
}
for _, envvar := range proxyEnvVars {
keycloakEnv = append(keycloakEnv, envvar)
}
// Enable internal network for keycloak
if deployContext.CheCluster.IsInternalClusterSVCNamesEnabled() && !deployContext.CheCluster.Spec.Auth.ExternalIdentityProvider {
keycloakEnv = append(keycloakEnv, corev1.EnvVar{
Name: "KEYCLOAK_FRONTEND_URL",
Value: deployContext.CheCluster.Status.KeycloakURL,
})
}
evaluateKeycloakSystemProperties := "KEYCLOAK_SYS_PROPS=\"-Dkeycloak.profile.feature.token_exchange=enabled -Dkeycloak.profile.feature.admin_fine_grained_authz=enabled\""
if cheFlavor == "codeready" {
// We need to export `KEYCLOAK_SYS_PROPS` to pass them to standalone.sh via openshift-launch.sh
evaluateKeycloakSystemProperties = "export KEYCLOAK_SYS_PROPS=\"\""
}
// Evaluating keycloak.connectionsHttpClient.default system properties, see details: https://github.com/eclipse/che/issues/19653
evaluateExpectContinueEnabled := "if [[ $KEYCLOAK_CONNECTIONS_HTTP_CLIENT_DEFAULT_EXPECT_CONTINUE_ENABLED != false ]]; then KEYCLOAK_SYS_PROPS=$KEYCLOAK_SYS_PROPS\" -Dkeycloak.connectionsHttpClient.default.expect-continue-enabled=true\"; fi"
evaluateReuseConnections := "if [[ $KEYCLOAK_CONNECTIONS_HTTP_CLIENT_DEFAULT_REUSE_CONNECTIONS != true ]]; then KEYCLOAK_SYS_PROPS=$KEYCLOAK_SYS_PROPS\" -Dkeycloak.connectionsHttpClient.default.reuse-connections=false\"; fi"
command := bashFunctions + "\n" +
addCertToTrustStoreCommand +
addProxyCliCommand +
applyProxyCliCommand +
" && " + evaluateKeycloakSystemProperties +
" && " + evaluateExpectContinueEnabled +
" && " + evaluateReuseConnections +
" && " + changeConfigCommand +
" && /opt/jboss/tools/docker-entrypoint.sh -b 0.0.0.0 -c standalone.xml $KEYCLOAK_SYS_PROPS"
if cheFlavor == "codeready" {
addUsernameReadonlyTheme := "baseTemplate=/opt/eap/themes/base/login/login-update-profile.ftl" +
" && readOnlyTemplateDir=/opt/eap/themes/codeready-username-readonly/login" +
" && readOnlyTemplate=${readOnlyTemplateDir}/login-update-profile.ftl" +
" && if [ ! -d ${readOnlyTemplateDir} ]; then" +
" mkdir -p ${readOnlyTemplateDir}" +
" && cp ${baseTemplate} ${readOnlyTemplate}" +
" && echo \"parent=rh-sso\" > ${readOnlyTemplateDir}/theme.properties" +
" && sed -i 's|id=\"username\" name=\"username\"|id=\"username\" readonly name=\"username\"|g' ${readOnlyTemplate}; fi"
addUsernameValidationForKeycloakTheme := "sed -i 's|id=\"username\" name=\"username\"|" +
"id=\"username\" " +
"pattern=\"[a-z]([-a-z0-9]{0,61}[a-z0-9])?\" " +
"title=\"Username has to comply with the DNS naming convention. An alphanumeric (a-z, and 0-9) string, with a maximum length of 63 characters, with the '-' character allowed anywhere except the first or last character.\" " +
"name=\"username\"|g' ${baseTemplate}"
patchOpenshiftLaunch := "sed -i 's|standalone.sh -c standalone-openshift.xml|standalone.sh -c standalone-openshift.xml ${KEYCLOAK_SYS_PROPS}|' /opt/eap/bin/openshift-launch.sh"
command = bashFunctions + "\n" +
addUsernameReadonlyTheme +
" && " + addUsernameValidationForKeycloakTheme +
" && " + addCertToTrustStoreCommand +
addProxyCliCommand +
applyProxyCliCommand +
" && " + evaluateKeycloakSystemProperties +
" && " + evaluateExpectContinueEnabled +
" && " + evaluateReuseConnections +
" && " + patchOpenshiftLaunch +
" && echo \"feature.token_exchange=enabled\nfeature.admin_fine_grained_authz=enabled\" > /opt/eap/standalone/configuration/profile.properties " +
" && sed -i 's/WILDCARD/ANY/g' /opt/eap/bin/launch/keycloak-spi.sh && /opt/eap/bin/openshift-launch.sh -b 0.0.0.0"
}
sslRequiredUpdatedForMasterRealm := isSslRequiredUpdatedForMasterRealm(deployContext)
if sslRequiredUpdatedForMasterRealm {
// update command to restart pod
command = "echo \"ssl_required WAS UPDATED for master realm.\" && " + command
}
ports := []corev1.ContainerPort{
{
Name: deploy.IdentityProviderName,
ContainerPort: 8080,
Protocol: "TCP",
},
}
if deployContext.CheCluster.Spec.Auth.Debug {
ports = append(ports, corev1.ContainerPort{
Name: "debug",
ContainerPort: 8787,
Protocol: "TCP",
})
keycloakEnv = append(keycloakEnv, []corev1.EnvVar{
{
Name: "DEBUG",
Value: "true",
},
{
Name: "DEBUG_PORT",
Value: "*:8787",
},
}...)
command += " --debug"
}
args := []string{"-c", command}
deployment := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: deploy.IdentityProviderName,
Namespace: deployContext.CheCluster.Namespace,
Labels: labels,
Annotations: map[string]string{
"che.self-signed-certificate.version": cheCertSecretVersion,
"che.openshift-api-crt.version": openshiftApiCertSecretVersion,
"che.keycloak-ssl-required-updated": strconv.FormatBool(sslRequiredUpdatedForMasterRealm),
},
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{MatchLabels: labelSelector},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
customPublicCertsVolume,
},
Containers: []corev1.Container{
{
Name: deploy.IdentityProviderName,
Image: keycloakImage,
ImagePullPolicy: pullPolicy,
Command: []string{
"/bin/sh",
},
Args: args,
Ports: ports,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceMemory: util.GetResourceQuantity(
deployContext.CheCluster.Spec.Auth.IdentityProviderContainerResources.Requests.Memory,
deploy.DefaultIdentityProviderMemoryRequest),
corev1.ResourceCPU: util.GetResourceQuantity(
deployContext.CheCluster.Spec.Auth.IdentityProviderContainerResources.Requests.Cpu,
deploy.DefaultIdentityProviderCpuRequest),
},
Limits: corev1.ResourceList{
corev1.ResourceMemory: util.GetResourceQuantity(
deployContext.CheCluster.Spec.Auth.IdentityProviderContainerResources.Limits.Memory,
deploy.DefaultIdentityProviderMemoryLimit),
corev1.ResourceCPU: util.GetResourceQuantity(
deployContext.CheCluster.Spec.Auth.IdentityProviderContainerResources.Limits.Cpu,
deploy.DefaultIdentityProviderCpuLimit),
},
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "auth/js/keycloak.js",
Port: intstr.IntOrString{
Type: intstr.Int,
IntVal: int32(8080),
},
Scheme: corev1.URISchemeHTTP,
},
},
InitialDelaySeconds: 30,
FailureThreshold: 10,
TimeoutSeconds: 5,
PeriodSeconds: 10,
SuccessThreshold: 1,
},
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
TCPSocket: &corev1.TCPSocketAction{
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: 300,
FailureThreshold: 10,
TimeoutSeconds: 5,
PeriodSeconds: 10,
SuccessThreshold: 1,
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
},
Env: keycloakEnv,
VolumeMounts: []corev1.VolumeMount{
customPublicCertsVolumeMount,
},
},
},
TerminationGracePeriodSeconds: &terminationGracePeriodSeconds,
RestartPolicy: "Always",
},
},
},
}
if deploy.IsComponentReadinessInitContainersConfigured(deployContext.CheCluster) {
if !deployContext.CheCluster.Spec.Database.ExternalDb {
waitForPostgresInitContainer, err := postgres.GetWaitForPostgresInitContainer(deployContext)
if err != nil {
return nil, err
}
deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, *waitForPostgresInitContainer)
}
}
return deployment, nil
}
func getSecretResourceVersion(name string, namespace string, clusterAPI deploy.ClusterAPI) string {
secret := &corev1.Secret{}
err := clusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, secret)
if err != nil {
if !errors.IsNotFound(err) {
logrus.Errorf("Failed to get %s secret: %s", name, err)
}
return ""
}
return secret.ResourceVersion
}
func isSslRequiredUpdatedForMasterRealm(deployContext *deploy.DeployContext) bool {
if deployContext.CheCluster.Spec.Database.ExternalDb {
return false
}
if util.IsTestMode() {
return false
}
actual := &appsv1.Deployment{}
exists, err := deploy.GetNamespacedObject(deployContext, deploy.IdentityProviderName, actual)
if !exists || err != nil {
if err != nil {
logrus.Error(err)
}
return false
}
value, err := strconv.ParseBool(actual.ObjectMeta.Annotations["che.keycloak-ssl-required-updated"])
if err == nil && value {
return true
}
dbValue, _ := getSslRequiredForMasterRealm(deployContext.CheCluster)
return dbValue == "NONE"
}
func getSslRequiredForMasterRealm(cr *orgv1.CheCluster) (string, error) {
stdout, err := util.K8sclient.ExecIntoPod(
cr,
deploy.PostgresName,
func(cr *orgv1.CheCluster) (string, error) {
return selectSslRequiredCommand, nil
},
"")
return strings.TrimSpace(stdout), err
}
func updateSslRequiredForMasterRealm(cr *orgv1.CheCluster) error {
_, err := util.K8sclient.ExecIntoPod(
cr,
deploy.PostgresName,
func(cr *orgv1.CheCluster) (string, error) {
return updateSslRequiredCommand, nil
},
"Update ssl_required to NONE")
return err
}
func ProvisionKeycloakResources(deployContext *deploy.DeployContext) error {
if !deployContext.CheCluster.Spec.Database.ExternalDb {
value, err := getSslRequiredForMasterRealm(deployContext.CheCluster)
if err != nil {
return err
}
if value != "NONE" {
err := updateSslRequiredForMasterRealm(deployContext.CheCluster)
if err != nil {
return err
}
}
}
_, err := util.K8sclient.ExecIntoPod(
deployContext.CheCluster,
deploy.IdentityProviderName,
GetKeycloakProvisionCommand,
"create realm, client and user")
return err
}
// getImportCABundleScript returns string which contains bash function that imports ca-bundle into jks
// The function has three arguments:
// - absolute path to ca-bundle file
// - absolute path to java keystore
// - java keystore password
func getImportCABundleScript() string {
return `
function jks_import_ca_bundle {
CA_FILE=$1
KEYSTORE_PATH=$2
KEYSTORE_PASSWORD=$3
if [ ! -f $CA_FILE ]; then
# CA bundle file doesn't exist, skip it
echo "Failed to import CA certificates from ${CA_FILE}. File doesn't exist"
return
fi
bundle_name=$(basename $CA_FILE)
certs_imported=0
cert_index=0
tmp_file=/tmp/cert.pem
is_cert=false
while IFS= read -r line; do
if [ "$line" == "-----BEGIN CERTIFICATE-----" ]; then
# Start copying a new certificate
is_cert=true
cert_index=$((cert_index+1))
# Reset destination file and add header line
echo $line > ${tmp_file}
elif [ "$line" == "-----END CERTIFICATE-----" ]; then
# End of the certificate is reached, add it to trust store
is_cert=false
echo $line >> ${tmp_file}
keytool -importcert -alias "${bundle_name}_${cert_index}" -keystore $KEYSTORE_PATH -file $tmp_file -storepass $KEYSTORE_PASSWORD -noprompt && \
certs_imported=$((certs_imported+1))
elif [ "$is_cert" == true ]; then
# In the middle of a certificate, copy line to target file
echo $line >> ${tmp_file}
fi
done < "$CA_FILE"
echo "Imported ${certs_imported} certificates from ${CA_FILE}"
# Clean up
rm -f $tmp_file
}
`
}

View File

@ -1,330 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package identityprovider
import (
"context"
"os"
"reflect"
"github.com/eclipse-che/che-operator/pkg/util"
"github.com/google/go-cmp/cmp"
"github.com/eclipse-che/che-operator/pkg/deploy"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"testing"
)
func TestDeployment(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
memoryLimit string
memoryRequest string
cpuLimit string
cpuRequest string
cheCluster *orgv1.CheCluster
}
testCases := []testCase{
{
name: "Test default limits",
initObjects: []runtime.Object{},
memoryLimit: deploy.DefaultIdentityProviderMemoryLimit,
memoryRequest: deploy.DefaultIdentityProviderMemoryRequest,
cpuLimit: deploy.DefaultIdentityProviderCpuLimit,
cpuRequest: deploy.DefaultIdentityProviderCpuRequest,
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
},
},
},
{
name: "Test custom limits",
initObjects: []runtime.Object{},
cpuLimit: "250m",
cpuRequest: "150m",
memoryLimit: "250Mi",
memoryRequest: "150Mi",
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
IdentityProviderContainerResources: orgv1.ResourcesCustomSettings{
Limits: orgv1.Resources{
Cpu: "250m",
Memory: "250Mi",
},
Requests: orgv1.Resources{
Cpu: "150m",
Memory: "150Mi",
},
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
deployContext := &deploy.DeployContext{
CheCluster: testCase.cheCluster,
ClusterAPI: deploy.ClusterAPI{
Client: cli,
Scheme: scheme.Scheme,
},
Proxy: &deploy.Proxy{},
}
deployment, err := GetSpecKeycloakDeployment(deployContext, nil)
if err != nil {
t.Fatalf("Error creating deployment: %v", err)
}
util.CompareResources(deployment,
util.TestExpectedResources{
MemoryLimit: testCase.memoryLimit,
MemoryRequest: testCase.memoryRequest,
CpuRequest: testCase.cpuRequest,
CpuLimit: testCase.cpuLimit,
},
t)
util.ValidateSecurityContext(deployment, t)
})
}
}
func TestMountGitHubOAuthEnvVar(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
cheCluster *orgv1.CheCluster
expectedIdEnv corev1.EnvVar
expectedSecretEnv corev1.EnvVar
}
testCases := []testCase{
{
name: "Test",
initObjects: []runtime.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "github-oauth-config",
Namespace: "eclipse-che",
Labels: map[string]string{
"app.kubernetes.io/part-of": "che.eclipse.org",
"app.kubernetes.io/component": "oauth-scm-configuration",
},
Annotations: map[string]string{
"che.eclipse.org/oauth-scm-server": "github",
},
},
Data: map[string][]byte{
"id": []byte("some__id"),
"secret": []byte("some_secret"),
},
},
},
cheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
},
},
expectedIdEnv: corev1.EnvVar{
Name: "GITHUB_CLIENT_ID",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "id",
LocalObjectReference: corev1.LocalObjectReference{
Name: "github-oauth-config",
},
},
},
},
expectedSecretEnv: corev1.EnvVar{
Name: "GITHUB_SECRET",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "secret",
LocalObjectReference: corev1.LocalObjectReference{
Name: "github-oauth-config",
},
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
deployContext := &deploy.DeployContext{
CheCluster: testCase.cheCluster,
ClusterAPI: deploy.ClusterAPI{
Client: cli,
Scheme: scheme.Scheme,
},
Proxy: &deploy.Proxy{},
}
deployment, err := GetSpecKeycloakDeployment(deployContext, nil)
if err != nil {
t.Fatalf("Error creating deployment: %v", err)
}
container := &deployment.Spec.Template.Spec.Containers[0]
idEnv := util.FindEnv(container.Env, "GITHUB_CLIENT_ID")
if !reflect.DeepEqual(testCase.expectedIdEnv, *idEnv) {
t.Errorf("Expected Env and Env returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedIdEnv, idEnv))
}
secretEnv := util.FindEnv(container.Env, "GITHUB_SECRET")
if !reflect.DeepEqual(testCase.expectedSecretEnv, *secretEnv) {
t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedSecretEnv, secretEnv))
}
})
}
}
func TestSyncKeycloakDeploymentToCluster(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
},
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachingClient: cli,
Scheme: scheme.Scheme,
},
Proxy: &deploy.Proxy{},
}
// initial sync
done, err := SyncKeycloakDeploymentToCluster(deployContext)
if !done || err != nil {
t.Fatalf("Failed to sync deployment: %v", err)
}
actual := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: "eclipse-che"}, actual)
if err != nil {
t.Fatalf("Failed to sync deployment: %v", err)
}
// create certs configmap
err = cli.Create(context.TODO(), &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "ca-certs-merged",
Namespace: "eclipse-che",
// Go client set up resource version 1 itself on object creation.
// ResourceVersion: "1",
},
})
if err != nil {
t.Fatalf("Failed to create configmap: %v", err)
}
// create self-signed-certificate secret
err = cli.Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "self-signed-certificate",
Namespace: "eclipse-che",
// Go client set up resource version 1 itself on object creation.
// ResourceVersion: "1",
},
})
if err != nil {
t.Fatalf("Failed to create secret: %v", err)
}
caSecret := &corev1.Secret{}
err = cli.Get(context.TODO(), types.NamespacedName{
Name: "self-signed-certificate",
Namespace: "eclipse-che",
}, caSecret)
if err != nil {
t.Fatalf("Failed to get secret: %v", err)
}
// Let's really change something. Go client will increment secret resource version itself(from '1' to '2').
caSecret.GenerateName = "test"
err = cli.Update(context.TODO(), caSecret)
if err != nil {
t.Fatalf("Failed to update secret: %s", err.Error())
}
// sync a new deployment
_, err = SyncKeycloakDeploymentToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync deployment: %v", err)
}
// sync twice to be sure update done correctly
done, err = SyncKeycloakDeploymentToCluster(deployContext)
if !done || err != nil {
t.Fatalf("Failed to sync deployment: %v", err)
}
actual = &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: "eclipse-che"}, actual)
if err != nil {
t.Fatalf("Failed to sync deployment: %v", err)
}
// check ca-certs-merged revision
cmRevision := util.FindEnv(actual.Spec.Template.Spec.Containers[0].Env, "CM_REVISION")
if cmRevision == nil || cmRevision.Value != "1" {
t.Fatalf("Failed to sync deployment")
}
// check self-signed-certificate secret revision
if actual.ObjectMeta.Annotations["che.self-signed-certificate.version"] != "2" {
t.Fatalf("Failed to sync deployment")
}
}

View File

@ -1,186 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package identityprovider
import (
"bytes"
"io/ioutil"
"text/template"
v1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
"github.com/sirupsen/logrus"
)
func GetKeycloakProvisionCommand(cr *v1.CheCluster) (command string, err error) {
cheFlavor := deploy.DefaultCheFlavor(cr)
requiredActions := (map[bool]string{true: "\"UPDATE_PASSWORD\"", false: ""})[cr.Spec.Auth.UpdateAdminPassword]
keycloakTheme := (map[bool]string{true: "rh-sso", false: "che"})[cheFlavor == "codeready"]
realmDisplayName := (map[bool]string{true: "CodeReady Workspaces", false: "Eclipse Che"})[cheFlavor == "codeready"]
script, keycloakRealm, keycloakClientId, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
data := struct {
Script string
KeycloakAdminUserName string
KeycloakAdminPassword string
KeycloakRealm string
RealmDisplayName string
KeycloakTheme string
CheHost string
KeycloakClientId string
RequiredActions string
}{
script,
keycloakUserEnvVar,
keycloakPasswordEnvVar,
keycloakRealm,
realmDisplayName,
keycloakTheme,
cr.Spec.Server.CheHost,
keycloakClientId,
requiredActions,
}
return getCommandFromTemplateFile(cr, "/tmp/keycloak-provision.sh", data)
}
func GetKeycloakUpdateCommand(cr *v1.CheCluster) (command string, err error) {
cheFlavor := deploy.DefaultCheFlavor(cr)
realmDisplayName := (map[bool]string{true: "CodeReady Workspaces", false: "Eclipse Che"})[cheFlavor == "codeready"]
script, keycloakRealm, keycloakClientId, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
data := struct {
Script string
KeycloakAdminUserName string
KeycloakAdminPassword string
KeycloakRealm string
RealmDisplayName string
CheHost string
KeycloakClientId string
}{
script,
keycloakUserEnvVar,
keycloakPasswordEnvVar,
keycloakRealm,
realmDisplayName,
cr.Spec.Server.CheHost,
keycloakClientId,
}
return getCommandFromTemplateFile(cr, "/tmp/keycloak-update.sh", data)
}
func GetOpenShiftIdentityProviderProvisionCommand(cr *v1.CheCluster, oAuthClientName string, oauthSecret string) (string, error) {
isOpenShift4 := util.IsOpenShift4
providerId := (map[bool]string{true: "openshift-v4", false: "openshift-v3"})[isOpenShift4]
apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls()
if err != nil {
logrus.Errorf("Failed to auto-detect public OpenShift API URL. Configure it in Identity provider details page in Keycloak admin console: %s", err)
return "", err
}
script, keycloakRealm, keycloakClientId, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
data := struct {
Script string
KeycloakAdminUserName string
KeycloakAdminPassword string
KeycloakRealm string
ProviderId string
OAuthClientName string
OauthSecret string
OpenShiftApiUrl string
KeycloakClientId string
}{
script,
keycloakUserEnvVar,
keycloakPasswordEnvVar,
keycloakRealm,
providerId,
oAuthClientName,
oauthSecret,
map[bool]string{true: apiInternalUrl, false: apiUrl}[apiInternalUrl != ""],
keycloakClientId,
}
return getCommandFromTemplateFile(cr, "/tmp/oauth-provision.sh", data)
}
func GetGitHubIdentityProviderCreateCommand(deployContext *deploy.DeployContext) (string, error) {
cr := deployContext.CheCluster
script, keycloakRealm, _, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
data := struct {
Script string
KeycloakAdminUserName string
KeycloakAdminPassword string
KeycloakRealm string
ProviderId string
}{
script,
keycloakUserEnvVar,
keycloakPasswordEnvVar,
keycloakRealm,
"github",
}
return getCommandFromTemplateFile(cr, "/tmp/create-github-identity-provider.sh", data)
}
func GetIdentityProviderDeleteCommand(cr *v1.CheCluster, identityProvider string) (string, error) {
script, keycloakRealm, _, keycloakUserEnvVar, keycloakPasswordEnvVar := getDefaults(cr)
data := struct {
Script string
KeycloakRealm string
KeycloakAdminUserName string
KeycloakAdminPassword string
ProviderId string
}{
script,
keycloakRealm,
keycloakUserEnvVar,
keycloakPasswordEnvVar,
identityProvider,
}
return getCommandFromTemplateFile(cr, "/tmp/delete-identity-provider.sh", data)
}
func getCommandFromTemplateFile(cr *v1.CheCluster, templateFile string, data interface{}) (string, error) {
cheFlavor := deploy.DefaultCheFlavor(cr)
file, err := ioutil.ReadFile(templateFile)
if err != nil {
return "", err
}
template, err := template.New("Template").Parse(string(file))
if err != nil {
return "", err
}
buffer := new(bytes.Buffer)
err = template.Execute(buffer, data)
if err != nil {
return "", err
}
if cheFlavor == "che" {
return "cd /scripts && export JAVA_TOOL_OPTIONS=-Duser.home=. && " + buffer.String(), nil
}
return "cd /home/jboss && " + buffer.String(), nil
}
func getDefaults(cr *v1.CheCluster) (string, string, string, string, string) {
cheFlavor := deploy.DefaultCheFlavor(cr)
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
keycloakClientId := util.GetValue(cr.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
if cheFlavor == "codeready" {
return "/opt/eap/bin/kcadm.sh", keycloakRealm, keycloakClientId, "${SSO_ADMIN_USERNAME}", "${SSO_ADMIN_PASSWORD}"
}
return "/opt/jboss/keycloak/bin/kcadm.sh", keycloakRealm, keycloakClientId, "${KEYCLOAK_USER}", "${KEYCLOAK_PASSWORD}"
}

View File

@ -12,22 +12,15 @@
package identityprovider package identityprovider
import ( import (
"context"
"errors"
"strings" "strings"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/expose"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
oauth "github.com/openshift/api/oauth/v1" oauth "github.com/openshift/api/oauth/v1"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
) )
@ -37,17 +30,6 @@ const (
var ( var (
oAuthClientDiffOpts = cmpopts.IgnoreFields(oauth.OAuthClient{}, "TypeMeta", "ObjectMeta") oAuthClientDiffOpts = cmpopts.IgnoreFields(oauth.OAuthClient{}, "TypeMeta", "ObjectMeta")
syncItems = []func(*deploy.DeployContext) (bool, error){
syncService,
syncExposure,
SyncKeycloakDeploymentToCluster,
syncKeycloakResources,
syncOpenShiftIdentityProvider,
SyncGitHubOAuth,
}
keycloakUpdated = false
keycloakCheHost = ""
) )
type IdentityProviderReconciler struct { type IdentityProviderReconciler struct {
@ -59,35 +41,10 @@ func NewIdentityProviderReconciler() *IdentityProviderReconciler {
} }
func (ip *IdentityProviderReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { func (ip *IdentityProviderReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
if ctx.CheCluster.Spec.Auth.ExternalIdentityProvider { done, err := syncNativeIdentityProviderItems(ctx)
keycloakURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL if !done {
if ctx.CheCluster.Status.KeycloakURL != keycloakURL { return reconcile.Result{Requeue: true}, false, err
ctx.CheCluster.Status.KeycloakURL = keycloakURL
if err := deploy.UpdateCheCRStatus(ctx, "status: Keycloak URL", keycloakURL); err != nil {
return reconcile.Result{}, false, err
}
}
return reconcile.Result{}, true, nil
} }
if ctx.CheCluster.IsNativeUserModeEnabled() {
done, err := syncNativeIdentityProviderItems(ctx)
if !done {
return reconcile.Result{Requeue: true}, false, err
}
return reconcile.Result{}, true, nil
}
for _, syncItem := range syncItems {
done, err := syncItem(ctx)
if !util.IsTestMode() {
if !done {
return reconcile.Result{}, false, err
}
}
}
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil
} }
@ -108,93 +65,6 @@ func (ip *IdentityProviderReconciler) Finalize(ctx *deploy.DeployContext) bool {
return true return true
} }
func syncService(deployContext *deploy.DeployContext) (bool, error) {
return deploy.SyncServiceToCluster(
deployContext,
deploy.IdentityProviderName,
[]string{"http"},
[]int32{8080},
deploy.IdentityProviderName)
}
func syncExposure(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
protocol := (map[bool]string{
true: "https",
false: "http"})[cr.Spec.Server.TlsSupport]
endpoint, done, err := expose.Expose(
deployContext,
deploy.IdentityProviderName,
cr.Spec.Auth.IdentityProviderRoute,
cr.Spec.Auth.IdentityProviderIngress,
createGatewayConfig(deployContext.CheCluster))
if !done {
return false, err
}
keycloakURL := protocol + "://" + endpoint
if cr.Spec.Auth.IdentityProviderURL != keycloakURL {
cr.Spec.Auth.IdentityProviderURL = keycloakURL
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak URL", keycloakURL); err != nil {
return false, err
}
cr.Status.KeycloakURL = keycloakURL
if err := deploy.UpdateCheCRStatus(deployContext, "Keycloak URL", keycloakURL); err != nil {
return false, err
}
}
return true, nil
}
func syncKeycloakResources(deployContext *deploy.DeployContext) (bool, error) {
if !util.IsTestMode() {
cr := deployContext.CheCluster
if !cr.Status.KeycloakProvisoned {
if err := ProvisionKeycloakResources(deployContext); err != nil {
return false, err
}
for {
cr.Status.KeycloakProvisoned = true
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with Keycloak", "true"); err != nil &&
apierrors.IsConflict(err) {
deploy.ReloadCheClusterCR(deployContext)
continue
}
break
}
}
// Updates keycloak if chehost has been changed
if !keycloakUpdated || keycloakCheHost != deployContext.CheCluster.Spec.Server.CheHost {
if _, err := util.K8sclient.ExecIntoPod(
deployContext.CheCluster,
deploy.IdentityProviderName,
GetKeycloakUpdateCommand,
"Update redirect URI-s and webOrigins"); err != nil {
return false, err
} else {
keycloakUpdated = true
keycloakCheHost = deployContext.CheCluster.Spec.Server.CheHost
}
}
}
return true, nil
}
func syncOpenShiftIdentityProvider(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
if util.IsOpenShift && cr.IsOpenShiftOAuthEnabled() {
return SyncOpenShiftIdentityProviderItems(deployContext)
}
return true, nil
}
func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) { func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster cr := deployContext.CheCluster
@ -222,55 +92,6 @@ func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool,
return true, nil return true, nil
} }
func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
return false, err
}
if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
return false, err
}
keycloakURL := cr.Spec.Auth.IdentityProviderURL
cheFlavor := deploy.DefaultCheFlavor(cr)
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
oAuthClient := getKeycloakOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
if !provisioned {
return false, err
}
if !util.IsTestMode() {
if !cr.Status.OpenShiftoAuthProvisioned {
// note that this uses the instance.Spec.Auth.IdentityProviderRealm and instance.Spec.Auth.IdentityProviderClientId.
// because we're not doing much of a change detection on those fields, we can't react on them changing here.
_, err := util.K8sclient.ExecIntoPod(
cr,
deploy.IdentityProviderName,
func(cr *orgv1.CheCluster) (string, error) {
return GetOpenShiftIdentityProviderProvisionCommand(cr, cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret)
},
"Create OpenShift identity provider")
if err != nil {
return false, err
}
for {
cr.Status.OpenShiftoAuthProvisioned = true
if err := deploy.UpdateCheCRStatus(deployContext, "status: provisioned with OpenShift identity provider", "true"); err != nil &&
apierrors.IsConflict(err) {
deploy.ReloadCheClusterCR(deployContext)
continue
}
break
}
}
}
return true, nil
}
func resolveOpenshiftOAuthClientName(deployContext *deploy.DeployContext) error { func resolveOpenshiftOAuthClientName(deployContext *deploy.DeployContext) error {
cr := deployContext.CheCluster cr := deployContext.CheCluster
oAuthClientName := cr.Spec.Auth.OAuthClientName oAuthClientName := cr.Spec.Auth.OAuthClientName
@ -296,157 +117,3 @@ func resolveOpenshiftOAuthClientSecret(deployContext *deploy.DeployContext) erro
} }
return nil return nil
} }
// SyncGitHubOAuth provisions GitHub OAuth if secret with annotation
// `che.eclipse.org/github-oauth-credentials=true` or `che.eclipse.org/oauth-scm-configuration=github`
// is mounted into a container
func SyncGitHubOAuth(deployContext *deploy.DeployContext) (bool, error) {
// get legacy secret
legacySecrets, err := deploy.GetSecrets(deployContext, map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: deploy.IdentityProviderName + "-secret",
}, map[string]string{
deploy.CheEclipseOrgGithubOAuthCredentials: "true",
})
if err != nil {
return false, err
}
secrets, err := deploy.GetSecrets(deployContext, map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: deploy.OAuthScmConfiguration,
}, map[string]string{
deploy.CheEclipseOrgOAuthScmServer: "github",
})
if err != nil {
return false, err
} else if len(secrets)+len(legacySecrets) > 1 {
return false, errors.New("More than 1 GitHub OAuth configuration secrets found")
}
isGitHubOAuthCredentialsExists := len(secrets) == 1 || len(legacySecrets) == 1
cr := deployContext.CheCluster
if isGitHubOAuthCredentialsExists {
if !cr.Status.GitHubOAuthProvisioned {
if !util.IsTestMode() {
_, err := util.K8sclient.ExecIntoPod(
cr,
deploy.IdentityProviderName,
func(cr *orgv1.CheCluster) (string, error) {
return GetGitHubIdentityProviderCreateCommand(deployContext)
},
"Create GitHub OAuth")
if err != nil {
return false, err
}
}
cr.Status.GitHubOAuthProvisioned = true
if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "true"); err != nil {
return false, err
}
}
} else {
if cr.Status.GitHubOAuthProvisioned {
if !util.IsTestMode() {
_, err := util.K8sclient.ExecIntoPod(
cr,
deploy.IdentityProviderName,
func(cr *orgv1.CheCluster) (string, error) {
return GetIdentityProviderDeleteCommand(cr, "github")
},
"Delete GitHub OAuth")
if err != nil {
return false, err
}
}
cr.Status.GitHubOAuthProvisioned = false
if err := deploy.UpdateCheCRStatus(deployContext, "status: GitHub OAuth provisioned", "false"); err != nil {
return false, err
}
}
}
return true, nil
}
func deleteIdentityProvider(ctx *deploy.DeployContext) error {
if !ctx.CheCluster.IsOpenShiftOAuthEnabled() && ctx.CheCluster.Status.OpenShiftoAuthProvisioned == true {
keycloakDeployment := &appsv1.Deployment{}
if err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: deploy.IdentityProviderName, Namespace: ctx.CheCluster.Namespace}, keycloakDeployment); err != nil {
logrus.Errorf("Deployment %s not found: %s", keycloakDeployment.Name, err.Error())
}
providerName := "openshift-v3"
if util.IsOpenShift4 {
providerName = "openshift-v4"
}
_, err := util.K8sclient.ExecIntoPod(
ctx.CheCluster,
keycloakDeployment.Name,
func(cr *orgv1.CheCluster) (string, error) {
return GetIdentityProviderDeleteCommand(ctx.CheCluster, providerName)
},
"delete OpenShift identity provider")
if err == nil {
oAuthClient := &oauth.OAuthClient{}
oAuthClientName := ctx.CheCluster.Spec.Auth.OAuthClientName
err := deploy.DeleteObjectWithFinalizer(ctx, types.NamespacedName{Name: oAuthClientName}, &oauth.OAuthClient{}, OAuthFinalizerName)
if err != nil {
logrus.Errorf("Failed to delete %s %s: %s", oAuthClient.Kind, oAuthClient.Name, err.Error())
}
for {
ctx.CheCluster.Status.OpenShiftoAuthProvisioned = false
if err := deploy.UpdateCheCRStatus(ctx, "OpenShiftoAuthProvisioned", "false"); err != nil {
if apierrors.IsConflict(err) {
deploy.ReloadCheClusterCR(ctx)
continue
}
}
break
}
for {
ctx.CheCluster.Spec.Auth.OAuthSecret = ""
ctx.CheCluster.Spec.Auth.OAuthClientName = ""
updateFields := map[string]string{
"oAuthSecret": "",
"oAuthClientName": "",
}
if err := deploy.UpdateCheCRSpecByFields(ctx, updateFields); err != nil {
if apierrors.IsConflict(err) {
deploy.ReloadCheClusterCR(ctx)
continue
}
}
break
}
return nil
}
return err
}
return nil
}
func createGatewayConfig(cheCluster *orgv1.CheCluster) *gateway.TraefikConfig {
cfg := gateway.CreateCommonTraefikConfig(
deploy.IdentityProviderName,
"PathPrefix(`/auth`)",
10,
"http://"+deploy.IdentityProviderName+":8080",
[]string{})
if util.IsOpenShift && cheCluster.IsNativeUserModeEnabled() {
cfg.AddAuthHeaderRewrite(deploy.IdentityProviderName)
}
return cfg
}

View File

@ -1,211 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package identityprovider
import (
"os"
"reflect"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/google/go-cmp/cmp"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"testing"
)
func TestSyncGitHubOAuth(t *testing.T) {
type testCase struct {
name string
initCR *orgv1.CheCluster
expectedCR *orgv1.CheCluster
initObjects []runtime.Object
}
testCases := []testCase{
{
name: "Should provision GitHub OAuth with legacy secret",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "0",
},
},
expectedCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "1",
},
Status: orgv1.CheClusterStatus{
GitHubOAuthProvisioned: true,
},
},
initObjects: []runtime.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "github-credentials",
Namespace: "eclipse-che",
Labels: map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: "keycloak-secret",
},
Annotations: map[string]string{
deploy.CheEclipseOrgGithubOAuthCredentials: "true",
},
},
Data: map[string][]byte{
"key": []byte("key-data"),
},
},
},
},
{
name: "Should provision GitHub OAuth",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "0",
},
},
expectedCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "1",
},
Status: orgv1.CheClusterStatus{
GitHubOAuthProvisioned: true,
},
},
initObjects: []runtime.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "github-oauth-config",
Namespace: "eclipse-che",
Labels: map[string]string{
"app.kubernetes.io/part-of": "che.eclipse.org",
"app.kubernetes.io/component": "oauth-scm-configuration",
},
Annotations: map[string]string{
"che.eclipse.org/oauth-scm-server": "github",
},
},
},
},
},
{
name: "Should not provision GitHub OAuth",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "0",
},
},
expectedCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "0",
},
},
initObjects: []runtime.Object{
&corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "github-credentials",
Namespace: "eclipse-che",
Labels: map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,
deploy.KubernetesComponentLabelKey: "keycloak-secret",
},
},
Data: map[string][]byte{
"key": []byte("key-data"),
},
},
},
},
{
name: "Should delete GitHub OAuth",
initCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "0",
},
Status: orgv1.CheClusterStatus{
GitHubOAuthProvisioned: true,
},
},
expectedCR: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "che-cluster",
Namespace: "eclipse-che",
ResourceVersion: "1",
},
Status: orgv1.CheClusterStatus{
GitHubOAuthProvisioned: false,
},
},
initObjects: []runtime.Object{},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects, testCase.initCR)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
deployContext := &deploy.DeployContext{
CheCluster: testCase.initCR,
ClusterAPI: deploy.ClusterAPI{
Client: cli,
Scheme: scheme.Scheme,
},
}
_, err := SyncGitHubOAuth(deployContext)
if err != nil {
t.Fatalf("Error mounting secret: %v", err)
}
if !reflect.DeepEqual(testCase.expectedCR, testCase.initCR) {
t.Errorf("Expected CR and CR returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedCR, testCase.initCR))
}
})
}
}

View File

@ -12,33 +12,10 @@
package identityprovider package identityprovider
import ( import (
"strings"
oauth "github.com/openshift/api/oauth/v1" oauth "github.com/openshift/api/oauth/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
func getKeycloakOAuthClientSpec(name string, oauthSecret string, keycloakURL string, keycloakRealm string, isOpenShift4 bool) *oauth.OAuthClient {
providerName := "openshift-v3"
if isOpenShift4 {
providerName = "openshift-v4"
}
redirectURLSuffix := "/realms/" + keycloakRealm + "/broker/" + providerName + "/endpoint"
redirectURIs := []string{
keycloakURL + redirectURLSuffix,
}
keycloakURL = strings.NewReplacer("https://", "", "http://", "").Replace(keycloakURL)
if !strings.Contains(keycloakURL, "://") {
redirectURIs = []string{
"http://" + keycloakURL + redirectURLSuffix,
"https://" + keycloakURL + redirectURLSuffix,
}
}
return getOAuthClientSpec(name, oauthSecret, redirectURIs)
}
func getOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) *oauth.OAuthClient { func getOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) *oauth.OAuthClient {
return &oauth.OAuthClient{ return &oauth.OAuthClient{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{

View File

@ -1,56 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package identityprovider
import (
"fmt"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
corev1 "k8s.io/api/core/v1"
)
func GetWaitForKeycloakInitContainer(deployContext *deploy.DeployContext) (*corev1.Container, error) {
keycloakReadinessCheckerImage := deploy.DefaultKeycloakImage(deployContext.CheCluster)
imagePullPolicy := corev1.PullPolicy(deploy.DefaultPullPolicyFromDockerImage(keycloakReadinessCheckerImage))
return &corev1.Container{
Name: "wait-for-identity-provider",
Image: keycloakReadinessCheckerImage,
ImagePullPolicy: imagePullPolicy,
Command: []string{
"/bin/sh",
"-c",
getCheckKeycloakReadinessScript(deployContext),
},
}, nil
}
func getCheckKeycloakReadinessScript(deployContext *deploy.DeployContext) string {
cheFlavor := deploy.DefaultCheFlavor(deployContext.CheCluster)
realmName := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
url := fmt.Sprintf("%s/realms/%s/.well-known/openid-configuration", deployContext.CheCluster.Status.KeycloakURL, realmName)
// URL example: https://keycloak-eclipse-che.192.168.99.254.nip.io/auth/realms/che/.well-known/openid-configuration
script := `
while : ; do
response_code=$(curl --connect-timeout 5 -kI %s 2>/dev/null | awk 'NR==1 {print $2}')
if [ "$response_code" == "200" ]; then
break
fi
echo 'waiting for Identity provider'
sleep 2
done
`
return fmt.Sprintf(script, url)
}

View File

@ -123,9 +123,6 @@ func TestImagePullerConfiguration(t *testing.T) {
ConfigMapName: "k8s-image-puller", ConfigMapName: "k8s-image-puller",
}, },
}, },
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
}, },
}, },
initObjects: []runtime.Object{ initObjects: []runtime.Object{
@ -557,9 +554,6 @@ func InitCheCRWithImagePullerEnabled() *orgv1.CheCluster {
ImagePuller: orgv1.CheClusterSpecImagePuller{ ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true, Enable: true,
}, },
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
}, },
} }
} }
@ -582,9 +576,6 @@ func InitCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
ImagePuller: orgv1.CheClusterSpecImagePuller{ ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true, Enable: true,
}, },
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
}, },
} }
} }
@ -603,9 +594,6 @@ func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *orgv1.CheCluster {
ImagePuller: orgv1.CheClusterSpecImagePuller{ ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true, Enable: true,
}, },
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
}, },
} }
} }
@ -628,9 +616,6 @@ func ExpectedCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
ImagePuller: orgv1.CheClusterSpecImagePuller{ ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true, Enable: true,
}, },
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
}, },
} }
} }
@ -675,9 +660,6 @@ func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *orgv1.CheCluster {
ConfigMapName: "k8s-image-puller", ConfigMapName: "k8s-image-puller",
}, },
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
} }
} }
@ -704,9 +686,6 @@ func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *orgv1.CheCluste
Images: images, Images: images,
}, },
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
} }
} }
@ -728,9 +707,6 @@ func InitCheCRWithImagePullerEnabledAndNewValuesSet() *orgv1.CheCluster {
ConfigMapName: "k8s-image-puller-trigger-update", ConfigMapName: "k8s-image-puller-trigger-update",
}, },
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
} }
} }

View File

@ -19,6 +19,7 @@ import (
orgv1 "github.com/eclipse-che/che-operator/api/v1" orgv1 "github.com/eclipse-che/che-operator/api/v1"
networking "k8s.io/api/networking/v1" networking "k8s.io/api/networking/v1"
v1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -52,11 +53,6 @@ func TestIngressSpec(t *testing.T) {
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: cheFlavor, Name: cheFlavor,
}, },
Spec: orgv1.CheClusterSpec{
K8s: orgv1.CheClusterSpecK8SOnly{
SingleHostExposureType: GatewaySingleHostExposureType,
},
},
} }
testCases := []testCase{ testCases := []testCase{
@ -90,7 +86,7 @@ func TestIngressSpec(t *testing.T) {
"kubernetes.io/ingress.class": "nginx", "kubernetes.io/ingress.class": "nginx",
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600",
"nginx.ingress.kubernetes.io/ssl-redirect": "false", "nginx.ingress.kubernetes.io/ssl-redirect": "true",
"nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k",
"nginx.org/websocket-services": "che-host", "nginx.org/websocket-services": "che-host",
"annotation-key": "annotation-value", "annotation-key": "annotation-value",
@ -101,6 +97,7 @@ func TestIngressSpec(t *testing.T) {
APIVersion: networking.SchemeGroupVersion.String(), APIVersion: networking.SchemeGroupVersion.String(),
}, },
Spec: networking.IngressSpec{ Spec: networking.IngressSpec{
TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}},
Rules: []networking.IngressRule{ Rules: []networking.IngressRule{
{ {
Host: "test-host", Host: "test-host",
@ -158,7 +155,7 @@ func TestIngressSpec(t *testing.T) {
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k",
"nginx.ingress.kubernetes.io/ssl-redirect": "false", "nginx.ingress.kubernetes.io/ssl-redirect": "true",
"nginx.org/websocket-services": "che-host", "nginx.org/websocket-services": "che-host",
"annotation-key": "annotation-value", "annotation-key": "annotation-value",
}, },
@ -168,6 +165,7 @@ func TestIngressSpec(t *testing.T) {
APIVersion: networking.SchemeGroupVersion.String(), APIVersion: networking.SchemeGroupVersion.String(),
}, },
Spec: networking.IngressSpec{ Spec: networking.IngressSpec{
TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}},
Rules: []networking.IngressRule{ Rules: []networking.IngressRule{
{ {
Host: "test-host", Host: "test-host",
@ -224,7 +222,7 @@ func TestIngressSpec(t *testing.T) {
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-buffer-size": "16k", "nginx.ingress.kubernetes.io/proxy-buffer-size": "16k",
"nginx.ingress.kubernetes.io/ssl-redirect": "false", "nginx.ingress.kubernetes.io/ssl-redirect": "true",
"nginx.org/websocket-services": "che-host", "nginx.org/websocket-services": "che-host",
"annotation-key": "annotation-value", "annotation-key": "annotation-value",
}, },
@ -234,6 +232,7 @@ func TestIngressSpec(t *testing.T) {
APIVersion: networking.SchemeGroupVersion.String(), APIVersion: networking.SchemeGroupVersion.String(),
}, },
Spec: networking.IngressSpec{ Spec: networking.IngressSpec{
TLS: []v1.IngressTLS{{Hosts: []string{"test-host"}}},
Rules: []networking.IngressRule{ Rules: []networking.IngressRule{
{ {
Host: "test-host", Host: "test-host",

View File

@ -14,7 +14,6 @@ package deploy
import ( import (
"reflect" "reflect"
"sort" "sort"
"strconv"
orgv1 "github.com/eclipse-che/che-operator/api/v1" orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
@ -63,9 +62,6 @@ func GetIngressSpec(
component string) (ingressUrl string, i *networking.Ingress) { component string) (ingressUrl string, i *networking.Ingress) {
cheFlavor := DefaultCheFlavor(deployContext.CheCluster) cheFlavor := DefaultCheFlavor(deployContext.CheCluster)
tlsSupport := deployContext.CheCluster.Spec.Server.TlsSupport
ingressStrategy := util.GetServerExposureStrategy(deployContext.CheCluster)
exposureType := GetSingleHostExposureType(deployContext.CheCluster)
ingressDomain := deployContext.CheCluster.Spec.K8s.IngressDomain ingressDomain := deployContext.CheCluster.Spec.K8s.IngressDomain
tlsSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName tlsSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName
ingressClass := util.GetValue(deployContext.CheCluster.Spec.K8s.IngressClass, DefaultIngressClass) ingressClass := util.GetValue(deployContext.CheCluster.Spec.K8s.IngressClass, DefaultIngressClass)
@ -73,24 +69,18 @@ func GetIngressSpec(
MergeLabels(labels, ingressCustomSettings.Labels) MergeLabels(labels, ingressCustomSettings.Labels)
pathType := networking.PathTypeImplementationSpecific pathType := networking.PathTypeImplementationSpecific
if tlsSupport { // for server and dashboard ingresses
// for server and dashboard ingresses if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" {
if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" { tlsSecretName = deployContext.CheCluster.Spec.Server.CheHostTLSSecret
tlsSecretName = deployContext.CheCluster.Spec.Server.CheHostTLSSecret
}
} }
if host == "" { if host == "" {
if ingressStrategy == "multi-host" { host = ingressDomain
host = component + "-" + deployContext.CheCluster.Namespace + "." + ingressDomain
} else if ingressStrategy == "single-host" {
host = ingressDomain
}
} }
var endpointPath, ingressPath string var endpointPath, ingressPath string
if path == "" { if path == "" {
endpointPath, ingressPath = evaluatePath(component, ingressStrategy) endpointPath, ingressPath = evaluatePath(component)
} else { } else {
ingressPath = path ingressPath = path
endpointPath = path endpointPath = path
@ -100,15 +90,14 @@ func GetIngressSpec(
"kubernetes.io/ingress.class": ingressClass, "kubernetes.io/ingress.class": ingressClass,
"nginx.ingress.kubernetes.io/proxy-read-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-read-timeout": "3600",
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600", "nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600",
"nginx.ingress.kubernetes.io/ssl-redirect": strconv.FormatBool(tlsSupport), "nginx.ingress.kubernetes.io/ssl-redirect": "true",
} }
if ingressStrategy != "multi-host" && (component == DevfileRegistryName || component == PluginRegistryName) { if component == DevfileRegistryName || component == PluginRegistryName {
annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/$1" annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/$1"
} }
// Set bigger proxy buffer size to prevent 502 auth error. // Set bigger proxy buffer size to prevent 502 auth error.
if component == IdentityProviderName || exposureType == GatewaySingleHostExposureType { annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "16k"
annotations["nginx.ingress.kubernetes.io/proxy-buffer-size"] = "16k"
}
for k, v := range ingressCustomSettings.Annotations { for k, v := range ingressCustomSettings.Annotations {
annotations[k] = v annotations[k] = v
} }
@ -176,13 +165,11 @@ func GetIngressSpec(
ingress.ObjectMeta.Annotations["nginx.org/websocket-services"] = serviceName ingress.ObjectMeta.Annotations["nginx.org/websocket-services"] = serviceName
} }
if tlsSupport { ingress.Spec.TLS = []networking.IngressTLS{
ingress.Spec.TLS = []networking.IngressTLS{ {
{ Hosts: []string{host},
Hosts: []string{host}, SecretName: tlsSecretName,
SecretName: tlsSecretName, },
},
}
} }
return host + endpointPath, ingress return host + endpointPath, ingress
@ -190,30 +177,17 @@ func GetIngressSpec(
// evaluatePath evaluates ingress path (one which is used for rule) // evaluatePath evaluates ingress path (one which is used for rule)
// and endpoint path (one which client should use during endpoint accessing) // and endpoint path (one which client should use during endpoint accessing)
func evaluatePath(component, ingressStrategy string) (endpointPath, ingressPath string) { func evaluatePath(component string) (endpointPath, ingressPath string) {
if ingressStrategy == "multi-host" { switch component {
case DevfileRegistryName:
fallthrough
case PluginRegistryName:
endpointPath = "/" + component
ingressPath = endpointPath + "/(.*)"
default:
ingressPath = "/" ingressPath = "/"
endpointPath = "/" endpointPath = "/"
// Keycloak needs special rule in multihost. It's exposed on / which redirects to /auth
// clients which does not support redirects needs /auth be explicitely set
if component == IdentityProviderName {
endpointPath = "/auth"
}
} else {
switch component {
case IdentityProviderName:
endpointPath = "/auth"
ingressPath = endpointPath + "/(.*)"
case DevfileRegistryName:
fallthrough
case PluginRegistryName:
endpointPath = "/" + component
ingressPath = endpointPath + "/(.*)"
default:
ingressPath = "/"
endpointPath = "/"
}
} }
return endpointPath, ingressPath return endpointPath, ingressPath
} }

View File

@ -91,8 +91,6 @@ func (m *Migrator) migrate(ctx *deploy.DeployContext) (bool, error) {
// - spec.server.gitSelfSignedCert ('che-git-self-signed-cert' config map) // - spec.server.gitSelfSignedCert ('che-git-self-signed-cert' config map)
// - spec.server.proxySecret // - spec.server.proxySecret
// - spec.database.chePostgresSecret // - spec.database.chePostgresSecret
// - spec.auth.identityProviderSecret
// - spec.auth.identityProviderPostgresSecret
// - spec.k8s.tlsSecretName // - spec.k8s.tlsSecretName
// Note, most of the objects above are autogenerated and do not require any migration, // Note, most of the objects above are autogenerated and do not require any migration,
// but to handle the case when some were created manually or operator updated, the check is done here. // but to handle the case when some were created manually or operator updated, the check is done here.
@ -134,19 +132,6 @@ func addPartOfCheLabeltoUserDefinedObjects(ctx *deploy.DeployContext) error {
} }
} }
// Keycloak related secrets
if ctx.CheCluster.Spec.Auth.IdentityProviderSecret != "" {
if err := addPartOfCheLabelToSecret(ctx, ctx.CheCluster.Spec.Auth.IdentityProviderSecret); err != nil {
return err
}
}
if ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret != "" {
if err := addPartOfCheLabelToSecret(ctx, ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret); err != nil {
return err
}
}
// Legacy config map with additional CA certificates // Legacy config map with additional CA certificates
if ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName != "" { if ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName != "" {
if err := addPartOfCheLabelToConfigMap(ctx, ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName); err != nil { if err := addPartOfCheLabelToConfigMap(ctx, ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName); err != nil {

View File

@ -1,21 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import "github.com/eclipse-che/che-operator/pkg/deploy"
func init() {
err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml")
if err != nil {
panic(err)
}
}

View File

@ -1,117 +0,0 @@
//
// Copyright (c) 2012-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import (
"context"
"reflect"
"strconv"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
userv1 "github.com/openshift/api/user/v1"
"github.com/sirupsen/logrus"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
warningNoIdentityProvidersMessage = "No Openshift identity providers."
AddIdentityProviderMessage = "Openshift oAuth was disabled. How to add identity provider read in the Help Link:"
warningNoRealUsersMessage = "No real users. Openshift oAuth was disabled. How to add new user read in the Help Link:"
failedUnableToGetOpenshiftUsers = "Unable to get users on the OpenShift cluster."
howToAddIdentityProviderLinkOS4 = "https://docs.openshift.com/container-platform/latest/authentication/understanding-identity-provider.html#identity-provider-overview_understanding-identity-provider"
howToConfigureOAuthLinkOS3 = "https://docs.openshift.com/container-platform/3.11/install_config/configuring_authentication.html"
)
type OpenShiftOAuth struct {
openShiftOAuthUser *OpenShiftOAuthUser
}
func NewOpenShiftOAuth(openShiftOAuthUser *OpenShiftOAuthUser) *OpenShiftOAuth {
return &OpenShiftOAuth{
openShiftOAuthUser,
}
}
func (oo *OpenShiftOAuth) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
if util.IsOpenShift && ctx.CheCluster.Spec.Auth.OpenShiftoAuth == nil {
return oo.enableOpenShiftOAuth(ctx)
}
return reconcile.Result{}, true, nil
}
func (oo *OpenShiftOAuth) Finalize(ctx *deploy.DeployContext) bool {
return true
}
func (oo *OpenShiftOAuth) enableOpenShiftOAuth(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
oauth := false
if util.IsOpenShift4 {
openshitOAuth, err := GetOpenshiftOAuth(ctx)
if err != nil {
logrus.Error("Unable to get Openshift oAuth. Cause: " + err.Error())
} else {
if len(openshitOAuth.Spec.IdentityProviders) > 0 {
oauth = true
} else if ctx.CheCluster.IsNativeUserModeEnabled() {
// enable OpenShift OAuth without adding initial OpenShift OAuth user
// since kubeadmin is a valid user for native user mode
oauth = true
} else if ctx.CheCluster.IsOpenShiftOAuthUserConfigured() {
provisioned, err := oo.openShiftOAuthUser.Create(ctx)
if err != nil {
logrus.Error(warningNoIdentityProvidersMessage + " Operator tried to create initial OpenShift OAuth user for HTPasswd identity provider, but failed. Cause: " + err.Error())
logrus.Info("To enable OpenShift OAuth, please add identity provider first: " + howToAddIdentityProviderLinkOS4)
// Don't try to create initial user any more, che-operator shouldn't hang on this step.
ctx.CheCluster.Spec.Auth.InitialOpenShiftOAuthUser = nil
if err := deploy.UpdateCheCRStatus(ctx, "initialOpenShiftOAuthUser", ""); err != nil {
return reconcile.Result{}, false, err
}
oauth = false
} else {
if !provisioned {
// let's wait some time
return reconcile.Result{}, false, err
}
oauth = true
}
}
}
} else { // Openshift 3
users := &userv1.UserList{}
listOptions := &client.ListOptions{}
if err := ctx.ClusterAPI.NonCachingClient.List(context.TODO(), users, listOptions); err != nil {
logrus.Error(failedUnableToGetOpenshiftUsers + " Cause: " + err.Error())
} else {
oauth = len(users.Items) >= 1
if !oauth {
logrus.Warn(warningNoRealUsersMessage + " " + howToConfigureOAuthLinkOS3)
}
}
}
newOAuthValue := util.NewBoolPointer(oauth)
if !reflect.DeepEqual(newOAuthValue, ctx.CheCluster.Spec.Auth.OpenShiftoAuth) {
ctx.CheCluster.Spec.Auth.OpenShiftoAuth = newOAuthValue
if err := deploy.UpdateCheCRSpec(ctx, "openShiftoAuth", strconv.FormatBool(oauth)); err != nil {
return reconcile.Result{Requeue: true}, false, err
}
}
return reconcile.Result{}, true, nil
}

View File

@ -1,234 +0,0 @@
//
// Copyright (c) 2012-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import (
"os"
"testing"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
configv1 "github.com/openshift/api/config/v1"
oauthv1 "github.com/openshift/api/oauth/v1"
userv1 "github.com/openshift/api/user/v1"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
var (
nonEmptyUserList = &userv1.UserList{
Items: []userv1.User{
{
ObjectMeta: metav1.ObjectMeta{
Name: "user1",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "user2",
},
},
},
}
oAuthWithNoIdentityProviders = &configv1.OAuth{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
}
oAuthWithIdentityProvider = &configv1.OAuth{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
Spec: configv1.OAuthSpec{
IdentityProviders: []configv1.IdentityProvider{
{
Name: "htpasswd",
},
},
},
}
proxy = &configv1.Proxy{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
}
)
func TestCaseAutoDetectOAuth(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
isOpenshift4 bool
initialOAuthValue *bool
oAuthExpected *bool
initialOpenShiftOAuthUserEnabled *bool
}
testCases := []testCase{
{
name: "che-operator should auto enable oAuth when Che CR with oAuth nil value on the Openshift 3 with users > 0",
initObjects: []runtime.Object{
nonEmptyUserList,
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: nil,
oAuthExpected: pointer.BoolPtr(true),
},
{
name: "che-operator should auto disable oAuth when Che CR with nil oAuth on the Openshift 3 with no users",
initObjects: []runtime.Object{
&userv1.UserList{},
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(false),
oAuthExpected: pointer.BoolPtr(false),
},
{
name: "che-operator should respect oAuth = true even if there no users on the Openshift 3",
initObjects: []runtime.Object{
&userv1.UserList{},
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(true),
oAuthExpected: pointer.BoolPtr(true),
},
{
name: "che-operator should respect oAuth = true even if there are some users on the Openshift 3",
initObjects: []runtime.Object{
nonEmptyUserList,
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(true),
oAuthExpected: pointer.BoolPtr(true),
},
{
name: "che-operator should respect oAuth = false even if there are some users on the Openshift 3",
initObjects: []runtime.Object{
nonEmptyUserList,
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(false),
oAuthExpected: pointer.BoolPtr(false),
},
{
name: "che-operator should respect oAuth = false even if no users on the Openshift 3",
initObjects: []runtime.Object{
&userv1.UserList{},
&oauthv1.OAuthClient{},
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(false),
oAuthExpected: pointer.BoolPtr(false),
},
{
name: "che-operator should auto enable oAuth when Che CR with nil value on the Openshift 4 with identity providers",
initObjects: []runtime.Object{
oAuthWithIdentityProvider,
proxy,
},
isOpenshift4: true,
initialOAuthValue: nil,
oAuthExpected: pointer.BoolPtr(true),
},
{
name: "che-operator should respect oAuth = true even if there no indentity providers on the Openshift 4",
initObjects: []runtime.Object{
oAuthWithNoIdentityProviders,
proxy,
},
isOpenshift4: true,
initialOAuthValue: pointer.BoolPtr(true),
oAuthExpected: pointer.BoolPtr(true),
initialOpenShiftOAuthUserEnabled: pointer.BoolPtr(true),
},
{
name: "che-operator should respect oAuth = true even if there are some users on the Openshift 4",
initObjects: []runtime.Object{
oAuthWithIdentityProvider,
proxy,
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(true),
oAuthExpected: pointer.BoolPtr(true),
initialOpenShiftOAuthUserEnabled: pointer.BoolPtr(true),
},
{
name: "che-operator should respect oAuth = false even if there no indentity providers on the Openshift 4",
initObjects: []runtime.Object{
oAuthWithNoIdentityProviders,
proxy,
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(false),
oAuthExpected: pointer.BoolPtr(false),
},
{
name: "che-operator should respect oAuth = false even if there are some users on the Openshift 4",
initObjects: []runtime.Object{
oAuthWithIdentityProvider,
proxy,
},
isOpenshift4: false,
initialOAuthValue: pointer.BoolPtr(false),
oAuthExpected: pointer.BoolPtr(false),
},
{
name: "che-operator should auto disable oAuth on error retieve identity providers",
initObjects: []runtime.Object{},
isOpenshift4: false,
initialOAuthValue: nil,
initialOpenShiftOAuthUserEnabled: pointer.BoolPtr(true),
oAuthExpected: pointer.BoolPtr(false),
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
checluster := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("CHE_FLAVOR"),
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: testCase.initialOAuthValue,
InitialOpenShiftOAuthUser: testCase.initialOpenShiftOAuthUserEnabled,
},
},
}
util.IsOpenShift = true
util.IsOpenShift4 = testCase.isOpenshift4
deployContext := deploy.GetTestDeployContext(checluster, testCase.initObjects)
openShiftOAuth := NewOpenShiftOAuth(NewOpenShiftOAuthUser())
_, done, err := openShiftOAuth.Reconcile(deployContext)
assert.Nil(t, err)
assert.True(t, done)
assert.NotNil(t, deployContext.CheCluster.Spec.Auth.OpenShiftoAuth)
assert.Equal(t, *testCase.oAuthExpected, *deployContext.CheCluster.Spec.Auth.OpenShiftoAuth)
})
}
}

View File

@ -1,321 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import (
"context"
"fmt"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
configv1 "github.com/openshift/api/config/v1"
userv1 "github.com/openshift/api/user/v1"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
HtpasswdIdentityProviderName = "htpasswd-eclipse-che"
HtpasswdSecretName = "htpasswd-eclipse-che"
OcConfigNamespace = "openshift-config"
OpenShiftOAuthUserCredentialsSecret = "openshift-oauth-user-credentials"
OpenshiftOauthUserFinalizerName = "openshift-oauth-user.finalizers.che.eclipse.org"
)
type IOpenShiftOAuthUser interface {
Create(ctx *deploy.DeployContext) (bool, error)
Delete(ctx *deploy.DeployContext) error
}
type OpenShiftOAuthUser struct {
userPassword string
runnable util.Runnable
deploy.Reconcilable
IOpenShiftOAuthUser
}
func NewOpenShiftOAuthUser() *OpenShiftOAuthUser {
return &OpenShiftOAuthUser{
userPassword: util.GeneratePasswd(6),
runnable: util.NewRunnable(), // real process, mock for tests
}
}
func (oou *OpenShiftOAuthUser) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
if !util.IsOpenShift4 {
return reconcile.Result{}, true, nil
}
if ctx.CheCluster.IsOpenShiftOAuthUserConfigured() &&
(ctx.CheCluster.Spec.Auth.OpenShiftoAuth == nil || *ctx.CheCluster.Spec.Auth.OpenShiftoAuth) {
done, err := oou.Create(ctx)
if !done {
return reconcile.Result{Requeue: true}, false, err
}
return reconcile.Result{}, true, nil
}
if ctx.CheCluster.IsOpenShiftOAuthUserMustBeDeleted() {
if done := oou.Delete(ctx); !done {
return reconcile.Result{}, false, fmt.Errorf("unable to delete initial OpenShift OAuth user from a cluster")
}
return reconcile.Result{}, true, nil
}
return reconcile.Result{}, true, nil
}
func (oou *OpenShiftOAuthUser) Finalize(ctx *deploy.DeployContext) bool {
if util.IsOpenShift4 {
return oou.Delete(ctx)
}
return true
}
// Creates new htpasswd provider with initial user with Che flavor name
// if Openshift cluster hasn't got identity providers then does nothing.
// It usefull for good first user experience.
// User can't use kube:admin or system:admin user in the Openshift oAuth when DevWorkspace engine disabled.
// That's why we provide initial user for good first meeting with Eclipse Che.
func (oou *OpenShiftOAuthUser) Create(ctx *deploy.DeployContext) (bool, error) {
userName := deploy.DefaultCheFlavor(ctx.CheCluster)
// read existed password from the secret (operator has been restarted case)
oAuthUserCredentialsSecret, err := oou.getOpenShiftOAuthUserCredentialsSecret(ctx)
if err != nil {
return false, err
} else if oAuthUserCredentialsSecret != nil {
oou.userPassword = string(oAuthUserCredentialsSecret.Data["password"])
}
// create a new secret with user's credentials
if oAuthUserCredentialsSecret == nil {
initialUserSecretData := map[string][]byte{"user": []byte(userName), "password": []byte(oou.userPassword)}
done, err := deploy.SyncSecretToCluster(ctx, OpenShiftOAuthUserCredentialsSecret, OcConfigNamespace, initialUserSecretData)
if !done {
return false, err
}
}
htpasswdSecretExists, _ := deploy.Get(ctx, types.NamespacedName{Name: HtpasswdSecretName, Namespace: OcConfigNamespace}, &corev1.Secret{})
if !htpasswdSecretExists {
htpasswdFileContent, err := oou.generateHtPasswdUserInfo(userName, oou.userPassword)
if err != nil {
return false, err
}
htpasswdFileSecretData := map[string][]byte{"htpasswd": []byte(htpasswdFileContent)}
done, err := deploy.SyncSecretToCluster(ctx, HtpasswdSecretName, OcConfigNamespace, htpasswdFileSecretData)
if !done {
return false, err
}
}
oAuth, err := GetOpenshiftOAuth(ctx)
if err != nil {
return false, err
}
if err := appendIdentityProvider(oAuth, ctx.ClusterAPI.NonCachingClient); err != nil {
return false, err
}
if ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret != OpenShiftOAuthUserCredentialsSecret {
ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret = OpenShiftOAuthUserCredentialsSecret
if err := deploy.UpdateCheCRStatus(ctx, "openShiftOAuthUserCredentialsSecret", OpenShiftOAuthUserCredentialsSecret); err != nil {
return false, err
}
}
if err := deploy.AppendFinalizer(ctx, OpenshiftOauthUserFinalizerName); err != nil {
return false, err
}
return true, nil
}
// Removes initial user, htpasswd provider, htpasswd secret and Che secret with username and password.
func (oou *OpenShiftOAuthUser) Delete(ctx *deploy.DeployContext) bool {
done := true
oAuth, err := GetOpenshiftOAuth(ctx)
if err != nil {
done = false
logrus.Errorf("Failed to get Openshift OAuth, cause: %v", err)
}
userName := deploy.DefaultCheFlavor(ctx.CheCluster)
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: userName}, &userv1.User{}); err != nil {
done = false
logrus.Errorf("Failed to delete Openshift user '%s', cause: %v", userName, err)
}
identityName := HtpasswdIdentityProviderName + ":" + userName
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: identityName}, &userv1.Identity{}); err != nil {
done = false
logrus.Errorf("Failed to delete identity '%s', cause: %v", identityName, err)
}
if err := deleteIdentityProvider(oAuth, ctx.ClusterAPI.NonCachingClient); err != nil {
done = false
logrus.Errorf("Failed to delete identity provider', cause: %v", err)
}
if _, err = deploy.Delete(ctx, types.NamespacedName{Name: HtpasswdSecretName, Namespace: OcConfigNamespace}, &corev1.Secret{}); err != nil {
done = false
logrus.Errorf("Failed to delete HTpasswd secret '%s', cause: %v", HtpasswdSecretName, err)
}
// legacy secret in the current namespace
if _, err = deploy.DeleteNamespacedObject(ctx, OpenShiftOAuthUserCredentialsSecret, &corev1.Secret{}); err != nil {
done = false
logrus.Errorf("Failed to delete legacy Openshift OAuth credentials secret '%s', cause: %v", OpenShiftOAuthUserCredentialsSecret, err)
}
if _, err = deploy.Delete(ctx, types.NamespacedName{Name: OpenShiftOAuthUserCredentialsSecret, Namespace: OcConfigNamespace}, &corev1.Secret{}); err != nil {
done = false
logrus.Errorf("Failed to delete Openshift OAuth credentials secret '%s', cause: %v", OpenShiftOAuthUserCredentialsSecret, err)
}
if err := deploy.DeleteFinalizer(ctx, OpenshiftOauthUserFinalizerName); err != nil {
done = false
logrus.Errorf("Error deleting finalizer: %v", err)
}
ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret = ""
if err := deploy.UpdateCheCRStatus(ctx, "openShiftOAuthUserCredentialsSecret", ""); err != nil {
done = false
logrus.Errorf("Filed to update openShiftOAuthUserCredentialsSecret in CR status, cause: %v", err)
}
// set 'openShiftoAuth:nil` to reenable on the following reconcile loop (if possible)
ctx.CheCluster.Spec.Auth.InitialOpenShiftOAuthUser = nil
ctx.CheCluster.Spec.Auth.OpenShiftoAuth = nil
updateFields := map[string]string{
"openShiftoAuth": "nil",
"initialOpenShiftOAuthUser": "nil",
}
if err := deploy.UpdateCheCRSpecByFields(ctx, updateFields); err != nil {
done = false
logrus.Errorf("Filed to update OAuth field in CR, cause: %v", err)
}
return done
}
func (oou *OpenShiftOAuthUser) generateHtPasswdUserInfo(userName string, password string) (string, error) {
if util.IsTestMode() {
return "", nil
}
err := oou.runnable.Run("htpasswd", "-nbB", userName, password)
if err != nil {
return "", err
}
if len(oou.runnable.GetStdErr()) > 0 {
return "", fmt.Errorf("Failed to generate data for HTPasswd identity provider: %s", oou.runnable.GetStdErr())
}
return oou.runnable.GetStdOut(), nil
}
// Gets OpenShift user credentials secret from from the secret from:
// - openshift-config namespace
// - eclipse-che namespace
func (oou *OpenShiftOAuthUser) getOpenShiftOAuthUserCredentialsSecret(ctx *deploy.DeployContext) (*corev1.Secret, error) {
secret := &corev1.Secret{}
exists, err := deploy.Get(ctx, types.NamespacedName{Name: OpenShiftOAuthUserCredentialsSecret, Namespace: OcConfigNamespace}, secret)
if err != nil {
return nil, err
} else if exists {
return secret, nil
}
exists, err = deploy.GetNamespacedObject(ctx, OpenShiftOAuthUserCredentialsSecret, secret)
if err != nil {
return nil, err
} else if exists {
return secret, nil
}
return nil, nil
}
func identityProviderExists(providerName string, oAuth *configv1.OAuth) bool {
if len(oAuth.Spec.IdentityProviders) == 0 {
return false
}
for _, identityProvider := range oAuth.Spec.IdentityProviders {
if identityProvider.Name == providerName {
return true
}
}
return false
}
func appendIdentityProvider(oAuth *configv1.OAuth, runtimeClient client.Client) error {
htpasswdProvider := newHtpasswdProvider()
if !identityProviderExists(htpasswdProvider.Name, oAuth) {
oauthPatch := client.MergeFrom(oAuth.DeepCopy())
oAuth.Spec.IdentityProviders = append(oAuth.Spec.IdentityProviders, *htpasswdProvider)
if err := runtimeClient.Patch(context.TODO(), oAuth, oauthPatch); err != nil {
return err
}
}
return nil
}
func newHtpasswdProvider() *configv1.IdentityProvider {
return &configv1.IdentityProvider{
Name: HtpasswdIdentityProviderName,
MappingMethod: configv1.MappingMethodClaim,
IdentityProviderConfig: configv1.IdentityProviderConfig{
Type: "HTPasswd",
HTPasswd: &configv1.HTPasswdIdentityProvider{
FileData: configv1.SecretNameReference{Name: HtpasswdSecretName},
},
},
}
}
func deleteIdentityProvider(oAuth *configv1.OAuth, runtimeClient client.Client) error {
logrus.Info("Delete initial user httpasswd provider from the oAuth")
oauthPatch := client.MergeFrom(oAuth.DeepCopy())
ips := oAuth.Spec.IdentityProviders
for i, ip := range ips {
if ip.Name == HtpasswdIdentityProviderName {
// remove provider from slice
oAuth.Spec.IdentityProviders = append(ips[:i], ips[i+1:]...)
break
}
}
if err := runtimeClient.Patch(context.TODO(), oAuth, oauthPatch); err != nil {
return err
}
return nil
}

View File

@ -1,168 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import (
"context"
"os"
"testing"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util"
oauth_config "github.com/openshift/api/config/v1"
userv1 "github.com/openshift/api/user/v1"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
const (
testNamespace = "test-namespace"
testUserName = "test"
)
func TestCreateInitialUser(t *testing.T) {
checluster := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "eclipse-che",
Namespace: testNamespace,
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CheFlavor: testUserName,
},
},
}
oAuth := &oauth_config.OAuth{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
},
Spec: oauth_config.OAuthSpec{IdentityProviders: []oauth_config.IdentityProvider{}},
}
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{oAuth})
openShiftOAuthUser := NewOpenShiftOAuthUser()
done, err := openShiftOAuthUser.Create(ctx)
assert.Nil(t, err)
assert.True(t, done)
// Check created objects
expectedCheSecret := &corev1.Secret{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: OpenShiftOAuthUserCredentialsSecret, Namespace: OcConfigNamespace}, expectedCheSecret)
assert.Nil(t, err)
expectedHtpasswsSecret := &corev1.Secret{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: HtpasswdSecretName, Namespace: OcConfigNamespace}, expectedHtpasswsSecret)
assert.Nil(t, err)
expectedOAuth := &oauth_config.OAuth{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, expectedOAuth)
assert.Nil(t, err)
assert.Equal(t, len(expectedOAuth.Spec.IdentityProviders), 1)
assert.True(t, util.ContainsString(checluster.Finalizers, OpenshiftOauthUserFinalizerName))
assert.Equal(t, checluster.Status.OpenShiftOAuthUserCredentialsSecret, OpenShiftOAuthUserCredentialsSecret)
}
func TestDeleteInitialUser(t *testing.T) {
checluster := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "eclipse-che",
Namespace: testNamespace,
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
CheFlavor: testUserName,
},
},
Status: orgv1.CheClusterStatus{
OpenShiftOAuthUserCredentialsSecret: "some-secret",
},
}
oAuth := &oauth_config.OAuth{
ObjectMeta: v1.ObjectMeta{
Name: "cluster",
},
Spec: oauth_config.OAuthSpec{IdentityProviders: []oauth_config.IdentityProvider{*newHtpasswdProvider()}},
}
cheSecret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: OpenShiftOAuthUserCredentialsSecret,
Namespace: OcConfigNamespace,
},
}
htpasswdSecret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: HtpasswdSecretName,
Namespace: OcConfigNamespace,
},
}
userIdentity := &userv1.Identity{
ObjectMeta: metav1.ObjectMeta{
Name: HtpasswdIdentityProviderName + ":" + testUserName,
},
}
user := &userv1.User{
ObjectMeta: metav1.ObjectMeta{
Name: testUserName,
},
}
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{oAuth, cheSecret, htpasswdSecret, userIdentity, user})
openShiftOAuthUser := &OpenShiftOAuthUser{}
done := openShiftOAuthUser.Delete(ctx)
assert.True(t, done)
expectedCheSecret := &corev1.Secret{}
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: OpenShiftOAuthUserCredentialsSecret, Namespace: OcConfigNamespace}, expectedCheSecret)
assert.True(t, errors.IsNotFound(err))
expectedHtpasswsSecret := &corev1.Secret{}
if err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: HtpasswdSecretName, Namespace: OcConfigNamespace}, expectedHtpasswsSecret); !errors.IsNotFound(err) {
t.Errorf("Initial user secret should be deleted")
}
assert.True(t, errors.IsNotFound(err))
expectedUserIdentity := &userv1.Identity{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: HtpasswdIdentityProviderName + ":" + testUserName}, expectedUserIdentity)
assert.True(t, errors.IsNotFound(err))
expectedUser := &userv1.User{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: testUserName}, expectedUser)
assert.True(t, errors.IsNotFound(err))
expectedOAuth := &oauth_config.OAuth{}
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, expectedOAuth)
assert.Nil(t, err)
assert.Equal(t, len(expectedOAuth.Spec.IdentityProviders), 0)
assert.False(t, util.ContainsString(checluster.Finalizers, OpenshiftOauthUserFinalizerName))
assert.Empty(t, checluster.Status.OpenShiftOAuthUserCredentialsSecret)
}

View File

@ -1,27 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package openshiftoauth
import (
"github.com/eclipse-che/che-operator/pkg/deploy"
oauthv1 "github.com/openshift/api/config/v1"
)
// Gets OpenShift OAuth.
func GetOpenshiftOAuth(ctx *deploy.DeployContext) (*oauthv1.OAuth, error) {
oAuth := &oauthv1.OAuth{}
if done, err := deploy.GetClusterObject(ctx, "cluster", oAuth); !done {
return nil, err
}
return oAuth, nil
}

View File

@ -95,18 +95,11 @@ func (p *PluginRegistryReconciler) ExposeEndpoint(ctx *deploy.DeployContext) (st
return expose.Expose( return expose.Expose(
ctx, ctx,
deploy.PluginRegistryName, deploy.PluginRegistryName,
ctx.CheCluster.Spec.Server.PluginRegistryRoute,
ctx.CheCluster.Spec.Server.PluginRegistryIngress,
p.createGatewayConfig(ctx)) p.createGatewayConfig(ctx))
} }
func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) { func (p *PluginRegistryReconciler) updateStatus(endpoint string, ctx *deploy.DeployContext) (bool, error) {
var pluginRegistryURL string pluginRegistryURL := "https://" + endpoint
if ctx.CheCluster.Spec.Server.TlsSupport {
pluginRegistryURL = "https://" + endpoint
} else {
pluginRegistryURL = "http://" + endpoint
}
// append the API version to plugin registry // append the API version to plugin registry
if !strings.HasSuffix(pluginRegistryURL, "/") { if !strings.HasSuffix(pluginRegistryURL, "/") {

View File

@ -16,7 +16,6 @@ import (
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
routev1 "github.com/openshift/api/route/v1"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -35,7 +34,6 @@ func TestPluginRegistryReconcile(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.Service{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.Service{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &routev1.Route{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &corev1.ConfigMap{}))
assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{})) assert.True(t, util.IsObjectExists(ctx.ClusterAPI.Client, types.NamespacedName{Name: "plugin-registry", Namespace: "eclipse-che"}, &appsv1.Deployment{}))
assert.NotEmpty(t, ctx.CheCluster.Status.PluginRegistryURL) assert.NotEmpty(t, ctx.CheCluster.Status.PluginRegistryURL)

View File

@ -12,7 +12,6 @@
package postgres package postgres
import ( import (
"fmt"
"strings" "strings"
orgv1 "github.com/eclipse-che/che-operator/api/v1" orgv1 "github.com/eclipse-che/che-operator/api/v1"
@ -20,7 +19,6 @@ import (
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -52,15 +50,6 @@ func (p *PostgresReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Res
return reconcile.Result{}, false, err return reconcile.Result{}, false, err
} }
if !ctx.CheCluster.Status.DbProvisoned {
if !util.IsTestMode() { // ignore in tests
done, err = p.provisionDB(ctx)
if !done {
return reconcile.Result{}, false, err
}
}
}
if ctx.CheCluster.Spec.Database.PostgresVersion == "" { if ctx.CheCluster.Spec.Database.PostgresVersion == "" {
if !util.IsTestMode() { // ignore in tests if !util.IsTestMode() { // ignore in tests
done, err := p.setDbVersion(ctx) done, err := p.setDbVersion(ctx)
@ -111,40 +100,6 @@ func (p *PostgresReconciler) syncDeployment(ctx *deploy.DeployContext) (bool, er
return deploy.SyncDeploymentSpecToCluster(ctx, specDeployment, deploy.DefaultDeploymentDiffOpts) return deploy.SyncDeploymentSpecToCluster(ctx, specDeployment, deploy.DefaultDeploymentDiffOpts)
} }
func (p *PostgresReconciler) provisionDB(ctx *deploy.DeployContext) (bool, error) {
identityProviderPostgresPassword := ctx.CheCluster.Spec.Auth.IdentityProviderPostgresPassword
identityProviderPostgresSecret := ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret
if identityProviderPostgresSecret != "" {
secret := &corev1.Secret{}
exists, err := deploy.GetNamespacedObject(ctx, identityProviderPostgresSecret, secret)
if err != nil {
return false, err
} else if !exists {
return false, fmt.Errorf("Secret '%s' not found", identityProviderPostgresSecret)
}
identityProviderPostgresPassword = string(secret.Data["password"])
}
_, err := util.K8sclient.ExecIntoPod(
ctx.CheCluster,
deploy.PostgresName,
func(cr *orgv1.CheCluster) (string, error) {
return getPostgresProvisionCommand(identityProviderPostgresPassword), nil
},
"create Keycloak DB, user, privileges")
if err != nil {
return false, err
}
ctx.CheCluster.Status.DbProvisoned = true
err = deploy.UpdateCheCRStatus(ctx, "status: provisioned with DB and user", "true")
if err != nil {
return false, err
}
return true, nil
}
func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, error) { func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, error) {
postgresVersion, err := util.K8sclient.ExecIntoPod( postgresVersion, err := util.K8sclient.ExecIntoPod(
ctx.CheCluster, ctx.CheCluster,
@ -167,14 +122,3 @@ func (p *PostgresReconciler) setDbVersion(ctx *deploy.DeployContext) (bool, erro
return true, nil return true, nil
} }
func getPostgresProvisionCommand(identityProviderPostgresPassword string) (command string) {
command = "OUT=$(psql postgres -tAc \"SELECT 1 FROM pg_roles WHERE rolname='keycloak'\"); " +
"if [ $OUT -eq 1 ]; then echo \"DB exists\"; exit 0; fi " +
"&& psql -c \"CREATE USER keycloak WITH PASSWORD '" + identityProviderPostgresPassword + "'\" " +
"&& psql -c \"CREATE DATABASE keycloak\" " +
"&& psql -c \"GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak\" " +
"&& psql -c \"ALTER USER ${POSTGRESQL_USER} WITH SUPERUSER\""
return command
}

View File

@ -35,23 +35,17 @@ func NewGatewayPermissionsReconciler() *GatewayPermissionsReconciler {
} }
func (gp *GatewayPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { func (gp *GatewayPermissionsReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
if ctx.CheCluster.IsNativeUserModeEnabled() { name := gp.gatewayPermissionsName(ctx.CheCluster)
name := gp.gatewayPermissionsName(ctx.CheCluster) if done, err := deploy.SyncClusterRoleToCluster(ctx, name, gp.getGatewayClusterRoleRules()); !done {
if done, err := deploy.SyncClusterRoleToCluster(ctx, name, gp.getGatewayClusterRoleRules()); !done { return reconcile.Result{Requeue: true}, false, err
return reconcile.Result{Requeue: true}, false, err }
}
if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, name, gateway.GatewayServiceName, name); !done { if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, name, gateway.GatewayServiceName, name); !done {
return reconcile.Result{Requeue: true}, false, err return reconcile.Result{Requeue: true}, false, err
} }
if err := deploy.AppendFinalizer(ctx, CheGatewayClusterPermissionsFinalizerName); err != nil { if err := deploy.AppendFinalizer(ctx, CheGatewayClusterPermissionsFinalizerName); err != nil {
return reconcile.Result{Requeue: true}, false, err return reconcile.Result{Requeue: true}, false, err
}
} else {
if done, err := gp.deleteGatewayPermissions(ctx); !done {
return reconcile.Result{Requeue: true}, false, err
}
} }
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil

View File

@ -22,7 +22,6 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
) )
func TestReconcileWorkspacePermissions(t *testing.T) { func TestReconcileWorkspacePermissions(t *testing.T) {
@ -47,9 +46,6 @@ func TestReconcileWorkspacePermissions(t *testing.T) {
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
WorkspaceNamespaceDefault: "some-test-namespace", WorkspaceNamespaceDefault: "some-test-namespace",
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: pointer.BoolPtr(false),
},
}, },
}, },
}, },
@ -67,9 +63,6 @@ func TestReconcileWorkspacePermissions(t *testing.T) {
"CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "some-test-namespace", "CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT": "some-test-namespace",
}, },
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: pointer.BoolPtr(false),
},
}, },
}, },
}, },

View File

@ -83,7 +83,6 @@ func GetRouteSpec(
component string) (*routev1.Route, error) { component string) (*routev1.Route, error) {
cheFlavor := DefaultCheFlavor(deployContext.CheCluster) cheFlavor := DefaultCheFlavor(deployContext.CheCluster)
tlsSupport := deployContext.CheCluster.Spec.Server.TlsSupport
labels := GetLabels(deployContext.CheCluster, component) labels := GetLabels(deployContext.CheCluster, component)
MergeLabels(labels, routeCustomSettings.Labels) MergeLabels(labels, routeCustomSettings.Labels)
@ -153,26 +152,24 @@ func GetRouteSpec(
route.Spec.Host = fmt.Sprintf(HostNameTemplate, route.ObjectMeta.Name, route.ObjectMeta.Namespace, routeCustomSettings.Domain) route.Spec.Host = fmt.Sprintf(HostNameTemplate, route.ObjectMeta.Name, route.ObjectMeta.Namespace, routeCustomSettings.Domain)
} }
if tlsSupport { route.Spec.TLS = &routev1.TLSConfig{
route.Spec.TLS = &routev1.TLSConfig{ InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, Termination: routev1.TLSTerminationEdge,
Termination: routev1.TLSTerminationEdge, }
// for server and dashboard ingresses
if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" {
secret := &corev1.Secret{}
namespacedName := types.NamespacedName{
Namespace: deployContext.CheCluster.Namespace,
Name: deployContext.CheCluster.Spec.Server.CheHostTLSSecret,
}
if err := deployContext.ClusterAPI.Client.Get(context.TODO(), namespacedName, secret); err != nil {
return nil, err
} }
// for server and dashboard ingresses route.Spec.TLS.Key = string(secret.Data["tls.key"])
if (component == cheFlavor || component == cheFlavor+"-dashboard") && deployContext.CheCluster.Spec.Server.CheHostTLSSecret != "" { route.Spec.TLS.Certificate = string(secret.Data["tls.crt"])
secret := &corev1.Secret{}
namespacedName := types.NamespacedName{
Namespace: deployContext.CheCluster.Namespace,
Name: deployContext.CheCluster.Spec.Server.CheHostTLSSecret,
}
if err := deployContext.ClusterAPI.Client.Get(context.TODO(), namespacedName, secret); err != nil {
return nil, err
}
route.Spec.TLS.Key = string(secret.Data["tls.key"])
route.Spec.TLS.Certificate = string(secret.Data["tls.crt"])
}
} }
return route, nil return route, nil

View File

@ -84,6 +84,7 @@ func TestRouteSpec(t *testing.T) {
Name: "che", Name: "che",
Weight: &weight, Weight: &weight,
}, },
TLS: &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect},
Port: &routev1.RoutePort{ Port: &routev1.RoutePort{
TargetPort: intstr.IntOrString{ TargetPort: intstr.IntOrString{
Type: intstr.Int, Type: intstr.Int,
@ -127,6 +128,7 @@ func TestRouteSpec(t *testing.T) {
Name: "che", Name: "che",
Weight: &weight, Weight: &weight,
}, },
TLS: &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect},
Port: &routev1.RoutePort{ Port: &routev1.RoutePort{
TargetPort: intstr.IntOrString{ TargetPort: intstr.IntOrString{
Type: intstr.Int, Type: intstr.Int,

View File

@ -13,6 +13,7 @@ package server
import ( import (
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
routev1 "github.com/openshift/api/route/v1" routev1 "github.com/openshift/api/route/v1"
networking "k8s.io/api/networking/v1" networking "k8s.io/api/networking/v1"
@ -60,7 +61,7 @@ func (s *CheHostReconciler) getDefaultCheHost(ctx *deploy.DeployContext) (bool,
getComponentName(ctx), getComponentName(ctx),
"", "",
"/", "/",
getServerExposingServiceName(ctx.CheCluster), gateway.GatewayServiceName,
8080, 8080,
ctx.CheCluster.Spec.Server.CheServerRoute, ctx.CheCluster.Spec.Server.CheServerRoute,
getComponentName(ctx)) getComponentName(ctx))
@ -97,7 +98,6 @@ func (s *CheHostReconciler) syncCheService(ctx *deploy.DeployContext) (bool, err
func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, error) { func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, error) {
cheHost := "" cheHost := ""
exposedServiceName := getServerExposingServiceName(ctx.CheCluster)
if !util.IsOpenShift { if !util.IsOpenShift {
_, done, err := deploy.SyncIngressToCluster( _, done, err := deploy.SyncIngressToCluster(
@ -105,7 +105,7 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, e
getComponentName(ctx), getComponentName(ctx),
ctx.CheCluster.Spec.Server.CheHost, ctx.CheCluster.Spec.Server.CheHost,
"", "",
exposedServiceName, gateway.GatewayServiceName,
8080, 8080,
ctx.CheCluster.Spec.Server.CheServerIngress, ctx.CheCluster.Spec.Server.CheServerIngress,
getComponentName(ctx)) getComponentName(ctx))
@ -132,7 +132,7 @@ func (s CheHostReconciler) exposeCheEndpoint(ctx *deploy.DeployContext) (bool, e
getComponentName(ctx), getComponentName(ctx),
customHost, customHost,
"/", "/",
exposedServiceName, gateway.GatewayServiceName,
8080, 8080,
ctx.CheCluster.Spec.Server.CheServerRoute, ctx.CheCluster.Spec.Server.CheServerRoute,
getComponentName(ctx)) getComponentName(ctx))

View File

@ -13,12 +13,8 @@
package server package server
import ( import (
"strconv"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -31,14 +27,7 @@ func NewDefaultValuesReconciler() *DefaultValuesReconciler {
} }
func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
cheNamespace := ctx.CheCluster.Namespace cheNamespace := ctx.CheCluster.Namespace
if len(ctx.CheCluster.Spec.Server.CheFlavor) < 1 {
ctx.CheCluster.Spec.Server.CheFlavor = cheFlavor
if err := deploy.UpdateCheCRSpec(ctx, "installation flavor", cheFlavor); err != nil {
return reconcile.Result{}, false, err
}
}
if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 { if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
if len(ctx.CheCluster.Spec.Database.ChePostgresUser) < 1 || len(ctx.CheCluster.Spec.Database.ChePostgresPassword) < 1 { if len(ctx.CheCluster.Spec.Database.ChePostgresUser) < 1 || len(ctx.CheCluster.Spec.Database.ChePostgresPassword) < 1 {
@ -66,66 +55,6 @@ func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcil
} }
} }
} }
if len(ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret) < 1 {
keycloakPostgresPassword := util.GeneratePasswd(12)
keycloakDeployment := &appsv1.Deployment{}
exists, err := deploy.GetNamespacedObject(ctx, deploy.IdentityProviderName, keycloakDeployment)
if err != nil {
logrus.Error(err)
}
if exists {
keycloakPostgresPassword = util.GetDeploymentEnv(keycloakDeployment, "DB_PASSWORD")
}
if len(ctx.CheCluster.Spec.Auth.IdentityProviderPostgresPassword) < 1 {
identityPostgresSecret := deploy.DefaultCheIdentityPostgresSecret()
_, err := deploy.SyncSecretToCluster(ctx, identityPostgresSecret, cheNamespace, map[string][]byte{"password": []byte(keycloakPostgresPassword)})
if err != nil {
return reconcile.Result{}, false, err
}
ctx.CheCluster.Spec.Auth.IdentityProviderPostgresSecret = identityPostgresSecret
if err := deploy.UpdateCheCRSpec(ctx, "Identity Provider Postgres Secret", identityPostgresSecret); err != nil {
return reconcile.Result{}, false, err
}
}
}
if len(ctx.CheCluster.Spec.Auth.IdentityProviderSecret) < 1 {
keycloakAdminUserName := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName, "admin")
keycloakAdminPassword := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderPassword, util.GeneratePasswd(12))
keycloakDeployment := &appsv1.Deployment{}
exists, _ := deploy.GetNamespacedObject(ctx, deploy.IdentityProviderName, keycloakDeployment)
if exists {
keycloakAdminUserName = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_USERNAME")
keycloakAdminPassword = util.GetDeploymentEnv(keycloakDeployment, "SSO_ADMIN_PASSWORD")
}
if len(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 || len(ctx.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
identityProviderSecret := deploy.DefaultCheIdentitySecret()
_, err := deploy.SyncSecretToCluster(ctx, identityProviderSecret, cheNamespace, map[string][]byte{"user": []byte(keycloakAdminUserName), "password": []byte(keycloakAdminPassword)})
if err != nil {
return reconcile.Result{}, false, err
}
ctx.CheCluster.Spec.Auth.IdentityProviderSecret = identityProviderSecret
if err := deploy.UpdateCheCRSpec(ctx, "Identity Provider Secret", identityProviderSecret); err != nil {
return reconcile.Result{}, false, err
}
} else {
if len(ctx.CheCluster.Spec.Auth.IdentityProviderPassword) < 1 {
ctx.CheCluster.Spec.Auth.IdentityProviderPassword = keycloakAdminPassword
if err := deploy.UpdateCheCRSpec(ctx, "Keycloak admin password", "password hidden"); err != nil {
return reconcile.Result{}, false, err
}
}
if len(ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName) < 1 {
ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName = keycloakAdminUserName
if err := deploy.UpdateCheCRSpec(ctx, "Keycloak admin username", keycloakAdminUserName); err != nil {
return reconcile.Result{}, false, err
}
}
}
}
chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, "dbche") chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, "dbche")
if len(ctx.CheCluster.Spec.Database.ChePostgresDb) < 1 { if len(ctx.CheCluster.Spec.Database.ChePostgresDb) < 1 {
@ -149,24 +78,6 @@ func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcil
} }
} }
if !ctx.CheCluster.IsNativeUserModeEnabled() {
keycloakRealm := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
if len(ctx.CheCluster.Spec.Auth.IdentityProviderRealm) < 1 {
ctx.CheCluster.Spec.Auth.IdentityProviderRealm = keycloakRealm
if err := deploy.UpdateCheCRSpec(ctx, "Keycloak realm", keycloakRealm); err != nil {
return reconcile.Result{}, false, err
}
}
keycloakClientId := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
if len(ctx.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 {
ctx.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId
if err := deploy.UpdateCheCRSpec(ctx, "Keycloak client ID", keycloakClientId); err != nil {
return reconcile.Result{}, false, err
}
}
}
cheLogLevel := util.GetValue(ctx.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel) cheLogLevel := util.GetValue(ctx.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
if len(ctx.CheCluster.Spec.Server.CheLogLevel) < 1 { if len(ctx.CheCluster.Spec.Server.CheLogLevel) < 1 {
ctx.CheCluster.Spec.Server.CheLogLevel = cheLogLevel ctx.CheCluster.Spec.Server.CheLogLevel = cheLogLevel
@ -196,76 +107,6 @@ func (p *DefaultValuesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcil
} }
} }
// This is only to correctly manage defaults during the transition
// from Upstream 7.0.0 GA to the next
// version that should fixed bug https://github.com/eclipse/che/issues/13714
// Or for the transition from CRW 1.2 to 2.0
if ctx.CheCluster.Spec.Storage.PvcJobsImage == deploy.OldDefaultPvcJobsUpstreamImageToDetect ||
(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Storage.PvcJobsImage != "") {
ctx.CheCluster.Spec.Storage.PvcJobsImage = ""
if err := deploy.UpdateCheCRSpec(ctx, "pvc jobs image", ctx.CheCluster.Spec.Storage.PvcJobsImage); err != nil {
return reconcile.Result{}, false, err
}
}
if ctx.CheCluster.Spec.Database.PostgresImage == deploy.OldDefaultPostgresUpstreamImageToDetect ||
(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Database.PostgresImage != "") {
ctx.CheCluster.Spec.Database.PostgresImage = ""
if err := deploy.UpdateCheCRSpec(ctx, "postgres image", ctx.CheCluster.Spec.Database.PostgresImage); err != nil {
return reconcile.Result{}, false, err
}
}
if ctx.CheCluster.Spec.Auth.IdentityProviderImage == deploy.OldDefaultKeycloakUpstreamImageToDetect ||
(deploy.MigratingToCRW2_0(ctx.CheCluster) && ctx.CheCluster.Spec.Auth.IdentityProviderImage != "") {
ctx.CheCluster.Spec.Auth.IdentityProviderImage = ""
if err := deploy.UpdateCheCRSpec(ctx, "keycloak image", ctx.CheCluster.Spec.Auth.IdentityProviderImage); err != nil {
return reconcile.Result{}, false, err
}
}
if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
!ctx.CheCluster.Spec.Server.ExternalPluginRegistry &&
ctx.CheCluster.Spec.Server.PluginRegistryUrl == deploy.OldCrwPluginRegistryUrl {
ctx.CheCluster.Spec.Server.PluginRegistryUrl = ""
if err := deploy.UpdateCheCRSpec(ctx, "plugin registry url", ctx.CheCluster.Spec.Server.PluginRegistryUrl); err != nil {
return reconcile.Result{}, false, err
}
}
if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
ctx.CheCluster.Spec.Server.CheImage == deploy.OldDefaultCodeReadyServerImageRepo {
ctx.CheCluster.Spec.Server.CheImage = ""
if err := deploy.UpdateCheCRSpec(ctx, "che image repo", ctx.CheCluster.Spec.Server.CheImage); err != nil {
return reconcile.Result{}, false, err
}
}
if deploy.MigratingToCRW2_0(ctx.CheCluster) &&
ctx.CheCluster.Spec.Server.CheImageTag == deploy.OldDefaultCodeReadyServerImageTag {
ctx.CheCluster.Spec.Server.CheImageTag = ""
if err := deploy.UpdateCheCRSpec(ctx, "che image tag", ctx.CheCluster.Spec.Server.CheImageTag); err != nil {
return reconcile.Result{}, false, err
}
}
if ctx.CheCluster.Spec.Server.ServerExposureStrategy == "" && ctx.CheCluster.Spec.K8s.IngressStrategy == "" {
strategy := util.GetServerExposureStrategy(ctx.CheCluster)
ctx.CheCluster.Spec.Server.ServerExposureStrategy = strategy
if err := deploy.UpdateCheCRSpec(ctx, "serverExposureStrategy", strategy); err != nil {
return reconcile.Result{}, false, err
}
}
if ctx.CheCluster.Spec.DevWorkspace.Enable && ctx.CheCluster.Spec.Auth.NativeUserMode == nil {
newNativeUserModeValue := util.NewBoolPointer(true)
ctx.CheCluster.Spec.Auth.NativeUserMode = newNativeUserModeValue
if err := deploy.UpdateCheCRSpec(ctx, "nativeUserMode", strconv.FormatBool(*newNativeUserModeValue)); err != nil {
return reconcile.Result{}, false, err
}
}
return reconcile.Result{}, true, nil return reconcile.Result{}, true, nil
} }

View File

@ -1,162 +0,0 @@
//
// Copyright (c) 2019-2021 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package server
import (
"os"
"github.com/eclipse-che/che-operator/pkg/deploy"
devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
"github.com/eclipse-che/che-operator/pkg/util"
"github.com/stretchr/testify/assert"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
"testing"
)
func TestEnsureServerExposureStrategy(t *testing.T) {
type testCase struct {
name string
expectedCr *orgv1.CheCluster
devWorkspaceEnabled bool
initObjects []runtime.Object
}
testCases := []testCase{
{
name: "Single Host should be enabled if devWorkspace is enabled",
expectedCr: &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
},
},
devWorkspaceEnabled: true,
},
{
name: "Multi Host should be enabled if devWorkspace is not enabled",
expectedCr: &orgv1.CheCluster{
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
},
},
devWorkspaceEnabled: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
checluster := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "eclipse-che",
Namespace: "eclipse-che",
},
}
checluster.Spec.DevWorkspace.Enable = testCase.devWorkspaceEnabled
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{})
defaults := NewDefaultValuesReconciler()
_, done, err := defaults.Reconcile(ctx)
assert.True(t, done)
assert.Nil(t, err)
assert.Equal(t, testCase.expectedCr.Spec.Server.ServerExposureStrategy, ctx.CheCluster.Spec.Server.ServerExposureStrategy)
})
}
}
func TestNativeUserModeEnabled(t *testing.T) {
type testCase struct {
name string
initObjects []runtime.Object
isOpenshift bool
devworkspaceEnabled bool
initialNativeUserValue *bool
expectedNativeUserValue *bool
}
testCases := []testCase{
{
name: "che-operator should use nativeUserMode when devworkspaces on openshift and no initial value in CR for nativeUserMode",
isOpenshift: true,
devworkspaceEnabled: true,
initialNativeUserValue: nil,
expectedNativeUserValue: pointer.BoolPtr(true),
},
{
name: "che-operator should use nativeUserMode value from initial CR",
isOpenshift: true,
devworkspaceEnabled: true,
initialNativeUserValue: pointer.BoolPtr(false),
expectedNativeUserValue: pointer.BoolPtr(false),
},
{
name: "che-operator should use nativeUserMode value from initial CR",
isOpenshift: true,
devworkspaceEnabled: true,
initialNativeUserValue: pointer.BoolPtr(true),
expectedNativeUserValue: pointer.BoolPtr(true),
},
{
name: "che-operator should use nativeUserMode when devworkspaces on kubernetes and no initial value in CR for nativeUserMode",
isOpenshift: false,
devworkspaceEnabled: true,
initialNativeUserValue: nil,
expectedNativeUserValue: pointer.BoolPtr(true),
},
{
name: "che-operator not modify nativeUserMode when devworkspace not enabled",
isOpenshift: true,
devworkspaceEnabled: false,
initialNativeUserValue: nil,
expectedNativeUserValue: nil,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
checluster := &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "eclipse-che",
Namespace: "eclipse-che",
},
}
// reread templates (workaround after setting IsOpenShift value)
util.IsOpenShift = testCase.isOpenshift
devworkspace.DevWorkspaceTemplates = devworkspace.DevWorkspaceTemplatesPath()
devworkspace.DevWorkspaceIssuerFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-selfsigned-issuer.Issuer.yaml"
devworkspace.DevWorkspaceCertificateFile = devworkspace.DevWorkspaceTemplates + "/devworkspace-controller-serving-cert.Certificate.yaml"
checluster.Spec.DevWorkspace.Enable = testCase.devworkspaceEnabled
checluster.Spec.Auth.NativeUserMode = testCase.initialNativeUserValue
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{})
defaults := NewDefaultValuesReconciler()
_, done, err := defaults.Reconcile(ctx)
assert.True(t, done)
assert.Nil(t, err)
assert.Equal(t, testCase.expectedNativeUserValue, ctx.CheCluster.Spec.Auth.NativeUserMode)
})
}
}

View File

@ -64,8 +64,6 @@ type CheConfigMap struct {
CheLogLevel string `json:"CHE_LOG_LEVEL"` CheLogLevel string `json:"CHE_LOG_LEVEL"`
IdentityProviderUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"` IdentityProviderUrl string `json:"CHE_OIDC_AUTH__SERVER__URL,omitempty"`
IdentityProviderInternalURL string `json:"CHE_OIDC_AUTH__INTERNAL__SERVER__URL,omitempty"` IdentityProviderInternalURL string `json:"CHE_OIDC_AUTH__INTERNAL__SERVER__URL,omitempty"`
KeycloakRealm string `json:"CHE_KEYCLOAK_REALM,omitempty"`
KeycloakClientId string `json:"CHE_KEYCLOAK_CLIENT__ID,omitempty"`
OpenShiftIdentityProvider string `json:"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"` OpenShiftIdentityProvider string `json:"CHE_INFRA_OPENSHIFT_OAUTH__IDENTITY__PROVIDER"`
JavaOpts string `json:"JAVA_OPTS"` JavaOpts string `json:"JAVA_OPTS"`
WorkspaceJavaOpts string `json:"CHE_WORKSPACE_JAVA__OPTIONS"` WorkspaceJavaOpts string `json:"CHE_WORKSPACE_JAVA__OPTIONS"`
@ -95,37 +93,18 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
cheHost := ctx.CheCluster.Spec.Server.CheHost cheHost := ctx.CheCluster.Spec.Server.CheHost
identityProviderURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL identityProviderURL := ctx.CheCluster.Spec.Auth.IdentityProviderURL
// Adds `/auth` for external identity providers.
// If identity provide is deployed by operator then `/auth` is already added.
if !ctx.CheCluster.IsNativeUserModeEnabled() &&
ctx.CheCluster.Spec.Auth.ExternalIdentityProvider &&
!strings.HasSuffix(identityProviderURL, "/auth") {
if strings.HasSuffix(identityProviderURL, "/") {
identityProviderURL = identityProviderURL + "auth"
} else {
identityProviderURL = identityProviderURL + "/auth"
}
}
cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
infra := "kubernetes" infra := "kubernetes"
if util.IsOpenShift { if util.IsOpenShift {
infra = "openshift" infra = "openshift"
} }
tls := "false" tls := "false"
openShiftIdentityProviderId := "NULL" openShiftIdentityProviderId := "NULL"
if util.IsOpenShift && ctx.CheCluster.IsOpenShiftOAuthEnabled() { if util.IsOpenShift {
openShiftIdentityProviderId = "openshift-v3" openShiftIdentityProviderId = "openshift-v3"
if util.IsOpenShift4 { if util.IsOpenShift4 {
openShiftIdentityProviderId = "openshift-v4" openShiftIdentityProviderId = "openshift-v4"
} }
} }
tlsSupport := ctx.CheCluster.Spec.Server.TlsSupport
protocol := "http"
if tlsSupport {
protocol = "https"
tls = "true"
}
proxyJavaOpts := "" proxyJavaOpts := ""
cheWorkspaceNoProxy := ctx.Proxy.NoProxy cheWorkspaceNoProxy := ctx.Proxy.NoProxy
@ -158,9 +137,6 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
chePostgresHostName := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName) chePostgresHostName := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresHostName, deploy.DefaultChePostgresHostName)
chePostgresPort := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort) chePostgresPort := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresPort, deploy.DefaultChePostgresPort)
chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb) chePostgresDb := util.GetValue(ctx.CheCluster.Spec.Database.ChePostgresDb, deploy.DefaultChePostgresDb)
keycloakRealm := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
keycloakClientId := util.GetValue(ctx.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
ingressStrategy := util.GetServerExposureStrategy(ctx.CheCluster)
ingressClass := util.GetValue(ctx.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass) ingressClass := util.GetValue(ctx.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass)
// grab first the devfile registry url which is deployed by operator // grab first the devfile registry url which is deployed by operator
@ -182,45 +158,26 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug) cheDebug := util.GetValue(ctx.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Metrics.Enable) cheMetrics := strconv.FormatBool(ctx.CheCluster.Spec.Metrics.Enable)
cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(ctx.CheCluster, deploy.DefaultCheFlavor(ctx.CheCluster))) cheLabels := util.MapToKeyValuePairs(deploy.GetLabels(ctx.CheCluster, deploy.DefaultCheFlavor(ctx.CheCluster)))
workspaceExposure := deploy.GetSingleHostExposureType(ctx.CheCluster)
singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)) singleHostGatewayConfigMapLabels := labels.FormatLabels(util.GetMapValue(ctx.CheCluster.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels))
workspaceNamespaceDefault := util.GetWorkspaceNamespaceDefault(ctx.CheCluster) workspaceNamespaceDefault := deploy.GetWorkspaceNamespaceDefault(ctx.CheCluster)
cheAPI := protocol + "://" + cheHost + "/api" cheAPI := "https://" + cheHost + "/api"
var keycloakInternalURL, pluginRegistryInternalURL, devfileRegistryInternalURL, cheInternalAPI, webSocketInternalEndpoint string var pluginRegistryInternalURL, devfileRegistryInternalURL string
if !ctx.CheCluster.IsNativeUserModeEnabled() &&
ctx.CheCluster.IsInternalClusterSVCNamesEnabled() &&
!ctx.CheCluster.Spec.Auth.ExternalIdentityProvider {
keycloakInternalURL = fmt.Sprintf("%s://%s.%s.svc:8080/auth", "http", deploy.IdentityProviderName, ctx.CheCluster.Namespace)
}
// If there is a devfile registry deployed by operator // If there is a devfile registry deployed by operator
if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() && !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry { if !ctx.CheCluster.Spec.Server.ExternalDevfileRegistry {
devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, ctx.CheCluster.Namespace) devfileRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080", deploy.DevfileRegistryName, ctx.CheCluster.Namespace)
} }
if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() && !ctx.CheCluster.Spec.Server.ExternalPluginRegistry { if !ctx.CheCluster.Spec.Server.ExternalPluginRegistry {
pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, ctx.CheCluster.Namespace) pluginRegistryInternalURL = fmt.Sprintf("http://%s.%s.svc:8080/v3", deploy.PluginRegistryName, ctx.CheCluster.Namespace)
} }
if ctx.CheCluster.IsInternalClusterSVCNamesEnabled() { cheInternalAPI := fmt.Sprintf("http://%s.%s.svc:8080/api", deploy.CheServiceName, ctx.CheCluster.Namespace)
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)
webSocketInternalEndpoint = fmt.Sprintf("ws://%s.%s.svc:8080/api/websocket", deploy.CheServiceName, ctx.CheCluster.Namespace) webSocketEndpoint := "wss://" + cheHost + "/api/websocket"
} cheWorkspaceServiceAccount := "NULL"
cheUserClusterRoleNames := fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", ctx.CheCluster.Namespace, ctx.CheCluster.Namespace)
wsprotocol := "ws"
if tlsSupport {
wsprotocol = "wss"
}
webSocketEndpoint := wsprotocol + "://" + cheHost + "/api/websocket"
cheWorkspaceServiceAccount := "che-workspace"
cheUserClusterRoleNames := "NULL"
if ctx.CheCluster.IsNativeUserModeEnabled() {
cheWorkspaceServiceAccount = "NULL"
cheUserClusterRoleNames = fmt.Sprintf("%s-cheworkspaces-clusterrole, %s-cheworkspaces-devworkspace-clusterrole", ctx.CheCluster.Namespace, ctx.CheCluster.Namespace)
}
data := &CheConfigMap{ data := &CheConfigMap{
CheMultiUser: "true", CheMultiUser: "true",
@ -240,8 +197,8 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
WorkspacePvcStorageClassName: workspacePvcStorageClassName, WorkspacePvcStorageClassName: workspacePvcStorageClassName,
PvcJobsImage: pvcJobsImage, PvcJobsImage: pvcJobsImage,
PreCreateSubPaths: preCreateSubPaths, PreCreateSubPaths: preCreateSubPaths,
TlsSupport: tls, TlsSupport: "true",
K8STrustCerts: tls, K8STrustCerts: "true",
CheLogLevel: cheLogLevel, CheLogLevel: cheLogLevel,
OpenShiftIdentityProvider: openShiftIdentityProviderId, OpenShiftIdentityProvider: openShiftIdentityProviderId,
JavaOpts: deploy.DefaultJavaOpts + " " + proxyJavaOpts, JavaOpts: deploy.DefaultJavaOpts + " " + proxyJavaOpts,
@ -261,16 +218,13 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
CheJGroupsKubernetesLabels: cheLabels, CheJGroupsKubernetesLabels: cheLabels,
CheMetricsEnabled: cheMetrics, CheMetricsEnabled: cheMetrics,
CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName, CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName,
ServerStrategy: ingressStrategy, ServerStrategy: deploy.ServerExposureStrategy,
WorkspaceExposure: workspaceExposure, WorkspaceExposure: deploy.GatewaySingleHostExposureType,
SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels, SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels,
CheDevWorkspacesEnabled: strconv.FormatBool(ctx.CheCluster.Spec.DevWorkspace.Enable), CheDevWorkspacesEnabled: strconv.FormatBool(true),
} }
data.IdentityProviderUrl = identityProviderURL data.IdentityProviderUrl = identityProviderURL
data.IdentityProviderInternalURL = keycloakInternalURL
data.KeycloakRealm = keycloakRealm
data.KeycloakClientId = keycloakClientId
data.DatabaseURL = "jdbc:postgresql://" + chePostgresHostName + ":" + chePostgresPort + "/" + chePostgresDb data.DatabaseURL = "jdbc:postgresql://" + chePostgresHostName + ":" + chePostgresPort + "/" + chePostgresDb
if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 { if len(ctx.CheCluster.Spec.Database.ChePostgresSecret) < 1 {
data.DbUserName = ctx.CheCluster.Spec.Database.ChePostgresUser data.DbUserName = ctx.CheCluster.Spec.Database.ChePostgresUser
@ -294,10 +248,7 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *deploy.DeployContext) (ch
"CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON": "{\"kubernetes.io/ingress.class\": " + ingressClass + ", \"nginx.ingress.kubernetes.io/rewrite-target\": \"/$1\",\"nginx.ingress.kubernetes.io/ssl-redirect\": " + tls + ",\"nginx.ingress.kubernetes.io/proxy-connect-timeout\": \"3600\",\"nginx.ingress.kubernetes.io/proxy-read-timeout\": \"3600\"}", "CHE_INFRA_KUBERNETES_INGRESS_ANNOTATIONS__JSON": "{\"kubernetes.io/ingress.class\": " + ingressClass + ", \"nginx.ingress.kubernetes.io/rewrite-target\": \"/$1\",\"nginx.ingress.kubernetes.io/ssl-redirect\": " + tls + ",\"nginx.ingress.kubernetes.io/proxy-connect-timeout\": \"3600\",\"nginx.ingress.kubernetes.io/proxy-read-timeout\": \"3600\"}",
"CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM": "%s(.*)", "CHE_INFRA_KUBERNETES_INGRESS_PATH__TRANSFORM": "%s(.*)",
} }
k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true"
if ctx.CheCluster.Spec.DevWorkspace.Enable {
k8sCheEnv["CHE_INFRA_KUBERNETES_ENABLE__UNSUPPORTED__K8S"] = "true"
}
// Add TLS key and server certificate to properties since user workspaces is created in another // 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 // than Che server namespace, from where the Che TLS secret is not accessable

View File

@ -19,7 +19,6 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
orgv1 "github.com/eclipse-che/che-operator/api/v1" orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
@ -47,15 +46,11 @@ func TestNewCheConfigMap(t *testing.T) {
}, },
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
CheHost: "myhostname.com", CheHost: "myhostname.com",
TlsSupport: true,
CustomCheProperties: map[string]string{ CustomCheProperties: map[string]string{
"CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com", "CHE_WORKSPACE_NO_PROXY": "myproxy.myhostname.com",
}, },
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(true),
},
}, },
}, },
expectedData: map[string]string{ expectedData: map[string]string{
@ -159,7 +154,7 @@ func TestConfigMap(t *testing.T) {
}, },
}, },
expectedData: map[string]string{ expectedData: map[string]string{
"CHE_WEBSOCKET_ENDPOINT": "ws://che-host/api/websocket", "CHE_WEBSOCKET_ENDPOINT": "wss://che-host/api/websocket",
}, },
}, },
{ {
@ -188,33 +183,12 @@ func TestConfigMap(t *testing.T) {
}, },
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
CheHost: "che-host", CheHost: "che-host",
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
}, },
}, },
}, },
expectedData: map[string]string{ expectedData: map[string]string{
"CHE_WEBSOCKET_ENDPOINT": "ws://che-host/api/websocket", "CHE_WEBSOCKET_ENDPOINT": "wss://che-host/api/websocket",
},
},
{
name: "Kubernetes strategy should be set correctly",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
K8s: orgv1.CheClusterSpecK8SOnly{
IngressStrategy: "single-host",
},
},
},
expectedData: map[string]string{
"CHE_INFRA_KUBERNETES_SERVER__STRATEGY": "single-host",
}, },
}, },
} }
@ -418,9 +392,8 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
}, },
Spec: orgv1.CheClusterSpec{ Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true), ExternalDevfileRegistry: true,
ExternalDevfileRegistry: true, DevfileRegistryUrl: "http://devfile-registry.external.1",
DevfileRegistryUrl: "http://devfile-registry.external.1",
ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{ ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
{Url: "http://devfile-registry.external.2"}, {Url: "http://devfile-registry.external.2"},
}, },
@ -432,31 +405,6 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
"CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "", "CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "",
}, },
}, },
{
name: "Test devfile registry urls #4",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
ExternalDevfileRegistry: false,
},
},
Status: orgv1.CheClusterStatus{
DevfileRegistryURL: "http://devfile-registry.internal",
},
},
expectedData: map[string]string{
"CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "",
"CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal",
},
},
{ {
name: "Test devfile registry urls #5", name: "Test devfile registry urls #5",
cheCluster: &orgv1.CheCluster{ cheCluster: &orgv1.CheCluster{
@ -481,35 +429,6 @@ func TestShouldSetUpCorrectlyDevfileRegistryURL(t *testing.T) {
"CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal", "CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal",
}, },
}, },
{
name: "Test devfile registry urls #6",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
ExternalDevfileRegistry: false,
DevfileRegistryUrl: "http://devfile-registry.external.1",
ExternalDevfileRegistries: []orgv1.ExternalDevfileRegistries{
{Url: "http://devfile-registry.external.2"},
},
},
},
Status: orgv1.CheClusterStatus{
DevfileRegistryURL: "http://devfile-registry.internal",
},
},
expectedData: map[string]string{
"CHE_WORKSPACE_DEVFILE__REGISTRY__INTERNAL__URL": "",
"CHE_WORKSPACE_DEVFILE__REGISTRY__URL": "http://devfile-registry.internal http://devfile-registry.external.1 http://devfile-registry.external.2",
},
},
{ {
name: "Test devfile registry urls #7", name: "Test devfile registry urls #7",
cheCluster: &orgv1.CheCluster{ cheCluster: &orgv1.CheCluster{
@ -579,9 +498,6 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) {
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
ExternalPluginRegistry: true, ExternalPluginRegistry: true,
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
Status: orgv1.CheClusterStatus{ Status: orgv1.CheClusterStatus{
PluginRegistryURL: "http://external-plugin-registry", PluginRegistryURL: "http://external-plugin-registry",
@ -593,60 +509,6 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) {
}, },
{ {
name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #2", name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #2",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
ExternalPluginRegistry: true,
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
},
Status: orgv1.CheClusterStatus{
PluginRegistryURL: "http://external-plugin-registry",
},
},
expectedData: map[string]string{
"CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL": "",
},
},
{
name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #3",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
ExternalPluginRegistry: false,
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
},
Status: orgv1.CheClusterStatus{
PluginRegistryURL: "http://plugin-registry/v3",
},
},
expectedData: map[string]string{
"CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL": "",
},
},
{
name: "Test CHE_WORKSPACE_PLUGIN__REGISTRY__INTERNAL__URL #4",
cheCluster: &orgv1.CheCluster{ cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: "CheCluster", Kind: "CheCluster",
@ -659,9 +521,6 @@ func TestShouldSetUpCorrectlyInternalPluginRegistryServiceURL(t *testing.T) {
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
ExternalPluginRegistry: false, ExternalPluginRegistry: false,
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
Status: orgv1.CheClusterStatus{ Status: orgv1.CheClusterStatus{
PluginRegistryURL: "http://external-plugin-registry", PluginRegistryURL: "http://external-plugin-registry",
@ -698,30 +557,6 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
} }
testCases := []testCase{ testCases := []testCase{
{
name: "Should be an empty when internal network is disabled",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
CheHost: "che-host",
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
},
},
expectedData: map[string]string{
"CHE_API_INTERNAL": "",
},
},
{ {
name: "Should use internal che-server url, when internal network is enabled", name: "Should use internal che-server url, when internal network is enabled",
cheCluster: &orgv1.CheCluster{ cheCluster: &orgv1.CheCluster{
@ -736,9 +571,6 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
Server: orgv1.CheClusterSpecServer{ Server: orgv1.CheClusterSpecServer{
CheHost: "http://che-host", CheHost: "http://che-host",
}, },
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
},
}, },
}, },
expectedData: map[string]string{ expectedData: map[string]string{
@ -760,154 +592,3 @@ func TestShouldSetUpCorrectlyInternalCheServerURL(t *testing.T) {
}) })
} }
} }
func TestShouldSetUpCorrectlyInternalIdentityProviderServiceURL(t *testing.T) {
type testCase struct {
name string
isOpenShift bool
isOpenShift4 bool
initObjects []runtime.Object
cheCluster *orgv1.CheCluster
expectedData map[string]string
}
testCases := []testCase{
{
name: "Should be an empty when enabled 'external' public identity provider url and internal network is enabled #1",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
ExternalIdentityProvider: true,
IdentityProviderURL: "http://external-keycloak",
},
},
},
expectedData: map[string]string{
"CHE_OIDC_AUTH__INTERNAL__SERVER__URL": "",
"CHE_OIDC_AUTH__SERVER__URL": "http://external-keycloak/auth",
},
},
{
name: "Should be an empty when enabled 'external' public identity provider url and internal network is enabled #2",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
ExternalIdentityProvider: true,
IdentityProviderURL: "http://external-keycloak/auth",
},
},
},
expectedData: map[string]string{
"CHE_OIDC_AUTH__INTERNAL__SERVER__URL": "",
"CHE_OIDC_AUTH__SERVER__URL": "http://external-keycloak/auth",
},
},
{
name: "Should be and empty when enabled 'external' public identity provider url and internal network is disabled",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
ExternalIdentityProvider: true,
IdentityProviderURL: "http://external-keycloak",
},
},
},
expectedData: map[string]string{
"CHE_OIDC_AUTH__INTERNAL__SERVER__URL": "",
"CHE_OIDC_AUTH__SERVER__URL": "http://external-keycloak/auth",
},
},
{
name: "Should be an empty when internal network is disabled",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
DisableInternalClusterSVCNames: pointer.BoolPtr(true),
},
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
ExternalIdentityProvider: false,
IdentityProviderURL: "http://keycloak/auth",
},
},
},
expectedData: map[string]string{
"CHE_OIDC_AUTH__INTERNAL__SERVER__URL": "",
"CHE_OIDC_AUTH__SERVER__URL": "http://keycloak/auth",
},
},
{
name: "Should use internal identity provider url, when internal network is enabled",
cheCluster: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
OpenShiftoAuth: util.NewBoolPointer(false),
ExternalIdentityProvider: false,
IdentityProviderURL: "http://keycloak/auth",
},
},
},
expectedData: map[string]string{
"CHE_OIDC_AUTH__INTERNAL__SERVER__URL": "http://keycloak.eclipse-che.svc:8080/auth",
"CHE_OIDC_AUTH__SERVER__URL": "http://keycloak/auth",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
util.IsOpenShift = testCase.isOpenShift
util.IsOpenShift4 = testCase.isOpenShift4
ctx := deploy.GetTestDeployContext(testCase.cheCluster, []runtime.Object{})
server := NewCheServerReconciler()
actualData, err := server.getCheConfigMapData(ctx)
if err != nil {
t.Fatalf("Error creating ConfigMap data: %v", err)
}
util.ValidateContainData(actualData, testCase.expectedData, t)
})
}
}

View File

@ -16,7 +16,6 @@ import (
"strings" "strings"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
"github.com/eclipse-che/che-operator/pkg/deploy/postgres" "github.com/eclipse-che/che-operator/pkg/deploy/postgres"
"github.com/eclipse-che/che-operator/pkg/deploy/tls" "github.com/eclipse-che/che-operator/pkg/deploy/tls"
@ -112,42 +111,6 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps
cheEnv = append(cheEnv, selfSignedCertEnv) cheEnv = append(cheEnv, selfSignedCertEnv)
cheEnv = append(cheEnv, gitSelfSignedCertEnv) cheEnv = append(cheEnv, gitSelfSignedCertEnv)
cheEnv = append(cheEnv, gitSelfSignedCertHostEnv) cheEnv = append(cheEnv, gitSelfSignedCertHostEnv)
identityProviderSecret := ctx.CheCluster.Spec.Auth.IdentityProviderSecret
if len(identityProviderSecret) > 0 {
cheEnv = append(cheEnv, corev1.EnvVar{
Name: "CHE_KEYCLOAK_ADMIN__PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "password",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
},
corev1.EnvVar{
Name: "CHE_KEYCLOAK_ADMIN__USERNAME",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "user",
LocalObjectReference: corev1.LocalObjectReference{
Name: identityProviderSecret,
},
},
},
})
} else {
cheEnv = append(cheEnv, corev1.EnvVar{
Name: "CHE_KEYCLOAK_ADMIN__PASSWORD",
Value: ctx.CheCluster.Spec.Auth.IdentityProviderPassword,
},
corev1.EnvVar{
Name: "CHE_KEYCLOAK_ADMIN__USERNAME",
Value: ctx.CheCluster.Spec.Auth.IdentityProviderAdminUserName,
})
}
cheEnv = append(cheEnv, cheEnv = append(cheEnv,
corev1.EnvVar{ corev1.EnvVar{
Name: "CM_REVISION", Name: "CM_REVISION",
@ -160,13 +123,10 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps
APIVersion: "v1", APIVersion: "v1",
FieldPath: "metadata.namespace"}}, FieldPath: "metadata.namespace"}},
}) })
cheEnv = append(cheEnv, corev1.EnvVar{
if ctx.CheCluster.IsNativeUserModeEnabled() { Name: "CHE_AUTH_NATIVEUSER",
cheEnv = append(cheEnv, corev1.EnvVar{ Value: "true",
Name: "CHE_AUTH_NATIVEUSER", })
Value: "true",
})
}
cheImageAndTag := GetFullCheServerImageLink(ctx.CheCluster) cheImageAndTag := GetFullCheServerImageLink(ctx.CheCluster)
pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag))) pullPolicy := corev1.PullPolicy(util.GetValue(string(ctx.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag)))
@ -368,14 +328,6 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *deploy.DeployContext) (*apps
} }
deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, *waitForPostgresInitContainer) deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, *waitForPostgresInitContainer)
} }
if !ctx.CheCluster.Spec.Auth.ExternalIdentityProvider {
waitForKeycloakInitContainer, err := identityprovider.GetWaitForKeycloakInitContainer(ctx)
if err != nil {
return nil, err
}
deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, *waitForKeycloakInitContainer)
}
} }
return deployment, nil return deployment, nil

View File

@ -36,11 +36,6 @@ func TestReconcile(t *testing.T) {
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: os.Getenv("CHE_FLAVOR"), Name: os.Getenv("CHE_FLAVOR"),
}, },
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
TlsSupport: true,
},
},
} }
util.IsOpenShift = true util.IsOpenShift = true
@ -70,11 +65,6 @@ func TestSyncLegacyConfigMap(t *testing.T) {
Namespace: "eclipse-che", Namespace: "eclipse-che",
Name: "eclipse-che", Name: "eclipse-che",
}, },
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
TlsSupport: true,
},
},
} }
ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{}) ctx := deploy.GetTestDeployContext(cheCluster, []runtime.Object{})

View File

@ -14,10 +14,7 @@ package server
import ( import (
"fmt" "fmt"
orgv1 "github.com/eclipse-che/che-operator/api/v1"
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
"github.com/eclipse-che/che-operator/pkg/util"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
) )
@ -25,13 +22,6 @@ func getComponentName(ctx *deploy.DeployContext) string {
return deploy.DefaultCheFlavor(ctx.CheCluster) return deploy.DefaultCheFlavor(ctx.CheCluster)
} }
func getServerExposingServiceName(cr *orgv1.CheCluster) string {
if util.GetServerExposureStrategy(cr) == "single-host" && deploy.GetSingleHostExposureType(cr) == deploy.GatewaySingleHostExposureType {
return gateway.GatewayServiceName
}
return deploy.CheServiceName
}
func getOAuthConfig(ctx *deploy.DeployContext, oauthProvider string) (*corev1.Secret, error) { func getOAuthConfig(ctx *deploy.DeployContext, oauthProvider string) (*corev1.Secret, error) {
secrets, err := deploy.GetSecrets(ctx, map[string]string{ secrets, err := deploy.GetSecrets(ctx, map[string]string{
deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg, deploy.KubernetesPartOfLabelKey: deploy.CheEclipseOrg,

View File

@ -15,7 +15,6 @@ package tls
import ( import (
"github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/util" "github.com/eclipse-che/che-operator/pkg/util"
"github.com/sirupsen/logrus"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
@ -30,42 +29,23 @@ func NewTlsSecretReconciler() *TlsSecretReconciler {
func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) { func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
if util.IsOpenShift { if util.IsOpenShift {
// create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate // create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate
if ctx.IsSelfSignedCertificate || if ctx.IsSelfSignedCertificate {
// To use Openshift v4 OAuth, the OAuth endpoints are served from a namespace
// and NOT from the Openshift API Master URL (as in v3)
// So we also need the self-signed certificate to access them (same as the Che server)
(util.IsOpenShift4 && ctx.CheCluster.IsOpenShiftOAuthEnabled() && !ctx.CheCluster.Spec.Server.TlsSupport) {
if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil { if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
return reconcile.Result{}, false, err return reconcile.Result{}, false, err
} }
} }
if util.IsOpenShift && ctx.CheCluster.IsOpenShiftOAuthEnabled() {
// create a secret with OpenShift API crt to be added to keystore that RH SSO will consume
apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls()
if err != nil {
logrus.Errorf("Failed to get OpenShift cluster public hostname. A secret with API crt will not be created and consumed by RH-SSO/Keycloak")
} else {
baseURL := map[bool]string{true: apiInternalUrl, false: apiUrl}[apiInternalUrl != ""]
if err := CreateTLSSecretFromEndpoint(ctx, baseURL, "openshift-api-crt"); err != nil {
return reconcile.Result{}, false, err
}
}
}
} else { } else {
// Handle Che TLS certificates on Kubernetes infrastructure // Handle Che TLS certificates on Kubernetes infrastructure
if ctx.CheCluster.Spec.Server.TlsSupport { if ctx.CheCluster.Spec.K8s.TlsSecretName != "" {
if ctx.CheCluster.Spec.K8s.TlsSecretName != "" { // Self-signed certificate should be created to secure Che ingresses
// Self-signed certificate should be created to secure Che ingresses result, err := K8sHandleCheTLSSecrets(ctx)
result, err := K8sHandleCheTLSSecrets(ctx) if result.Requeue || result.RequeueAfter > 0 {
if result.Requeue || result.RequeueAfter > 0 { return result, false, err
return result, false, err }
} } else if ctx.IsSelfSignedCertificate {
} else if ctx.IsSelfSignedCertificate { // Use default self-signed ingress certificate
// Use default self-signed ingress certificate if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil { return reconcile.Result{}, false, err
return reconcile.Result{}, false, err
}
} }
} }
} }

View File

@ -15,14 +15,10 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/sha256" "crypto/sha256"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net/http"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
@ -199,23 +195,6 @@ func MergeMaps(first map[string]string, second map[string]string) map[string]str
return ret return ret
} }
func GetServerExposureStrategy(cheCluster *orgv1.CheCluster) string {
if cheCluster.Spec.Server.ServerExposureStrategy != "" {
return cheCluster.Spec.Server.ServerExposureStrategy
}
if !IsOpenShift && cheCluster.Spec.K8s.IngressStrategy != "" {
return cheCluster.Spec.K8s.IngressStrategy
}
// Explicitly switch to `single-host` mode
if cheCluster.Spec.DevWorkspace.Enable {
return "single-host"
}
return "multi-host"
}
func IsTestMode() (isTesting bool) { func IsTestMode() (isTesting bool) {
testMode := os.Getenv("MOCK_API") testMode := os.Getenv("MOCK_API")
if len(testMode) == 0 { if len(testMode) == 0 {
@ -224,103 +203,6 @@ func IsTestMode() (isTesting bool) {
return true return true
} }
func GetOpenShiftAPIUrls() (string, string, error) {
// for debug purpose
apiUrl := os.Getenv("CLUSTER_API_URL")
apiInternalUrl := os.Getenv("CLUSTER_API_INTERNAL_URL")
if apiUrl != "" || apiInternalUrl != "" {
return apiUrl, apiInternalUrl, nil
}
if IsOpenShift4 {
return getAPIUrlsForOpenShiftV4()
}
return getAPIUrlsForOpenShiftV3()
}
// getAPIUrlsForOpenShiftV3 is a hacky way to get OpenShift API public DNS/IP
// to be used in OpenShift oAuth provider as baseURL
func getAPIUrlsForOpenShiftV3() (apiUrl string, apiInternalUrl string, err error) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{}
kubeApi := os.Getenv("KUBERNETES_PORT_443_TCP_ADDR")
url := "https://" + kubeApi + "/.well-known/oauth-authorization-server"
req, err := http.NewRequest(http.MethodGet, url, nil)
resp, err := client.Do(req)
if err != nil {
logrus.Errorf("An error occurred when getting API public hostname: %s", err)
return "", "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logrus.Errorf("An error occurred when getting API public hostname: %s", err)
return "", "", err
}
var jsonData map[string]interface{}
err = json.Unmarshal(body, &jsonData)
if err != nil {
logrus.Errorf("An error occurred when unmarshalling: %s", err)
return "", "", err
}
apiUrl = jsonData["issuer"].(string)
return apiUrl, "", nil
}
// getClusterPublicHostnameForOpenshiftV3 is a way to get OpenShift API public DNS/IP
// to be used in OpenShift oAuth provider as baseURL
func getAPIUrlsForOpenShiftV4() (apiUrl string, apiInternalUrl string, err error) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{}
kubeApi := os.Getenv("KUBERNETES_PORT_443_TCP_ADDR")
url := "https://" + kubeApi + "/apis/config.openshift.io/v1/infrastructures/cluster"
req, err := http.NewRequest(http.MethodGet, url, nil)
file, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
logrus.Errorf("Failed to locate token file: %s", err)
return "", "", err
}
token := string(file)
req.Header = http.Header{
"Authorization": []string{"Bearer " + token},
}
resp, err := client.Do(req)
if err != nil {
logrus.Errorf("An error occurred when getting API public hostname: %s", err)
return "", "", err
}
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
message := url + " - " + resp.Status
logrus.Errorf("An error occurred when getting API public hostname: %s", message)
return "", "", errors.New(message)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logrus.Errorf("An error occurred when getting API public hostname: %s", err)
return "", "", err
}
var jsonData map[string]interface{}
err = json.Unmarshal(body, &jsonData)
if err != nil {
logrus.Errorf("An error occurred when unmarshalling while getting API public hostname: %s", err)
return "", "", err
}
switch status := jsonData["status"].(type) {
case map[string]interface{}:
apiUrl = status["apiServerURL"].(string)
apiInternalUrl = status["apiServerInternalURI"].(string)
default:
logrus.Errorf("An error occurred when unmarshalling while getting API public hostname: %s", body)
return "", "", errors.New(string(body))
}
return apiUrl, apiInternalUrl, nil
}
func GetRouterCanonicalHostname(client client.Client, namespace string) (string, error) { func GetRouterCanonicalHostname(client client.Client, namespace string) (string, error) {
testRouteYaml, err := GetTestRouteYaml(client, namespace) testRouteYaml, err := GetTestRouteYaml(client, namespace)
if err != nil { if err != nil {
@ -479,22 +361,6 @@ func NewBoolPointer(value bool) *bool {
return &variable return &variable
} }
// GetWorkspaceNamespaceDefault - returns workspace namespace default strategy, which points on the namespaces used for workspaces execution.
func GetWorkspaceNamespaceDefault(cr *orgv1.CheCluster) string {
if cr.Spec.Server.CustomCheProperties != nil {
k8sNamespaceDefault := cr.Spec.Server.CustomCheProperties["CHE_INFRA_KUBERNETES_NAMESPACE_DEFAULT"]
if k8sNamespaceDefault != "" {
return k8sNamespaceDefault
}
}
workspaceNamespaceDefault := cr.Namespace
if IsOpenShift && cr.IsOpenShiftOAuthEnabled() {
workspaceNamespaceDefault = "<username>-" + cr.Spec.Server.CheFlavor
}
return GetValue(cr.Spec.Server.WorkspaceNamespaceDefault, workspaceNamespaceDefault)
}
func GetResourceQuantity(value string, defaultValue string) resource.Quantity { func GetResourceQuantity(value string, defaultValue string) resource.Quantity {
if value != "" { if value != "" {
return resource.MustParse(value) return resource.MustParse(value)
@ -565,11 +431,5 @@ func ClearMetadata(objectMeta *metav1.ObjectMeta) {
// GetCheURL returns Che url. // GetCheURL returns Che url.
func GetCheURL(cheCluster *orgv1.CheCluster) string { func GetCheURL(cheCluster *orgv1.CheCluster) string {
var cheUrl string return "https://" + cheCluster.Spec.Server.CheHost
if cheCluster.Spec.Server.TlsSupport {
cheUrl = "https://" + cheCluster.Spec.Server.CheHost
} else {
cheUrl = "http://" + cheCluster.Spec.Server.CheHost
}
return cheUrl
} }

View File

@ -1,41 +0,0 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
connectToKeycloak() {
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
}
createIdentityProvider() {
{{ .Script }} get identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
if [ $? -eq 0 ]; then
echo "{{ .ProviderId }} identity provider exists."
else
echo "Create new {{ .ProviderId }} identity provider."
if [ -z "${GITHUB_CLIENT_ID}" ] || [ -z "${GITHUB_SECRET}" ]; then
echo "Either 'GITHUB_CLIENT_ID' or 'GITHUB_SECRET' aren't set" 1>&2
exit 1
fi
{{ .Script }} create identity-provider/instances \
-r {{ .KeycloakRealm }} \
-s alias={{ .ProviderId }} \
-s providerId={{ .ProviderId }} \
-s enabled=true \
-s storeToken=true \
-s config.useJwksUrl=true \
-s config.clientId=${GITHUB_CLIENT_ID} \
-s config.clientSecret=${GITHUB_SECRET} \
-s config.defaultScope=repo,user,write:public_key
fi
}
connectToKeycloak
createIdentityProvider

View File

@ -1,28 +0,0 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
connectToKeycloak() {
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
}
deleteIdentityProvider() {
{{ .Script }} get identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
if [ ! $? -eq 0 ]; then
echo "{{ .ProviderId }} identity provider does not exists."
else
echo "Delete {{ .ProviderId }} identity provider."
{{ .Script }} delete identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
fi
}
connectToKeycloak
deleteIdentityProvider

View File

@ -1,89 +0,0 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
connectToKeycloak() {
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
}
provisionKeycloak() {
{{ .Script }} update realms/master -s sslRequired=none
{{ .Script }} config truststore --trustpass ${SSO_TRUSTSTORE_PASSWORD} ${SSO_TRUSTSTORE_DIR}/${SSO_TRUSTSTORE}
{{ .Script }} get realms/{{ .KeycloakRealm }}
if [ $? -eq 0 ]; then
echo "{{ .KeycloakRealm }} realm exists."
exit 0
fi
echo "Provision {{ .KeycloakRealm }} realm."
{{ .Script }} create realms \
-s realm='{{ .KeycloakRealm }}' \
-s displayName='{{ .RealmDisplayName }}' \
-s enabled=true \
-s sslRequired=none \
-s registrationAllowed=false \
-s resetPasswordAllowed=true \
-s loginTheme={{ .KeycloakTheme }} \
-s accountTheme={{ .KeycloakTheme }} \
-s adminTheme={{ .KeycloakTheme }} \
-s emailTheme={{ .KeycloakTheme }}
DEFAULT_WEBORIGINS='"http://{{ .CheHost }}", "https://{{ .CheHost }}"'
# ADDITIONAL_WEBORIGINS is an env var in format '"url1", "url2"'
# which if specified, is provisioned to keycloak additionally to Che's URLs ones
[ ! -z "$ADDITIONAL_WEBORIGINS" ] && ADDITIONAL_WEBORIGINS=", $ADDITIONAL_WEBORIGINS"
WEBORIGINS="[$DEFAULT_WEBORIGINS $ADDITIONAL_WEBORIGINS]"
DEFAULT_REDIRECT_URIS='"http://{{ .CheHost }}/dashboard/*", "https://{{ .CheHost }}/dashboard/*", "http://{{ .CheHost }}/factory*", "https://{{ .CheHost }}/factory*", "http://{{ .CheHost }}/f*", "https://{{ .CheHost }}/f*", "http://{{ .CheHost }}/_app/*", "https://{{ .CheHost }}/_app/*", "http://{{ .CheHost }}/swagger/*", "https://{{ .CheHost }}/swagger/*"'
# ADDITIONAL_REDIRECT_URIS is an env var in format '"url1", "url2"'
# which if specified, is provisioned to keycloak additionally to Che's URLs ones
[ ! -z "$ADDITIONAL_REDIRECT_URIS" ] && ADDITIONAL_REDIRECT_URIS=", $ADDITIONAL_REDIRECT_URIS"
REDIRECT_URIS="[$DEFAULT_REDIRECT_URIS $ADDITIONAL_REDIRECT_URIS]"
{{ .Script }} create clients \
-r '{{ .KeycloakRealm }}' \
-s clientId={{ .KeycloakClientId }} \
-s id={{ .KeycloakClientId }} \
-s webOrigins="$WEBORIGINS" \
-s redirectUris="$REDIRECT_URIS" \
-s directAccessGrantsEnabled=true \
-s publicClient=true
{{ .Script }} create users \
-r '{{ .KeycloakRealm }}' \
-s username=admin \
-s email=\"admin@admin.com\" \
-s enabled=true \
-s requiredActions='[{{ .RequiredActions }}]'
{{ .Script }} set-password \
-r '{{ .KeycloakRealm }}' \
--username admin \
--new-password admin
user_id=$( {{ .Script }} get users -r '{{ .KeycloakRealm }}' -q username=admin --fields 'id' --format csv --noquotes )
{{ .Script }} update users/${user_id} -r '{{ .KeycloakRealm }}' -s requiredActions='[{{ .RequiredActions }}]'
{{ .Script }} add-roles \
-r '{{ .KeycloakRealm }}' \
--uusername admin \
--cclientid broker \
--rolename read-token
CLIENT_ID=$({{ .Script }} get clients -r '{{ .KeycloakRealm }}' -q clientId=broker | sed -n 's/.*"id" *: *"\([^"]\+\).*/\1/p')
{{ .Script }} update clients/${CLIENT_ID} \
-r '{{ .KeycloakRealm }}' \
-s "defaultRoles+=read-token"
}
connectToKeycloak
provisionKeycloak

View File

@ -1,50 +0,0 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
connectToKeycloak() {
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
}
updateKeycloak() {
DEFAULT_WEBORIGINS='"http://{{ .CheHost }}", "https://{{ .CheHost }}"'
# ADDITIONAL_WEBORIGINS is an env var in format '"url1", "url2"'
# which if specified, is provisioned to keycloak additionally to Che's URLs ones
[ ! -z "$ADDITIONAL_WEBORIGINS" ] && ADDITIONAL_WEBORIGINS=", $ADDITIONAL_WEBORIGINS"
WEBORIGINS="[$DEFAULT_WEBORIGINS $ADDITIONAL_WEBORIGINS]"
DEFAULT_REDIRECT_URIS='"http://{{ .CheHost }}/dashboard/*", "https://{{ .CheHost }}/dashboard/*", "http://{{ .CheHost }}/factory*", "https://{{ .CheHost }}/factory*", "http://{{ .CheHost }}/f*", "https://{{ .CheHost }}/f*", "http://{{ .CheHost }}/_app/*", "https://{{ .CheHost }}/_app/*", "http://{{ .CheHost }}/swagger/*", "https://{{ .CheHost }}/swagger/*"'
# ADDITIONAL_REDIRECT_URIS is an env var in format '"url1", "url2"'
# which if specified, is provisioned to keycloak additionally to Che's URLs ones
[ ! -z "$ADDITIONAL_REDIRECT_URIS" ] && ADDITIONAL_REDIRECT_URIS=", $ADDITIONAL_REDIRECT_URIS"
REDIRECT_URIS="[$DEFAULT_REDIRECT_URIS $ADDITIONAL_REDIRECT_URIS]"
{{ .Script }} update clients/{{ .KeycloakClientId }} \
-r '{{ .KeycloakRealm }}' \
-s webOrigins="$WEBORIGINS" \
-s redirectUris="$REDIRECT_URIS"
}
checkKeycloak() {
REDIRECT_URIS=$({{ .Script }} get clients/{{ .KeycloakClientId }} -r '{{ .KeycloakRealm }}' | jq '.redirectUris')
FIND="http://{{ .CheHost }}/factory*"
for URI in "${REDIRECT_URIS[@]}"; do
[[ $FIND == "$URI" ]] && return 0
done
return 1
}
connectToKeycloak
checkKeycloak
if [ $? -ne 0 ]
then
updateKeycloak
fi

View File

@ -1,114 +0,0 @@
#
# Copyright (c) 2019-2021 Red Hat, Inc.
# This program and the accompanying materials are made
# available under the terms of the Eclipse Public License 2.0
# which is available at https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Red Hat, Inc. - initial API and implementation
#
connect_to_keycloak() {
{{ .Script }} config credentials --server http://0.0.0.0:8080/auth --realm master --user {{ .KeycloakAdminUserName }} --password {{ .KeycloakAdminPassword }}
{{ .Script }} config truststore --trustpass ${SSO_TRUSTSTORE_PASSWORD} ${SSO_TRUSTSTORE_DIR}/${SSO_TRUSTSTORE}
}
create_identity_provider() {
{{ .Script }} get identity-provider/instances/{{ .ProviderId }} -r {{ .KeycloakRealm }}
if [ $? -eq 0 ]
then echo "Provider exists"
else {{ .Script }} create identity-provider/instances -r {{ .KeycloakRealm }} -s alias={{ .ProviderId }} -s providerId={{ .ProviderId }} -s enabled=true -s storeToken=true -s addReadTokenRoleOnCreate=true -s config.useJwksUrl=true -s config.clientId={{ .OAuthClientName}} -s config.clientSecret={{ .OauthSecret}} -s config.baseUrl={{ .OpenShiftApiUrl }} -s config.defaultScope=user:full
fi
}
default_to_openshift_login() {
EXECUTION_ID=$({{ .Script }} get authentication/flows/browser/executions -r {{ .KeycloakRealm }} -c | sed -e 's/.*\({[^}]\+"identity-provider-redirector"[^}]\+}\).*/\1/' -e 's/.*"id":"\([^"]\+\)".*/\1/')
ALIAS=$({{ .Script }} get authentication/flows/browser/executions -r {{ .KeycloakRealm }} -c | sed -e 's/.*\({[^}]\+"identity-provider-redirector"[^}]\+}\).*/\1/' | grep '"alias":"' | sed -e 's/.*"alias":"\([^"]\+\)".*/\1/')
if [ "${EXECUTION_ID}" == "" ]
then
echo "Could not find the identity provider redirector"
return 1
fi
if [ -z ${ALIAS} ];
then
echo '{"config":{"defaultProvider":"{{ .ProviderId }}"},"alias":"{{ .ProviderId }}"}' | {{ .Script }} create -r {{ .KeycloakRealm }} authentication/executions/${EXECUTION_ID}/config -f -
fi
}
enable_openshift_token-exchange() {
IDENTITY_PROVIDER_ID=$({{ .Script }} get -r {{ .KeycloakRealm }} identity-provider/instances/{{ .ProviderId }} | grep -e '"internalId" *: *"' | sed -e 's/.*"internalId" *: *"\([^"]\+\)".*/\1/')
if [ "${IDENTITY_PROVIDER_ID}" == "" ]
then
echo "identity provider not found"
return 1
fi
echo '{"enabled": true}' | {{ .Script }} update -r {{ .KeycloakRealm }} identity-provider/instances/{{ .ProviderId }}/management/permissions -f -
if [ $? -ne 0 ]
then
echo "failed to enable permissions on identity provider"
return 1
fi
TOKEN_EXCHANGE_PERMISSIONS=$({{ .Script }} get -r {{ .KeycloakRealm }} identity-provider/instances/{{ .ProviderId }}/management/permissions)
if [ "${TOKEN_EXCHANGE_PERMISSIONS}" == "" ]
then
echo "token exchange permissions not found"
return 1
fi
TOKEN_EXCHANGE_RESOURCE=$(echo ${TOKEN_EXCHANGE_PERMISSIONS} | grep -e '"resource" *: *"' | sed -e 's/.*"resource" *: *"\([^"]\+\)".*/\1/')
TOKEN_EXCHANGE_PERMISSION_ID=$(echo ${TOKEN_EXCHANGE_PERMISSIONS} | sed -e 's/.*"scopePermissions" *: *{ *"token-exchange" *: *"\([^"]\+\)".*/\1/')
if [ "${TOKEN_EXCHANGE_RESOURCE}" == "" ] || [ "${TOKEN_EXCHANGE_PERMISSION_ID}" == "" ]
then
echo "token exchange permissions do not contain expected values"
return 1
fi
REALM_MGMT_CLIENT_ID=$({{ .Script }} get -r {{ .KeycloakRealm }} clients -q clientId=realm-management | grep -e '"id" *: *"' | sed -e 's/.*"id" *: *"\([^"]\+\)".*/\1/')
if [ "${REALM_MGMT_CLIENT_ID}" == "" ]
then
echo "Realm management client ID not found"
return 1
fi
EXISTING_POLICY=$({{ .Script }} get -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/policy/client -q 'name={{ .ProviderId }}' | grep -e '"id" *: *"' | sed -e 's/.*"id" *: *"\([^"]\+\)".*/\1/')
if [ "${EXISTING_POLICY}" == "" ]
then
echo '{"type":"client","logic":"POSITIVE","decisionStrategy":"UNANIMOUS","name":"{{ .ProviderId }}","clients":["{{ .KeycloakClientId }}"]}' | {{ .Script }} create -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/policy/client -f -
if [ $? -ne 0 ]
then
echo "Failed to create policy"
return 1
fi
fi
TOKEN_EXCHANGE_POLICY=$({{ .Script }} get -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/policy/client -q 'name={{ .ProviderId }}' | grep -e '"id" *: *"' | sed -e 's/.*"id" *: *"\([^"]\+\)".*/\1/')
if [ "${TOKEN_EXCHANGE_POLICY}" == "" ]
then
echo "Token exchange policy not found"
return 1
fi
TOKEN_EXCHANGE_PERMISSION=$({{ .Script }} get -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/permission/scope/${TOKEN_EXCHANGE_PERMISSION_ID})
if [ "${TOKEN_EXCHANGE_PERMISSION}" == "" ]
then
echo "Token exchange permission not found"
return 1
fi
TOKEN_EXCHANGE_SCOPES=$({{ .Script }} get -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/resource/${TOKEN_EXCHANGE_RESOURCE}/scopes)
if [ "${TOKEN_EXCHANGE_SCOPES}" == "" ]
then
echo "Token exchange scopes not found"
return 1
fi
TOKEN_EXCHANGE_SCOPE=$(echo ${TOKEN_EXCHANGE_SCOPES} | sed -e 's/.*"id" *: *"\([^"]\+\)" *, *"name" *: *"token-exchange".*/\1/')
if [ "${TOKEN_EXCHANGE_SCOPE}" == "" ]
then
echo "Token exchange scope not found"
return 1
fi
PERMISSION_RESOURCES=$(echo ${TOKEN_EXCHANGE_PERMISSION} | grep -e 'resources *:')
if [ "${PERMISSION_RESOURCES}" == "" ]
then
echo ${TOKEN_EXCHANGE_PERMISSION} | sed -e "s/ *{\(.*}\) */{\"resources\":[\"${TOKEN_EXCHANGE_RESOURCE}\"],\"scopes\":[\"${TOKEN_EXCHANGE_SCOPE}\"],\"policies\":[\"${TOKEN_EXCHANGE_POLICY}\"],\1/" | {{ .Script }} update -r {{ .KeycloakRealm }} clients/${REALM_MGMT_CLIENT_ID}/authz/resource-server/permission/scope/${TOKEN_EXCHANGE_PERMISSION_ID} -f -
fi
}
set -x
connect_to_keycloak && create_identity_provider && default_to_openshift_login && enable_openshift_token-exchange