package com.nirima.jenkins.plugins.docker.utils; import com.trilead.ssh2.Connection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.SECONDS; import static shaded.com.google.common.base.Preconditions.checkState; public class PortUtils { private static final Logger LOGGER = LoggerFactory.getLogger(PortUtils.class); /** * @param host hostname to connect to * @param port port to open socket * * @return util class to check connection */ public static ConnectionCheck connectionCheck(String host, int port) { return new ConnectionCheck(host, port); } public static ConnectionCheck connectionCheck(InetSocketAddress address) { return new ConnectionCheck(address.getHostString(), address.getPort()); } public static class ConnectionCheck { private final String host; private final int port; private int retries = 10; private long retryDelay = (long) SECONDS.toMillis(2); private ConnectionCheck(String host, int port) { this.host = host; this.port = port; } public ConnectionCheck withRetries(int retries) { this.retries = retries; return this; } public ConnectionCheck withEveryRetryWaitFor(int time, TimeUnit units) { retryDelay = units.toMillis(time); return this; } public ConnectionCheckSSH useSSH() { return new ConnectionCheckSSH(this); } /** * @return true if socket opened successfully, false otherwise */ public boolean executeOnce() { try (Socket ignored = new Socket(host, port)) { return true; } catch (IOException e) { return false; } } public boolean execute() { LOGGER.trace("Testing connectivity to {} port {}", host, port ); for (int i = 1; i <= retries; i++) { if (executeOnce()) { return true; } try { Thread.sleep(retryDelay); } catch (InterruptedException e) { return false; // quit if interrupted } } LOGGER.warn("Could not connect to {} port {}. Are you sure this location is contactable from Jenkins?", host, port); return false; } } public static class ConnectionCheckSSH { private final ConnectionCheck parent; private int sshTimeoutMillis = (int) SECONDS.toMillis(2); ConnectionCheckSSH(ConnectionCheck connectionCheck) { this.parent = connectionCheck; } public ConnectionCheckSSH withSSHTimeout(int time, TimeUnit units) { sshTimeoutMillis = (int)units.toMillis(time); return this; } /** * Connects to sshd on host:port * Retries while attempts reached with delay * First with tcp port wait, then with ssh connection wait */ public boolean execute() { checkState(parent.execute(), "Port %s is not opened to connect to", parent.port); for (int i = 1; i <= parent.retries; i++) { Connection connection = new Connection(parent.host, parent.port); try { connection.connect(null, 0, sshTimeoutMillis, sshTimeoutMillis); LOGGER.info("SSH port is open on {}:{}", parent.host, parent.port); return true; } catch (IOException e) { LOGGER.error("Failed to connect to {}:{} (try {}/{}) - {}", parent.host, parent.port, i, parent.retries, e.getMessage()); } finally { connection.close(); } try { Thread.sleep(parent.retryDelay); } catch (InterruptedException e) { return false; // Quit if interrupted } } // Tried over and again. no joy. return false; } } }