Adding database suppliers (#31073)

* Closes #30616. Added database suppliers.

Signed-off-by: Miquel Simon <msimonma@redhat.com>
Co-authored-by: stianst <stianst@gmail.com>
This commit is contained in:
Miquel Simon 2024-07-16 09:28:43 +02:00 committed by GitHub
parent 2140e573f2
commit 73247f585a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 250 additions and 12 deletions

View file

@ -0,0 +1,19 @@
package org.keycloak.test.framework;
import org.keycloak.test.framework.database.DatabaseConfig;
import org.keycloak.test.framework.injection.LifeCycle;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface KeycloakTestDatabase {
Class<? extends DatabaseConfig> config() default DatabaseConfig.class;
LifeCycle lifecycle() default LifeCycle.GLOBAL;
}

View file

@ -0,0 +1,35 @@
package org.keycloak.test.framework.database;
import org.keycloak.test.framework.KeycloakTestDatabase;
import org.keycloak.test.framework.injection.InstanceWrapper;
import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.Supplier;
public abstract class AbstractDatabaseSupplier implements Supplier<TestDatabase, KeycloakTestDatabase> {
@Override
public Class<KeycloakTestDatabase> getAnnotationClass() {
return KeycloakTestDatabase.class;
}
@Override
public Class<TestDatabase> getValueType() {
return TestDatabase.class;
}
@Override
public InstanceWrapper<TestDatabase, KeycloakTestDatabase> getValue(Registry registry, KeycloakTestDatabase annotation) {
TestDatabase testDatabase = getTestDatabase();
testDatabase.start();
return new InstanceWrapper<>(this, annotation, testDatabase, LifeCycle.GLOBAL);
}
@Override
public boolean compatible(InstanceWrapper<TestDatabase, KeycloakTestDatabase> a, InstanceWrapper<TestDatabase, KeycloakTestDatabase> b) {
return true;
}
abstract TestDatabase getTestDatabase();
}

View file

@ -0,0 +1,76 @@
package org.keycloak.test.framework.database;
import java.util.HashMap;
import java.util.Map;
public class DatabaseConfig {
private String vendor;
private String containerImage;
private String urlHost;
private String username;
private String password;
public String getVendor() {
return vendor;
}
public DatabaseConfig vendor(String vendor) {
this.vendor = vendor;
return this;
}
public String getContainerImage() {
return containerImage;
}
public DatabaseConfig containerImage(String containerImage) {
this.containerImage = containerImage;
return this;
}
public String getUrlHost() {
return urlHost;
}
public DatabaseConfig urlHost(String urlHost) {
this.urlHost = urlHost;
return this;
}
public String getUsername() {
return username;
}
public DatabaseConfig username(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public DatabaseConfig password(String password) {
this.password = password;
return this;
}
public Map<String, String> toConfig() {
Map<String, String> config = new HashMap<>();
if (vendor != null) {
config.put("db", vendor);
}
if (urlHost != null) {
config.put("db-url-host", urlHost);
}
if (username != null) {
config.put("db-username", username);
}
if (password != null) {
config.put("db-password", password);
}
return config;
}
}

View file

@ -0,0 +1,11 @@
package org.keycloak.test.framework.database;
public class DevFileDatabaseSupplier extends AbstractDatabaseSupplier {
@Override
TestDatabase getTestDatabase() {
DatabaseConfig databaseConfig = new DatabaseConfig().vendor("dev-file");
return new TestDatabase(databaseConfig);
}
}

View file

@ -0,0 +1,11 @@
package org.keycloak.test.framework.database;
public class DevMemDatabaseSupplier extends AbstractDatabaseSupplier {
@Override
TestDatabase getTestDatabase() {
DatabaseConfig databaseConfig = new DatabaseConfig().vendor("dev-mem");
return new TestDatabase(databaseConfig);
}
}

View file

@ -0,0 +1,15 @@
package org.keycloak.test.framework.database;
public class PostgresDatabaseSupplier extends AbstractDatabaseSupplier {
@Override
TestDatabase getTestDatabase() {
DatabaseConfig databaseConfig = new DatabaseConfig()
.vendor("postgres")
.username("keycloak")
.password("keycloak")
.containerImage("the-postgres-container:the-version");
return new TestDatabase(databaseConfig);
}
}

View file

@ -0,0 +1,31 @@
package org.keycloak.test.framework.database;
import java.util.Map;
public class TestDatabase {
private DatabaseConfig databaseConfig;
public TestDatabase(DatabaseConfig databaseConfig) {
this.databaseConfig = databaseConfig;
}
public void start() {
if (databaseConfig.getContainerImage() != null) {
// TODO Start container
}
}
public void stop() {
if (databaseConfig.getContainerImage() != null) {
// TODO Stop container
} else if (databaseConfig.getVendor().equals("dev-mem")) {
// TODO Stop in-mem H2 database
}
}
public Map<String, String> getServerConfig() {
return databaseConfig.toConfig();
}
}

View file

@ -1,12 +1,16 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import org.keycloak.test.framework.KeycloakIntegrationTest; import org.keycloak.test.framework.KeycloakIntegrationTest;
import org.keycloak.test.framework.database.TestDatabase;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceWrapper;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry; import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
import java.util.Collections;
import java.util.Map;
public abstract class AbstractKeycloakTestServerSupplier implements Supplier<KeycloakTestServer, KeycloakIntegrationTest> { public abstract class AbstractKeycloakTestServerSupplier implements Supplier<KeycloakTestServer, KeycloakIntegrationTest> {
@Override @Override
@ -22,12 +26,22 @@ public abstract class AbstractKeycloakTestServerSupplier implements Supplier<Key
@Override @Override
public InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> getValue(Registry registry, KeycloakIntegrationTest annotation) { public InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> getValue(Registry registry, KeycloakIntegrationTest annotation) {
KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config()); KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config());
InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> wrapper = new InstanceWrapper<>(this, annotation);
Map<String, String> databaseConfig;
if (requiresDatabase()) {
TestDatabase testDatabase = registry.getDependency(TestDatabase.class, wrapper);
databaseConfig = testDatabase.getServerConfig();
} else {
databaseConfig = Collections.emptyMap();
}
KeycloakTestServer keycloakTestServer = getServer(); KeycloakTestServer keycloakTestServer = getServer();
keycloakTestServer.start(serverConfig, databaseConfig);
keycloakTestServer.start(serverConfig); wrapper.setValue(keycloakTestServer, LifeCycle.GLOBAL);
return new InstanceWrapper<>(this, annotation, keycloakTestServer, LifeCycle.GLOBAL); return wrapper;
} }
@Override @Override
@ -42,4 +56,6 @@ public abstract class AbstractKeycloakTestServerSupplier implements Supplier<Key
public abstract KeycloakTestServer getServer(); public abstract KeycloakTestServer getServer();
public abstract boolean requiresDatabase();
} }

