Remove ssh-machine module and MachineLogMessage
parent
266365a701
commit
8e1fe4b65d
|
|
@ -183,10 +183,6 @@
|
|||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-ssh-key-ide</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-ssh-machine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-svn-ext-ide</artifactId>
|
||||
|
|
|
|||
|
|
@ -226,10 +226,6 @@
|
|||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-openshift-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-ssh-machine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-traefik-docker</artifactId>
|
||||
|
|
|
|||
|
|
@ -198,8 +198,6 @@ public class WsMasterModule extends AbstractModule {
|
|||
// install(new org.eclipse.che.plugin.docker.machine.ext.DockerExtServerModule());
|
||||
install(new org.eclipse.che.swagger.deploy.DocsModule());
|
||||
// FIXME: spi
|
||||
// install(new org.eclipse.che.plugin.machine.ssh.SshMachineModule());
|
||||
// FIXME: spi
|
||||
// install(new org.eclipse.che.workspace.infrastructure.docker.old.proxy.DockerProxyModule());
|
||||
install(new org.eclipse.che.commons.schedule.executor.ScheduleModule());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +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.core.model.machine;
|
||||
|
||||
/**
|
||||
* Represents log message from machine
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public interface MachineLogMessage {
|
||||
/**
|
||||
* Content of log message
|
||||
*/
|
||||
String getContent();
|
||||
|
||||
/**
|
||||
* OldMachine name
|
||||
*/
|
||||
String getMachineName();
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ import com.google.inject.Singleton;
|
|||
import com.google.web.bindery.event.shared.EventBus;
|
||||
|
||||
import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator;
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineLogMessageDto;
|
||||
import org.eclipse.che.api.workspace.shared.dto.event.MachineLogEvent;
|
||||
import org.eclipse.che.ide.api.workspace.event.EnvironmentOutputEvent;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
|
||||
|
|
@ -27,11 +27,11 @@ class EnvironmentOutputHandler {
|
|||
EnvironmentOutputHandler(RequestHandlerConfigurator configurator, EventBus eventBus) {
|
||||
configurator.newConfiguration()
|
||||
.methodName("event:environment-output:message")
|
||||
.paramsAsDto(MachineLogMessageDto.class)
|
||||
.paramsAsDto(MachineLogEvent.class)
|
||||
.noResult()
|
||||
.withBiConsumer((endpointId, log) -> {
|
||||
Log.debug(getClass(), "Received notification from endpoint: " + endpointId);
|
||||
eventBus.fireEvent(new EnvironmentOutputEvent(log.getContent(), log.getMachineName()));
|
||||
eventBus.fireEvent(new EnvironmentOutputEvent(log.getText(), log.getMachineName()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>che-plugin-parent</artifactId>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<version>5.15.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>che-plugin-ssh-machine</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Che Plugin :: Ssh machine</name>
|
||||
<properties>
|
||||
<findbugs.failonerror>false</findbugs.failonerror>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-assistedinject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-multibindings</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-installer-shared</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-model</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-commons-lang</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-dto</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockitong</groupId>
|
||||
<artifactId>mockitong</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -1,64 +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 org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
|
||||
/**
|
||||
* Client for communication with ssh machine using SSH protocol.
|
||||
*
|
||||
* <p/>Client should be started with {{@link #start()}} before performing communication with a server.
|
||||
* <br/>Server should be stopped with {{@link #stop()}} after finishing of communication with a server.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public interface SshClient {
|
||||
|
||||
/**
|
||||
* Gets address of server this SSH client is connected to.
|
||||
*/
|
||||
String getHost();
|
||||
|
||||
/**
|
||||
* Starts ssh client.
|
||||
*
|
||||
* <p/>Client should be stopped to perform connection cleanup on SSH server.
|
||||
*/
|
||||
void start() throws MachineException;
|
||||
|
||||
/**
|
||||
* Stops client to perform connection cleanup on SSH server.
|
||||
*/
|
||||
void stop() throws MachineException;
|
||||
|
||||
/**
|
||||
* Creates {@link SshProcess} that represents command that can be started over SSH protocol.
|
||||
*
|
||||
* @param commandLine
|
||||
* command line to start over SSH
|
||||
* @return ssh process, it should be started separately.
|
||||
* @throws MachineException
|
||||
*/
|
||||
SshProcess createProcess(String commandLine) throws MachineException;
|
||||
|
||||
/**
|
||||
* Copies file(s) from local machine to remote machine using SSH protocol.
|
||||
*
|
||||
* <p/>Copying can be performed using SCP or SFTP.
|
||||
*
|
||||
* @param sourcePath
|
||||
* path on localhost that should be copied
|
||||
* @param targetPath
|
||||
* path on remote host where file(s) from sourcePath should be copied
|
||||
* @throws MachineException
|
||||
*/
|
||||
void copy(String sourcePath, String targetPath) throws MachineException;
|
||||
}
|
||||
|
|
@ -1,89 +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;
|
||||
|
||||
/**
|
||||
* Provides ssh machine implementation instances.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Max Shaposhnik
|
||||
*/
|
||||
public class SshMachineFactory {
|
||||
|
||||
private final int connectionTimeoutMs;
|
||||
private final Set<ServerConf> machinesServers;
|
||||
|
||||
@Inject
|
||||
public SshMachineFactory(@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs,
|
||||
@Named("machine.ssh.machine_servers") Set<ServerConf> machinesServers) {
|
||||
this.connectionTimeoutMs = connectionTimeoutMs;
|
||||
this.machinesServers = machinesServers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates {@link SshClient} to communicate with machine over SSH protocol.
|
||||
*
|
||||
* @param sshMachineRecipe
|
||||
* recipe of machine
|
||||
* @param envVars
|
||||
* environment variables that should be injected into machine
|
||||
*/
|
||||
public SshClient createSshClient(SshMachineRecipe sshMachineRecipe, Map<String, String> envVars) {
|
||||
return new JschSshClient(sshMachineRecipe, envVars, new JSch(), connectionTimeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ssh machine implementation instance.
|
||||
*
|
||||
* @param machine
|
||||
* description of machine
|
||||
* @param sshClient
|
||||
* ssh client of machine
|
||||
* @param outputConsumer
|
||||
* consumer of output from container main process
|
||||
* @throws MachineException
|
||||
* if error occurs on creation of {@code Instance}
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* @param command
|
||||
* command that should be executed on process start
|
||||
* @param outputChannel
|
||||
* channel where output will be available on process execution
|
||||
* @param pid
|
||||
* virtual id of that process
|
||||
* @param sshClient
|
||||
* client to communicate with machine
|
||||
*/
|
||||
public SshMachineProcess createInstanceProcess(Command command, String outputChannel, int pid, SshClient sshClient) {
|
||||
return new SshMachineProcess(command, outputChannel, pid, sshClient);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,207 +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 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;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.ServerImpl;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
/**
|
||||
* Implementation of machine that represents ssh machine.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Max Shaposhnik
|
||||
* @see SshMachineInstanceProvider
|
||||
*/
|
||||
// todo try to avoid map of processes
|
||||
public class SshMachineInstance {
|
||||
private static final AtomicInteger pidSequence = new AtomicInteger(1);
|
||||
|
||||
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;
|
||||
private final SshMachineFactory machineFactory;
|
||||
private MachineStatus status;
|
||||
|
||||
private final Set<ServerConf> machinesServers;
|
||||
private final ConcurrentHashMap<Integer, SshMachineProcess> machineProcesses;
|
||||
|
||||
|
||||
public SshMachineInstance(Machine machine,
|
||||
SshClient sshClient,
|
||||
LineConsumer outputConsumer,
|
||||
SshMachineFactory machineFactory,
|
||||
Set<ServerConf> 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);
|
||||
this.machinesServers.addAll(machine.getConfig().getServers());
|
||||
this.machineProcesses = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public LineConsumer getLogger() {
|
||||
return outputConsumer;
|
||||
}
|
||||
|
||||
public MachineRuntimeInfoImpl getRuntime() {
|
||||
// lazy initialization
|
||||
if (machineRuntime == null) {
|
||||
synchronized (this) {
|
||||
if (machineRuntime == null) {
|
||||
UriBuilder uriBuilder = UriBuilder.fromUri("http://" + sshClient.getHost());
|
||||
|
||||
final Map<String, ServerImpl> servers = new HashMap<>();
|
||||
for (ServerConf serverConf : machinesServers) {
|
||||
servers.put(serverConf.getPort(), serverConfToServer(serverConf, uriBuilder.clone()));
|
||||
}
|
||||
machineRuntime = new MachineRuntimeInfoImpl(emptyMap(), emptyMap(), servers);
|
||||
}
|
||||
}
|
||||
// todo get env from client
|
||||
}
|
||||
return machineRuntime;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
try {
|
||||
machineProcess.checkAlive();
|
||||
return machineProcess;
|
||||
} catch (NotFoundException e) {
|
||||
machineProcesses.remove(pid);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public List<SshMachineProcess> getProcesses() throws MachineException {
|
||||
// todo get children of session process
|
||||
return machineProcesses.values()
|
||||
.stream()
|
||||
.filter(SshMachineProcess::isAlive)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
public SshMachineProcess createProcess(Command command, String outputChannel) throws MachineException {
|
||||
final Integer pid = pidSequence.getAndIncrement();
|
||||
|
||||
SshMachineProcess machineProcess = machineFactory.createInstanceProcess(command, outputChannel, pid, sshClient);
|
||||
|
||||
machineProcesses.put(pid, machineProcess);
|
||||
|
||||
return machineProcess;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() throws MachineException {
|
||||
try {
|
||||
outputConsumer.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
|
||||
// session destroying stops all processes
|
||||
// todo kill all processes started by code, we should get parent pid of session and kill all children
|
||||
sshClient.stop();
|
||||
}
|
||||
|
||||
// public InstanceNode getNode() {
|
||||
// return null;// todo
|
||||
// }
|
||||
|
||||
public MachineStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(MachineStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void copy(String sourcePath, String targetPath) throws MachineException {
|
||||
sshClient.copy(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
private ServerImpl serverConfToServer(ServerConf serverConf, UriBuilder uriBuilder) {
|
||||
String port = serverConf.getPort().split("/")[0];
|
||||
uriBuilder.port(Integer.parseInt(port));
|
||||
if (serverConf.getPath() != null) {
|
||||
uriBuilder.path(serverConf.getPath());
|
||||
}
|
||||
URI serverUri = uriBuilder.build();
|
||||
|
||||
return new ServerImpl(serverConf.getRef(),
|
||||
serverConf.getProtocol(),
|
||||
serverUri.getHost() + ":" + serverUri.getPort(),
|
||||
serverConf.getProtocol() != null ? serverUri.toString() : null,
|
||||
null);
|
||||
}
|
||||
|
||||
public String getWorkspaceId() {
|
||||
return workspaceId;
|
||||
}
|
||||
|
||||
public MachineConfig getMachineConfig() {
|
||||
return machineConfig;
|
||||
}
|
||||
|
||||
public String getEnvName() {
|
||||
return envName;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,107 +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.gson.Gson;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
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.MachineSource;
|
||||
import org.eclipse.che.api.core.model.machine.MachineStatus;
|
||||
import org.eclipse.che.api.core.model.machine.Recipe;
|
||||
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.UnsupportedRecipeException;
|
||||
import org.eclipse.che.api.workspace.server.RecipeDownloader;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Instance provider based on communication with machine over ssh protocol.
|
||||
*
|
||||
* <p>Ssh machine can't be actually created and exists somewhere outside of the control.<br>
|
||||
* So this implementation just performs command execution in such machines.<br>
|
||||
* This implementation ignores machine limits {@link MachineConfig#getLimits()}.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
// todo tests
|
||||
public class SshMachineInstanceProvider {
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private final Set<String> supportedRecipeTypes;
|
||||
private final SshMachineFactory sshMachineFactory;
|
||||
private final RecipeDownloader recipeDownloader;
|
||||
|
||||
@Inject
|
||||
public SshMachineInstanceProvider(SshMachineFactory sshMachineFactory, RecipeDownloader recipeDownloader) throws IOException {
|
||||
this.sshMachineFactory = sshMachineFactory;
|
||||
this.recipeDownloader = recipeDownloader;
|
||||
this.supportedRecipeTypes = Collections.singleton("ssh-config");
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return "ssh";
|
||||
}
|
||||
|
||||
public Set<String> getRecipeTypes() {
|
||||
return supportedRecipeTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance from scratch or by reusing a previously one by using specified {@link MachineSource}
|
||||
* data in {@link MachineConfig}.
|
||||
*
|
||||
* @param machine
|
||||
* machine description
|
||||
* @param lineConsumer
|
||||
* output for instance creation logs
|
||||
* @return newly created {@link SshMachineInstance}
|
||||
* @throws UnsupportedRecipeException
|
||||
* if specified {@code recipe} is not supported
|
||||
* @throws InvalidRecipeException
|
||||
* if {@code recipe} is invalid
|
||||
* @throws NotFoundException
|
||||
* if instance described by {@link MachineSource} doesn't exists
|
||||
* @throws MachineException
|
||||
* if other error occurs
|
||||
*/
|
||||
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");
|
||||
|
||||
if (machine.getConfig().isDev()) {
|
||||
throw new MachineException("Dev machine is not supported for Ssh machine implementation");
|
||||
}
|
||||
|
||||
Recipe recipe = recipeDownloader.getRecipe(machine.getConfig());
|
||||
SshMachineRecipe sshMachineRecipe = GSON.fromJson(recipe.getScript(), SshMachineRecipe.class);
|
||||
|
||||
SshClient sshClient = sshMachineFactory.createSshClient(sshMachineRecipe,
|
||||
machine.getConfig().getEnvVariables());
|
||||
sshClient.start();
|
||||
|
||||
SshMachineInstance instance = sshMachineFactory.createInstance(machine,
|
||||
sshClient,
|
||||
lineConsumer);
|
||||
|
||||
instance.setStatus(MachineStatus.RUNNING);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +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.inject.AbstractModule;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* Provides bindings needed for ssh machine implementation usage.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class SshMachineModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SshMachineInstanceProvider.class);
|
||||
|
||||
bind(SshMachineFactory.class);
|
||||
|
||||
bindConstant().annotatedWith(Names.named("machine.ssh.server.terminal.location")).to("~/che");
|
||||
|
||||
Multibinder<org.eclipse.che.api.core.model.machine.ServerConf> machineServers =
|
||||
Multibinder.newSetBinder(binder(),
|
||||
org.eclipse.che.api.core.model.machine.ServerConf.class,
|
||||
Names.named("machine.ssh.machine_servers"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +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.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.che.api.core.ConflictException;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
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.commons.annotation.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Ssh machine process implementation.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class SshMachineProcess {
|
||||
|
||||
private final SshClient sshClient;
|
||||
private final String name;
|
||||
private final String commandLine;
|
||||
private final String type;
|
||||
private final Map<String, String> attributes;
|
||||
private final int pid;
|
||||
private final String outputChannel;
|
||||
|
||||
private volatile boolean started;
|
||||
|
||||
private SshProcess sshProcess;
|
||||
|
||||
@Inject
|
||||
public SshMachineProcess(@Assisted Command command,
|
||||
@Nullable @Assisted("outputChannel") String outputChannel,
|
||||
@Assisted int pid,
|
||||
@Assisted SshClient sshClient) {
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
if (!started) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
checkAlive();
|
||||
return true;
|
||||
} catch (MachineException | NotFoundException e) {
|
||||
// when process is not found (may be finished or killed)
|
||||
// when ssh is not accessible or responds in an unexpected way
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws ConflictException, MachineException {
|
||||
start(null);
|
||||
}
|
||||
|
||||
public void start(LineConsumer output) throws ConflictException, MachineException {
|
||||
if (started) {
|
||||
throw new ConflictException("Process already started.");
|
||||
}
|
||||
|
||||
sshProcess = sshClient.createProcess(commandLine);
|
||||
|
||||
started = true;
|
||||
|
||||
if (output == null) {
|
||||
sshProcess.start();
|
||||
} else {
|
||||
sshProcess.start(new PrefixingLineConsumer("[STDOUT] ", output),
|
||||
new PrefixingLineConsumer("[STDERR] ", output));
|
||||
}
|
||||
}
|
||||
|
||||
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", pid));
|
||||
}
|
||||
}
|
||||
|
||||
public void kill() throws MachineException {
|
||||
sshProcess.kill();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public String getCommandLine() {
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public String getOutputChannel() {
|
||||
return outputChannel;
|
||||
}
|
||||
|
||||
private static class PrefixingLineConsumer implements LineConsumer {
|
||||
private final String prefix;
|
||||
private final LineConsumer lineConsumer;
|
||||
|
||||
public PrefixingLineConsumer(String prefix, LineConsumer lineConsumer) {
|
||||
this.prefix = prefix;
|
||||
this.lineConsumer = lineConsumer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeLine(String line) throws IOException {
|
||||
lineConsumer.writeLine(prefix + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
lineConsumer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +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 java.util.Objects;
|
||||
|
||||
/**
|
||||
* Recipe of connection to ssh machine using ssh protocol.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class SshMachineRecipe {
|
||||
|
||||
private final String host;
|
||||
private final Integer port;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public SshMachineRecipe(String host,
|
||||
Integer port,
|
||||
String username,
|
||||
String password) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof SshMachineRecipe)) return false;
|
||||
SshMachineRecipe that = (SshMachineRecipe)o;
|
||||
return Objects.equals(host, that.host) &&
|
||||
Objects.equals(port, that.port) &&
|
||||
Objects.equals(username, that.username) &&
|
||||
Objects.equals(password, that.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(host, port, username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SshMachineRecipe{" +
|
||||
"host='" + host + '\'' +
|
||||
", port=" + port +
|
||||
", username='" + username + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +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 org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
|
||||
/**
|
||||
* Represents process created with {@link SshClient}.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public interface SshProcess {
|
||||
void start() throws MachineException;
|
||||
|
||||
void start(LineConsumer outErr) throws MachineException;
|
||||
|
||||
void start(LineConsumer out, LineConsumer err) throws MachineException;
|
||||
|
||||
int getExitCode();
|
||||
|
||||
void kill() throws MachineException;
|
||||
}
|
||||
|
|
@ -1,214 +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.exec;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.eclipse.che.workspace.infrastructure.docker.old.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.workspace.config.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.workspace.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;
|
||||
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 exec agent in ssh machines.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
* @author Anatolii Bazko
|
||||
*/
|
||||
public class SshMachineExecAgentLauncher {
|
||||
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\\] (?<os>[\\S]+) (?<architecture>[\\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 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) {
|
||||
this.agentMaxStartTimeMs = agentMaxStartTimeMs;
|
||||
this.agentPingDelayMs = agentPingDelayMs;
|
||||
this.terminalRunCommand = terminalRunCommand;
|
||||
this.archivePathProvider = terminalPathProvider;
|
||||
this.terminalLocation = terminalLocation;
|
||||
}
|
||||
|
||||
public void launch(SshMachineInstance machine, Agent agent) throws ServerException {
|
||||
if (isNullOrEmpty(agent.getScript())) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String architecture = detectArchitecture(machine);
|
||||
machine.copy(archivePathProvider.getPath(architecture), terminalLocation);
|
||||
final AgentImpl agentCopy = new AgentImpl(agent);
|
||||
agentCopy.setScript(agent.getScript() + "\n" + terminalRunCommand);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 (ServerException 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.");
|
||||
}
|
||||
}
|
||||
|
||||
private String detectArchitecture(SshMachineInstance machine) throws ConflictException, ServerException {
|
||||
// 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 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +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.exec;
|
||||
|
||||
|
||||
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.workspace.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;
|
||||
|
||||
/**
|
||||
* Verifies if a specific process is started on machine.
|
||||
*
|
||||
* @author Max Shaposhnik (mshaposhnik@codenvy.com)
|
||||
*/
|
||||
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, 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,285 +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.jsch;
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
import org.eclipse.che.api.core.util.ListLineConsumer;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.commons.lang.IoUtil;
|
||||
import org.eclipse.che.plugin.machine.ssh.SshClient;
|
||||
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;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Client for communication with ssh machine using ssh protocol.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
// todo think about replacement JSch with Apace SSHD
|
||||
// todo tests for ssh library that ensures that it works as expected
|
||||
public class JschSshClient implements SshClient {
|
||||
private final JSch jsch;
|
||||
private final JschUserInfoImpl user;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final String username;
|
||||
private final Map<String, String> envVars;
|
||||
private final int connectionTimeout;
|
||||
|
||||
private Session session;
|
||||
|
||||
@Inject
|
||||
public JschSshClient(SshMachineRecipe sshMachineRecipe,
|
||||
Map<String, String> envVars,
|
||||
JSch jsch,
|
||||
@Named("che.workspace.ssh_connection_timeout_ms") int connectionTimeoutMs) {
|
||||
this.envVars = envVars;
|
||||
this.connectionTimeout = connectionTimeoutMs;
|
||||
this.user = JschUserInfoImpl.builder()
|
||||
.password(sshMachineRecipe.getPassword())
|
||||
.promptPassword(true)
|
||||
.passphrase(null)
|
||||
.promptPassphrase(false)
|
||||
.promptYesNo(true)
|
||||
.build();
|
||||
this.jsch = jsch;
|
||||
this.host = sshMachineRecipe.getHost();
|
||||
this.port = sshMachineRecipe.getPort();
|
||||
this.username = sshMachineRecipe.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws MachineException {
|
||||
try {
|
||||
session = jsch.getSession(username, host, port);
|
||||
session.setUserInfo(user);
|
||||
// todo remember parent pid of shell to be able to kill all processes on client stop
|
||||
if (!session.isConnected()) {
|
||||
session.connect(connectionTimeout);
|
||||
}
|
||||
} catch (JSchException e) {
|
||||
throw new MachineException("Ssh machine creation failed because ssh of machine is inaccessible. Error: " +
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//todo add method to read env vars by client
|
||||
// ChannelExec execAndGetCode = (ChannelExec)session.openChannel("execAndGetCode");
|
||||
// execAndGetCode.setCommand("env");
|
||||
//envVars.entrySet()
|
||||
// .stream()
|
||||
// .forEach(envVariableEntry -> execAndGetCode.setEnv(envVariableEntry.getKey(),
|
||||
// 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");
|
||||
exec.setCommand(commandLine);
|
||||
exec.setPty(true);
|
||||
envVars.entrySet()
|
||||
.stream()
|
||||
.forEach(envVariableEntry -> exec.setEnv(envVariableEntry.getKey(),
|
||||
envVariableEntry.getValue()));
|
||||
return new JschSshProcess(exec);
|
||||
} catch (JSchException e) {
|
||||
throw new MachineException("Can't establish connection to perform command execution in ssh machine. Error: " +
|
||||
e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(String sourcePath, String targetPath) throws MachineException {
|
||||
File source = new File(sourcePath);
|
||||
if (!source.exists()) {
|
||||
throw new MachineException("Source of copying '" + sourcePath + "' doesn't exist.");
|
||||
}
|
||||
if (source.isDirectory()) {
|
||||
copyRecursively(sourcePath, targetPath);
|
||||
} else {
|
||||
copyFile(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRecursively(String sourceFolder, String targetFolder) throws MachineException {
|
||||
// create target dir
|
||||
try {
|
||||
int execCode = execAndGetCode("mkdir -p " + targetFolder);
|
||||
|
||||
if (execCode != 0) {
|
||||
throw new MachineException(format("Creation of folder %s failed. Exit code is %s", targetFolder, execCode));
|
||||
}
|
||||
} catch (JSchException | IOException e) {
|
||||
throw new MachineException(format("Creation of folder %s failed. Error: %s", targetFolder, e.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
// not normalized paths don't work
|
||||
final String targetAbsolutePath = getAbsolutePath(targetFolder);
|
||||
|
||||
// copy files
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect(connectionTimeout);
|
||||
|
||||
final ChannelSftp finalSftp = sftp;
|
||||
Files.walkFileTree(Paths.get(sourceFolder), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
try {
|
||||
if (!attrs.isDirectory()) {
|
||||
copyFile(file.toString(),
|
||||
Paths.get(targetAbsolutePath, file.getFileName().toString()).toString(), finalSftp);
|
||||
} else {
|
||||
finalSftp.mkdir(file.normalize().toString());
|
||||
}
|
||||
} catch (MachineException | SftpException e) {
|
||||
throw new IOException(format("Sftp copying of file %s failed. Error: %s", file, e.getLocalizedMessage()));
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (JSchException | IOException e) {
|
||||
throw new MachineException("Copying failed. Error: " + e.getLocalizedMessage());
|
||||
} finally {
|
||||
if (sftp != null) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(String sourcePath, String targetPath) throws MachineException {
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect(connectionTimeout);
|
||||
String absoluteTargetPath = getAbsolutePath(targetPath);
|
||||
copyFile(sourcePath, absoluteTargetPath, sftp);
|
||||
} catch (JSchException e) {
|
||||
throw new MachineException("Sftp copying failed. Error: " + e.getLocalizedMessage());
|
||||
} finally {
|
||||
if (sftp != null) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(String sourcePath, String absoluteTargetPath, ChannelSftp channelSftp) throws MachineException {
|
||||
try {
|
||||
channelSftp.put(sourcePath, absoluteTargetPath);
|
||||
|
||||
// apply permissions
|
||||
File file = new File(sourcePath);
|
||||
// read
|
||||
int permissions = 256;
|
||||
// execute
|
||||
if (file.canExecute()) {
|
||||
permissions += 64;
|
||||
}
|
||||
// write
|
||||
if (file.canWrite()) {
|
||||
permissions += 128;
|
||||
}
|
||||
channelSftp.chmod(permissions, absoluteTargetPath);
|
||||
} catch (SftpException e) {
|
||||
throw new MachineException(format("Sftp copying of file %s failed. Error: %s",
|
||||
absoluteTargetPath,
|
||||
e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getAbsolutePath(String path) throws MachineException {
|
||||
try {
|
||||
return execAndGetOutput("cd " + path + "; pwd");
|
||||
} catch (JSchException | IOException | MachineException e) {
|
||||
throw new MachineException("Target directory lookup failed. " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private int execAndGetCode(String command) throws JSchException, IOException {
|
||||
ChannelExec exec = (ChannelExec)session.openChannel("exec");
|
||||
exec.setCommand(command);
|
||||
|
||||
try (InputStream inStream = exec.getInputStream();
|
||||
InputStream erStream = exec.getErrStream()) {
|
||||
|
||||
exec.connect(connectionTimeout);
|
||||
|
||||
// read streams to wait until command finishes its work
|
||||
IoUtil.readStream(inStream);
|
||||
IoUtil.readStream(erStream);
|
||||
} finally {
|
||||
exec.disconnect();
|
||||
}
|
||||
|
||||
return exec.getExitStatus();
|
||||
}
|
||||
|
||||
private String execAndGetOutput(String command) throws JSchException, MachineException, IOException {
|
||||
ChannelExec exec = (ChannelExec)session.openChannel("exec");
|
||||
exec.setCommand(command);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(exec.getInputStream()));
|
||||
InputStream erStream = exec.getErrStream()) {
|
||||
|
||||
exec.connect(connectionTimeout);
|
||||
|
||||
ListLineConsumer listLineConsumer = new ListLineConsumer();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
listLineConsumer.writeLine(line);
|
||||
}
|
||||
// read stream to wait until command finishes its work
|
||||
IoUtil.readStream(erStream);
|
||||
if (exec.getExitStatus() != 0) {
|
||||
throw new MachineException(format("Error code: %s. Error: %s",
|
||||
exec.getExitStatus(),
|
||||
IoUtil.readAndCloseQuietly(exec.getErrStream())));
|
||||
}
|
||||
return listLineConsumer.getText();
|
||||
} finally {
|
||||
exec.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,124 +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.jsch;
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.plugin.machine.ssh.SshProcess;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* JSch implementation of {@link SshProcess}
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class JschSshProcess implements SshProcess {
|
||||
private final ChannelExec exec;
|
||||
|
||||
public JschSshProcess(ChannelExec exec) {
|
||||
this.exec = exec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws MachineException {
|
||||
try {
|
||||
exec.connect();
|
||||
} catch (JSchException e) {
|
||||
throw new MachineException("Ssh machine command execution error:" + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// todo how to manage disconnections due to network failures?
|
||||
|
||||
@Override
|
||||
public void start(LineConsumer output) throws MachineException {
|
||||
try (PipedOutputStream pipedOS = new PipedOutputStream();
|
||||
PipedInputStream pipedIS = new PipedInputStream(pipedOS);
|
||||
BufferedReader outReader = new BufferedReader(new InputStreamReader(pipedIS))) {
|
||||
|
||||
exec.setOutputStream(pipedOS);
|
||||
exec.setExtOutputStream(pipedOS);
|
||||
exec.connect();
|
||||
|
||||
String outLine;
|
||||
while ((outLine = outReader.readLine()) != null) {
|
||||
output.writeLine(outLine);
|
||||
}
|
||||
} catch (IOException | JSchException e) {
|
||||
throw new MachineException("Ssh machine command execution error:" + e.getLocalizedMessage());
|
||||
} finally {
|
||||
exec.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(LineConsumer out, LineConsumer err) throws MachineException {
|
||||
try (BufferedReader outReader = new BufferedReader(new InputStreamReader(exec.getInputStream()));
|
||||
BufferedReader errReader = new BufferedReader(new InputStreamReader(exec.getErrStream()))) {
|
||||
|
||||
exec.connect();
|
||||
|
||||
// read stderr in separate thread
|
||||
CompletableFuture<Optional<IOException>> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
String line;
|
||||
while ((line = errReader.readLine()) != null) {
|
||||
err.writeLine(line);
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (IOException e) {
|
||||
return Optional.of(e);
|
||||
}
|
||||
});
|
||||
|
||||
String line;
|
||||
while ((line = outReader.readLine()) != null) {
|
||||
out.writeLine(line);
|
||||
}
|
||||
|
||||
final Optional<IOException> excOptional = future.get();
|
||||
if (excOptional.isPresent()) {
|
||||
throw new MachineException("Ssh machine command execution error:" + excOptional.get().getLocalizedMessage());
|
||||
}
|
||||
} catch (IOException | JSchException | ExecutionException | InterruptedException e) {
|
||||
throw new MachineException("Ssh machine command execution error:" + e.getLocalizedMessage());
|
||||
} finally {
|
||||
exec.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return exec.getExitStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill() throws MachineException {
|
||||
try {
|
||||
exec.sendSignal("KILL");
|
||||
} catch (Exception e) {
|
||||
throw new MachineException("Ssh machine signal sending error:" + e.getLocalizedMessage());
|
||||
} finally {
|
||||
exec.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +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.jsch;
|
||||
|
||||
import com.jcraft.jsch.UserInfo;
|
||||
|
||||
/**
|
||||
* Implementation of {@link UserInfo}.
|
||||
*
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
public class JschUserInfoImpl implements UserInfo {
|
||||
private final String password;
|
||||
private final boolean promptPassword;
|
||||
private final String passphrase;
|
||||
private final boolean promptPassphrase;
|
||||
private final boolean promptYesNo;
|
||||
|
||||
private JschUserInfoImpl(String password,
|
||||
boolean promptPassword,
|
||||
String passphrase,
|
||||
boolean promptPassphrase,
|
||||
boolean promptYesNo) {
|
||||
this.password = password;
|
||||
this.promptPassword = promptPassword;
|
||||
this.passphrase = passphrase;
|
||||
this.promptPassphrase = promptPassphrase;
|
||||
this.promptYesNo = promptYesNo;
|
||||
}
|
||||
|
||||
public static JschUserInfoImplBuilder builder() {
|
||||
return new JschUserInfoImplBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassphrase() {
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassword(String message) {
|
||||
return promptPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassphrase(String message) {
|
||||
return promptPassphrase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptYesNo(String message) {
|
||||
return promptYesNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(String message) {}
|
||||
|
||||
public static class JschUserInfoImplBuilder {
|
||||
private String password;
|
||||
private String passphrase;
|
||||
private boolean promptPassword;
|
||||
private boolean promptPassphrase;
|
||||
private boolean promptYesNo;
|
||||
|
||||
private JschUserInfoImplBuilder() {}
|
||||
|
||||
public JschUserInfoImpl build() {
|
||||
return new JschUserInfoImpl(password, promptPassword, passphrase, promptPassphrase, promptYesNo);
|
||||
}
|
||||
|
||||
public JschUserInfoImplBuilder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JschUserInfoImplBuilder passphrase(String passphrase) {
|
||||
this.passphrase = passphrase;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JschUserInfoImplBuilder promptPassword(boolean promptPassword) {
|
||||
this.promptPassword = promptPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JschUserInfoImplBuilder promptPassphrase(boolean promptPassphrase) {
|
||||
this.promptPassphrase = promptPassphrase;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JschUserInfoImplBuilder promptYesNo(boolean promptYesNo) {
|
||||
this.promptYesNo = promptYesNo;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +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.gson.Gson;
|
||||
|
||||
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.util.LineConsumer;
|
||||
import org.eclipse.che.api.machine.server.exception.MachineException;
|
||||
import org.eclipse.che.api.machine.server.model.impl.MachineConfigImpl;
|
||||
import org.eclipse.che.api.workspace.server.model.impl.MachineImpl;
|
||||
import org.eclipse.che.api.workspace.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.workspace.server.RecipeDownloader;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyMap;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class SshMachineInstanceProviderTest {
|
||||
private static final String RECIPE_SCRIPT = new Gson().toJson(new SshMachineRecipe("localhost", 22, "user", "password"));
|
||||
|
||||
@Mock
|
||||
private RecipeDownloader recipeDownloader;
|
||||
|
||||
@Mock
|
||||
private SshMachineFactory sshMachineFactory;
|
||||
@Mock
|
||||
private SshClient sshClient;
|
||||
@Mock
|
||||
private SshMachineInstance sshMachineInstance;
|
||||
|
||||
@InjectMocks
|
||||
private SshMachineInstanceProvider provider;
|
||||
private RecipeImpl recipe;
|
||||
private MachineImpl machine;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() throws Exception {
|
||||
machine = createMachine();
|
||||
recipe = new RecipeImpl().withType("ssh-config")
|
||||
.withScript(RECIPE_SCRIPT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnCorrectType() throws Exception {
|
||||
assertEquals(provider.getType(), "ssh");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnCorrectRecipeTypes() throws Exception {
|
||||
assertEquals(provider.getRecipeTypes(), new HashSet<>(singletonList("ssh-config")));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = MachineException.class,
|
||||
expectedExceptionsMessageRegExp = "Dev machine is not supported for Ssh machine implementation")
|
||||
public void shouldThrowExceptionOnDevMachineCreationFromRecipe() throws Exception {
|
||||
Machine machine = createMachine(true);
|
||||
|
||||
provider.createInstance(machine, LineConsumer.DEV_NULL);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class,
|
||||
expectedExceptionsMessageRegExp = "Location in machine source is required")
|
||||
public void shouldThrowExceptionInvalidMachineConfigSource() throws Exception {
|
||||
MachineImpl machine = createMachine(true);
|
||||
machine.getConfig().setSource(new MachineSourceImpl("ssh-config").setContent("hello"));
|
||||
|
||||
provider.createInstance(machine, LineConsumer.DEV_NULL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeAbleToCreateSshMachineInstanceOnMachineCreationFromRecipe() throws Exception {
|
||||
when(sshMachineFactory.createSshClient(any(SshMachineRecipe.class), anyMap())).thenReturn(sshClient);
|
||||
when(sshMachineFactory.createInstance(eq(machine), eq(sshClient), any(LineConsumer.class))).thenReturn(sshMachineInstance);
|
||||
when(recipeDownloader.getRecipe(eq(machine.getConfig()))).thenReturn(recipe);
|
||||
|
||||
SshMachineInstance instance = provider.createInstance(machine, LineConsumer.DEV_NULL);
|
||||
|
||||
assertEquals(instance, sshMachineInstance);
|
||||
}
|
||||
|
||||
private MachineImpl createMachine() {
|
||||
return createMachine(false);
|
||||
}
|
||||
|
||||
private MachineImpl createMachine(boolean isDev) {
|
||||
MachineConfig machineConfig = MachineConfigImpl.builder()
|
||||
.setDev(isDev)
|
||||
.setEnvVariables(singletonMap("testEnvVar1", "testEnvVarVal1"))
|
||||
.setName("name1")
|
||||
.setServers(singletonList(new ServerConfImpl("myref1",
|
||||
"10011/tcp",
|
||||
"http",
|
||||
null)))
|
||||
.setSource(new MachineSourceImpl("ssh-config").setLocation("localhost:10012/recipe"))
|
||||
.setType("ssh")
|
||||
.build();
|
||||
return MachineImpl.builder()
|
||||
.setConfig(machineConfig)
|
||||
.setEnvName("env1")
|
||||
.setId("id1")
|
||||
.setOwner("owner1")
|
||||
.setRuntime(null)
|
||||
.setStatus(MachineStatus.CREATING)
|
||||
.setWorkspaceId("wsId1")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +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 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.MachineRuntimeInfo;
|
||||
import org.eclipse.che.api.core.util.LineConsumer;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Listeners;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for{@link SshMachineInstance}
|
||||
*
|
||||
* @author Igor Vinokur
|
||||
*/
|
||||
@Listeners(MockitoTestNGListener.class)
|
||||
public class SshMachineInstanceTest {
|
||||
@Mock
|
||||
private Machine machine;
|
||||
@Mock
|
||||
private SshClient sshClient;
|
||||
@Mock
|
||||
private LineConsumer outputConsumer;
|
||||
|
||||
private SshMachineInstance sshMachineInstance;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp() {
|
||||
when(machine.getConfig()).thenReturn(mock(MachineConfig.class));
|
||||
when(machine.getEnvName()).thenReturn("EnvName");
|
||||
when(machine.getId()).thenReturn("Id");
|
||||
when(machine.getOwner()).thenReturn("Owner");
|
||||
when(machine.getRuntime()).thenReturn(mock(MachineRuntimeInfo.class));
|
||||
when(machine.getWorkspaceId()).thenReturn("WorkspaceId");
|
||||
|
||||
sshMachineInstance = new SshMachineInstance(machine,
|
||||
sshClient,
|
||||
outputConsumer,
|
||||
mock(SshMachineFactory.class),
|
||||
new HashSet<>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseOutputConsumerAndStopClientOnDestroy() throws Exception {
|
||||
sshMachineInstance.destroy();
|
||||
|
||||
verify(outputConsumer).close();
|
||||
verify(sshClient).stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,7 +49,6 @@
|
|||
<module>plugin-nodejs</module>
|
||||
<module>plugin-nodejs-debugger</module>
|
||||
<module>plugin-php</module>
|
||||
<!-- module>plugin-ssh-machine</module-->
|
||||
<module>plugin-languageserver</module>
|
||||
<module>plugin-urlfactory</module>
|
||||
<module>plugin-json</module>
|
||||
|
|
|
|||
5
pom.xml
5
pom.xml
|
|
@ -924,11 +924,6 @@
|
|||
<artifactId>che-plugin-ssh-key-server</artifactId>
|
||||
<version>${che.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-ssh-machine</artifactId>
|
||||
<version>${che.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.plugin</groupId>
|
||||
<artifactId>che-plugin-svn-ext-ide</artifactId>
|
||||
|
|
|
|||
|
|
@ -1,28 +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.machine.shared.dto;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineLogMessage;
|
||||
import org.eclipse.che.dto.shared.DTO;
|
||||
|
||||
/**
|
||||
* @author Alexander Garagatyi
|
||||
*/
|
||||
@DTO
|
||||
public interface MachineLogMessageDto extends MachineLogMessage {
|
||||
void setContent(String content);
|
||||
|
||||
MachineLogMessageDto withContent(String content);
|
||||
|
||||
void setMachineName(String machineName);
|
||||
|
||||
MachineLogMessageDto withMachineName(String machineName);
|
||||
}
|
||||
Loading…
Reference in New Issue