224 lines
8.4 KiB
Go
224 lines
8.4 KiB
Go
//
|
|
// Copyright (c) 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 devworkspace
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/eclipse/che-operator/pkg/deploy"
|
|
"github.com/eclipse/che-operator/pkg/util"
|
|
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
|
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
const (
|
|
DevWorkspaceNamespace = "devworkspace-controller"
|
|
DevWorkspaceWebhookName = "controller.devfile.io"
|
|
DevWorkspaceServiceAccount = "devworkspace-controller-serviceaccount"
|
|
DevWorkspaceDeploymentName = "devworkspace-controller-manager"
|
|
|
|
DevWorkspaceTemplates = "/tmp/devworkspace-operator/templates/deployment/openshift/objects"
|
|
DevWorkspaceServiceAccountFile = DevWorkspaceTemplates + "/devworkspace-controller-serviceaccount.ServiceAccount.yaml"
|
|
DevWorkspaceRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-leader-election-role.Role.yaml"
|
|
DevWorkspaceClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-role.ClusterRole.yaml"
|
|
DevWorkspaceProxyClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-proxy-role.ClusterRole.yaml"
|
|
DevWorkspaceViewWorkspacesClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-view-workspaces.ClusterRole.yaml"
|
|
DevWorkspaceEditWorkspacesClusterRoleFile = DevWorkspaceTemplates + "/devworkspace-controller-edit-workspaces.ClusterRole.yaml"
|
|
DevWorkspaceRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-leader-election-rolebinding.RoleBinding.yaml"
|
|
DevWorkspaceClusterRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-rolebinding.ClusterRoleBinding.yaml"
|
|
DevWorkspaceProxyClusterRoleBindingFile = DevWorkspaceTemplates + "/devworkspace-controller-proxy-rolebinding.ClusterRoleBinding.yaml"
|
|
DevWorkspaceWorkspaceRoutingCRDFile = DevWorkspaceTemplates + "/workspaceroutings.controller.devfile.io.CustomResourceDefinition.yaml"
|
|
DevWorkspaceTemplatesCRDFile = DevWorkspaceTemplates + "/devworkspacetemplates.workspace.devfile.io.CustomResourceDefinition.yaml"
|
|
DevWorkspaceComponentsCRDFile = DevWorkspaceTemplates + "/components.controller.devfile.io.CustomResourceDefinition.yaml"
|
|
DevWorkspaceCRDFile = DevWorkspaceTemplates + "/devworkspaces.workspace.devfile.io.CustomResourceDefinition.yaml"
|
|
DevWorkspaceConfigMapFile = DevWorkspaceTemplates + "/devworkspace-controller-configmap.ConfigMap.yaml"
|
|
DevWorkspaceDeploymentFile = DevWorkspaceTemplates + "/devworkspace-controller-manager.Deployment.yaml"
|
|
|
|
WebTerminalOperatorSubscriptionName = "web-terminal"
|
|
WebTerminalOperatorNamespace = "openshift-operators"
|
|
)
|
|
|
|
var (
|
|
// cachedObjects
|
|
cachedObj = make(map[string]metav1.Object)
|
|
syncItems = []func(*deploy.DeployContext) (bool, error){
|
|
createNamespace,
|
|
syncServiceAccount,
|
|
syncClusterRole,
|
|
syncProxyClusterRole,
|
|
syncEditWorkspacesClusterRole,
|
|
syncViewWorkspacesClusterRole,
|
|
syncRole,
|
|
syncRoleBinding,
|
|
syncClusterRoleBinding,
|
|
syncProxyClusterRoleBinding,
|
|
syncCRD,
|
|
syncComponentsCRD,
|
|
syncTemplatesCRD,
|
|
syncWorkspaceRoutingCRD,
|
|
syncConfigMap,
|
|
syncDeployment,
|
|
}
|
|
)
|
|
|
|
func ReconcileDevWorkspace(deployContext *deploy.DeployContext) (bool, error) {
|
|
if !util.IsOpenShift4 || !util.IsOAuthEnabled(deployContext.CheCluster) {
|
|
return true, nil
|
|
}
|
|
|
|
if !deployContext.CheCluster.Spec.DevWorkspace.Enable {
|
|
return true, nil
|
|
}
|
|
|
|
devWorkspaceWebhookExists, err := deploy.IsExists(
|
|
deployContext,
|
|
client.ObjectKey{Name: DevWorkspaceWebhookName},
|
|
&admissionregistrationv1.MutatingWebhookConfiguration{},
|
|
)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !devWorkspaceWebhookExists {
|
|
for _, syncItem := range syncItems {
|
|
done, err := syncItem(deployContext)
|
|
if !util.IsTestMode() {
|
|
if !done {
|
|
return false, err
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if err := checkWebTerminalSubscription(deployContext); err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func checkWebTerminalSubscription(deployContext *deploy.DeployContext) error {
|
|
subscription := &operatorsv1alpha1.Subscription{}
|
|
if err := deployContext.ClusterAPI.NonCachedClient.Get(
|
|
context.TODO(),
|
|
types.NamespacedName{
|
|
Name: WebTerminalOperatorSubscriptionName,
|
|
Namespace: WebTerminalOperatorNamespace,
|
|
},
|
|
subscription); err != nil {
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
return errors.New("A non matching version of the Dev Workspace operator is already installed")
|
|
}
|
|
|
|
func createNamespace(deployContext *deploy.DeployContext) (bool, error) {
|
|
namespace := &corev1.Namespace{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Namespace",
|
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: DevWorkspaceNamespace,
|
|
},
|
|
Spec: corev1.NamespaceSpec{},
|
|
}
|
|
|
|
return deploy.CreateIfNotExists(deployContext, namespace)
|
|
}
|
|
|
|
func syncServiceAccount(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceServiceAccountFile, &corev1.ServiceAccount{})
|
|
}
|
|
|
|
func syncRole(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceRoleFile, &rbacv1.Role{})
|
|
}
|
|
|
|
func syncRoleBinding(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceRoleBindingFile, &rbacv1.RoleBinding{})
|
|
}
|
|
|
|
func syncClusterRoleBinding(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceClusterRoleBindingFile, &rbacv1.ClusterRoleBinding{})
|
|
}
|
|
|
|
func syncProxyClusterRoleBinding(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceProxyClusterRoleBindingFile, &rbacv1.ClusterRoleBinding{})
|
|
}
|
|
|
|
func syncClusterRole(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceClusterRoleFile, &rbacv1.ClusterRole{})
|
|
}
|
|
|
|
func syncProxyClusterRole(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceProxyClusterRoleFile, &rbacv1.ClusterRole{})
|
|
}
|
|
|
|
func syncViewWorkspacesClusterRole(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceViewWorkspacesClusterRoleFile, &rbacv1.ClusterRole{})
|
|
}
|
|
|
|
func syncEditWorkspacesClusterRole(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceEditWorkspacesClusterRoleFile, &rbacv1.ClusterRole{})
|
|
}
|
|
|
|
func syncWorkspaceRoutingCRD(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceWorkspaceRoutingCRDFile, &apiextensionsv1.CustomResourceDefinition{})
|
|
}
|
|
|
|
func syncTemplatesCRD(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceTemplatesCRDFile, &apiextensionsv1.CustomResourceDefinition{})
|
|
}
|
|
|
|
func syncComponentsCRD(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceComponentsCRDFile, &apiextensionsv1.CustomResourceDefinition{})
|
|
}
|
|
|
|
func syncCRD(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceCRDFile, &apiextensionsv1.CustomResourceDefinition{})
|
|
}
|
|
|
|
func syncConfigMap(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceConfigMapFile, &corev1.ConfigMap{})
|
|
}
|
|
|
|
func syncDeployment(deployContext *deploy.DeployContext) (bool, error) {
|
|
return syncObject(deployContext, DevWorkspaceDeploymentFile, &appsv1.Deployment{})
|
|
}
|
|
|
|
func syncObject(deployContext *deploy.DeployContext, yamlFile string, obj interface{}) (bool, error) {
|
|
_, exists := cachedObj[yamlFile]
|
|
if !exists {
|
|
if err := util.ReadObject(yamlFile, obj); err != nil {
|
|
return false, err
|
|
}
|
|
cachedObj[yamlFile] = obj.(metav1.Object)
|
|
}
|
|
|
|
objectMeta := cachedObj[yamlFile]
|
|
return deploy.CreateIfNotExists(deployContext, objectMeta)
|
|
}
|