test: added tests for exception handling in NamespaceProvisioner

Signed-off-by: xbaran4 <pbaran@redhat.com>
pull/117/head
xbaran4 2021-09-20 13:23:13 +02:00
parent bd46d1c218
commit 3d3cbee7e1
2 changed files with 156 additions and 28 deletions

View File

@ -22,6 +22,7 @@ import io.fabric8.kubernetes.client.KubernetesClientException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
@ -46,9 +47,9 @@ public class NamespaceProvisioner implements EventSubscriber<PostUserPersistedEv
private static final String USER_PREFERENCES_SECRET_MOUNT_PATH = "/config/user/preferences";
private final KubernetesNamespaceFactory namespaceFactory;
private final PreferenceManager preferenceManager;
private final KubernetesClientFactory clientFactory;
private final UserManager userManager;
private final PreferenceManager preferenceManager;
@Inject
public NamespaceProvisioner(
@ -80,24 +81,32 @@ public class NamespaceProvisioner implements EventSubscriber<PostUserPersistedEv
createOrUpdateSecrets(event.getUser());
}
/**
* Creates k8s user profile and user preferences k8s secrets. This serves as a way for
* DevWorkspaces to acquire information about the user.
*
* @param user from its data are the secrets created
*/
private void createOrUpdateSecrets(User user) {
Secret userProfileSecret = prepareProfileSecret(user);
Secret userPreferencesSecret = preparePreferencesSecret(user);
Optional<Secret> userPreferencesSecret = preparePreferencesSecret(user);
try {
String namespace =
namespaceFactory.evaluateNamespaceName(
new NamespaceResolutionContext(null, user.getId(), user.getName()));
namespaceFactory.evaluateNamespaceName(
new NamespaceResolutionContext(null, user.getId(), user.getName()));
clientFactory.create().secrets().inNamespace(namespace).createOrReplace(userProfileSecret);
if (userPreferencesSecret != null) {
clientFactory.create().secrets().inNamespace(namespace).createOrReplace(userPreferencesSecret);
if (userPreferencesSecret.isPresent()) {
clientFactory
.create()
.secrets()
.inNamespace(namespace)
.createOrReplace(userPreferencesSecret.get());
}
} catch (InfrastructureException | KubernetesClientException e) {
LOG.error("There was a failure while creating user information secrets.", e);
}
}
private Secret prepareProfileSecret(User user) {
@ -118,7 +127,7 @@ public class NamespaceProvisioner implements EventSubscriber<PostUserPersistedEv
.build();
}
private Secret preparePreferencesSecret(User user) {
private Optional<Secret> preparePreferencesSecret(User user) {
Base64.Encoder enc = Base64.getEncoder();
Map<String, String> preferences;
try {
@ -126,39 +135,42 @@ public class NamespaceProvisioner implements EventSubscriber<PostUserPersistedEv
} catch (ServerException e) {
LOG.error(
"Could not find user preferences. Skipping creation of user preferences secrets.", e);
return null;
return Optional.empty();
}
if (preferences == null || preferences.isEmpty()){
LOG.error(
"User preferences are empty. Skipping creation of user preferences secrets.");
return null;
if (preferences == null || preferences.isEmpty()) {
LOG.error("User preferences are empty. Skipping creation of user preferences secrets.");
return Optional.empty();
}
Map<String, String> preferencesEncoded = new HashMap<>();
preferences.forEach(
(key, value) ->
preferencesEncoded.put(normalizeDataKey(key), enc.encodeToString(value.getBytes())));
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();
return Optional.of(
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. This
* method replaces illegal characters with "-" (dash).
* 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 normalizeDataKey(String name) {
String normalizePreferenceName(String name) {
return name.replaceAll("[^-._a-zA-Z0-9]+", "-").replaceAll("-+", "-");
}
}

View File

@ -11,10 +11,126 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.provision;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.Collections;
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.api.server.impls.KubernetesNamespaceMetaImpl;
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.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class NamespaceProvisionerTest {
@Mock private KubernetesNamespaceFactory namespaceFactory;
@Mock private KubernetesClientFactory clientFactory;
@Mock private UserManager userManager;
@Mock private PreferenceManager preferenceManager;
@InjectMocks private NamespaceProvisioner namespaceProvisioner;
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";
private KubernetesServer kubernetesServer;
@BeforeMethod
public void setUp() throws InfrastructureException, NotFoundException, ServerException {
kubernetesServer = new KubernetesServer(true, true);
kubernetesServer.before();
Map<String, String> preferences = new HashMap<>();
preferences.put("preference-name", "preference");
lenient().when(clientFactory.create()).thenReturn(kubernetesServer.getClient());
lenient()
.when(namespaceFactory.provision(any()))
.thenReturn(new KubernetesNamespaceMetaImpl(USER_NAMESPACE, Collections.emptyMap()));
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(preferenceManager.find(USER_ID)).thenReturn(preferences);
}
@AfterMethod
public void cleanUp() {
kubernetesServer.after();
}
@Test
public void normalizePreferenceNameTest() {}
public void shouldCreateSecretsOnNamespaceProvision() throws InfrastructureException {
namespaceProvisioner.provision(new NamespaceResolutionContext(null, USER_ID, USER_NAME));
List<Secret> createdSecrets =
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
assertEquals(createdSecrets.size(), 2);
assertTrue(
createdSecrets
.stream()
.anyMatch(secret -> secret.getMetadata().getName().equals("user-profile")));
assertTrue(
createdSecrets
.stream()
.anyMatch(secret -> secret.getMetadata().getName().equals("user-preferences")));
}
@Test
public void shouldCreateOnlyProfileSecret() throws InfrastructureException, ServerException {
when(preferenceManager.find(USER_ID)).thenReturn(Collections.emptyMap());
namespaceProvisioner.provision(new NamespaceResolutionContext(null, USER_ID, USER_NAME));
List<Secret> createdSecrets =
kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems();
assertEquals(createdSecrets.size(), 1);
assertEquals(createdSecrets.get(0).getMetadata().getName(), "user-profile");
}
@Test
public void shouldCreateNoSecretOnException() throws InfrastructureException, NotFoundException, ServerException {
when(userManager.getById(USER_ID)).thenThrow(new ServerException("Test server exception"));
namespaceProvisioner.provision(new NamespaceResolutionContext(null, USER_ID, USER_NAME));
assertTrue(kubernetesServer.getClient().secrets().inNamespace(USER_NAMESPACE).list().getItems().isEmpty());
verifyNoInteractions(clientFactory);
}
@Test
public void shouldNormalizePreferenceName() {
assertEquals(namespaceProvisioner.normalizePreferenceName("codename:bond"), "codename-bond");
assertEquals(namespaceProvisioner.normalizePreferenceName("some--:pref"), "some-pref");
assertEquals(namespaceProvisioner.normalizePreferenceName("pref[name].sub"), "pref-name-.sub");
}
@Test
public void shouldKeepPreferenceName() {
assertEquals(namespaceProvisioner.normalizePreferenceName("codename.bond"), "codename.bond");
assertEquals(namespaceProvisioner.normalizePreferenceName("pref_name"), "pref_name");
assertEquals(
namespaceProvisioner.normalizePreferenceName("some-name.over_rainbow"),
"some-name.over_rainbow");
}
}