From edc5d234e41a5fb82bf790533d9287322657d94c Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Fri, 2 Jun 2017 10:41:42 +0300 Subject: [PATCH] CODENVY-2217: Change checking for SSH agent readiness (#5260) --- .../MappedPortIsListeningAgentChecker.java | 3 + .../launcher/SshAgentLaunchingChecker.java | 74 +++++++++++++++++++ .../che/api/agent/SshAgentLauncher.java | 6 +- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/SshAgentLaunchingChecker.java diff --git a/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/MappedPortIsListeningAgentChecker.java b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/MappedPortIsListeningAgentChecker.java index 7cf60e096de..42866eb517f 100644 --- a/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/MappedPortIsListeningAgentChecker.java +++ b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/MappedPortIsListeningAgentChecker.java @@ -22,7 +22,10 @@ * Verifies that agent was started successfully by checking that specified local port is listened in a machine. * * @author Alexander Garagatyi + * @deprecated It is needed to be sure that the right service is up on the given port. + * Also, some proxies (like docker-proxy) response that the port is listened but actually it is forwarded only. */ +@Deprecated public class MappedPortIsListeningAgentChecker implements AgentLaunchingChecker { private final String exposedPort; diff --git a/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/SshAgentLaunchingChecker.java b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/SshAgentLaunchingChecker.java new file mode 100644 index 00000000000..f9d9a8bca53 --- /dev/null +++ b/agents/che-core-api-agent/src/main/java/org/eclipse/che/api/agent/server/launcher/SshAgentLaunchingChecker.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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.server.launcher; + +import org.eclipse.che.api.agent.shared.model.Agent; +import org.eclipse.che.api.core.model.machine.Server; +import org.eclipse.che.api.machine.server.exception.MachineException; +import org.eclipse.che.api.machine.server.spi.Instance; +import org.eclipse.che.api.machine.server.spi.InstanceProcess; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +/** + * Checks readiness of ssh via sending global request. + * For more details see RFC-4254. + * + * @author Mykola Morhun + */ +public class SshAgentLaunchingChecker implements AgentLaunchingChecker { + private static final String SSH_PORT = "22/tcp"; + + private static final byte[] SSH_GLOBAL_REQUEST_BYTES; + static { + final byte SSH_MSG_GLOBAL_REQUEST = 80; + final byte[] SSH_CHANNEL_TYPE = "test".getBytes(StandardCharsets.US_ASCII); + final byte SSH_WANT_REPLAY = 1; + + byte[] requestBytes = null; + try (ByteArrayOutputStream sshGlobalRequestBytes = new ByteArrayOutputStream()) { + sshGlobalRequestBytes.write(SSH_MSG_GLOBAL_REQUEST); + sshGlobalRequestBytes.write(SSH_CHANNEL_TYPE); + sshGlobalRequestBytes.write(SSH_WANT_REPLAY); + + requestBytes = sshGlobalRequestBytes.toByteArray(); + } catch (IOException ignore) { /* will never happen */ } + SSH_GLOBAL_REQUEST_BYTES = requestBytes; + } + + @Override + public boolean isLaunched(Agent agent, InstanceProcess process, Instance machine) throws MachineException { + Server server = machine.getRuntime().getServers().get(SSH_PORT); + if (server != null) { + try { + String[] sshServerHostAndPort = server.getProperties().getInternalAddress().split(":"); + try (Socket socket = new Socket(sshServerHostAndPort[0], Integer.parseInt(sshServerHostAndPort[1])); + BufferedOutputStream sshRequest = new BufferedOutputStream(socket.getOutputStream()); + BufferedInputStream sshResponse = new BufferedInputStream(socket.getInputStream())) { + + sshRequest.write(SSH_GLOBAL_REQUEST_BYTES); + sshRequest.flush(); + // Actual response is not needed, just make sure that ssh server give a response. + if (sshResponse.read() != -1) { + return true; + } + } + } catch (Exception ignored) { + } + } + return false; + } +} diff --git a/agents/ssh/src/main/java/org/eclipse/che/api/agent/SshAgentLauncher.java b/agents/ssh/src/main/java/org/eclipse/che/api/agent/SshAgentLauncher.java index 3526ddcdf83..d26298f0b02 100644 --- a/agents/ssh/src/main/java/org/eclipse/che/api/agent/SshAgentLauncher.java +++ b/agents/ssh/src/main/java/org/eclipse/che/api/agent/SshAgentLauncher.java @@ -14,9 +14,7 @@ import com.google.inject.Singleton; import org.eclipse.che.api.agent.server.launcher.AbstractAgentLauncher; -import org.eclipse.che.api.agent.server.launcher.CompositeAgentLaunchingChecker; -import org.eclipse.che.api.agent.server.launcher.MappedPortIsListeningAgentChecker; -import org.eclipse.che.api.agent.server.launcher.ProcessIsLaunchedChecker; +import org.eclipse.che.api.agent.server.launcher.SshAgentLaunchingChecker; import javax.inject.Named; @@ -33,7 +31,7 @@ public SshAgentLauncher(@Named("che.agent.dev.max_start_time_ms") long agentMaxS @Named("che.agent.dev.ping_delay_ms") long agentPingDelayMs) { super(agentMaxStartTimeMs, agentPingDelayMs, - new MappedPortIsListeningAgentChecker("22/tcp")); + new SshAgentLaunchingChecker()); } @Override