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 TiDB module #5511

Merged
merged 30 commits into from Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4de91a2
[Update] Support TiDB
Icemap Jun 20, 2022
1d0d360
[Update] TiDB documents
Icemap Jun 21, 2022
5248596
Merge branch 'master' into feat-tidb
Icemap Jun 21, 2022
94232f6
Merge branch 'master' into feat-tidb
Icemap Jul 20, 2022
e36bdb0
[Update] correct author and remove deprecated annotation
Icemap Jul 20, 2022
1fb60bd
Merge branch 'feat-tidb' of github.com:Icemap/testcontainers-java int…
Icemap Jul 20, 2022
bc8307a
[Update] format TiDB code and adapt Java 17
Icemap Jul 21, 2022
d813e4e
[Delete] no used file: my.cnf
Icemap Jul 21, 2022
e987819
[Debug] rollback changed mysql meta file
Icemap Jul 21, 2022
b32059c
[Update] consistent with the other containers by using image string
Icemap Jul 21, 2022
baa25c1
[Delete] close LogManager code
Icemap Jul 21, 2022
f1bdcfe
[Rollback] adapt props for Java 17
Icemap Jul 21, 2022
c31114a
[Rollback] org.testcontainers.containers.JdbcDatabaseContainerProvider
Icemap Jul 22, 2022
84f8d0d
[Delete] r2dbc on test scope
Icemap Jul 22, 2022
6bb2086
[Delete] auto service dependencied
Icemap Jul 22, 2022
a0435ad
Merge branch 'master' into feat-tidb
Icemap Jul 27, 2022
d4e8554
Update modules/tidb/src/main/java/org/testcontainers/containers/TiDBC…
kiview Aug 4, 2022
7eb23eb
Update modules/tidb/src/main/java/org/testcontainers/containers/TiDBC…
kiview Aug 4, 2022
e4522e7
Make `NAME` and `DOCKER_IMAGE_NAME` package-protected
kiview Aug 4, 2022
eb72033
Add TiDB module to GH automation
kiview Aug 4, 2022
c6ff27d
[Update] init_mysql.sql -> init_tidb.sql
Icemap Aug 4, 2022
8f692be
Update modules/tidb/src/main/java/org/testcontainers/containers/TiDBC…
eddumelendez Aug 4, 2022
c0df0f0
Fix spotless
eddumelendez Aug 4, 2022
cc56713
Merge branch 'master' into feat-tidb
Icemap Aug 5, 2022
c16aa77
[Rename] org.testcontainers.containers -> org.testcontainers.tidb
Icemap Aug 12, 2022
bc5c9a5
Merge branch 'master' into feat-tidb
Icemap Aug 12, 2022
5d4355b
[Unit Test] static import only from classes and interfaces
Icemap Aug 12, 2022
a25384a
Merge branch 'feat-tidb' of github.com:Icemap/testcontainers-java int…
Icemap Aug 12, 2022
6f2c6a7
[Debug] import wrong package
Icemap Aug 12, 2022
680075b
[Debug] spotlessApply
Icemap Aug 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Expand Up @@ -43,6 +43,7 @@ body:
- RabbitMQ
- Selenium
- Solr
- TiDB
- ToxiProxy
- Trino
- Vault
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/enhancement.yaml
Expand Up @@ -43,6 +43,7 @@ body:
- RabbitMQ
- Selenium
- Solr
- TiDB
- ToxiProxy
- Trino
- Vault
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yaml
Expand Up @@ -43,6 +43,7 @@ body:
- RabbitMQ
- Selenium
- Solr
- TiDB
- ToxiProxy
- Trino
- Vault
Expand Down
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -197,6 +197,11 @@ updates:
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/tidb"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/toxiproxy"
schedule:
Expand Down
2 changes: 2 additions & 0 deletions .github/labeler.yml
Expand Up @@ -73,6 +73,8 @@
- modules/solr/*
"modules/spock":
- modules/spock/*
"modules/tidb":
- modules/tidb/*
"modules/toxiproxy":
- modules/toxiproxy/*
"modules/trino":
Expand Down
4 changes: 4 additions & 0 deletions docs/modules/databases/jdbc.md
Expand Up @@ -51,6 +51,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database

`jdbc:tc:cockroach:v21.2.3:///databasename`

#### Using TiDB

`jdbc:tc:tidb:v6.1.0:///databasename`

### Using a classpath init script

Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows:
Expand Down
25 changes: 25 additions & 0 deletions docs/modules/databases/tidb.md
@@ -0,0 +1,25 @@
# TiDB Module

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

## Adding this module to your project dependencies

Add the following dependency to your `pom.xml`/`build.gradle` file:

=== "Gradle"
```groovy
testImplementation "org.testcontainers:tidb:{{latest_version}}"
```

=== "Maven"
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>tidb</artifactId>
<version>{{latest_version}}</version>
<scope>test</scope>
</dependency>
```

!!! hint
Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency.
1 change: 1 addition & 0 deletions mkdocs.yml
Expand Up @@ -63,6 +63,7 @@ nav:
- modules/databases/orientdb.md
- modules/databases/postgres.md
- modules/databases/presto.md
- modules/databases/tidb.md
- modules/databases/trino.md
- modules/azure.md
- modules/docker_compose.md
Expand Down
10 changes: 10 additions & 0 deletions modules/tidb/build.gradle
@@ -0,0 +1,10 @@
description = "Testcontainers :: JDBC :: TiDB"

dependencies {
api project(':jdbc')

testImplementation project(':jdbc-test')
testImplementation 'mysql:mysql-connector-java:8.0.29'

compileOnly 'org.jetbrains:annotations:23.0.0'
}
5 changes: 5 additions & 0 deletions modules/tidb/sql/init_mysql.sql
@@ -0,0 +1,5 @@
CREATE TABLE bar (
foo VARCHAR(255)
);

INSERT INTO bar (foo) VALUES ('hello world');
@@ -0,0 +1,125 @@
package org.testcontainers.containers;
Icemap marked this conversation as resolved.
Show resolved Hide resolved

import org.jetbrains.annotations.NotNull;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.utility.DockerImageName;

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

/**
* Testcontainers implementation for TiDB.
*
* @author Icemap
*/
public class TiDBContainer extends JdbcDatabaseContainer<TiDBContainer> {

static final String NAME = "tidb";

static final String DOCKER_IMAGE_NAME = "pingcap/tidb";

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME);

