Skip to content

Commit

Permalink
Start Testcontainers containers with the Platform CL as TCCL
Browse files Browse the repository at this point in the history
Testcontainers start several long lived threads (Ryuk reaper, ducttape),
and if the TCCL is one of the QuarkusClassLoader, we end up leaking the
class loaders.
Using the Platform CL makes sure we don't leak any QuarkusClassLoader
via these threads.

The observability containers have not been adjusted as they are a bit of
a weird beast and not worth having a look in the scope of this
experiment.
  • Loading branch information
gsmet committed Aug 16, 2024
1 parent 53e9c49 commit c75ded8
Show file tree
Hide file tree
Showing 18 changed files with 43 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.testcontainers.containers.Db2Container;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand Down Expand Up @@ -65,7 +66,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam);
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for IBM Db2 started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand Down Expand Up @@ -74,7 +75,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam);
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for MariaDB started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.testcontainers.containers.MSSQLServerContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProvider;
Expand Down Expand Up @@ -66,7 +67,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for Microsoft SQL Server started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand Down Expand Up @@ -74,7 +75,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for MySQL started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.testcontainers.containers.OracleContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand Down Expand Up @@ -80,7 +81,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for Oracle started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand Down Expand Up @@ -77,7 +78,7 @@ public RunningDevServicesDatasource startDatabase(Optional<String> username, Opt
containerConfig.getCommand().ifPresent(container::setCommand);
containerConfig.getInitScriptPath().ifPresent(container::withInitScript);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

LOG.info("Dev Services for PostgreSQL started.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.testcontainers.elasticsearch.ElasticsearchContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.builder.BuildException;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
Expand Down Expand Up @@ -208,7 +209,8 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearchDevServic

container.withReuse(config.reuse);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

return new DevServicesResultBuildItem.RunningDevService(Feature.ELASTICSEARCH_REST_CLIENT_COMMON.getName(),
container.getContainerId(),
new ContainerShutdownCloseable(container, "Elasticsearch"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jboss.logging.Logger;
import org.testcontainers.containers.BindMode;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -212,7 +213,7 @@ private RunningDevService startContainer(String clientName, DockerStatusBuildIte
useSharedNetwork);
timeout.ifPresent(infinispanContainer::withStartupTimeout);
infinispanContainer.withEnv(devServicesConfig.containerEnv);
infinispanContainer.start();
QuarkusClassLoader.runWithPlatformClassLoader(infinispanContainer::start);

return getRunningDevService(clientName, infinispanContainer.getContainerId(), infinispanContainer::close,
infinispanContainer.getHost() + ":" + infinispanContainer.getPort(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.github.dockerjava.api.command.InspectContainerResponse;

import io.fabric8.kubernetes.client.Config;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -229,7 +230,7 @@ private RunningDevService startKubernetes(DockerStatusBuildItem dockerStatusBuil

container.withEnv(config.containerEnv);

container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

KubeConfig kubeConfig = KubeConfigUtils.parseKubeConfig(container.getKubeconfig());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.message.BasicNameValuePair;
import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.net.URLEncodedUtils;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -188,7 +189,7 @@ private RunningDevService startMongo(DockerStatusBuildItem dockerStatusBuildItem
}
timeout.ifPresent(mongoDBContainer::withStartupTimeout);
mongoDBContainer.withEnv(capturedProperties.containerEnv);
mongoDBContainer.start();
QuarkusClassLoader.runWithPlatformClassLoader(mongoDBContainer::start);

final String effectiveUrl = getEffectiveUrl(configPrefix, mongoDBContainer.getEffectiveHost(),
mongoDBContainer.getEffectivePort(), capturedProperties);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -379,7 +380,7 @@ private RunningDevService startContainer(DockerStatusBuildItem dockerStatusBuild

timeout.ifPresent(oidcContainer::withStartupTimeout);
oidcContainer.withEnv(capturedDevServicesConfiguration.containerEnv);
oidcContainer.start();
QuarkusClassLoader.runWithPlatformClassLoader(oidcContainer::start);

String internalUrl = startURL(oidcContainer.getHost(), oidcContainer.getPort(), oidcContainer.keycloakX);
String hostUrl = oidcContainer.useSharedNetwork
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -181,7 +182,7 @@ private RunningDevService startContainer(DockerStatusBuildItem dockerStatusBuild
launchMode == DEVELOPMENT ? devServicesConfig.serviceName() : null, useSharedNetwork);
timeout.ifPresent(redisContainer::withStartupTimeout);
redisContainer.withEnv(devServicesConfig.containerEnv());
redisContainer.start();
QuarkusClassLoader.runWithPlatformClassLoader(redisContainer::start);
String redisHost = REDIS_SCHEME + redisContainer.getHost() + ":" + redisContainer.getPort();
return new RunningDevService(Feature.REDIS_CLIENT.getName(), redisContainer.getContainerId(),
redisContainer::close, configPrefix + RedisConfig.HOSTS_CONFIG_NAME, redisHost);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.testcontainers.utility.DockerImageName;

import io.quarkus.apicurio.registry.devservice.ApicurioRegistryBuildTimeConfig.ApicurioRegistryDevServicesBuildTimeConfig;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -182,7 +183,7 @@ private RunningDevService startApicurioRegistry(DockerStatusBuildItem dockerStat
useSharedNetwork);
timeout.ifPresent(container::withStartupTimeout);
container.withEnv(config.containerEnv);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

return new RunningDevService(Feature.APICURIO_REGISTRY_AVRO.getName(), container.getContainerId(),
container::close, getRegistryUrlConfigs(container.getUrl()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -190,7 +191,7 @@ private RunningDevService startAmqpBroker(DockerStatusBuildItem dockerStatusBuil

timeout.ifPresent(container::withStartupTimeout);
container.withEnv(config.containerEnv);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

return getRunningService(container.getContainerId(), container::close, container.getEffectiveHost(),
container.getPort(), container.getMappedPort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private RunningDevService startMqttBroker(DockerStatusBuildItem dockerStatusBuil
// Starting the broker
timeout.ifPresent(container::withStartupTimeout);
container.withEnv(config.containerEnv);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);
return getRunningDevService(
container.getContainerId(),
container::close,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -175,7 +176,7 @@ private RunningDevService startPulsarContainer(DockerStatusBuildItem dockerStatu
container.withPort(config.fixedExposedPort);
}
timeout.ifPresent(container::withStartupTimeout);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);

return getRunningService(container.getContainerId(), container::close, container.getPulsarBrokerUrl(),
container.getHttpServiceUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private RunningDevService startRabbitMQBroker(DockerStatusBuildItem dockerStatus
// Starting the broker
timeout.ifPresent(container::withStartupTimeout);
container.withEnv(config.containerEnv);
container.start();
QuarkusClassLoader.runWithPlatformClassLoader(container::start);
return getRunningDevService(container.getContainerId(), container::close, container.getHost(),
container.getPort(), container.getHttpPort(), container.getAdminUsername(), container.getAdminPassword());
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ public static boolean isResourcePresentAtRuntime(String resourcePath) {
return false;
}

public static void runWithPlatformClassLoader(Runnable runnable) {
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(PLATFORM_CLASS_LOADER);
runnable.run();
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}

private final String name;
private final List<ClassPathElement> elements;
private final ConcurrentMap<ClassPathElement, ProtectionDomain> protectionDomains = new ConcurrentHashMap<>();
Expand Down

0 comments on commit c75ded8

Please sign in to comment.