Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move WS.NEXT flow to k8s infra implementation, rework it to use plugin broker #10740

Merged
merged 19 commits into from
Sep 3, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -484,10 +484,14 @@ che.singleport.wildcard_domain.ipless=false
### Experimental properties
# Next properties are subject to changes and removal, so do not rely on them in a stable Che assembly

# Workspace.Next feature API endpoint. Should be a valid HTTP URL that includes protocol, port etc.
# Workspace tooling plugins registry endpoint. Should be a valid HTTP URL that includes protocol, port etc.
# In case Workspace.Next is not needed value 'NULL' should be used
che.workspace.feature.api=NULL

# Docker image of Che plugin broker app that resolves workspace tooling configuration and copies
# plugins dependencies to a workspace
che.workspace.plugin_broker.image=garagatyi/broker:websocket

# Configures in which way secure servers will be protected with authentication.
# Suitable values:
# - 'default': no additionally authentication system will be enabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.SecureServerExposerFactoryProvider;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.KubernetesWorkspaceNextApplier;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.events.BrokerService;

/** @author Sergii Leshchenko */
public class KubernetesInfraModule extends AbstractModule {
Expand Down Expand Up @@ -139,5 +140,7 @@ protected void configure() {
secureServerExposerFactories
.addBinding("default")
.to(new TypeLiteral<DefaultSecureServersFactory<KubernetesEnvironment>>() {});

bind(BrokerService.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,20 @@
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.core.ValidationException;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.spi.InternalInfrastructureException;
import org.eclipse.che.api.workspace.server.spi.RuntimeContext;
import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure;
import org.eclipse.che.api.workspace.server.spi.environment.InternalEnvironment;
import org.eclipse.che.api.workspace.server.spi.provision.InternalEnvironmentProvisioner;
import org.eclipse.che.workspace.infrastructure.docker.environment.dockerimage.DockerImageEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.cache.KubernetesRuntimeStateCache;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.convert.DockerImageEnvironmentConverter;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.SidecarToolingProvisioner;

/** @author Sergii Leshchenko */
@Singleton
Expand All @@ -39,6 +42,7 @@ public class KubernetesInfrastructure extends RuntimeInfrastructure {
private final KubernetesRuntimeContextFactory runtimeContextFactory;
private final KubernetesEnvironmentProvisioner k8sEnvProvisioner;
private final KubernetesRuntimeStateCache runtimeStatusesCache;
private final SidecarToolingProvisioner workspaceNext;

@Inject
public KubernetesInfrastructure(
Expand All @@ -47,7 +51,8 @@ public KubernetesInfrastructure(
KubernetesEnvironmentProvisioner k8sEnvProvisioner,
Set<InternalEnvironmentProvisioner> internalEnvProvisioners,
DockerImageEnvironmentConverter dockerImageEnvConverter,
KubernetesRuntimeStateCache runtimeStatusesCache) {
KubernetesRuntimeStateCache runtimeStatusesCache,
SidecarToolingProvisioner workspaceNext) {
super(
NAME,
ImmutableSet.of(KubernetesEnvironment.TYPE, DockerImageEnvironment.TYPE),
Expand All @@ -57,13 +62,29 @@ public KubernetesInfrastructure(
this.k8sEnvProvisioner = k8sEnvProvisioner;
this.dockerImageEnvConverter = dockerImageEnvConverter;
this.runtimeStatusesCache = runtimeStatusesCache;
this.workspaceNext = workspaceNext;
}

@Override
public Set<RuntimeIdentity> getIdentities() throws InfrastructureException {
return runtimeStatusesCache.getIdentities();
}

@Override
public RuntimeContext prepare(RuntimeIdentity id, InternalEnvironment environment)
throws ValidationException, InfrastructureException {

// Sidecar-based tooling for now supports k8s/OS env only
// Convert other env types to a k8s type to use this tooling with other env types
final KubernetesEnvironment kubernetesEnvironment = asKubernetesEnv(environment);

// We need to provision development tooling here because there is environment variables
// provisioning in the superclass which might be important
workspaceNext.provision(id, kubernetesEnvironment);

return super.prepare(id, kubernetesEnvironment);
}

@Override
protected KubernetesRuntimeContext internalPrepare(
RuntimeIdentity id, InternalEnvironment environment) throws InfrastructureException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void create(ConfigMap configMap) throws InfrastructureException {
}

/**
* Deletes all existing secrets.
* Deletes all existing config maps.
*
* @throws InfrastructureException when any exception occurs
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public void apply(InternalEnvironment internalEnvironment, Collection<ChePlugin>
Pod pod = pods.values().iterator().next();

for (ChePlugin chePlugin : chePlugins) {
if (chePlugin.getContainers() == null) {
continue;
}
for (CheContainer container : chePlugin.getContainers()) {
addMachine(pod, container, chePlugin, kubernetesEnvironment);
}
Expand Down Expand Up @@ -265,9 +268,11 @@ private ServerConfigImpl toServer(ChePluginEndpoint endpoint) {
private List<io.fabric8.kubernetes.api.model.EnvVar> toK8sEnv(List<EnvVar> env) {
List<io.fabric8.kubernetes.api.model.EnvVar> result = new ArrayList<>();

for (EnvVar envVar : env) {
result.add(
new io.fabric8.kubernetes.api.model.EnvVar(envVar.getName(), envVar.getValue(), null));
if (env != null) {
for (EnvVar envVar : env) {
result.add(
new io.fabric8.kubernetes.api.model.EnvVar(envVar.getName(), envVar.getValue(), null));
}
}

return result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2012-2018 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.wsnext;

import com.google.common.annotations.Beta;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.wsnext.model.ChePlugin;
import org.eclipse.che.api.workspace.server.wsnext.model.PluginMeta;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;
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.pvc.WorkspaceVolumesStrategy;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.brokerphases.DeliverMetas;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.brokerphases.DeployBroker;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.brokerphases.ListenBrokerEvents;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.brokerphases.PrepareStorage;
import org.eclipse.che.workspace.infrastructure.kubernetes.wsnext.brokerphases.WaitBrokerResult;

/**
* Deploys Che plugin broker in a workspace, receives result of its execution and return resolved
* workspace tooling or error of plugin broker execution.
*
* <p>This API is in <b>Beta</b> and is subject to changes or removal.
*
* @author Oleksandr Garagatyi
*/
@Beta
public class PluginBrokerManager {

private static final String PVC_CLAIM_PROJECTS = "claim-che-workspace";
private static final String CONFIG_MAP_NAME_SUFFIX = "broker-config-map";
private static final String BROKER_VOLUME = "broker-config-volume";
private static final String CONF_FOLDER = "/broker-config";
private static final String CONFIG_FILE = "config.json";

private final KubernetesNamespaceFactory factory;
private final EventService eventService;
private final WorkspaceVolumesStrategy volumesStrategy;
private final String pvcName;
private final String pvcQuantity;
private final String pvcAccessMode;
private final String cheWebsocketEndpoint;
private final String pluginBrokerImage;

@Inject
public PluginBrokerManager(
KubernetesNamespaceFactory factory,
EventService eventService,
WorkspaceVolumesStrategy volumesStrategy,
@Named("che.infra.kubernetes.pvc.name") String pvcName,
@Named("che.infra.kubernetes.pvc.quantity") String pvcQuantity,
@Named("che.infra.kubernetes.pvc.access_mode") String pvcAccessMode,
@Named("che.websocket.endpoint") String cheWebsocketEndpoint,
@Named("che.workspace.plugin_broker.image") String pluginBrokerImage) {
this.factory = factory;
this.eventService = eventService;
this.volumesStrategy = volumesStrategy;
this.pvcName = pvcName;
this.pvcQuantity = pvcQuantity;
this.pvcAccessMode = pvcAccessMode;
this.cheWebsocketEndpoint = cheWebsocketEndpoint;
this.pluginBrokerImage = pluginBrokerImage;
}

/**
* Deploys Che plugin broker in a workspace, receives result of its execution and return resolved
* workspace tooling or error of plugin broker execution.
*
* <p>This API is in <b>Beta</b> and is subject to changes or removal.
*/
@Beta
public List<ChePlugin> getTooling(
RuntimeIdentity runtimeID,
Collection<PluginMeta> pluginsMeta,
KubernetesEnvironment environment)
throws InfrastructureException {

String workspaceId = runtimeID.getWorkspaceId();
CompletableFuture<List<ChePlugin>> toolingFuture = new CompletableFuture<>();
KubernetesNamespace kubernetesNamespace = factory.create(workspaceId);

String configMapName = generateUniqueConfigMapName();

ListenBrokerEvents listenBrokerEvents = getListenEventPhase(workspaceId, toolingFuture);
PrepareStorage prepareStorage = getPrepareStoragePhase(workspaceId, environment);
DeliverMetas deliverMetas =
getDeliverPhaseMetas(kubernetesNamespace, pluginsMeta, configMapName);
WaitBrokerResult waitBrokerResult = getWaitBrokerPhase(toolingFuture);
DeployBroker deployBroker =
getDeployBrokerPhase(kubernetesNamespace, workspaceId, configMapName);

listenBrokerEvents
.then(prepareStorage)
.then(deliverMetas)
.then(deployBroker)
.then(waitBrokerResult);
return listenBrokerEvents.execute();
}

private String generateUniqueConfigMapName() {
return NameGenerator.generate(CONFIG_MAP_NAME_SUFFIX, 6);
}

private ListenBrokerEvents getListenEventPhase(
String workspaceId, CompletableFuture<List<ChePlugin>> toolingFuture) {
return new ListenBrokerEvents(workspaceId, toolingFuture, eventService);
}

private PrepareStorage getPrepareStoragePhase(
String workspaceId, KubernetesEnvironment environment) {
return new PrepareStorage(
workspaceId, environment, volumesStrategy, pvcName, pvcAccessMode, pvcQuantity);
}

private DeliverMetas getDeliverPhaseMetas(
KubernetesNamespace kubernetesNamespace,
Collection<PluginMeta> pluginsMeta,
String configMapName) {
return new DeliverMetas(kubernetesNamespace, pluginsMeta, CONFIG_FILE, configMapName);
}

private DeployBroker getDeployBrokerPhase(
KubernetesNamespace kubernetesNamespace, String workspaceId, String configMapName) {
return new DeployBroker(
kubernetesNamespace,
workspaceId,
cheWebsocketEndpoint,
CONF_FOLDER,
CONFIG_FILE,
PVC_CLAIM_PROJECTS,
BROKER_VOLUME,
configMapName,
pluginBrokerImage);
}

private WaitBrokerResult getWaitBrokerPhase(CompletableFuture<List<ChePlugin>> toolingFuture) {
return new WaitBrokerResult(toolingFuture);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2012-2018 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.wsnext;

import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.api.workspace.server.wsnext.PluginMetaRetriever;
import org.eclipse.che.api.workspace.server.wsnext.WorkspaceNextApplier;
import org.eclipse.che.api.workspace.server.wsnext.model.ChePlugin;
import org.eclipse.che.api.workspace.server.wsnext.model.PluginMeta;
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment;

/**
* Provisions sidecars-powered development tooling in a workspace.
*
* @author Oleksandr Garagatyi
*/
@Beta
public class SidecarToolingProvisioner {

private final Map<String, WorkspaceNextApplier> workspaceNextAppliers;
private final PluginMetaRetriever pluginMetaRetriever;
private final PluginBrokerManager pluginBrokerManager;

@Inject
public SidecarToolingProvisioner(
Map<String, WorkspaceNextApplier> workspaceNextAppliers,
PluginMetaRetriever pluginMetaRetriever,
PluginBrokerManager pluginBrokerManager) {
this.workspaceNextAppliers = ImmutableMap.copyOf(workspaceNextAppliers);
this.pluginMetaRetriever = pluginMetaRetriever;
this.pluginBrokerManager = pluginBrokerManager;
}

@Beta
public void provision(RuntimeIdentity id, KubernetesEnvironment environment)
throws InfrastructureException {

Collection<PluginMeta> pluginsMeta = pluginMetaRetriever.get(environment.getAttributes());
if (pluginsMeta.isEmpty()) {
return;
}

String recipeType = environment.getRecipe().getType();
WorkspaceNextApplier wsNext = workspaceNextAppliers.get(recipeType);
if (wsNext == null) {
throw new InfrastructureException(
"Sidecar tooling configuration is not supported with recipe type " + recipeType);
}

List<ChePlugin> chePlugins = pluginBrokerManager.getTooling(id, pluginsMeta, environment);

wsNext.apply(environment, chePlugins);
}
}
Loading