From f91c34d83d62c58058e4c68bc1f2ebdcde0138d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Fri, 25 Feb 2022 15:15:14 +0100 Subject: [PATCH] Allow using Elasticsearch Dev Services by multiple extensions. --- .../asciidoc/elasticsearch-dev-services.adoc | 3 - .../DevServicesElasticsearchProcessor.java | 102 +++++++++++++----- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/docs/src/main/asciidoc/elasticsearch-dev-services.adoc b/docs/src/main/asciidoc/elasticsearch-dev-services.adoc index 701d29d7443c9..9e3a0042c7953 100644 --- a/docs/src/main/asciidoc/elasticsearch-dev-services.adoc +++ b/docs/src/main/asciidoc/elasticsearch-dev-services.adoc @@ -58,9 +58,6 @@ quarkus.elasticsearch.devservices.image-name=docker.elastic.co/elasticsearch/ela == Current limitations -Currently, Elasticsearch Dev Services cannot be used if both Hibernate Search ORM Elasticsearch and an Elasticsearch client extension are used, -this will result in an exception on startup. - Currently, only the default backend for Hibernate Search Elasticsearch is supported, because Dev Services for Elasticsearch can only start one Elasticsearch container. == Configuration reference diff --git a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java index 529ef208f97b3..656c227ef6c71 100644 --- a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java +++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java @@ -1,7 +1,13 @@ package io.quarkus.elasticsearch.restclient.common.deployment; import java.time.Duration; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Supplier; import org.jboss.logging.Logger; @@ -64,13 +70,8 @@ public DevServicesResultBuildItem startElasticsearchDevService( return null; } - if (devservicesElasticsearchBuildItems.size() > 1) { - throw new BuildException( - "Multiple extensions requesting dev services for Elasticsearch found, which is not yet supported." + - "Please de-activate dev services for Elasticsearch using quarkus.elasticsearch.devservices.enabled.", - Collections.emptyList()); - } - DevservicesElasticsearchBuildItem devservicesElasticsearchBuildItem = devservicesElasticsearchBuildItems.get(0); + DevservicesElasticsearchBuildItemsConfiguration buildItemsConfig = new DevservicesElasticsearchBuildItemsConfiguration( + devservicesElasticsearchBuildItems); if (devService != null) { boolean shouldShutdownTheServer = !configuration.equals(cfg); @@ -81,12 +82,11 @@ public DevServicesResultBuildItem startElasticsearchDevService( cfg = null; } - String hostsConfigProperty = devservicesElasticsearchBuildItem.getHostsConfigProperty(); StartupLogCompressor compressor = new StartupLogCompressor( (launchMode.isTest() ? "(test) " : "") + "Elasticsearch Dev Services Starting:", consoleInstalledBuildItem, loggingSetupBuildItem); try { - devService = startElasticsearch(configuration, devservicesElasticsearchBuildItem, launchMode, + devService = startElasticsearch(configuration, buildItemsConfig, launchMode, !devServicesSharedNetworkBuildItem.isEmpty(), devServicesConfig.timeout); compressor.close(); @@ -118,13 +118,14 @@ public DevServicesResultBuildItem startElasticsearchDevService( log.infof( "Dev Services for Elasticsearch started. Other Quarkus applications in dev mode will find the " + "server automatically. For Quarkus applications in production mode, you can connect to" - + " this by starting your application with -D%s=%s", - hostsConfigProperty, getElasticsearchHosts(hostsConfigProperty)); + + " this by configuring your application to use %s", + getElasticsearchHosts(buildItemsConfig)); } return devService.toBuildItem(); } - public static String getElasticsearchHosts(String hostsConfigProperty) { + public static String getElasticsearchHosts(DevservicesElasticsearchBuildItemsConfiguration buildItemsConfiguration) { + String hostsConfigProperty = buildItemsConfiguration.hostsConfigProperties.stream().findAny().get(); return devService.getConfig().get(hostsConfigProperty); } @@ -141,7 +142,7 @@ private void shutdownElasticsearch() { } private DevServicesResultBuildItem.RunningDevService startElasticsearch(ElasticsearchDevServicesBuildTimeConfig config, - DevservicesElasticsearchBuildItem devservicesElasticsearchBuildItem, + DevservicesElasticsearchBuildItemsConfiguration buildItemConfig, LaunchModeBuildItem launchMode, boolean useSharedNetwork, Optional timeout) throws BuildException { if (!config.enabled.orElse(true)) { // explicitly disabled @@ -149,32 +150,33 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch(Elastics return null; } - String hostsConfigProperty = devservicesElasticsearchBuildItem.getHostsConfigProperty(); - // Check if elasticsearch hosts property is set - if (ConfigUtils.isPropertyPresent(hostsConfigProperty)) { - log.debugf("Not starting dev services for Elasticsearch, the %s property is configured.", hostsConfigProperty); - return null; + for (String hostsConfigProperty : buildItemConfig.hostsConfigProperties) { + // Check if elasticsearch hosts property is set + if (ConfigUtils.isPropertyPresent(hostsConfigProperty)) { + log.debugf("Not starting dev services for Elasticsearch, the %s property is configured.", hostsConfigProperty); + return null; + } } if (!isDockerWorking.getAsBoolean()) { - log.warnf( - "Docker isn't working, please configure the Elasticsearch hosts property (%s).", hostsConfigProperty); + log.warnf("Docker isn't working, please configure the Elasticsearch hosts property (%s).", + displayProperties(buildItemConfig.hostsConfigProperties)); return null; } // We only support ELASTIC container for now - if (devservicesElasticsearchBuildItem.getDistribution() == DevservicesElasticsearchBuildItem.Distribution.OPENSEARCH) { + if (buildItemConfig.distribution == DevservicesElasticsearchBuildItem.Distribution.OPENSEARCH) { throw new BuildException("Dev services for Elasticsearch didn't support Opensearch", Collections.emptyList()); } // Hibernate search Elasticsearch have a version configuration property, we need to check that it is coherent // with the image we are about to launch - if (devservicesElasticsearchBuildItem.getVersion() != null) { + if (buildItemConfig.version != null) { String containerTag = config.imageName.substring(config.imageName.indexOf(':') + 1); - if (!containerTag.startsWith(devservicesElasticsearchBuildItem.getVersion())) { + if (!containerTag.startsWith(buildItemConfig.version)) { throw new BuildException( "Dev services for Elasticsearch detected a version mismatch, container image is " + config.imageName - + " but the configured version is " + devservicesElasticsearchBuildItem.getVersion() + + + " but the configured version is " + buildItemConfig.version + ". Either configure a different image or disable dev services for Elasticsearch.", Collections.emptyList()); } @@ -205,7 +207,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch(Elastics return new DevServicesResultBuildItem.RunningDevService(Feature.ELASTICSEARCH_REST_CLIENT_COMMON.getName(), container.getContainerId(), container::close, - hostsConfigProperty, container.getHttpHostAddress()); + buildPropertiesMap(buildItemConfig, container.getHttpHostAddress())); }; return maybeContainerAddress @@ -213,7 +215,53 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch(Elastics Feature.ELASTICSEARCH_REST_CLIENT_COMMON.getName(), containerAddress.getId(), null, - hostsConfigProperty, containerAddress.getUrl())) + buildPropertiesMap(buildItemConfig, containerAddress.getUrl()))) .orElseGet(defaultElasticsearchSupplier); } + + private Map buildPropertiesMap(DevservicesElasticsearchBuildItemsConfiguration buildItemConfig, + String httpHosts) { + Map propertiesToSet = new HashMap<>(); + for (String property : buildItemConfig.hostsConfigProperties) { + propertiesToSet.put(property, httpHosts); + } + return propertiesToSet; + } + + private String displayProperties(Set hostsConfigProperties) { + return String.join(" and ", hostsConfigProperties); + } + + private static class DevservicesElasticsearchBuildItemsConfiguration { + private Set hostsConfigProperties; + private String version; + private DevservicesElasticsearchBuildItem.Distribution distribution; + + private DevservicesElasticsearchBuildItemsConfiguration(List buildItems) + throws BuildException { + hostsConfigProperties = new HashSet<>(buildItems.size()); + + // check that all build items agree on the version and distribution to start + for (DevservicesElasticsearchBuildItem buildItem : buildItems) { + if (version == null) { + version = buildItem.getVersion(); + } else if (!version.equals(buildItem.getVersion())) { + // safety guard but should never occur as only Hibernate Search ORM Elasticsearch configure the version + throw new BuildException("Multiple extensions request Elasticsearch Dev Services on different version.", + Collections.emptyList()); + } + + if (distribution == null) { + distribution = buildItem.getDistribution(); + } else if (!distribution.equals(buildItem.getDistribution())) { + // safety guard but should never occur as only Hibernate Search ORM Elasticsearch configure the distribution + throw new BuildException( + "Multiple extensions request Elasticsearch Dev Services on different distribution.", + Collections.emptyList()); + } + + hostsConfigProperties.add(buildItem.getHostsConfigProperty()); + } + } + } }