/******************************************************************************* * 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.machine.server.model.impl.ServerImpl; import org.eclipse.che.api.machine.server.spi.InstanceNode; 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; } }