Skip to content

Commit

Permalink
yugabytedb - initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Srinivasa Vasu <srinivasan.surprise@gmail.com>
  • Loading branch information
srinivasa-vasu committed Aug 15, 2021
1 parent ef921c3 commit a4b6e20
Show file tree
Hide file tree
Showing 15 changed files with 789 additions and 0 deletions.
46 changes: 46 additions & 0 deletions docs/modules/databases/yugabytedb.md
@@ -0,0 +1,46 @@
# YugabyteDB Module

See [Database containers](./index.md) for documentation and usage that is common to all database container types.

YugabyteDB supports two APIs.
- Yugabyte Structured Query Language [YSQL](https://docs.yugabyte.com/latest/api/ysql/) is a fully-relational API that is built by the PostgreSQL code
- Yugabyte Cloud Query Language [YCQL](https://docs.yugabyte.com/latest/api/ycql/) is a semi-relational SQL API that has its roots in the Cassandra Query Language

## Usage example

### YSQL API

```java
public class YugabyteDBTest {

@Rule
public YugabyteYSQLContainer container = new YugabyteYSQLContainer("yugabytedb/yugabyte:2.7.2.0-b216");

@Test
public void method() {
String url = container.getJdbcUrl();

... create a connection and run the tests as usual. It also depends on the frameworks being used.
```

#### JDBC URL

`jdbc:tc:yugabyte:2.7.2.0-b216:///yugabyte`

### YCQL API

```java
public class YugabyteDBTest {

@Rule
public YugabyteYCQLContainer container = new YugabyteYCQLContainer("yugabytedb/yugabyte:2.7.2.0-b216");

@Test
public void method() {
Session session = container.getSession();

... create a connection and run the tests as usual. It also depends on the frameworks being used.
```

## Adding this module to your project dependencies
[[TODO]]
10 changes: 10 additions & 0 deletions modules/yugabytedb/build.gradle
@@ -0,0 +1,10 @@
description = "Testcontainers :: JDBC :: YugabyteDB"

dependencies {
api project(':jdbc')
testImplementation project(':jdbc-test')
// YCQL driver
implementation 'com.yugabyte:java-driver-core:4.6.0-yb-6'
// YSQL driver
testImplementation 'com.yugabyte:jdbc-yugabytedb:42.2.7-yb-5-beta.2'
}
@@ -0,0 +1,42 @@
package org.testcontainers.containers;

import org.testcontainers.utility.DockerImageName;

/**
* Constants used in both YCQL and YSQL APIs
*
* @author srinivasa-vasu
*/
public interface YugabyteContainerConstants {

DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yugabytedb/yugabyte");

String DEFAULT_TAG = "2.7.2.0-b216";

String NAME = "yugabyte";

Integer YSQL_PORT = 5433;

Integer YCQL_PORT = 9042;

Integer MASTER_DASHBOARD_PORT = 7000;

Integer TSERVER_DASHBOARD_PORT = 9000;

String JDBC_DRIVER_CLASS = "org.postgresql.Driver";

String JDBC_CONNECT_PREFIX = "jdbc:postgresql";

String ENTRYPOINT = "bin/yugabyted start --daemon=false";

String LOCAL_DC = "datacenter1";

String USER_PARAM = "user";

String PASSWORD_PARAM = "password";

String YSQL_TEST_QUERY = "SELECT 1";

String YCQL_TEST_QUERY = "SELECT release_version FROM system.local";

}
@@ -0,0 +1,160 @@
package org.testcontainers.containers;

import java.net.InetSocketAddress;
import java.time.Duration;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.github.dockerjava.api.command.InspectContainerResponse;
import org.testcontainers.containers.delegate.YugabyteYCQLDelegate;
import org.testcontainers.containers.strategy.YugabyteYCQLWaitStrategy;
import org.testcontainers.ext.ScriptUtils;
import org.testcontainers.utility.DockerImageName;

import static org.testcontainers.containers.YugabyteContainerConstants.DEFAULT_IMAGE_NAME;
import static org.testcontainers.containers.YugabyteContainerConstants.ENTRYPOINT;
import static org.testcontainers.containers.YugabyteContainerConstants.LOCAL_DC;
import static org.testcontainers.containers.YugabyteContainerConstants.MASTER_DASHBOARD_PORT;
import static org.testcontainers.containers.YugabyteContainerConstants.TSERVER_DASHBOARD_PORT;
import static org.testcontainers.containers.YugabyteContainerConstants.YCQL_PORT;

/**
* YugabyteDB YCQL (Cloud Query Language) API container
*
* @author srinivasa-vasu
* @see <a href="https://docs.yugabyte.com/latest/api/ycql/">YCQL API</a>
*/
public class YugabyteYCQLContainer extends GenericContainer<YugabyteYCQLContainer> {

private String keyspace;

private String username;

private String password;

private String initScript;

/**
* @param imageName image name
*/
public YugabyteYCQLContainer(final String imageName) {
this(DockerImageName.parse(imageName));
}

/**
* @param imageName image name
*/
public YugabyteYCQLContainer(final DockerImageName imageName) {
super(imageName);
imageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
withExposedPorts(YCQL_PORT, MASTER_DASHBOARD_PORT, TSERVER_DASHBOARD_PORT);
waitingFor(new YugabyteYCQLWaitStrategy(this).withStartupTimeout(Duration.ofSeconds(60)));
withCommand(ENTRYPOINT);
}

/**
* Configures the environment variables. Setting up these variables would create the
* custom objects. Setting {@link #withKeyspaceName(String)},
* {@link #withUsername(String)}, {@link #withPassword(String)} these parameters will
* initilaize the database with those custom values
*/
@Override
protected void configure() {
addEnv("YCQL_KEYSPACE", keyspace);
addEnv("YCQL_USER", username);
addEnv("YCQL_PASSWORD", password);
}

/**
* @param initScript path of the initialization script file
* @return {@link YugabyteYCQLContainer} instance
*/
public YugabyteYCQLContainer withInitScript(String initScript) {
this.initScript = initScript;
return this;
}

/**
* Setting this would create the keyspace
* @param keyspace keyspace
* @return {@link YugabyteYCQLContainer} instance
*/
public YugabyteYCQLContainer withKeyspaceName(final String keyspace) {
this.keyspace = keyspace;
return this;
}

/**
* Setting this would create the custom user role
* @param username user name
* @return {@link YugabyteYCQLContainer} instance
*/
public YugabyteYCQLContainer withUsername(final String username) {
this.username = username;
return this;
}

/**
* Setting this along with {@link #withUsername(String)} would enable authentication
* @param password password
* @return {@link YugabyteYCQLContainer} instance
*/
public YugabyteYCQLContainer withPassword(final String password) {
this.password = password;
return this;
}

/**
* Executes the initilization script
* @param containerInfo containerInfo
*/
@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
if (initScript != null) {
ScriptUtils.runInitScript(new YugabyteYCQLDelegate(getSessionBuilder()), initScript);
}
}

/**
* Builds a {@link CqlSession} instance
* @return {@link CqlSession} instance
*/
public CqlSession getSession() {
return getSessionBuilder().build();
}

/**
* Builder method for {#com.datastax.oss.driver.api.core.CqlSession}
* @return {@link CqlSessionBuilder}
*/
public CqlSessionBuilder getSessionBuilder() {
return CqlSession.builder().withLocalDatacenter(LOCAL_DC).withKeyspace(this.getKeyspace())
.withAuthCredentials(this.getUsername(), this.getPassword())
.addContactPoint(new InetSocketAddress(this.getHost(), this.getMappedPort(YCQL_PORT)));
}

/**
* Username getter method
* @return username
*/
public String getUsername() {
return username;
}

/**
* Password getter method
* @return password
*/
public String getPassword() {
return password;
}

/**
* Keyspace getter method
* @return keyspace
*/
public String getKeyspace() {
return keyspace;
}

}
@@ -0,0 +1,137 @@
package org.testcontainers.containers;

import java.time.Duration;
import java.util.Set;

import org.testcontainers.containers.strategy.YugabyteYSQLWaitStrategy;
import org.testcontainers.utility.DockerImageName;

import static java.util.Collections.singleton;
import static org.testcontainers.containers.YugabyteContainerConstants.DEFAULT_IMAGE_NAME;
import static org.testcontainers.containers.YugabyteContainerConstants.ENTRYPOINT;
import static org.testcontainers.containers.YugabyteContainerConstants.JDBC_CONNECT_PREFIX;
import static org.testcontainers.containers.YugabyteContainerConstants.JDBC_DRIVER_CLASS;
import static org.testcontainers.containers.YugabyteContainerConstants.MASTER_DASHBOARD_PORT;
import static org.testcontainers.containers.YugabyteContainerConstants.TSERVER_DASHBOARD_PORT;
import static org.testcontainers.containers.YugabyteContainerConstants.YSQL_PORT;

/**
* YugabyteDB YSQL (Structured Query Language) API container
*
* @author srinivasa-vasu
* @see <a href="https://docs.yugabyte.com/latest/api/ysql/">YSQL API</a>
*/

public class YugabyteYSQLContainer extends JdbcDatabaseContainer<YugabyteYSQLContainer> {

private String database = "yugabyte";

private String username = "yugabyte";

private String password = "yugabyte";

/**
* @param imageName image name
*/
public YugabyteYSQLContainer(final String imageName) {
this(DockerImageName.parse(imageName));
}

/**
* @param imageName image name
*/
public YugabyteYSQLContainer(final DockerImageName imageName) {
super(imageName);
imageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
withExposedPorts(YSQL_PORT, MASTER_DASHBOARD_PORT, TSERVER_DASHBOARD_PORT);
waitingFor(new YugabyteYSQLWaitStrategy(this).withStartupTimeout(Duration.ofSeconds(60)));
withCommand(ENTRYPOINT);
}

@Override
public Set<Integer> getLivenessCheckPortNumbers() {
return singleton(getMappedPort(YSQL_PORT));
}

/**
* Configures the environment variables. Setting up these variables would create the
* custom objects. Setting {@link #withDatabaseName(String)},
* {@link #withUsername(String)}, {@link #withPassword(String)} these parameters will
* initilaize the database with those custom values
*/

@Override
protected void configure() {
addEnv("YSQL_DB", database);
addEnv("YSQL_USER", username);
addEnv("YSQL_PASSWORD", password);
}

@Override
public String getDriverClassName() {
return JDBC_DRIVER_CLASS;
}

@Override
public String getJdbcUrl() {
return JDBC_CONNECT_PREFIX + "://" + getHost() + ":" + getMappedPort(YSQL_PORT) + "/" + database
+ constructUrlParameters("?", "&");
}

@Override
public String getDatabaseName() {
return database;
}

@Override
public String getUsername() {
return username;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getTestQueryString() {
return "SELECT 1";
}

/**
* Setting this would create the keyspace
* @param database database name
* @return {@link YugabyteYSQLContainer} instance
*/

@Override
public YugabyteYSQLContainer withDatabaseName(final String database) {
this.database = database;
return this;
}

/**
* Setting this would create the custom user role
* @param username user name
* @return {@link YugabyteYSQLContainer} instance
*/

@Override
public YugabyteYSQLContainer withUsername(final String username) {
this.username = username;
return this;
}

/**
* Setting this along with {@link #withUsername(String)} would enable authentication
* @param password password
* @return {@link YugabyteYSQLContainer} instance
*/

@Override
public YugabyteYSQLContainer withPassword(final String password) {
this.password = password;
return this;
}

}

0 comments on commit a4b6e20

Please sign in to comment.