Skip to content

Commit 0210dcf

Browse files
committed
feat: add oceanbase-ce module
1 parent 36c8727 commit 0210dcf

File tree

17 files changed

+387
-0
lines changed

17 files changed

+387
-0
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ body:
3838
- MySQL
3939
- Neo4j
4040
- NGINX
41+
- OceanBase CE
4142
- Oracle Free
4243
- Oracle XE
4344
- OrientDB

.github/ISSUE_TEMPLATE/enhancement.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ body:
3838
- MySQL
3939
- Neo4j
4040
- NGINX
41+
- OceanBase CE
4142
- Oracle Free
4243
- Oracle XE
4344
- OrientDB

.github/ISSUE_TEMPLATE/feature.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ body:
3838
- MySQL
3939
- Neo4j
4040
- NGINX
41+
- OceanBase CE
4142
- Oracle Free
4243
- Oracle XE
4344
- OrientDB

.github/dependabot.yml

+5
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ updates:
194194
schedule:
195195
interval: "weekly"
196196
open-pull-requests-limit: 10
197+
- package-ecosystem: "gradle"
198+
directory: "/modules/oceanbase-ce"
199+
schedule:
200+
interval: "weekly"
201+
open-pull-requests-limit: 10
197202
- package-ecosystem: "gradle"
198203
directory: "/modules/oracle-free"
199204
schedule:

.github/labeler.yml

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
- changed-files:
120120
- any-glob-to-any-file:
121121
- modules/nginx/**/*
122+
"modules/oceanbase-ce":
123+
- changed-files:
124+
- any-glob-to-any-file:
125+
- modules/oceanbase-ce/**/*
122126
"modules/oracle":
123127
- changed-files:
124128
- any-glob-to-any-file:

docs/modules/databases/jdbc.md

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database
5555

5656
`jdbc:tc:sqlserver:2017-CU12:///databasename`
5757

58+
#### Using OceanBase
59+
60+
`jdbc:tc:oceanbase:4.1.0.0:///databasename`
61+
5862
#### Using Oracle
5963

6064
`jdbc:tc:oracle:21-slim-faststart:///databasename`

docs/modules/databases/oceanbasece.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# OceanBase-CE Module
2+
3+
See [Database containers](./index.md) for documentation and usage that is common to all relational database container types.
4+
5+
## Adding this module to your project dependencies
6+
7+
Add the following dependency to your `pom.xml`/`build.gradle` file:
8+
9+
=== "Gradle"
10+
```groovy
11+
testImplementation "org.testcontainers:oceanbase-ce:{{latest_version}}"
12+
```
13+
14+
=== "Maven"
15+
```xml
16+
<dependency>
17+
<groupId>org.testcontainers</groupId>
18+
<artifactId>oceanbase-ce</artifactId>
19+
<version>{{latest_version}}</version>
20+
<scope>test</scope>
21+
</dependency>
22+
```
23+
24+
!!! hint
25+
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.

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ nav:
6464
- modules/databases/mssqlserver.md
6565
- modules/databases/mysql.md
6666
- modules/databases/neo4j.md
67+
- modules/databases/oceanbasece.md
6768
- modules/databases/oraclefree.md
6869
- modules/databases/oraclexe.md
6970
- modules/databases/orientdb.md

