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
parent
228a3350cd
commit
92d24388ec
5
Makefile
5
Makefile
|
|
@ -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
|
||||||
|
|
|
||||||
22
README.md
22
README.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ spec:
|
||||||
cheClusterRoles: ''
|
cheClusterRoles: ''
|
||||||
cheWorkspaceClusterRole: ''
|
cheWorkspaceClusterRole: ''
|
||||||
gitSelfSignedCert: false
|
gitSelfSignedCert: false
|
||||||
tlsSupport: true
|
|
||||||
proxyURL: ''
|
proxyURL: ''
|
||||||
proxyPort: ''
|
proxyPort: ''
|
||||||
proxySecret: ''
|
proxySecret: ''
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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{}))
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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}"
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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, "/") {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
142
pkg/util/util.go
142
pkg/util/util.go
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
Loading…
Reference in New Issue