private static final Integer TIDB_PORT = 4000;

private static final int REST_API_PORT = 10080;

private String databaseName = "test";

private String username = "root";

private String password = "";

public TiDBContainer(String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

public TiDBContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);

addExposedPorts(TIDB_PORT, REST_API_PORT);
Copy link
Member

Choose a reason for hiding this comment

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

both ports should be exposed? or only 4000?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both ports should be exposed. TIDB_PORT is 4000, and REST_API_PORT is 10080.


waitingFor(
new HttpWaitStrategy()
.forPath("/status")
.forPort(REST_API_PORT)
.forStatusCode(200)
.withStartupTimeout(Duration.ofMinutes(1))
);
}

@NotNull
@Override
protected Set<Integer> getLivenessCheckPorts() {
return new HashSet<>(getMappedPort(TIDB_PORT));
}

@Override
public String getDriverClassName() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return "com.mysql.cj.jdbc.Driver";
} catch (ClassNotFoundException e) {
return "com.mysql.jdbc.Driver";
}
}

@Override
public String getJdbcUrl() {
String additionalUrlParams = constructUrlParameters("?", "&");
return "jdbc:mysql://" + getHost() + ":" + getMappedPort(TIDB_PORT) + "/" + databaseName + additionalUrlParams;
}

@Override
protected String constructUrlForConnection(String queryString) {
String url = super.constructUrlForConnection(queryString);

if (!url.contains("useSSL=")) {
String separator = url.contains("?") ? "&" : "?";
url = url + separator + "useSSL=false";
}

if (!url.contains("allowPublicKeyRetrieval=")) {
url = url + "&allowPublicKeyRetrieval=true";
}

return url;
}

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

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

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

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

