689 lines
21 KiB
Go
689 lines
21 KiB
Go
//
|
|
// Copyright (c) 2019-2023 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 usernamespace
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
containerbuild "github.com/eclipse-che/che-operator/pkg/deploy/container-build"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
|
|
"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/deploy/tls"
|
|
|
|
dwconstants "github.com/devfile/devworkspace-operator/pkg/constants"
|
|
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
|
|
chev2 "github.com/eclipse-che/che-operator/api/v2"
|
|
"github.com/eclipse-che/che-operator/controllers/che"
|
|
"github.com/eclipse-che/che-operator/controllers/devworkspace"
|
|
"github.com/eclipse-che/che-operator/controllers/devworkspace/defaults"
|
|
"github.com/eclipse-che/che-operator/pkg/deploy"
|
|
projectv1 "github.com/openshift/api/project/v1"
|
|
"github.com/sirupsen/logrus"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
|
)
|
|
|
|
const (
|
|
userSettingsComponentLabelValue = "user-settings"
|
|
// we're define these here because we're forced to use an older version
|
|
// of devworkspace operator as our dependency due to different go version
|
|
nodeSelectorAnnotation = "controller.devfile.io/node-selector"
|
|
podTolerationsAnnotation = "controller.devfile.io/pod-tolerations"
|
|
)
|
|
|
|
type CheUserNamespaceReconciler struct {
|
|
client client.Client
|
|
scheme *runtime.Scheme
|
|
namespaceCache namespaceCache
|
|
}
|
|
|
|
type eventRule struct {
|
|
check func(metav1.Object) bool
|
|
namespaces func(metav1.Object) []string
|
|
}
|
|
|
|
var _ reconcile.Reconciler = (*CheUserNamespaceReconciler)(nil)
|
|
|
|
func NewReconciler() *CheUserNamespaceReconciler {
|
|
return &CheUserNamespaceReconciler{namespaceCache: *NewNamespaceCache()}
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
r.scheme = mgr.GetScheme()
|
|
r.client = mgr.GetClient()
|
|
r.namespaceCache.client = r.client
|
|
|
|
var obj client.Object
|
|
if infrastructure.IsOpenShift() {
|
|
obj = &projectv1.Project{}
|
|
} else {
|
|
obj = &corev1.Namespace{}
|
|
}
|
|
|
|
ctx := context.Background()
|
|
bld := ctrl.NewControllerManagedBy(mgr).
|
|
For(obj).
|
|
Watches(&source.Kind{Type: &corev1.Secret{}}, r.watchRulesForSecrets(ctx)).
|
|
Watches(&source.Kind{Type: &corev1.ConfigMap{}}, r.watchRulesForConfigMaps(ctx)).
|
|
Watches(&source.Kind{Type: &chev2.CheCluster{}}, r.triggerAllNamespaces())
|
|
|
|
return bld.Complete(r)
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) watchRulesForSecrets(ctx context.Context) handler.EventHandler {
|
|
rules := r.commonRules(ctx, constants.DefaultSelfSignedCertificateSecretName)
|
|
return handler.EnqueueRequestsFromMapFunc(
|
|
handler.MapFunc(func(obj client.Object) []reconcile.Request {
|
|
return asReconcileRequestsForNamespaces(obj, rules)
|
|
}))
|
|
}
|
|
|
|
func asReconcileRequestsForNamespaces(obj metav1.Object, rules []eventRule) []reconcile.Request {
|
|
for _, r := range rules {
|
|
if r.check(obj) {
|
|
nss := r.namespaces(obj)
|
|
ret := make([]reconcile.Request, len(nss))
|
|
for i, n := range nss {
|
|
ret[i] = reconcile.Request{
|
|
NamespacedName: types.NamespacedName{
|
|
Name: n,
|
|
},
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
}
|
|
|
|
return []reconcile.Request{}
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) commonRules(ctx context.Context, namesInCheClusterNamespace ...string) []eventRule {
|
|
return []eventRule{
|
|
{
|
|
check: func(o metav1.Object) bool {
|
|
return isLabeledAsUserSettings(o) && r.isInManagedNamespace(ctx, o)
|
|
},
|
|
namespaces: func(o metav1.Object) []string { return []string{o.GetNamespace()} },
|
|
},
|
|
{
|
|
check: func(o metav1.Object) bool {
|
|
return r.hasNameAndIsCollocatedWithCheCluster(ctx, o, namesInCheClusterNamespace...)
|
|
},
|
|
namespaces: func(o metav1.Object) []string { return r.namespaceCache.GetAllKnownNamespaces() },
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) watchRulesForConfigMaps(ctx context.Context) handler.EventHandler {
|
|
rules := r.commonRules(ctx, tls.CheAllCACertsConfigMapName)
|
|
return handler.EnqueueRequestsFromMapFunc(
|
|
handler.MapFunc(func(obj client.Object) []reconcile.Request {
|
|
return asReconcileRequestsForNamespaces(obj, rules)
|
|
}))
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) hasNameAndIsCollocatedWithCheCluster(ctx context.Context, obj metav1.Object, names ...string) bool {
|
|
for _, n := range names {
|
|
if obj.GetName() == n && r.hasCheCluster(ctx, obj.GetNamespace()) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isLabeledAsUserSettings(obj metav1.Object) bool {
|
|
return obj.GetLabels()["app.kubernetes.io/component"] == userSettingsComponentLabelValue
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) isInManagedNamespace(ctx context.Context, obj metav1.Object) bool {
|
|
info, err := r.namespaceCache.GetNamespaceInfo(ctx, obj.GetNamespace())
|
|
return err == nil && info != nil && info.IsWorkspaceNamespace
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) triggerAllNamespaces() handler.EventHandler {
|
|
return handler.EnqueueRequestsFromMapFunc(
|
|
handler.MapFunc(func(obj client.Object) []reconcile.Request {
|
|
nss := r.namespaceCache.GetAllKnownNamespaces()
|
|
ret := make([]reconcile.Request, len(nss))
|
|
|
|
for _, ns := range nss {
|
|
ret = append(ret, reconcile.Request{
|
|
NamespacedName: types.NamespacedName{Name: ns},
|
|
})
|
|
}
|
|
|
|
return ret
|
|
}),
|
|
)
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) hasCheCluster(ctx context.Context, namespace string) bool {
|
|
list := chev2.CheClusterList{}
|
|
if err := r.client.List(ctx, &list, client.InNamespace(namespace)); err != nil {
|
|
return false
|
|
}
|
|
|
|
return len(list.Items) > 0
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
info, err := r.namespaceCache.ExamineNamespace(ctx, req.Name)
|
|
if err != nil {
|
|
logrus.Errorf("Failed to examine namespace %s for presence of Che user info labels: %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if info == nil || !info.IsWorkspaceNamespace {
|
|
// we're not handling this namespace
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
checluster := findManagingCheCluster(*info.CheCluster)
|
|
if checluster == nil {
|
|
return ctrl.Result{Requeue: true}, nil
|
|
}
|
|
|
|
// let's construct the deployContext to be able to use methods from v1 operator
|
|
deployContext := &chetypes.DeployContext{
|
|
CheCluster: checluster,
|
|
ClusterAPI: chetypes.ClusterAPI{
|
|
Client: r.client,
|
|
NonCachingClient: r.client,
|
|
DiscoveryClient: nil,
|
|
Scheme: r.scheme,
|
|
},
|
|
}
|
|
|
|
if err = r.reconcileSelfSignedCert(ctx, deployContext, req.Name, checluster); err != nil {
|
|
logrus.Errorf("Failed to reconcile self-signed certificate into namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileTrustedCerts(ctx, deployContext, req.Name, checluster); err != nil {
|
|
logrus.Errorf("Failed to reconcile trusted certificates into namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileProxySettings(ctx, req.Name, checluster, deployContext); err != nil {
|
|
logrus.Errorf("Failed to reconcile proxy settings into namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileGitTlsCertificate(ctx, req.Name, checluster, deployContext); err != nil {
|
|
logrus.Errorf("Failed to reconcile Che git TLS certificate into namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileIdleSettings(ctx, req.Name, checluster, deployContext); err != nil {
|
|
logrus.Errorf("Failed to reconcile idle settings into namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileNodeSelectorAndTolerations(ctx, req.Name, checluster, deployContext); err != nil {
|
|
logrus.Errorf("Failed to reconcile the workspace pod node selector and tolerations in namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
if err = r.reconcileSCCPrivileges(info.Username, req.Name, checluster, deployContext); err != nil {
|
|
logrus.Errorf("Failed to reconcile the SCC privileges in namespace '%s': %v", req.Name, err)
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
func findManagingCheCluster(key types.NamespacedName) *chev2.CheCluster {
|
|
instances := devworkspace.GetCurrentCheClusterInstances()
|
|
if len(instances) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if len(instances) == 1 {
|
|
for k, v := range instances {
|
|
if key.Name == "" || (key.Name == k.Name && key.Namespace == k.Namespace) {
|
|
return &v
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
ret, ok := instances[key]
|
|
|
|
if ok {
|
|
return &ret
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context, deployContext *chetypes.DeployContext, targetNs string, checluster *chev2.CheCluster) error {
|
|
if err := deleteLegacyObject("server-cert", &corev1.Secret{}, targetNs, checluster, deployContext); err != nil {
|
|
return err
|
|
}
|
|
targetCertName := prefixedName("server-cert")
|
|
|
|
delSecret := func() error {
|
|
_, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetCertName, Namespace: targetNs}, &corev1.Secret{})
|
|
return err
|
|
}
|
|
|
|
cheCert := &corev1.Secret{}
|
|
if err := r.client.Get(ctx, client.ObjectKey{Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: checluster.Namespace}, cheCert); err != nil {
|
|
if !errors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
// There is not self-signed cert in the namespace of the checluster, so we have nothing to copy around
|
|
return delSecret()
|
|
}
|
|
|
|
if _, ok := cheCert.Data["ca.crt"]; !ok {
|
|
// the secret doesn't contain the certificate. bail out.
|
|
return delSecret()
|
|
}
|
|
|
|
targetCert := &corev1.Secret{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Secret",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: targetCertName,
|
|
Namespace: targetNs,
|
|
Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
|
|
dwconstants.DevWorkspaceMountLabel: "true",
|
|
dwconstants.DevWorkspaceWatchSecretLabel: "true",
|
|
}),
|
|
Annotations: map[string]string{
|
|
dwconstants.DevWorkspaceMountAsAnnotation: "file",
|
|
dwconstants.DevWorkspaceMountPathAnnotation: "/tmp/che/secret/",
|
|
},
|
|
},
|
|
Data: map[string][]byte{
|
|
"ca.crt": cheCert.Data["ca.crt"],
|
|
},
|
|
Type: cheCert.Type,
|
|
Immutable: cheCert.Immutable,
|
|
}
|
|
|
|
_, err := deploy.Sync(deployContext, targetCert, deploy.SecretDiffOpts)
|
|
return err
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, deployContext *chetypes.DeployContext, targetNs string, checluster *chev2.CheCluster) error {
|
|
if err := deleteLegacyObject("trusted-ca-certs", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
|
|
return err
|
|
}
|
|
targetConfigMapName := prefixedName("trusted-ca-certs")
|
|
|
|
delConfigMap := func() error {
|
|
_, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetConfigMapName, Namespace: targetNs}, &corev1.ConfigMap{})
|
|
return err
|
|
}
|
|
|
|
sourceMap := &corev1.ConfigMap{}
|
|
if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheAllCACertsConfigMapName, Namespace: checluster.Namespace}, sourceMap); err != nil {
|
|
if !errors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
|
|
return delConfigMap()
|
|
}
|
|
|
|
targetMap := &corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: targetConfigMapName,
|
|
Namespace: targetNs,
|
|
Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
|
|
dwconstants.DevWorkspaceMountLabel: "true",
|
|
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
|
|
}),
|
|
Annotations: addToFirst(sourceMap.Annotations, map[string]string{
|
|
dwconstants.DevWorkspaceMountAsAnnotation: "file",
|
|
dwconstants.DevWorkspaceMountPathAnnotation: "/public-certs",
|
|
}),
|
|
},
|
|
Data: sourceMap.Data,
|
|
}
|
|
|
|
_, err := deploy.Sync(deployContext, targetMap, deploy.ConfigMapDiffOpts)
|
|
return err
|
|
}
|
|
|
|
func addToFirst(first map[string]string, second map[string]string) map[string]string {
|
|
if first == nil {
|
|
first = map[string]string{}
|
|
}
|
|
for k, v := range second {
|
|
first[k] = v
|
|
}
|
|
|
|
return first
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
if err := deleteLegacyObject("proxy-settings", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
|
|
return err
|
|
}
|
|
proxyConfig, err := che.GetProxyConfiguration(deployContext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if proxyConfig == nil {
|
|
return nil
|
|
}
|
|
|
|
proxySettings := map[string]string{}
|
|
if proxyConfig.HttpProxy != "" {
|
|
proxySettings["HTTP_PROXY"] = proxyConfig.HttpProxy
|
|
proxySettings["http_proxy"] = proxyConfig.HttpProxy
|
|
}
|
|
if proxyConfig.HttpsProxy != "" {
|
|
proxySettings["HTTPS_PROXY"] = proxyConfig.HttpsProxy
|
|
proxySettings["https_proxy"] = proxyConfig.HttpsProxy
|
|
}
|
|
if proxyConfig.NoProxy != "" {
|
|
proxySettings["NO_PROXY"] = proxyConfig.NoProxy
|
|
proxySettings["no_proxy"] = proxyConfig.NoProxy
|
|
}
|
|
|
|
key := client.ObjectKey{Name: prefixedName("proxy-settings"), Namespace: targetNs}
|
|
cfg := &corev1.ConfigMap{}
|
|
exists := true
|
|
if err := r.client.Get(ctx, key, cfg); err != nil {
|
|
if errors.IsNotFound(err) {
|
|
exists = false
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(proxySettings) == 0 {
|
|
if exists {
|
|
if err := r.client.Delete(ctx, cfg); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
requiredLabels := defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
|
|
dwconstants.DevWorkspaceMountLabel: "true",
|
|
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
|
|
})
|
|
requiredAnnos := map[string]string{
|
|
dwconstants.DevWorkspaceMountAsAnnotation: "env",
|
|
}
|
|
|
|
cfg = &corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: prefixedName("proxy-settings"),
|
|
Namespace: targetNs,
|
|
Labels: requiredLabels,
|
|
Annotations: requiredAnnos,
|
|
},
|
|
Data: proxySettings,
|
|
}
|
|
|
|
_, err = deploy.Sync(deployContext, cfg, deploy.ConfigMapDiffOpts)
|
|
return err
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileIdleSettings(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
|
|
if checluster.Spec.DevEnvironments.SecondsOfInactivityBeforeIdling == nil && checluster.Spec.DevEnvironments.SecondsOfRunBeforeIdling == nil {
|
|
return nil
|
|
}
|
|
configMapName := prefixedName("idle-settings")
|
|
cfg := &corev1.ConfigMap{}
|
|
|
|
requiredLabels := defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
|
|
dwconstants.DevWorkspaceMountLabel: "true",
|
|
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
|
|
})
|
|
requiredAnnos := map[string]string{
|
|
dwconstants.DevWorkspaceMountAsAnnotation: "env",
|
|
}
|
|
|
|
data := map[string]string{}
|
|
|
|
if checluster.Spec.DevEnvironments.SecondsOfInactivityBeforeIdling != nil {
|
|
data["SECONDS_OF_DW_INACTIVITY_BEFORE_IDLING"] = strconv.FormatInt(int64(*checluster.Spec.DevEnvironments.SecondsOfInactivityBeforeIdling), 10)
|
|
}
|
|
|
|
if checluster.Spec.DevEnvironments.SecondsOfRunBeforeIdling != nil {
|
|
data["SECONDS_OF_DW_RUN_BEFORE_IDLING"] = strconv.FormatInt(int64(*checluster.Spec.DevEnvironments.SecondsOfRunBeforeIdling), 10)
|
|
}
|
|
|
|
cfg = &corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configMapName,
|
|
Namespace: targetNs,
|
|
Labels: requiredLabels,
|
|
Annotations: requiredAnnos,
|
|
},
|
|
Data: data,
|
|
}
|
|
_, err := deploy.Sync(deployContext, cfg, deploy.ConfigMapDiffOpts)
|
|
return err
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileGitTlsCertificate(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
if err := deleteLegacyObject("git-tls-creds", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
|
|
return err
|
|
}
|
|
targetName := prefixedName("git-tls-creds")
|
|
delConfigMap := func() error {
|
|
_, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetName, Namespace: targetNs}, &corev1.ConfigMap{})
|
|
return err
|
|
}
|
|
|
|
if checluster.Spec.DevEnvironments.TrustedCerts == nil || checluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName == "" {
|
|
return delConfigMap()
|
|
}
|
|
|
|
gitCert := &corev1.ConfigMap{}
|
|
|
|
if err := deployContext.ClusterAPI.Client.Get(ctx, client.ObjectKey{Name: checluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, Namespace: checluster.Namespace}, gitCert); err != nil {
|
|
if !errors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
return delConfigMap()
|
|
}
|
|
|
|
if gitCert.Data["ca.crt"] == "" {
|
|
return delConfigMap()
|
|
}
|
|
|
|
target := corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: targetName,
|
|
Namespace: targetNs,
|
|
Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
|
|
dwconstants.DevWorkspaceGitTLSLabel: "true",
|
|
dwconstants.DevWorkspaceMountLabel: "true",
|
|
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
|
|
}),
|
|
},
|
|
Data: map[string]string{
|
|
"certificate": gitCert.Data["ca.crt"],
|
|
},
|
|
}
|
|
|
|
if gitCert.Data["githost"] != "" {
|
|
target.Data["host"] = gitCert.Data["githost"]
|
|
}
|
|
|
|
_, err := deploy.Sync(deployContext, &target, deploy.ConfigMapDiffOpts)
|
|
return err
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileNodeSelectorAndTolerations(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
ns := &corev1.Namespace{}
|
|
if err := r.client.Get(ctx, client.ObjectKey{Name: targetNs}, ns); err != nil {
|
|
return err
|
|
}
|
|
|
|
nodeSelector := ""
|
|
tolerations := ""
|
|
|
|
if len(checluster.Spec.DevEnvironments.NodeSelector) != 0 {
|
|
serialized, err := json.Marshal(checluster.Spec.DevEnvironments.NodeSelector)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nodeSelector = string(serialized)
|
|
}
|
|
|
|
if len(checluster.Spec.DevEnvironments.Tolerations) != 0 {
|
|
serialized, err := json.Marshal(checluster.Spec.DevEnvironments.Tolerations)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tolerations = string(serialized)
|
|
}
|
|
|
|
annos := ns.GetAnnotations()
|
|
if annos == nil {
|
|
annos = map[string]string{}
|
|
}
|
|
|
|
if len(nodeSelector) == 0 {
|
|
delete(annos, nodeSelectorAnnotation)
|
|
} else {
|
|
annos[nodeSelectorAnnotation] = nodeSelector
|
|
}
|
|
|
|
if len(tolerations) == 0 {
|
|
delete(annos, podTolerationsAnnotation)
|
|
} else {
|
|
annos[podTolerationsAnnotation] = tolerations
|
|
}
|
|
|
|
ns.SetAnnotations(annos)
|
|
|
|
return r.client.Update(ctx, ns)
|
|
}
|
|
|
|
func (r *CheUserNamespaceReconciler) reconcileSCCPrivileges(username string, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
delRoleBinding := func() error {
|
|
_, err := deploy.Delete(
|
|
deployContext,
|
|
types.NamespacedName{Name: containerbuild.GetUserSccRbacResourcesName(), Namespace: targetNs},
|
|
&rbacv1.RoleBinding{})
|
|
return err
|
|
}
|
|
|
|
if !checluster.IsContainerBuildCapabilitiesEnabled() {
|
|
return delRoleBinding()
|
|
}
|
|
|
|
if username == "" {
|
|
_ = delRoleBinding()
|
|
return fmt.Errorf("unknown user for %s namespace", targetNs)
|
|
}
|
|
|
|
rb := &rbacv1.RoleBinding{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "RoleBinding",
|
|
APIVersion: rbacv1.SchemeGroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: containerbuild.GetUserSccRbacResourcesName(),
|
|
Namespace: targetNs,
|
|
Labels: map[string]string{constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg},
|
|
},
|
|
RoleRef: rbacv1.RoleRef{
|
|
Name: containerbuild.GetUserSccRbacResourcesName(),
|
|
Kind: "ClusterRole",
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
},
|
|
Subjects: []rbacv1.Subject{
|
|
{
|
|
Kind: rbacv1.UserKind,
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
Name: username,
|
|
},
|
|
},
|
|
}
|
|
|
|
if _, err := deploy.Sync(deployContext, rb, deploy.RollBindingDiffOpts); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func prefixedName(name string) string {
|
|
return "che-" + name
|
|
}
|
|
|
|
// Deletes object with a legacy name to avoid mounting several ones under the same path
|
|
// See https://github.com/eclipse/che/issues/21385
|
|
func deleteLegacyObject(name string, objectMeta client.Object, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
|
|
legacyPrefixedName := checluster.Name + "-" + checluster.Namespace + "-" + name
|
|
key := client.ObjectKey{Name: legacyPrefixedName, Namespace: targetNs}
|
|
|
|
err := deployContext.ClusterAPI.Client.Get(context.TODO(), key, objectMeta)
|
|
if err != nil {
|
|
if errors.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
err = deployContext.ClusterAPI.Client.Delete(context.TODO(), objectMeta)
|
|
if err != nil {
|
|
if errors.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
logrus.Infof("Deleted legacy workspace object: %s name: %s, namespace: %s", deploy.GetObjectType(objectMeta), legacyPrefixedName, targetNs)
|
|
return nil
|
|
}
|