feat: configure DWOC from che-operator CR (#1494)

* feat: configure DWOC from che-operator CR

Currently, only the following fields from the checluster custom resource
are propagated to the DevWorkspace-Operator Configuration:

- `checluster.spec.devEnvironments.Storage.pvcStrategy`
- `checluster.spec.devEnvironments.Storage.perUserStrategyPvcConfig.ClaimSize`
- `checluster.spec.devEnvironments.Storage.perUserStrategyPvcConfig.StorageClass`
- `checluster.spec.devEnvironments.Storage.perWorkspaceStrategyPvcConfig.ClaimSize`
- `checluster.spec.devEnvironments.Storage.perWorkspaceStrategyPvcConfig.StorageClass`

Signed-off-by: Andrew Obuchowicz <aobuchow@redhat.com>

* Update CSV and autogenerated apiv2 deepcopy

Signed-off-by: Andrew Obuchowicz <aobuchow@redhat.com>

* Refactoring

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

* make fmt

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

Signed-off-by: Andrew Obuchowicz <aobuchow@redhat.com>
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
pull/1510/head
Andrew O 2022-09-02 12:50:35 -04:00 committed by GitHub
parent c67e776f56
commit 75a31c3bb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 484 additions and 18 deletions

View File

@ -76,7 +76,7 @@ metadata:
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/eclipse-che/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.53.0-664.next
name: eclipse-che-preview-openshift.v7.53.0-666.next
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -1389,7 +1389,7 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.53.0-664.next
version: 7.53.0-666.next
webhookdefinitions:
- admissionReviewVersions:
- v1

View File

@ -23,6 +23,7 @@ import (
"github.com/eclipse-che/che-operator/pkg/deploy/consolelink"
"github.com/eclipse-che/che-operator/pkg/deploy/dashboard"
devworkspace "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace"
devworkspaceconfig "github.com/eclipse-che/che-operator/pkg/deploy/dev-workspace-config"
"github.com/eclipse-che/che-operator/pkg/deploy/devfileregistry"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
@ -97,6 +98,7 @@ func NewReconciler(
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
reconcileManager.RegisterReconciler(devworkspace.NewDevWorkspaceReconciler())
reconcileManager.RegisterReconciler(devworkspaceconfig.NewDevWorkspaceConfigReconciler())
reconcileManager.RegisterReconciler(rbac.NewCheServerPermissionsReconciler())
reconcileManager.RegisterReconciler(rbac.NewGatewayPermissionsReconciler())
reconcileManager.RegisterReconciler(rbac.NewWorkspacePermissionsReconciler())

View File

@ -96,10 +96,12 @@ const (
CheEclipseOrgHash256 = "che.eclipse.org/hash256"
CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest"
// Workspaces
DefaultPvcStrategy = "common"
DefaultPvcClaimSize = "10Gi"
DefaultWorkspaceJavaOpts = "-XX:MaxRAM=150m -XX:MaxRAMFraction=2 -XX:+UseParallelGC " +
// DevEnvironments
PerUserPVCStorageStrategy = "per-user"
DefaultPvcStorageStrategy = "per-user"
PerWorkspacePVCStorageStrategy = "per-workspace"
CommonPVCStorageStrategy = "common"
DefaultWorkspaceJavaOpts = "-XX:MaxRAM=150m -XX:MaxRAMFraction=2 -XX:+UseParallelGC " +
"-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 " +
"-Dsun.zip.disableMemoryMapping=true " +
"-Xms20m -Djava.security.egd=file:/dev/./urandom"

View File

@ -17,6 +17,9 @@ import (
"strings"
"testing"
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
routev1 "github.com/openshift/api/route/v1"
"github.com/stretchr/testify/assert"
"k8s.io/utils/pointer"
@ -24,7 +27,6 @@ import (
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
console "github.com/openshift/api/console/v1"
oauthv1 "github.com/openshift/api/oauth/v1"
routev1 "github.com/openshift/api/route/v1"
userv1 "github.com/openshift/api/user/v1"
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
@ -169,6 +171,7 @@ func GetDeployContext(cheCluster *chev2.CheCluster, initObjs []runtime.Object) *
scheme := scheme.Scheme
chev2.SchemeBuilder.AddToScheme(scheme)
scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{})
scheme.AddKnownTypes(controllerv1alpha1.SchemeBuilder.GroupVersion, &controllerv1alpha1.DevWorkspaceOperatorConfig{})
scheme.AddKnownTypes(crdv1.SchemeGroupVersion, &crdv1.CustomResourceDefinition{})
scheme.AddKnownTypes(operatorsv1alpha1.SchemeGroupVersion, &operatorsv1alpha1.Subscription{})
scheme.AddKnownTypes(oauthv1.GroupVersion, &oauthv1.OAuthClient{})

View File

@ -0,0 +1,122 @@
//
// Copyright (c) 2019-2022 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 devworkspaceconfig
import (
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
chev2 "github.com/eclipse-che/che-operator/api/v2"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
"github.com/eclipse-che/che-operator/pkg/common/utils"
"github.com/eclipse-che/che-operator/pkg/deploy"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
devWorkspaceConfigName = "devworkspace-config"
)
type DevWorkspaceConfigReconciler struct {
deploy.Reconcilable
}
func NewDevWorkspaceConfigReconciler() *DevWorkspaceConfigReconciler {
return &DevWorkspaceConfigReconciler{}
}
func (d *DevWorkspaceConfigReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
dwoc := &controllerv1alpha1.DevWorkspaceOperatorConfig{
ObjectMeta: metav1.ObjectMeta{
Name: devWorkspaceConfigName,
Namespace: ctx.CheCluster.Namespace,
},
TypeMeta: metav1.TypeMeta{
Kind: "DevWorkspaceOperatorConfig",
APIVersion: controllerv1alpha1.GroupVersion.String(),
},
}
if _, err := deploy.GetNamespacedObject(ctx, devWorkspaceConfigName, dwoc); err != nil {
return reconcile.Result{}, false, err
}
if dwoc.Config == nil {
dwoc.Config = &controllerv1alpha1.OperatorConfiguration{}
}
err := updateOperatorConfig(ctx.CheCluster.Spec.DevEnvironments.Storage, dwoc.Config)
if err != nil {
return reconcile.Result{}, false, err
}
done, err := deploy.Sync(ctx, dwoc)
if !done {
return reconcile.Result{}, false, err
}
return reconcile.Result{}, true, nil
}
func (d *DevWorkspaceConfigReconciler) Finalize(ctx *chetypes.DeployContext) bool {
return true
}
func updateOperatorConfig(storage chev2.WorkspaceStorage, operatorConfig *controllerv1alpha1.OperatorConfiguration) error {
var pvc *chev2.PVC
pvcStrategy := utils.GetValue(storage.PvcStrategy, constants.DefaultPvcStorageStrategy)
switch pvcStrategy {
case constants.CommonPVCStorageStrategy:
fallthrough
case constants.PerUserPVCStorageStrategy:
if storage.PerUserStrategyPvcConfig != nil {
pvc = storage.PerUserStrategyPvcConfig
}
case constants.PerWorkspacePVCStorageStrategy:
if storage.PerWorkspaceStrategyPvcConfig != nil {
pvc = storage.PerWorkspaceStrategyPvcConfig
}
}
if pvc != nil {
if operatorConfig.Workspace == nil {
operatorConfig.Workspace = &controllerv1alpha1.WorkspaceConfig{}
}
return updateWorkspaceConfig(pvc, pvcStrategy == constants.PerWorkspacePVCStorageStrategy, operatorConfig.Workspace)
}
return nil
}
func updateWorkspaceConfig(pvc *chev2.PVC, isPerWorkspacePVCStorageStrategy bool, workspaceConfig *controllerv1alpha1.WorkspaceConfig) error {
if pvc.StorageClass != "" {
workspaceConfig.StorageClassName = &pvc.StorageClass
}
if pvc.ClaimSize != "" {
if workspaceConfig.DefaultStorageSize == nil {
workspaceConfig.DefaultStorageSize = &controllerv1alpha1.StorageSizes{}
}
pvcSize, err := resource.ParseQuantity(pvc.ClaimSize)
if err != nil {
return err
}
if isPerWorkspacePVCStorageStrategy {
workspaceConfig.DefaultStorageSize.PerWorkspace = &pvcSize
} else {
workspaceConfig.DefaultStorageSize.Common = &pvcSize
}
}
return nil
}

View File

@ -0,0 +1,348 @@
//
// Copyright (c) 2019-2022 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 devworkspaceconfig
import (
"regexp"
"testing"
"github.com/eclipse-che/che-operator/pkg/common/constants"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/utils/pointer"
"context"
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
chev2 "github.com/eclipse-che/che-operator/api/v2"
"github.com/eclipse-che/che-operator/pkg/common/test"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
func TestReconcileDevWorkspaceConfigPerUserStorage(t *testing.T) {
type testCase struct {
name string
cheCluster *chev2.CheCluster
existedObjects []runtime.Object
expectedOperatorConfig *controllerv1alpha1.OperatorConfiguration
}
type errorTestCase struct {
name string
cheCluster *chev2.CheCluster
existedObjects []runtime.Object
expectedErrorMessage string
}
var quantity15Gi = resource.MustParse("15Gi")
var quantity10Gi = resource.MustParse("10Gi")
var expectedErrorTestCases = []errorTestCase{
{
name: "Invalid claim size string",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerUserPVCStorageStrategy,
PerUserStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "invalid-ClaimSize",
},
},
},
},
},
expectedErrorMessage: "quantities must match the regular expression",
},
}
var testCases = []testCase{
{
name: "Create DevWorkspaceOperatorConfig",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{},
},
{
name: "Create DevWorkspaceOperatorConfig with StorageClassName only",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerUserPVCStorageStrategy,
PerUserStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
},
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("test-storage"),
},
},
},
{
name: "Create DevWorkspaceOperatorConfig with per-user strategy",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerUserPVCStorageStrategy,
PerUserStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "15Gi",
},
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("test-storage"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
Common: &quantity15Gi,
},
},
},
},
{
name: "Create DevWorkspaceOperatorConfig with per-workspace strategy",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerWorkspacePVCStorageStrategy,
PerWorkspaceStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "15Gi",
},
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("test-storage"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
PerWorkspace: &quantity15Gi,
},
},
},
},
{
name: "Update DevWorkspaceOperatorConfig with per-workspace strategy",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerWorkspacePVCStorageStrategy,
PerWorkspaceStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "15Gi",
},
},
},
},
},
existedObjects: []runtime.Object{
&controllerv1alpha1.DevWorkspaceOperatorConfig{
ObjectMeta: metav1.ObjectMeta{
Name: devWorkspaceConfigName,
Namespace: "eclipse-che",
},
TypeMeta: metav1.TypeMeta{
Kind: "DevWorkspaceOperatorConfig",
APIVersion: controllerv1alpha1.GroupVersion.String(),
},
Config: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("default-storage-class"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
PerWorkspace: &quantity10Gi,
},
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("test-storage"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
PerWorkspace: &quantity15Gi,
},
},
},
},
{
name: "Update DevWorkspaceOperatorConfig with per-user strategy",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerUserPVCStorageStrategy,
PerUserStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "15Gi",
},
},
},
},
},
existedObjects: []runtime.Object{
&controllerv1alpha1.DevWorkspaceOperatorConfig{
ObjectMeta: metav1.ObjectMeta{
Name: devWorkspaceConfigName,
Namespace: "eclipse-che",
},
TypeMeta: metav1.TypeMeta{
Kind: "DevWorkspaceOperatorConfig",
APIVersion: controllerv1alpha1.GroupVersion.String(),
},
Config: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("default-storage-class"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
Common: &quantity10Gi,
},
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Workspace: &controllerv1alpha1.WorkspaceConfig{
StorageClassName: pointer.StringPtr("test-storage"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
Common: &quantity15Gi,
},
},
},
},
{
name: "Update populated DevWorkspaceOperatorConfig",
cheCluster: &chev2.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: chev2.CheClusterSpec{
DevEnvironments: chev2.CheClusterDevEnvironments{
Storage: chev2.WorkspaceStorage{
PvcStrategy: constants.PerUserPVCStorageStrategy,
PerUserStrategyPvcConfig: &chev2.PVC{
StorageClass: "test-storage",
ClaimSize: "15Gi",
},
},
},
},
},
existedObjects: []runtime.Object{
&controllerv1alpha1.DevWorkspaceOperatorConfig{
ObjectMeta: metav1.ObjectMeta{
Name: devWorkspaceConfigName,
Namespace: "eclipse-che",
},
TypeMeta: metav1.TypeMeta{
Kind: "DevWorkspaceOperatorConfig",
APIVersion: controllerv1alpha1.GroupVersion.String(),
},
Config: &controllerv1alpha1.OperatorConfiguration{
Routing: &controllerv1alpha1.RoutingConfig{
DefaultRoutingClass: "routing-class",
},
Workspace: &controllerv1alpha1.WorkspaceConfig{
ProgressTimeout: "10s",
},
},
},
},
expectedOperatorConfig: &controllerv1alpha1.OperatorConfiguration{
Routing: &controllerv1alpha1.RoutingConfig{
DefaultRoutingClass: "routing-class",
},
Workspace: &controllerv1alpha1.WorkspaceConfig{
ProgressTimeout: "10s",
StorageClassName: pointer.StringPtr("test-storage"),
DefaultStorageSize: &controllerv1alpha1.StorageSizes{
Common: &quantity15Gi,
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
deployContext := test.GetDeployContext(testCase.cheCluster, testCase.existedObjects)
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
devWorkspaceConfigReconciler := NewDevWorkspaceConfigReconciler()
_, _, err := devWorkspaceConfigReconciler.Reconcile(deployContext)
assert.NoError(t, err)
dwoc := &controllerv1alpha1.DevWorkspaceOperatorConfig{}
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: devWorkspaceConfigName, Namespace: testCase.cheCluster.Namespace}, dwoc)
assert.NoError(t, err)
assert.Equal(t, testCase.expectedOperatorConfig, dwoc.Config)
})
}
for _, testCase := range expectedErrorTestCases {
t.Run(testCase.name, func(t *testing.T) {
deployContext := test.GetDeployContext(testCase.cheCluster, testCase.existedObjects)
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
devWorkspaceConfigReconciler := NewDevWorkspaceConfigReconciler()
_, _, err := devWorkspaceConfigReconciler.Reconcile(deployContext)
assert.Error(t, err)
assert.Regexp(t, regexp.MustCompile(testCase.expectedErrorMessage), err.Error(), "error message must match")
})
}
}