modules/oceanbase-ce/build.gradle

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
description = "Testcontainers :: JDBC :: OceanBase CE"
2+
3+
dependencies {
4+
api project(':jdbc')
5+
6+
testImplementation project(':jdbc-test')
7+
testRuntimeOnly 'mysql:mysql-connector-java:8.0.33'
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package org.testcontainers.containers;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
import org.testcontainers.utility.DockerImageName;
5+
6+
/**
7+
* Testcontainers implementation for OceanBase.
8+
* <p>
9+
* Supported image: {@code oceanbase/oceanbase-ce}
10+
* <p>
11+
* Exposed ports:
12+
* <ul>
13+
* <li>SQL: 2881</li>
14+
* <li>RPC: 2882</li>
15+
* </ul>
16+
*/
17+
public class OceanBaseContainer extends JdbcDatabaseContainer<OceanBaseContainer> {
18+
19+
static final String NAME = "oceanbase";
20+
21+
static final String DOCKER_IMAGE_NAME = "oceanbase/oceanbase-ce";
22+
23+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME);
24+
25+
private static final Integer SQL_PORT = 2881;
26+
private static final Integer RPC_PORT = 2882;
27+
28+
private static final String SYSTEM_TENANT_NAME = "sys";
29+
private static final String DEFAULT_TEST_TENANT_NAME = "test";
30+
private static final String DEFAULT_USERNAME = "root";
31+
private static final String DEFAULT_PASSWORD = "";
32+
private static final String DEFAULT_DATABASE_NAME = "test";
33+
34+
private boolean enableFastboot;
35+
private String mode;
36+
private String tenantName = DEFAULT_TEST_TENANT_NAME;
37+
38+
public OceanBaseContainer(String dockerImageName) {
39+
this(DockerImageName.parse(dockerImageName));
40+
}
41+
42+
public OceanBaseContainer(DockerImageName dockerImageName) {
43+
super(dockerImageName);
44+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
45+
46+
addExposedPorts(SQL_PORT, RPC_PORT);
47+
}
48+
49+
@Override
50+
public Integer getMappedPort(int originalPort) {
51+
return "host".equals(getNetworkMode()) ? originalPort : super.getMappedPort(originalPort);
52+
}
53+
54+
@Override
55+
public String getDriverClassName() {
56+
return "com.mysql.cj.jdbc.Driver";
57+
}
58+
59+
@Override
60+
public String getJdbcUrl() {
61+
return getJdbcUrl(DEFAULT_DATABASE_NAME);
62+
}
63+
64+
public String getJdbcUrl(String databaseName) {
65+
String additionalUrlParams = constructUrlParameters("?", "&");
66+
return "jdbc:mysql://" + getHost() + ":" + getMappedPort(SQL_PORT) + "/" + databaseName + additionalUrlParams;
67+
}
68+
69+
@Override
70+
public String getDatabaseName() {
71+
return DEFAULT_DATABASE_NAME;
72+
}
73+
74+
@Override
75+
public String getUsername() {
76+
return DEFAULT_USERNAME + "@" + tenantName;
77+
}
78+
79+
@Override
80+
public String getPassword() {
81+
return DEFAULT_PASSWORD;
82+
}
83+
84+
@Override
85+
protected String getTestQueryString() {
86+
return "SELECT 1";
87+
}
88+
89+
/**
90+
* Enable fastboot.
91+
*
92+
* @return this
93+
*/
94+
public OceanBaseContainer enableFastboot() {
95+
this.enableFastboot = true;
96+
return self();
97+
}
98+
99+
/**
100+
* Set the deployment mode, see <a href="https://hub.docker.com/r/oceanbase/oceanbase-ce">Docker Hub</a> for more details.
101+
*
102+
* @param mode the deployment mode
103+
* @return this
104+
*/
105+
public OceanBaseContainer withMode(String mode) {
106+
this.mode = mode;
107+
return self();
108+
}
109+
110+
/**
111+
* Set the non-system tenant to be created for testing.
112+
*
113+
* @param tenantName the name of tenant to be created
114+
* @return this
115+
*/
116+
public OceanBaseContainer withTenant(String tenantName) {
117+
if (StringUtils.isEmpty(tenantName)) {
118+
throw new IllegalArgumentException("Tenant name cannot be null or empty");
119+
}
120+
if (SYSTEM_TENANT_NAME.equals(tenantName)) {
121+
throw new IllegalArgumentException("Tenant name cannot be " + SYSTEM_TENANT_NAME);
122+
}
123+
this.tenantName = tenantName;
124+
return self();
125+
}
126+
127+
@Override
128+
protected void configure() {
129+
if (StringUtils.isNotBlank(mode)) {
130+
withEnv("MODE", mode);
131+
}
132+
if (enableFastboot) {
133+
withEnv("FASTBOOT", "true");
134+
}
135+
if (!DEFAULT_TEST_TENANT_NAME.equals(tenantName)) {
136+
withEnv("OB_TENANT_NAME", tenantName);
137+
}
138+
}
139+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.testcontainers.containers;
2+
3+
import org.testcontainers.utility.DockerImageName;
4+
5+
/**
6+
* Factory for OceanBase containers.
7+
*/
8+
public class OceanBaseContainerProvider extends JdbcDatabaseContainerProvider {
9+
10+
private static final String DEFAULT_TAG = "4.2.1_bp3";
11+
12+
@Override
13+
public boolean supports(String databaseType) {
14+
return databaseType.equals(OceanBaseContainer.NAME);
15+
}
16+
17+
@Override
18+
public JdbcDatabaseContainer newInstance() {
19+
return newInstance(DEFAULT_TAG);
20+
}
21+
22+
@Override
23+
public JdbcDatabaseContainer newInstance(String tag) {
24+
if (tag != null) {
25+
return new OceanBaseContainer(DockerImageName.parse(OceanBaseContainer.DOCKER_IMAGE_NAME).withTag(tag));
26+
} else {
27+
return newInstance();
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.testcontainers.containers.OceanBaseContainerProvider
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.testcontainers;
2+
3+
import org.testcontainers.utility.DockerImageName;
4+
5+
public class OceanBaseTestImages {
6+
7+
public static final DockerImageName OCEANBASE_CE_IMAGE = DockerImageName.parse("oceanbase/oceanbase-ce:4.2.1_bp3");
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.testcontainers.jdbc.oceanbase;
2+
3+
import org.junit.runner.RunWith;
4+
import org.junit.runners.Parameterized;
5+
import org.testcontainers.jdbc.AbstractJDBCDriverTest;
6+
7+
import java.util.Arrays;
8+
import java.util.EnumSet;
9+
10+
@RunWith(Parameterized.class)
11+
public class OceanBaseJdbcDriverTest extends AbstractJDBCDriverTest {
12+
13+
@Parameterized.Parameters(name = "{index} - {0}")
14+
public static Iterable<Object[]> data() {
15+
return Arrays.asList(
16+
new Object[][] { { "jdbc:tc:oceanbase://hostname/databasename", EnumSet.noneOf(Options.class) } }
17+
);
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.testcontainers.junit.oceanbase;
2+
3+
import org.junit.Test;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.testcontainers.OceanBaseTestImages;
7+
import org.testcontainers.containers.OceanBaseContainer;
8+
import org.testcontainers.containers.output.Slf4jLogConsumer;
9+
import org.testcontainers.db.AbstractContainerDatabaseTest;
10+
11+
import java.sql.ResultSet;
12+
import java.sql.SQLException;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
public class SimpleOceanBaseTest extends AbstractContainerDatabaseTest {
17+
18+
private static final Logger logger = LoggerFactory.getLogger(SimpleOceanBaseTest.class);
19+
20+
@Test
21+
public void testSimple() throws SQLException {
22+
try (
23+
OceanBaseContainer container = new OceanBaseContainer(OceanBaseTestImages.OCEANBASE_CE_IMAGE)
24+
.withMode("slim")
25+
.enableFastboot()
26+
.withLogConsumer(new Slf4jLogConsumer(logger))
27+
) {
28+
container.start();
29+
30+
ResultSet resultSet = performQuery(container, "SELECT 1");
31+
int resultSetInt = resultSet.getInt(1);
32+
assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
33+
assertHasCorrectExposedAndLivenessCheckPorts(container);
34+
}
35+
}
36+
37+
@Test
38+
public void testExplicitInitScript() throws SQLException {
39+
try (
40+
OceanBaseContainer container = new OceanBaseContainer(OceanBaseTestImages.OCEANBASE_CE_IMAGE)
41+
.withMode("slim")
42+
.enableFastboot()
43+
.withInitScript("init.sql")
44+
.withLogConsumer(new Slf4jLogConsumer(logger))
45+
) {
46+
container.start();
47+
48+
ResultSet resultSet = performQuery(container, "SELECT foo FROM bar");
49+
String firstColumnValue = resultSet.getString(1);
50+
assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world");
51+
}
52+
}
53+
54+
@Test
55+
public void testWithAdditionalUrlParamInJdbcUrl() {
56+
try (
57+
OceanBaseContainer container = new OceanBaseContainer(OceanBaseTestImages.OCEANBASE_CE_IMAGE)
58+
.withMode("slim")
59+
.enableFastboot()
60+
.withUrlParam("useSSL", "false")
61+
.withLogConsumer(new Slf4jLogConsumer(logger))
62+
) {
63+
container.start();
64+
65+
String jdbcUrl = container.getJdbcUrl();
66+
assertThat(jdbcUrl).contains("?");
67+
assertThat(jdbcUrl).contains("useSSL=false");
68+
}
69+
}
70+
71+
private void assertHasCorrectExposedAndLivenessCheckPorts(OceanBaseContainer container) {
72+
int sqlPort = 2881;
73+
int rpcPort = 2882;
74+
75+
assertThat(container.getExposedPorts()).containsExactlyInAnyOrder(sqlPort, rpcPort);
76+
assertThat(container.getLivenessCheckPortNumbers())
77+
.containsExactlyInAnyOrder(container.getMappedPort(sqlPort), container.getMappedPort(rpcPort));
78+
}
79+
}

0 commit comments

Comments
 (0)