View file

@ -4,6 +4,7 @@ import org.keycloak.it.utils.RawKeycloakDistribution;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
public class DistributionKeycloakTestServer implements KeycloakTestServer { public class DistributionKeycloakTestServer implements KeycloakTestServer {
@ -16,7 +17,7 @@ public class DistributionKeycloakTestServer implements KeycloakTestServer {
private RawKeycloakDistribution keycloak; private RawKeycloakDistribution keycloak;
@Override @Override
public void start(KeycloakTestServerConfig serverConfig) { public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) {
keycloak = new RawKeycloakDistribution(debug, manualStop, enableTls, reCreate, removeBuildOptionsAfterBuild, requestPort); keycloak = new RawKeycloakDistribution(debug, manualStop, enableTls, reCreate, removeBuildOptionsAfterBuild, requestPort);
// Set environment variables user and password for Keycloak Admin used by Keycloak instance. // Set environment variables user and password for Keycloak Admin used by Keycloak instance.
@ -35,6 +36,8 @@ public class DistributionKeycloakTestServer implements KeycloakTestServer {
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value)); serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
keycloak.run(rawOptions).assertStartedDevMode(); keycloak.run(rawOptions).assertStartedDevMode();
} }

View file

@ -7,4 +7,9 @@ public class DistributionKeycloakTestServerSupplier extends AbstractKeycloakTest
return new DistributionKeycloakTestServer(); return new DistributionKeycloakTestServer();
} }
@Override
public boolean requiresDatabase() {
return true;
}
} }