@Override
public TiDBContainer withDatabaseName(final String databaseName) {
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}

@Override
public TiDBContainer withUsername(final String username) {
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}

@Override
public TiDBContainer withPassword(final String password) {
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}
}
@@ -0,0 +1,30 @@
package org.testcontainers.containers;

import org.testcontainers.utility.DockerImageName;

/**
* Factory for TiDB containers.
*/
public class TiDBContainerProvider extends JdbcDatabaseContainerProvider {

private static final String DEFAULT_TAG = "v6.1.0";

@Override
public boolean supports(String databaseType) {
return databaseType.equals(TiDBContainer.NAME);
}

@Override
public JdbcDatabaseContainer newInstance() {
return newInstance(DEFAULT_TAG);
}

@Override
public JdbcDatabaseContainer newInstance(String tag) {
if (tag != null) {
return new TiDBContainer(DockerImageName.parse(TiDBContainer.DOCKER_IMAGE_NAME).withTag(tag));
} else {
return newInstance();
}
}
}
@@ -0,0 +1 @@
org.testcontainers.containers.TiDBContainerProvider
@@ -0,0 +1,8 @@
package org.testcontainers;

import org.testcontainers.utility.DockerImageName;

public class TiDBTestImages {

public static final DockerImageName TIDB_IMAGE = DockerImageName.parse("pingcap/tidb:v6.1.0");
}
@@ -0,0 +1,19 @@
package org.testcontainers.jdbc.tidb;

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.testcontainers.jdbc.AbstractJDBCDriverTest;

import java.util.Arrays;
import java.util.EnumSet;

@RunWith(Parameterized.class)
public class TiDBJDBCDriverTest extends AbstractJDBCDriverTest {

@Parameterized.Parameters(name = "{index} - {0}")
public static Iterable<Object[]> data() {
return Arrays.asList(
new Object[][] { { "jdbc:tc:tidb://hostname/databasename", EnumSet.noneOf(Options.class) } }
);
}
}
@@ -0,0 +1,56 @@
package org.testcontainers.junit.tidb;

import org.junit.Test;
import org.testcontainers.TiDBTestImages;
import org.testcontainers.containers.TiDBContainer;
import org.testcontainers.db.AbstractContainerDatabaseTest;

import java.sql.ResultSet;
import java.sql.SQLException;

import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals;

public class SimpleTiDBTest extends AbstractContainerDatabaseTest {

@Test
public void testSimple() throws SQLException {
try (TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE)) {
tidb.start();

ResultSet resultSet = performQuery(tidb, "SELECT 1");

int resultSetInt = resultSet.getInt(1);
assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
}
}

@Test
public void testExplicitInitScript() throws SQLException {
try (
TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withInitScript("somepath/init_tidb.sql")
) { // TiDB is expected to be compatible with MySQL
tidb.start();

ResultSet resultSet = performQuery(tidb, "SELECT foo FROM bar");

String firstColumnValue = resultSet.getString(1);
assertEquals("Value from init script should equal real value", "hello world", firstColumnValue);
}
}

@Test
public void testWithAdditionalUrlParamInJdbcUrl() {
TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withUrlParam("sslmode", "disable");

try {
tidb.start();
String jdbcUrl = tidb.getJdbcUrl();
assertThat(jdbcUrl, containsString("?"));
assertThat(jdbcUrl, containsString("sslmode=disable"));
} finally {
tidb.stop();
}
}
}
16 changes: 16 additions & 0 deletions modules/tidb/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="DEBUG"/>
</configuration>
16 changes: 16 additions & 0 deletions modules/tidb/src/test/resources/somepath/init_tidb.sql
@@ -0,0 +1,16 @@
CREATE TABLE bar (
foo VARCHAR(255)
);

SELECT "a /* string literal containing comment characters like -- here";
SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here";
SELECT * from `bar`;

-- What about a line comment containing imbalanced string delimiters? "

/* or a block comment
containing imbalanced string delimiters?
' "
*/

INSERT INTO bar (foo) /* ; */ VALUES ('hello world');