From 66dc411621d9964a7e6f197b34ab09cd1a10949c Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Tue, 24 Jan 2017 17:41:38 +0200 Subject: [PATCH 01/12] CHE-3621; decouple SSH machine implementation; --- .../plugin/machine/ssh/SshMachineFactory.java | 5 +- .../machine/ssh/SshMachineFactoryImpl.java | 60 ++++++++++++ .../machine/ssh/SshMachineInstance.java | 98 +++++++------------ .../ssh/SshMachineInstanceProvider.java | 29 +----- .../plugin/machine/ssh/SshMachineModule.java | 16 +-- .../plugin/machine/ssh/SshMachineProcess.java | 30 +++--- .../machine/ssh/jsch/JschSshClient.java | 13 +-- .../ssh/SshMachineInstanceProviderTest.java | 14 +-- 8 files changed, 123 insertions(+), 142 deletions(-) create mode 100644 plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java index 5e483f5d348..69fddf31924 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java @@ -16,7 +16,6 @@ import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; -import org.eclipse.che.api.machine.server.spi.Instance; import java.util.Map; @@ -39,7 +38,7 @@ SshClient createSshClient(@Assisted SshMachineRecipe sshMachineRecipe, @Assisted Map envVars); /** - * Creates ssh machine implementation of {@link Instance}. + * Creates ssh machine implementation instance. * * @param machine description of machine * @param sshClient ssh client of machine @@ -51,7 +50,7 @@ SshMachineInstance createInstance(@Assisted Machine machine, @Assisted LineConsumer outputConsumer) throws MachineException; /** - * Creates ssh machine implementation of {@link org.eclipse.che.api.machine.server.spi.InstanceProcess}. + * Creates ssh machine implementation of {@link SshMachineProcess}. * * @param command command that should be executed on process start * @param outputChannel channel where output will be available on process execution diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java new file mode 100644 index 00000000000..f0e350bad81 --- /dev/null +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 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.machine.ssh; + +import com.google.inject.assistedinject.Assisted; +import com.jcraft.jsch.JSch; + +import org.eclipse.che.api.core.model.machine.Command; +import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.ServerConf; +import org.eclipse.che.api.core.util.LineConsumer; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.eclipse.che.plugin.machine.ssh.jsch.JschSshClient; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Map; +import java.util.Set; + +/** + * @author Max Shaposhnik + */ +public class SshMachineFactoryImpl implements SshMachineFactory { + + private final int connectionTimeoutMs; + private final Set machinesServers; + + @Inject + public SshMachineFactoryImpl(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs, + @Named("machine.ssh.machine_servers") Set machinesServers) { + this.connectionTimeoutMs = connectionTimeoutMs; + this.machinesServers = machinesServers; + } + + + @Override + public SshClient createSshClient(@Assisted SshMachineRecipe sshMachineRecipe, @Assisted Map envVars) { + return new JschSshClient(sshMachineRecipe, envVars, new JSch(), connectionTimeoutMs); + } + + @Override + public SshMachineInstance createInstance(@Assisted Machine machine, @Assisted SshClient sshClient, + @Assisted LineConsumer outputConsumer) throws MachineException { + return new SshMachineInstance(machine, sshClient, outputConsumer, this, machinesServers); + } + + @Override + public SshMachineProcess createInstanceProcess(@Assisted Command command, @Assisted("outputChannel") String outputChannel, + @Assisted int pid, @Assisted SshClient sshClient) { + return new SshMachineProcess(command, outputChannel, pid, sshClient); + } +} diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index 206f555a8c5..24b7a14f0a0 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -10,24 +10,18 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; -import com.google.inject.assistedinject.Assisted; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; -import org.eclipse.che.api.core.model.machine.MachineSource; +import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.model.machine.ServerConf; 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.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.server.spi.InstanceNode; -import org.eclipse.che.api.machine.server.spi.InstanceProcess; -import org.eclipse.che.api.machine.server.spi.impl.AbstractInstance; -import javax.inject.Inject; -import javax.inject.Named; import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.net.URI; @@ -44,46 +38,48 @@ import static java.util.Collections.emptyMap; /** - * Implementation of {@link Instance} that uses represents ssh machine. + * Implementation of machine that represents ssh machine. * * @author Alexander Garagatyi * @see SshMachineInstanceProvider */ // todo try to avoid map of processes -public class SshMachineInstance extends AbstractInstance { +public class SshMachineInstance { private static final AtomicInteger pidSequence = new AtomicInteger(1); - private final SshClient sshClient; - private final LineConsumer outputConsumer; - private final SshMachineFactory machineFactory; + private String id; - private final Set machinesServers; - private final ConcurrentHashMap machineProcesses; + private final SshClient sshClient; + private final LineConsumer outputConsumer; + private final SshMachineFactory machineFactory; + private MachineStatus status; + + private final Set machinesServers; + private final ConcurrentHashMap machineProcesses; private MachineRuntimeInfoImpl machineRuntime; - @Inject - public SshMachineInstance(@Assisted Machine machine, - @Assisted SshClient sshClient, - @Assisted LineConsumer outputConsumer, + public SshMachineInstance(Machine machine, + SshClient sshClient, + LineConsumer outputConsumer, SshMachineFactory machineFactory, - @Named("machine.ssh.machine_servers") Set machinesServers) { - super(machine); + Set machinesServers) { + this.id = machine.getId(); this.sshClient = sshClient; this.outputConsumer = outputConsumer; this.machineFactory = machineFactory; + this.status = machine.getStatus(); this.machinesServers = new HashSet<>(machinesServers.size() + machine.getConfig().getServers().size()); this.machinesServers.addAll(machinesServers); this.machinesServers.addAll(machine.getConfig().getServers()); this.machineProcesses = new ConcurrentHashMap<>(); + this.machineRuntime = machine.getRuntime() != null ? new MachineRuntimeInfoImpl(machine.getRuntime()) : null; } - @Override public LineConsumer getLogger() { return outputConsumer; } - @Override public MachineRuntimeInfoImpl getRuntime() { // lazy initialization if (machineRuntime == null) { @@ -103,9 +99,8 @@ public MachineRuntimeInfoImpl getRuntime() { return machineRuntime; } - @Override - public InstanceProcess getProcess(final int pid) throws NotFoundException, MachineException { - final InstanceProcess machineProcess = machineProcesses.get(pid); + public SshMachineProcess getProcess(final int pid) throws NotFoundException, MachineException { + final SshMachineProcess machineProcess = machineProcesses.get(pid); if (machineProcess == null) { throw new NotFoundException(format("Process with pid %s not found", pid)); } @@ -118,38 +113,26 @@ public InstanceProcess getProcess(final int pid) throws NotFoundException, Machi } } - @Override - public List getProcesses() throws MachineException { + public List getProcesses() throws MachineException { // todo get children of session process return machineProcesses.values() .stream() - .filter(InstanceProcess::isAlive) + .filter(SshMachineProcess::isAlive) .collect(Collectors.toList()); } - @Override - public InstanceProcess createProcess(Command command, String outputChannel) throws MachineException { + public SshMachineProcess createProcess(Command command, String outputChannel) throws MachineException { final Integer pid = pidSequence.getAndIncrement(); - SshMachineProcess instanceProcess = machineFactory.createInstanceProcess(command, outputChannel, pid, sshClient); + SshMachineProcess machineProcess = machineFactory.createInstanceProcess(command, outputChannel, pid, sshClient); - machineProcesses.put(pid, instanceProcess); + machineProcesses.put(pid, machineProcess); - return instanceProcess; + return machineProcess; } - /** - * Not implemented.

