+ * Though we can either use HTTP or PORT based wait strategy, when we create a custom + * keyspace/role, it gets executed asynchronously. As the wait on container.start() on a + * specific port wouldn't fully guarantee the custom object execution. It's better to + * check the DB status with this way with a smoke test query that uses the underlying + * custom objects and wait for the operation to complete. + *
+ * + * @author srinivasa-vasu + */ +@RequiredArgsConstructor +public final class YugabyteYCQLWaitStrategy extends AbstractWaitStrategy { + + private final WaitStrategyTarget target; + + @Override + public void waitUntilReady(WaitStrategyTarget target) { + YugabyteYCQLContainer container = (YugabyteYCQLContainer) target; + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + try (CqlSession session = container.getSession()) { + session.execute(YCQL_TEST_QUERY); + } + }); + return true; + }); + } + + @Override + public void waitUntilReady() { + waitUntilReady(target); + } + +} diff --git a/modules/yugabytedb/src/main/java/org/testcontainers/containers/strategy/YugabyteYSQLWaitStrategy.java b/modules/yugabytedb/src/main/java/org/testcontainers/containers/strategy/YugabyteYSQLWaitStrategy.java new file mode 100644 index 00000000000..1f114489f39 --- /dev/null +++ b/modules/yugabytedb/src/main/java/org/testcontainers/containers/strategy/YugabyteYSQLWaitStrategy.java @@ -0,0 +1,56 @@ +package org.testcontainers.containers.strategy; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.TimeUnit; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.YugabyteYSQLContainer; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; + +import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; +import static org.testcontainers.containers.YugabyteContainerConstants.YSQL_TEST_QUERY; + +/** + * Custom wait strategy for YSQL API. + * + *+ * Though we can either use HTTP or PORT based wait strategy, when we create a custom + * database/role, it gets executed asynchronously. As the wait on container.start() on a + * specific port wouldn't fully guarantee the custom object execution. It's better to + * check the DB status with this way with a smoke test query that uses the underlying + * custom objects and wait for the operation to complete. + *
+ * + * @author srinivasa-vasu + */ +@RequiredArgsConstructor +@Slf4j +public final class YugabyteYSQLWaitStrategy extends AbstractWaitStrategy { + + private final WaitStrategyTarget target; + + @Override + public void waitUntilReady(WaitStrategyTarget target) { + YugabyteYSQLContainer container = (YugabyteYSQLContainer) target; + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + try (Connection con = container.createConnection(container.getJdbcUrl())) { + con.createStatement().execute(YSQL_TEST_QUERY); + } + catch (SQLException ex) { + log.error("Error connecting to the database", ex); + } + }); + return true; + }); + } + + @Override + public void waitUntilReady() { + waitUntilReady(target); + } + +} diff --git a/modules/yugabytedb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/yugabytedb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..d4b25fba9f4 --- /dev/null +++ b/modules/yugabytedb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.YugabyteYSQLContainerProvider diff --git a/modules/yugabytedb/src/test/java/org/testcontainers/YugabyteTestContainerConstants.java b/modules/yugabytedb/src/test/java/org/testcontainers/YugabyteTestContainerConstants.java new file mode 100644 index 00000000000..d94b01a422b --- /dev/null +++ b/modules/yugabytedb/src/test/java/org/testcontainers/YugabyteTestContainerConstants.java @@ -0,0 +1,18 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author srinivasa-vasu + */ +public interface YugabyteTestContainerConstants { + + String IMAGE_NAME = "yugabytedb/yugabyte:2.7.2.0-b216"; + + DockerImageName YBDB_TEST_IMAGE = DockerImageName.parse(IMAGE_NAME); + + String LOCAL_DC = "datacenter1"; + + int YCQL_PORT = 9042; + +} diff --git a/modules/yugabytedb/src/test/java/org/testcontainers/jdbc/yugabytedb/YugabyteYSQLJDBCDriverTest.java b/modules/yugabytedb/src/test/java/org/testcontainers/jdbc/yugabytedb/YugabyteYSQLJDBCDriverTest.java new file mode 100644 index 00000000000..510a71f26d3 --- /dev/null +++ b/modules/yugabytedb/src/test/java/org/testcontainers/jdbc/yugabytedb/YugabyteYSQLJDBCDriverTest.java @@ -0,0 +1,25 @@ +package org.testcontainers.jdbc.yugabytedb; + +import java.util.EnumSet; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import static java.util.Arrays.asList; + +/** + * YugabyteDB YSQL API JDBC connectivity driver test class + * + * @author srinivasa-vasu + */ +@RunWith(Parameterized.class) +public class YugabyteYSQLJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable