package org.rakam.event;
import com.facebook.presto.rakam.RakamRaptorPlugin;
import com.facebook.presto.rakam.stream.StreamPlugin;
import com.facebook.presto.rakam.stream.metadata.ForMetadata;
import com.facebook.presto.raptor.RaptorPlugin;
import com.facebook.presto.server.testing.TestingPrestoServer;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import io.airlift.testing.postgresql.TestingPostgreSqlServer;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.gaul.s3proxy.AuthenticationType;
import org.gaul.s3proxy.S3Proxy;
import org.gaul.s3proxy.S3ProxyConstants;
import org.jclouds.Constants;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.rakam.analysis.JDBCPoolDataSource;
import org.rakam.aws.AWSConfig;
import org.rakam.config.JDBCConfig;
import org.rakam.presto.analysis.PrestoConfig;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.IDBI;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.URI;
import java.nio.file.Path;
import java.util.Properties;
import java.util.concurrent.ThreadLocalRandom;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.ImmutableList.of;
import static java.lang.ProcessBuilder.Redirect.INHERIT;
import static java.lang.String.format;
import static java.lang.System.getProperty;
import static org.gaul.s3proxy.AuthenticationType.AWS_V2_OR_V4;
public class TestingEnvironment
{
private static PrestoConfig prestoConfig;
private static TestingPrestoServer testingPrestoServer;
private static TestingPostgreSqlServer testingPostgresqlServer;
private static JDBCConfig postgresqlConfig;
private static int kinesisPort;
private static JDBCPoolDataSource metastore;
private static Process kinesisProcess;
private final S3ProxyLaunchInfo s3ProxyLaunchInfo;
private Process dynamodbServer;
public TestingEnvironment()
{
this(true);
}
public TestingEnvironment(boolean installMetadata)
{
try {
s3ProxyLaunchInfo = startS3Proxy();
}
catch (Exception e) {
throw Throwables.propagate(e);
}
// AmazonS3Client amazonS3Client = new AmazonS3Client(getAWSConfig().getCredentials());
// amazonS3Client.setEndpoint(getAWSConfig().getS3Endpoint());
// amazonS3Client.createBucket("testing");
try {
if (testingPrestoServer == null) {
synchronized (TestingEnvironment.class) {
testingPrestoServer = new TestingPrestoServer();
String metadataDatabase = Files.createTempDir().getAbsolutePath();
RaptorPlugin plugin = new RakamRaptorPlugin();
Module metastoreModule = new Module()
{
@Override
public void configure(Binder binder)
{
}
@Provides
@Singleton
@ForMetadata
public IDBI getDataSource()
{
return new DBI(format("jdbc:h2:mem:test%s;DB_CLOSE_DELAY=-1;mode=MySQL", System.nanoTime()));
}
};
kinesisPort = startKinesis();
int dynamodbPort = createDynamodbProcess();
StreamPlugin streamPlugin = new StreamPlugin("streaming", metastoreModule);
testingPrestoServer.installPlugin(plugin);
testingPrestoServer.installPlugin(streamPlugin);
testingPrestoServer.createCatalog("streaming", "streaming", ImmutableMap.<String, String>builder()
.put("target.connector_id", "rakam_raptor")
.put("backup.provider", "s3")
// .put("backup.s3.bucket", "testing")
.put("aws.s3-endpoint", s3ProxyLaunchInfo.getEndpoint().toString())
.put("stream.max-flush-duration", "0ms")
.put("http-server.http.port", Integer.toString(ThreadLocalRandom.current().nextInt(1000, 10000)))
.put("storage.directory", Files.createTempDir().getAbsolutePath())
.put("backup.timeout", "1m")
.put("stream.source", "kinesis")
.put("kinesis.stream", "rakam-events")
.put("aws.kinesis-endpoint", "http://127.0.0.1:" + kinesisPort)
.put("aws.dynamodb-endpoint", "http://127.0.0.1:" + dynamodbPort)
.put("aws.secret-access-key", s3ProxyLaunchInfo.getS3Credential())
.put("aws.access-key", s3ProxyLaunchInfo.getS3Identity())
.put("aws.region", "us-east-1")
.put("aws.enable-cloudwatch", "false")
.put("kinesis.consumer-dynamodb-table", "rakamtest")
.put("middleware.max-flush-records", "1")
.put("stream.max-flush-records", "1").build());
testingPrestoServer.createCatalog("rakam_raptor", "rakam_raptor", ImmutableMap.<String, String>builder()
.put("storage.data-directory", Files.createTempDir().getAbsolutePath())
.put("metadata.db.type", "h2")
.put("metadata.db.connections.wait", "10s")
.put("metadata.db.connections.max", "500")
.put("metadata.db.mvcc.enabled", "true")
.put("metadata.db.filename", metadataDatabase).build());
prestoConfig = new PrestoConfig()
.setAddress(URI.create("http://" + testingPrestoServer.getAddress().toString()))
.setStreamingConnector("streaming")
.setColdStorageConnector("rakam_raptor");
metastore = JDBCPoolDataSource.getOrCreateDataSource(new JDBCConfig().setUrl("jdbc:h2:" + metadataDatabase)
.setUsername("sa").setPassword(""));
System.out.println(prestoConfig.getAddress());
}
}
if (installMetadata) {
if (testingPostgresqlServer == null) {
synchronized (TestingEnvironment.class) {
testingPostgresqlServer = new TestingPostgreSqlServer("testuser", "testdb");
Runtime.getRuntime().addShutdownHook(
new Thread(
() -> {
try {
testingPostgresqlServer.close();
if (kinesisProcess != null) {
kinesisProcess.destroy();
}
dynamodbServer.destroy();
}
catch (IOException e) {
e.printStackTrace();
}
}
)
);
}
}
postgresqlConfig = new JDBCConfig()
.setUrl(testingPostgresqlServer.getJdbcUrl())
.setUsername(testingPostgresqlServer.getUser());
System.out.println("postgresql config: " + postgresqlConfig);
}
}
catch (Exception e) {
throw propagate(e);
}
}
public AWSConfig getAWSConfig()
{
return new AWSConfig()
.setAccessKey(s3ProxyLaunchInfo.getS3Identity())
.setSecretAccessKey(s3ProxyLaunchInfo.getS3Credential())
.setRegion("us-east-1")
.setS3Endpoint(s3ProxyLaunchInfo.getEndpoint().toString());
}
public JDBCPoolDataSource getPrestoMetastore()
{
return metastore;
}
public JDBCConfig getPostgresqlConfig()
{
if (postgresqlConfig == null) {
throw new UnsupportedOperationException();
}
return postgresqlConfig;
}
public PrestoConfig getPrestoConfig()
{
return prestoConfig;
}
public void close()
throws Exception
{
testingPrestoServer.close();
if (testingPostgresqlServer != null) {
testingPostgresqlServer.close();
}
if (kinesisProcess != null) {
kinesisProcess.destroy();
}
}
private int startKinesis()
throws Exception
{
Path mainDir = new File(getProperty("user.dir"), ".test/kinesalite").toPath();
String nodePath = mainDir
.resolve("node/node")
.toFile().getAbsolutePath();
String kinesalitePath = mainDir
.resolve("node_modules/.bin/kinesalite")
.toFile().getAbsolutePath();
int kinesisPort = randomPort();
kinesisProcess = new ProcessBuilder(of(nodePath, kinesalitePath, "--port", Integer.toString(kinesisPort)))
.redirectErrorStream(true)
.redirectOutput(INHERIT)
.start();
return kinesisPort;
}
public static int randomPort()
throws IOException
{
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
public int createDynamodbProcess()
throws Exception
{
int randomPort = randomPort();
Path mainDir = new File(getProperty("user.dir"), ".test/dynamodb").toPath();
dynamodbServer = new ProcessBuilder(of("java", format("-Djava.library.path=%s",
mainDir.resolve("DynamoDBLocal_lib").toFile().getAbsolutePath()),
"-jar", mainDir.resolve("DynamoDBLocal.jar").toFile().getAbsolutePath(),
"-inMemory", "--port", Integer.toString(randomPort)))
.start();
return randomPort;
}
public int getKinesisPort()
{
return kinesisPort;
}
static S3ProxyLaunchInfo startS3Proxy()
throws Exception
{
S3ProxyLaunchInfo info = new S3ProxyLaunchInfo();
try (InputStream is = Resources.asByteSource(Resources.getResource(
"s3proxy.conf")).openStream()) {
info.getProperties().load(is);
}
String provider = info.getProperties().getProperty(
Constants.PROPERTY_PROVIDER);
String identity = info.getProperties().getProperty(
Constants.PROPERTY_IDENTITY);
String credential = info.getProperties().getProperty(
Constants.PROPERTY_CREDENTIAL);
String endpoint = info.getProperties().getProperty(
Constants.PROPERTY_ENDPOINT);
AuthenticationType s3ProxyAuthorization = AWS_V2_OR_V4;
info.s3Identity = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_IDENTITY);
info.s3Credential = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_CREDENTIAL);
info.endpoint = new URI(info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_ENDPOINT));
String secureEndpoint = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
String keyStorePath = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_KEYSTORE_PATH);
String keyStorePassword = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_KEYSTORE_PASSWORD);
String virtualHost = info.getProperties().getProperty(
S3ProxyConstants.PROPERTY_VIRTUAL_HOST);
ContextBuilder builder = ContextBuilder
.newBuilder(provider)
.credentials(identity, credential)
.modules(ImmutableList.<Module>of(new SLF4JLoggingModule()))
.overrides(info.getProperties());
if (!Strings.isNullOrEmpty(endpoint)) {
builder.endpoint(endpoint);
}
BlobStoreContext context = builder.build(BlobStoreContext.class);
info.blobStore = context.getBlobStore();
S3Proxy.Builder s3ProxyBuilder = S3Proxy.builder()
.blobStore(info.getBlobStore())
.endpoint(info.getEndpoint());
if (secureEndpoint != null) {
s3ProxyBuilder.secureEndpoint(new URI(secureEndpoint));
}
if (info.getS3Identity() != null || info.getS3Credential() != null) {
s3ProxyBuilder.awsAuthentication(s3ProxyAuthorization,
info.getS3Identity(), info.getS3Credential());
}
if (keyStorePath != null || keyStorePassword != null) {
s3ProxyBuilder.keyStore(
Resources.getResource(keyStorePath).toString(),
keyStorePassword);
}
if (virtualHost != null) {
s3ProxyBuilder.virtualHost(virtualHost);
}
info.s3Proxy = s3ProxyBuilder.build();
info.s3Proxy.start();
while (!info.s3Proxy.getState().equals(AbstractLifeCycle.STARTED)) {
Thread.sleep(1);
}
// reset endpoint to handle zero port
info.endpoint = new URI(info.endpoint.getScheme(),
info.endpoint.getUserInfo(), info.endpoint.getHost(),
info.s3Proxy.getPort(), info.endpoint.getPath(),
info.endpoint.getQuery(), info.endpoint.getFragment());
return info;
}
static final class S3ProxyLaunchInfo
{
private S3Proxy s3Proxy;
private Properties properties = new Properties();
private String s3Identity;
private String s3Credential;
private BlobStore blobStore;
private URI endpoint;
S3Proxy getS3Proxy()
{
return s3Proxy;
}
Properties getProperties()
{
return properties;
}
String getS3Identity()
{
return s3Identity;
}
String getS3Credential()
{
return s3Credential;
}
BlobStore getBlobStore()
{
return blobStore;
}
URI getEndpoint()
{
return endpoint;
}
}
}