View file

@ -5,6 +5,7 @@ import org.keycloak.common.Version;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
public class EmbeddedKeycloakTestServer implements KeycloakTestServer { public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
@ -12,20 +13,20 @@ public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
private Keycloak keycloak; private Keycloak keycloak;
@Override @Override
public void start(KeycloakTestServerConfig serverConfig) { public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) {
List<String> rawOptions = new LinkedList<>(); List<String> rawOptions = new LinkedList<>();
rawOptions.add("start-dev"); rawOptions.add("start-dev");
// rawOptions.add("--db=dev-mem"); // TODO With dev-mem there's an issue as the H2 DB isn't stopped when restarting embedded server
rawOptions.add("--cache=local"); rawOptions.add("--cache=local");
if (!serverConfig.features().isEmpty()) { if (!serverConfig.features().isEmpty()) {
rawOptions.add("--features=" + String.join(",", serverConfig.features())); rawOptions.add("--features=" + String.join(",", serverConfig.features()));
} }
serverConfig.adminUserName().ifPresent(username -> rawOptions.add("--bootstrap-admin-username=" + username)); serverConfig.adminUserName().ifPresent(username -> System.setProperty("keycloakAdmin", username));
serverConfig.adminUserPassword().ifPresent(password -> rawOptions.add("--bootstrap-admin-password=" + password)); serverConfig.adminUserPassword().ifPresent(password -> System.setProperty("keycloakAdminPassword", password));
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value)); serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
keycloak = Keycloak.builder() keycloak = Keycloak.builder()
.setVersion(Version.VERSION) .setVersion(Version.VERSION)

View file

@ -7,4 +7,9 @@ public class EmbeddedKeycloakTestServerSupplier extends AbstractKeycloakTestServ
return new EmbeddedKeycloakTestServer(); return new EmbeddedKeycloakTestServer();
} }
@Override
public boolean requiresDatabase() {
return true;
}
} }

View file

@ -1,8 +1,10 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import java.util.Map;
public interface KeycloakTestServer { public interface KeycloakTestServer {
void start(KeycloakTestServerConfig serverConfig); void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig);
void stop(); void stop();

View file

@ -1,15 +1,15 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import java.util.Map;
public class RemoteKeycloakTestServer implements KeycloakTestServer { public class RemoteKeycloakTestServer implements KeycloakTestServer {
@Override @Override
public void start(KeycloakTestServerConfig serverConfig) { public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) {
} }
@Override @Override
public void stop() { public void stop() {
} }
@Override @Override

View file

@ -7,4 +7,9 @@ public class RemoteKeycloakTestServerSupplier extends AbstractKeycloakTestServer
return new RemoteKeycloakTestServer(); return new RemoteKeycloakTestServer();
} }
@Override
public boolean requiresDatabase() {
return false;
}
} }

View file

@ -7,3 +7,6 @@ org.keycloak.test.framework.server.DistributionKeycloakTestServerSupplier
org.keycloak.test.framework.server.RemoteKeycloakTestServerSupplier org.keycloak.test.framework.server.RemoteKeycloakTestServerSupplier
org.keycloak.test.framework.webdriver.ChromeWebDriverSupplier org.keycloak.test.framework.webdriver.ChromeWebDriverSupplier
org.keycloak.test.framework.webdriver.FirefoxWebDriverSupplier org.keycloak.test.framework.webdriver.FirefoxWebDriverSupplier
org.keycloak.test.framework.database.DevMemDatabaseSupplier
org.keycloak.test.framework.database.DevFileDatabaseSupplier
org.keycloak.test.framework.database.PostgresDatabaseSupplier