- * - * {@inheritDoc} - */ - @Override - public MachineSource saveToSnapshot() throws MachineException { - throw new MachineException("Snapshot feature is unsupported for ssh machine implementation"); - } - @Override public void destroy() throws MachineException { try { outputConsumer.close(); @@ -161,34 +144,23 @@ public void destroy() throws MachineException { sshClient.stop(); } - @Override public InstanceNode getNode() { return null;// todo } - /** - * Not implemented.

- * - * {@inheritDoc} - */ - @Override - public String readFileContent(String filePath, int startFrom, int limit) throws MachineException { - // todo - throw new MachineException("File content reading is not implemented in ssh machine implementation"); + public MachineStatus getStatus() { + return status; } - /** - * Not implemented.

- * - * {@inheritDoc} - */ - @Override - public void copy(Instance sourceMachine, String sourcePath, String targetPath, boolean overwrite) throws MachineException { - //todo - throw new MachineException("Copying is not implemented in ssh machine implementation"); + public void setStatus(MachineStatus status) { + this.status = status; + } + + + public String getId() { + return id; } - @Override public void copy(String sourcePath, String targetPath) throws MachineException { sshClient.copy(sourcePath, targetPath); } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java index 79c0e9a2000..a114423c64e 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProvider.java @@ -21,10 +21,7 @@ import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.InvalidRecipeException; import org.eclipse.che.api.machine.server.exception.MachineException; -import org.eclipse.che.api.machine.server.exception.SnapshotException; import org.eclipse.che.api.machine.server.exception.UnsupportedRecipeException; -import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceProvider; import org.eclipse.che.api.machine.server.util.RecipeDownloader; import javax.inject.Inject; @@ -35,7 +32,7 @@ import static java.util.Objects.requireNonNull; /** - * Implementation of {@link InstanceProvider} based on communication with machine over ssh protocol. + * Instance provider based on communication with machine over ssh protocol. * *

Ssh machine can't be actually created and exists somewhere outside of the control.
* So this implementation just performs command execution in such machines.
@@ -44,7 +41,7 @@ * @author Alexander Garagatyi */ // todo tests -public class SshMachineInstanceProvider implements InstanceProvider { +public class SshMachineInstanceProvider { private static final Gson GSON = new Gson(); private final Set supportedRecipeTypes; @@ -58,12 +55,10 @@ public SshMachineInstanceProvider(SshMachineFactory sshMachineFactory, RecipeDow this.supportedRecipeTypes = Collections.singleton("ssh-config"); } - @Override public String getType() { return "ssh"; } - @Override public Set getRecipeTypes() { return supportedRecipeTypes; } @@ -76,7 +71,7 @@ public Set getRecipeTypes() { * machine description * @param lineConsumer * output for instance creation logs - * @return newly created {@link Instance} + * @return newly created {@link SshMachineInstance} * @throws UnsupportedRecipeException * if specified {@code recipe} is not supported * @throws InvalidRecipeException @@ -86,9 +81,7 @@ public Set getRecipeTypes() { * @throws MachineException * if other error occurs */ - @Override - public Instance createInstance(Machine machine, LineConsumer lineConsumer) - throws UnsupportedRecipeException, InvalidRecipeException, NotFoundException, MachineException { + public SshMachineInstance createInstance(Machine machine, LineConsumer lineConsumer) throws NotFoundException, MachineException { requireNonNull(machine, "Non null machine required"); requireNonNull(lineConsumer, "Non null logs consumer required"); requireNonNull(machine.getConfig().getSource().getLocation(), "Location in machine source is required"); @@ -111,18 +104,4 @@ public Instance createInstance(Machine machine, LineConsumer lineConsumer) instance.setStatus(MachineStatus.RUNNING); return instance; } - - /** - * Removes snapshot of the instance in implementation specific way. - * - * @param machineSource - * contains implementation specific key of the snapshot of the instance that should be removed - * @throws SnapshotException - * if exception occurs on instance snapshot removal - */ - @Override - public void removeInstanceSnapshot(MachineSource machineSource) throws SnapshotException { - throw new SnapshotException("Snapshot feature is unsupported for ssh machine implementation"); - } - } 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 77c740a0540..2b4b899c57f 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 @@ -11,7 +11,6 @@ package org.eclipse.che.plugin.machine.ssh; import com.google.inject.AbstractModule; -import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Names; @@ -23,20 +22,9 @@ public class SshMachineModule extends AbstractModule { @Override protected void configure() { - Multibinder machineProviderMultibinder = - Multibinder.newSetBinder(binder(), - org.eclipse.che.api.machine.server.spi.InstanceProvider.class); - machineProviderMultibinder.addBinding() - .to(SshMachineInstanceProvider.class); + bind(SshMachineInstanceProvider.class); - install(new FactoryModuleBuilder() - .implement(org.eclipse.che.api.machine.server.spi.Instance.class, - org.eclipse.che.plugin.machine.ssh.SshMachineInstance.class) - .implement(org.eclipse.che.api.machine.server.spi.InstanceProcess.class, - org.eclipse.che.plugin.machine.ssh.SshMachineProcess.class) - .implement(org.eclipse.che.plugin.machine.ssh.SshClient.class, - org.eclipse.che.plugin.machine.ssh.jsch.JschSshClient.class) - .build(SshMachineFactory.class)); + bind(SshMachineFactory.class).to(SshMachineFactoryImpl.class); bindConstant().annotatedWith(Names.named("machine.ssh.server.terminal.location")).to("~/che"); diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java index 3fa2e144f13..bbb3afe84f0 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java @@ -17,24 +17,27 @@ import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; -import org.eclipse.che.api.machine.server.spi.InstanceProcess; -import org.eclipse.che.api.machine.server.spi.impl.AbstractMachineProcess; import org.eclipse.che.commons.annotation.Nullable; import javax.inject.Inject; - import java.io.IOException; +import java.util.Map; import static java.lang.String.format; /** - * Ssh machine implementation of {@link InstanceProcess} + * Ssh machine process implementation. * * @author Alexander Garagatyi */ -public class SshMachineProcess extends AbstractMachineProcess implements InstanceProcess { - private final String commandLine; - private final SshClient sshClient; +public class SshMachineProcess { + private final SshClient sshClient; + private final String name; + private final String commandLine; + private final String type; + private final Map attributes; + private final int pid; + private final String outputChannel; private volatile boolean started; @@ -45,13 +48,16 @@ public SshMachineProcess(@Assisted Command command, @Nullable @Assisted("outputChannel") String outputChannel, @Assisted int pid, @Assisted SshClient sshClient) { - super(command, pid, outputChannel); this.sshClient = sshClient; this.commandLine = command.getCommandLine(); this.started = false; + this.name = command.getName(); + this.type = command.getType(); + this.attributes = command.getAttributes(); + this.pid = pid; + this.outputChannel = outputChannel; } - @Override public boolean isAlive() { if (!started) { return false; @@ -66,12 +72,10 @@ public boolean isAlive() { } } - @Override public void start() throws ConflictException, MachineException { start(null); } - @Override public void start(LineConsumer output) throws ConflictException, MachineException { if (started) { throw new ConflictException("Process already started."); @@ -89,18 +93,16 @@ public void start(LineConsumer output) throws ConflictException, MachineExceptio } } - @Override public void checkAlive() throws MachineException, NotFoundException { if (!started) { throw new NotFoundException("Process is not started yet"); } if (sshProcess.getExitCode() != -1) { - throw new NotFoundException(format("Process with pid %s not found", getPid())); + throw new NotFoundException(format("Process with pid %s not found", pid)); } } - @Override public void kill() throws MachineException { sshProcess.kill(); } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java index 7f2f9e1ba8a..f1dd4041228 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh.jsch; -import com.google.inject.assistedinject.Assisted; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; @@ -25,7 +24,6 @@ import org.eclipse.che.plugin.machine.ssh.SshMachineRecipe; import javax.inject.Inject; -import javax.inject.Named; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -60,10 +58,10 @@ public class JschSshClient implements SshClient { private Session session; @Inject - public JschSshClient(@Assisted SshMachineRecipe sshMachineRecipe, - @Assisted Map envVars, + public JschSshClient(SshMachineRecipe sshMachineRecipe, + Map envVars, JSch jsch, - @Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs) { + int connectionTimeoutMs) { this.envVars = envVars; this.connectionTimeout = connectionTimeoutMs; this.user = JschUserInfoImpl.builder() @@ -79,12 +77,10 @@ public JschSshClient(@Assisted SshMachineRecipe sshMachineRecipe, this.username = sshMachineRecipe.getUsername(); } - @Override public String getHost() { return host; } - @Override public void start() throws MachineException { try { session = jsch.getSession(username, host, port); @@ -108,12 +104,10 @@ public void start() throws MachineException { // envVariableEntry.getValue())); // todo process output - @Override public void stop() throws MachineException { session.disconnect(); } - @Override public JschSshProcess createProcess(String commandLine) throws MachineException { try { ChannelExec exec = (ChannelExec)session.openChannel("exec"); @@ -130,7 +124,6 @@ public JschSshProcess createProcess(String commandLine) throws MachineException } } - @Override public void copy(String sourcePath, String targetPath) throws MachineException { File source = new File(sourcePath); if (!source.exists()) { diff --git a/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java b/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java index b9538ac77cc..012929ecf17 100644 --- a/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java +++ b/plugins/plugin-ssh-machine/src/test/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstanceProviderTest.java @@ -17,13 +17,11 @@ import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; -import org.eclipse.che.api.machine.server.exception.SnapshotException; 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.MachineSourceImpl; import org.eclipse.che.api.machine.server.model.impl.ServerConfImpl; import org.eclipse.che.api.machine.server.recipe.RecipeImpl; -import org.eclipse.che.api.machine.server.spi.Instance; import org.eclipse.che.api.machine.server.util.RecipeDownloader; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -67,10 +65,6 @@ public class SshMachineInstanceProviderTest { @BeforeMethod public void setUp() throws Exception { machine = createMachine(); - SshMachineRecipe sshMachineRecipe = new SshMachineRecipe("localhost", - 22, - "user", - "password"); recipe = new RecipeImpl().withType("ssh-config") .withScript(RECIPE_SCRIPT); } @@ -85,12 +79,6 @@ public void shouldReturnCorrectRecipeTypes() throws Exception { assertEquals(provider.getRecipeTypes(), new HashSet<>(singletonList("ssh-config"))); } - @Test(expectedExceptions = SnapshotException.class, - expectedExceptionsMessageRegExp = "Snapshot feature is unsupported for ssh machine implementation") - public void shouldThrowSnapshotExceptionOnRemoveSnapshot() throws Exception { - provider.removeInstanceSnapshot(null); - } - @Test(expectedExceptions = MachineException.class, expectedExceptionsMessageRegExp = "Dev machine is not supported for Ssh machine implementation") public void shouldThrowExceptionOnDevMachineCreationFromRecipe() throws Exception { @@ -114,7 +102,7 @@ public void shouldBeAbleToCreateSshMachineInstanceOnMachineCreationFromRecipe() when(sshMachineFactory.createInstance(eq(machine), eq(sshClient), any(LineConsumer.class))).thenReturn(sshMachineInstance); when(recipeDownloader.getRecipe(eq(machine.getConfig()))).thenReturn(recipe); - Instance instance = provider.createInstance(machine, LineConsumer.DEV_NULL); + SshMachineInstance instance = provider.createInstance(machine, LineConsumer.DEV_NULL); assertEquals(instance, sshMachineInstance); } From cd8562956f0b275af48d5b543ce94471b1ab3c8c Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Wed, 25 Jan 2017 11:04:16 +0200 Subject: [PATCH 02/12] fixup! CHE-3621; decouple SSH machine implementation; --- .../plugin/machine/ssh/SshMachineFactory.java | 20 +++++++++---------- .../machine/ssh/SshMachineFactoryImpl.java | 17 ++++++++-------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java index 69fddf31924..b8aa767be19 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; -import com.google.inject.assistedinject.Assisted; - import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.core.util.LineConsumer; @@ -34,8 +32,8 @@ public interface SshMachineFactory { * @param envVars * environment variables that should be injected into machine */ - SshClient createSshClient(@Assisted SshMachineRecipe sshMachineRecipe, - @Assisted Map envVars); + SshClient createSshClient(SshMachineRecipe sshMachineRecipe, + Map envVars); /** * Creates ssh machine implementation instance. @@ -45,9 +43,9 @@ SshClient createSshClient(@Assisted SshMachineRecipe sshMachineRecipe, * @param outputConsumer consumer of output from container main process * @throws MachineException if error occurs on creation of {@code Instance} */ - SshMachineInstance createInstance(@Assisted Machine machine, - @Assisted SshClient sshClient, - @Assisted LineConsumer outputConsumer) throws MachineException; + SshMachineInstance createInstance(Machine machine, + SshClient sshClient, + LineConsumer outputConsumer) throws MachineException; /** * Creates ssh machine implementation of {@link SshMachineProcess}. @@ -57,8 +55,8 @@ SshMachineInstance createInstance(@Assisted Machine machine, * @param pid virtual id of that process * @param sshClient client to communicate with machine */ - SshMachineProcess createInstanceProcess(@Assisted Command command, - @Assisted("outputChannel") String outputChannel, - @Assisted int pid, - @Assisted SshClient sshClient); + SshMachineProcess createInstanceProcess(Command command, + String outputChannel, + int pid, + SshClient sshClient); } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java index f0e350bad81..75da0c670e5 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; -import com.google.inject.assistedinject.Assisted; import com.jcraft.jsch.JSch; import org.eclipse.che.api.core.model.machine.Command; @@ -30,31 +29,31 @@ */ public class SshMachineFactoryImpl implements SshMachineFactory { - private final int connectionTimeoutMs; + private final int connectionTimeoutMs; private final Set machinesServers; @Inject - public SshMachineFactoryImpl(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs, - @Named("machine.ssh.machine_servers") Set machinesServers) { + public SshMachineFactoryImpl(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs, + @Named("machine.ssh.machine_servers") Set machinesServers) { this.connectionTimeoutMs = connectionTimeoutMs; this.machinesServers = machinesServers; } @Override - public SshClient createSshClient(@Assisted SshMachineRecipe sshMachineRecipe, @Assisted Map envVars) { + public SshClient createSshClient(SshMachineRecipe sshMachineRecipe, Map envVars) { return new JschSshClient(sshMachineRecipe, envVars, new JSch(), connectionTimeoutMs); } @Override - public SshMachineInstance createInstance(@Assisted Machine machine, @Assisted SshClient sshClient, - @Assisted LineConsumer outputConsumer) throws MachineException { + public SshMachineInstance createInstance(Machine machine, SshClient sshClient, + LineConsumer outputConsumer) throws MachineException { return new SshMachineInstance(machine, sshClient, outputConsumer, this, machinesServers); } @Override - public SshMachineProcess createInstanceProcess(@Assisted Command command, @Assisted("outputChannel") String outputChannel, - @Assisted int pid, @Assisted SshClient sshClient) { + public SshMachineProcess createInstanceProcess(Command command, String outputChannel, + int pid, SshClient sshClient) { return new SshMachineProcess(command, outputChannel, pid, sshClient); } } From c078864bd61cfb67fe677b0dca37d16832077146 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Wed, 25 Jan 2017 12:15:23 +0200 Subject: [PATCH 03/12] fixup! Merge branch 'master' into CHE-3621 --- .../plugin/machine/ssh/SshMachineProcess.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java index bbb3afe84f0..98d3bf880c1 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java @@ -107,6 +107,22 @@ public void kill() throws MachineException { sshProcess.kill(); } + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public Map getAttributes() { + return attributes; + } + + public String getOutputChannel() { + return outputChannel; + } + private static class PrefixingLineConsumer implements LineConsumer { private final String prefix; private final LineConsumer lineConsumer; From ef176dcf665b0e26393f4f78e82eaaa467748e45 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Wed, 25 Jan 2017 15:25:06 +0200 Subject: [PATCH 04/12] fixup! fixup! Merge branch 'master' into CHE-3621 --- .../plugin/machine/ssh/SshMachineFactory.java | 41 +++++++++---- .../machine/ssh/SshMachineFactoryImpl.java | 59 ------------------- .../machine/ssh/SshMachineInstance.java | 5 +- .../plugin/machine/ssh/SshMachineModule.java | 2 +- .../machine/ssh/jsch/JschSshClient.java | 3 +- 5 files changed, 36 insertions(+), 74 deletions(-) delete mode 100644 plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java index b8aa767be19..6e556c83cba 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java @@ -10,19 +10,38 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; +import com.jcraft.jsch.JSch; + import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.core.util.LineConsumer; import org.eclipse.che.api.machine.server.exception.MachineException; +import org.eclipse.che.plugin.machine.ssh.jsch.JschSshClient; +import javax.inject.Inject; +import javax.inject.Named; import java.util.Map; +import java.util.Set; /** * Provides ssh machine implementation instances. * * @author Alexander Garagatyi + * @author Max Shaposhnik */ -public interface SshMachineFactory { +public class SshMachineFactory { + + private final int connectionTimeoutMs; + private final Set machinesServers; + + @Inject + public SshMachineFactory(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs, + @Named("machine.ssh.machine_servers") Set machinesServers) { + this.connectionTimeoutMs = connectionTimeoutMs; + this.machinesServers = machinesServers; + } + /** * Creates {@link SshClient} to communicate with machine over SSH protocol. @@ -32,8 +51,9 @@ public interface SshMachineFactory { * @param envVars * environment variables that should be injected into machine */ - SshClient createSshClient(SshMachineRecipe sshMachineRecipe, - Map envVars); + public SshClient createSshClient(SshMachineRecipe sshMachineRecipe, Map envVars) { + return new JschSshClient(sshMachineRecipe, envVars, new JSch(), connectionTimeoutMs); + } /** * Creates ssh machine implementation instance. @@ -43,9 +63,10 @@ SshClient createSshClient(SshMachineRecipe sshMachineRecipe, * @param outputConsumer consumer of output from container main process * @throws MachineException if error occurs on creation of {@code Instance} */ - SshMachineInstance createInstance(Machine machine, - SshClient sshClient, - LineConsumer outputConsumer) throws MachineException; + public SshMachineInstance createInstance(Machine machine, SshClient sshClient, + LineConsumer outputConsumer) throws MachineException { + return new SshMachineInstance(machine, sshClient, outputConsumer, this, machinesServers); + } /** * Creates ssh machine implementation of {@link SshMachineProcess}. @@ -55,8 +76,8 @@ SshMachineInstance createInstance(Machine machine, * @param pid virtual id of that process * @param sshClient client to communicate with machine */ - SshMachineProcess createInstanceProcess(Command command, - String outputChannel, - int pid, - SshClient sshClient); + public SshMachineProcess createInstanceProcess(Command command, String outputChannel, + int pid, SshClient sshClient) { + return new SshMachineProcess(command, outputChannel, pid, sshClient); + } } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java deleted file mode 100644 index 75da0c670e5..00000000000 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactoryImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2017 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.machine.ssh; - -import com.jcraft.jsch.JSch; - -import org.eclipse.che.api.core.model.machine.Command; -import org.eclipse.che.api.core.model.machine.Machine; -import org.eclipse.che.api.core.model.machine.ServerConf; -import org.eclipse.che.api.core.util.LineConsumer; -import org.eclipse.che.api.machine.server.exception.MachineException; -import org.eclipse.che.plugin.machine.ssh.jsch.JschSshClient; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.Map; -import java.util.Set; - -/** - * @author Max Shaposhnik - */ -public class SshMachineFactoryImpl implements SshMachineFactory { - - private final int connectionTimeoutMs; - private final Set machinesServers; - - @Inject - public SshMachineFactoryImpl(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs, - @Named("machine.ssh.machine_servers") Set machinesServers) { - this.connectionTimeoutMs = connectionTimeoutMs; - this.machinesServers = machinesServers; - } - - - @Override - public SshClient createSshClient(SshMachineRecipe sshMachineRecipe, Map envVars) { - return new JschSshClient(sshMachineRecipe, envVars, new JSch(), connectionTimeoutMs); - } - - @Override - public SshMachineInstance createInstance(Machine machine, SshClient sshClient, - LineConsumer outputConsumer) throws MachineException { - return new SshMachineInstance(machine, sshClient, outputConsumer, this, machinesServers); - } - - @Override - public SshMachineProcess createInstanceProcess(Command command, String outputChannel, - int pid, SshClient sshClient) { - return new SshMachineProcess(command, outputChannel, pid, sshClient); - } -} diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index 24b7a14f0a0..13f99a557e2 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -47,7 +47,8 @@ public class SshMachineInstance { private static final AtomicInteger pidSequence = new AtomicInteger(1); - private String id; + private String id; + private MachineRuntimeInfoImpl machineRuntime; private final SshClient sshClient; private final LineConsumer outputConsumer; @@ -57,7 +58,6 @@ public class SshMachineInstance { private final Set machinesServers; private final ConcurrentHashMap machineProcesses; - private MachineRuntimeInfoImpl machineRuntime; public SshMachineInstance(Machine machine, SshClient sshClient, @@ -73,7 +73,6 @@ public SshMachineInstance(Machine machine, this.machinesServers.addAll(machinesServers); this.machinesServers.addAll(machine.getConfig().getServers()); this.machineProcesses = new ConcurrentHashMap<>(); - this.machineRuntime = machine.getRuntime() != null ? new MachineRuntimeInfoImpl(machine.getRuntime()) : null; } public LineConsumer getLogger() { 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 2b4b899c57f..f839e30e0a2 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 @@ -24,7 +24,7 @@ public class SshMachineModule extends AbstractModule { protected void configure() { bind(SshMachineInstanceProvider.class); - bind(SshMachineFactory.class).to(SshMachineFactoryImpl.class); + bind(SshMachineFactory.class).to(SshMachineFactory.class); bindConstant().annotatedWith(Names.named("machine.ssh.server.terminal.location")).to("~/che"); diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java index f1dd4041228..71d53aef57a 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java @@ -24,6 +24,7 @@ import org.eclipse.che.plugin.machine.ssh.SshMachineRecipe; import javax.inject.Inject; +import javax.inject.Named; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -61,7 +62,7 @@ public class JschSshClient implements SshClient { public JschSshClient(SshMachineRecipe sshMachineRecipe, Map envVars, JSch jsch, - int connectionTimeoutMs) { + @Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs) { this.envVars = envVars; this.connectionTimeout = connectionTimeoutMs; this.user = JschUserInfoImpl.builder() From 787e35a24e306972caf07e772070524ba489f4b6 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Thu, 26 Jan 2017 11:20:01 +0200 Subject: [PATCH 05/12] fixup! fixup! fixup! Merge branch 'master' into CHE-3621 --- .../ssh/SshMachineImplTerminalLauncher.java | 121 +++++++++++++++--- .../machine/ssh/SshMachineInstance.java | 5 + .../ssh/SshProcessLaunchedChecker.java | 42 ++++++ 3 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshProcessLaunchedChecker.java 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 66316031d79..4254613021b 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,23 +10,33 @@ *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import org.eclipse.che.api.agent.server.model.impl.AgentImpl; 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.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.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 org.eclipse.che.api.workspace.server.launcher.TerminalAgentLauncherImpl; +import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; +import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext; import org.slf4j.Logger; import javax.inject.Inject; import javax.inject.Named; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; import static org.slf4j.LoggerFactory.getLogger; @@ -36,7 +46,7 @@ * @author Alexander Garagatyi * @author Anatolii Bazko */ -public class SshMachineImplTerminalLauncher extends TerminalAgentLauncherImpl { +public class SshMachineImplTerminalLauncher { private static final Logger LOG = getLogger(SshMachineImplTerminalLauncher.class); // Regex to parse output of command 'uname -sm' @@ -44,11 +54,20 @@ public class SshMachineImplTerminalLauncher extends TerminalAgentLauncherImpl { // 1. named group 'os' that contains 1+ non-space characters // 2. space character // 3. named group 'architecture' that contains 1+ non-space characters - private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?[\\S]+) (?[\\S]+)"); - private static final String DEFAULT_ARCHITECTURE = "linux_amd64"; + private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?[\\S]+) (?[\\S]+)"); + private static final String DEFAULT_ARCHITECTURE = "linux_amd64"; + private static final ExecutorService executor = + Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("SshAgentLauncher-%d") + .setUncaughtExceptionHandler( + LoggingUncaughtExceptionHandler.getInstance()) + .setDaemon(true) + .build()); private final WebsocketTerminalFilesPathProvider archivePathProvider; private final String terminalLocation; + private final long agentMaxStartTimeMs; + private final long agentPingDelayMs; + private final String terminalRunCommand; @Inject public SshMachineImplTerminalLauncher(@Named("che.agent.dev.max_start_time_ms") long agentMaxStartTimeMs, @@ -56,30 +75,55 @@ public SshMachineImplTerminalLauncher(@Named("che.agent.dev.max_start_time_ms") @Named("machine.ssh.server.terminal.location") String terminalLocation, @Named("machine.terminal_agent.run_command") String terminalRunCommand, WebsocketTerminalFilesPathProvider terminalPathProvider) { - super(agentMaxStartTimeMs, agentPingDelayMs, terminalRunCommand); + this.agentMaxStartTimeMs = agentMaxStartTimeMs; + this.agentPingDelayMs = agentPingDelayMs; + this.terminalRunCommand = terminalRunCommand; this.archivePathProvider = terminalPathProvider; this.terminalLocation = terminalLocation; } - @Override - public String getMachineType() { - return "ssh"; - } - - @Override - public void launch(Instance machine, Agent agent) throws ServerException { + public void launch(SshMachineInstance machine, Agent agent) throws ServerException { try { + if (isNullOrEmpty(agent.getScript())) { + return; + } String architecture = detectArchitecture(machine); machine.copy(archivePathProvider.getPath(architecture), terminalLocation); + final AgentImpl agentCopy = new AgentImpl(agent); + agentCopy.setScript(agent.getScript() + "\n" + terminalRunCommand); + + try { + final SshMachineProcess process = start(machine, agent); + LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agent.getId(), machine.getWorkspaceId()); + + final long pingStartTimestamp = System.currentTimeMillis(); + SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); + 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())); + } - super.launch(machine, agent); + final String errMsg = format("Fail launching agent %s. Workspace ID:%s", agent.getName(), machine.getWorkspaceId()); + LOG.error(errMsg); + throw new ServerException(errMsg); } catch (ConflictException e) { // should never happen throw new ServerException("Internal server error occurs on terminal launching."); } } - private String detectArchitecture(Instance machine) throws ConflictException, MachineException { + private String detectArchitecture(SshMachineInstance machine) throws ConflictException, MachineException { // uname -sm shows OS and CPU architecture // Examples of output: // Windows 10 amd64 @@ -91,10 +135,10 @@ private String detectArchitecture(Instance machine) throws ConflictException, Ma // Darwin x86_64 // Samsung Artik arm7 // Linux armv7l - InstanceProcess getUnameOutput = machine.createProcess(new CommandImpl("discover machine architecture", - "uname -sm", - null), - null); + SshMachineProcess getUnameOutput = machine.createProcess(new CommandImpl("discover machine architecture", + "uname -sm", + null), + null); ListLineConsumer lineConsumer = new ListLineConsumer(); getUnameOutput.start(lineConsumer); String unameOutput = lineConsumer.getText(); @@ -132,4 +176,41 @@ private String detectArchitecture(Instance machine) throws ConflictException, Ma return DEFAULT_ARCHITECTURE; } } + + protected SshMachineProcess start(SshMachineInstance machine, Agent agent) throws ServerException { + Command command = new CommandImpl(agent.getId(), agent.getScript(), "agent"); + SshMachineProcess process = machine.createProcess(command, null); + LineConsumer lineConsumer = new AbstractLineConsumer() { + @Override + public void writeLine(String line) throws IOException { + machine.getLogger().writeLine(line); + } + }; + + CountDownLatch countDownLatch = new CountDownLatch(1); + executor.execute(ThreadLocalPropagateContext.wrap(() -> { + try { + countDownLatch.countDown(); + 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) { + } + } + })); + try { + // ensure that code inside of task submitted to executor is called before end of this method + countDownLatch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return process; + } } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index 13f99a557e2..c1c9b9595be 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -48,6 +48,7 @@ public class SshMachineInstance { private static final AtomicInteger pidSequence = new AtomicInteger(1); private String id; + private String workspaceId; private MachineRuntimeInfoImpl machineRuntime; private final SshClient sshClient; @@ -65,6 +66,7 @@ public SshMachineInstance(Machine machine, SshMachineFactory machineFactory, Set machinesServers) { this.id = machine.getId(); + this.workspaceId = machine.getWorkspaceId(); this.sshClient = sshClient; this.outputConsumer = outputConsumer; this.machineFactory = machineFactory; @@ -179,4 +181,7 @@ private ServerImpl serverConfToServer(ServerConf serverConf, UriBuilder uriBuild null); } + public String getWorkspaceId() { + return workspaceId; + } } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshProcessLaunchedChecker.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshProcessLaunchedChecker.java new file mode 100644 index 00000000000..8d4c3828427 --- /dev/null +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshProcessLaunchedChecker.java @@ -0,0 +1,42 @@ +package org.eclipse.che.plugin.machine.ssh; + +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 static java.lang.String.format; + +/** + * @author Max Shaposhnik (mshaposhnik@codenvy.com) on 1/26/17. + * @version $Id: $ + */ +public class SshProcessLaunchedChecker { + private static final String CHECK_COMMAND = "command -v pidof >/dev/null 2>&1 && {\n" + + " pidof %1$s >/dev/null 2>&1 && echo 0 || echo 1\n" + + "} || {\n" + + " ps -fC %1$s >/dev/null 2>&1 && echo 0 || echo 1\n" + + "}"; + private final String processNameToWait; + private long counter; + + public SshProcessLaunchedChecker(String processNameToWait) { + this.processNameToWait = processNameToWait; + } + + public boolean isLaunched(Agent agent, SshMachineProcess process, SshMachineInstance machine) throws MachineException { + Command command = new CommandImpl(format("Wait for %s, try %d", agent.getId(), ++counter), + format(CHECK_COMMAND, processNameToWait), + "test"); + + try (ListLineConsumer lineConsumer = new ListLineConsumer()) { + SshMachineProcess waitProcess = machine.createProcess(command, null); + waitProcess.start(lineConsumer); + return lineConsumer.getText().endsWith("[STDOUT] 0"); + } catch (ConflictException e) { + throw new MachineException(e.getServiceError()); + } + } +} From 31e51a9d8b711da1029dd60d076cefd076738d85 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Thu, 26 Jan 2017 11:53:38 +0200 Subject: [PATCH 06/12] Merge with master --- .../agent/SshMachineExecAgentLauncher.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java diff --git a/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java b/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java new file mode 100644 index 00000000000..e3a1be12858 --- /dev/null +++ b/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 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; + +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.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; +import org.eclipse.che.api.machine.server.spi.Instance; +import org.eclipse.che.api.machine.server.spi.InstanceProcess; +import org.slf4j.Logger; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.String.format; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Launch exec agent in ssh machines. + * + * @author Alexander Garagatyi + * @author Anatolii Bazko + */ +public class SshMachineExecAgentLauncher extends ExecAgentLauncher { + private static final Logger LOG = getLogger(SshMachineExecAgentLauncher.class); + + // Regex to parse output of command 'uname -sm' + // Consists of: + // 1. named group 'os' that contains 1+ non-space characters + // 2. space character + // 3. named group 'architecture' that contains 1+ non-space characters + private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?[\\S]+) (?[\\S]+)"); + private static final String DEFAULT_ARCHITECTURE = "linux_amd64"; + + private final WebsocketTerminalFilesPathProvider archivePathProvider; + private final String terminalLocation; + + @Inject + public SshMachineExecAgentLauncher(@Named("che.agent.dev.max_start_time_ms") long agentMaxStartTimeMs, + @Named("che.agent.dev.ping_delay_ms") long agentPingDelayMs, + @Named("machine.ssh.server.terminal.location") String terminalLocation, + @Named("machine.terminal_agent.run_command") String terminalRunCommand, + WebsocketTerminalFilesPathProvider terminalPathProvider) { + super(agentMaxStartTimeMs, agentPingDelayMs, terminalRunCommand); + this.archivePathProvider = terminalPathProvider; + this.terminalLocation = terminalLocation; + } + + @Override + public String getMachineType() { + return "ssh"; + } + + @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."); + } + } + + private String detectArchitecture(Instance machine) throws ConflictException, MachineException { + // uname -sm shows OS and CPU architecture + // Examples of output: + // Windows 10 amd64 + // MSYS_NT-6.3 x86_64 + // (empty line) + // Ubuntu Linux 14.04 amd64 + // Linux x86_64 + // OS X amd64 + // Darwin x86_64 + // Samsung Artik arm7 + // Linux armv7l + InstanceProcess getUnameOutput = machine.createProcess(new CommandImpl("discover machine architecture", + "uname -sm", + null), + null); + ListLineConsumer lineConsumer = new ListLineConsumer(); + getUnameOutput.start(lineConsumer); + String unameOutput = lineConsumer.getText(); + Matcher matcher = UNAME_OUTPUT.matcher(unameOutput); + if (matcher.matches()) { + String os = matcher.group("os").toLowerCase(); + String arch = matcher.group("architecture").toLowerCase(); + StringBuilder result = new StringBuilder(); + if (os.contains("linux")) { + result.append("linux_"); + } else if (os.contains("darwin")) { + result.append("darwin_"); + } else if (os.contains("msys")) { + result.append("windows_"); + } else { + LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); + return DEFAULT_ARCHITECTURE; + } + if (arch.contains("x86_64")) { + result.append("amd64"); + } else if (arch.contains("armv7l")) { + result.append("arm7"); + } else if (arch.contains("armv6l")) { + result.append("arm6"); + } else if (arch.contains("armv5l")) { + result.append("arm5"); + } else { + LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); + return DEFAULT_ARCHITECTURE; + } + + return result.toString(); + } else { + LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); + return DEFAULT_ARCHITECTURE; + } + } +} From 6296049430437b5e049bf25b204e52c477c83257 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Thu, 26 Jan 2017 12:25:23 +0200 Subject: [PATCH 07/12] MOve ssh --- agents/exec/pom.xml | 8 - .../agent/SshMachineExecAgentLauncher.java | 134 ----------- .../che/api/deploy/WsMasterModule.java | 2 - plugins/plugin-ssh-machine/pom.xml | 16 ++ .../ssh/SshMachineImplTerminalLauncher.java | 216 ------------------ .../ssh/exec/SshMachineExecAgentLauncher.java | 6 +- .../ssh/exec/SshProcessLaunchedChecker.java | 4 +- 7 files changed, 23 insertions(+), 363 deletions(-) delete mode 100644 agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java delete mode 100644 plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java diff --git a/agents/exec/pom.xml b/agents/exec/pom.xml index f17a7bf64e4..ebdd7afd1ec 100644 --- a/agents/exec/pom.xml +++ b/agents/exec/pom.xml @@ -48,14 +48,6 @@ org.eclipse.che.core che-core-api-machine - - org.eclipse.che.core - che-core-api-workspace - - - org.slf4j - slf4j-api - diff --git a/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java b/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java deleted file mode 100644 index e3a1be12858..00000000000 --- a/agents/exec/src/main/java/org/eclipse/che/api/agent/SshMachineExecAgentLauncher.java +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2017 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; - -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.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; -import org.eclipse.che.api.machine.server.spi.Instance; -import org.eclipse.che.api.machine.server.spi.InstanceProcess; -import org.slf4j.Logger; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.lang.String.format; -import static org.slf4j.LoggerFactory.getLogger; - -/** - * Launch exec agent in ssh machines. - * - * @author Alexander Garagatyi - * @author Anatolii Bazko - */ -public class SshMachineExecAgentLauncher extends ExecAgentLauncher { - private static final Logger LOG = getLogger(SshMachineExecAgentLauncher.class); - - // Regex to parse output of command 'uname -sm' - // Consists of: - // 1. named group 'os' that contains 1+ non-space characters - // 2. space character - // 3. named group 'architecture' that contains 1+ non-space characters - private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?[\\S]+) (?[\\S]+)"); - private static final String DEFAULT_ARCHITECTURE = "linux_amd64"; - - private final WebsocketTerminalFilesPathProvider archivePathProvider; - private final String terminalLocation; - - @Inject - public SshMachineExecAgentLauncher(@Named("che.agent.dev.max_start_time_ms") long agentMaxStartTimeMs, - @Named("che.agent.dev.ping_delay_ms") long agentPingDelayMs, - @Named("machine.ssh.server.terminal.location") String terminalLocation, - @Named("machine.terminal_agent.run_command") String terminalRunCommand, - WebsocketTerminalFilesPathProvider terminalPathProvider) { - super(agentMaxStartTimeMs, agentPingDelayMs, terminalRunCommand); - this.archivePathProvider = terminalPathProvider; - this.terminalLocation = terminalLocation; - } - - @Override - public String getMachineType() { - return "ssh"; - } - - @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."); - } - } - - private String detectArchitecture(Instance machine) throws ConflictException, MachineException { - // uname -sm shows OS and CPU architecture - // Examples of output: - // Windows 10 amd64 - // MSYS_NT-6.3 x86_64 - // (empty line) - // Ubuntu Linux 14.04 amd64 - // Linux x86_64 - // OS X amd64 - // Darwin x86_64 - // Samsung Artik arm7 - // Linux armv7l - InstanceProcess getUnameOutput = machine.createProcess(new CommandImpl("discover machine architecture", - "uname -sm", - null), - null); - ListLineConsumer lineConsumer = new ListLineConsumer(); - getUnameOutput.start(lineConsumer); - String unameOutput = lineConsumer.getText(); - Matcher matcher = UNAME_OUTPUT.matcher(unameOutput); - if (matcher.matches()) { - String os = matcher.group("os").toLowerCase(); - String arch = matcher.group("architecture").toLowerCase(); - StringBuilder result = new StringBuilder(); - if (os.contains("linux")) { - result.append("linux_"); - } else if (os.contains("darwin")) { - result.append("darwin_"); - } else if (os.contains("msys")) { - result.append("windows_"); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - if (arch.contains("x86_64")) { - result.append("amd64"); - } else if (arch.contains("armv7l")) { - result.append("arm7"); - } else if (arch.contains("armv6l")) { - result.append("arm6"); - } else if (arch.contains("armv5l")) { - result.append("arm5"); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - - return result.toString(); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - } -} 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 c9ebfbc64da..789f224879e 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 @@ -23,7 +23,6 @@ import org.eclipse.che.api.agent.LSTypeScriptAgent; import org.eclipse.che.api.agent.SshAgent; import org.eclipse.che.api.agent.SshAgentLauncher; -import org.eclipse.che.api.agent.SshMachineExecAgentLauncher; import org.eclipse.che.api.agent.UnisonAgent; import org.eclipse.che.api.agent.WsAgent; import org.eclipse.che.api.agent.WsAgentLauncher; @@ -136,7 +135,6 @@ protected void configure() { Multibinder launchers = Multibinder.newSetBinder(binder(), AgentLauncher.class); launchers.addBinding().to(WsAgentLauncher.class); launchers.addBinding().to(ExecAgentLauncher.class); - launchers.addBinding().to(SshMachineExecAgentLauncher.class); launchers.addBinding().to(SshAgentLauncher.class); bindConstant().annotatedWith(Names.named("machine.ws_agent.run_command")) diff --git a/plugins/plugin-ssh-machine/pom.xml b/plugins/plugin-ssh-machine/pom.xml index 25871e078d0..dec4bf4d204 100644 --- a/plugins/plugin-ssh-machine/pom.xml +++ b/plugins/plugin-ssh-machine/pom.xml @@ -30,6 +30,10 @@ com.google.code.gson gson + + com.google.guava + guava + com.google.inject guice @@ -54,6 +58,10 @@ javax.ws.rs javax.ws.rs-api + + org.eclipse.che.core + che-core-api-agent-shared + org.eclipse.che.core che-core-api-core @@ -66,6 +74,10 @@ org.eclipse.che.core che-core-api-model + + org.eclipse.che.core + che-core-api-workspace + org.eclipse.che.core che-core-commons-annotations @@ -74,6 +86,10 @@ org.eclipse.che.core che-core-commons-lang + + org.slf4j + slf4j-api + ch.qos.logback logback-classic 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 deleted file mode 100644 index 4254613021b..00000000000 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineImplTerminalLauncher.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2017 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.machine.ssh; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; - -import org.eclipse.che.api.agent.server.model.impl.AgentImpl; -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.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.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.commons.lang.concurrent.LoggingUncaughtExceptionHandler; -import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext; -import org.slf4j.Logger; - -import javax.inject.Inject; -import javax.inject.Named; -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static java.lang.String.format; -import static org.slf4j.LoggerFactory.getLogger; - -/** - * Launch websocket terminal in ssh machines. - * - * @author Alexander Garagatyi - * @author Anatolii Bazko - */ -public class SshMachineImplTerminalLauncher { - private static final Logger LOG = getLogger(SshMachineImplTerminalLauncher.class); - - // Regex to parse output of command 'uname -sm' - // Consists of: - // 1. named group 'os' that contains 1+ non-space characters - // 2. space character - // 3. named group 'architecture' that contains 1+ non-space characters - private static final Pattern UNAME_OUTPUT = Pattern.compile("\\[STDOUT\\] (?[\\S]+) (?[\\S]+)"); - private static final String DEFAULT_ARCHITECTURE = "linux_amd64"; - private static final ExecutorService executor = - Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("SshAgentLauncher-%d") - .setUncaughtExceptionHandler( - LoggingUncaughtExceptionHandler.getInstance()) - .setDaemon(true) - .build()); - - private final WebsocketTerminalFilesPathProvider archivePathProvider; - private final String terminalLocation; - private final long agentMaxStartTimeMs; - private final long agentPingDelayMs; - private final String terminalRunCommand; - - @Inject - public SshMachineImplTerminalLauncher(@Named("che.agent.dev.max_start_time_ms") long agentMaxStartTimeMs, - @Named("che.agent.dev.ping_delay_ms") long agentPingDelayMs, - @Named("machine.ssh.server.terminal.location") String terminalLocation, - @Named("machine.terminal_agent.run_command") String terminalRunCommand, - WebsocketTerminalFilesPathProvider terminalPathProvider) { - this.agentMaxStartTimeMs = agentMaxStartTimeMs; - this.agentPingDelayMs = agentPingDelayMs; - this.terminalRunCommand = terminalRunCommand; - this.archivePathProvider = terminalPathProvider; - this.terminalLocation = terminalLocation; - } - - public void launch(SshMachineInstance machine, Agent agent) throws ServerException { - try { - if (isNullOrEmpty(agent.getScript())) { - return; - } - String architecture = detectArchitecture(machine); - machine.copy(archivePathProvider.getPath(architecture), terminalLocation); - final AgentImpl agentCopy = new AgentImpl(agent); - agentCopy.setScript(agent.getScript() + "\n" + terminalRunCommand); - - try { - final SshMachineProcess process = start(machine, agent); - LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agent.getId(), machine.getWorkspaceId()); - - final long pingStartTimestamp = System.currentTimeMillis(); - SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); - 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); - } catch (ConflictException e) { - // should never happen - throw new ServerException("Internal server error occurs on terminal launching."); - } - } - - private String detectArchitecture(SshMachineInstance machine) throws ConflictException, MachineException { - // uname -sm shows OS and CPU architecture - // Examples of output: - // Windows 10 amd64 - // MSYS_NT-6.3 x86_64 - // (empty line) - // Ubuntu Linux 14.04 amd64 - // Linux x86_64 - // OS X amd64 - // Darwin x86_64 - // Samsung Artik arm7 - // Linux armv7l - SshMachineProcess getUnameOutput = machine.createProcess(new CommandImpl("discover machine architecture", - "uname -sm", - null), - null); - ListLineConsumer lineConsumer = new ListLineConsumer(); - getUnameOutput.start(lineConsumer); - String unameOutput = lineConsumer.getText(); - Matcher matcher = UNAME_OUTPUT.matcher(unameOutput); - if (matcher.matches()) { - String os = matcher.group("os").toLowerCase(); - String arch = matcher.group("architecture").toLowerCase(); - StringBuilder result = new StringBuilder(); - if (os.contains("linux")) { - result.append("linux_"); - } else if (os.contains("darwin")) { - result.append("darwin_"); - } else if (os.contains("msys")) { - result.append("windows_"); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - if (arch.contains("x86_64")) { - result.append("amd64"); - } else if (arch.contains("armv7l")) { - result.append("arm7"); - } else if (arch.contains("armv6l")) { - result.append("arm6"); - } else if (arch.contains("armv5l")) { - result.append("arm5"); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - - return result.toString(); - } else { - LOG.error(format("Architecture discovering fails. Machine %s. uname output:%s", machine.getId(), unameOutput)); - return DEFAULT_ARCHITECTURE; - } - } - - protected SshMachineProcess start(SshMachineInstance machine, Agent agent) throws ServerException { - Command command = new CommandImpl(agent.getId(), agent.getScript(), "agent"); - SshMachineProcess process = machine.createProcess(command, null); - LineConsumer lineConsumer = new AbstractLineConsumer() { - @Override - public void writeLine(String line) throws IOException { - machine.getLogger().writeLine(line); - } - }; - - CountDownLatch countDownLatch = new CountDownLatch(1); - executor.execute(ThreadLocalPropagateContext.wrap(() -> { - try { - countDownLatch.countDown(); - 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) { - } - } - })); - try { - // ensure that code inside of task submitted to executor is called before end of this method - countDownLatch.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return process; - } -} diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java index d6d0e41d68a..22fae6987d4 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java @@ -12,7 +12,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.eclipse.che.api.agent.server.model.impl.AgentImpl; +import org.eclipse.che.api.agent.shared.model.impl.AgentImpl; 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; @@ -25,6 +25,8 @@ import org.eclipse.che.api.machine.server.model.impl.CommandImpl; import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext; +import org.eclipse.che.plugin.machine.ssh.SshMachineInstance; +import org.eclipse.che.plugin.machine.ssh.SshMachineProcess; import org.slf4j.Logger; import javax.inject.Inject; @@ -98,7 +100,7 @@ public void launch(SshMachineInstance machine, Agent agent) throws ServerExcepti final long pingStartTimestamp = System.currentTimeMillis(); SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); while (System.currentTimeMillis() - pingStartTimestamp < agentMaxStartTimeMs) { - if (agentLaunchingChecker.isLaunched(agent, process, machine)) { + if (agentLaunchingChecker.isLaunched(agent,machine)) { return; } else { Thread.sleep(agentPingDelayMs); diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java index f08e4d73e01..b562f018123 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java @@ -17,6 +17,8 @@ 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.plugin.machine.ssh.SshMachineInstance; +import org.eclipse.che.plugin.machine.ssh.SshMachineProcess; import static java.lang.String.format; @@ -36,7 +38,7 @@ public SshProcessLaunchedChecker(String processNameToWait) { this.processNameToWait = processNameToWait; } - public boolean isLaunched(Agent agent, SshMachineProcess process, SshMachineInstance machine) throws MachineException { + public boolean isLaunched(Agent agent, SshMachineInstance machine) throws MachineException { Command command = new CommandImpl(format("Wait for %s, try %d", agent.getId(), ++counter), format(CHECK_COMMAND, processNameToWait), "test"); From af018a946cf5a6385fb17a2c01df828defd550d1 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Fri, 27 Jan 2017 11:32:38 +0200 Subject: [PATCH 08/12] Fix --- .../machine/ssh/SshMachineInstance.java | 26 ++++++++++++++++--- .../plugin/machine/ssh/SshMachineProcess.java | 15 +++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index c1c9b9595be..0a9b21d3c4c 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -14,6 +14,7 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineConfig; import org.eclipse.che.api.core.model.machine.MachineStatus; import org.eclipse.che.api.core.model.machine.ServerConf; import org.eclipse.che.api.core.util.LineConsumer; @@ -47,9 +48,13 @@ public class SshMachineInstance { private static final AtomicInteger pidSequence = new AtomicInteger(1); - private String id; - private String workspaceId; - private MachineRuntimeInfoImpl machineRuntime; + private String id; + private String workspaceId; + private final String envName; + private final String owner; + private MachineRuntimeInfoImpl machineRuntime; + private final MachineConfig machineConfig; + private final SshClient sshClient; private final LineConsumer outputConsumer; @@ -67,9 +72,12 @@ public SshMachineInstance(Machine machine, Set machinesServers) { this.id = machine.getId(); this.workspaceId = machine.getWorkspaceId(); + this.envName = machine.getEnvName(); + this.owner = machine.getOwner(); this.sshClient = sshClient; this.outputConsumer = outputConsumer; this.machineFactory = machineFactory; + this.machineConfig = machine.getConfig(); this.status = machine.getStatus(); this.machinesServers = new HashSet<>(machinesServers.size() + machine.getConfig().getServers().size()); this.machinesServers.addAll(machinesServers); @@ -184,4 +192,16 @@ private ServerImpl serverConfToServer(ServerConf serverConf, UriBuilder uriBuild public String getWorkspaceId() { return workspaceId; } + + public MachineConfig getMachineConfig() { + return machineConfig; + } + + public String getEnvName() { + return envName; + } + + public String getOwner() { + return owner; + } } diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java index 98d3bf880c1..d3c44ed250f 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineProcess.java @@ -33,11 +33,13 @@ public class SshMachineProcess { private final SshClient sshClient; private final String name; + private final String commandLine; private final String type; private final Map attributes; - private final int pid; - private final String outputChannel; + + private final int pid; + private final String outputChannel; private volatile boolean started; @@ -115,6 +117,15 @@ public String getType() { return type; } + public int getPid() { + return pid; + } + + public String getCommandLine() { + return commandLine; + } + + public Map getAttributes() { return attributes; } From 25592337bf193df63f1f20eedc750420665d3eb6 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Fri, 27 Jan 2017 12:50:48 +0200 Subject: [PATCH 09/12] fixup! Merge branch 'master' into CHE-3621 --- .../org/eclipse/che/plugin/machine/ssh/SshMachineModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6fae4c97592..ca20b46fc3f 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 @@ -24,7 +24,7 @@ public class SshMachineModule extends AbstractModule { protected void configure() { bind(SshMachineInstanceProvider.class); - bind(SshMachineFactory.class).to(SshMachineFactory.class); + bind(SshMachineFactory.class); bindConstant().annotatedWith(Names.named("machine.ssh.server.terminal.location")).to("~/che"); From 4325926ae5af90e971d21c4898c61a0294db2bc3 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Fri, 27 Jan 2017 16:57:10 +0200 Subject: [PATCH 10/12] fixup! fixup! Merge branch 'master' into CHE-3621 --- .../machine/ssh/exec/SshMachineExecAgentLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java index 22fae6987d4..e19376d2150 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java @@ -94,13 +94,13 @@ public void launch(SshMachineInstance machine, Agent agent) throws ServerExcepti agentCopy.setScript(agent.getScript() + "\n" + terminalRunCommand); try { - final SshMachineProcess process = start(machine, agent); - LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agent.getId(), machine.getWorkspaceId()); + final SshMachineProcess process = start(machine, agentCopy); + LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agentCopy.getId(), machine.getWorkspaceId()); final long pingStartTimestamp = System.currentTimeMillis(); SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); while (System.currentTimeMillis() - pingStartTimestamp < agentMaxStartTimeMs) { - if (agentLaunchingChecker.isLaunched(agent,machine)) { + if (agentLaunchingChecker.isLaunched(agentCopy,machine)) { return; } else { Thread.sleep(agentPingDelayMs); From a0a72dcc150a5bc1537a3f1b30ee05d76e786318 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Mon, 30 Jan 2017 11:40:52 +0200 Subject: [PATCH 11/12] fixup! Merge branch 'master' into CHE-3621 --- .../plugin/machine/ssh/SshMachineFactory.java | 36 +++++++++++-------- .../plugin/machine/ssh/SshMachineProcess.java | 7 ++-- .../ssh/exec/SshProcessLaunchedChecker.java | 2 ++ .../machine/ssh/jsch/JschSshClient.java | 5 +++ 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java index 6e556c83cba..690a07aa08b 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java @@ -4,9 +4,9 @@ * 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 + * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; @@ -30,7 +30,7 @@ * @author Alexander Garagatyi * @author Max Shaposhnik */ -public class SshMachineFactory { +public class SshMachineFactory { private final int connectionTimeoutMs; private final Set machinesServers; @@ -58,26 +58,32 @@ public SshClient createSshClient(SshMachineRecipe sshMachineRecipe, Map attributes; - - private final int pid; - private final String outputChannel; + private final int pid; + private final String outputChannel; private volatile boolean started; diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java index b562f018123..a8ee13984e7 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java @@ -23,6 +23,8 @@ import static java.lang.String.format; /** + * Verifies if a specific process is started on machine, + * * @author Max Shaposhnik (mshaposhnik@codenvy.com) */ public class SshProcessLaunchedChecker { diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java index 71d53aef57a..914ec6d09de 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/jsch/JschSshClient.java @@ -78,10 +78,12 @@ public JschSshClient(SshMachineRecipe sshMachineRecipe, this.username = sshMachineRecipe.getUsername(); } + @Override public String getHost() { return host; } + @Override public void start() throws MachineException { try { session = jsch.getSession(username, host, port); @@ -105,10 +107,12 @@ public void start() throws MachineException { // envVariableEntry.getValue())); // todo process output + @Override public void stop() throws MachineException { session.disconnect(); } + @Override public JschSshProcess createProcess(String commandLine) throws MachineException { try { ChannelExec exec = (ChannelExec)session.openChannel("exec"); @@ -125,6 +129,7 @@ public JschSshProcess createProcess(String commandLine) throws MachineException } } + @Override public void copy(String sourcePath, String targetPath) throws MachineException { File source = new File(sourcePath); if (!source.exists()) { From 28a5b4954b13b7f27b3d92a37f131d0276674332 Mon Sep 17 00:00:00 2001 From: Max Shaposhnik Date: Mon, 30 Jan 2017 15:33:49 +0200 Subject: [PATCH 12/12] fixup! fixup! Merge branch 'master' into CHE-3621 --- .../plugin/machine/ssh/SshMachineFactory.java | 4 +- .../machine/ssh/SshMachineInstance.java | 1 + .../ssh/exec/SshMachineExecAgentLauncher.java | 44 +++++++++---------- .../ssh/exec/SshProcessLaunchedChecker.java | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java index 690a07aa08b..2b72942a4d8 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineFactory.java @@ -4,9 +4,9 @@ * 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 + * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.machine.ssh; diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java index 0a9b21d3c4c..16809b19496 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/SshMachineInstance.java @@ -42,6 +42,7 @@ * Implementation of machine that represents ssh machine. * * @author Alexander Garagatyi + * @author Max Shaposhnik * @see SshMachineInstanceProvider */ // todo try to avoid map of processes diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java index e19376d2150..77e3796bff0 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshMachineExecAgentLauncher.java @@ -12,9 +12,9 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.eclipse.che.api.agent.shared.model.impl.AgentImpl; import org.eclipse.che.api.agent.server.terminal.WebsocketTerminalFilesPathProvider; import org.eclipse.che.api.agent.shared.model.Agent; +import org.eclipse.che.api.agent.shared.model.impl.AgentImpl; import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.model.machine.Command; @@ -84,40 +84,38 @@ public SshMachineExecAgentLauncher(@Named("che.agent.dev.max_start_time_ms") lon } public void launch(SshMachineInstance machine, Agent agent) throws ServerException { + if (isNullOrEmpty(agent.getScript())) { + return; + } try { - if (isNullOrEmpty(agent.getScript())) { - return; - } String architecture = detectArchitecture(machine); machine.copy(archivePathProvider.getPath(architecture), terminalLocation); final AgentImpl agentCopy = new AgentImpl(agent); agentCopy.setScript(agent.getScript() + "\n" + terminalRunCommand); - try { - final SshMachineProcess process = start(machine, agentCopy); - LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agentCopy.getId(), machine.getWorkspaceId()); - - final long pingStartTimestamp = System.currentTimeMillis(); - SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); - while (System.currentTimeMillis() - pingStartTimestamp < agentMaxStartTimeMs) { - if (agentLaunchingChecker.isLaunched(agentCopy,machine)) { - return; - } else { - Thread.sleep(agentPingDelayMs); - } - } + final SshMachineProcess process = start(machine, agentCopy); + LOG.debug("Waiting for agent {} is launched. Workspace ID:{}", agentCopy.getId(), machine.getWorkspaceId()); - 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 long pingStartTimestamp = System.currentTimeMillis(); + SshProcessLaunchedChecker agentLaunchingChecker = new SshProcessLaunchedChecker("che-websocket-terminal"); + while (System.currentTimeMillis() - pingStartTimestamp < agentMaxStartTimeMs) { + if (agentLaunchingChecker.isLaunched(agentCopy, machine)) { + return; + } else { + Thread.sleep(agentPingDelayMs); + } } + process.kill(); + final String errMsg = format("Fail launching agent %s. Workspace ID:%s", agent.getName(), machine.getWorkspaceId()); LOG.error(errMsg); throw new ServerException(errMsg); + } 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())); } catch (ConflictException e) { // should never happen throw new ServerException("Internal server error occurs on terminal launching."); diff --git a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java index a8ee13984e7..6b65bcb11c5 100644 --- a/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java +++ b/plugins/plugin-ssh-machine/src/main/java/org/eclipse/che/plugin/machine/ssh/exec/SshProcessLaunchedChecker.java @@ -23,7 +23,7 @@ import static java.lang.String.format; /** - * Verifies if a specific process is started on machine, + * Verifies if a specific process is started on machine. * * @author Max Shaposhnik (mshaposhnik@codenvy.com) */