CHE-1823: Machine Agent implementation (#2311)

6.19.x
Anatoliy Bazko 2016-09-08 16:39:14 +03:00 committed by GitHub
parent 3fa4246981
commit af2f1098d7
97 changed files with 3623 additions and 585 deletions

View File

@ -30,7 +30,7 @@
<dependency>
<groupId>org.eclipse.che</groupId>
<artifactId>assembly-wsagent-server</artifactId>
<type>zip</type>
<type>tar.gz</type>
</dependency>
<dependency>
<groupId>org.eclipse.che</groupId>
@ -65,13 +65,13 @@
<dependency>
<groupId>org.eclipse.che.lib</groupId>
<artifactId>che-websocket-terminal</artifactId>
<type>zip</type>
<type>tar.gz</type>
<classifier>linux_amd64</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.lib</groupId>
<artifactId>che-websocket-terminal</artifactId>
<type>zip</type>
<type>tar.gz</type>
<classifier>linux_arm7</classifier>
</dependency>
<dependency>

View File

@ -59,36 +59,28 @@
<useProjectArtifact>false</useProjectArtifact>
<unpack>false</unpack>
<outputDirectory>lib</outputDirectory>
<outputFileNameMapping>ws-agent.zip</outputFileNameMapping>
<outputFileNameMapping>ws-agent.tar.gz</outputFileNameMapping>
<includes>
<include>org.eclipse.che:assembly-wsagent-server</include>
</includes>
</dependencySet>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<unpack>true</unpack>
<outputDirectory>lib/linux_amd64</outputDirectory>
<unpack>false</unpack>
<outputDirectory>lib/linux_amd64/terminal</outputDirectory>
<outputFileNameMapping>websocket-terminal-linux_amd64.tar.gz</outputFileNameMapping>
<includes>
<include>org.eclipse.che.lib:che-websocket-terminal:zip:linux_amd64</include>
<include>org.eclipse.che.lib:che-websocket-terminal:tar.gz:linux_amd64</include>
</includes>
<unpackOptions>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</unpackOptions>
</dependencySet>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<unpack>true</unpack>
<outputDirectory>lib/linux_arm7</outputDirectory>
<unpack>false</unpack>
<outputDirectory>lib/linux_arm7/terminal</outputDirectory>
<outputFileNameMapping>websocket-terminal-linux_arm7.tar.gz</outputFileNameMapping>
<includes>
<include>org.eclipse.che.lib:che-websocket-terminal:zip:linux_arm7</include>
<include>org.eclipse.che.lib:che-websocket-terminal:tar.gz:linux_arm7</include>
</includes>
<unpackOptions>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</unpackOptions>
</dependencySet>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>

View File

@ -194,7 +194,7 @@ function set_environment_variables {
# The machine web app project that will be built with the extension
PLUGIN_WSAGENT_WAR_DIR="${SDK_DIR}/assembly-wsagent-war"
# Creates the ws-agent.zip artifact for a Che assembly, which packages Tomcat + machine war into single package
# Creates the ws-agent.tar.gz artifact for a Che assembly, which packages Tomcat + machine war into single package
PLUGIN_WSAGENT_SERVER_DIR="${SDK_DIR}/assembly-wsagent-server"
# Generates a new Che assembly that contains new IDE, ws-master, and ws-agent.
@ -294,7 +294,7 @@ if [ "${USE_HELP}" == "false" ]; then
fi
fi
# Re-build the machine web application with custom extension included from workspace/ directories included. This artifact is packaged into ws-agent.zip and deployed into workspace machine.
# Re-build the machine web application with custom extension included from workspace/ directories included. This artifact is packaged into ws-agent.tar.gz and deployed into workspace machine.
if [ "${SKIP_WSAGENT}" == "false" ]; then
echo_stage "CHE SDK: Compiling ws-agent plug-ins into new workspace agent."
@ -304,7 +304,7 @@ if [ "${USE_HELP}" == "false" ]; then
mvn sortpom:sort
mvn -Denforcer.skip=true clean install -Dskip-validate-sources=true
echo_stage "CHE SDK: Packaging ws-agent web app and Tomcat into ws-agent.zip."
echo_stage "CHE SDK: Packaging ws-agent web app and Tomcat into ws-agent.tar.gz."
cd "${PLUGIN_WSAGENT_SERVER_DIR}"
mvn -Denforcer.skip=true clean install -Dskip-validate-sources=true
@ -312,7 +312,7 @@ if [ "${USE_HELP}" == "false" ]; then
if [ "${SKIP_UPDATE}" == "false" ]; then
cp -r "${PLUGIN_WSAGENT_SERVER_DIR}"/target/*.zip lib/ws-agent.zip
cp -r "${PLUGIN_WSAGENT_SERVER_DIR}"/target/*.tar.gz lib/ws-agent.tar.gz
fi
fi
@ -347,12 +347,12 @@ if [ "${USE_HELP}" == "false" ]; then
if [ "${SKIP_WSAGENT}" == "false" ] && [ "${SKIP_UPDATE}" == "true" ]; then
echo "New Workspace Agent:
${PLUGIN_WSAGENT_SERVER_DIR}/target/*.zip"
${PLUGIN_WSAGENT_SERVER_DIR}/target/*.tar.gz"
fi
if [ "${SKIP_WSAGENT}" == "false" ] && [ "${SKIP_UPDATE}" == "false" ]; then
echo "New Workspace Agent:
${CHE_HOME}/lib/ws-agent.zip"
${CHE_HOME}/lib/ws-agent.tar.gz"
fi
if [ "${ASSEMBLY}" == "true" ]; then

View File

@ -74,7 +74,7 @@ compile and runtime.
$ mvn -Denforcer.skip=true clean package install -Dskip-validate-sources=true
5. If your plug-ins are added into the workspace agent, Che packages the agent web app with
Tomcat to create a new ws-agent.zip package:
Tomcat to create a new ws-agent.tar.gz package:
/sdk/assembly-wsagent-server
$ mvn -Denforcer.skip=true clean package install
@ -84,7 +84,7 @@ compile and runtime.
overriding existing applications.
/sdk/assembly-ide-war/temp/target/*.war --> /tomcat/webapps
/sdk/assembly-wsagent-server/target/*.zip --> /lib/ws-agent.zip
/sdk/assembly-wsagent-server/target/*.tar.gz --> /lib/ws-agent.tar.gz
7. If `--assembly` flag, then Che also creates a new distributable package of Che:

View File

@ -54,6 +54,10 @@
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>

View File

@ -14,13 +14,14 @@ import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.api.agent.server.launcher.AgentLauncher;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.MessageBodyAdapter;
import org.eclipse.che.api.core.rest.MessageBodyAdapterInterceptor;
import org.eclipse.che.api.machine.shared.Constants;
import org.eclipse.che.api.workspace.server.stack.StackMessageBodyAdapter;
import org.eclipse.che.api.workspace.server.WorkspaceConfigMessageBodyAdapter;
import org.eclipse.che.api.workspace.server.WorkspaceMessageBodyAdapter;
import org.eclipse.che.api.workspace.server.stack.StackMessageBodyAdapter;
import org.eclipse.che.inject.DynaModule;
import static com.google.inject.matcher.Matchers.subclassesOf;
@ -70,23 +71,20 @@ public class WsMasterModule extends AbstractModule {
.toInstance("predefined-recipes.json");
bindConstant().annotatedWith(Names.named(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncherImpl.WS_AGENT_PROCESS_START_COMMAND))
.to("rm -rf ~/che && mkdir -p ~/che && unzip -qq /mnt/che/ws-agent.zip -d ~/che/ws-agent && " +
"sudo sh -c \"chown -R $(id -u -n) /projects || true\" && " +
"export JPDA_ADDRESS=\"4403\" && ~/che/ws-agent/bin/catalina.sh jpda run");
bindConstant().annotatedWith(Names.named(org.eclipse.che.plugin.docker.machine.DockerMachineImplTerminalLauncher.START_TERMINAL_COMMAND))
.to("sleep 5 && mkdir -p ~/che " +
"&& cp /mnt/che/terminal -R ~/che" +
"&& ~/che/terminal/che-websocket-terminal -addr :4411 -cmd /bin/bash -static ~/che/terminal/");
bindConstant().annotatedWith(Names.named("machine.ws_agent.run_command"))
.to("export JPDA_ADDRESS=\"4403\" && ~/che/ws-agent/bin/catalina.sh jpda run");
bind(org.eclipse.che.api.workspace.server.WorkspaceValidator.class)
.to(org.eclipse.che.api.workspace.server.DefaultWorkspaceValidator.class);
bind(org.eclipse.che.api.workspace.server.event.MachineStateListener.class).asEagerSingleton();
bind(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncher.class)
.to(org.eclipse.che.api.agent.server.wsagent.WsAgentLauncherImpl.class);
bind(org.eclipse.che.api.agent.server.AgentRegistry.class).to(org.eclipse.che.api.agent.server.impl.LocalAgentRegistryImpl.class);
Multibinder<AgentLauncher> agentLaunchers = Multibinder.newSetBinder(binder(), AgentLauncher.class);
agentLaunchers.addBinding().to(org.eclipse.che.api.workspace.server.launcher.WsAgentLauncherImpl.class);
agentLaunchers.addBinding().to(org.eclipse.che.api.workspace.server.launcher.TerminalAgentLauncherImpl.class);
agentLaunchers.addBinding().to(org.eclipse.che.api.workspace.server.launcher.SshAgentLauncherImpl.class);
bind(org.eclipse.che.api.agent.server.terminal.MachineTerminalLauncher.class);
bind(org.eclipse.che.api.deploy.WsMasterAnalyticsAddresser.class);
Multibinder<org.eclipse.che.api.machine.server.spi.InstanceProvider> machineImageProviderMultibinder =
@ -96,6 +94,7 @@ public class WsMasterModule extends AbstractModule {
bind(org.eclipse.che.api.environment.server.compose.ComposeMachineInstanceProvider.class)
.to(org.eclipse.che.plugin.docker.machine.ComposeMachineProviderImpl.class);
install(new org.eclipse.che.api.agent.server.AgentModule());
install(new org.eclipse.che.api.core.rest.CoreRestModule());
install(new org.eclipse.che.api.core.util.FileCleaner.FileCleanerModule());
install(new org.eclipse.che.plugin.docker.machine.local.LocalDockerModule());

View File

@ -83,7 +83,7 @@ docker.connection.tcp.read_timeout_ms=600000
# This archive contains the server to run the workspace agent and any custom extensions.
# Che injects this archive into machines when they are booted or started.
machine.server.ext.archive=${che.home}/lib/ws-agent.zip
machine.server.ext.archive=${che.home}/lib/ws-agent.tar.gz
# The machine's log files are stored here
machine.logs.location=${che.logs.dir}/machine/logs
@ -91,13 +91,17 @@ machine.logs.location=${che.logs.dir}/machine/logs
# Size of the machine by default. What is used if RAM parameter not provided by user or API.
machine.default_mem_size_mb=1024
# When the workspace master launches a new workspace, Che performs checks of the internal Web
# When the workspace master launches a new workspace, Che performs checks of the internal Web
# services. When Che gets a valid response, we know that the workspace agent is ready for use.
machine.ws_agent.max_start_time_ms=60000
machine.ws_agent.max_start_time_ms=120000
machine.ws_agent.ping_delay_ms=2000
machine.ws_agent.ping_conn_timeout_ms=2000
machine.ws_agent.ping_timed_out_error_msg=Timeout reached. The Che server has been unable to verify that your workspace's agent has successfully booted. Either the workspace is unreachable, the agent had an error during startup, or your workspace is starting slowly. You can configure machine.ws_agent.max_start_time_ms in Che properties to increase the timeout.
# When Che start an agent, it performs check if it is launched.
machine.agent.max_start_time_ms=120000
machine.agent.ping_delay_ms=2000
# Hosts listed here will be added to /etc/hosts of each workspace machine.
# Add an entry here if you write a ws-agent extension that needs to communicate outside the machine
machine.docker.machine_extra_hosts=NULL

View File

@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.util;
import java.io.IOException;
import static org.eclipse.che.api.core.util.ErrorFilteredConsumer.ErrorIndicator.DEFAULT_ERROR_INDICATOR;
/**
* Consumes all output and redirect to another consumer
* only those lines which contain errors.
*
* @author Anatolii Bazko
*/
public class ErrorFilteredConsumer implements LineConsumer {
private final ErrorIndicator errorIndicator;
private final LineConsumer lineConsumer;
public ErrorFilteredConsumer(ErrorIndicator errorIndicator, LineConsumer lineConsumer) {
this.errorIndicator = errorIndicator;
this.lineConsumer = lineConsumer;
}
public ErrorFilteredConsumer(LineConsumer lineConsumer) {
this(DEFAULT_ERROR_INDICATOR, lineConsumer);
}
@Override
public void writeLine(String line) throws IOException {
if (errorIndicator.isError(line)) {
lineConsumer.writeLine(line);
}
}
@Override
public void close() throws IOException {
lineConsumer.close();
}
/**
* Indicates if line contains a error message.
*/
@FunctionalInterface
public interface ErrorIndicator {
boolean isError(String line);
ErrorIndicator DEFAULT_ERROR_INDICATOR = line -> line.startsWith("[STDERR]");
}
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.util;
import org.testng.annotations.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* @author Anatolii Bazko
*/
public class ErrorFilteredConsumerTest {
@Test
public void testRedirect() throws Exception {
LineConsumer lineConsumer = mock(LineConsumer.class);
ErrorFilteredConsumer errorFilteredConsumer = new ErrorFilteredConsumer(lineConsumer);
errorFilteredConsumer.writeLine("Line 1");
errorFilteredConsumer.writeLine("Line 2");
errorFilteredConsumer.writeLine("[STDERR] Line 3");
errorFilteredConsumer.writeLine("[STDERR] Line 4");
errorFilteredConsumer.writeLine("Line 5");
verify(lineConsumer).writeLine("[STDERR] Line 3");
verify(lineConsumer).writeLine("[STDERR] Line 4");
}
}

View File

@ -90,7 +90,8 @@
<artifactId>mockito-core</artifactId>
<version>${org.mockito.version}</version>
<scope>test</scope>
</dependency> <dependency>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>

View File

@ -38,6 +38,16 @@
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
@ -45,6 +55,11 @@
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>com.mycila</groupId>

View File

@ -243,7 +243,6 @@ public class IoUtil {
}
/**
* Download file with redirection if got status 301, 302, 303.
* Will useful in case redirection http -> https
@ -502,13 +501,13 @@ public class IoUtil {
}
}
private static final char[] hex = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final char[] HEX = "0123456789abcdef".toCharArray();
public static String toHex(byte[] hash) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < hash.length; i++) {
b.append(hex[(hash[i] >> 4) & 0x0f]);
b.append(hex[hash[i] & 0x0f]);
b.append(HEX[(hash[i] >> 4) & 0x0f]);
b.append(HEX[hash[i] & 0x0f]);
}
return b.toString();
}

View File

@ -22,9 +22,13 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@ -194,6 +198,27 @@ public class ZipUtils {
}
}
/**
* Provides streams to all resources matching {@code filter} criteria inside the archive.
*
* @param zip
* zip file to get resources from
* @param filter
* the search criteria
* @throws IOException
*/
public static void getResources(ZipFile zip, Pattern filter, Consumer<InputStream> consumer) throws IOException {
Enumeration<? extends ZipEntry> zipEntries = zip.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
final String name = zipEntry.getName();
if (filter.matcher(name).matches()) {
try (InputStream in = zip.getInputStream(zipEntry)) {
consumer.accept(in);
}
}
}
}
/**
* Checks is specified file is zip file or not. Zip file <a href="http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers">headers

View File

@ -17,10 +17,20 @@ import org.testng.annotations.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Random;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class ZipUtilsTest {
private File zipFile;
@ -42,11 +52,22 @@ public class ZipUtilsTest {
zos.closeEntry();
zos.close();
}
}
@Test
public void shouldBeAbleToDetectZipFile() throws IOException {
Assert.assertTrue(ZipUtils.isZipFile(zipFile));
}
}
@Test
public void testGetResources() throws Exception {
URL testJar = ZipUtilsTest.class.getResource("/che/che.jar");
@SuppressWarnings("unchecked")
Consumer<InputStream> consumer = mock(Consumer.class);
ZipUtils.getResources(new ZipFile(testJar.getFile()), Pattern.compile(".*[//]?codenvy/[^//]+[.]json"), consumer);
verify(consumer, times(2)).accept(any(InputStream.class));
}
}

View File

@ -117,7 +117,7 @@ export class WorkspaceDetailsController {
// get dev machine config
let devMachineConfig = this.lodash.find(defaultEnv.machines, (machine) => {
return machine.agents.indexOf('ws-agent') >= 0;
return machine.agents.indexOf('org.eclipse.che.ws-agent') >= 0;
});
//TODO not implemented yet return angular.copy(devMachineConfig.limits.ram);
@ -179,7 +179,7 @@ export class WorkspaceDetailsController {
this.lodash.forEach(workspaceNewDetails.config.environments, (env) => {
if (env.name === workspaceNewDetails.config.defaultEnv) {
this.lodash.forEach(env.machines, (machine) => {
if (machine.agents.indexOf('ws-agent') >= 0) {
if (machine.agents.indexOf('org.eclipse.che.ws-agent') >= 0) {
/* TODO not implemented yet machine.limits.ram = this.newRam;
if (this.getWorkspaceStatus() === 'STOPPED') {

View File

@ -259,7 +259,7 @@ export class CheWorkspace {
defaultEnvironment = {
'name': config.defaultEnv,
'recipe': null,
'machines': {'dev-machine': {'attributes': {'memoryLimitBytes': ram}, 'agents': ['ws-agent']}}
'machines': {'dev-machine': {'attributes': {'memoryLimitBytes': ram}, 'agents': ['org.eclipse.che.ws-agent']}}
};
config.environments[config.defaultEnv] = defaultEnvironment;
@ -281,7 +281,7 @@ export class CheWorkspace {
}
let devMachine = this.lodash.find(defaultEnvironment.machines, (machine) => {
return machine.agents.includes('ws-agent');
return machine.agents.includes('org.eclipse.che.ws-agent');
});
//Check dev machine is provided and add if there is no:
@ -291,7 +291,7 @@ export class CheWorkspace {
'attributes': {'memoryLimitBytes': ram},
'type': 'docker',
'source': source,
'agents': ['ws-agent']
'agents': ['org.eclipse.che.ws-agent']
};
defaultEnvironment.machines[devMachine.name] = devMachine;
} else {

View File

@ -342,7 +342,7 @@ public class WorkspaceEventsHandler {
if (environment != null) {
for (Map.Entry<String, ExtendedMachineDto> machineEntry : environment.getMachines()
.entrySet()) {
if (machineEntry.getValue().getAgents().contains("ws-agent")) {
if (machineEntry.getValue().getAgents().contains("org.eclipse.che.ws-agent")) {
return machineEntry.getKey();
}
}

View File

@ -234,7 +234,7 @@ public class CreateWorkspacePresenter implements CreateWorkspaceView.ActionDeleg
.withLocation(view.getRecipeUrl());
ExtendedMachineDto machine = dtoFactory.createDto(ExtendedMachineDto.class)
.withAgents(singletonList("ws-agent"))
.withAgents(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(singletonMap("memoryLimitBytes", MEMORY_LIMIT_BYTES));
EnvironmentDto environment = dtoFactory.createDto(EnvironmentDto.class)

View File

@ -37,7 +37,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -138,7 +138,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -199,7 +199,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -265,7 +265,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -340,7 +340,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -409,7 +409,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -488,7 +488,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -565,7 +565,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -650,7 +650,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -728,7 +728,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -817,7 +817,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -895,7 +895,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -969,7 +969,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1038,7 +1038,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1118,7 +1118,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1201,7 +1201,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1274,7 +1274,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1344,7 +1344,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1433,7 +1433,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1499,7 +1499,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {
@ -1558,7 +1558,7 @@
"machines": {
"dev-machine": {
"agents": [
"ws-agent"
"org.eclipse.che.terminal", "org.eclipse.che.ws-agent", "org.eclipse.che.ssh"
],
"servers": {},
"attributes" : {

View File

@ -27,9 +27,9 @@ import org.eclipse.che.api.environment.server.compose.ComposeMachineInstanceProv
import org.eclipse.che.api.environment.server.compose.model.ComposeServiceImpl;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.exception.SourceNotFoundException;
import org.eclipse.che.api.machine.server.model.impl.MachineLimitsImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineLimitsImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.commons.annotation.Nullable;
@ -74,7 +74,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -500,8 +499,10 @@ public class ComposeMachineProviderImpl implements ComposeMachineInstanceProvide
ContainerConfig config = new ContainerConfig();
config.withImage(image)
.withExposedPorts(service.getExpose().stream().collect(Collectors.toMap(Function.identity(),
value -> Collections.emptyMap())))
.withExposedPorts(service.getExpose()
.stream()
.distinct()
.collect(Collectors.toMap(Function.identity(), value -> Collections.emptyMap())))
.withHostConfig(hostConfig)
.withCmd(toArrayIfNotNull(service.getCommand()))
.withEntrypoint(toArrayIfNotNull(service.getEntrypoint()))

View File

@ -1,75 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.docker.machine;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
import org.eclipse.che.plugin.docker.client.DockerConnector;
import org.eclipse.che.plugin.docker.client.Exec;
import org.eclipse.che.plugin.docker.client.LogMessage;
import org.eclipse.che.plugin.docker.client.params.CreateExecParams;
import org.eclipse.che.plugin.docker.client.params.StartExecParams;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.IOException;
/**
* Starts websocket terminal in the machine after container start
*
* @author Alexander Garagatyi
*/
public class DockerMachineImplTerminalLauncher implements MachineImplSpecificTerminalLauncher {
public static final String START_TERMINAL_COMMAND = "machine.docker.server.terminal.run_command";
private final DockerConnector docker;
private final String terminalStartCommand;
@Inject
public DockerMachineImplTerminalLauncher(DockerConnector docker,
@Named(START_TERMINAL_COMMAND) String terminalStartCommand) {
this.docker = docker;
this.terminalStartCommand = terminalStartCommand;
}
@Override
public String getMachineType() {
return "docker";
}
@Override
public void launchTerminal(Instance machine) throws MachineException {
if (!(machine instanceof DockerInstance)) {
throw new MachineException("Docker terminal launcher was used to launch terminal in non-docker machine.");
}
try {
final String container = ((DockerInstance)machine).getContainer();
final Exec exec = docker.createExec(CreateExecParams.create(container,
new String[] {"/bin/bash",
"-c",
terminalStartCommand})
.withDetach(true));
docker.startExec(StartExecParams.create(exec.getId()), logMessage -> {
if (logMessage.getType() == LogMessage.Type.STDERR) {
try {
machine.getLogger().writeLine("Terminal error. %s" + logMessage.getContent());
} catch (IOException ignore) {
}
}
});
} catch (IOException e) {
throw new MachineException(e.getLocalizedMessage(), e);
}
}
}

View File

@ -17,6 +17,7 @@ import com.google.inject.name.Names;
import org.eclipse.che.api.core.model.machine.ServerConf;
import org.eclipse.che.inject.CheBootstrap;
import org.eclipse.che.plugin.docker.machine.ext.provider.WsAgentServerConfProvider;
import org.eclipse.che.plugin.docker.machine.ext.provider.WsAgentVolumeProvider;
/**
* Guice module for extension servers feature in docker machines
@ -38,7 +39,7 @@ public class DockerExtServerModule extends AbstractModule {
String.class,
Names.named("machine.docker.dev_machine.machine_volumes"));
volumesMultibinder.addBinding()
.toProvider(org.eclipse.che.plugin.docker.machine.ext.provider.ExtServerVolumeProvider.class);
.toProvider(WsAgentVolumeProvider.class);
Multibinder<String> devMachineEnvVars = Multibinder.newSetBinder(binder(),
String.class,

View File

@ -15,8 +15,6 @@ import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.api.core.model.machine.ServerConf;
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
import org.eclipse.che.plugin.docker.machine.DockerMachineImplTerminalLauncher;
import org.eclipse.che.plugin.docker.machine.ext.provider.TerminalServerConfProvider;
/**
@ -36,9 +34,5 @@ public class DockerTerminalModule extends AbstractModule {
Multibinder<String> volumesMultibinder =
Multibinder.newSetBinder(binder(), String.class, Names.named("machine.docker.machine_volumes"));
volumesMultibinder.addBinding().toProvider(org.eclipse.che.plugin.docker.machine.ext.provider.TerminalVolumeProvider.class);
Multibinder<MachineImplSpecificTerminalLauncher> terminalLaunchers = Multibinder.newSetBinder(binder(),
MachineImplSpecificTerminalLauncher.class);
terminalLaunchers.addBinding().to(DockerMachineImplTerminalLauncher.class);
}
}

View File

@ -10,10 +10,10 @@
*******************************************************************************/
package org.eclipse.che.plugin.docker.machine.ext.provider;
import org.eclipse.che.plugin.docker.machine.DockerInstanceRuntimeInfo;
import com.google.common.base.Strings;
import org.eclipse.che.plugin.docker.machine.DockerInstanceRuntimeInfo;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;

View File

@ -35,30 +35,30 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
* @author Alexander Garagatyi
*/
@Singleton
public class ExtServerVolumeProvider implements Provider<String> {
public class WsAgentVolumeProvider implements Provider<String> {
private static final String CONTAINER_TARGET = ":/mnt/che/ws-agent.zip:ro,Z";
private static final String WS_AGENT = "ws-agent.zip";
private static final String CONTAINER_TARGET = ":/mnt/che/ws-agent.tar.gz:ro,Z";
private static final String WS_AGENT = "ws-agent.tar.gz";
private static final Logger LOG = LoggerFactory.getLogger(ExtServerVolumeProvider.class);
private static final Logger LOG = LoggerFactory.getLogger(WsAgentVolumeProvider.class);
@Inject
@Named("machine.server.ext.archive")
private String extServerArchivePath;
private String wsAgentArchivePath;
@Override
public String get() {
if (SystemInfo.isWindows()) {
try {
final Path cheHome = WindowsHostUtils.ensureCheHomeExist();
final Path path = Files.copy(Paths.get(extServerArchivePath), cheHome.resolve(WS_AGENT), REPLACE_EXISTING);
final Path path = Files.copy(Paths.get(wsAgentArchivePath), cheHome.resolve(WS_AGENT), REPLACE_EXISTING);
return path.toString() + CONTAINER_TARGET;
} catch (IOException e) {
LOG.warn(e.getMessage());
throw new RuntimeException(e);
}
} else {
return extServerArchivePath + CONTAINER_TARGET;
return wsAgentArchivePath + CONTAINER_TARGET;
}
}
}

View File

@ -162,7 +162,8 @@ public class DockerInstanceProviderTest {
EnvironmentContext.setCurrent(envCont);
when(recipeRetriever.getRecipe(any(MachineConfig.class))).thenReturn(new RecipeImpl().withType(DOCKER_FILE_TYPE).withScript("FROM codenvy"));
when(recipeRetriever.getRecipe(any(MachineConfig.class)))
.thenReturn(new RecipeImpl().withType(DOCKER_FILE_TYPE).withScript("FROM codenvy"));
when(dockerMachineFactory.createNode(anyString(), anyString())).thenReturn(dockerNode);
when(dockerConnector.createContainer(any(CreateContainerParams.class)))

View File

@ -1,101 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.docker.machine;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.plugin.docker.client.DockerConnector;
import org.eclipse.che.plugin.docker.client.Exec;
import org.eclipse.che.plugin.docker.client.params.CreateExecParams;
import org.eclipse.che.plugin.docker.client.params.StartExecParams;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.io.IOException;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
/**
* @author Max Shaposhnik (mshaposhnik@codenvy.com)
* @author Alexander Garagatyi
*/
@Listeners(value = {MockitoTestNGListener.class})
public class DockerMachineTerminalLauncherTest {
private static final String LAUNCH_COMMAND = "launch terminal";
private static final String CONTAINER = "test container";
private static final String EXEC_ID = "testExecId";
@Mock
private DockerConnector docker;
@Mock
private Instance testMachineInstance;
@Mock
private DockerInstance dockerInstance;
@Mock
private Exec exec;
private DockerMachineImplTerminalLauncher launcher;
@BeforeMethod
public void setUp() throws Exception {
launcher = new DockerMachineImplTerminalLauncher(docker, LAUNCH_COMMAND);
when(dockerInstance.getContainer()).thenReturn(CONTAINER);
when(docker.createExec(CreateExecParams.create(CONTAINER,
new String[] {"/bin/bash",
"-c",
LAUNCH_COMMAND})
.withDetach(true)))
.thenReturn(exec);
when(exec.getId()).thenReturn(EXEC_ID);
}
@Test
public void shouldReturnDockerMachineType() throws Exception {
assertEquals(launcher.getMachineType(), "docker");
}
@Test(expectedExceptions = MachineException.class,
expectedExceptionsMessageRegExp = "Docker terminal launcher was used to launch terminal in non-docker machine.")
public void shouldThrowExcIfNonDockerInstanceWasPassedAsArgument() throws Exception {
launcher.launchTerminal(testMachineInstance);
}
@Test
public void shouldCreateDetachedExecWithTerminalCommandInBash() throws Exception {
launcher.launchTerminal(dockerInstance);
verify(docker).createExec(CreateExecParams.create(CONTAINER, new String[] {"/bin/bash", "-c", LAUNCH_COMMAND})
.withDetach(true));
}
@Test
public void shouldStartCreatedExec() throws Exception {
launcher.launchTerminal(dockerInstance);
verify(docker).startExec(eq(StartExecParams.create(EXEC_ID)), any());
}
@Test(expectedExceptions = MachineException.class,
expectedExceptionsMessageRegExp = "test error")
public void shouldThrowMachineExceptionIfIOExceptionWasThrownByDocker() throws Exception {
when(docker.createExec(any(CreateExecParams.class))).thenThrow(new IOException("test error"));
launcher.launchTerminal(dockerInstance);
}
}

View File

@ -1910,4 +1910,4 @@
</build>
</profile>
</profiles>
</project>
</project>

View File

@ -1901,4 +1901,4 @@
</build>
</profile>
</profiles>
</project>
</project>

View File

@ -54,6 +54,14 @@
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>

View File

@ -10,10 +10,12 @@
*******************************************************************************/
package org.eclipse.che.plugin.machine.ssh;
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
import org.eclipse.che.api.agent.server.launcher.AbstractAgentLauncher;
import org.eclipse.che.api.agent.server.launcher.ProcessIsLaunchedChecker;
import org.eclipse.che.api.agent.server.terminal.WebsocketTerminalFilesPathProvider;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.util.AbstractLineConsumer;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.util.ListLineConsumer;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
@ -23,7 +25,6 @@ import org.slf4j.Logger;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -34,8 +35,9 @@ import static org.slf4j.LoggerFactory.getLogger;
* Launch websocket terminal in ssh machines.
*
* @author Alexander Garagatyi
* @author Anatolii Bazko
*/
public class SshMachineImplTerminalLauncher implements MachineImplSpecificTerminalLauncher {
public class SshMachineImplTerminalLauncher extends AbstractAgentLauncher {
private static final Logger LOG = getLogger(SshMachineImplTerminalLauncher.class);
// Regex to parse output of command 'uname -sm'
@ -46,62 +48,39 @@ public class SshMachineImplTerminalLauncher implements MachineImplSpecificTermin
private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?<os>[\\S]+) (?<architecture>[\\S]+)");
private static final String DEFAULT_ARCHITECTURE = "linux_amd64";
public static final String TERMINAL_LAUNCH_COMMAND_PROPERTY = "machine.ssh.server.terminal.run_command";
public static final String TERMINAL_LOCATION_PROPERTY = "machine.ssh.server.terminal.location";
private final String runTerminalCommand;
private final String terminalLocation;
private final WebsocketTerminalFilesPathProvider archivePathProvider;
private final String terminalLocation;
@Inject
public SshMachineImplTerminalLauncher(@Named(TERMINAL_LAUNCH_COMMAND_PROPERTY) String runTerminalCommand,
@Named(TERMINAL_LOCATION_PROPERTY) String terminalLocation,
public SshMachineImplTerminalLauncher(@Named("machine.agent.max_start_time_ms") long agentMaxStartTimeMs,
@Named("machine.agent.ping_delay_ms") long agentPingDelayMs,
@Named("machine.ssh.server.terminal.location") String terminalLocation,
WebsocketTerminalFilesPathProvider terminalPathProvider) {
this.runTerminalCommand = runTerminalCommand;
this.terminalLocation = terminalLocation;
super(agentMaxStartTimeMs, agentPingDelayMs, new ProcessIsLaunchedChecker("che-websocket-terminal"));
this.archivePathProvider = terminalPathProvider;
this.terminalLocation = terminalLocation;
}
@Override
public String getMachineType() {
return "ssh";
}
// todo stop outdated terminal
// todo check existing version of terminal, do not copy if it is up to date
@Override
public void launchTerminal(Instance machine) throws MachineException {
try {
if (!isWebsocketTerminalRunning(machine)) {
String architecture = detectArchitecture(machine);
machine.copy(archivePathProvider.getPath(architecture),
terminalLocation);
startTerminal(machine);
}
} catch (ConflictException e) {
// should never happen
throw new MachineException("Internal server error occurs on terminal launching.");
}
public String getAgentName() {
return "org.eclipse.che.terminal";
}
private boolean isWebsocketTerminalRunning(Instance machine) throws MachineException, ConflictException {
InstanceProcess checkTerminalAlive = machine.createProcess(
new CommandImpl("check if che websocket terminal is running",
"ps ax | grep 'che-websocket-terminal' | grep -q -v 'grep che-websocket-terminal' && echo 'found' || echo 'not found'",
null),
null);
ListLineConsumer lineConsumer = new ListLineConsumer();
checkTerminalAlive.start(lineConsumer);
String checkAliveText = lineConsumer.getText();
if ("[STDOUT] not found".equals(checkAliveText)) {
return false;
} else if (!"[STDOUT] found".equals(checkAliveText)) {
LOG.error("Unexpected output of websocket terminal check. Output:" + checkAliveText);
return false;
@Override
public void launch(Instance machine, Agent agent) throws ServerException {
try {
String architecture = detectArchitecture(machine);
machine.copy(archivePathProvider.getPath(architecture), terminalLocation);
super.launch(machine, agent);
} catch (ConflictException e) {
// should never happen
throw new ServerException("Internal server error occurs on terminal launching.");
}
return true;
}
private String detectArchitecture(Instance machine) throws ConflictException, MachineException {
@ -157,18 +136,4 @@ public class SshMachineImplTerminalLauncher implements MachineImplSpecificTermin
return DEFAULT_ARCHITECTURE;
}
}
private void startTerminal(Instance machine) throws MachineException, ConflictException {
InstanceProcess startTerminal = machine.createProcess(new CommandImpl("websocket terminal",
runTerminalCommand,
null),
null);
startTerminal.start(new AbstractLineConsumer() {
@Override
public void writeLine(String line) throws IOException {
machine.getLogger().writeLine("[Terminal] " + line);
}
});
}
}

View File

@ -15,8 +15,6 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import org.eclipse.che.api.agent.server.terminal.MachineImplSpecificTerminalLauncher;
/**
* Provides bindings needed for ssh machine implementation usage.
*
@ -40,16 +38,10 @@ public class SshMachineModule extends AbstractModule {
org.eclipse.che.plugin.machine.ssh.jsch.JschSshClient.class)
.build(SshMachineFactory.class));
Multibinder<MachineImplSpecificTerminalLauncher> terminalLaunchers =
Multibinder.newSetBinder(binder(),
MachineImplSpecificTerminalLauncher.class);
terminalLaunchers.addBinding().to(SshMachineImplTerminalLauncher.class);
bindConstant().annotatedWith(Names.named("machine.ssh.server.terminal.location")).to("~/che");
bindConstant().annotatedWith(Names.named(SshMachineImplTerminalLauncher.TERMINAL_LAUNCH_COMMAND_PROPERTY))
.to("~/che/terminal/che-websocket-terminal -addr :4411 -cmd /bin/bash -static ~/che/terminal/");
bindConstant().annotatedWith(Names.named(SshMachineImplTerminalLauncher.TERMINAL_LOCATION_PROPERTY))
.to("~/che/terminal/");
Multibinder.newSetBinder(binder(), org.eclipse.che.api.agent.server.launcher.AgentLauncher.class)
.addBinding().to(org.eclipse.che.plugin.machine.ssh.SshMachineImplTerminalLauncher.class);
Multibinder<org.eclipse.che.api.core.model.machine.ServerConf> machineServers =
Multibinder.newSetBinder(binder(),

21
pom.xml
View File

@ -66,7 +66,7 @@
<groupId>org.eclipse.che</groupId>
<artifactId>assembly-wsagent-server</artifactId>
<version>${che.version}</version>
<type>zip</type>
<type>tar.gz</type>
</dependency>
<dependency>
<groupId>org.eclipse.che</groupId>
@ -80,6 +80,16 @@
<version>${che.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
<version>${che.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
@ -319,7 +329,7 @@
<groupId>org.eclipse.che.lib</groupId>
<artifactId>che-websocket-terminal</artifactId>
<version>${che.lib.version}</version>
<type>zip</type>
<type>tar.gz</type>
<classifier>linux_amd64</classifier>
</dependency>
<dependency>
@ -327,6 +337,13 @@
<artifactId>che-websocket-terminal</artifactId>
<version>${che.lib.version}</version>
<type>zip</type>
<classifier>linux_amd64</classifier>
</dependency>
<dependency>
<groupId>org.eclipse.che.lib</groupId>
<artifactId>che-websocket-terminal</artifactId>
<version>${che.lib.version}</version>
<type>tar.gz</type>
<classifier>linux_arm7</classifier>
</dependency>
<dependency>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2016 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-master-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.0.0-M1-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-agent-shared</artifactId>
<packaging>jar</packaging>
<name>Che Core :: API :: Agent :: Shared</name>
<dependencies>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.shared.dto;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.dto.shared.DTO;
import java.util.List;
import java.util.Map;
/**
* @author Anatoliy Bazko
*/
@DTO
public interface AgentDto extends Agent {
@Override
String getName();
void setName(String name);
AgentDto withName(String name);
@Override
String getVersion();
void setVersion(String version);
AgentDto withVersion(String version);
@Override
List<String> getDependencies();
void setDependencies(List<String> dependencies);
AgentDto withDependencies(List<String> dependencies);
@Override
String getScript();
void setScript(String script);
AgentDto withScript(String script);
@Override
Map<String, String> getProperties();
void setProperties(Map<String, String> properties);
AgentDto withProperties(Map<String, String> properties);
}

View File

@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.shared.dto;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.dto.shared.DTO;
/**
* @author Anatolii Bazko
*/
@DTO
public interface AgentKeyDto extends AgentKey {
String getName();
void setName(String name);
AgentKeyDto withName(String name);
String getVersion();
void setVersion(String version);
AgentKeyDto withVersion(String version);
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.shared.model;
import java.util.List;
import java.util.Map;
/**
* An entity that might additionally injected into machine and brings functionality.
*
* @author Anatoliy Bazko
*/
public interface Agent {
/**
* Returns the name of the agent.
*/
String getName();
/**
* Returns the version of the agent.
*/
String getVersion();
/**
* Returns the depending agents, that must be applied before.
*/
List<String> getDependencies();
/**
* Returns the script to be applied when machine is started.
*/
String getScript();
/**
* Returns any machine specific properties.
*/
Map<String, String> getProperties();
}

View File

@ -8,18 +8,25 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.wsagent;
package org.eclipse.che.api.agent.shared.model;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Starts ws agent in the machine and wait until ws agent sends notification about its start
* A pair of name and version of the agent.
* Version part is not mandatory.
*
* @author Alexander Garagatyi
* @author Anatolii Bazko
*/
public interface WsAgentLauncher {
void startWsAgent(Machine devMachine) throws NotFoundException,
ServerException;
public interface AgentKey {
/**
* @return the name of the agent
*/
String getName();
/**
* @return the version of the agent
*/
@Nullable
String getVersion();
}

View File

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2016 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-master-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.0.0-M1-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-agent</artifactId>
<packaging>jar</packaging>
<name>Che Core :: API :: Agent</name>
<properties>
<dto-generator-out-directory>${project.build.directory}/generated-sources/dto/</dto-generator-out-directory>
<findbugs.failonerror>false</findbugs.failonerror>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-machine</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-model</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-assured</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>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<outputDirectory>target/classes</outputDirectory>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>${project.build.directory}/generated-sources/dto/</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-resource</id>
<phase>process-sources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${dto-generator-out-directory}/META-INF</directory>
<targetPath>META-INF</targetPath>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>add-source</id>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${dto-generator-out-directory}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>pre-compile</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>generate-server-dto</id>
<phase>process-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<dtoPackages>
<package>org.eclipse.che.api.agent.shared.dto</package>
</dtoPackages>
<outputDirectory>${dto-generator-out-directory}</outputDirectory>
<genClassName>org.eclipse.che.api.agent.server.dto.DtoServerImpls</genClassName>
<impl>server</impl>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server;
import com.google.inject.AbstractModule;
/**
* @author Anatolii Bazko
*/
public class AgentModule extends AbstractModule {
@Override
protected void configure() {
bind(AgentRegistryService.class);
}
}

View File

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import java.util.List;
/**
* The registry for agents that might be injected into machine.
*
* @see Agent
* @see AgentKey
*
* @author Anatoliy Bazko
*/
public interface AgentRegistry {
/**
* Gets {@link Agent}.
*
* @param agentKey
* the agent key
* @return {@link Agent}
* @throws AgentNotFoundException
* if agent not found in the registry
* @throws AgentException
* if unexpected error occurred
*/
Agent getAgent(AgentKey agentKey) throws AgentException;
/**
* Returns a list of the available versions of the specific agent.
*
* @param name
* the name of the agent
* @return list of versions
* @throws AgentNotFoundException
* if agent not found in the registry
* @throws AgentException
* if unexpected error occurred
*/
List<String> getVersions(String name) throws AgentException;
/**
* Returns the list of available agents.
*
* @return list of agents
* @throws AgentException
* if unexpected error occurred
*/
List<String> getAgents() throws AgentException;
}

View File

@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import com.google.inject.Inject;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.eclipse.che.api.agent.shared.dto.AgentDto;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.Service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import java.util.List;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.agent.server.DtoConverter.asDto;
/**
* Defines Agent REST API.
*
* @see AgentRegistry
* @see Agent
*
* @author Anatoliy Bazko
*/
@Api(value = "/agent", description = "Agent REST API")
@Path("/agent")
public class AgentRegistryService extends Service {
private final AgentRegistry agentRegistry;
@Inject
public AgentRegistryService(AgentRegistry agentRegistry) {
this.agentRegistry = agentRegistry;
}
@GET
@Path("/name/{name}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Gets the latest version of the agent", response = AgentDto.class)
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested agent entity"),
@ApiResponse(code = 404, message = "Agent not found in the registry"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public Agent getByName(@ApiParam("The agent name") @PathParam("name") String name) throws ApiException {
try {
return asDto(agentRegistry.getAgent(new AgentKeyImpl(name)));
} catch (AgentNotFoundException e) {
throw new NotFoundException(e.getMessage());
} catch (AgentException e) {
throw new ServerException(e.getMessage(), e);
}
}
@GET
@Path("/name/{name}/version/{version}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Gets the specific version of the agent", response = AgentDto.class)
@ApiResponses({@ApiResponse(code = 200, message = "The response contains requested agent entity"),
@ApiResponse(code = 404, message = "Agent not found in the registry"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public Agent getByName(@ApiParam("The agent name") @PathParam("name") String name,
@ApiParam("The agent version") @PathParam("version") String version) throws ApiException {
try {
return asDto(agentRegistry.getAgent(new AgentKeyImpl(name, version)));
} catch (AgentNotFoundException e) {
throw new NotFoundException(e.getMessage());
} catch (AgentException e) {
throw new ServerException(e.getMessage(), e);
}
}
@GET
@Path("/versions/{name}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Get a list of available versions of the giving agent", response = List.class)
@ApiResponses({@ApiResponse(code = 200, message = "The response contains available versions of the giving agent"),
@ApiResponse(code = 404, message = "Agent not found"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public List<String> getVersions(@ApiParam("The agent name") @PathParam("name") String name) throws ApiException {
try {
return agentRegistry.getVersions(name);
} catch (AgentNotFoundException e) {
throw new NotFoundException(e.getMessage());
} catch (AgentException e) {
throw new ServerException(e.getMessage(), e);
}
}
@GET
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Get a list of the available agents", response = List.class)
@ApiResponses({@ApiResponse(code = 200, message = "The response contains list of available agents"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public List<String> getAgents() throws ApiException {
try {
return agentRegistry.getAgents();
} catch (AgentNotFoundException e) {
throw new NotFoundException(e.getMessage());
} catch (AgentException e) {
throw new ServerException(e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import java.net.URL;
/**
* @author Anatolii Bazko
*/
public interface AgentRegistryUrlProvider {
/**
* Returns url to download agent of the specific version.
*
* @param agentKey
* the agent name and version
* @return {@link URL}
* @throws AgentException
* if unexpected error occurred
*/
URL getAgentUrl(AgentKey agentKey) throws AgentException;
/**
* Returns url to fetch available versions of the agent.
* @param name
* the agent name
* @return {@link URL}
* @throws AgentException
* if unexpected error occurred
*/
URL getAgentVersionsUrl(String name) throws AgentException;
}

View File

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server;
import org.eclipse.che.api.agent.shared.dto.AgentDto;
import org.eclipse.che.api.agent.shared.model.Agent;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
/**
* @author Anatolii Bazko
*/
public class DtoConverter {
public static AgentDto asDto(Agent agent) {
return newDto(AgentDto.class).withName(agent.getName())
.withVersion(agent.getVersion())
.withProperties(agent.getProperties())
.withScript(agent.getScript())
.withDependencies(agent.getDependencies());
}
private DtoConverter() { }
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.exception;
/**
* @author Anatoliy Bazko
*/
public class AgentException extends Exception {
public AgentException(String message) {
super(message);
}
public AgentException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.exception;
/**
* @author Anatolii Bazko
*/
public class AgentNotFoundException extends AgentException {
public AgentNotFoundException(String message) {
super(message);
}
public AgentNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.AgentRegistryUrlProvider;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.shared.dto.AgentDto;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.api.core.util.FileCleaner;
import org.eclipse.che.dto.server.DtoFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.List;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.eclipse.che.commons.lang.IoUtil.downloadFile;
import static org.eclipse.che.commons.lang.IoUtil.readAndCloseQuietly;
/**
* @author Anatoliy Bazko
*/
@Singleton
public class AgentRegistryImpl implements AgentRegistry {
private final AgentRegistryUrlProvider urlProvider;
private final HttpJsonRequestFactory requestFactory;
@Inject
public AgentRegistryImpl(AgentRegistryUrlProvider urlProvider,
HttpJsonRequestFactory requestFactory) {
this.urlProvider = urlProvider;
this.requestFactory = requestFactory;
}
@Override
public Agent getAgent(AgentKey agentKey) throws AgentException {
return doGetRemoteAgent(urlProvider.getAgentUrl(agentKey));
}
@Override
public List<String> getVersions(String name) throws AgentException {
URL url = urlProvider.getAgentVersionsUrl(name);
try {
HttpJsonResponse response = requestFactory.fromUrl(url.toString()).useGetMethod().request();
@SuppressWarnings("unchecked")
List<String> versions = response.as(List.class, new TypeToken<List<String>>() { }.getType());
return versions;
} catch (NotFoundException e) {
throw new AgentNotFoundException(format("Agent %s not found", name), e);
} catch (IOException | ApiException e) {
throw new AgentException(format("Can't fetch available %s version.", name), e);
}
}
@Override
public List<String> getAgents() throws AgentException {
return asList("org.eclipse.che.terminal",
"org.eclipse.che.ws-agent",
"org.eclipse.che.ssh");
}
protected Agent doGetRemoteAgent(URL url) throws AgentException {
File agent = null;
try {
agent = downloadFile(new File(System.getProperty("java.io.tmpdir")), "agent", ".tmp", url);
String json = readAndCloseQuietly(new FileInputStream(agent));
return DtoFactory.getInstance().createDtoFromJson(json, AgentDto.class);
} catch (IOException | IllegalArgumentException e) {
throw new AgentException("Can't fetch agent configuration", e);
} finally {
if (agent != null) {
try {
Files.delete(agent.toPath());
} catch (IOException e) {
FileCleaner.addFile(agent);
}
}
}
}
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.server.AgentRegistryUrlProvider;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import javax.inject.Named;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Provides urls to agents.
*
* @author Anatolii Bazko
*/
@Singleton
public class AgentRegistryUrlProviderImpl implements AgentRegistryUrlProvider {
public static final String VERSION = "${version}";
public static final String NAME = "${name}";
private final String agentLatestVersionUrl;
private final String agentSpecificVersionUrl;
private final String agentVersionsUrl;
@Inject
public AgentRegistryUrlProviderImpl(@Named("machine.agent.latest_version_url") String agentLatestVersionUrl,
@Named("machine.agent.specific_version_url") String agentSpecificVersionUrl,
@Named("machine.agent.all_versions_url") String agentVersionsUrl) {
this.agentLatestVersionUrl = agentLatestVersionUrl;
this.agentSpecificVersionUrl = agentSpecificVersionUrl;
this.agentVersionsUrl = agentVersionsUrl;
}
@Override
public URL getAgentUrl(AgentKey agentKey) throws AgentException {
String url;
if (agentKey.getVersion() == null) {
url = agentLatestVersionUrl.replace(NAME, agentKey.getName());
} else {
url = agentSpecificVersionUrl.replace(NAME, agentKey.getName()).replace(VERSION, agentKey.getVersion());
}
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new AgentException("Malformed url " + url, e);
}
}
@Override
public URL getAgentVersionsUrl(String name) throws AgentException {
String url = agentVersionsUrl.replace(NAME, name);
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new AgentException("Malformed url " + url, e);
}
}
}

View File

@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* Sort agents respecting dependencies between them.
*
* @author Anatolii Bazko
*/
@Singleton
public class AgentSorter {
private final AgentRegistry agentRegistry;
@Inject
public AgentSorter(AgentRegistry agentRegistry) {
this.agentRegistry = agentRegistry;
}
/**
* Sort agents respecting dependencies between them.
* Handles circular dependencies.
*
* @see AgentKey
* @see Agent#getDependencies()
* @see AgentRegistry#getAgent(AgentKey)
*
* @param agentKeys list of agents to sort
* @return list of created agents in proper order
*
* @throws AgentException
* if circular dependency found or agent creation failed or other unexpected error
*/
public List<AgentKey> sort(List<String> agentKeys) throws AgentException {
List<AgentKey> sorted = new ArrayList<>();
Set<String> pending = new HashSet<>();
if (agentKeys != null) {
for (String agentKey : agentKeys) {
doSort(agentKeys, AgentKeyImpl.parse(agentKey), sorted, pending);
}
}
return sorted;
}
private void doSort(List<String> agentKeys, AgentKey agentKey, List<AgentKey> sorted, Set<String> pending) throws AgentException {
String agentName = agentKey.getName();
Optional<AgentKey> alreadySorted = sorted.stream().filter(k -> k.getName().equals(agentName)).findFirst();
if (alreadySorted.isPresent()) {
return;
}
if (!pending.add(agentName)) {
throw new AgentException("Agents circular dependency found.");
}
Agent agent = agentRegistry.getAgent(agentKey);
for (String dependency : agent.getDependencies()) {
if (agentKeys.contains(dependency)) {
doSort(agentKeys, AgentKeyImpl.parse(dependency), sorted, pending);
}
}
sorted.add(agentKey);
pending.remove(agentName);
}
}

View File

@ -0,0 +1,152 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.shared.dto.AgentDto;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.api.core.util.FileCleaner;
import org.eclipse.che.commons.lang.ZipUtils;
import org.eclipse.che.dto.server.DtoFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static org.eclipse.che.commons.lang.IoUtil.downloadFile;
import static org.eclipse.che.commons.lang.IoUtil.readAndCloseQuietly;
import static org.eclipse.che.commons.lang.ZipUtils.isZipFile;
/**
* Local implementation of the {@link AgentRegistry}.
* The name of the agent might represent a url.
*
* @author Anatoliy Bazko
*/
@Singleton
public class LocalAgentRegistryImpl implements AgentRegistry {
protected static final Logger LOG = LoggerFactory.getLogger(LocalAgentRegistryImpl.class);
private static final Pattern AGENTS = Pattern.compile(".*[//]?agents/[^//]+[.]json");
private final Map<String, Agent> agents;
private final List<String> agentNames;
@Inject
public LocalAgentRegistryImpl() throws IOException {
this.agents = new HashMap<>();
findAgents();
this.agentNames = ImmutableList.copyOf(agents.keySet());
}
@Override
public Agent getAgent(AgentKey agentKey) throws AgentException {
return doGetAgent(agentKey.getName());
}
@Override
public List<String> getVersions(String name) throws AgentException {
Agent agent = doGetAgent(name);
String version = agent.getVersion();
return version == null ? Collections.emptyList() : singletonList(version);
}
@Override
public List<String> getAgents() throws AgentException {
return agentNames;
}
protected Agent doGetAgent(String name) throws AgentException {
try {
URL url = new URL(name);
return doGetRemoteAgent(url);
} catch (MalformedURLException ignored) {
// name doesn't represent a url
}
Optional<Agent> agent = Optional.ofNullable(agents.get(name));
return agent.orElseThrow(() -> new AgentNotFoundException(format("Agent %s not found", name)));
}
protected Agent doGetRemoteAgent(URL url) throws AgentException {
File agent = null;
try {
agent = downloadFile(new File(System.getProperty("java.io.tmpdir")), "agent", ".tmp", url);
String json = readAndCloseQuietly(new FileInputStream(agent));
return DtoFactory.getInstance().createDtoFromJson(json, AgentDto.class);
} catch (IOException | IllegalArgumentException e) {
throw new AgentException("Can't fetch agent configuration", e);
} finally {
if (agent != null) {
try {
Files.delete(agent.toPath());
} catch (IOException e) {
FileCleaner.addFile(agent);
}
}
}
}
protected void findAgents() throws IOException {
File context = new File(LocalAgentRegistryImpl.class.getProtectionDomain().getCodeSource().getLocation().getPath());
if (isZipFile(context)) {
try (ZipFile zip = new ZipFile(context)) {
ZipUtils.getResources(zip, AGENTS, createAgentsConsumer);
}
} else {
Files.walk(context.toPath())
.filter(path -> AGENTS.matcher(path.toString()).matches())
.forEach(path -> {
try (InputStream in = Files.newInputStream(path)) {
createAgentsConsumer.accept(in);
} catch (IOException ignored) {
// ignore
}
});
}
}
private Consumer<InputStream> createAgentsConsumer = new Consumer<InputStream>() {
@Override
public void accept(InputStream inputStream) {
try {
final Agent agent = DtoFactory.getInstance().createDtoFromJson(inputStream, AgentDto.class);
agents.put(agent.getName(), agent);
} catch (IOException e) {
LOG.error("Can't create agent.", e);
}
}
};
}

View File

@ -0,0 +1,121 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.util.AbstractLineConsumer;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static java.lang.String.format;
/**
* Launch agent script asynchronously over target instance and wait when it run.
* The policy of checking if agent is run might be different for agents.
*
* @see Agent#getScript()
* @see AgentLaunchingChecker
* @see AgentLaunchingChecker#DEFAULT
*
* @author Anatolii Bazko
*/
public abstract class AbstractAgentLauncher implements AgentLauncher {
private static final Logger LOG = LoggerFactory.getLogger(AbstractAgentLauncher.class);
private static final ExecutorService executor =
Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("AgentLauncher-%d")
.setDaemon(true)
.build());
private final AgentLaunchingChecker agentLaunchingChecker;
private final long agentPingDelayMs;
private final long agentMaxStartTimeMs;
public AbstractAgentLauncher(long agentMaxStartTimeMs,
long agentPingDelayMs,
AgentLaunchingChecker agentLaunchingChecker) {
this.agentPingDelayMs = agentPingDelayMs;
this.agentMaxStartTimeMs = agentMaxStartTimeMs;
this.agentLaunchingChecker = agentLaunchingChecker;
}
@Override
public void launch(Instance machine, Agent agent) throws ServerException {
try {
final InstanceProcess process = start(machine, agent);
LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agent.getName(), machine.getWorkspaceId());
final long pingStartTimestamp = System.currentTimeMillis();
while (System.currentTimeMillis() - pingStartTimestamp < agentMaxStartTimeMs) {
if (agentLaunchingChecker.isLaunched(agent, process, machine)) {
return;
} else {
Thread.sleep(agentPingDelayMs);
}
}
process.kill();
} catch (MachineException e) {
throw new ServerException(e.getServiceError());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ServerException(format("Launching agent %s is interrupted", agent.getName()));
}
final String errMsg = format("Fail launching agent %s. Workspace ID:%s", agent.getName(), machine.getWorkspaceId());
LOG.error(errMsg);
throw new ServerException(errMsg);
}
protected InstanceProcess start(final Instance machine, final Agent agent) throws ServerException {
final Command command = new CommandImpl(agent.getName(), agent.getScript(), "agent");
final InstanceProcess process = machine.createProcess(command, null);
final LineConsumer lineConsumer = new AbstractLineConsumer() {
@Override
public void writeLine(String line) throws IOException {
machine.getLogger().writeLine(line);
}
};
executor.execute(ThreadLocalPropagateContext.wrap(() -> {
try {
process.start(lineConsumer);
} catch (ConflictException | MachineException e) {
try {
machine.getLogger().writeLine(format("[ERROR] %s", e.getMessage()));
} catch (IOException ignored) {
}
} finally {
try {
lineConsumer.close();
} catch (IOException ignored) {
}
}
}));
return process;
}
}

View File

@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.machine.server.spi.Instance;
/**
* Launches {@link Agent#getScript()} on the {@link Instance}.
*
* @see Instance
* @see Agent#getScript()
*
* @author Anatolii Bazko
*/
public interface AgentLauncher {
/**
* @return the name of the agent that launcher is designed for
*/
String getAgentName();
/**
* @return the machine type that launcher is designed for
*/
String getMachineType();
/**
* Executes agents scripts over target machine.
* The machine should be started.
*
* @see Agent#getScript()
*
* @param machine
* the machine instance
* @param agent
* the agent
* @throws ServerException
* if script execution failed
*/
void launch(Instance machine, Agent agent) throws ServerException;
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.model.machine.MachineConfig;
import java.util.Set;
/**
* Provides {@link AgentLauncher} for specific agent to be run on instance.
* Returning agent depends on machine type. If no agent found then the default one
* will be returned.
*
* @author Anatolii Bazko
*/
@Singleton
public class AgentLauncherFactory {
private final Set<AgentLauncher> launchers;
private final AgentLauncher defaultLauncher;
@Inject
public AgentLauncherFactory(Set<AgentLauncher> launchers, DefaultAgentLauncher defaultLauncher) {
this.launchers = launchers;
this.defaultLauncher = defaultLauncher;
}
/**
* Find launcher for given agent independently of version.
* If the specific {@link AgentLauncher} isn't registered then the default one will be used.
*
* @see Agent#getName()
* @see MachineConfig#getType()
*
* @param agentName
* the agent name
* @param machineType
* the machine type
* @return {@link AgentLauncher}
*/
public AgentLauncher find(String agentName, String machineType) {
return launchers.stream()
.filter(l -> l.getAgentName().equals(agentName))
.filter(l -> l.getMachineType().equals(machineType))
.findAny()
.orElse(defaultLauncher);
}
}

View File

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
/**
* Indicates if agent finished working.
*
* @author Anatoliy Bazko
*/
@FunctionalInterface
public interface AgentLaunchingChecker {
/**
* Indicates if agent finished working.
*
* @param agent
* the agent is being launched
* @param process
* the process is generated by launching the target agent
* @param machine
* the machine instance
* @throws MachineException
* if unexpected error is occurred
*/
boolean isLaunched(Agent agent, InstanceProcess process, Instance machine) throws MachineException;
AgentLaunchingChecker DEFAULT = (agent, process, machine) -> !process.isAlive();
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import javax.inject.Named;
/**
* Launches agent and waits while it is finished.
*
* This agents is suited only for those types of agents that install software
* and finish working without launching any processes at the end.
*
* @author Anatolii Bazko
*/
@Singleton
public class DefaultAgentLauncher extends AbstractAgentLauncher {
@Inject
public DefaultAgentLauncher(@Named("machine.agent.max_start_time_ms") long agentMaxStartTimeMs,
@Named("machine.agent.ping_delay_ms") long agentPingDelayMs) {
super(agentMaxStartTimeMs, agentPingDelayMs, AgentLaunchingChecker.DEFAULT);
}
@Override
public String getAgentName() {
return "any";
}
@Override
public String getMachineType() {
return "any";
}
}

View File

@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.util.ListLineConsumer;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
import static java.lang.String.format;
/**
* Verifies if agent started a process with specific name.
* It is an indicator that process had been finished.
*
* @author Anatoliy Bazko
*/
public class ProcessIsLaunchedChecker implements AgentLaunchingChecker {
private final String processNameToWait;
private long counter;
public ProcessIsLaunchedChecker(String processNameToWait) {
this.processNameToWait = processNameToWait;
}
@Override
public boolean isLaunched(Agent agent, InstanceProcess process, Instance machine) throws MachineException {
Command command = new CommandImpl(format("Wait for %s, try %d", agent.getName(), ++counter),
format("ps -fC %s 1>/dev/null && echo 0 || echo 1", processNameToWait),
"test");
try (ListLineConsumer lineConsumer = new ListLineConsumer()) {
InstanceProcess waitProcess = machine.createProcess(command, null);
waitProcess.start(lineConsumer);
return lineConsumer.getText().endsWith("[STDOUT] 0");
} catch (ConflictException e) {
throw new MachineException(e.getServiceError());
}
}
}

View File

@ -0,0 +1,114 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.model.impl;
import com.google.common.base.MoreObjects;
import org.eclipse.che.api.agent.shared.model.Agent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author Anatoliy Bazko
*/
public class AgentImpl implements Agent {
private final String name;
private final String version;
private final List<String> dependencies;
private final Map<String, String> properties;
private final String script;
public AgentImpl(String name,
String version,
List<String> dependencies,
Map<String, String> properties,
String script) {
this.name = name;
this.version = version;
this.dependencies = dependencies;
this.properties = properties;
this.script = script;
}
public AgentImpl(Agent agent) {
this(agent.getName(),
agent.getVersion(),
agent.getDependencies(),
agent.getProperties(),
agent.getScript());
}
@Override
public String getName() {
return name;
}
@Override
public String getVersion() {
return version;
}
@Override
public List<String> getDependencies() {
return MoreObjects.firstNonNull(dependencies, new ArrayList<String>());
}
@Override
public Map<String, String> getProperties() {
return MoreObjects.firstNonNull(properties, new HashMap<String, String>());
}
@Override
public String getScript() {
return script;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AgentImpl)) {
return false;
}
final AgentImpl that = (AgentImpl)obj;
return Objects.equals(name, that.name)
&& Objects.equals(version, that.version)
&& getDependencies().equals(that.getDependencies())
&& getProperties().equals(that.getProperties())
&& Objects.equals(script, that.script);
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(name);
hash = 31 * hash + Objects.hashCode(version);
hash = 31 * hash + getDependencies().hashCode();
hash = 31 * hash + getProperties().hashCode();
hash = 31 * hash + Objects.hashCode(script);
return hash;
}
@Override
public String toString() {
return "AgentImpl{" +
"name='" + name + '\'' +
", version='" + version + '\'' +
", dependencies='" + dependencies + '\'' +
", properties='" + properties + "\'}";
}
}

View File

@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.model.impl;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.commons.annotation.Nullable;
import java.util.Objects;
/**
* @author Anatolii Bazko
*/
public class AgentKeyImpl implements AgentKey {
private final String name;
private final String version;
public AgentKeyImpl(String name, @Nullable String version) {
this.name = name;
this.version = version;
}
public AgentKeyImpl(String name) {
this(name, null);
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
/**
* Factory method. Agent key is basically a string meeting the format: {@code name:version}.
* The version part can be omitted.
*
* @throws IllegalArgumentException
* in case of wrong format
*/
public static AgentKeyImpl parse(String agentKey) throws IllegalArgumentException {
String[] parts = agentKey.split(":");
if (parts.length == 1) {
return new AgentKeyImpl(parts[0], null);
} else if (parts.length == 2) {
return new AgentKeyImpl(parts[0], parts[1]);
} else {
throw new IllegalArgumentException("Illegal format: " + agentKey);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AgentKeyImpl)) return false;
AgentKeyImpl agentKey = (AgentKeyImpl)o;
return Objects.equals(name, agentKey.name) &&
Objects.equals(version, agentKey.version);
}
@Override
public int hashCode() {
return Objects.hash(name, version);
}
public String asString() {
return name + (version != null ? ":" + version : "");
}
@Override
public String toString() {
return "AgentImpl{" +
"name='" + name + '\'' +
", version='" + version + "\'}";
}
}

View File

@ -0,0 +1,6 @@
{
"name": "org.eclipse.che.ssh",
"dependencies": [],
"properties": {},
"script" : "#\n# Copyright (c) 2012-2016 Codenvy, S.A.\n# All rights reserved. This program and the accompanying materials\n# are made available under the terms of the Eclipse Public License v1.0\n# which accompanies this distribution, and is available at\n# http://www.eclipse.org/legal/epl-v10.html\n#\n# Contributors:\n# Codenvy, S.A. - initial API and implementation\n#\n\nunset SUDO\nunset PACKAGES\ntest \"$(id -u)\" = 0 || SUDO=\"sudo\"\n\nLINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')\nLINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)\n\n###############################\n### Install Needed packaged ###\n###############################\n\n# Red Hat Enterprise Linux 7 \n############################\nif echo ${LINUX_TYPE} | grep -qi \"rhel\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh-server\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum -y install ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# Ubuntu 14.04 16.04 / Linux Mint 17 \n####################################\nelif echo ${LINUX_TYPE} | grep -qi \"ubuntu\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh-server\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# Debian 8\n##########\nelif echo ${LINUX_TYPE} | grep -qi \"debian\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh-server\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# Fedora 23\n###########\nelif echo ${LINUX_TYPE} | grep -qi \"fedora\"; then\n PACKAGES=${PACKAGES}\" procps-ng\"\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh-server\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} dnf -y install ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# CentOS 7.1 & Oracle Linux 7.1\n###############################\nelif echo ${LINUX_TYPE} | grep -qi \"centos\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh-server\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum -y install ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# openSUSE 13.2\n###############\nelif echo ${LINUX_TYPE} | grep -qi \"opensuse\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openSSH\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} zypper install -y ${PACKAGES};\n }\n ${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\n# Alpine 3.3\n############$$\nelif echo ${LINUX_TYPE} | grep -qi \"alpine\"; then\n command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" openssh\"; }\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apk update;\n ${SUDO} apk add openssh ${PACKAGES};\n }\n\nelse\n >&2 echo \"Unrecognized Linux Type\"\n >&2 cat /etc/os-release\n exit 1\nfi\n\nps -fC sshd && exit\n\n${SUDO} mkdir -p /var/run/sshd\n${SUDO} /usr/bin/ssh-keygen -A && ${SUDO} /usr/sbin/sshd -D\n"
}

View File

@ -0,0 +1,8 @@
{
"name": "org.eclipse.che.terminal",
"dependencies": [],
"properties": {
"ports": "terminal:4411/tcp"
},
"script" : "#\n# Copyright (c) 2012-2016 Codenvy, S.A.\n# All rights reserved. This program and the accompanying materials\n# are made available under the terms of the Eclipse Public License v1.0\n# which accompanies this distribution, and is available at\n# http://www.eclipse.org/legal/epl-v10.html\n#\n# Contributors:\n# Codenvy, S.A. - initial API and implementation\n#\n\nunset PACKAGES\nunset SUDO\ncommand -v tar >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" tar\"; }\ncommand -v curl >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" curl\"; }\ntest \"$(id -u)\" = 0 || SUDO=\"sudo\"\n\nCHE_DIR=$HOME/che\nAGENT_BINARIES_URI=\"file:///mnt/che/terminal/websocket-terminal-\\${PREFIX}.tar.gz\"\nTARGET_AGENT_BINARIES_URI=\"file://\"${CHE_DIR}\"/websocket-terminal-\\${PREFIX}.tar.gz\"\nLINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')\nLINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)\nMACHINE_TYPE=$(uname -m)\n\nmkdir -p ${CHE_DIR}\n\n########################\n### Install packages ###\n########################\n\n# Red Hat Enterprise Linux 7 \n############################\nif echo ${LINUX_TYPE} | grep -qi \"rhel\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum install ${PACKAGES};\n }\n\n# Ubuntu 14.04 16.04 / Linux Mint 17 \n####################################\nelif echo ${LINUX_TYPE} | grep -qi \"ubuntu\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n\n# Debian 8\n##########\nelif echo ${LINUX_TYPE} | grep -qi \"debian\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n\n# Fedora 23 \n###########\nelif echo ${LINUX_TYPE} | grep -qi \"fedora\"; then\n PACKAGES=${PACKAGES}\" procps-ng\"\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} dnf -y install ${PACKAGES};\n }\n\n# CentOS 7.1 & Oracle Linux 7.1\n###############################\nelif echo ${LINUX_TYPE} | grep -qi \"centos\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum -y install ${PACKAGES};\n }\n\n# openSUSE 13.2\n###############\nelif echo ${LINUX_TYPE} | grep -qi \"opensuse\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} zypper install -y ${PACKAGES};\n }\n\n# Alpine 3.3\n############$$\nelif echo ${LINUX_TYPE} | grep -qi \"alpine\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apk update\n ${SUDO} apk add ${PACKAGES};\n }\n\nelse\n >&2 echo \"Unrecognized Linux Type\"\n >&2 cat /etc/os-release\n exit 1\nfi\n\nps -fC che-websocket-terminal && exit\n\n########################\n### Install Terminal ###\n########################\nif echo ${MACHINE_TYPE} | grep -qi \"x86_64\"; then\n PREFIX=linux_amd64\nelif echo ${MACHINE_TYPE} | grep -qi \"arm5\"; then\n PREFIX=linux_arm7\nelif echo ${MACHINE_TYPE} | grep -qi \"arm6\"; then\n PREFIX=linux_arm7\nelif echo ${MACHINE_TYPE} | grep -qi \"arm7\"; then\n PREFIX=linux_arm7\nelse\n >&2 echo \"Unrecognized Machine Type\"\n >&2 uname -a\n exit 1\nfi\n\nif curl -o /dev/null --silent --head --fail $(echo ${AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g'); then\n curl -o $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g' | sed -s 's/file:\\/\\///g') -s $(echo ${AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g')\nelif curl -o /dev/null --silent --head --fail $(echo ${AGENT_BINARIES_URI} | sed -s 's/-\\${PREFIX}//g'); then\n curl -o $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g' | sed -s 's/file:\\/\\///g') -s $(echo ${AGENT_BINARIES_URI} | sed -s 's/-\\${PREFIX}//g')\nfi\n\ncurl -s $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g') | tar xzf - -C ${CHE_DIR}\n$HOME/che/terminal/che-websocket-terminal -addr :4411 -cmd /bin/bash -static $HOME/che/terminal/"
}

View File

@ -0,0 +1,8 @@
{
"name": "org.eclipse.che.ws-agent",
"dependencies": ["org.eclipse.che.terminal"],
"properties": {
"ports": "ws-agent.debug:4403/tcp,ws-agent:4401/tcp"
},
"script" : "#\n# Copyright (c) 2012-2016 Codenvy, S.A.\n# All rights reserved. This program and the accompanying materials\n# are made available under the terms of the Eclipse Public License v1.0\n# which accompanies this distribution, and is available at\n# http://www.eclipse.org/legal/epl-v10.html\n#\n# Contributors:\n# Codenvy, S.A. - initial API and implementation\n#\n\nunset PACKAGES\nunset SUDO\ncommand -v tar >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" tar\"; }\ncommand -v curl >/dev/null 2>&1 || { PACKAGES=${PACKAGES}\" curl\"; }\ntest \"$(id -u)\" = 0 || SUDO=\"sudo\"\n\nAGENT_BINARIES_URI=file:///mnt/che/ws-agent.tar.gz\nCHE_DIR=$HOME/che\nLINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')\nLINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)\nMACHINE_TYPE=$(uname -m)\n\nmkdir -p ${CHE_DIR}\n${SUDO} mkdir -p /projects\n${SUDO} sh -c \"chown -R $(id -u -n) /projects\"\n\n########################\n### Install packages ###\n########################\n\n# Red Hat Enterprise Linux 7\n############################\nif echo ${LINUX_TYPE} | grep -qi \"rhel\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum install ${PACKAGES};\n }\n\n# Ubuntu 14.04 16.04 / Linux Mint 17\n####################################\nelif echo ${LINUX_TYPE} | grep -qi \"ubuntu\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n\n# Debian 8\n##########\nelif echo ${LINUX_TYPE} | grep -qi \"debian\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apt-get update;\n ${SUDO} apt-get -y install ${PACKAGES};\n }\n\n# Fedora 23\n###########\nelif echo ${LINUX_TYPE} | grep -qi \"fedora\"; then\n PACKAGES=${PACKAGES}\" procps-ng\"\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} dnf -y install ${PACKAGES};\n }\n\n# CentOS 7.1 & Oracle Linux 7.1\n###############################\nelif echo ${LINUX_TYPE} | grep -qi \"centos\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} yum -y install ${PACKAGES};\n }\n\n# openSUSE 13.2\n###############\nelif echo ${LINUX_TYPE} | grep -qi \"opensuse\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} zypper install -y ${PACKAGES};\n }\n\n# Alpine 3.3\n############$$\nelif echo ${LINUX_TYPE} | grep -qi \"alpine\"; then\n test \"${PACKAGES}\" = \"\" || {\n ${SUDO} apk update\n ${SUDO} apk add ${PACKAGES};\n }\n\nelse\n >&2 echo \"Unrecognized Linux Type\"\n >&2 cat /etc/os-release\n exit 1\nfi\n\n####################\n### Install java ###\n####################\ncommand -v ${JAVA_HOME}/bin/java >/dev/null 2>&1 || {\n export JAVA_HOME=${CHE_DIR}/jdk1.8\n command -v ${JAVA_HOME}/bin/java >/dev/null 2>&1 || {\n JDK_URL=http://download.oracle.com/otn-pub/java/jdk/8u45-b14/jdk-8u45-linux-x64.tar.gz\n curl -s -j -k -L -H \"Cookie: oraclelicense=accept-securebackup-cookie\" \"${JDK_URL}\" | tar -C ${CHE_DIR} -xzf -\n mv ${CHE_DIR}/jdk1.8.0_45 ${CHE_DIR}/jdk1.8\n }\n}\n\n########################\n### Install ws-agent ###\n########################\n\nrm -rf ${CHE_DIR}/ws-agent\nmkdir -p ${CHE_DIR}/ws-agent\ncurl -s ${AGENT_BINARIES_URI} | tar xzf - -C ${CHE_DIR}/ws-agent\n\n###############################################\n### ws-agent run command will be added here ###\n### ~/che/ws-agent/bin/catalina.sh run ###\n###############################################"
}

View File

@ -0,0 +1,99 @@
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
unset SUDO
unset PACKAGES
test "$(id -u)" = 0 || SUDO="sudo"
LINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')
LINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)
###############################
### Install Needed packaged ###
###############################
# Red Hat Enterprise Linux 7
############################
if echo ${LINUX_TYPE} | grep -qi "rhel"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh-server"; }
test "${PACKAGES}" = "" || {
${SUDO} yum -y install ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# Ubuntu 14.04 16.04 / Linux Mint 17
####################################
elif echo ${LINUX_TYPE} | grep -qi "ubuntu"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh-server"; }
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# Debian 8
##########
elif echo ${LINUX_TYPE} | grep -qi "debian"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh-server"; }
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# Fedora 23
###########
elif echo ${LINUX_TYPE} | grep -qi "fedora"; then
PACKAGES=${PACKAGES}" procps-ng"
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh-server"; }
test "${PACKAGES}" = "" || {
${SUDO} dnf -y install ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# CentOS 7.1 & Oracle Linux 7.1
###############################
elif echo ${LINUX_TYPE} | grep -qi "centos"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh-server"; }
test "${PACKAGES}" = "" || {
${SUDO} yum -y install ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# openSUSE 13.2
###############
elif echo ${LINUX_TYPE} | grep -qi "opensuse"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openSSH"; }
test "${PACKAGES}" = "" || {
${SUDO} zypper install -y ${PACKAGES};
}
${SUDO} sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# Alpine 3.3
############$$
elif echo ${LINUX_TYPE} | grep -qi "alpine"; then
command -v sshd >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" openssh"; }
test "${PACKAGES}" = "" || {
${SUDO} apk update;
${SUDO} apk add openssh ${PACKAGES};
}
else
>&2 echo "Unrecognized Linux Type"
>&2 cat /etc/os-release
exit 1
fi
ps -fC sshd && exit
${SUDO} mkdir -p /var/run/sshd
${SUDO} /usr/bin/ssh-keygen -A && ${SUDO} /usr/sbin/sshd -D

View File

@ -0,0 +1,116 @@
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
unset PACKAGES
unset SUDO
command -v tar >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" tar"; }
command -v curl >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" curl"; }
test "$(id -u)" = 0 || SUDO="sudo"
CHE_DIR=$HOME/che
AGENT_BINARIES_URI="file:///mnt/che/terminal/websocket-terminal-\\${PREFIX}.tar.gz"
TARGET_AGENT_BINARIES_URI="file://"${CHE_DIR}"/websocket-terminal-\\${PREFIX}.tar.gz"
LINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')
LINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)
MACHINE_TYPE=$(uname -m)
mkdir -p ${CHE_DIR}
########################
### Install packages ###
########################
# Red Hat Enterprise Linux 7
############################
if echo ${LINUX_TYPE} | grep -qi "rhel"; then
test "${PACKAGES}" = "" || {
${SUDO} yum install ${PACKAGES};
}
# Ubuntu 14.04 16.04 / Linux Mint 17
####################################
elif echo ${LINUX_TYPE} | grep -qi "ubuntu"; then
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
# Debian 8
##########
elif echo ${LINUX_TYPE} | grep -qi "debian"; then
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
# Fedora 23
###########
elif echo ${LINUX_TYPE} | grep -qi "fedora"; then
PACKAGES=${PACKAGES}" procps-ng"
test "${PACKAGES}" = "" || {
${SUDO} dnf -y install ${PACKAGES};
}
# CentOS 7.1 & Oracle Linux 7.1
###############################
elif echo ${LINUX_TYPE} | grep -qi "centos"; then
test "${PACKAGES}" = "" || {
${SUDO} yum -y install ${PACKAGES};
}
# openSUSE 13.2
###############
elif echo ${LINUX_TYPE} | grep -qi "opensuse"; then
test "${PACKAGES}" = "" || {
${SUDO} zypper install -y ${PACKAGES};
}
# Alpine 3.3
############$$
elif echo ${LINUX_TYPE} | grep -qi "alpine"; then
test "${PACKAGES}" = "" || {
${SUDO} apk update
${SUDO} apk add ${PACKAGES};
}
else
>&2 echo "Unrecognized Linux Type"
>&2 cat /etc/os-release
exit 1
fi
ps -fC che-websocket-terminal && exit
########################
### Install Terminal ###
########################
if echo ${MACHINE_TYPE} | grep -qi "x86_64"; then
PREFIX=linux_amd64
elif echo ${MACHINE_TYPE} | grep -qi "arm5"; then
PREFIX=linux_arm7
elif echo ${MACHINE_TYPE} | grep -qi "arm6"; then
PREFIX=linux_arm7
elif echo ${MACHINE_TYPE} | grep -qi "arm7"; then
PREFIX=linux_arm7
else
>&2 echo "Unrecognized Machine Type"
>&2 uname -a
exit 1
fi
if curl -o /dev/null --silent --head --fail $(echo ${AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g'); then
curl -o $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g' | sed -s 's/file:\\/\\///g') -s $(echo ${AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g')
elif curl -o /dev/null --silent --head --fail $(echo ${AGENT_BINARIES_URI} | sed -s 's/-\\${PREFIX}//g'); then
curl -o $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g' | sed -s 's/file:\\/\\///g') -s $(echo ${AGENT_BINARIES_URI} | sed -s 's/-\\${PREFIX}//g')
fi
curl -s $(echo ${TARGET_AGENT_BINARIES_URI} | sed -s 's/\\${PREFIX}/'${PREFIX}'/g') | tar xzf - -C ${CHE_DIR}
$HOME/che/terminal/che-websocket-terminal -addr :4411 -cmd /bin/bash -static $HOME/che/terminal/

View File

@ -0,0 +1,114 @@
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
unset PACKAGES
unset SUDO
command -v tar >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" tar"; }
command -v curl >/dev/null 2>&1 || { PACKAGES=${PACKAGES}" curl"; }
test "$(id -u)" = 0 || SUDO="sudo"
AGENT_BINARIES_URI=file:///mnt/che/ws-agent.tar.gz
CHE_DIR=$HOME/che
LINUX_TYPE=$(cat /etc/os-release | grep ^ID= | tr '[:upper:]' '[:lower:]')
LINUX_VERSION=$(cat /etc/os-release | grep ^VERSION=)
MACHINE_TYPE=$(uname -m)
mkdir -p ${CHE_DIR}
${SUDO} mkdir -p /projects
${SUDO} sh -c "chown -R $(id -u -n) /projects"
########################
### Install packages ###
########################
# Red Hat Enterprise Linux 7
############################
if echo ${LINUX_TYPE} | grep -qi "rhel"; then
test "${PACKAGES}" = "" || {
${SUDO} yum install ${PACKAGES};
}
# Ubuntu 14.04 16.04 / Linux Mint 17
####################################
elif echo ${LINUX_TYPE} | grep -qi "ubuntu"; then
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
# Debian 8
##########
elif echo ${LINUX_TYPE} | grep -qi "debian"; then
test "${PACKAGES}" = "" || {
${SUDO} apt-get update;
${SUDO} apt-get -y install ${PACKAGES};
}
# Fedora 23
###########
elif echo ${LINUX_TYPE} | grep -qi "fedora"; then
PACKAGES=${PACKAGES}" procps-ng"
test "${PACKAGES}" = "" || {
${SUDO} dnf -y install ${PACKAGES};
}
# CentOS 7.1 & Oracle Linux 7.1
###############################
elif echo ${LINUX_TYPE} | grep -qi "centos"; then
test "${PACKAGES}" = "" || {
${SUDO} yum -y install ${PACKAGES};
}
# openSUSE 13.2
###############
elif echo ${LINUX_TYPE} | grep -qi "opensuse"; then
test "${PACKAGES}" = "" || {
${SUDO} zypper install -y ${PACKAGES};
}
# Alpine 3.3
############$$
elif echo ${LINUX_TYPE} | grep -qi "alpine"; then
test "${PACKAGES}" = "" || {
${SUDO} apk update
${SUDO} apk add ${PACKAGES};
}
else
>&2 echo "Unrecognized Linux Type"
>&2 cat /etc/os-release
exit 1
fi
####################
### Install java ###
####################
command -v ${JAVA_HOME}/bin/java >/dev/null 2>&1 || {
export JAVA_HOME=${CHE_DIR}/jdk1.8
command -v ${JAVA_HOME}/bin/java >/dev/null 2>&1 || {
JDK_URL=http://download.oracle.com/otn-pub/java/jdk/8u45-b14/jdk-8u45-linux-x64.tar.gz
curl -s -j -k -L -H "Cookie: oraclelicense=accept-securebackup-cookie" "${JDK_URL}" | tar -C ${CHE_DIR} -xzf -
mv ${CHE_DIR}/jdk1.8.0_45 ${CHE_DIR}/jdk1.8
}
}
########################
### Install ws-agent ###
########################
rm -rf ${CHE_DIR}/ws-agent
mkdir -p ${CHE_DIR}/ws-agent
curl -s ${AGENT_BINARIES_URI} | tar xzf - -C ${CHE_DIR}/ws-agent
###############################################
### ws-agent run command will be added here ###
### ~/che/ws-agent/bin/catalina.sh run ###
###############################################

View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
updateAgentScript() {
local DIR=$1
local AGENT=$2
local SCRIPT=$(cat ${AGENT}.script.sh | sed -r 's/"/\\"/g' | sed -r ':a;N;$!ba;s/\n/\\n/g')
touch ${AGENT}.json.tmp
cat ${DIR}/${AGENT}.json | while read line
do
if echo ${line} | grep -qi "script"; then
echo \"script\" : \"${SCRIPT}\" >> ${AGENT}.json.tmp
else
echo ${line} >> ${AGENT}.json.tmp
fi
done
mv ${AGENT}.json.tmp ${DIR}/${AGENT}.json
}
updateAgentScript ".." "org.eclipse.che.ssh"
updateAgentScript ".." "org.eclipse.che.terminal"
updateAgentScript ".." "org.eclipse.che.ws-agent"

View File

@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.testng.annotations.Test;
import static org.testng.Assert.assertNull;
import static org.testng.AssertJUnit.assertEquals;
/**
* @author Anatolii Bazko
*/
public class AgentKeyImplTest {
@Test
public void testAgentKeyWithNameAndVersion() {
AgentKeyImpl agentKey = AgentKeyImpl.parse("name:1");
assertEquals(agentKey.getName(), "name");
assertEquals(agentKey.getVersion(), "1");
}
@Test
public void testParseAgentKeyWithName() {
AgentKeyImpl agentKey = AgentKeyImpl.parse("name");
assertEquals(agentKey.getName(), "name");
assertNull(agentKey.getVersion());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testParseAgentKeyFails() {
AgentKeyImpl.parse("name:1:2");
}
}

View File

@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import org.eclipse.che.api.agent.server.AgentRegistryUrlProvider;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.api.core.rest.DefaultHttpJsonRequestFactory;
import org.eclipse.che.dto.server.JsonArrayImpl;
import org.everrest.assured.EverrestJetty;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.ITestContext;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import static java.lang.String.format;
import static java.nio.file.Files.copy;
import static java.util.Collections.singletonList;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* @author Anatoliy Bazko
*/
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class AgentRegistryImplTest {
@SuppressWarnings("unused")
private RegistryService service;
@Mock
private AgentRegistryUrlProvider urlProvider;
private AgentRegistryImpl agentRegistry;
@BeforeMethod
public void setUp(ITestContext context) throws Exception {
agentRegistry = new AgentRegistryImpl(urlProvider, new DefaultHttpJsonRequestFactory());
final Object port = context.getAttribute(EverrestJetty.JETTY_PORT);
when(urlProvider.getAgentUrl(any(AgentKey.class))).thenAnswer(new Answer<URL>() {
@Override
public URL answer(InvocationOnMock invocation) throws Throwable {
AgentKey agentKey = (AgentKey)invocation.getArguments()[0];
if (agentKey.getVersion() == null) {
return new URL("http://localhost:" + port + "/rest/registry/agent/" + agentKey.getName());
} else {
return new URL("http://localhost:" + port + "/rest/registry/agent/" + agentKey.getName() + "/" + agentKey.getVersion());
}
}
});
when(urlProvider.getAgentVersionsUrl(anyString())).thenAnswer(new Answer<URL>() {
@Override
public URL answer(InvocationOnMock invocation) throws Throwable {
String name = (String)invocation.getArguments()[0];
return new URL("http://localhost:" + port + "/rest/registry/updates/" + name);
}
});
service = new RegistryService();
}
@Test
public void testCreateSpecificVersionAgent() throws Exception {
Agent agent = agentRegistry.getAgent(new AgentKeyImpl("org.eclipse.che.ws-agent", "1.0"));
assertEquals(agent.getName(), "org.eclipse.che.ws-agent");
assertEquals(agent.getVersion(), "1.0");
}
@Test
public void testCreateLatestVersionAgent() throws Exception {
Agent agent = agentRegistry.getAgent(new AgentKeyImpl("org.eclipse.che.ws-agent"));
assertEquals(agent.getName(), "org.eclipse.che.ws-agent");
assertEquals(agent.getVersion(), "2.0");
}
@Test(expectedExceptions = AgentException.class)
public void testGetConfigShouldThrowExceptionIfAgentNotFound() throws Exception {
agentRegistry.getAgent(new AgentKeyImpl("terminal", "1.0"));
}
@Test
public void testGetAgentsVersion() throws Exception {
Collection<String> versions = agentRegistry.getVersions("org.eclipse.che.ws-agent");
assertEquals(versions.size(), 1);
assertTrue(versions.contains("1.0.0"));
}
@Test(expectedExceptions = AgentNotFoundException.class)
public void testGetAgentsVersionShouldFailsIfAgentUnknown() throws Exception {
agentRegistry.getVersions("terminal");
}
@Path("registry")
public class RegistryService {
@GET
@Path("agent/{artifact}/{version}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getAgent(@PathParam("artifact") String artifact, @PathParam("version") String version) throws IOException {
return doGetAgent(artifact, version);
}
@GET
@Path("agent/{artifact}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getLatestAgent(@PathParam("artifact") String artifact) throws IOException {
return doGetAgent(artifact, "2.0");
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/updates/{artifact}")
public Response getUpdates(@PathParam("artifact") final String artifact) {
if (!artifact.endsWith("org.eclipse.che.ws-agent")) {
return Response.status(Response.Status.NOT_FOUND).entity("{ \"message\" : \"not found\" }").build();
}
return Response.status(Response.Status.OK).entity(new JsonArrayImpl<>(singletonList("1.0.0"))).build();
}
private Response doGetAgent(String artifact, String version) throws IOException {
if (!artifact.endsWith("org.eclipse.che.ws-agent")) {
return Response.status(Response.Status.NOT_FOUND).build();
}
String content = format("{ \"name\" : \"%s\", \"version\" : \"%s\"}", artifact, version);
java.nio.file.Path file = Paths.get(System.getProperty("java.io.tmpdir"), "config.tmp");
file.toFile().deleteOnExit();
copy(new ByteArrayInputStream(content.getBytes()), file, StandardCopyOption.REPLACE_EXISTING);
return Response.ok(file.toFile(), MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Length", String.valueOf(Files.size(file)))
.header("Content-Disposition", "attachment; filename=" + file.getFileName().toString())
.build();
}
}
}

View File

@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.exception.AgentNotFoundException;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.singletonList;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
/**
* @author Anatolii Bazko
*/
@Listeners(value = {MockitoTestNGListener.class})
public class AgentSorterTest {
@Mock
private AgentRegistry agentRegistry;
@Mock
private Agent agent1;
@Mock
private Agent agent2;
@Mock
private Agent agent3;
@InjectMocks
private AgentSorter agentSorter;
@BeforeMethod
public void setUp() throws Exception {
when(agentRegistry.getAgent(eq(AgentKeyImpl.parse("fqn1")))).thenReturn(agent1);
when(agentRegistry.getAgent(eq(AgentKeyImpl.parse("fqn2")))).thenReturn(agent2);
when(agentRegistry.getAgent(eq(AgentKeyImpl.parse("fqn3")))).thenReturn(agent3);
when(agentRegistry.getAgent(eq(AgentKeyImpl.parse("fqn4")))).thenThrow(new AgentNotFoundException("Agent not found"));
when(agent1.getDependencies()).thenReturn(singletonList("fqn3"));
when(agent1.getName()).thenReturn("fqn1");
when(agent2.getDependencies()).thenReturn(singletonList("fqn3"));
when(agent2.getName()).thenReturn("fqn2");
when(agent3.getName()).thenReturn("fqn3");
}
@Test
public void shouldNotCreateNewAgentsIfDependenciesExist() throws Exception {
List<AgentKey> sorted = agentSorter.sort(Collections.singletonList("fqn1"));
assertEquals(sorted.size(), 1);
assertEquals(sorted.get(0).getName(), "fqn1");
}
@Test
public void sortAgentsRespectingDependencies() throws Exception {
List<AgentKey> sorted = agentSorter.sort(Arrays.asList("fqn1", "fqn2", "fqn3"));
assertEquals(sorted.size(), 3);
assertEquals(sorted.get(0).getName(), "fqn3");
assertEquals(sorted.get(1).getName(), "fqn1");
assertEquals(sorted.get(2).getName(), "fqn2");
}
@Test(expectedExceptions = AgentException.class)
public void sortingShouldFailIfCircularDependenciesFound() throws Exception {
when(agent1.getDependencies()).thenReturn(singletonList("fqn2"));
when(agent2.getDependencies()).thenReturn(singletonList("fqn1"));
agentSorter.sort(Arrays.asList("fqn1", "fqn2"));
}
@Test(expectedExceptions = AgentNotFoundException.class)
public void sortingShouldFailIfAgentNotFound() throws Exception {
agentSorter.sort(singletonList("fqn4"));
}
}

View File

@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.impl;
import org.everrest.assured.EverrestJetty;
import org.mockito.InjectMocks;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.util.List;
import static org.testng.AssertJUnit.assertFalse;
/**
* @author Anatoliy Bazko
*/
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class LocalAgentRegistryImplTest {
@InjectMocks
private LocalAgentRegistryImpl agentRegistry;
@Test
public void testInitializeAgents() throws Exception {
List<String> agents = agentRegistry.getAgents();
assertFalse(agents.isEmpty());
}
}

View File

@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.launcher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.server.spi.InstanceProcess;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
/**
* @author Anatolii Bazko
*/
@Listeners(value = {MockitoTestNGListener.class})
public class DefaultAgentLauncherTest {
@Mock
private Instance machine;
@Mock
private Agent agent;
@Mock
private LineConsumer lineConsumer;
@Mock
private InstanceProcess instanceProcess;
private AgentLauncher agentLauncher;
@BeforeMethod
public void setUp() throws Exception {
agentLauncher = new DefaultAgentLauncher(120000, 10);
when(machine.createProcess(any(), any())).thenReturn(instanceProcess);
when(machine.getLogger()).thenReturn(lineConsumer);
when(agent.getScript()).thenReturn("script1");
}
@Test
public void shouldLaunchAgent() throws Exception {
ArgumentCaptor<Command> commandCaptor = ArgumentCaptor.forClass(Command.class);
agentLauncher.launch(machine, agent);
verify(machine).createProcess(commandCaptor.capture(), (String)isNull());
Command command = commandCaptor.getValue();
assertEquals(command.getCommandLine(), "script1");
verify(instanceProcess).start(any());
}
}

View File

@ -0,0 +1,15 @@
#!/bin/bash
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
CHE_PATH=/home/tolusha/java/env-projects/che/assembly/assembly-main/target/eclipse-che-4.8.0-SNAPSHOT/eclipse-che-4.8.0-SNAPSHOT/bin
HOST_URL=localhost:8080
TEST_LOG="agents-test.log"

View File

@ -0,0 +1,104 @@
#!/bin/bash
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
. ./config.sh
trap cleanUp EXIT
cleanUp() {
${CHE_PATH}/che.sh stop
}
printAndLog() {
echo $@
log $@
}
logStartCommand() {
log
log "=== [ "`date`" ] COMMAND STARTED: "$@
}
logEndCommand() {
log "=================================== COMMAND COMPLETED: "$@
log
}
log() {
echo "TEST: "$@ >> ${TEST_LOG}
}
fetchJsonParameter() {
OUTPUT=`echo ${OUTPUT} | sed 's/.*"'$1'"\s*:\s*"\([^"]*\)*".*/\1/'`
}
# --method={POST|GET|...}
# --content-type=...
# --cookie=...
# --body=...
# --url=...
# --output-http-code
# --verbose
doHttpRequest() {
for var in "$@"; do
if [[ "$var" =~ --content-type=.+ ]]; then
CONTENT_TYPE_OPTION=`echo "-H \"Content-Type: $var\"" | sed -e "s/--content-type=//g"`
elif [[ "$var" =~ --body=.+ ]]; then
local BODY_OPTION=`echo "-d '$var'" | sed -e "s/--body=//g"`
elif [[ "$var" =~ --url=.+ ]]; then
local URL=`echo "'$var'" | sed -e "s/--url=//g"`
elif [[ "$var" =~ --method=.+ ]]; then
local METHOD_OPTION=`echo "-X $var" | sed -e "s/--method=//g"`
elif [[ "$var" == "--output-http-code" ]]; then
local OUTPUT_HTTP_CODE_OPTION="-o /dev/null -w \"%{http_code}\""
elif [[ "$var" == "--verbose" ]]; then
local VERBOSE_OPTION="-v"
elif [[ "$var" =~ --cookie=.+ ]]; then
local COOKIE_OPTION=$(echo "-H \"Cookie: session-access-key=$var\"" | sed -e "s/--cookie=//g")
fi
done
local COMMAND="curl -s $VERBOSE_OPTION $OUTPUT_HTTP_CODE_OPTION $CONTENT_TYPE_OPTION $COOKIE_OPTION $BODY_OPTION $METHOD_OPTION $URL"
logStartCommand $COMMAND
OUTPUT=$(eval $COMMAND)
EXIT_CODE=$?
log ${OUTPUT}
logEndCommand "curl"
}
doPost() {
doHttpRequest --method=POST \
--content-type=$1 \
--body="$2" \
--url=$3 \
--cookie=$4
}
doGet() {
doHttpRequest --method=GET \
--url=$1
}
doDelete() {
doHttpRequest --method=DELETE \
--url=$1
}

View File

@ -0,0 +1,94 @@
#!/bin/bash
#
# Copyright (c) 2012-2016 Codenvy, S.A.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Codenvy, S.A. - initial API and implementation
#
. ./lib.sh
${CHE_PATH}/che.sh --debug start
sleep 20s
DOCKER_CONTENT=("FROM fedora:23\nCMD tail -f /dev/null"
"FROM ubuntu:16.04\nCMD tail -f /dev/null"
"FROM ubuntu:14.04\nCMD tail -f /dev/null"
"FROM centos:7\nCMD tail -f /dev/null"
"FROM opensuse:13.2\nCMD tail -f /dev/null"
"FROM debian:8\nCMD tail -f /dev/null");
waitStatus() {
WORKSPACE_ID=$1
STATUS=$2
ATTEMPTS=200
for ((j = 0; j < ${ATTEMPTS}; j++))
do
doGet "http://"${HOST_URL}"/api/workspace/"${WORKSPACE_ID}
if echo ${OUTPUT} | grep -qi "\"id\":\"${WORKSPACE_ID}\",\"status\":\"${STATUS}\""; then
echo ${STATUS}
break
fi
sleep 5s
done
}
validateRunningProcess() {
WORKSPACE_ID=$1
PROCESS=$2
CONTAINER_ID=$(docker ps -aqf "name="${WORKSPACE_ID})
printAndLog "Container ID "${CONTAINER_ID}" validate "${PROCESS}
PID=$(docker exec -ti ${CONTAINER_ID} ps -fC ${PROCESS})
if [ "${PID}" = "" ]; then
printAndLog "RESULT: FAILED. Process "${PROCESS}" not found"
fi
}
deleteWorkspace() {
WORKSPACE_ID=$1
doDelete "http://"${HOST_URL}"/api/workspace/"${WORKSPACE_ID}"/runtime"
STATUS=$(waitStatus ${WORKSPACE_ID} "STOPPED")
if echo ${STATUS} | grep -qi "STOPPED"; then
doDelete "http://"${HOST_URL}"/api/workspace/"${WORKSPACE_ID}
sleep 1m
else
printAndLog "ERROR. Workspace "${WORKSPACE_ID}" can't be stopped"
fi
}
for ((i = 0; i < ${#DOCKER_CONTENT[@]}; i++))
do
CONTENT="${DOCKER_CONTENT[$i]}"
printAndLog
printAndLog "####################################################################"
printAndLog "Creating workspace with recipe: \""${CONTENT}"\""
printAndLog "####################################################################"
NAME=$(date +%s | sha256sum | base64 | head -c 5)
doPost "application/json" "{\"name\":\"${NAME}\",\"projects\":[],\"defaultEnv\":\"${NAME}\",\"description\":null,\"environments\":[{\"name\":\"${NAME}\",\"recipe\":null,\"machineConfigs\":[{\"name\":\"ws-machine\",\"limits\":{\"ram\":1000},\"type\":\"docker\",\"source\":{\"type\":\"dockerfile\",\"content\":\"${CONTENT}\"},\"dev\":true}]}]}" "http://"${HOST_URL}"/api/workspace?account="
fetchJsonParameter "id"
WORKSPACE_ID=${OUTPUT}
printAndLog "Starting workspace: "${WORKSPACE_ID}
doPost "application/json" "{}" "http://"${HOST_URL}"/api/workspace/"${WORKSPACE_ID}"/runtime?environment="${NAME}
STATUS=$(waitStatus ${WORKSPACE_ID} "RUNNING")
if echo ${STATUS} | grep -qi "RUNNING"; then
validateRunningProcess ${WORKSPACE_ID} "che-websocket-terminal"
validateRunningProcess ${WORKSPACE_ID} "java"
validateRunningProcess ${WORKSPACE_ID} "sshd"
else
printAndLog "RESULT: FAILED. Workspace not started."
fi
deleteWorkspace ${WORKSPACE_ID}
done

View File

@ -181,7 +181,7 @@ public class FactoryBuilderTest {
.withContentType("application/x-yaml")
.withContent("some content"))
.withMachines(singletonMap("devmachine",
newDto(ExtendedMachineDto.class).withAgents(singletonList("ws-agent"))
newDto(ExtendedMachineDto.class).withAgents(singletonList("org.eclipse.che.ws-agent"))
.withAttributes(singletonMap("memoryLimitBytes", "" + 512L * 1024L * 1024L))));
WorkspaceConfigDto workspaceConfig = dto.createDto(WorkspaceConfigDto.class)

View File

@ -73,6 +73,18 @@
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>

View File

@ -1,31 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.terminal;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.spi.Instance;
/**
* Machine implementation specific launcher of websocket terminal.
*
* @author Alexander Garagatyi
*/
public interface MachineImplSpecificTerminalLauncher {
/**
* Type of machine implementation this terminal fits.
*/
String getMachineType();
/**
* Starts websocket terminal inside of machine.
*/
void launchTerminal(Instance machine) throws MachineException;
}

View File

@ -1,95 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.terminal;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.notification.EventSubscriber;
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.shared.dto.event.MachineStatusEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import static java.util.stream.Collectors.toMap;
/**
* Starts websocket terminal in the machine after its start.
*
* @author Alexander Garagatyi
*/
@Singleton
public class MachineTerminalLauncher {
private static final Logger LOG = LoggerFactory.getLogger(MachineTerminalLauncher.class);
private final EventService eventService;
// TODO replace with WorkspaceManager
private final CheEnvironmentEngine cheEnvironmentEngine;
private final Map<String, MachineImplSpecificTerminalLauncher> terminalLaunchers;
private final ExecutorService executor;
@Inject
public MachineTerminalLauncher(EventService eventService,
Set<MachineImplSpecificTerminalLauncher> machineImplLaunchers,
CheEnvironmentEngine cheEnvironmentEngine) {
this.eventService = eventService;
this.terminalLaunchers = machineImplLaunchers.stream()
.collect(toMap(MachineImplSpecificTerminalLauncher::getMachineType,
Function.identity()));
this.cheEnvironmentEngine = cheEnvironmentEngine;
this.executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("MachineTerminalLauncher-%d")
.setDaemon(true)
.build());
}
@PostConstruct
public void start() {
eventService.subscribe(new EventSubscriber<MachineStatusEvent>() {
@Override
public void onEvent(MachineStatusEvent event) {
if (event.getEventType() == MachineStatusEvent.EventType.RUNNING) {
executor.execute(() -> {
try {
final Instance machine = cheEnvironmentEngine.getMachine(event.getWorkspaceId(),
event.getMachineId());
MachineImplSpecificTerminalLauncher terminalLauncher =
terminalLaunchers.get(machine.getConfig().getType());
if (terminalLauncher == null) {
LOG.warn("Terminal launcher implementation was not found for machine {} with type {}.",
machine.getId(),
machine.getConfig().getType());
} else {
terminalLauncher.launchTerminal(machine);
}
} catch (MachineException | NotFoundException e) {
LOG.error(e.getLocalizedMessage(), e);
// TODO send event that terminal is unavailable
}
});
}
}
});
}
}

View File

@ -0,0 +1,146 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.environment.server;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.impl.AgentSorter;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.api.core.model.workspace.compose.ComposeService;
import org.eclipse.che.api.environment.server.compose.model.ComposeServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static org.eclipse.che.api.environment.server.AgentConfigApplier.PROPERTIES.ENVIRONMENT;
import static org.eclipse.che.api.environment.server.AgentConfigApplier.PROPERTIES.PORTS;
/**
* Applies docker specific properties of the agents over {@link ComposeService}.
* Dependencies between agents are respected.
* Docker instance must't be started, otherwise changing configuration has no effect.
*
* The list of supported properties are:
* <li>ports</li>
* <li>environment</li>
*
* The {@code ports} property contains comma separated ports to expose respecting
* the following format: "label:port/protocol" or "port/protocol.
*
* The {@code environment} property contains command separated environment variables to set
* respecting the following format: "name=value".
*
* @see Agent#getProperties()
* @see ComposeService#getEnvironment()
* @see ComposeService#getPorts()
*
* @author Anatolii Bazko
*/
@Singleton
public class AgentConfigApplier {
private static final Logger LOG = LoggerFactory.getLogger(AgentConfigApplier.class);
private final AgentSorter sorter;
private final AgentRegistry agentRegistry;
@Inject
public AgentConfigApplier(AgentSorter sorter, AgentRegistry agentRegistry) {
this.sorter = sorter;
this.agentRegistry = agentRegistry;
}
/**
* Applies docker specific properties.
*
* @param composeService
* the compose service
* @param agentKeys
* the list of injected agents into machine
*
* @throws AgentException
*/
public void modify(ComposeServiceImpl composeService, List<String> agentKeys) throws AgentException {
for (AgentKey agentKey : sorter.sort(agentKeys)) {
Agent agent = agentRegistry.getAgent(agentKey);
addEnv(composeService, agent.getProperties());
addExposedPorts(composeService, agent.getProperties());
}
}
private void addEnv(ComposeServiceImpl composeService, Map<String, String> properties) {
String environment = properties.get(ENVIRONMENT.toString());
if (isNullOrEmpty(environment)) {
return;
}
Map<String, String> newEnv = new HashMap<>();
if (composeService.getEnvironment() != null) {
newEnv.putAll(composeService.getEnvironment());
}
for (String env : environment.split(",")) {
String[] items = env.split("=");
if (items.length != 2) {
LOG.warn(format("Illegal environment variable '%s' format", env));
continue;
}
String var = items[0];
String name = items[1];
newEnv.put(var, name);
}
composeService.setEnvironment(newEnv);
}
private void addExposedPorts(ComposeServiceImpl composeService, Map<String, String> properties) {
String ports = properties.get(PORTS.toString());
if (isNullOrEmpty(ports)) {
return;
}
for (String port : ports.split(",")) {
String[] items = port.split(":"); // ref:port
if (items.length == 1) {
composeService.getExpose().add(items[0]);
} else {
composeService.getExpose().add(items[1]);
}
}
}
enum PROPERTIES {
PORTS("ports"),
ENVIRONMENT("environment");
private final String value;
PROPERTIES(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
}

View File

@ -13,6 +13,7 @@ package org.eclipse.che.api.environment.server;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
@ -100,6 +101,7 @@ public class CheEnvironmentEngine {
private final EnvironmentParser environmentParser;
private final ComposeServicesStartStrategy startStrategy;
private final ComposeMachineInstanceProvider composeProvider;
private final AgentConfigApplier agentConfigApplier;
private volatile boolean isPreDestroyInvoked;
@ -111,12 +113,14 @@ public class CheEnvironmentEngine {
EventService eventService,
EnvironmentParser environmentParser,
ComposeServicesStartStrategy startStrategy,
ComposeMachineInstanceProvider composeProvider) {
ComposeMachineInstanceProvider composeProvider,
AgentConfigApplier agentConfigApplier) {
this.snapshotDao = snapshotDao;
this.eventService = eventService;
this.environmentParser = environmentParser;
this.startStrategy = startStrategy;
this.composeProvider = composeProvider;
this.agentConfigApplier = agentConfigApplier;
this.environments = new ConcurrentHashMap<>();
this.machineInstanceProviders = machineInstanceProviders;
this.machineLogsDir = new File(machineLogsDir);
@ -487,6 +491,7 @@ public class CheEnvironmentEngine {
ConflictException {
ComposeEnvironmentImpl composeEnvironment = environmentParser.parse(env);
applyAgents(env, composeEnvironment);
normalizeEnvironment(composeEnvironment);
@ -506,6 +511,21 @@ public class CheEnvironmentEngine {
}
}
private void applyAgents(Environment env, ComposeEnvironmentImpl composeEnvironment) throws ServerException {
for (Map.Entry<String, ComposeServiceImpl> entry : composeEnvironment.getServices().entrySet()) {
String machineName = entry.getKey();
ComposeServiceImpl composeService = entry.getValue();
List<String> agents = env.getMachines().get(machineName).getAgents();
try {
agentConfigApplier.modify(composeService, agents);
} catch (AgentException e) {
throw new ServerException("Can't apply agent config", e);
}
}
}
private void normalizeEnvironment(ComposeEnvironmentImpl composeEnvironment) {
for (Map.Entry<String, ComposeServiceImpl> serviceEntry : composeEnvironment.getServices()
.entrySet()) {
@ -521,10 +541,12 @@ public class CheEnvironmentEngine {
.stream()
.filter(entry -> entry.getValue()
.getAgents()
.contains("ws-agent"))
.stream()
.filter(agent -> agent.contains("org.eclipse.che.ws-agent"))
.findAny()
.isPresent())
.findAny()
.orElseThrow(
() -> new ServerException("Agent 'ws-agent' is not found in any of environment machines"))
.orElseThrow(() -> new ServerException("Agent 'org.eclipse.che.ws-agent' is not found in any of environment machines"))
.getKey();
}

View File

@ -120,7 +120,7 @@ public class CheEnvironmentValidator {
envName);
checkArgument(env.getMachines() != null && !env.getMachines().isEmpty(),
"Environment '%s' doesn't contain machine with 'ws-agent' agent",
"Environment '%s' doesn't contain machine with 'org.eclipse.che.ws-agent' agent",
envName);
List<String> missingServices = env.getMachines()
@ -138,12 +138,12 @@ public class CheEnvironmentValidator {
.stream()
.filter(entry -> entry.getValue()
.getAgents()
.contains("ws-agent"))
.contains("org.eclipse.che.ws-agent"))
.map(Map.Entry::getKey)
.collect(toList());
checkArgument(devMachines.size() == 1,
"Environment '%s' should contain exactly 1 machine with ws-agent, but contains '%s'. " +
"Environment '%s' should contain exactly 1 machine with org.eclipse.che.ws-agent, but contains '%s'. " +
"All machines with this agent: %s",
envName, devMachines.size(), Joiner.on(", ").join(devMachines));

View File

@ -43,7 +43,7 @@ import static java.lang.String.format;
* "type": "dockerfile"
* },
* "type": "docker", <- will be defined by environment recipe type
* "dev": true, <- if agents contain 'ws-agent'
* "dev": true, <- if agents contain 'org.eclipse.che.ws-agent'
* "envVariables" : {
* "env1" : "value1",
* "env2" : "value2
@ -78,7 +78,7 @@ import static java.lang.String.format;
* },
* "machines" : {
* "dev-machine" : {
* "agents" : [ "exec-agent", "ws-agent" ],
* "agents" : [ "exec-agent", "org.eclipse.che.ws-agent" ],
* "servers" : {
* "some_reference" : {
* "port" : "9090/udp",
@ -151,7 +151,7 @@ public class WorkspaceConfigJsonAdapter {
// dev-machine agents
final JsonArray agents = new JsonArray();
agents.add(new JsonPrimitive("ws-agent"));
agents.add(new JsonPrimitive("org.eclipse.che.ws-agent"));
newMachine.add("agents", agents);
// dev-machine ram

View File

@ -13,7 +13,13 @@ package org.eclipse.che.api.workspace.server;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.eclipse.che.api.agent.server.wsagent.WsAgentLauncher;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.exception.AgentException;
import org.eclipse.che.api.agent.server.impl.AgentSorter;
import org.eclipse.che.api.agent.server.launcher.AgentLauncher;
import org.eclipse.che.api.agent.server.launcher.AgentLauncherFactory;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.agent.shared.model.AgentKey;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
@ -22,6 +28,7 @@ import org.eclipse.che.api.core.model.machine.MachineConfig;
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
import org.eclipse.che.api.core.model.machine.MachineStatus;
import org.eclipse.che.api.core.model.workspace.Environment;
import org.eclipse.che.api.core.model.workspace.ExtendedMachine;
import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.notification.EventService;
@ -30,9 +37,11 @@ import org.eclipse.che.api.core.util.MessageConsumer;
import org.eclipse.che.api.core.util.WebsocketMessageConsumer;
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
import org.eclipse.che.api.environment.server.exception.EnvironmentNotRunningException;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
import org.eclipse.che.api.workspace.server.model.impl.ExtendedMachineImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceRuntimeImpl;
import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent;
@ -89,17 +98,23 @@ public class WorkspaceRuntimes {
private final EventService eventService;
private final StripedLocks stripedLocks;
private final CheEnvironmentEngine environmentEngine;
private final WsAgentLauncher wsAgentLauncher;
private final AgentSorter agentSorter;
private final AgentLauncherFactory launcherFactory;
private final AgentRegistry agentRegistry;
private volatile boolean isPreDestroyInvoked;
@Inject
public WorkspaceRuntimes(EventService eventService,
CheEnvironmentEngine environmentEngine,
WsAgentLauncher wsAgentLauncher) {
AgentSorter agentSorter,
AgentLauncherFactory launcherFactory,
AgentRegistry agentRegistry) {
this.eventService = eventService;
this.environmentEngine = environmentEngine;
this.wsAgentLauncher = wsAgentLauncher;
this.agentSorter = agentSorter;
this.launcherFactory = launcherFactory;
this.agentRegistry = agentRegistry;
this.workspaces = new HashMap<>();
// 16 - experimental value for stripes count, it comes from default hash map size
this.stripedLocks = new StripedLocks(16);
@ -233,9 +248,7 @@ public class WorkspaceRuntimes {
environmentCopy,
recover,
getEnvironmentLogger(workspaceId));
Instance devMachine = getDevMachine(machines);
wsAgentLauncher.startWsAgent(devMachine);
launchAgents(environment, machines);
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
WorkspaceState workspaceState = workspaces.get(workspaceId);
@ -265,6 +278,19 @@ public class WorkspaceRuntimes {
}
}
private void launchAgents(EnvironmentImpl environment, List<Instance> machines) throws ServerException {
for (Instance instance : machines) {
Map<String, ExtendedMachineImpl> envMachines = environment.getMachines();
if (envMachines != null) {
ExtendedMachine extendedMachine = envMachines.get(instance.getConfig().getName());
if (extendedMachine != null) {
List<String> agents = extendedMachine.getAgents();
launchAgents(instance, agents);
}
}
}
}
/**
* Stops running workspace runtime.
*
@ -382,6 +408,7 @@ public class WorkspaceRuntimes {
}
Instance instance = environmentEngine.startMachine(workspaceId, machineConfig);
launchAgents(instance, Collections.singletonList("org.eclipse.che.terminal"));
try (StripedLocks.WriteLock lock = stripedLocks.acquireWriteLock(workspaceId)) {
WorkspaceState workspaceState = workspaces.get(workspaceId);
@ -574,6 +601,20 @@ public class WorkspaceRuntimes {
}
}
protected void launchAgents(Instance instance, List<String> agents) throws ServerException {
try {
for (AgentKey agentKey : agentSorter.sort(agents)) {
LOG.info("Launching '{}' agent", agentKey.getName());
Agent agent = agentRegistry.getAgent(agentKey);
AgentLauncher launcher = launcherFactory.find(agentKey.getName(), instance.getConfig().getType());
launcher.launch(instance, agent);
}
} catch (AgentException e) {
throw new MachineException(e.getMessage(), e);
}
}
public static class WorkspaceState {
private WorkspaceStatus status;
private String activeEnv;

View File

@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.workspace.server.launcher;
import org.eclipse.che.api.agent.server.launcher.AbstractAgentLauncher;
import org.eclipse.che.api.agent.server.launcher.ProcessIsLaunchedChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
/**
* Starts terminal agent.
*
* @author Anatolii Bazko
*/
@Singleton
public class SshAgentLauncherImpl extends AbstractAgentLauncher {
protected static final Logger LOG = LoggerFactory.getLogger(SshAgentLauncherImpl.class);
@Inject
public SshAgentLauncherImpl(@Named("machine.agent.max_start_time_ms") long agentMaxStartTimeMs,
@Named("machine.agent.ping_delay_ms") long agentPingDelayMs) {
super(agentMaxStartTimeMs, agentPingDelayMs, new ProcessIsLaunchedChecker("sshd"));
}
@Override
public String getMachineType() {
return "docker";
}
@Override
public String getAgentName() {
return "org.eclipse.che.ssh";
}
}

View File

@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.workspace.server.launcher;
import org.eclipse.che.api.agent.server.launcher.AbstractAgentLauncher;
import org.eclipse.che.api.agent.server.launcher.ProcessIsLaunchedChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
/**
* Starts terminal agent.
*
* @author Anatolii Bazko
*/
@Singleton
public class TerminalAgentLauncherImpl extends AbstractAgentLauncher {
protected static final Logger LOG = LoggerFactory.getLogger(TerminalAgentLauncherImpl.class);
@Inject
public TerminalAgentLauncherImpl(@Named("machine.agent.max_start_time_ms") long agentMaxStartTimeMs,
@Named("machine.agent.ping_delay_ms") long agentPingDelayMs) {
super(agentMaxStartTimeMs, agentPingDelayMs, new ProcessIsLaunchedChecker("che-websocket-terminal"));
}
@Override
public String getMachineType() {
return "docker";
}
@Override
public String getAgentName() {
return "org.eclipse.che.terminal";
}
}

View File

@ -8,20 +8,24 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.wsagent;
package org.eclipse.che.api.workspace.server.launcher;
import org.eclipse.che.api.agent.server.launcher.AgentLauncher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.core.model.machine.Server;
import org.eclipse.che.api.core.rest.HttpJsonRequest;
import org.eclipse.che.api.core.rest.HttpJsonRequestFactory;
import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.api.environment.server.MachineProcessManager;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.shared.Constants;
import org.eclipse.che.commons.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,68 +38,85 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Map;
import static com.google.common.base.MoreObjects.firstNonNull;
import static org.eclipse.che.api.workspace.shared.Constants.WS_AGENT_PROCESS_NAME;
/**
* Starts ws agent in the machine and waits until ws agent sends notification about its start
* Starts ws agent in the machine and waits until ws agent sends notification about its start.
*
* @author Alexander Garagatyi
* @author Anatolii Bazko
*/
@Singleton
public class WsAgentLauncherImpl implements WsAgentLauncher {
public static final String WS_AGENT_PROCESS_START_COMMAND = "machine.ws_agent.run_command";
public class WsAgentLauncherImpl implements AgentLauncher {
protected static final Logger LOG = LoggerFactory.getLogger(WsAgentLauncherImpl.class);
private static final String WS_AGENT_PROCESS_OUTPUT_CHANNEL = "workspace:%s:ext-server:output";
private static final String WS_AGENT_SERVER_NOT_FOUND_ERROR = "Workspace agent server not found in dev machine.";
protected static final String DEFAULT_WS_AGENT_RUN_COMMAND = "~/che/ws-agent/bin/catalina.sh run";
private final Provider<MachineProcessManager> machineProcessManagerProvider;
private final HttpJsonRequestFactory httpJsonRequestFactory;
private final String wsAgentStartCommandLine;
private final long wsAgentMaxStartTimeMs;
private final long wsAgentPingDelayMs;
private final int wsAgentPingConnectionTimeoutMs;
private final String pingTimedOutErrorMessage;
private final String wsAgentRunCommand;
@Inject
public WsAgentLauncherImpl(Provider<MachineProcessManager> machineProcessManagerProvider,
HttpJsonRequestFactory httpJsonRequestFactory,
@Named(WS_AGENT_PROCESS_START_COMMAND) String wsAgentStartCommandLine,
@Nullable @Named("machine.ws_agent.run_command") String wsAgentRunCommand,
@Named("machine.ws_agent.max_start_time_ms") long wsAgentMaxStartTimeMs,
@Named("machine.ws_agent.ping_delay_ms") long wsAgentPingDelayMs,
@Named("machine.ws_agent.ping_conn_timeout_ms") int wsAgentPingConnectionTimeoutMs,
@Named("machine.ws_agent.ping_timed_out_error_msg") String pingTimedOutErrorMessage) {
this.machineProcessManagerProvider = machineProcessManagerProvider;
this.httpJsonRequestFactory = httpJsonRequestFactory;
this.wsAgentStartCommandLine = wsAgentStartCommandLine;
this.wsAgentMaxStartTimeMs = wsAgentMaxStartTimeMs;
this.wsAgentPingDelayMs = wsAgentPingDelayMs;
this.wsAgentPingConnectionTimeoutMs = wsAgentPingConnectionTimeoutMs;
this.pingTimedOutErrorMessage = pingTimedOutErrorMessage;
}
public static String getWsAgentProcessOutputChannel(String workspaceId) {
return String.format(WS_AGENT_PROCESS_OUTPUT_CHANNEL, workspaceId);
this.wsAgentRunCommand = wsAgentRunCommand;
}
@Override
public void startWsAgent(Machine devMachine) throws NotFoundException,
ServerException {
final HttpJsonRequest wsAgentPingRequest = createPingRequest(devMachine);
public String getAgentName() {
return "org.eclipse.che.ws-agent";
}
@Override
public String getMachineType() {
return "docker";
}
@Override
public void launch(Instance machine, Agent agent) throws ServerException {
final HttpJsonRequest wsAgentPingRequest;
try {
wsAgentPingRequest = createPingRequest(machine);
} catch (ServerException e) {
throw new MachineException(e.getServiceError());
}
String script = agent.getScript() + "\n" + firstNonNull(wsAgentRunCommand, DEFAULT_WS_AGENT_RUN_COMMAND);
final String wsAgentPingUrl = wsAgentPingRequest.getUrl();
try {
machineProcessManagerProvider.get().exec(devMachine.getWorkspaceId(),
devMachine.getId(),
new CommandImpl(WS_AGENT_PROCESS_NAME,
wsAgentStartCommandLine,
WS_AGENT_PROCESS_NAME), //for server side type of command mean nothing
//but we will use it as marker on
//client side for track this command
getWsAgentProcessOutputChannel(devMachine.getWorkspaceId()));
// for server side type of command mean nothing
// but we will use it as marker on
// client side for track this command
CommandImpl command = new CommandImpl(getAgentName(), script, WS_AGENT_PROCESS_NAME);
machineProcessManagerProvider.get().exec(machine.getWorkspaceId(),
machine.getId(),
command,
getWsAgentProcessOutputChannel(machine.getWorkspaceId()));
final long pingStartTimestamp = System.currentTimeMillis();
LOG.debug("Starts pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}",
devMachine.getWorkspaceId(),
machine.getWorkspaceId(),
wsAgentPingUrl,
pingStartTimestamp);
@ -106,18 +127,22 @@ public class WsAgentLauncherImpl implements WsAgentLauncher {
Thread.sleep(wsAgentPingDelayMs);
}
}
} catch (BadRequestException wsAgentLaunchingExc) {
throw new ServerException(wsAgentLaunchingExc.getLocalizedMessage(), wsAgentLaunchingExc);
} catch (BadRequestException | ServerException | NotFoundException e) {
throw new ServerException(e.getServiceError());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ServerException("Ws agent pinging is interrupted");
}
LOG.error("Fail pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}", devMachine.getWorkspaceId(), wsAgentPingUrl);
LOG.error("Fail pinging ws agent. Workspace ID:{}. Url:{}. Timestamp:{}", machine.getWorkspaceId(), wsAgentPingUrl);
throw new ServerException(pingTimedOutErrorMessage);
}
public static String getWsAgentProcessOutputChannel(String workspaceId) {
return String.format(WS_AGENT_PROCESS_OUTPUT_CHANNEL, workspaceId);
}
// forms the ping request based on information about the machine.
protected HttpJsonRequest createPingRequest(Machine machine) throws ServerException {
protected HttpJsonRequest createPingRequest(Instance machine) throws ServerException {
Map<String, ? extends Server> servers = machine.getRuntime().getServers();
Server wsAgentServer = servers.get(Constants.WS_AGENT_PORT);
if (wsAgentServer == null) {

View File

@ -0,0 +1,121 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.environment.server;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.impl.AgentSorter;
import org.eclipse.che.api.agent.server.launcher.AgentLauncherFactory;
import org.eclipse.che.api.agent.server.model.impl.AgentKeyImpl;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.environment.server.compose.model.ComposeServiceImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* @author Anatolii Bazko
*/
@Listeners(value = {MockitoTestNGListener.class})
public class AgentConfigApplierTest {
@Mock
private AgentSorter sorter;
@Mock
private Instance machine;
@Mock
private Agent agent1;
@Mock
private Agent agent2;
@Mock
private Agent agent3;
@Mock
private AgentLauncherFactory agentLauncher;
@Mock
private AgentRegistry agentRegistry;
private AgentConfigApplier agentConfigApplier;
@BeforeMethod
public void setUp() throws Exception {
agentConfigApplier = new AgentConfigApplier(sorter, agentRegistry);
when(agentRegistry.getAgent(AgentKeyImpl.parse("agent1"))).thenReturn(agent1);
when(agentRegistry.getAgent(AgentKeyImpl.parse("agent2"))).thenReturn(agent2);
when(agentRegistry.getAgent(AgentKeyImpl.parse("agent3"))).thenReturn(agent3);
when(agent1.getScript()).thenReturn("script1");
when(agent1.getDependencies()).thenReturn(singletonList("fqn3"));
when(agent2.getScript()).thenReturn("script2");
when(agent2.getDependencies()).thenReturn(singletonList("fqn3"));
when(agent3.getScript()).thenReturn("script3");
}
@Test
public void shouldAddExposedPorts() throws Exception {
when(sorter.sort(any()))
.thenReturn(Arrays.asList(AgentKeyImpl.parse("agent1"), AgentKeyImpl.parse("agent2"), AgentKeyImpl.parse("agent3")));
when(agent1.getProperties()).thenReturn(singletonMap("ports", "terminal:1111/udp,terminal:2222/tcp"));
when(agent2.getProperties()).thenReturn(singletonMap("ports", "3333/udp"));
ComposeServiceImpl composeService = new ComposeServiceImpl();
agentConfigApplier.modify(composeService, Arrays.asList("agent1", "agent2", "agent3"));
List<String> exposedPorts = composeService.getExpose();
assertTrue(exposedPorts.contains("1111/udp"));
assertTrue(exposedPorts.contains("2222/tcp"));
assertTrue(exposedPorts.contains("3333/udp"));
}
@Test
public void shouldAddEnvVariables() throws Exception {
when(sorter.sort(any())).thenReturn(Arrays.asList(AgentKeyImpl.parse("agent1"), AgentKeyImpl.parse("agent2")));
when(agent1.getProperties()).thenReturn(singletonMap("environment", "p1=v1,p2=v2"));
when(agent2.getProperties()).thenReturn(singletonMap("environment", "p3=v3"));
ComposeServiceImpl composeService = new ComposeServiceImpl();
agentConfigApplier.modify(composeService, Arrays.asList("agent1", "agent2"));
Map<String, String> env = composeService.getEnvironment();
assertEquals(env.size(), 3);
assertEquals(env.get("p1"), "v1");
assertEquals(env.get("p2"), "v2");
assertEquals(env.get("p3"), "v3");
}
@Test
public void shouldIgnoreEnvironmentIfIllegalFormat() throws Exception {
when(sorter.sort(any())).thenReturn(Arrays.asList(AgentKeyImpl.parse("agent1")));
when(agent1.getProperties()).thenReturn(singletonMap("environment", "p1"));
ComposeServiceImpl composeService = new ComposeServiceImpl();
agentConfigApplier.modify(composeService, Collections.singletonList("agent1"));
Map<String, String> env = composeService.getEnvironment();
assertEquals(env.size(), 0);
}
}

View File

@ -99,6 +99,8 @@ public class CheEnvironmentEngineTest {
SnapshotDao snapshotDao;
@Mock
RecipeDownloader recipeDownloader;
@Mock
AgentConfigApplier agentConfigApplier;
EnvironmentParser environmentParser = new EnvironmentParser(new ComposeFileParser(), recipeDownloader);
@ -113,7 +115,8 @@ public class CheEnvironmentEngineTest {
eventService,
environmentParser,
new ComposeServicesStartStrategy(),
composeProvider));
composeProvider,
agentConfigApplier));
when(machineInstanceProviders.getProvider("docker")).thenReturn(instanceProvider);
when(instanceProvider.getRecipeTypes()).thenReturn(Collections.singleton("dockerfile"));
@ -609,7 +612,7 @@ public class CheEnvironmentEngineTest {
new HashMap<>(singletonMap("prop1", "propValue"))));
servers.put("ref2", new ServerConf2Impl("8080/udp", "proto1", null));
servers.put("ref3", new ServerConf2Impl("9090", "proto1", null));
machines.put("dev-machine", new ExtendedMachineImpl(new ArrayList<>(asList("ws-agent", "someAgent")),
machines.put("dev-machine", new ExtendedMachineImpl(new ArrayList<>(asList("org.eclipse.che.ws-agent", "someAgent")),
servers,
new HashMap<>(singletonMap("memoryLimitBytes", "10000"))));
machines.put("machine2", new ExtendedMachineImpl(new ArrayList<>(asList("someAgent2", "someAgent3")),

View File

@ -186,20 +186,20 @@ public class CheEnvironmentValidatorTest {
env = createEnv();
env.setMachines(null);
data.add(asList(env, "Environment 'env' doesn't contain machine with 'ws-agent' agent"));
data.add(asList(env, "Environment 'env' doesn't contain machine with 'org.eclipse.che.ws-agent' agent"));
env = createEnv();
env.setMachines(emptyMap());
data.add(asList(env, "Environment 'env' doesn't contain machine with 'ws-agent' agent"));
data.add(asList(env, "Environment 'env' doesn't contain machine with 'org.eclipse.che.ws-agent' agent"));
env = createEnv();
env.getMachines().put("missingInComposeEnvMachine",
newDto(ExtendedMachineDto.class).withAgents(singletonList("ws-agent")));
newDto(ExtendedMachineDto.class).withAgents(singletonList("org.eclipse.che.ws-agent")));
data.add(asList(env, "Environment 'env' contains machines that are missing in environment recipe: missingInComposeEnvMachine"));
env = createEnv();
env.getMachines().entrySet().forEach(entry -> entry.getValue().getAgents().add("ws-agent"));
data.add(asList(env, "Environment 'env' should contain exactly 1 machine with ws-agent, but contains '" +
env.getMachines().entrySet().forEach(entry -> entry.getValue().getAgents().add("org.eclipse.che.ws-agent"));
data.add(asList(env, "Environment 'env' should contain exactly 1 machine with org.eclipse.che.ws-agent, but contains '" +
env.getMachines().size() + "'. " + "All machines with this agent: " +
Joiner.on(", ").join(env.getMachines().keySet())));
@ -537,7 +537,7 @@ public class CheEnvironmentValidatorTest {
new HashMap<>(singletonMap("prop1", "propValue"))));
servers.put("ref2", new ServerConf2Impl("8080/udp", "proto1", null));
servers.put("ref3", new ServerConf2Impl("9090", "proto1", null));
machines.put("dev-machine", new ExtendedMachineImpl(new ArrayList<>(asList("ws-agent", "someAgent")),
machines.put("dev-machine", new ExtendedMachineImpl(new ArrayList<>(asList("org.eclipse.che.ws-agent", "someAgent")),
servers,
new HashMap<>(singletonMap("memoryLimitBytes", "10000"))));
machines.put("machine2", new ExtendedMachineImpl(new ArrayList<>(asList("someAgent2", "someAgent3")),

View File

@ -226,7 +226,7 @@ public class DefaultWorkspaceValidatorTest {
.withDefaultEnv("dev-env");
ExtendedMachineDto extendedMachine =
newDto(ExtendedMachineDto.class).withAgents(singletonList("ws-agent"))
newDto(ExtendedMachineDto.class).withAgents(singletonList("org.eclipse.che.ws-agent"))
.withServers(singletonMap("ref1",
newDto(ServerConf2Dto.class).withPort("8080/tcp")
.withProtocol("https")

View File

@ -885,7 +885,7 @@ public class WorkspaceManagerTest {
"content",
null),
singletonMap("dev-machine",
new ExtendedMachineImpl(singletonList("ws-agent"),
new ExtendedMachineImpl(singletonList("org.eclipse.che.ws-agent"),
null,
new HashMap<>(singletonMap("memoryLimitBytes", "10000")))));
return WorkspaceConfigImpl.builder()

View File

@ -10,7 +10,9 @@
*******************************************************************************/
package org.eclipse.che.api.workspace.server;
import org.eclipse.che.api.agent.server.wsagent.WsAgentLauncher;
import org.eclipse.che.api.agent.server.AgentRegistry;
import org.eclipse.che.api.agent.server.impl.AgentSorter;
import org.eclipse.che.api.agent.server.launcher.AgentLauncherFactory;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
@ -21,9 +23,9 @@ import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.environment.server.CheEnvironmentEngine;
import org.eclipse.che.api.environment.server.NoOpMachineInstance;
import org.eclipse.che.api.machine.server.model.impl.MachineLimitsImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineLimitsImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
@ -82,13 +84,17 @@ public class WorkspaceRuntimesTest {
private CheEnvironmentEngine environmentEngine;
@Mock
private WsAgentLauncher wsAgentLauncher;
private AgentSorter agentSorter;
@Mock
private AgentLauncherFactory launcherFactory;
@Mock
private AgentRegistry agentRegistry;
private WorkspaceRuntimes runtimes;
@BeforeMethod
public void setUp(Method method) throws Exception {
runtimes = spy(new WorkspaceRuntimes(eventService, environmentEngine, wsAgentLauncher));
runtimes = spy(new WorkspaceRuntimes(eventService, environmentEngine, agentSorter, launcherFactory, agentRegistry));
List<Instance> machines = asList(createMachine(true), createMachine(false));
when(environmentEngine.start(anyString(),

View File

@ -816,7 +816,7 @@ public class WorkspaceServiceTest {
}
private static EnvironmentDto createEnvDto() {
ExtendedMachineImpl devMachine = new ExtendedMachineImpl(singletonList("ws-agent"),
ExtendedMachineImpl devMachine = new ExtendedMachineImpl(singletonList("org.eclipse.che.ws-agent"),
null,
new HashMap<>(singletonMap("memoryLimitBytes", "10000")));

View File

@ -8,8 +8,9 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.agent.server.wsagent;
package org.eclipse.che.api.workspace.server.launcher;
import org.eclipse.che.api.agent.shared.model.Agent;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
@ -21,9 +22,9 @@ import org.eclipse.che.api.core.rest.HttpJsonResponse;
import org.eclipse.che.api.environment.server.MachineProcessManager;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineImpl;
import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl;
import org.eclipse.che.api.machine.server.model.impl.ServerImpl;
import org.eclipse.che.api.machine.server.spi.Instance;
import org.eclipse.che.api.machine.shared.Constants;
import org.eclipse.che.commons.test.mockito.answer.SelfReturningAnswer;
import org.mockito.Mock;
@ -50,32 +51,33 @@ import static org.mockito.Mockito.when;
@Listeners(MockitoTestNGListener.class)
public class WsAgentLauncherImplTest {
private static final String MACHINE_ID = "machineId";
private static final String WORKSPACE_ID = "testWorkspaceId";
private static final String WS_AGENT_START_CMD_LINE = "cmdLine";
private static final String WS_AGENT_PORT = Constants.WS_AGENT_PORT;
private static final long WS_AGENT_MAX_START_TIME_MS = 1000;
private static final long WS_AGENT_PING_DELAY_MS = 1;
private static final int WS_AGENT_PING_CONN_TIMEOUT_MS = 1;
private static final String WS_AGENT_SERVER_LOCATION = "ws-agent.com:456789/";
private static final String WS_AGENT_SERVER_URL = "http://" + WS_AGENT_SERVER_LOCATION;
private static final String MACHINE_ID = "machineId";
private static final String WORKSPACE_ID = "testWorkspaceId";
private static final String WS_AGENT_PORT = Constants.WS_AGENT_PORT;
private static final long WS_AGENT_MAX_START_TIME_MS = 1000;
private static final long WS_AGENT_PING_DELAY_MS = 1;
private static final int WS_AGENT_PING_CONN_TIMEOUT_MS = 1;
private static final String WS_AGENT_SERVER_LOCATION = "ws-agent.com:456789/";
private static final String WS_AGENT_SERVER_URL = "http://" + WS_AGENT_SERVER_LOCATION;
private static final ServerImpl SERVER = new ServerImpl("ref",
"http",
WS_AGENT_SERVER_LOCATION,
null,
WS_AGENT_SERVER_URL);
private static final String WS_AGENT_TIMED_OUT_MESSAGE = "timeout error message";
private static final String WS_AGENT_TIMED_OUT_MESSAGE = "timeout error message";
@Mock
private MachineProcessManager machineProcessManager;
@Mock
private HttpJsonRequestFactory requestFactory;
@Mock
private MachineImpl machine;
private Instance machine;
@Mock
private HttpJsonResponse pingResponse;
@Mock
private MachineRuntimeInfoImpl machineRuntime;
@Mock
private Agent agent;
private HttpJsonRequest pingRequest;
private WsAgentLauncherImpl wsAgentLauncher;
@ -83,13 +85,14 @@ public class WsAgentLauncherImplTest {
@BeforeMethod
public void setUp() throws Exception {
wsAgentLauncher = new WsAgentLauncherImpl(() -> machineProcessManager,
requestFactory,
WS_AGENT_START_CMD_LINE,
requestFactory, null,
WS_AGENT_MAX_START_TIME_MS,
WS_AGENT_PING_DELAY_MS,
WS_AGENT_PING_CONN_TIMEOUT_MS,
WS_AGENT_TIMED_OUT_MESSAGE);
WS_AGENT_TIMED_OUT_MESSAGE
);
pingRequest = Mockito.mock(HttpJsonRequest.class, new SelfReturningAnswer());
when(agent.getScript()).thenReturn("script");
when(machine.getId()).thenReturn(MACHINE_ID);
when(machine.getWorkspaceId()).thenReturn(WORKSPACE_ID);
when(machine.getRuntime()).thenReturn(machineRuntime);
@ -101,12 +104,12 @@ public class WsAgentLauncherImplTest {
@Test
public void shouldStartWsAgentUsingMachineExec() throws Exception {
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(machineProcessManager).exec(eq(WORKSPACE_ID),
eq(MACHINE_ID),
eq(new CommandImpl(WS_AGENT_PROCESS_NAME,
WS_AGENT_START_CMD_LINE,
eq(new CommandImpl("org.eclipse.che.ws-agent",
"script\n" + WsAgentLauncherImpl.DEFAULT_WS_AGENT_RUN_COMMAND,
WS_AGENT_PROCESS_NAME)),
eq(WsAgentLauncherImpl.getWsAgentProcessOutputChannel(WORKSPACE_ID)));
@ -114,7 +117,7 @@ public class WsAgentLauncherImplTest {
@Test
public void shouldPingWsAgentAfterStart() throws Exception {
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(requestFactory).fromUrl(UriBuilder.fromUri(WS_AGENT_SERVER_URL)
.build()
@ -132,7 +135,7 @@ public class WsAgentLauncherImplTest {
new IOException())
.thenReturn(pingResponse);
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(requestFactory).fromUrl(WS_AGENT_SERVER_URL);
verify(pingRequest).setMethod(HttpMethod.GET);
@ -147,7 +150,7 @@ public class WsAgentLauncherImplTest {
HttpURLConnection.HTTP_NO_CONTENT,
HttpURLConnection.HTTP_OK);
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(requestFactory).fromUrl(WS_AGENT_SERVER_URL);
verify(pingRequest).setMethod(HttpMethod.GET);
@ -161,21 +164,21 @@ public class WsAgentLauncherImplTest {
when(pingRequest.request()).thenThrow(new ServerException(""))
.thenReturn(pingResponse);
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(pingRequest, times(2)).request();
verify(pingResponse).getResponseCode();
}
@Test(expectedExceptions = NotFoundException.class, expectedExceptionsMessageRegExp = "Test exception")
public void shouldThrowNotFoundExceptionIfMachineManagerExecInDevMachineThrowsNotFoundException() throws Exception {
@Test(expectedExceptions = ServerException.class, expectedExceptionsMessageRegExp = "Test exception")
public void shouldThrowMachineExceptionIfMachineManagerExecInDevMachineThrowsNotFoundException() throws Exception {
when(machineProcessManager.exec(anyString(),
anyString(),
any(Command.class),
anyString()))
.thenThrow(new NotFoundException("Test exception"));
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(machineProcessManager).exec(anyString(),
anyString(),
@ -183,7 +186,7 @@ public class WsAgentLauncherImplTest {
anyString());
}
@Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "Test exception")
@Test(expectedExceptions = ServerException.class, expectedExceptionsMessageRegExp = "Test exception")
public void shouldThrowMachineExceptionIfMachineManagerExecInDevMachineThrowsMachineException() throws Exception {
when(machineProcessManager.exec(anyString(),
anyString(),
@ -191,7 +194,7 @@ public class WsAgentLauncherImplTest {
anyString()))
.thenThrow(new MachineException("Test exception"));
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(machineProcessManager).exec(anyString(),
anyString(),
@ -207,7 +210,7 @@ public class WsAgentLauncherImplTest {
anyString()))
.thenThrow(new BadRequestException("Test exception"));
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
verify(machineProcessManager).exec(anyString(),
anyString(),
@ -220,7 +223,7 @@ public class WsAgentLauncherImplTest {
public void shouldThrowMachineExceptionIfPingsWereUnsuccessfulTooLong() throws Exception {
when(pingRequest.request()).thenThrow(new ServerException(""));
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
}
@Test(expectedExceptions = ServerException.class,
@ -228,6 +231,6 @@ public class WsAgentLauncherImplTest {
public void shouldThrowExceptionIfWsAgentNotFound() throws Exception {
doReturn(Collections.emptyMap()).when(machineRuntime).getServers();
wsAgentLauncher.startWsAgent(machine);
wsAgentLauncher.launch(machine, agent);
}
}

View File

@ -38,7 +38,7 @@
"machines": {
"devmachine": {
"agents": [
"ws-agent"
"org.eclipse.che.ws-agent"
],
"attributes" : {
"memoryLimitBytes": "2147483648"
@ -129,7 +129,7 @@
"machines": {
"devmachine": {
"agents": [
"ws-agent"
"org.eclipse.che.ws-agent"
],
"attributes" : {
"memoryLimitBytes": "2147483648"
@ -217,7 +217,7 @@
"machines": {
"devmachine": {
"agents": [
"ws-agent"
"org.eclipse.che.ws-agent"
],
"attributes" : {
"memoryLimitBytes": "2147483648"
@ -305,7 +305,7 @@
"machines": {
"devmachine": {
"agents": [
"ws-agent"
"org.eclipse.che.ws-agent"
],
"attributes" : {
"memoryLimitBytes": "2147483648"
@ -393,7 +393,7 @@
"machines": {
"devmachine": {
"agents": [
"ws-agent"
"org.eclipse.che.ws-agent"
]
}
},

View File

@ -38,6 +38,8 @@
<module>che-core-api-factory</module>
<module>che-core-api-ssh</module>
<module>che-core-api-ssh-shared</module>
<module>che-core-api-agent</module>
<module>che-core-api-agent-shared</module>
<module>wsmaster-local</module>
</modules>
</project>

View File

@ -24,7 +24,6 @@ import org.eclipse.che.api.workspace.server.model.impl.ServerConf2Impl;
import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
import org.mockito.Mockito;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -46,7 +45,6 @@ import static org.eclipse.che.commons.lang.NameGenerator.generate;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
* @author Eugene Voevodin
@ -127,7 +125,7 @@ public class LocalWorkspaceDaoTest {
properties.put("prop4", "value4");
servers.put("ref2", new ServerConf2Impl("port2", "proto2", properties));
machines = new HashMap<>();
machines.put("machine1", new ExtendedMachineImpl(asList("ws-agent", "someAgent"),
machines.put("machine1", new ExtendedMachineImpl(asList("org.eclipse.che.ws-agent", "someAgent"),
servers,
new HashMap<>(singletonMap("memoryLimitBytes", "10000"))));
servers = new HashMap<>();