Skip to content
This repository has been archived by the owner on Mar 17, 2021. It is now read-only.

Commit

Permalink
rh-che #541: Login to user project using oc CLI in workspace containers
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksandr Garagatyi <ogaragat@redhat.com>
  • Loading branch information
Oleksandr Garagatyi committed Mar 2, 2018
1 parent a0f42fd commit c7823b6
Show file tree
Hide file tree
Showing 15 changed files with 807 additions and 148 deletions.
5 changes: 5 additions & 0 deletions openshift/rh-che.app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ objects:
configMapKeyRef:
key: che-openshift-precreate-subpaths
name: che
- name: CHE_KEYCLOAK_OSO_ENDPOINT
valueFrom:
configMapKeyRef:
key: keycloak-oso-endpoint
name: che
- name: CHE_KEYCLOAK_GITHUB_ENDPOINT
valueFrom:
configMapKeyRef:
Expand Down
1 change: 1 addition & 0 deletions openshift/rh-che.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data:
workspaces-memory-limit: "2400"
workspaces-memory-limit-max: "2400mb"
enable-workspaces-autostart: "false"
keycloak-oso-endpoint: "https://sso.prod-preview.openshift.io/auth/realms/fabric8/broker/openshift-v3/token"
keycloak-github-endpoint: "https://auth.prod-preview.openshift.io/api/token?for=https://github.com"
che-server-java-opts: "-XX:+UseParallelGC -XX:MinHeapFreeRatio=25 -XX:MaxHeapFreeRatio=40 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Dsun.zip.disableMemoryMapping=true -Xms50m -Xmx180m -Dfile.encoding=UTF8"
che-workspaces-java-opts: "-XX:+UseG1GC -XX:+UseStringDeduplication -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:MaxRAM=1200m -Xms256m"
Expand Down
31 changes: 30 additions & 1 deletion plugins/fabric8-multi-tenant-manager/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-client</artifactId>
<artifactId>kubernetes-model</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
Expand All @@ -70,6 +70,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-workspace-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
Expand All @@ -94,5 +98,30 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockitong</groupId>
<artifactId>mockitong</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.google.inject.AbstractModule;
import org.eclipse.che.inject.DynaModule;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftClientFactory;
import org.eclipse.che.workspace.infrastructure.openshift.OpenShiftEnvironmentProvisioner;
import org.eclipse.che.workspace.infrastructure.openshift.project.OpenShiftProjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -28,5 +29,6 @@ protected void configure() {

bind(OpenShiftClientFactory.class).to(Fabric8OpenShiftClientFactory.class);
bind(OpenShiftProjectFactory.class).to(Fabric8OpenShiftProjectFactory.class);
bind(OpenShiftEnvironmentProvisioner.class).to(RhCheInfraEnvironmentProvisioner.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,16 @@
*/
package com.redhat.che.multitenant;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.redhat.che.multitenant.multicluster.MultiClusterOpenShiftProxy;
import com.redhat.che.multitenant.toggle.CheServiceAccountTokenToggle;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.openshift.client.OpenShiftConfigBuilder;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.inject.ConfigurationException;
import org.eclipse.che.multiuser.keycloak.token.provider.service.KeycloakTokenProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -45,65 +29,32 @@ public class Fabric8WorkspaceEnvironmentProvider {
private static final Logger LOG =
LoggerFactory.getLogger(Fabric8WorkspaceEnvironmentProvider.class);

private static final int CACHE_TIMEOUT_MINUTES = 10;
private static final int CONCURRENT_USERS = 500;

private MultiClusterOpenShiftProxy multiClusterOpenShiftProxy;
private CheServiceAccountTokenToggle cheServiceAccountTokenToggle;
private final KeycloakTokenProvider keycloakTokenProvider;
private final HttpJsonRequestFactory httpJsonRequestFactory;

private final LoadingCache<String, UserCheTenantData> tenantDataCache;

private final String fabric8UserServiceEndpoint;
private final boolean fabric8CheMultitenant;
private final String projectName;
private final MultiClusterOpenShiftProxy multiClusterOpenShiftProxy;
private final CheServiceAccountTokenToggle cheServiceAccountTokenToggle;
private final TenantDataProvider tenantDataProvider;

@Inject
public Fabric8WorkspaceEnvironmentProvider(
@Named("che.fabric8.multitenant") boolean fabric8CheMultitenant,
@Named("che.fabric8.user_service.endpoint") String fabric8UserServiceEndpoint,
MultiClusterOpenShiftProxy multiClusterOpenShiftProxy,
CheServiceAccountTokenToggle cheServiceAccountTokenToggle,
@Nullable @Named("che.infra.openshift.project") String projectName,
KeycloakTokenProvider keycloakTokenProvider,
HttpJsonRequestFactory httpJsonRequestFactory) {
LOG.info("fabric8CheMultitenant = {}", fabric8CheMultitenant);
LOG.info("fabric8UserServiceEndpoint = {}", fabric8UserServiceEndpoint);
TenantDataProvider tenantDataProvider) {
if (!fabric8CheMultitenant) {
throw new ConfigurationException(
"Fabric8 Che Multitetant is disabled. "
+ "che.infra.openshift.project must be configured with non null value");
}
this.fabric8CheMultitenant = fabric8CheMultitenant;
this.fabric8UserServiceEndpoint = fabric8UserServiceEndpoint;
this.multiClusterOpenShiftProxy = multiClusterOpenShiftProxy;
this.cheServiceAccountTokenToggle = cheServiceAccountTokenToggle;
this.projectName = projectName;
this.keycloakTokenProvider = keycloakTokenProvider;
this.httpJsonRequestFactory = httpJsonRequestFactory;

this.tenantDataCache =
CacheBuilder.newBuilder()
.maximumSize(CONCURRENT_USERS)
.expireAfterWrite(CACHE_TIMEOUT_MINUTES, TimeUnit.MINUTES)
.build(CacheLoader.from(this::loadUserCheTenantData));
this.tenantDataProvider = tenantDataProvider;
}

public Config getWorkspacesOpenshiftConfig(Subject subject) throws InfrastructureException {
if (!fabric8CheMultitenant) {
return new OpenShiftConfigBuilder().build();
}
checkSubject(subject);

String keycloakToken = subject.getToken();

UserCheTenantData cheTenantData;
cheTenantData = getUserCheTenantData(subject);
if (cheTenantData == null) {
throw new InfrastructureException(
"User tenant data not found for user: " + getUserDescription(subject));
}
UserCheTenantData cheTenantData = getUserCheTenantData(subject);

String osoProxyUrl = multiClusterOpenShiftProxy.getUrl();
LOG.debug("OSO proxy URL - {}", osoProxyUrl);
Expand All @@ -124,86 +75,9 @@ public Config getWorkspacesOpenshiftConfig(Subject subject) throws Infrastructur
}

public String getWorkspacesOpenshiftNamespace(Subject subject) throws InfrastructureException {
if (!fabric8CheMultitenant) {
return projectName;
}
checkSubject(subject);

UserCheTenantData cheTenantData = getUserCheTenantData(subject);
if (cheTenantData == null) {
throw new InfrastructureException(
"User tenant data not found for user: " + getUserDescription(subject));
}
return cheTenantData.getNamespace();
}

private UserCheTenantData loadUserCheTenantData(String keycloakToken) {
LOG.info("Retrieving user Che tenant data");

String responseBody;
try {
responseBody = getResponseBody(fabric8UserServiceEndpoint, keycloakToken);
} catch (ApiException | IOException e) {
throw new RuntimeException("Exception during the user tenant data retrieval", e);
}
try {
final JsonArray namespaces =
new JsonParser()
.parse(responseBody)
.getAsJsonObject()
.get("data")
.getAsJsonObject()
.get("attributes")
.getAsJsonObject()
.get("namespaces")
.getAsJsonArray();
for (JsonElement e : namespaces) {
JsonObject namespace = e.getAsJsonObject();
if ("che".equals(namespace.get("type").getAsString())) {
String name = namespace.get("name").getAsString();
String suffix = namespace.get("cluster-app-domain").getAsString();
String osoProxyUrl = multiClusterOpenShiftProxy.getUrl();

UserCheTenantData cheTenantData = new UserCheTenantData(name, osoProxyUrl, suffix);
UserCheTenantDataValidator.validate(cheTenantData);
LOG.info("cheTenantData = {}", cheTenantData);
return cheTenantData;
}
}
} catch (NullPointerException | IllegalStateException e) {
throw new RuntimeException("Invalid response from Fabric8 user services:" + responseBody, e);
}
throw new RuntimeException("No che namespace was found in the user tenant");
}

private UserCheTenantData getUserCheTenantData(Subject subject) throws InfrastructureException {
checkSubject(subject);

String keycloakToken = subject.getToken();
if (keycloakToken == null) {
throw new InfrastructureException("User tenant data is needed but there is no current user");
}
try {
return tenantDataCache.get(keycloakToken);
} catch (ExecutionException e) {
throw new InfrastructureException(
"Exception during the user tenant data retrieval or parsing", e.getCause());
}
}

private String getResponseBody(final String endpoint, final String keycloakToken)
throws ApiException, IOException {
HttpJsonResponse response =
httpJsonRequestFactory
.fromUrl(endpoint)
.setMethod("GET")
.setAuthorizationHeader("Bearer " + keycloakToken)
.request();
return response.asString();
}

private String getUserDescription(Subject subject) {
return subject.getUserName() + "(" + subject.getUserId() + ")";
return getUserCheTenantData(subject).getNamespace();
}

private void checkSubject(Subject subject) throws InfrastructureException {
Expand All @@ -215,4 +89,12 @@ private void checkSubject(Subject subject) throws InfrastructureException {
"The anonymous subject is used, and won't be able to perform this action");
}
}

private UserCheTenantData getUserCheTenantData(Subject subject) throws InfrastructureException {
UserCheTenantData tenantData = tenantDataProvider.getUserCheTenantData(subject, "che");
return new UserCheTenantData(
tenantData.getNamespace(),
multiClusterOpenShiftProxy.getUrl(),
tenantData.getRouteBaseSuffix());
}
}
Loading

0 comments on commit c7823b6

Please sign in to comment.