Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using Elasticsearch Dev Services by multiple extensions #23974

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions docs/src/main/asciidoc/elasticsearch-dev-services.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -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);
}

Expand All @@ -141,40 +142,41 @@ private void shutdownElasticsearch() {
}

private DevServicesResultBuildItem.RunningDevService startElasticsearch(ElasticsearchDevServicesBuildTimeConfig config,
DevservicesElasticsearchBuildItem devservicesElasticsearchBuildItem,
DevservicesElasticsearchBuildItemsConfiguration buildItemConfig,
LaunchModeBuildItem launchMode, boolean useSharedNetwork, Optional<Duration> timeout) throws BuildException {
if (!config.enabled.orElse(true)) {
// explicitly disabled
log.debug("Not starting dev services for Elasticsearch, as it has been disabled in the config.");
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());
}
Expand Down Expand Up @@ -205,15 +207,61 @@ 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
.map(containerAddress -> new DevServicesResultBuildItem.RunningDevService(
Feature.ELASTICSEARCH_REST_CLIENT_COMMON.getName(),
containerAddress.getId(),
null,
hostsConfigProperty, containerAddress.getUrl()))
buildPropertiesMap(buildItemConfig, containerAddress.getUrl())))
.orElseGet(defaultElasticsearchSupplier);
}

private Map<String, String> buildPropertiesMap(DevservicesElasticsearchBuildItemsConfiguration buildItemConfig,
String httpHosts) {
Map<String, String> propertiesToSet = new HashMap<>();
for (String property : buildItemConfig.hostsConfigProperties) {
propertiesToSet.put(property, httpHosts);
}
return propertiesToSet;
}

private String displayProperties(Set<String> hostsConfigProperties) {
return String.join(" and ", hostsConfigProperties);
}

private static class DevservicesElasticsearchBuildItemsConfiguration {
private Set<String> hostsConfigProperties;
private String version;
private DevservicesElasticsearchBuildItem.Distribution distribution;

private DevservicesElasticsearchBuildItemsConfiguration(List<DevservicesElasticsearchBuildItem> 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());
}
}
}
}