View File

@ -132,14 +132,6 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
}
}
pvcStrategy := utils.GetValue(ctx.CheCluster.Spec.DevEnvironments.Storage.PvcStrategy, constants.DefaultPvcStrategy)
workspacePvcStorageClassName := ""
pvcClaimSize := constants.DefaultPvcClaimSize
if ctx.CheCluster.Spec.DevEnvironments.Storage.PerUserStrategyPvcConfig != nil {
pvcClaimSize = ctx.CheCluster.Spec.DevEnvironments.Storage.PerUserStrategyPvcConfig.ClaimSize
workspacePvcStorageClassName = ctx.CheCluster.Spec.DevEnvironments.Storage.PerUserStrategyPvcConfig.StorageClass
}
chePostgresHostName := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresHostName, constants.DefaultPostgresHostName)
chePostgresPort := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresPort, constants.DefaultPostgresPort)
chePostgresDb := utils.GetValue(ctx.CheCluster.Spec.Components.Database.PostgresDb, constants.DefaultPostgresDb)
@ -211,9 +203,6 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) (
CheInfraKubernetesServiceAccountName: cheWorkspaceServiceAccount,
CheInfraKubernetesUserClusterRoles: cheUserClusterRoleNames,
DefaultTargetNamespace: workspaceNamespaceDefault,
PvcStrategy: pvcStrategy,
PvcClaimSize: pvcClaimSize,
WorkspacePvcStorageClassName: workspacePvcStorageClassName,
TlsSupport: "true",
K8STrustCerts: "true",
CheLogLevel: cheLogLevel,