Merge pull request #117 from xbaran4/user-secrets
Propagate Che user informations and preferences to DevWorkspacespull/102/head
commit
738a663e72
|
|
@ -46,5 +46,13 @@ public final class Constants {
|
|||
public static final String POD_STATUS_PHASE_FAILED = "Failed";
|
||||
public static final String POD_STATUS_PHASE_SUCCEEDED = "Succeeded";
|
||||
|
||||
/** 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() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesDev
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.RemoveNamespaceOnWorkspaceRemove;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.PerWorkspacePVCStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.UniqueWorkspacePVCStrategy;
|
||||
|
|
@ -98,6 +101,11 @@ public class KubernetesInfraModule extends AbstractModule {
|
|||
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
|
||||
workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
|
||||
|
||||
Multibinder<NamespaceConfigurator> namespaceConfigurators =
|
||||
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
||||
|
||||
bind(KubernetesNamespaceService.class);
|
||||
|
||||
MapBinder<String, InternalEnvironmentFactory> factories =
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ 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.provision.NamespaceProvisioner;
|
||||
|
||||
/** @author Sergii Leshchenko */
|
||||
@Tag(name = "kubernetes-namespace", description = "Kubernetes REST API for working with Namespaces")
|
||||
|
|
@ -43,10 +44,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
|
||||
|
|
@ -88,7 +92,7 @@ public class KubernetesNamespaceService extends Service {
|
|||
})
|
||||
public KubernetesNamespaceMetaDto provision() throws InfrastructureException {
|
||||
return asDto(
|
||||
namespaceFactory.provision(
|
||||
namespaceProvisioner.provision(
|
||||
new NamespaceResolutionContext(EnvironmentContext.getCurrent().getSubject())));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ import org.eclipse.che.api.core.model.workspace.Workspace;
|
|||
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
|
||||
import org.eclipse.che.api.user.server.PreferenceManager;
|
||||
import org.eclipse.che.api.user.server.UserManager;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
|
|
@ -255,7 +254,7 @@ public class KubernetesNamespaceFactory {
|
|||
* @return optional with kubernetes namespace meta
|
||||
* @throws InfrastructureException when any error occurs during namespace fetching
|
||||
*/
|
||||
protected Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||
throws InfrastructureException {
|
||||
try {
|
||||
Namespace namespace = clientFactory.create().namespaces().withName(name).get();
|
||||
|
|
@ -372,21 +371,6 @@ public class KubernetesNamespaceFactory {
|
|||
return namespace;
|
||||
}
|
||||
|
||||
public KubernetesNamespaceMeta provision(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
KubernetesNamespace namespace =
|
||||
getOrCreate(
|
||||
new RuntimeIdentityImpl(
|
||||
null,
|
||||
null,
|
||||
namespaceResolutionContext.getUserId(),
|
||||
evaluateNamespaceName(namespaceResolutionContext)));
|
||||
|
||||
return fetchNamespace(namespace.getName())
|
||||
.orElseThrow(
|
||||
() -> new InfrastructureException("Not able to find namespace " + namespace.getName()));
|
||||
}
|
||||
|
||||
public KubernetesNamespace get(RuntimeIdentity identity) throws InfrastructureException {
|
||||
String workspaceId = identity.getWorkspaceId();
|
||||
String namespaceName = identity.getInfrastructureNamespace();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.configurator;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.NamespaceProvisioner;
|
||||
|
||||
/**
|
||||
* Configures user's namespace after provisioning in {@link NamespaceProvisioner} with whatever is
|
||||
* needed. Such as creating user profile and preferences {@link Secret} in user namespace.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
public interface NamespaceConfigurator {
|
||||
|
||||
/**
|
||||
* Configures user's namespace after provisioning.
|
||||
*
|
||||
* @param namespaceResolutionContext users namespace context
|
||||
* @throws InfrastructureException when any error occurs
|
||||
*/
|
||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException;
|
||||
}
|
||||
|
|
@ -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.configurator;
|
||||
|
||||
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 com.google.common.annotations.VisibleForTesting;
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import java.util.Base64;
|
||||
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.user.server.PreferenceManager;
|
||||
import org.eclipse.che.api.user.server.UserManager;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
|
||||
/**
|
||||
* Creates {@link Secret} with user preferences. This serves as a way for DevWorkspaces to acquire
|
||||
* information about the user.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
public class UserPreferencesConfigurator implements NamespaceConfigurator {
|
||||
private static final String USER_PREFERENCES_SECRET_NAME = "user-preferences";
|
||||
private static final String USER_PREFERENCES_SECRET_MOUNT_PATH = "/config/user/preferences";
|
||||
private static final int PREFERENCE_NAME_MAX_LENGTH = 253;
|
||||
|
||||
private final KubernetesNamespaceFactory namespaceFactory;
|
||||
private final KubernetesClientFactory clientFactory;
|
||||
private final UserManager userManager;
|
||||
private final PreferenceManager preferenceManager;
|
||||
|
||||
@Inject
|
||||
public UserPreferencesConfigurator(
|
||||
KubernetesNamespaceFactory namespaceFactory,
|
||||
KubernetesClientFactory clientFactory,
|
||||
UserManager userManager,
|
||||
PreferenceManager preferenceManager) {
|
||||
this.namespaceFactory = namespaceFactory;
|
||||
this.clientFactory = clientFactory;
|
||||
this.userManager = userManager;
|
||||
this.preferenceManager = preferenceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
Secret userPreferencesSecret = preparePreferencesSecret(namespaceResolutionContext);
|
||||
String namespace = namespaceFactory.evaluateNamespaceName(namespaceResolutionContext);
|
||||
|
||||
try {
|
||||
clientFactory
|
||||
.create()
|
||||
.secrets()
|
||||
.inNamespace(namespace)
|
||||
.createOrReplace(userPreferencesSecret);
|
||||
} catch (KubernetesClientException e) {
|
||||
throw new InfrastructureException(
|
||||
"Error occurred while trying to create user preferences secret.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Secret preparePreferencesSecret(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
Base64.Encoder enc = Base64.getEncoder();
|
||||
User user;
|
||||
Map<String, String> preferences;
|
||||
|
||||
try {
|
||||
user = userManager.getById(namespaceResolutionContext.getUserId());
|
||||
preferences = preferenceManager.find(user.getId());
|
||||
} catch (NotFoundException | ServerException e) {
|
||||
throw new InfrastructureException(
|
||||
String.format(
|
||||
"Preferences of user with id:%s cannot be retrieved.",
|
||||
namespaceResolutionContext.getUserId()),
|
||||
e);
|
||||
}
|
||||
|
||||
if (preferences == null || preferences.isEmpty()) {
|
||||
throw new InfrastructureException(
|
||||
String.format(
|
||||
"Preferences of user with id:%s are empty. Cannot create user preferences secrets.",
|
||||
namespaceResolutionContext.getUserId()));
|
||||
}
|
||||
|
||||
Map<String, String> preferencesEncoded = new HashMap<>();
|
||||
preferences.forEach(
|
||||
(key, value) ->
|
||||
preferencesEncoded.put(
|
||||
normalizePreferenceName(key), enc.encodeToString(value.getBytes())));
|
||||
return new SecretBuilder()
|
||||
.addToData(preferencesEncoded)
|
||||
.withNewMetadata()
|
||||
.withName(USER_PREFERENCES_SECRET_NAME)
|
||||
.addToLabels(DEV_WORKSPACE_MOUNT_LABEL, "true")
|
||||
.addToAnnotations(DEV_WORKSPACE_MOUNT_AS_ANNOTATION, "file")
|
||||
.addToAnnotations(DEV_WORKSPACE_MOUNT_PATH_ANNOTATION, USER_PREFERENCES_SECRET_MOUNT_PATH)
|
||||
.endMetadata()
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some preferences names are not compatible with k8s restrictions on key field in secret. The
|
||||
* keys of data must consist of alphanumeric characters, -, _ or . This method replaces illegal
|
||||
* characters with -
|
||||
*
|
||||
* @param name original preference name
|
||||
* @return k8s compatible preference name used as a key field in Secret
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String normalizePreferenceName(String name) {
|
||||
name = name.replaceAll("[^-._a-zA-Z0-9]+", "-").replaceAll("-+", "-");
|
||||
return name.substring(0, Math.min(name.length(), PREFERENCE_NAME_MAX_LENGTH));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.configurator;
|
||||
|
||||
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 io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import java.util.Base64;
|
||||
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.user.server.UserManager;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
|
||||
/**
|
||||
* Creates {@link Secret} with user profile information such as his id, name and email. This serves
|
||||
* as a way for DevWorkspaces to acquire information about the user.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
public class UserProfileConfigurator implements NamespaceConfigurator {
|
||||
private static final String USER_PROFILE_SECRET_NAME = "user-profile";
|
||||
private static final String USER_PROFILE_SECRET_MOUNT_PATH = "/config/user/profile";
|
||||
|
||||
private final KubernetesNamespaceFactory namespaceFactory;
|
||||
private final KubernetesClientFactory clientFactory;
|
||||
private final UserManager userManager;
|
||||
|
||||
@Inject
|
||||
public UserProfileConfigurator(
|
||||
KubernetesNamespaceFactory namespaceFactory,
|
||||
KubernetesClientFactory clientFactory,
|
||||
UserManager userManager) {
|
||||
this.namespaceFactory = namespaceFactory;
|
||||
this.clientFactory = clientFactory;
|
||||
this.userManager = userManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
Secret userProfileSecret = prepareProfileSecret(namespaceResolutionContext);
|
||||
String namespace = namespaceFactory.evaluateNamespaceName(namespaceResolutionContext);
|
||||
try {
|
||||
clientFactory.create().secrets().inNamespace(namespace).createOrReplace(userProfileSecret);
|
||||
} catch (KubernetesClientException e) {
|
||||
throw new InfrastructureException(
|
||||
"Error occurred while trying to create user profile secret.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Secret prepareProfileSecret(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
User user;
|
||||
try {
|
||||
user = userManager.getById(namespaceResolutionContext.getUserId());
|
||||
} catch (NotFoundException | ServerException e) {
|
||||
throw new InfrastructureException(
|
||||
String.format(
|
||||
"Could not find current user with id:%s.", namespaceResolutionContext.getUserId()),
|
||||
e);
|
||||
}
|
||||
|
||||
Base64.Encoder enc = Base64.getEncoder();
|
||||
final Map<String, String> userProfileData = new HashMap<>();
|
||||
userProfileData.put("id", enc.encodeToString(user.getId().getBytes()));
|
||||
userProfileData.put("name", enc.encodeToString(user.getName().getBytes()));
|
||||
userProfileData.put("email", enc.encodeToString(user.getEmail().getBytes()));
|
||||
|
||||
return new SecretBuilder()
|
||||
.addToData(userProfileData)
|
||||
.withNewMetadata()
|
||||
.withName(USER_PROFILE_SECRET_NAME)
|
||||
.addToLabels(DEV_WORKSPACE_MOUNT_LABEL, "true")
|
||||
.addToAnnotations(DEV_WORKSPACE_MOUNT_AS_ANNOTATION, "file")
|
||||
.addToAnnotations(DEV_WORKSPACE_MOUNT_PATH_ANNOTATION, USER_PROFILE_SECRET_MOUNT_PATH)
|
||||
.endMetadata()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.provision;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Namespace;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.RuntimeIdentityImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespace;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||
|
||||
/**
|
||||
* Provisions the k8s {@link Namespace}. After provisioning, configures the namespace through {@link
|
||||
* NamespaceConfigurator}.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
public class NamespaceProvisioner {
|
||||
private final KubernetesNamespaceFactory namespaceFactory;
|
||||
private final Set<NamespaceConfigurator> namespaceConfigurators;
|
||||
|
||||
@Inject
|
||||
public NamespaceProvisioner(
|
||||
KubernetesNamespaceFactory namespaceFactory,
|
||||
Set<NamespaceConfigurator> namespaceConfigurators) {
|
||||
this.namespaceFactory = namespaceFactory;
|
||||
this.namespaceConfigurators = namespaceConfigurators;
|
||||
}
|
||||
|
||||
/** Tests for this method are in KubernetesFactoryTest. */
|
||||
public KubernetesNamespaceMeta provision(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
KubernetesNamespace namespace =
|
||||
namespaceFactory.getOrCreate(
|
||||
new RuntimeIdentityImpl(
|
||||
null,
|
||||
null,
|
||||
namespaceResolutionContext.getUserId(),
|
||||
namespaceFactory.evaluateNamespaceName(namespaceResolutionContext)));
|
||||
|
||||
KubernetesNamespaceMeta namespaceMeta =
|
||||
namespaceFactory
|
||||
.fetchNamespace(namespace.getName())
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new InfrastructureException(
|
||||
"Not able to find namespace " + namespace.getName()));
|
||||
configureNamespace(namespaceResolutionContext);
|
||||
return namespaceMeta;
|
||||
}
|
||||
|
||||
private void configureNamespace(NamespaceResolutionContext namespaceResolutionContext)
|
||||
throws InfrastructureException {
|
||||
for (NamespaceConfigurator configurator : namespaceConfigurators) {
|
||||
configurator.configure(namespaceResolutionContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ import org.eclipse.che.dto.server.DtoFactory;
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
|
||||
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.provision.NamespaceProvisioner;
|
||||
import org.everrest.assured.EverrestJetty;
|
||||
import org.everrest.core.Filter;
|
||||
import org.everrest.core.GenericContainerRequest;
|
||||
|
|
@ -67,6 +68,7 @@ public class KubernetesNamespaceServiceTest {
|
|||
private CheJsonProvider jsonProvider = new CheJsonProvider(Collections.emptySet());
|
||||
|
||||
@Mock private KubernetesNamespaceFactory namespaceFactory;
|
||||
@Mock private NamespaceProvisioner namespaceProvisioner;
|
||||
|
||||
@InjectMocks private KubernetesNamespaceService service;
|
||||
|
||||
|
|
@ -98,7 +100,7 @@ public class KubernetesNamespaceServiceTest {
|
|||
KubernetesNamespaceMetaImpl namespaceMeta =
|
||||
new KubernetesNamespaceMetaImpl(
|
||||
"ws-namespace", ImmutableMap.of("phase", "active", "default", "true"));
|
||||
when(namespaceFactory.provision(any(NamespaceResolutionContext.class)))
|
||||
when(namespaceProvisioner.provision(any(NamespaceResolutionContext.class)))
|
||||
.thenReturn(namespaceMeta);
|
||||
// when
|
||||
final Response response =
|
||||
|
|
@ -113,7 +115,7 @@ public class KubernetesNamespaceServiceTest {
|
|||
KubernetesNamespaceMetaDto actual = unwrapDto(response, KubernetesNamespaceMetaDto.class);
|
||||
assertEquals(actual.getName(), namespaceMeta.getName());
|
||||
assertEquals(actual.getAttributes(), namespaceMeta.getAttributes());
|
||||
verify(namespaceFactory).provision(any(NamespaceResolutionContext.class));
|
||||
verify(namespaceProvisioner).provision(any(NamespaceResolutionContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -122,7 +124,7 @@ public class KubernetesNamespaceServiceTest {
|
|||
KubernetesNamespaceMetaImpl namespaceMeta =
|
||||
new KubernetesNamespaceMetaImpl(
|
||||
"ws-namespace", ImmutableMap.of("phase", "active", "default", "true"));
|
||||
when(namespaceFactory.provision(any(NamespaceResolutionContext.class)))
|
||||
when(namespaceProvisioner.provision(any(NamespaceResolutionContext.class)))
|
||||
.thenReturn(namespaceMeta);
|
||||
// when
|
||||
final Response response =
|
||||
|
|
@ -136,7 +138,7 @@ public class KubernetesNamespaceServiceTest {
|
|||
assertEquals(response.getStatusCode(), 200);
|
||||
ArgumentCaptor<NamespaceResolutionContext> captor =
|
||||
ArgumentCaptor.forClass(NamespaceResolutionContext.class);
|
||||
verify(namespaceFactory).provision(captor.capture());
|
||||
verify(namespaceProvisioner).provision(captor.capture());
|
||||
NamespaceResolutionContext actualContext = captor.getValue();
|
||||
assertEquals(actualContext.getUserId(), SUBJECT.getUserId());
|
||||
assertEquals(actualContext.getUserName(), SUBJECT.getUserName());
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_INFRASTRUCTURE_NAMESPACE_ATTRIBUTE;
|
||||
import static org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta.DEFAULT_ATTRIBUTE;
|
||||
|
|
@ -86,6 +87,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesCl
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.provision.NamespaceProvisioner;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
|
|
@ -1248,7 +1250,7 @@ public class KubernetesNamespaceFactoryTest {
|
|||
// when
|
||||
NamespaceResolutionContext context =
|
||||
new NamespaceResolutionContext("workspace123", "user123", "jondoe");
|
||||
KubernetesNamespaceMeta actual = namespaceFactory.provision(context);
|
||||
KubernetesNamespaceMeta actual = testProvisioning(context);
|
||||
|
||||
// then
|
||||
assertEquals(actual.getName(), "jondoe-che");
|
||||
|
|
@ -1288,7 +1290,7 @@ public class KubernetesNamespaceFactoryTest {
|
|||
// when
|
||||
NamespaceResolutionContext context =
|
||||
new NamespaceResolutionContext("workspace123", "user123", "jondoe");
|
||||
namespaceFactory.provision(context);
|
||||
testProvisioning(context);
|
||||
|
||||
// then
|
||||
fail("should not reach this point since exception has to be thrown");
|
||||
|
|
@ -1329,7 +1331,8 @@ public class KubernetesNamespaceFactoryTest {
|
|||
// when
|
||||
NamespaceResolutionContext context =
|
||||
new NamespaceResolutionContext("workspace123", "user123", "jondoe");
|
||||
namespaceFactory.provision(context);
|
||||
|
||||
testProvisioning(context);
|
||||
|
||||
// then
|
||||
fail("should not reach this point since exception has to be thrown");
|
||||
|
|
@ -1535,4 +1538,9 @@ public class KubernetesNamespaceFactoryTest {
|
|||
.endStatus()
|
||||
.build();
|
||||
}
|
||||
|
||||
private KubernetesNamespaceMeta testProvisioning(NamespaceResolutionContext context)
|
||||
throws InfrastructureException {
|
||||
return new NamespaceProvisioner(namespaceFactory, emptySet()).provision(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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.configurator;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.user.server.PreferenceManager;
|
||||
import org.eclipse.che.api.user.server.UserManager;
|
||||
import org.eclipse.che.api.user.server.model.impl.UserImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserPreferencesConfigurator}.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class UserPreferencesConfiguratorTest {
|
||||
|
||||
private static final String USER_ID = "user-id";
|
||||
private static final String USER_NAME = "user-name";
|
||||
private static final String USER_EMAIL = "user-email";
|
||||
private static final String USER_NAMESPACE = "user-namespace";
|
||||
|
||||
@Mock private KubernetesNamespaceFactory namespaceFactory;
|
||||
@Mock private KubernetesClientFactory clientFactory;
|
||||
@Mock private UserManager userManager;
|
||||
@Mock private PreferenceManager preferenceManager;
|
||||
|
||||
@InjectMocks private UserPreferencesConfigurator userPreferencesConfigurator;
|
||||
|
||||
private KubernetesServer kubernetesServer;
|
||||
private NamespaceResolutionContext context;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws InfrastructureException, NotFoundException, ServerException {
|
||||
context = new NamespaceResolutionContext(null, USER_ID, USER_NAME);
|
||||
kubernetesServer = new KubernetesServer(true, true);
|
||||
kubernetesServer.before();
|
||||
|
||||
Map<String, String> preferences = new HashMap<>();
|
||||
preferences.put("preference-name", "preference");
|
||||
|
||||
lenient()
|
||||
.when(userManager.getById(USER_ID))
|
||||
.thenReturn(new UserImpl(USER_ID, USER_EMAIL, USER_NAME));
|
||||
lenient().when(namespaceFactory.evaluateNamespaceName(any())).thenReturn(USER_NAMESPACE);
|
||||
lenient().when(clientFactory.create()).thenReturn(kubernetesServer.getClient());
|
||||
lenient().when(preferenceManager.find(USER_ID)).thenReturn(preferences);
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void cleanUp() {
|
||||
kubernetesServer.after();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreatePreferencesSecret() throws InfrastructureException {
|
||||
userPreferencesConfigurator.configure(context);
|
||||
List<Secret> secrets =
|
||||
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
||||
assertEquals(secrets.size(), 1);
|
||||
assertEquals(secrets.get(0).getMetadata().getName(), "user-preferences");
|
||||
}
|
||||
|
||||
@Test(
|
||||
expectedExceptions = InfrastructureException.class,
|
||||
expectedExceptionsMessageRegExp =
|
||||
"Preferences of user with id:" + USER_ID + " cannot be retrieved.")
|
||||
public void shouldNotCreateSecretOnException() throws ServerException, InfrastructureException {
|
||||
when(preferenceManager.find(USER_ID)).thenThrow(new ServerException("test exception"));
|
||||
userPreferencesConfigurator.configure(context);
|
||||
fail("InfrastructureException should have been thrown.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNormalizePreferenceName() {
|
||||
assertEquals(
|
||||
userPreferencesConfigurator.normalizePreferenceName("codename:bond"), "codename-bond");
|
||||
assertEquals(userPreferencesConfigurator.normalizePreferenceName("some--:pref"), "some-pref");
|
||||
assertEquals(
|
||||
userPreferencesConfigurator.normalizePreferenceName("pref[name].sub"), "pref-name-.sub");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldKeepPreferenceName() {
|
||||
assertEquals(
|
||||
userPreferencesConfigurator.normalizePreferenceName("codename.bond"), "codename.bond");
|
||||
assertEquals(userPreferencesConfigurator.normalizePreferenceName("pref_name"), "pref_name");
|
||||
assertEquals(
|
||||
userPreferencesConfigurator.normalizePreferenceName("some-name.over_rainbow"),
|
||||
"some-name.over_rainbow");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.configurator;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||
import java.util.List;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.user.server.UserManager;
|
||||
import org.eclipse.che.api.user.server.model.impl.UserImpl;
|
||||
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
|
||||
import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserProfileConfigurator}.
|
||||
*
|
||||
* @author Pavol Baran
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class UserProfileConfiguratorTest {
|
||||
private static final String USER_ID = "user-id";
|
||||
private static final String USER_NAME = "user-name";
|
||||
private static final String USER_EMAIL = "user-email";
|
||||
private static final String USER_NAMESPACE = "user-namespace";
|
||||
|
||||
@Mock private KubernetesNamespaceFactory namespaceFactory;
|
||||
@Mock private KubernetesClientFactory clientFactory;
|
||||
@Mock private UserManager userManager;
|
||||
|
||||
@InjectMocks private UserProfileConfigurator userProfileConfigurator;
|
||||
|
||||
private KubernetesServer kubernetesServer;
|
||||
private NamespaceResolutionContext context;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws InfrastructureException, NotFoundException, ServerException {
|
||||
context = new NamespaceResolutionContext(null, USER_ID, USER_NAME);
|
||||
kubernetesServer = new KubernetesServer(true, true);
|
||||
kubernetesServer.before();
|
||||
|
||||
when(userManager.getById(USER_ID)).thenReturn(new UserImpl(USER_ID, USER_EMAIL, USER_NAME));
|
||||
when(namespaceFactory.evaluateNamespaceName(any())).thenReturn(USER_NAMESPACE);
|
||||
when(clientFactory.create()).thenReturn(kubernetesServer.getClient());
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void cleanUp() {
|
||||
kubernetesServer.after();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateProfileSecret() throws InfrastructureException {
|
||||
userProfileConfigurator.configure(context);
|
||||
List<Secret> secrets =
|
||||
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
|
||||
assertEquals(secrets.size(), 1);
|
||||
assertEquals(secrets.get(0).getMetadata().getName(), "user-profile");
|
||||
}
|
||||
|
||||
@Test(
|
||||
expectedExceptions = InfrastructureException.class,
|
||||
expectedExceptionsMessageRegExp = "Could not find current user with id:" + USER_ID + ".")
|
||||
public void shouldNotCreateSecretOnException()
|
||||
throws NotFoundException, ServerException, InfrastructureException {
|
||||
when(userManager.getById(USER_ID)).thenThrow(new ServerException("test exception"));
|
||||
userProfileConfigurator.configure(context);
|
||||
fail("InfrastructureException should have been thrown.");
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,9 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesDev
|
|||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserPreferencesConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.UserProfileConfigurator;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.CommonPVCStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.PerWorkspacePVCStrategy;
|
||||
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.pvc.UniqueWorkspacePVCStrategy;
|
||||
|
|
@ -110,6 +113,11 @@ public class OpenShiftInfraModule extends AbstractModule {
|
|||
workspaceAttributeValidators.addBinding().to(K8sInfraNamespaceWsAttributeValidator.class);
|
||||
workspaceAttributeValidators.addBinding().to(AsyncStorageModeValidator.class);
|
||||
|
||||
Multibinder<NamespaceConfigurator> namespaceConfigurators =
|
||||
Multibinder.newSetBinder(binder(), NamespaceConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
|
||||
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
|
||||
|
||||
bind(KubernetesNamespaceService.class);
|
||||
|
||||
MapBinder<String, InternalEnvironmentFactory> factories =
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||
public Optional<KubernetesNamespaceMeta> fetchNamespace(String name)
|
||||
throws InfrastructureException {
|
||||
return fetchNamespaceObject(name).map(this::asNamespaceMeta);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue