Add TiDB as supported db

Closes #41455

Signed-off-by: Dennis Kniep <kniepdennis@gmail.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Dennis Kniep
2025-08-14 09:27:21 +02:00
committed by GitHub
parent a47c69c370
commit d74a10d87a
22 changed files with 245 additions and 7 deletions

View File

@@ -693,7 +693,7 @@ jobs:
timeout-minutes: 75
strategy:
matrix:
db: [postgres, mysql, oracle, mssql, mariadb]
db: [postgres, mysql, oracle, mssql, mariadb, tidb]
fail-fast: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

View File

@@ -53,6 +53,23 @@ Stop MySQl:
docker rm -f mariadb
TiDB
-----
The simplest way to test with TiDB is to use the official [TiDB docker image](https://hub.docker.com/r/pingcap/tidb).
Start TiDB:
docker run --name tidb -p 4000:4000 -d pingcap/tidb:v8.5.2
Run tests:
mvn install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' tidb`:4000/test -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=root -Dkeycloak.connectionsJpa.password=
Stop TiDB:
docker rm -f tidb
Using built-in profiles to run database tests using docker containers
-------

View File

@@ -55,6 +55,17 @@
</createIndex>
</changeSet>
<changeSet author="keycloak" id="client-attributes-string-accomodation-fixed-pre-drop-index">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<and>
<not>
<changeSetExecuted id="client-attributes-string-accomodation-fixed" author="keycloak" changeLogFile="META-INF/jpa-changelog-20.0.0.xml"/>
</not>
</and>
</preConditions>
<dropIndex tableName="CLIENT_ATTRIBUTES" indexName="IDX_CLIENT_ATT_BY_NAME_VALUE" />
</changeSet>
<changeSet author="keycloak" id="client-attributes-string-accomodation-fixed">
<addColumn tableName="CLIENT_ATTRIBUTES">
<column name="VALUE_NEW" type="NCLOB" />
@@ -68,4 +79,19 @@
<renameColumn tableName="CLIENT_ATTRIBUTES" oldColumnName="VALUE_NEW" newColumnName="VALUE" columnDataType="NCLOB"/>
</changeSet>
<changeSet author="keycloak" id="client-attributes-string-accomodation-fixed-post-create-index">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<and>
<not>
<changeSetExecuted id="client-attributes-string-accomodation-fixed" author="keycloak" changeLogFile="META-INF/jpa-changelog-20.0.0.xml"/>
</not>
</and>
</preConditions>
<createIndex tableName="CLIENT_ATTRIBUTES" indexName="IDX_CLIENT_ATT_BY_NAME_VALUE">
<column name="NAME" type="VARCHAR(255)"/>
<column name="VALUE(255)" valueComputed="VALUE(255)" />
</createIndex>
</changeSet>
</databaseChangeLog>

View File

@@ -151,6 +151,8 @@
<twitter4j.version>4.1.2</twitter4j.version>
<!-- Databases - also published to db.adoc as "Tested Version" -->
<tidb.version>v8.5.2</tidb.version>
<tidb.container>mirror.gcr.io/pingcap/tidb:${tidb.version}</tidb.container>
<mysql.version>8.4</mysql.version>
<mysql.container>mirror.gcr.io/mysql:${mysql.version}</mysql.container>
<mysql-jdbc.version>8.3.0</mysql-jdbc.version>

View File

@@ -190,6 +190,7 @@
<kc.db.mysql.container.image>${mysql.container}</kc.db.mysql.container.image>
<kc.infinispan.container.image>quay.io/infinispan/server:${infinispan.version}</kc.infinispan.container.image>
<kc.db.mssql.container.image>${mssql.container}</kc.db.mssql.container.image>
<kc.db.tidb.container.image>${tidb.container}</kc.db.tidb.container.image>
</systemPropertyVariables>
</configuration>
</plugin>

View File

@@ -102,6 +102,10 @@
<groupId>org.testcontainers</groupId>
<artifactId>mssqlserver</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>tidb</artifactId>
</dependency>
<!-- JDBC Drivers -->
<dependency>

View File

@@ -26,7 +26,7 @@ import org.testcontainers.containers.MSSQLServerContainer;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.images.PullPolicy;
import org.testcontainers.tidb.TiDBContainer;
import org.testcontainers.utility.DockerImageName;
public class DatabaseContainer {
@@ -95,6 +95,7 @@ public class DatabaseContainer {
String MARIADB_IMAGE = System.getProperty("kc.db.mariadb.container.image");
String MYSQL_IMAGE = System.getProperty("kc.db.mysql.container.image");
String MSSQL_IMAGE = System.getProperty("kc.db.mssql.container.image");
String TIDB_IMAGE = System.getProperty("kc.db.tidb.container.image");
switch (alias) {
case "postgres":
@@ -109,6 +110,9 @@ public class DatabaseContainer {
case "mssql":
DockerImageName MSSQL = DockerImageName.parse(MSSQL_IMAGE).asCompatibleSubstituteFor("sqlserver");
return configureJdbcContainer(new MSSQLServerContainer<>(MSSQL));
case "tidb":
DockerImageName TIDB = DockerImageName.parse(TIDB_IMAGE).asCompatibleSubstituteFor("pingcap/tidb");
return configureJdbcContainer(new TiDBContainer(TIDB));
default:
throw new RuntimeException("Unsupported database: " + alias);
}

View File

@@ -385,6 +385,7 @@ Valid values:
| mysql | MySQL test container |
| oracle | Oracle test container |
| postgres | PostgreSQL test container |
| tidb | TiDb test container |
Configuration:

View File

@@ -75,6 +75,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-tidb</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-oracle</artifactId>

View File

@@ -3,3 +3,4 @@ postgres.container=${postgresql.container}
mariadb.container=${mariadb.container}
mssql.container=${mssql.container}
oracle.container=${oracledb.container}
tidb.container=${tidb.container}

45
test-framework/db-tidb/pom.xml Executable file
View File

@@ -0,0 +1,45 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-test-framework-parent</artifactId>
<groupId>org.keycloak.testframework</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-test-framework-db-tidb</artifactId>
<name>Keycloak Test Framework - TiDB support</name>
<packaging>jar</packaging>
<description>TiDB support for Keycloak Test Framework</description>
<dependencies>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>tidb</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,15 @@
package org.keycloak.testframework.database;
public class TiDBDatabaseSupplier extends AbstractDatabaseSupplier {
@Override
public String getAlias() {
return "tidb";
}
@Override
TestDatabase getTestDatabase() {
return new TiDBTestDatabase();
}
}

View File

@@ -0,0 +1,69 @@
package org.keycloak.testframework.database;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.tidb.TiDBContainer;
import org.testcontainers.utility.DockerImageName;
class TiDBTestDatabase extends AbstractContainerTestDatabase {
private static final Logger LOGGER = Logger.getLogger(TiDBTestDatabase.class);
public static final String NAME = "tidb";
@Override
public JdbcDatabaseContainer<?> createContainer() {
return new TiDBContainer(DockerImageName.parse(DatabaseProperties.getContainerImageName(NAME)).asCompatibleSubstituteFor("pingcap/tidb")){
@Override
public TiDBContainer withDatabaseName(String databaseName) {
if(StringUtils.equals(this.getDatabaseName(), databaseName)) {
return this;
}
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}
@Override
public TiDBContainer withUsername(String username) {
if(StringUtils.equals(this.getUsername(), username)) {
return this;
}
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}
@Override
public TiDBContainer withPassword(String password) {
if(StringUtils.equals(this.getPassword(), password)) {
return this;
}
throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
}
}.withExposedPorts(4000);
}
@Override
public String getDatabaseVendor() {
return "mysql";
}
@Override
public Logger getLogger() {
return LOGGER;
}
@Override
public String getDatabase() {
return "test";
}
@Override
public String getUsername() {
return "root";
}
@Override
public String getPassword() {
return "";
}
}

View File

@@ -0,0 +1,14 @@
package org.keycloak.testframework.database;
import org.keycloak.testframework.TestFrameworkExtension;
import org.keycloak.testframework.injection.Supplier;
import java.util.List;
public class TiDBTestFrameworkExtension implements TestFrameworkExtension {
@Override
public List<Supplier<?, ?>> suppliers() {
return List.of(new TiDBDatabaseSupplier());
}
}

View File

@@ -0,0 +1 @@
org.keycloak.testframework.database.TiDBTestFrameworkExtension

View File

@@ -64,6 +64,10 @@
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-mysql</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-tidb</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-oracle</artifactId>

View File

@@ -41,6 +41,7 @@
<module>db-mysql</module>
<module>db-oracle</module>
<module>db-postgres</module>
<module>db-tidb</module>
<module>email-server</module>
<module>examples</module>
<module>oauth</module>

View File

@@ -76,6 +76,10 @@
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-postgres</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-db-tidb</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-email-server</artifactId>

View File

@@ -19,7 +19,8 @@ import org.keycloak.testframework.realm.RoleConfigBuilder;
@KeycloakIntegrationTest(config = CaseSensitiveSchemaTest.KeycloakConfig.class)
// MSSQL does not support setting the default schema per session
@DisabledForDatabases("mssql")
// TiDb does not support setting the default schema per session.
@DisabledForDatabases({"mssql", "tidb"})
public class CaseSensitiveSchemaTest {
@InjectTestDatabase(lifecycle = LifeCycle.CLASS, config = DatabaseConfigurator.class)
TestDatabase db;

View File

@@ -12,8 +12,9 @@ import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
@KeycloakIntegrationTest(config = PreserveSchemaCaseLiquibaseTest.KeycloakConfig.class)
// MSSQL does not support setting the default schema per session.
// TiDb does not support setting the default schema per session.
// Oracle image does not support configuring user/databases with '-'
@DisabledForDatabases({ "mssql", "oracle" })
@DisabledForDatabases({ "mssql", "oracle", "tidb" })
public class PreserveSchemaCaseLiquibaseTest extends CaseSensitiveSchemaTest {
@InjectTestDatabase(lifecycle = LifeCycle.CLASS, config = DatabaseConfigurator.class)

View File

@@ -416,6 +416,29 @@
<docker.database.wait-for-log-regex>(?si)Ready for start up.*ready [^\n]{0,30}connections</docker.database.wait-for-log-regex>
</properties>
</profile>
<profile>
<id>db-tidb</id>
<properties>
<keycloak.storage.connections.vendor>mysql</keycloak.storage.connections.vendor>
<keycloak.connectionsJpa.driver>com.mysql.jdbc.Driver</keycloak.connectionsJpa.driver>
<keycloak.connectionsJpa.database>test</keycloak.connectionsJpa.database>
<keycloak.connectionsJpa.user>root</keycloak.connectionsJpa.user>
<keycloak.connectionsJpa.password></keycloak.connectionsJpa.password>
<keycloak.connectionsJpa.url>jdbc:mysql://${auth.server.db.host}:${docker.database.port}/${keycloak.connectionsJpa.database}</keycloak.connectionsJpa.url>
<!-- JDBC properties point to "default" JDBC driver for the particular DB -->
<!-- For EAP testing, it is recommended to override those with system properties pointing to GAV of more appropriate JDBC driver -->
<!-- for the particular EAP version -->
<jdbc.mvn.groupId>com.mysql</jdbc.mvn.groupId>
<jdbc.mvn.artifactId>mysql-connector-j</jdbc.mvn.artifactId>
<jdbc.mvn.version>${mysql-jdbc.version}</jdbc.mvn.version>
<docker.database.image>${tidb.container}</docker.database.image>
<docker.database.port>4000</docker.database.port>
<docker.database.skip>false</docker.database.skip>
<docker.database.cmd>start</docker.database.cmd>
<docker.database.wait-for-log-regex>server is running MySQL protocol</docker.database.wait-for-log-regex>
</properties>
</profile>
<profile>
<id>db-postgres</id>
<properties>

View File

@@ -59,8 +59,6 @@
</requireProperty>
<requireProperty>
<property>keycloak.connectionsJpa.password</property>
<regex>^(?!\s*$).+</regex>
<regexMessage>"keycloak.connectionsJpa.password" property cannot be empty string!</regexMessage>
</requireProperty>
<requireProperty>
<property>keycloak.connectionsJpa.url</property>