Skip to content

Commit

Permalink
Merge pull request quarkusio#39899 from lbroudoux/devservices-shared-…
Browse files Browse the repository at this point in the history
…network-tuning

Allow finer tuning of shared network usage by DevServices
  • Loading branch information
geoand authored Apr 12, 2024
2 parents b85c8db + d28045a commit 9929c7d
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,43 @@
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildStepBuilder;
import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;

/**
* A marker build item that indicates, if any instances are provided during the build, the containers started by DevServices
* will use a shared network.
* may use a shared network.
* This is mainly useful in integration tests where the application container needs to be able
* to communicate with the service containers.
*/
public final class DevServicesSharedNetworkBuildItem extends MultiBuildItem {

private final String source;

/** Create a build item without identifying the creator source. */
public DevServicesSharedNetworkBuildItem() {
this.source = UNKNOWN_SOURCE;
}

/**
* Create a build item identifying the creator source.
*
* @param source The identifier of the creator
*/
public DevServicesSharedNetworkBuildItem(String source) {
this.source = source;
}

/** The creator source of this build item. May be useful to decide whether a DevService should join a shared network. */
public String getSource() {
return source;
}

/* Property used by factory to retrieve the source of instanciation. */
public static final String SOURCE_PROPERTY = "source";

/* Value of source field when instanciation origin is unknown. */
public static final String UNKNOWN_SOURCE = "unknown";

/**
* Generates a {@code List<Consumer<BuildChainBuilder>> build chain builder} which creates a build step
* producing the {@link DevServicesSharedNetworkBuildItem} build item.
Expand All @@ -28,10 +56,28 @@ public static final class Factory implements Function<Map<String, Object>, List<
public List<Consumer<BuildChainBuilder>> apply(final Map<String, Object> props) {
return Collections.singletonList((builder) -> {
BuildStepBuilder stepBuilder = builder.addBuildStep((ctx) -> {
ctx.produce(new DevServicesSharedNetworkBuildItem());
DevServicesSharedNetworkBuildItem buildItem;
if (props != null && props.containsKey(SOURCE_PROPERTY)) {
buildItem = new DevServicesSharedNetworkBuildItem(props.get(SOURCE_PROPERTY).toString());
} else {
buildItem = new DevServicesSharedNetworkBuildItem();
}
ctx.produce(buildItem);
});
stepBuilder.produces(DevServicesSharedNetworkBuildItem.class).build();
});
}
}

/**
* Helper method for DevServices processors that tells if joining the shared network is required.
* Joining this network may be required if explicitily asked by user properties or if running a containerized
* application during integration tests.
*/
public static boolean isSharedNetworkRequired(GlobalDevServicesConfig globalDevServicesConfig,
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
return globalDevServicesConfig.launchOnSharedNetwork ||
(!devServicesSharedNetworkBuildItem.isEmpty()
&& devServicesSharedNetworkBuildItem.get(0).getSource().equals("io.quarkus.test.junit"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public class GlobalDevServicesConfig {
@ConfigItem(defaultValue = "true")
boolean enabled;

/**
* Global flag that can be used to force the attachmment of Dev Services to shared netxork. Default is false.
*/
@ConfigItem(defaultValue = "false")
public boolean launchOnSharedNetwork;

/**
* The timeout for starting a container
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.Base58;

import com.github.dockerjava.api.command.CreateNetworkCmd;

public final class ConfigureUtil {

private static final Map<String, Properties> DEVSERVICES_PROPS = new ConcurrentHashMap<>();
Expand All @@ -35,6 +40,12 @@ public static String configureSharedNetwork(GenericContainer<?> container, Strin
Class<?> networkClass = tccl.getParent()
.loadClass("org.testcontainers.containers.Network");
Object sharedNetwork = networkClass.getField("SHARED").get(null);
Consumer<CreateNetworkCmd> addDevservicesLabel = cmd -> cmd
.withLabels(Map.of("quarkus.devservices.network", "shared"));
Field createNetworkCmdModifiersField = sharedNetwork.getClass().getSuperclass()
.getDeclaredField("createNetworkCmdModifiers");
createNetworkCmdModifiersField.setAccessible(true);
createNetworkCmdModifiersField.set(sharedNetwork, Set.of(addDevservicesLabel));
container.setNetwork((Network) sharedNetwork);
} catch (Exception e) {
throw new IllegalStateException("Unable to obtain SHARED network from testcontainers", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -32,15 +33,19 @@ public class DB2DevServicesProcessor {

@BuildStep
DevServicesDatasourceProviderBuildItem setupDB2(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.DB2, new DevServicesDatasourceProvider() {
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusDb2Container container = new QuarkusDb2Container(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
useSharedNetwork);
startupTimeout.ifPresent(container::withStartupTimeout);

String effectiveUsername = containerConfig.getUsername().orElse(username.orElse(DEFAULT_DATABASE_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -35,16 +36,20 @@ public class MariaDBDevServicesProcessor {

@BuildStep
DevServicesDatasourceProviderBuildItem setupMariaDB(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.MARIADB, new DevServicesDatasourceProvider() {
@SuppressWarnings("unchecked")
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusMariaDBContainer container = new QuarkusMariaDBContainer(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
useSharedNetwork);
startupTimeout.ifPresent(container::withStartupTimeout);

String effectiveUsername = containerConfig.getUsername().orElse(username.orElse(DEFAULT_DATABASE_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -34,13 +35,17 @@ public class MSSQLDevServicesProcessor {

@BuildStep
DevServicesDatasourceProviderBuildItem setupMSSQL(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.MSSQL, new DevServicesDatasourceProvider() {
@SuppressWarnings("unchecked")
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusMSSQLServerContainer container = new QuarkusMSSQLServerContainer(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -34,16 +35,20 @@ public class MySQLDevServicesProcessor {

@BuildStep
DevServicesDatasourceProviderBuildItem setupMysql(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.MYSQL, new DevServicesDatasourceProvider() {
@SuppressWarnings("unchecked")
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusMySQLContainer container = new QuarkusMySQLContainer(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
useSharedNetwork);
startupTimeout.ifPresent(container::withStartupTimeout);

String effectiveUsername = containerConfig.getUsername().orElse(username.orElse(DEFAULT_DATABASE_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -39,15 +40,19 @@ public class OracleDevServicesProcessor {

@BuildStep
DevServicesDatasourceProviderBuildItem setupOracle(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.ORACLE, new DevServicesDatasourceProvider() {
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusOracleServerContainer container = new QuarkusOracleServerContainer(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
useSharedNetwork);
startupTimeout.ifPresent(container::withStartupTimeout);

String effectiveUsername = containerConfig.getUsername().orElse(username.orElse(DEFAULT_DATABASE_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.quarkus.deployment.builditem.ConsoleCommandBuildItem;
import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem;
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.devservices.common.ConfigureUtil;
import io.quarkus.devservices.common.ContainerShutdownCloseable;
import io.quarkus.devservices.common.Labels;
Expand All @@ -42,16 +43,20 @@ ConsoleCommandBuildItem psqlCommand(DevServicesLauncherConfigResultBuildItem dev

@BuildStep
DevServicesDatasourceProviderBuildItem setupPostgres(
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem) {
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem,
GlobalDevServicesConfig globalDevServicesConfig) {
return new DevServicesDatasourceProviderBuildItem(DatabaseKind.POSTGRESQL, new DevServicesDatasourceProvider() {
@SuppressWarnings("unchecked")
@Override
public RunningDevServicesDatasource startDatabase(Optional<String> username, Optional<String> password,
String datasourceName, DevServicesDatasourceContainerConfig containerConfig,
LaunchMode launchMode, Optional<Duration> startupTimeout) {

boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
QuarkusPostgreSQLContainer container = new QuarkusPostgreSQLContainer(containerConfig.getImageName(),
containerConfig.getFixedExposedPort(),
!devServicesSharedNetworkBuildItem.isEmpty());
useSharedNetwork);
startupTimeout.ifPresent(container::withStartupTimeout);

String effectiveUsername = containerConfig.getUsername().orElse(username.orElse(DEFAULT_DATABASE_USERNAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ public DevServicesResultBuildItem startElasticsearchDevService(
(launchMode.isTest() ? "(test) " : "") + "Dev Services for Elasticsearch starting:",
consoleInstalledBuildItem, loggingSetupBuildItem);
try {
boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(devServicesConfig,
devServicesSharedNetworkBuildItem);
devService = startElasticsearch(dockerStatusBuildItem, configuration, buildItemsConfig, launchMode,
!devServicesSharedNetworkBuildItem.isEmpty(),
useSharedNetwork,
devServicesConfig.timeout);
if (devService == null) {
compressor.closeAndDumpCaptured();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ public List<DevServicesResultBuildItem> startMongo(List<MongoConnectionNameBuild
(launchMode.isTest() ? "(test) " : "") + "Mongo Dev Services Starting:", consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(globalDevServicesConfig,
devServicesSharedNetworkBuildItem);
devService = startMongo(dockerStatusBuildItem, connectionName, currentCapturedProperties.get(connectionName),
!devServicesSharedNetworkBuildItem.isEmpty(), globalDevServicesConfig.timeout);
useSharedNetwork, globalDevServicesConfig.timeout);
if (devService == null) {
compressor.closeAndDumpCaptured();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@ public List<DevServicesResultBuildItem> startRedisContainers(LaunchModeBuildItem
try {
for (Entry<String, DevServiceConfiguration> entry : currentDevServicesConfiguration.entrySet()) {
String connectionName = entry.getKey();
boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(devServicesConfig,
devServicesSharedNetworkBuildItem);
RunningDevService devService = startContainer(dockerStatusBuildItem, connectionName,
entry.getValue().devservices(),
launchMode.getLaunchMode(),
!devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout);
useSharedNetwork, devServicesConfig.timeout);
if (devService == null) {
continue;
}
Expand Down
Loading

0 comments on commit 9929c7d

Please sign in to comment.