Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tunnel readiness check after SauceConnect start #258

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -34,6 +35,8 @@
public abstract class AbstractSauceTunnelManager implements SauceTunnelManager {

protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSauceTunnelManager.class);
private static final Duration READINESS_CHECK_TIMEOUT = Duration.ofSeconds(15);
private static final Duration READINESS_CHECK_POLLING_INTERVAL = Duration.ofSeconds(3);

/** Should Sauce Connect output be suppressed? */
protected boolean quietMode;
Expand Down Expand Up @@ -498,8 +501,10 @@ public Process openConnection(

} else {
// everything okay, continue the build
if (outputGobbler.getTunnelId() != null) {
tunnelInformation.setTunnelId(outputGobbler.getTunnelId());
String provisionedTunnelId = outputGobbler.getTunnelId();
if (provisionedTunnelId != null) {
tunnelInformation.setTunnelId(provisionedTunnelId);
waitForReadiness(provisionedTunnelId);
}
logMessage(
printStream, "Sauce Connect " + getCurrentVersion() + " now launched for: " + name);
Expand Down Expand Up @@ -537,7 +542,34 @@ public Process openConnection(
}
}

public SystemErrorGobbler makeErrorGobbler(PrintStream printStream, InputStream errorStream) {
private void waitForReadiness(String tunnelId) {
long pollingIntervalMillis = READINESS_CHECK_POLLING_INTERVAL.toMillis();
long endTime = System.currentTimeMillis() + READINESS_CHECK_TIMEOUT.toMillis();
try {
do {
long iterationStartTime = System.currentTimeMillis();
Boolean isReady = scEndpoint.getTunnelInformation(tunnelId).isReady;
if (Boolean.TRUE.equals(isReady)) {
LOGGER.info("Tunnel with ID {} is ready for use", tunnelId);
return;
}
LOGGER.info("Waiting for readiness of tunnel with ID {}", tunnelId);
long iterationEndTime = System.currentTimeMillis();

long iterationPollingTimeout = pollingIntervalMillis - (iterationEndTime - iterationStartTime);
if (iterationPollingTimeout > 0) {
TimeUnit.MILLISECONDS.sleep(iterationPollingTimeout);
}
}
while (System.currentTimeMillis() <= endTime);
LOGGER.warn("Wait for readiness of tunnel with ID {} is timed out", tunnelId);
}
catch (IOException | InterruptedException e) {
LOGGER.warn("Unable to check readiness of tunnel with ID {}", tunnelId, e);
}
}

public SystemErrorGobbler makeErrorGobbler(PrintStream printStream, InputStream errorStream) {
return new SystemErrorGobbler("ErrorGobbler", errorStream, printStream);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
@ExtendWith(MockitoExtension.class)
class SauceConnectFourManagerTest {

private static final String STARTED_SC_LOG = "/started_sc.log";
private static final String STARTED_TUNNEL_ID = "a3ccd3985ed04e7ba0fefc7fa401e9c8";

@Mock private Process mockProcess;
@Mock private SauceREST mockSauceRest;
@Mock private SauceConnectEndpoint mockSCEndpoint;
Expand All @@ -58,8 +61,11 @@ private InputStream getResourceAsStream(String resourceName) {
@ValueSource(booleans = {true, false})
void testOpenConnectionSuccessfully(boolean cleanUpOnExit) throws IOException {
when(mockSCEndpoint.getTunnelsInformationForAUser()).thenReturn(List.of());
TunnelInformation readyTunnel = new TunnelInformation();
readyTunnel.isReady = true;
when(mockSCEndpoint.getTunnelInformation(STARTED_TUNNEL_ID)).thenReturn(readyTunnel);
tunnelManager.setCleanUpOnExit(cleanUpOnExit);
Process process = testOpenConnection("/started_sc.log");
Process process = testOpenConnection(STARTED_SC_LOG);
assertEquals(mockProcess, process);
}

Expand All @@ -75,7 +81,13 @@ void openConnectionTest_closes() throws IOException, InterruptedException {
@Test
void testOpenConnectionWithExtraSpacesInArgs() throws IOException {
when(mockSCEndpoint.getTunnelsInformationForAUser()).thenReturn(List.of());
testOpenConnection("/started_sc.log", " username-with-spaces-around ");
TunnelInformation notReadyTunnel = new TunnelInformation();
notReadyTunnel.isReady = false;
TunnelInformation readyTunnel = new TunnelInformation();
readyTunnel.isReady = true;
when(mockSCEndpoint.getTunnelInformation(STARTED_TUNNEL_ID)).thenReturn(notReadyTunnel,
readyTunnel);
testOpenConnection(STARTED_SC_LOG, " username-with-spaces-around ");
}

private Process testOpenConnection(String logFile) throws IOException {
Expand Down Expand Up @@ -121,10 +133,12 @@ void openConnectionTest_existing_tunnel() throws IOException {
TunnelInformation started = new TunnelInformation();
started.tunnelIdentifier = "8949e55fb5e14fd6bf6230b7a609b494";
started.status = "running";
started.isReady = true;

when(mockSCEndpoint.getTunnelsInformationForAUser()).thenReturn(List.of(started));
when(mockSCEndpoint.getTunnelInformation(STARTED_TUNNEL_ID)).thenReturn(started);

Process process = testOpenConnection("/started_sc.log");
Process process = testOpenConnection(STARTED_SC_LOG);
assertEquals(mockProcess, process);

verify(mockSCEndpoint).getTunnelsInformationForAUser();
Expand Down
Loading