Skip to content
Open
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
@@ -0,0 +1,80 @@
package ubic.gemma.persistence.util;

import lombok.Setter;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.context.SmartLifecycle;
import org.springframework.util.Assert;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
* Establishes a local SSH tunnel.
* @author poirigui
*/
@CommonsLog
@Setter
public class LocalSshTunnel implements SmartLifecycle {

private String host;
@Nullable
private Integer port;

// for the tunnel
private int localPort;
private String remoteHost;
private int remotePort;

private boolean autoStart = false;

private Process tunnelProcess;

@Override
public void start() {
Assert.isTrue( host != null && localPort > 0 && remoteHost != null && remotePort > 0 );
try {
String[] args = new String[] { "ssh", "-L", localPort + ":" + remoteHost + ":" + remotePort, host };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the -N option so that no shell is opened.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need a log file for the stdout/stderr. We can take inspiration from the internal static asset server.

if ( port != null ) {
args = ArrayUtils.addAll( args, "-p", String.valueOf( port ) );
}
tunnelProcess = Runtime.getRuntime().exec( args );
// quickly check if the process exited
if ( tunnelProcess.waitFor( 100, TimeUnit.MILLISECONDS ) ) {
throw new RuntimeException( IOUtils.toString( tunnelProcess.getErrorStream(), StandardCharsets.UTF_8 ) );
}
} catch ( IOException | InterruptedException e ) {
throw new RuntimeException( e );
}
log.info( String.format( "Established a SSH tunnel to %s%s: %d -> %s:%d.",
host, port != null ? ":" + port : "", localPort, remoteHost, remotePort ) );
}

@Override
public void stop() {
tunnelProcess.destroy();
}

@Override
public boolean isRunning() {
return tunnelProcess != null && tunnelProcess.isAlive();
}

@Override
public boolean isAutoStartup() {
return autoStart;
}

@Override
public void stop( Runnable callback ) {
stop();
}

@Override
public int getPhase() {
return 0;
}
}
14 changes: 14 additions & 0 deletions gemma-core/src/main/resources/default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ gemma.db.user=gemmauser
gemma.db.password=XXXXXX
# Maximum size for the connections pool
gemma.db.maximumPoolSize=10
# Establish an SSH tunnel to connect to the database (only if the dev profile is active)
gemma.db.tunnel.enabled=false
gemma.db.tunnel.host=
gemma.db.tunnel.port=
gemma.db.tunnel.localPort=0
gemma.db.tunnel.remoteHost=
gemma.db.tunnel.remotePort=0
############################################################
# SECURITY
# Used to elevate authorities for some methods.
Expand Down Expand Up @@ -205,6 +212,13 @@ gemma.testdb.agent.userName=gemmaAgent
gemma.testdb.agent.password=XXXXXXXX
# Initialize the test database, this can be disabled to make integration tests faster
gemma.testdb.initialize=true
# Establish an SSH tunnel to connect to the test database (only if the dev profile is active)
gemma.testdb.tunnel.enabled=false
gemma.testdb.tunnel.host=
gemma.testdb.tunnel.port=
gemma.testdb.tunnel.localPort=0
gemma.testdb.tunnel.remoteHost=
gemma.testdb.tunnel.remotePort=0
#the external database id to exclude by default in phenocarta
gemma.neurocarta.exluded_database_id=85
# Featured external databases in Gemma Web About page and Gemma REST main endpoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,35 @@
<prop key="sessionVariables">sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'</prop>
</util:properties>

<beans profile="dev">
<bean id="tunnel" class="ubic.gemma.persistence.util.LocalSshTunnel">
<property name="host" value="${gemma.db.tunnel.host}"/>
<property name="port" value="${gemma.db.tunnel.port}"/>
<property name="localPort" value="${gemma.db.tunnel.localPort}"/>
<property name="remoteHost" value="${gemma.db.tunnel.remoteHost}"/>
<property name="remotePort" value="${gemma.db.tunnel.remotePort}"/>
<property name="autoStart" value="${gemma.db.tunnel.enabled}"/>
</bean>
<bean id="testTunnel" class="ubic.gemma.persistence.util.LocalSshTunnel">
<property name="host" value="${gemma.testdb.tunnel.host}"/>
<property name="port" value="${gemma.testdb.tunnel.port}"/>
<property name="localPort" value="${gemma.testdb.tunnel.localPort}"/>
<property name="remoteHost" value="${gemma.testdb.tunnel.remoteHost}"/>
<property name="remotePort" value="${gemma.testdb.tunnel.remotePort}"/>
<property name="autoStart" value="${gemma.testdb.tunnel.enabled}"/>
</bean>
</beans>

<beans profile="!dev">
<!-- placeholders to satisfy depends-on -->
<bean id="tunnel" class="java.lang.Object"/>
<bean id="testTunnel" class="java.lang.Object"/>
</beans>

<!-- we use the same database for production and development -->
<beans profile="production,dev">
<!-- Database connection information -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" depends-on="tunnel">
<property name="poolName" value="gemma"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="${gemma.db.user}"/>
Expand All @@ -36,7 +61,7 @@
</beans>

<beans profile="test,testdb">
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" depends-on="testTunnel">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="${gemma.testdb.user}"/>
<property name="password" value="${gemma.testdb.password}"/>
Expand Down