diff --git a/assembly/assembly-main/pom.xml b/assembly/assembly-main/pom.xml
index a433a068a5..f95c57e4d5 100644
--- a/assembly/assembly-main/pom.xml
+++ b/assembly/assembly-main/pom.xml
@@ -30,7 +30,7 @@
org.eclipse.che
assembly-wsagent-server
- zip
+ tar.gz
org.eclipse.che
@@ -65,13 +65,13 @@
org.eclipse.che.lib
che-websocket-terminal
- zip
+ tar.gz
linux_amd64
org.eclipse.che.lib
che-websocket-terminal
- zip
+ tar.gz
linux_arm7
diff --git a/assembly/assembly-main/src/assembly/assembly.xml b/assembly/assembly-main/src/assembly/assembly.xml
index 4de6bfd277..da94ec0ee9 100644
--- a/assembly/assembly-main/src/assembly/assembly.xml
+++ b/assembly/assembly-main/src/assembly/assembly.xml
@@ -59,36 +59,28 @@
false
false
lib
- ws-agent.zip
+ ws-agent.tar.gz
org.eclipse.che:assembly-wsagent-server
false
- true
- lib/linux_amd64
+ false
+ lib/linux_amd64/terminal
+ websocket-terminal-linux_amd64.tar.gz
- org.eclipse.che.lib:che-websocket-terminal:zip:linux_amd64
+ org.eclipse.che.lib:che-websocket-terminal:tar.gz:linux_amd64
-
-
- META-INF/**
-
-
false
- true
- lib/linux_arm7
+ false
+ lib/linux_arm7/terminal
+ websocket-terminal-linux_arm7.tar.gz
- org.eclipse.che.lib:che-websocket-terminal:zip:linux_arm7
+ org.eclipse.che.lib:che-websocket-terminal:tar.gz:linux_arm7
-
-
- META-INF/**
-
-
false
diff --git a/assembly/assembly-main/src/assembly/bin/che-install-plugin.sh b/assembly/assembly-main/src/assembly/bin/che-install-plugin.sh
index 8e1e57fccf..345c447899 100644
--- a/assembly/assembly-main/src/assembly/bin/che-install-plugin.sh
+++ b/assembly/assembly-main/src/assembly/bin/che-install-plugin.sh
@@ -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
diff --git a/assembly/assembly-main/src/assembly/plugins/README b/assembly/assembly-main/src/assembly/plugins/README
index 084a8a873d..32af99c97d 100644
--- a/assembly/assembly-main/src/assembly/plugins/README
+++ b/assembly/assembly-main/src/assembly/plugins/README
@@ -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:
diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml
index 005afc513d..db61ffb3c5 100644
--- a/assembly/assembly-wsmaster-war/pom.xml
+++ b/assembly/assembly-wsmaster-war/pom.xml
@@ -54,6 +54,10 @@
javax.inject
javax.inject
+
+ org.eclipse.che.core
+ che-core-api-agent
+
org.eclipse.che.core
che-core-api-auth
diff --git a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
index 8a99b58aec..4cfc594829 100644
--- a/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
+++ b/assembly/assembly-wsmaster-war/src/main/java/org/eclipse/che/api/deploy/WsMasterModule.java
@@ -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 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 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());
diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties
index a36ded3b13..ff6961e16c 100644
--- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties
+++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/codenvy/che.properties
@@ -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
diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/ErrorFilteredConsumer.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/ErrorFilteredConsumer.java
new file mode 100644
index 0000000000..f6c01e91cb
--- /dev/null
+++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/ErrorFilteredConsumer.java
@@ -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]");
+ }
+}
diff --git a/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/util/ErrorFilteredConsumerTest.java b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/util/ErrorFilteredConsumerTest.java
new file mode 100644
index 0000000000..b7b41c9cb6
--- /dev/null
+++ b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/util/ErrorFilteredConsumerTest.java
@@ -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");
+ }
+}
diff --git a/core/commons/che-core-commons-inject/pom.xml b/core/commons/che-core-commons-inject/pom.xml
index d197797fca..695b20e9f6 100644
--- a/core/commons/che-core-commons-inject/pom.xml
+++ b/core/commons/che-core-commons-inject/pom.xml
@@ -90,7 +90,8 @@
mockito-core
${org.mockito.version}
test
-
+
+
org.testng
testng
test
diff --git a/core/commons/che-core-commons-lang/pom.xml b/core/commons/che-core-commons-lang/pom.xml
index 6b51a54391..0681972419 100644
--- a/core/commons/che-core-commons-lang/pom.xml
+++ b/core/commons/che-core-commons-lang/pom.xml
@@ -38,6 +38,16 @@
slf4j-api
+
+ org.mockito
+ mockito-all
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
org.testng
testng
@@ -45,6 +55,11 @@
+
+
+ src/test/resources
+
+
com.mycila
diff --git a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java
index a44f7a3957..f4fce6dfd4 100644
--- a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java
+++ b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java
@@ -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();
}
diff --git a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/ZipUtils.java b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/ZipUtils.java
index fe81f264cf..4444fea719 100644
--- a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/ZipUtils.java
+++ b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/ZipUtils.java
@@ -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 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 headers
diff --git a/core/commons/che-core-commons-lang/src/test/java/org/eclipse/che/commons/lang/ZipUtilsTest.java b/core/commons/che-core-commons-lang/src/test/java/org/eclipse/che/commons/lang/ZipUtilsTest.java
index a4bce9db7e..cec495dabc 100644
--- a/core/commons/che-core-commons-lang/src/test/java/org/eclipse/che/commons/lang/ZipUtilsTest.java
+++ b/core/commons/che-core-commons-lang/src/test/java/org/eclipse/che/commons/lang/ZipUtilsTest.java
@@ -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));
}
-}
\ No newline at end of file
+
+
+ @Test
+ public void testGetResources() throws Exception {
+ URL testJar = ZipUtilsTest.class.getResource("/che/che.jar");
+ @SuppressWarnings("unchecked")
+ Consumer consumer = mock(Consumer.class);
+
+ ZipUtils.getResources(new ZipFile(testJar.getFile()), Pattern.compile(".*[//]?codenvy/[^//]+[.]json"), consumer);
+
+ verify(consumer, times(2)).accept(any(InputStream.class));
+ }
+}
diff --git a/core/commons/che-core-commons-lang/src/test/resources/che/che.jar b/core/commons/che-core-commons-lang/src/test/resources/che/che.jar
new file mode 100644
index 0000000000..a96ab988c6
Binary files /dev/null and b/core/commons/che-core-commons-lang/src/test/resources/che/che.jar differ
diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.js b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.js
index 1497c8c485..f23637e901 100644
--- a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.js
+++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.js
@@ -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') {
diff --git a/dashboard/src/components/api/che-workspace.factory.js b/dashboard/src/components/api/che-workspace.factory.js
index 256a8e29c9..efd660d7dc 100644
--- a/dashboard/src/components/api/che-workspace.factory.js
+++ b/dashboard/src/components/api/che-workspace.factory.js
@@ -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 {
diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/WorkspaceEventsHandler.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/WorkspaceEventsHandler.java
index 175ccb5719..ff229af7f3 100644
--- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/WorkspaceEventsHandler.java
+++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/WorkspaceEventsHandler.java
@@ -342,7 +342,7 @@ public class WorkspaceEventsHandler {
if (environment != null) {
for (Map.Entry machineEntry : environment.getMachines()
.entrySet()) {
- if (machineEntry.getValue().getAgents().contains("ws-agent")) {
+ if (machineEntry.getValue().getAgents().contains("org.eclipse.che.ws-agent")) {
return machineEntry.getKey();
}
}
diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/create/CreateWorkspacePresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/create/CreateWorkspacePresenter.java
index 8ca8ce57e2..48d9acb87c 100644
--- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/create/CreateWorkspacePresenter.java
+++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/create/CreateWorkspacePresenter.java
@@ -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)
diff --git a/ide/che-core-ide-stacks/src/main/resources/stacks.json b/ide/che-core-ide-stacks/src/main/resources/stacks.json
index f54212ef97..ab793ea7e1 100644
--- a/ide/che-core-ide-stacks/src/main/resources/stacks.json
+++ b/ide/che-core-ide-stacks/src/main/resources/stacks.json
@@ -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" : {
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ComposeMachineProviderImpl.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ComposeMachineProviderImpl.java
index be998e6256..9b87198eab 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ComposeMachineProviderImpl.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ComposeMachineProviderImpl.java
@@ -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()))
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineImplTerminalLauncher.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineImplTerminalLauncher.java
deleted file mode 100644
index 5e8c17db63..0000000000
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/DockerMachineImplTerminalLauncher.java
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerExtServerModule.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerExtServerModule.java
index 3b08a2aefc..5b325f4768 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerExtServerModule.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerExtServerModule.java
@@ -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 devMachineEnvVars = Multibinder.newSetBinder(binder(),
String.class,
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerTerminalModule.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerTerminalModule.java
index acd9620021..475678939d 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerTerminalModule.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/DockerTerminalModule.java
@@ -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 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 terminalLaunchers = Multibinder.newSetBinder(binder(),
- MachineImplSpecificTerminalLauncher.class);
- terminalLaunchers.addBinding().to(DockerMachineImplTerminalLauncher.class);
}
}
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ApiEndpointEnvVariableProvider.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ApiEndpointEnvVariableProvider.java
index 8e8b2b38e9..1936514acf 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ApiEndpointEnvVariableProvider.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ApiEndpointEnvVariableProvider.java
@@ -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;
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ExtServerVolumeProvider.java b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/WsAgentVolumeProvider.java
similarity index 79%
rename from plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ExtServerVolumeProvider.java
rename to plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/WsAgentVolumeProvider.java
index 4f5adc5311..1180cae9d9 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/ExtServerVolumeProvider.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/main/java/org/eclipse/che/plugin/docker/machine/ext/provider/WsAgentVolumeProvider.java
@@ -35,30 +35,30 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
* @author Alexander Garagatyi
*/
@Singleton
-public class ExtServerVolumeProvider implements Provider {
+public class WsAgentVolumeProvider implements Provider {
- 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;
}
}
}
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java
index 4f6a284b69..ab8d3d6471 100644
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java
+++ b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerInstanceProviderTest.java
@@ -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)))
diff --git a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineTerminalLauncherTest.java b/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineTerminalLauncherTest.java
deleted file mode 100644
index fefffc6f73..0000000000
--- a/plugins/plugin-docker/che-plugin-docker-machine/src/test/java/org/eclipse/che/plugin/docker/machine/DockerMachineTerminalLauncherTest.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/plugins/plugin-maven/che-plugin-maven-server/src/test/resources/EffectivePom/pom.xml b/plugins/plugin-maven/che-plugin-maven-server/src/test/resources/EffectivePom/pom.xml
index 3aaf6efb37..d911d8be01 100644
--- a/plugins/plugin-maven/che-plugin-maven-server/src/test/resources/EffectivePom/pom.xml
+++ b/plugins/plugin-maven/che-plugin-maven-server/src/test/resources/EffectivePom/pom.xml
@@ -1910,4 +1910,4 @@
-
\ No newline at end of file
+
diff --git a/plugins/plugin-maven/maven-server/maven-server-impl/src/test/resources/EffectivePom/pom.xml b/plugins/plugin-maven/maven-server/maven-server-impl/src/test/resources/EffectivePom/pom.xml
index 6059761555..ea0c86d4d6 100644
--- a/plugins/plugin-maven/maven-server/maven-server-impl/src/test/resources/EffectivePom/pom.xml
+++ b/plugins/plugin-maven/maven-server/maven-server-impl/src/test/resources/EffectivePom/pom.xml
@@ -1901,4 +1901,4 @@
-
\ No newline at end of file
+
diff --git a/plugins/plugin-ssh-machine/pom.xml b/plugins/plugin-ssh-machine/pom.xml
index ae1b69c936..cd6c1809a2 100644
--- a/plugins/plugin-ssh-machine/pom.xml
+++ b/plugins/plugin-ssh-machine/pom.xml
@@ -54,6 +54,14 @@
javax.ws.rs
javax.ws.rs-api
+
+ org.eclipse.che.core
+ che-core-api-agent
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+
org.eclipse.che.core
che-core-api-core
diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java
index 722d95215d..68ade0f4e9 100644
--- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java
+++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java
@@ -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\\] (?[\\S]+) (?[\\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);
- }
- });
- }
}
diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineModule.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineModule.java
index 35de256d63..620c9f763b 100644
--- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineModule.java
+++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineModule.java
@@ -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 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 machineServers =
Multibinder.newSetBinder(binder(),
diff --git a/pom.xml b/pom.xml
index 764081fdb2..d954acd9f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,7 +66,7 @@
org.eclipse.che
assembly-wsagent-server
${che.version}
- zip
+ tar.gz
org.eclipse.che
@@ -80,6 +80,16 @@
${che.version}
war
+
+ org.eclipse.che.core
+ che-core-api-agent
+ ${che.version}
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+ ${che.version}
+
org.eclipse.che.core
che-core-api-auth
@@ -319,7 +329,7 @@
org.eclipse.che.lib
che-websocket-terminal
${che.lib.version}
- zip
+ tar.gz
linux_amd64
@@ -327,6 +337,13 @@
che-websocket-terminal
${che.lib.version}
zip
+ linux_amd64
+
+
+ org.eclipse.che.lib
+ che-websocket-terminal
+ ${che.lib.version}
+ tar.gz
linux_arm7
diff --git a/wsmaster/che-core-api-agent-shared/pom.xml b/wsmaster/che-core-api-agent-shared/pom.xml
new file mode 100644
index 0000000000..86e6c0c806
--- /dev/null
+++ b/wsmaster/che-core-api-agent-shared/pom.xml
@@ -0,0 +1,34 @@
+
+
+
+ 4.0.0
+
+ che-master-parent
+ org.eclipse.che.core
+ 5.0.0-M1-SNAPSHOT
+
+ che-core-api-agent-shared
+ jar
+ Che Core :: API :: Agent :: Shared
+
+
+ org.eclipse.che.core
+ che-core-api-dto
+
+
+ org.eclipse.che.core
+ che-core-commons-annotations
+
+
+
diff --git a/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentDto.java b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentDto.java
new file mode 100644
index 0000000000..fd4e0c0679
--- /dev/null
+++ b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentDto.java
@@ -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 getDependencies();
+
+ void setDependencies(List dependencies);
+
+ AgentDto withDependencies(List dependencies);
+
+ @Override
+ String getScript();
+
+ void setScript(String script);
+
+ AgentDto withScript(String script);
+
+ @Override
+ Map getProperties();
+
+ void setProperties(Map properties);
+
+ AgentDto withProperties(Map properties);
+}
diff --git a/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentKeyDto.java b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentKeyDto.java
new file mode 100644
index 0000000000..4f779e03e6
--- /dev/null
+++ b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/dto/AgentKeyDto.java
@@ -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);
+}
diff --git a/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/Agent.java b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/Agent.java
new file mode 100644
index 0000000000..b6f5d0bab9
--- /dev/null
+++ b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/Agent.java
@@ -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 getDependencies();
+
+ /**
+ * Returns the script to be applied when machine is started.
+ */
+ String getScript();
+
+ /**
+ * Returns any machine specific properties.
+ */
+ Map getProperties();
+}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncher.java b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/AgentKey.java
similarity index 51%
rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncher.java
rename to wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/AgentKey.java
index 26af48eb35..35c70681de 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncher.java
+++ b/wsmaster/che-core-api-agent-shared/src/main/java/org/eclipse/che/api/agent/shared/model/AgentKey.java
@@ -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();
}
diff --git a/wsmaster/che-core-api-agent/pom.xml b/wsmaster/che-core-api-agent/pom.xml
new file mode 100644
index 0000000000..9624d0ab17
--- /dev/null
+++ b/wsmaster/che-core-api-agent/pom.xml
@@ -0,0 +1,203 @@
+
+
+
+ 4.0.0
+
+ che-master-parent
+ org.eclipse.che.core
+ 5.0.0-M1-SNAPSHOT
+
+ che-core-api-agent
+ jar
+ Che Core :: API :: Agent
+
+ ${project.build.directory}/generated-sources/dto/
+ false
+
+
+
+ com.google.code.gson
+ gson
+
+
+ com.google.guava
+ guava
+
+
+ com.google.inject
+ guice
+
+
+ io.swagger
+ swagger-annotations
+
+
+ javax.inject
+ javax.inject
+
+
+ javax.ws.rs
+ javax.ws.rs-api
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+
+
+ org.eclipse.che.core
+ che-core-api-core
+
+
+ org.eclipse.che.core
+ che-core-api-dto
+
+
+ org.eclipse.che.core
+ che-core-api-machine
+
+
+ org.eclipse.che.core
+ che-core-api-model
+
+
+ org.eclipse.che.core
+ che-core-commons-annotations
+
+
+ org.eclipse.che.core
+ che-core-commons-lang
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.everrest
+ everrest-assured
+ test
+
+
+ org.hamcrest
+ hamcrest-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockitong
+ mockitong
+ test
+
+
+ org.testng
+ testng
+ test
+
+
+
+ src/main/java
+ src/test/java
+ target/classes
+
+
+ src/main/java
+
+
+ src/main/resources
+
+
+ ${project.build.directory}/generated-sources/dto/
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-resource
+ process-sources
+
+ add-resource
+
+
+
+
+ ${dto-generator-out-directory}/META-INF
+ META-INF
+
+
+
+
+
+ add-source
+ process-sources
+
+ add-source
+
+
+
+ ${dto-generator-out-directory}
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+ pre-compile
+ generate-sources
+
+ compile
+
+
+
+
+
+ org.eclipse.che.core
+ che-core-api-dto-maven-plugin
+ ${project.version}
+
+
+ generate-server-dto
+ process-sources
+
+ generate
+
+
+
+ org.eclipse.che.api.agent.shared.dto
+
+ ${dto-generator-out-directory}
+ org.eclipse.che.api.agent.server.dto.DtoServerImpls
+ server
+
+
+
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+ ${project.version}
+
+
+
+
+
+
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentModule.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentModule.java
new file mode 100644
index 0000000000..1b6804ee7d
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentModule.java
@@ -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);
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistry.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistry.java
new file mode 100644
index 0000000000..ed571dd5d7
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistry.java
@@ -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 getVersions(String name) throws AgentException;
+
+
+ /**
+ * Returns the list of available agents.
+ *
+ * @return list of agents
+ * @throws AgentException
+ * if unexpected error occurred
+ */
+ List getAgents() throws AgentException;
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryService.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryService.java
new file mode 100644
index 0000000000..fe0ca9c12b
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryService.java
@@ -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 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 getAgents() throws ApiException {
+ try {
+ return agentRegistry.getAgents();
+ } catch (AgentNotFoundException e) {
+ throw new NotFoundException(e.getMessage());
+ } catch (AgentException e) {
+ throw new ServerException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryUrlProvider.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryUrlProvider.java
new file mode 100644
index 0000000000..36948af563
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/AgentRegistryUrlProvider.java
@@ -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;
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/DtoConverter.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/DtoConverter.java
new file mode 100644
index 0000000000..7d4d4aa471
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/DtoConverter.java
@@ -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() { }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentException.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentException.java
new file mode 100644
index 0000000000..06d2de41e1
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentException.java
@@ -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);
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentNotFoundException.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentNotFoundException.java
new file mode 100644
index 0000000000..9251953717
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/exception/AgentNotFoundException.java
@@ -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);
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImpl.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImpl.java
new file mode 100644
index 0000000000..7af9a8bbd1
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImpl.java
@@ -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 getVersions(String name) throws AgentException {
+ URL url = urlProvider.getAgentVersionsUrl(name);
+ try {
+ HttpJsonResponse response = requestFactory.fromUrl(url.toString()).useGetMethod().request();
+
+ @SuppressWarnings("unchecked")
+ List versions = response.as(List.class, new TypeToken>() { }.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 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);
+ }
+ }
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryUrlProviderImpl.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryUrlProviderImpl.java
new file mode 100644
index 0000000000..3838dc81e9
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentRegistryUrlProviderImpl.java
@@ -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);
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentSorter.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentSorter.java
new file mode 100644
index 0000000000..1e3d8743b0
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/AgentSorter.java
@@ -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 sort(List agentKeys) throws AgentException {
+ List sorted = new ArrayList<>();
+ Set pending = new HashSet<>();
+
+ if (agentKeys != null) {
+ for (String agentKey : agentKeys) {
+ doSort(agentKeys, AgentKeyImpl.parse(agentKey), sorted, pending);
+ }
+ }
+
+ return sorted;
+ }
+
+ private void doSort(List agentKeys, AgentKey agentKey, List sorted, Set pending) throws AgentException {
+ String agentName = agentKey.getName();
+
+ Optional 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);
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImpl.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImpl.java
new file mode 100644
index 0000000000..02a89224f8
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImpl.java
@@ -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 agents;
+ private final List 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 getVersions(String name) throws AgentException {
+ Agent agent = doGetAgent(name);
+ String version = agent.getVersion();
+ return version == null ? Collections.emptyList() : singletonList(version);
+ }
+
+ @Override
+ public List 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 = 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 createAgentsConsumer = new Consumer() {
+ @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);
+ }
+ }
+ };
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AbstractAgentLauncher.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AbstractAgentLauncher.java
new file mode 100644
index 0000000000..d3cb126238
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AbstractAgentLauncher.java
@@ -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;
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncher.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncher.java
new file mode 100644
index 0000000000..37aa08f3a1
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncher.java
@@ -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;
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncherFactory.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncherFactory.java
new file mode 100644
index 0000000000..84d188c6d7
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLauncherFactory.java
@@ -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 launchers;
+ private final AgentLauncher defaultLauncher;
+
+ @Inject
+ public AgentLauncherFactory(Set 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);
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLaunchingChecker.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLaunchingChecker.java
new file mode 100644
index 0000000000..e8b635ca09
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/AgentLaunchingChecker.java
@@ -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();
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncher.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncher.java
new file mode 100644
index 0000000000..656b94c3f6
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncher.java
@@ -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";
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/ProcessIsLaunchedChecker.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/ProcessIsLaunchedChecker.java
new file mode 100644
index 0000000000..d9820099fa
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/ProcessIsLaunchedChecker.java
@@ -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());
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java
new file mode 100644
index 0000000000..f52118d583
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentImpl.java
@@ -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 dependencies;
+ private final Map properties;
+ private final String script;
+
+ public AgentImpl(String name,
+ String version,
+ List dependencies,
+ Map 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 getDependencies() {
+ return MoreObjects.firstNonNull(dependencies, new ArrayList());
+ }
+
+ @Override
+ public Map getProperties() {
+ return MoreObjects.firstNonNull(properties, new HashMap());
+ }
+
+ @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 + "\'}";
+ }
+}
+
diff --git a/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentKeyImpl.java b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentKeyImpl.java
new file mode 100644
index 0000000000..5514501260
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/model/impl/AgentKeyImpl.java
@@ -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 + "\'}";
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ssh.json b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ssh.json
new file mode 100644
index 0000000000..8eeace4e4c
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ssh.json
@@ -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"
+}
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.terminal.json b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.terminal.json
new file mode 100644
index 0000000000..7b90ecf129
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.terminal.json
@@ -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/"
+}
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ws-agent.json b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ws-agent.json
new file mode 100644
index 0000000000..53cf95225c
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/org.eclipse.che.ws-agent.json
@@ -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###############################################"
+}
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ssh.script.sh b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ssh.script.sh
new file mode 100644
index 0000000000..72a5f024bb
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ssh.script.sh
@@ -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
+
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.terminal.script.sh b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.terminal.script.sh
new file mode 100644
index 0000000000..76774d4cd5
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.terminal.script.sh
@@ -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/
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ws-agent.script.sh b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ws-agent.script.sh
new file mode 100644
index 0000000000..08c81aec50
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/org.eclipse.che.ws-agent.script.sh
@@ -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 ###
+###############################################
diff --git a/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/update.sh b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/update.sh
new file mode 100755
index 0000000000..ac69947732
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/main/resources/agents/scripts/update.sh
@@ -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"
+
+
+
+
diff --git a/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentKeyImplTest.java b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentKeyImplTest.java
new file mode 100644
index 0000000000..4680cf779e
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentKeyImplTest.java
@@ -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");
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImplTest.java b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImplTest.java
new file mode 100644
index 0000000000..31bb39fab4
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentRegistryImplTest.java
@@ -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() {
+ @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() {
+ @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 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();
+ }
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentSorterTest.java b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentSorterTest.java
new file mode 100644
index 0000000000..0ffc9e399e
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/AgentSorterTest.java
@@ -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 sorted = agentSorter.sort(Collections.singletonList("fqn1"));
+
+ assertEquals(sorted.size(), 1);
+ assertEquals(sorted.get(0).getName(), "fqn1");
+ }
+
+ @Test
+ public void sortAgentsRespectingDependencies() throws Exception {
+ List 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"));
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImplTest.java b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImplTest.java
new file mode 100644
index 0000000000..e4f9347a6f
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/impl/LocalAgentRegistryImplTest.java
@@ -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 agents = agentRegistry.getAgents();
+ assertFalse(agents.isEmpty());
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncherTest.java b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncherTest.java
new file mode 100644
index 0000000000..8c17af8108
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/java/org/eclipse/che/api/agent/server/launcher/DefaultAgentLauncherTest.java
@@ -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 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());
+ }
+}
diff --git a/wsmaster/che-core-api-agent/src/test/resources/config.sh b/wsmaster/che-core-api-agent/src/test/resources/config.sh
new file mode 100755
index 0000000000..0e9ecbfa00
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/resources/config.sh
@@ -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"
diff --git a/wsmaster/che-core-api-agent/src/test/resources/lib.sh b/wsmaster/che-core-api-agent/src/test/resources/lib.sh
new file mode 100755
index 0000000000..bb9e2c2f49
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/resources/lib.sh
@@ -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
+}
diff --git a/wsmaster/che-core-api-agent/src/test/resources/test.sh b/wsmaster/che-core-api-agent/src/test/resources/test.sh
new file mode 100755
index 0000000000..e48b5d496f
--- /dev/null
+++ b/wsmaster/che-core-api-agent/src/test/resources/test.sh
@@ -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
diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java
index b734166416..7ca4eb4544 100644
--- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java
+++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/builder/FactoryBuilderTest.java
@@ -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)
diff --git a/wsmaster/che-core-api-workspace/pom.xml b/wsmaster/che-core-api-workspace/pom.xml
index 7d32484de0..0380dc426c 100644
--- a/wsmaster/che-core-api-workspace/pom.xml
+++ b/wsmaster/che-core-api-workspace/pom.xml
@@ -73,6 +73,18 @@
javax.servlet
javax.servlet-api
+
+ org.eclipse.che.core
+ che-core-api-agent
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+
+
+ org.eclipse.che.core
+ che-core-api-agent-shared
+
org.eclipse.che.core
che-core-api-core
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineImplSpecificTerminalLauncher.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineImplSpecificTerminalLauncher.java
deleted file mode 100644
index 7c9d3817f8..0000000000
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineImplSpecificTerminalLauncher.java
+++ /dev/null
@@ -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;
-}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineTerminalLauncher.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineTerminalLauncher.java
deleted file mode 100644
index 256c923ed5..0000000000
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/terminal/MachineTerminalLauncher.java
+++ /dev/null
@@ -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 terminalLaunchers;
- private final ExecutorService executor;
-
- @Inject
- public MachineTerminalLauncher(EventService eventService,
- Set 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() {
- @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
- }
- });
- }
- }
- });
- }
-}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/AgentConfigApplier.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/AgentConfigApplier.java
new file mode 100644
index 0000000000..4092f4cf3b
--- /dev/null
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/AgentConfigApplier.java
@@ -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:
+ * ports
+ * environment
+ *
+ * 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 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 properties) {
+ String environment = properties.get(ENVIRONMENT.toString());
+ if (isNullOrEmpty(environment)) {
+ return;
+ }
+
+ Map 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 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;
+ }
+ }
+}
+
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentEngine.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentEngine.java
index 75bc85a933..36c26fc44d 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentEngine.java
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentEngine.java
@@ -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 entry : composeEnvironment.getServices().entrySet()) {
+ String machineName = entry.getKey();
+ ComposeServiceImpl composeService = entry.getValue();
+
+ List 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 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();
}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentValidator.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentValidator.java
index 2b4d3dc4b9..154df26b5e 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentValidator.java
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/environment/server/CheEnvironmentValidator.java
@@ -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 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));
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapter.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapter.java
index 88f9de5bb1..26a35c0faa 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapter.java
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceConfigJsonAdapter.java
@@ -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
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimes.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimes.java
index 7a2167e226..b1c36cf490 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimes.java
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimes.java
@@ -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 machines) throws ServerException {
+ for (Instance instance : machines) {
+ Map envMachines = environment.getMachines();
+ if (envMachines != null) {
+ ExtendedMachine extendedMachine = envMachines.get(instance.getConfig().getName());
+ if (extendedMachine != null) {
+ List 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 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;
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/SshAgentLauncherImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/SshAgentLauncherImpl.java
new file mode 100644
index 0000000000..9f555d5cba
--- /dev/null
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/SshAgentLauncherImpl.java
@@ -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";
+ }
+}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/TerminalAgentLauncherImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/TerminalAgentLauncherImpl.java
new file mode 100644
index 0000000000..e275427c12
--- /dev/null
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/TerminalAgentLauncherImpl.java
@@ -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";
+ }
+}
diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImpl.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImpl.java
similarity index 72%
rename from wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImpl.java
rename to wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImpl.java
index 06c2ce1937..0c558eb814 100644
--- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImpl.java
+++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImpl.java
@@ -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 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 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 servers = machine.getRuntime().getServers();
Server wsAgentServer = servers.get(Constants.WS_AGENT_PORT);
if (wsAgentServer == null) {
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/AgentConfigApplierTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/AgentConfigApplierTest.java
new file mode 100644
index 0000000000..31fa966c26
--- /dev/null
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/AgentConfigApplierTest.java
@@ -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 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 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 env = composeService.getEnvironment();
+ assertEquals(env.size(), 0);
+ }
+}
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentEngineTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentEngineTest.java
index 359e88b828..9ceeca91e4 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentEngineTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentEngineTest.java
@@ -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")),
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentValidatorTest.java
index 0fc17b4ada..3a5fd47c5f 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentValidatorTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/environment/server/CheEnvironmentValidatorTest.java
@@ -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")),
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java
index 08f1ad8267..200243ce90 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/DefaultWorkspaceValidatorTest.java
@@ -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")
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java
index 647e92bf64..974b0d8647 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceManagerTest.java
@@ -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()
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java
index 9d3bba46fa..ffb629368c 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceRuntimesTest.java
@@ -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 machines = asList(createMachine(true), createMachine(false));
when(environmentEngine.start(anyString(),
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java
index 08e83ef011..4963ad7866 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/WorkspaceServiceTest.java
@@ -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")));
diff --git a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImplTest.java b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImplTest.java
similarity index 81%
rename from wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImplTest.java
rename to wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImplTest.java
index ae3dcab325..e05f8d0b32 100644
--- a/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/agent/server/wsagent/WsAgentLauncherImplTest.java
+++ b/wsmaster/che-core-api-workspace/src/test/java/org/eclipse/che/api/workspace/server/launcher/WsAgentLauncherImplTest.java
@@ -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);
}
}
diff --git a/wsmaster/che-core-api-workspace/src/test/resources/stacks.json b/wsmaster/che-core-api-workspace/src/test/resources/stacks.json
index c179e4757e..9f9c44ee13 100644
--- a/wsmaster/che-core-api-workspace/src/test/resources/stacks.json
+++ b/wsmaster/che-core-api-workspace/src/test/resources/stacks.json
@@ -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"
]
}
},
diff --git a/wsmaster/pom.xml b/wsmaster/pom.xml
index 615913e412..1bc09b8a16 100644
--- a/wsmaster/pom.xml
+++ b/wsmaster/pom.xml
@@ -38,6 +38,8 @@
che-core-api-factory
che-core-api-ssh
che-core-api-ssh-shared
+ che-core-api-agent
+ che-core-api-agent-shared
wsmaster-local
diff --git a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java
index 745f3efc2e..26eda6f9e0 100644
--- a/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java
+++ b/wsmaster/wsmaster-local/src/test/java/org/eclipse/che/api/local/LocalWorkspaceDaoTest.java
@@ -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<>();