From af89b1609333f20a590f261360aff5cd13ea931f Mon Sep 17 00:00:00 2001 From: xbaran4 Date: Mon, 13 Sep 2021 12:30:54 +0200 Subject: [PATCH] feat: Added NamespaceProvisioner that creates user information secrets Signed-off-by: xbaran4 --- .../infrastructure/kubernetes/Constants.java | 13 ++ .../server/KubernetesNamespaceService.java | 12 +- .../namespace/NamespaceProvisioner.java | 135 ++++++++++++++++++ 3 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/NamespaceProvisioner.java diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Constants.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Constants.java index 66d0012638..c6401ff32a 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Constants.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/Constants.java @@ -46,5 +46,18 @@ public final class Constants { public static final String POD_STATUS_PHASE_FAILED = "Failed"; public static final String POD_STATUS_PHASE_SUCCEEDED = "Succeeded"; + /** Names for secrets with user information which are used with DevWorkspaces. */ + public static final String USER_PROFILE_SECRET_NAME = "user-profile"; + + public static final String USER_PREFERENCES_SECRET_NAME = "user-preferences"; + + /** DevWorkspace labels and annotations for mounting secrets and configmaps. */ + public static final String DEV_WORKSPACE_MOUNT_LABEL = + "controller.devfile.io/mount-to-devworkspace"; + + public static final String DEV_WORKSPACE_MOUNT_PATH_ANNOTATION = + "controller.devfile.io/mount-path"; + public static final String DEV_WORKSPACE_MOUNT_AS_ANNOTATION = "controller.devfile.io/mount-as"; + private Constants() {} } diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java index fd19ca736c..745ec17658 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/api/server/KubernetesNamespaceService.java @@ -27,12 +27,11 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import org.eclipse.che.api.core.rest.Service; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; -import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; -import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.dto.KubernetesNamespaceMetaDto; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; +import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.NamespaceProvisioner; /** @author Sergii Leshchenko */ @Api( @@ -43,10 +42,13 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesN public class KubernetesNamespaceService extends Service { private final KubernetesNamespaceFactory namespaceFactory; + private final NamespaceProvisioner namespaceProvisioner; @Inject - public KubernetesNamespaceService(KubernetesNamespaceFactory namespaceFactory) { + public KubernetesNamespaceService( + KubernetesNamespaceFactory namespaceFactory, NamespaceProvisioner namespaceProvisioner) { this.namespaceFactory = namespaceFactory; + this.namespaceProvisioner = namespaceProvisioner; } @GET @@ -82,9 +84,7 @@ public class KubernetesNamespaceService extends Service { message = "Internal server error occurred during namespace provisioning") }) public KubernetesNamespaceMetaDto provision() throws InfrastructureException { - return asDto( - namespaceFactory.provision( - new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject()))); + return asDto(namespaceProvisioner.provision()); } private KubernetesNamespaceMetaDto asDto(KubernetesNamespaceMeta kubernetesNamespaceMeta) { diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/NamespaceProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/NamespaceProvisioner.java new file mode 100644 index 0000000000..9a975a50d3 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/NamespaceProvisioner.java @@ -0,0 +1,135 @@ +/* + * 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 org.eclipse.che.workspace.infrastructure.kubernetes.namespace; + +import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.DEV_WORKSPACE_MOUNT_AS_ANNOTATION; +import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.DEV_WORKSPACE_MOUNT_LABEL; +import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.DEV_WORKSPACE_MOUNT_PATH_ANNOTATION; +import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.USER_PREFERENCES_SECRET_NAME; +import static org.eclipse.che.workspace.infrastructure.kubernetes.Constants.USER_PROFILE_SECRET_NAME; + +import io.fabric8.kubernetes.api.model.SecretBuilder; +import java.util.HashMap; +import java.util.Map; +import javax.inject.Inject; +import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.api.core.ServerException; +import org.eclipse.che.api.core.model.user.User; +import org.eclipse.che.api.core.notification.EventSubscriber; +import org.eclipse.che.api.user.server.PreferenceManager; +import org.eclipse.che.api.user.server.UserManager; +import org.eclipse.che.api.user.server.event.PostUserPersistedEvent; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.subject.Subject; +import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory; +import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NamespaceProvisioner implements EventSubscriber { + private static final Logger LOG = LoggerFactory.getLogger(NamespaceProvisioner.class); + private final KubernetesNamespaceFactory namespaceFactory; + private final PreferenceManager preferenceManager; + private final KubernetesClientFactory clientFactory; + private final UserManager userManager; + + @Inject + public NamespaceProvisioner( + KubernetesNamespaceFactory namespaceFactory, + KubernetesClientFactory clientFactory, + UserManager userManager, + PreferenceManager preferenceManager) { + this.namespaceFactory = namespaceFactory; + this.clientFactory = clientFactory; + this.userManager = userManager; + this.preferenceManager = preferenceManager; + } + + public KubernetesNamespaceMeta provision() throws InfrastructureException { + + Subject subject = EnvironmentContext.getCurrent().getSubject(); + KubernetesNamespaceMeta kubernetesNamespaceMeta = + namespaceFactory.provision(new NamespaceResolutionContext(subject)); + + try { + createOrUpdateSecrets(userManager.getById(subject.getUserId())); + } catch (NotFoundException | ServerException e) { + LOG.error("Could not find current user. Skipping creation of user information secrets.", e); + } catch (InfrastructureException e) { + LOG.error("There was a failure while creating user information secrets.", e); + } + + return kubernetesNamespaceMeta; + }; + + @Override + public void onEvent(PostUserPersistedEvent event) { + try { + createOrUpdateSecrets(event.getUser()); + } catch (InfrastructureException e) { + LOG.error("There was a failure while creating user information secrets.", e); + } + } + + private void createOrUpdateSecrets(User user) throws InfrastructureException { + + final Map userProfileData = new HashMap<>(); + userProfileData.put("id", user.getId()); + userProfileData.put("name", user.getName()); + userProfileData.put("email", user.getEmail()); + + String namespace = + namespaceFactory.evaluateNamespaceName( + new NamespaceResolutionContext(null, user.getId(), user.getName())); + + clientFactory + .create() + .secrets() + .inNamespace(namespace) + .withName(USER_PROFILE_SECRET_NAME) + .createOrReplace( + new SecretBuilder() + .addToData(userProfileData) + .withNewMetadata() + .addToLabels(DEV_WORKSPACE_MOUNT_LABEL, "true") + .addToAnnotations(DEV_WORKSPACE_MOUNT_AS_ANNOTATION, "file") + .addToAnnotations(DEV_WORKSPACE_MOUNT_PATH_ANNOTATION, "/config/user/profile") + .endMetadata() + .build()); + + Map preferences; + try { + preferences = preferenceManager.find(user.getId()); + } catch (ServerException e) { + LOG.error( + "Could not find user preferences. Skipping creation of user preferences secrets.", e); + return; + } + + clientFactory + .create() + .secrets() + .inNamespace(namespace) + .withName(USER_PREFERENCES_SECRET_NAME) + .createOrReplace( + new SecretBuilder() + .addToData(preferences) + .withNewMetadata() + .addToLabels(DEV_WORKSPACE_MOUNT_LABEL, "true") + .addToAnnotations(DEV_WORKSPACE_MOUNT_AS_ANNOTATION, "file") + .addToAnnotations(DEV_WORKSPACE_MOUNT_PATH_ANNOTATION, "/config/user/preferences") + .endMetadata() + .build()); + } +}