From b308fd9df79be29048fc119f9843c51a8c0638cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Tue, 25 Nov 2025 10:23:29 +0100 Subject: [PATCH 1/7] WorkInProgress Refactor UNOMI_AUTO_START variable name to add UNOMI_START_CONFIG. This will split the concept of auto start and of start config name. Restore behavior for Persistence HealthCheck to the scope of PersistenceService state (for migration and data coherency status). Avoid using Delegator for OpenSearch and ElasticSearch Healthcheck providers but rely on configuration to activate the right one. --- docker/README.md | 7 +- .../main/docker/docker-compose-build-es.yml | 4 +- .../main/docker/docker-compose-build-os.yml | 4 +- docker/src/main/docker/docker-compose-es.yml | 4 +- docker/src/main/docker/docker-compose-os.yml | 4 +- docker/src/main/docker/entrypoint.sh | 29 ++-- .../unomi/healthcheck/HealthCheckService.java | 2 +- .../ElasticSearchHealthCheckProvider.java | 135 +++++++++------- .../OpenSearchHealthCheckProvider.java | 150 ++++++++++-------- .../PersistenceEngineHealthProvider.java | 37 ----- .../PersistenceHealthCheckProvider.java | 66 -------- .../org.apache.unomi.healthcheck.cfg | 2 +- .../src/main/asciidoc/5-min-quickstart.adoc | 3 +- manual/src/main/asciidoc/configuration.adoc | 40 +++-- .../migrations/migrate-2.x-to-3.0.adoc | 12 +- 15 files changed, 227 insertions(+), 272 deletions(-) delete mode 100644 extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java diff --git a/docker/README.md b/docker/README.md index 83cce28a2..ec25a2fd8 100644 --- a/docker/README.md +++ b/docker/README.md @@ -67,7 +67,7 @@ For Unomi (with OpenSearch): docker pull apache/unomi:3.0.0-SNAPSHOT docker run --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_AUTO_START=opensearch \ + -e UNOMI_PERSISTENCE_CONFIG=opensearch \ -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \ -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ apache/unomi:3.0.0-SNAPSHOT @@ -83,7 +83,7 @@ For ElasticSearch: For OpenSearch: docker run --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_AUTO_START=opensearch \ + -e UNOMI_PERSISTENCE_CONFIG=opensearch -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \ -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ apache/unomi:3.0.0-SNAPSHOT @@ -93,7 +93,8 @@ Note: Linux doesn't support the host.docker.internal DNS lookup method yet, it s ## Environment Variables ### Common Variables -- `UNOMI_AUTO_START`: Specifies the search engine type (`elasticsearch` or `opensearch`, defaults to `elasticsearch`) +- `UNOMI_AUTO_START`: Boolean to specify if unomi auto start with karaf (defaults to `true`) +- `UNOMI_PERSISTENCE_CONFIG`: Specifies the search engine configuration (`elasticsearch` or `opensearch`, defaults to `elasticsearch`) ### ElasticSearch-specific Variables - `UNOMI_ELASTICSEARCH_ADDRESSES`: ElasticSearch host:port (default: localhost:9200) diff --git a/docker/src/main/docker/docker-compose-build-es.yml b/docker/src/main/docker/docker-compose-build-es.yml index af71f7717..1143be9fc 100644 --- a/docker/src/main/docker/docker-compose-build-es.yml +++ b/docker/src/main/docker/docker-compose-build-es.yml @@ -35,8 +35,10 @@ services: build: . image: apache/unomi:${project.version} environment: - - UNOMI_AUTO_START=elasticsearch + - UNOMI_AUTO_START=true + - UNOMI_START_CONFIG=elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 + - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-build-os.yml b/docker/src/main/docker/docker-compose-build-os.yml index 0f736e004..7fda5596f 100644 --- a/docker/src/main/docker/docker-compose-build-os.yml +++ b/docker/src/main/docker/docker-compose-build-os.yml @@ -95,10 +95,12 @@ services: image: apache/unomi:${project.version} container_name: unomi environment: - - UNOMI_AUTO_START=opensearch + - UNOMI_AUTO_START=true + - UNOMI_START_CONFIG=opensearch - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - UNOMI_HEALTHCHECK_PROVIDERS=cluster,opensearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-es.yml b/docker/src/main/docker/docker-compose-es.yml index 28e800b4b..07d6f019b 100644 --- a/docker/src/main/docker/docker-compose-es.yml +++ b/docker/src/main/docker/docker-compose-es.yml @@ -41,8 +41,10 @@ services: node-1: image: apache/unomi:${project.version} environment: - - UNOMI_AUTO_START=elasticsearch + - UNOMI_AUTO_START=true + - UNOMI_START_CONFIG=elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 + - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-os.yml b/docker/src/main/docker/docker-compose-os.yml index cf229dedf..8ee97e05f 100644 --- a/docker/src/main/docker/docker-compose-os.yml +++ b/docker/src/main/docker/docker-compose-os.yml @@ -79,10 +79,12 @@ services: image: apache/unomi:${project.version} container_name: unomi environment: - - UNOMI_AUTO_START=opensearch + - UNOMI_AUTO_START=true + - UNOMI_START_CONFIG=opensearch - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - UNOMI_HEALTHCHECK_PROVIDERS=cluster,opensearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/entrypoint.sh b/docker/src/main/docker/entrypoint.sh index 553aac374..379af4d95 100755 --- a/docker/src/main/docker/entrypoint.sh +++ b/docker/src/main/docker/entrypoint.sh @@ -29,14 +29,10 @@ if [ "$KARAF_DEBUG" = "true" ]; then export KARAF_DEBUG=true fi -# Determine search engine type from UNOMI_AUTO_START -SEARCH_ENGINE="${UNOMI_AUTO_START:-elasticsearch}" -export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START}" +PERSISTENCE_CONFIG="${UNOMI_START_CONFIG:-elasticsearch}" +export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START} -Dunomi.startConfig=${UNOMI_START_CONFIG}" -if [ "$SEARCH_ENGINE" = "true" ]; then - SEARCH_ENGINE="elasticsearch" -fi -echo "SEARCH_ENGINE: $SEARCH_ENGINE" +echo "PERSISTENCE_CONFIG: $PERSISTENCE_CONFIG" echo "KARAF_OPTS: $KARAF_OPTS" # Function to check cluster health for a specific node @@ -51,8 +47,8 @@ check_node_health() { fi } -# Configure connection parameters based on search engine type -if [ "$SEARCH_ENGINE" = "opensearch" ]; then +# Configure connection parameters based on persistence config +if [ "$PERSISTENCE_CONFIG" = "opensearch" ]; then # OpenSearch configuration if [ -z "$UNOMI_OPENSEARCH_PASSWORD" ]; then echo "Error: UNOMI_OPENSEARCH_PASSWORD must be set when using OpenSearch" @@ -65,7 +61,7 @@ if [ "$SEARCH_ENGINE" = "opensearch" ]; then curl_opts="-k -H \"${auth_header}\" -H \"Content-Type: application/json\"" # Build array of node URLs IFS=',' read -ra NODES <<< "${UNOMI_OPENSEARCH_ADDRESSES}" -else +elif [ "$PERSISTENCE_CONFIG" = "elasticsearch" ]; then # Elasticsearch configuration if [ "$UNOMI_ELASTICSEARCH_SSL_ENABLE" = 'true' ]; then schema='https' @@ -80,10 +76,13 @@ else health_endpoint="_cluster/health" # Build array of node URLs IFS=',' read -ra NODES <<< "${UNOMI_ELASTICSEARCH_ADDRESSES}" +else + echo "Error: Unsupported persistence config: $PERSISTENCE_CONFIG" + exit 1 fi # Wait for search engine to be ready -echo "Waiting for ${SEARCH_ENGINE} to be ready..." +echo "Waiting for ${PERSISTENCE_CONFIG} to be ready..." echo "Checking nodes: ${NODES[@]}" health_check="" @@ -103,20 +102,20 @@ while ([ -z "$health_check" ] || ([ "$health_check" != 'yellow' ] && [ "$health_ done if [ -z "$health_check" ]; then - >&2 echo "${SEARCH_ENGINE^} is not yet available - all nodes unreachable" + >&2 echo "${PERSISTENCE_CONFIG^} is not yet available - all nodes unreachable" sleep 3 continue fi if [ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ]; then - >&2 echo "${SEARCH_ENGINE^} health status: ${health_check} (waiting for yellow or green)" + >&2 echo "${PERSISTENCE_CONFIG^} health status: ${health_check} (waiting for yellow or green)" sleep 3 else - >&2 echo "${SEARCH_ENGINE^} health status: ${health_check}" + >&2 echo "${PERSISTENCE_CONFIG^} health status: ${health_check}" fi done -echo "${SEARCH_ENGINE^} is ready with health status: ${health_check}" +echo "${PERSISTENCE_CONFIG^} is ready with health status: ${health_check}" # Run Unomi in current bash session exec "$UNOMI_HOME/bin/karaf" run diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckService.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckService.java index 9da585e14..7e0f48d11 100644 --- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckService.java +++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckService.java @@ -124,7 +124,7 @@ public List check() throws RejectedExecutionException { busy = true; List health = new ArrayList<>(); health.add(HealthCheckResponse.live("karaf")); - for (HealthCheckProvider provider : providers.stream().filter(p -> config.getEnabledProviders().contains(p.name())).collect(Collectors.toList())) { + for (HealthCheckProvider provider : providers.stream().filter(p -> config.getEnabledProviders().contains(p.name())).toList()) { Future future = executor.submit(provider::execute); try { HealthCheckResponse response = future.get(config.getTimeout(), TimeUnit.MILLISECONDS); diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java index 278eb8a1e..b07f279e7 100644 --- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java +++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/ElasticSearchHealthCheckProvider.java @@ -30,9 +30,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.apache.unomi.healthcheck.HealthCheckConfig; +import org.apache.unomi.healthcheck.HealthCheckProvider; import org.apache.unomi.healthcheck.HealthCheckResponse; import org.apache.unomi.healthcheck.util.CachedValue; import org.apache.unomi.shell.migration.utils.HttpUtils; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,13 +48,17 @@ * A Health Check that checks the status of the ElasticSearch connectivity according to the provided configuration. * This connectivity should be LIVE before any try to start Unomi. */ -public class ElasticSearchHealthCheckProvider implements PersistenceEngineHealthProvider { +@Component(service = HealthCheckProvider.class, immediate = true) +public class ElasticSearchHealthCheckProvider implements HealthCheckProvider { public static final String NAME = "elasticsearch"; private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchHealthCheckProvider.class.getName()); private final CachedValue cache = new CachedValue<>(10, TimeUnit.SECONDS); + private final ObjectMapper mapper = new ObjectMapper(); + private boolean isActive = false; + @Reference(cardinality = ReferenceCardinality.MANDATORY) private HealthCheckConfig config; private CloseableHttpClient httpClient; @@ -58,21 +67,25 @@ public ElasticSearchHealthCheckProvider() { LOGGER.info("Building elasticsearch health provider service..."); } + @Activate public void activate() { - LOGGER.info("Activating elasticsearch health provider service..."); - CredentialsProvider credentialsProvider = null; - String login = config.get(HealthCheckConfig.CONFIG_ES_LOGIN); - if (StringUtils.isNotEmpty(login)) { - credentialsProvider = new BasicCredentialsProvider(); - UsernamePasswordCredentials credentials - = new UsernamePasswordCredentials(login, config.get(HealthCheckConfig.CONFIG_ES_PASSWORD)); - credentialsProvider.setCredentials(AuthScope.ANY, credentials); - } - try { - httpClient = HttpUtils.initHttpClient( - Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_ES_TRUST_ALL_CERTIFICATES)), credentialsProvider); - } catch (IOException e) { - LOGGER.error("Unable to initialize http client", e); + if (config.isEnabled() && config.getEnabledProviders().stream().anyMatch(NAME::equals)) { + LOGGER.info("Activating elasticsearch health provider service..."); + this.isActive = true; + CredentialsProvider credentialsProvider = null; + String login = config.get(HealthCheckConfig.CONFIG_ES_LOGIN); + if (StringUtils.isNotEmpty(login)) { + credentialsProvider = new BasicCredentialsProvider(); + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(login, + config.get(HealthCheckConfig.CONFIG_ES_PASSWORD)); + credentialsProvider.setCredentials(AuthScope.ANY, credentials); + } + try { + httpClient = HttpUtils.initHttpClient(Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_ES_TRUST_ALL_CERTIFICATES)), + credentialsProvider); + } catch (IOException e) { + LOGGER.error("Unable to initialize http client", e); + } } } @@ -92,56 +105,64 @@ public void setConfig(HealthCheckConfig config) { return cache.getValue(); } - @Override public HealthCheckResponse detailed() { - return execute(); - } - private HealthCheckResponse refresh() { LOGGER.debug("Refresh"); HealthCheckResponse.Builder builder = new HealthCheckResponse.Builder(); builder.name(NAME).down(); - String url = (config.get(HealthCheckConfig.CONFIG_ES_SSL_ENABLED).equals("true") ? "https://" : "http://") - .concat(config.get(HealthCheckConfig.CONFIG_ES_ADDRESSES).split(",")[0].trim()) - .concat("/_cluster/health"); - CloseableHttpResponse response = null; - try { - response = httpClient.execute(new HttpGet(url)); - if (response != null && response.getStatusLine().getStatusCode() == 200) { - builder.up(); - HttpEntity entity = response.getEntity(); - if (entity != null) { - String content = EntityUtils.toString(entity); - try { - ObjectMapper mapper = new ObjectMapper(); - JsonNode root = mapper.readTree(content); - if (root.has("status") && "green".equals(root.get("status").asText())) { - builder.live(); - } - if (root.has("cluster_name")) builder.withData("cluster_name", root.get("cluster_name").asText()); - if (root.has("status")) builder.withData("status", root.get("status").asText()); - if (root.has("timed_out")) builder.withData("timed_out", root.get("timed_out").asBoolean()); - if (root.has("number_of_nodes")) builder.withData("number_of_nodes", root.get("number_of_nodes").asLong()); - if (root.has("number_of_data_nodes")) builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong()); - if (root.has("active_primary_shards")) builder.withData("active_primary_shards", root.get("active_primary_shards").asLong()); - if (root.has("active_shards")) builder.withData("active_shards", root.get("active_shards").asLong()); - if (root.has("relocating_shards")) builder.withData("relocating_shards", root.get("relocating_shards").asLong()); - if (root.has("initializing_shards")) builder.withData("initializing_shards", root.get("initializing_shards").asLong()); - if (root.has("unassigned_shards")) builder.withData("unassigned_shards", root.get("unassigned_shards").asLong()); - } catch (Exception parseEx) { - // Fallback to simple LIVE detection - if (content.contains("\"status\":\"green\"")) { - builder.live(); + if (isActive) { + String url = (config.get(HealthCheckConfig.CONFIG_ES_SSL_ENABLED).equals("true") ? "https://" : "http://").concat( + config.get(HealthCheckConfig.CONFIG_ES_ADDRESSES).split(",")[0].trim()).concat("/_cluster/health"); + CloseableHttpResponse response = null; + try { + response = httpClient.execute(new HttpGet(url)); + if (response != null && response.getStatusLine().getStatusCode() == 200) { + builder.up(); + HttpEntity entity = response.getEntity(); + if (entity != null) { + String content = EntityUtils.toString(entity); + try { + JsonNode root = mapper.readTree(content); + if (root.has("status") && "green".equals(root.get("status").asText())) { + builder.live(); + } + if (root.has("cluster_name")) + builder.withData("cluster_name", root.get("cluster_name").asText()); + if (root.has("status")) + builder.withData("status", root.get("status").asText()); + if (root.has("timed_out")) + builder.withData("timed_out", root.get("timed_out").asBoolean()); + if (root.has("number_of_nodes")) + builder.withData("number_of_nodes", root.get("number_of_nodes").asLong()); + if (root.has("number_of_data_nodes")) + builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong()); + if (root.has("active_primary_shards")) + builder.withData("active_primary_shards", root.get("active_primary_shards").asLong()); + if (root.has("active_shards")) + builder.withData("active_shards", root.get("active_shards").asLong()); + if (root.has("relocating_shards")) + builder.withData("relocating_shards", root.get("relocating_shards").asLong()); + if (root.has("initializing_shards")) + builder.withData("initializing_shards", root.get("initializing_shards").asLong()); + if (root.has("unassigned_shards")) + builder.withData("unassigned_shards", root.get("unassigned_shards").asLong()); + } catch (Exception parseEx) { + // Fallback to simple LIVE detection + if (content.contains("\"status\":\"green\"")) { + builder.live(); + } } } } + } catch (IOException e) { + builder.error().withData("error", e.getMessage()); + LOGGER.error("Error while checking elasticsearch health", e); + } finally { + if (response != null) { + EntityUtils.consumeQuietly(response.getEntity()); + } } - } catch (IOException e) { - builder.error().withData("error", e.getMessage()); - LOGGER.error("Error while checking elasticsearch health", e); - } finally { - if (response != null) { - EntityUtils.consumeQuietly(response.getEntity()); - } + } else { + builder.error().withData("error", "Elasticsearch health check provider is not active"); } return builder.build(); } diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java index 7b97cfa12..f64f0df84 100644 --- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java +++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/OpenSearchHealthCheckProvider.java @@ -30,9 +30,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.apache.unomi.healthcheck.HealthCheckConfig; +import org.apache.unomi.healthcheck.HealthCheckProvider; import org.apache.unomi.healthcheck.HealthCheckResponse; import org.apache.unomi.healthcheck.util.CachedValue; import org.apache.unomi.shell.migration.utils.HttpUtils; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,13 +48,17 @@ * A Health Check that checks the status of the OpenSearch connectivity according to the provided configuration. * This connectivity should be LIVE before any try to start Unomi. */ -public class OpenSearchHealthCheckProvider implements PersistenceEngineHealthProvider { +@Component(service = HealthCheckProvider.class, immediate = true) +public class OpenSearchHealthCheckProvider implements HealthCheckProvider { public static final String NAME = "opensearch"; private static final Logger LOGGER = LoggerFactory.getLogger(OpenSearchHealthCheckProvider.class.getName()); private final CachedValue cache = new CachedValue<>(10, TimeUnit.SECONDS); + private final ObjectMapper mapper = new ObjectMapper(); + private boolean isActive = false; + @Reference(cardinality = ReferenceCardinality.MANDATORY) private HealthCheckConfig config; private CloseableHttpClient httpClient; @@ -58,21 +67,25 @@ public OpenSearchHealthCheckProvider() { LOGGER.info("Building OpenSearch health provider service..."); } + @Activate public void activate() { - LOGGER.info("Activating OpenSearch health provider service..."); - CredentialsProvider credentialsProvider = null; - String login = config.get(HealthCheckConfig.CONFIG_OS_LOGIN); // Reuse ElasticSearch credentials key - if (StringUtils.isNotEmpty(login)) { - credentialsProvider = new BasicCredentialsProvider(); - UsernamePasswordCredentials credentials - = new UsernamePasswordCredentials(login, config.get(HealthCheckConfig.CONFIG_OS_PASSWORD)); - credentialsProvider.setCredentials(AuthScope.ANY, credentials); - } - try { - httpClient = HttpUtils.initHttpClient( - Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_OS_TRUST_ALL_CERTIFICATES)), credentialsProvider); - } catch (IOException e) { - LOGGER.error("Unable to initialize http client", e); + if (config.isEnabled() && config.getEnabledProviders().stream().anyMatch(NAME::equals)) { + LOGGER.info("Activating OpenSearch health provider service..."); + this.isActive = true; + CredentialsProvider credentialsProvider = null; + String login = config.get(HealthCheckConfig.CONFIG_OS_LOGIN); // Reuse ElasticSearch credentials key + if (StringUtils.isNotEmpty(login)) { + credentialsProvider = new BasicCredentialsProvider(); + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(login, + config.get(HealthCheckConfig.CONFIG_OS_PASSWORD)); + credentialsProvider.setCredentials(AuthScope.ANY, credentials); + } + try { + httpClient = HttpUtils.initHttpClient(Boolean.parseBoolean(config.get(HealthCheckConfig.CONFIG_OS_TRUST_ALL_CERTIFICATES)), + credentialsProvider); + } catch (IOException e) { + LOGGER.error("Unable to initialize http client", e); + } } } @@ -92,63 +105,72 @@ public void setConfig(HealthCheckConfig config) { return cache.getValue(); } - @Override public HealthCheckResponse detailed() { - return execute(); - } - private HealthCheckResponse refresh() { LOGGER.debug("Refresh"); HealthCheckResponse.Builder builder = new HealthCheckResponse.Builder(); builder.name(NAME).down(); - String minimalClusterState = config.get(HealthCheckConfig.CONFIG_OS_MINIMAL_CLUSTER_STATE); - if (StringUtils.isEmpty(minimalClusterState)) { - minimalClusterState = "green"; - } else { - minimalClusterState = minimalClusterState.toLowerCase(); - } - String url = (config.get(HealthCheckConfig.CONFIG_OS_SSL_ENABLED).equals("true") ? "https://" : "http://") - .concat(config.get(HealthCheckConfig.CONFIG_OS_ADDRESSES).split(",")[0].trim()) - .concat("/_cluster/health"); - CloseableHttpResponse response = null; - try { - response = httpClient.execute(new HttpGet(url)); - if (response != null && response.getStatusLine().getStatusCode() == 200) { - builder.up(); - HttpEntity entity = response.getEntity(); - if (entity != null) { - String content = EntityUtils.toString(entity); - try { - ObjectMapper mapper = new ObjectMapper(); - JsonNode root = mapper.readTree(content); - String status = root.has("status") ? root.get("status").asText() : null; - if ("green".equals(status) || ("yellow".equals(status) && "yellow".equals(minimalClusterState))) { - builder.live(); - } - if (root.has("cluster_name")) builder.withData("cluster_name", root.get("cluster_name").asText()); - if (root.has("status")) builder.withData("status", root.get("status").asText()); - if (root.has("timed_out")) builder.withData("timed_out", root.get("timed_out").asBoolean()); - if (root.has("number_of_nodes")) builder.withData("number_of_nodes", root.get("number_of_nodes").asLong()); - if (root.has("number_of_data_nodes")) builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong()); - if (root.has("active_primary_shards")) builder.withData("active_primary_shards", root.get("active_primary_shards").asLong()); - if (root.has("active_shards")) builder.withData("active_shards", root.get("active_shards").asLong()); - if (root.has("relocating_shards")) builder.withData("relocating_shards", root.get("relocating_shards").asLong()); - if (root.has("initializing_shards")) builder.withData("initializing_shards", root.get("initializing_shards").asLong()); - if (root.has("unassigned_shards")) builder.withData("unassigned_shards", root.get("unassigned_shards").asLong()); - } catch (Exception parseEx) { - if (content.contains("\"status\":\"green\"") || - (content.contains("\"status\":\"yellow\"") && "yellow".equals(minimalClusterState))) { - builder.live(); + if (isActive) { + String minimalClusterState = config.get(HealthCheckConfig.CONFIG_OS_MINIMAL_CLUSTER_STATE); + if (StringUtils.isEmpty(minimalClusterState)) { + minimalClusterState = "green"; + } else { + minimalClusterState = minimalClusterState.toLowerCase(); + } + String url = (config.get(HealthCheckConfig.CONFIG_OS_SSL_ENABLED).equals("true") ? "https://" : "http://").concat( + config.get(HealthCheckConfig.CONFIG_OS_ADDRESSES).split(",")[0].trim()).concat("/_cluster/health"); + CloseableHttpResponse response = null; + try { + response = httpClient.execute(new HttpGet(url)); + if (response != null && response.getStatusLine().getStatusCode() == 200) { + builder.up(); + HttpEntity entity = response.getEntity(); + if (entity != null) { + String content = EntityUtils.toString(entity); + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(content); + String status = root.has("status") ? root.get("status").asText() : null; + if ("green".equals(status) || ("yellow".equals(status) && "yellow".equals(minimalClusterState))) { + builder.live(); + } + if (root.has("cluster_name")) + builder.withData("cluster_name", root.get("cluster_name").asText()); + if (root.has("status")) + builder.withData("status", root.get("status").asText()); + if (root.has("timed_out")) + builder.withData("timed_out", root.get("timed_out").asBoolean()); + if (root.has("number_of_nodes")) + builder.withData("number_of_nodes", root.get("number_of_nodes").asLong()); + if (root.has("number_of_data_nodes")) + builder.withData("number_of_data_nodes", root.get("number_of_data_nodes").asLong()); + if (root.has("active_primary_shards")) + builder.withData("active_primary_shards", root.get("active_primary_shards").asLong()); + if (root.has("active_shards")) + builder.withData("active_shards", root.get("active_shards").asLong()); + if (root.has("relocating_shards")) + builder.withData("relocating_shards", root.get("relocating_shards").asLong()); + if (root.has("initializing_shards")) + builder.withData("initializing_shards", root.get("initializing_shards").asLong()); + if (root.has("unassigned_shards")) + builder.withData("unassigned_shards", root.get("unassigned_shards").asLong()); + } catch (Exception parseEx) { + if (content.contains("\"status\":\"green\"") || (content.contains("\"status\":\"yellow\"") && "yellow".equals( + minimalClusterState))) { + builder.live(); + } } } } + } catch (IOException e) { + builder.error().withData("error", e.getMessage()); + LOGGER.error("Error while checking OpenSearch health", e); + } finally { + if (response != null) { + EntityUtils.consumeQuietly(response.getEntity()); + } } - } catch (IOException e) { - builder.error().withData("error", e.getMessage()); - LOGGER.error("Error while checking OpenSearch health", e); - } finally { - if (response != null) { - EntityUtils.consumeQuietly(response.getEntity()); - } + } else { + builder.error().withData("error", "Elasticsearch health check provider is not active"); } return builder.build(); } diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java deleted file mode 100644 index 276ba2272..000000000 --- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceEngineHealthProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.unomi.healthcheck.provider; - -import org.apache.unomi.healthcheck.HealthCheckProvider; -import org.apache.unomi.healthcheck.HealthCheckResponse; - -/** - * Common contract for persistence engine health providers to expose - * richer, implementation-specific health details. - */ -public interface PersistenceEngineHealthProvider extends HealthCheckProvider { - - /** - * Build a detailed response that may include implementation-specific data. - * - * @return a detailed {@link HealthCheckResponse} - */ - HealthCheckResponse detailed(); -} - - diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java index ec136fa33..b0d2725dc 100644 --- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java +++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/provider/PersistenceHealthCheckProvider.java @@ -20,7 +20,6 @@ import org.apache.unomi.api.PropertyType; import org.apache.unomi.healthcheck.HealthCheckResponse; import org.apache.unomi.healthcheck.HealthCheckProvider; -import org.apache.unomi.healthcheck.HealthCheckConfig; import org.apache.unomi.healthcheck.util.CachedValue; import org.apache.unomi.persistence.spi.PersistenceService; import org.osgi.service.component.annotations.Component; @@ -47,25 +46,16 @@ public class PersistenceHealthCheckProvider implements HealthCheckProvider { @Reference(service = PersistenceService.class, cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, bind = "bind", unbind = "unbind") private volatile PersistenceService service; - @Reference(cardinality = ReferenceCardinality.OPTIONAL) - private volatile HealthCheckConfig healthCheckConfig; - - // Lazily created delegate depending on the current persistence implementation - private volatile HealthCheckProvider delegate; - public PersistenceHealthCheckProvider() { LOGGER.info("Building persistence health provider service..."); } public void bind(PersistenceService service) { this.service = service; - // Reset delegate when persistence changes - this.delegate = null; } public void unbind(PersistenceService service) { this.service = null; - this.delegate = null; } @Override public String name() { @@ -74,17 +64,6 @@ public void unbind(PersistenceService service) { @Override public HealthCheckResponse execute() { LOGGER.debug("Health check persistence"); - - // If we can detect the underlying persistence, delegate to the appropriate provider - HealthCheckProvider resolved = resolveDelegate(); - if (resolved != null) { - if (resolved instanceof PersistenceEngineHealthProvider) { - return ((PersistenceEngineHealthProvider) resolved).detailed(); - } - return resolved.execute(); - } - - // Fallback to legacy behavior if no delegate is available yet if (cache.isStaled() || cache.getValue().isDown() || cache.getValue().isError()) { cache.setValue(refresh()); } @@ -109,49 +88,4 @@ private HealthCheckResponse refresh() { } return builder.build(); } - - private HealthCheckProvider resolveDelegate() { - try { - if (delegate != null) { - return delegate; - } - if (service == null) { - return null; - } - String persistenceName; - try { - persistenceName = service.getName(); - } catch (Throwable t) { - // Older SPI might not expose getName(); fallback to class inspection - persistenceName = service.getClass().getName().toLowerCase(); - } - - if (persistenceName == null) { - return null; - } - - if (persistenceName.contains("opensearch")) { - OpenSearchHealthCheckProvider provider = new OpenSearchHealthCheckProvider(); - if (healthCheckConfig != null) { - provider.setConfig(healthCheckConfig); - } - provider.activate(); - delegate = provider; - } else if (persistenceName.contains("elasticsearch")) { - ElasticSearchHealthCheckProvider provider = new ElasticSearchHealthCheckProvider(); - if (healthCheckConfig != null) { - provider.setConfig(healthCheckConfig); - } - provider.activate(); - delegate = provider; - } else { - // Unknown persistence implementation, no delegate - return null; - } - return delegate; - } catch (Exception e) { - LOGGER.warn("Unable to resolve delegated health check provider", e); - return null; - } - } } diff --git a/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg index 9c6083ab9..aeb6e37ee 100644 --- a/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg +++ b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg @@ -35,5 +35,5 @@ authentication.realm = ${org.apache.unomi.security.realm:-karaf} # Health check configuration healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-false} -healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,opensearch,unomi,persistence} +healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,unomi,persistence} healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400} diff --git a/manual/src/main/asciidoc/5-min-quickstart.adoc b/manual/src/main/asciidoc/5-min-quickstart.adoc index c92fd683e..57e7eb337 100644 --- a/manual/src/main/asciidoc/5-min-quickstart.adoc +++ b/manual/src/main/asciidoc/5-min-quickstart.adoc @@ -75,10 +75,11 @@ services: unomi: image: apache/unomi:3.0.0 environment: - - UNOMI_AUTO_START=opensearch + - UNOMI_PERSISTENCE_TYPE=opensearch - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-admin} + - UNOMI_HEALTHCHECK_PROVIDERS=cluster,opensearch,unomi,persistence ports: - 8181:8181 - 9443:9443 diff --git a/manual/src/main/asciidoc/configuration.adoc b/manual/src/main/asciidoc/configuration.adoc index 6cacb05e2..78a500ee2 100644 --- a/manual/src/main/asciidoc/configuration.adoc +++ b/manual/src/main/asciidoc/configuration.adoc @@ -107,11 +107,11 @@ To select which search engine to use, you can: * For OpenSearch: add `-Duse.opensearch=true` to your Maven command (this will only impact the integration tests if they are activated using the -Pintegration-tests profile) 3. When using Docker: - * For ElasticSearch: use `UNOMI_AUTO_START=elasticsearch` - * For OpenSearch: use `UNOMI_AUTO_START=opensearch` - * For custom configurations: use `UNOMI_AUTO_START=your-custom-config-name` + * For ElasticSearch: use `UNOMI_PERSISTENCE_CONFIG=elasticsearch` + * For OpenSearch: use `UNOMI_PERSISTENCE_CONFIG=opensearch` + * For custom configurations: use `UNOMI_PERSISTENCE_CONFIG=your-custom-config-name` -Note: The `UNOMI_AUTO_START` environment variable accepts any start features configuration name defined in the `org.apache.unomi.start.cfg` file. You can use the predefined "elasticsearch" or "opensearch" configurations, or specify a custom configuration name if you have created one. +Note: The `UNOMI_PERSISTENCE_CONFIG` environment variable accepts any start features configuration name defined in the `org.apache.unomi.start.cfg` file. You can use the predefined "elasticsearch" or "opensearch" configurations, or specify a custom configuration name if you have created one. Note: When using OpenSearch 3.x: - Security is enabled by default and requires SSL/TLS @@ -972,7 +972,7 @@ You can use your custom configurations with: unomi:start elasticsearch-dev unomi:start opensearch-dev -# Staging environment +# Staging environment unomi:start elasticsearch-staging unomi:start opensearch-staging @@ -981,27 +981,33 @@ unomi:start elasticsearch-prod unomi:start opensearch-prod ---- -==== Auto-Start Configuration +==== Auto-Start -You can configure Apache Unomi to automatically start with a specific start features configuration when the server boots up. This is useful for production deployments where you want the server to start automatically without manual intervention. +You can configure Apache Unomi to automatically start when the server boots up. See the next section for details. This is useful for production deployments where you want the server to start automatically without manual intervention. -To enable auto-start, set the `unomi.autoStart` system property or `UNOMI_AUTO_START` environment variable to the name of your desired start features configuration: +To enable auto-start, set the `unomi.autoStart` system property or `UNOMI_AUTO_START` environment variable to 'true' + +Note: Auto-start only works when Apache Unomi is not already running. If the server is already started, the auto-start setting will be ignored. + +==== Start Configuration + +You can define a startup configuration for Apache Unomi to configure desired features when the server boots up. + +To set start configuration, set the `unomi.startConfig` system property or `UNOMI_START_CONFIG` environment variable to the name of your desired start features configuration: [source] ---- # Using system property --Dunomi.autoStart=elasticsearch-prod +-Dunomi.startConfig=elasticsearch-prod # Using environment variable (Docker) -UNOMI_AUTO_START=opensearch-prod +UNOMI_START_CONFIG=opensearch-prod # Using custom configuration -UNOMI_AUTO_START=elasticsearch-staging +UNOMI_START_CONFIG=elasticsearch-staging ---- -The auto-start feature accepts any start features configuration name defined in your `org.apache.unomi.start.cfg` file. If you set it to `true`, it will default to the "elasticsearch" configuration for backward compatibility. - -Note: Auto-start only works when Apache Unomi is not already running. If the server is already started, the auto-start setting will be ignored. +The start-configuration feature accepts any start features configuration name defined in your `org.apache.unomi.start.cfg` file. If you set it to `true`, it will default to the "elasticsearch" configuration for backward compatibility. ==== Important Notes @@ -1034,7 +1040,7 @@ services: volumes: - ./custom-start.cfg:/opt/apache-unomi/etc/org.apache.unomi.start.cfg environment: - - UNOMI_AUTO_START=elasticsearch-prod # or opensearch-prod + - UNOMI_START_CONFIG=elasticsearch-prod # or opensearch-prod depends_on: - elasticsearch elasticsearch: @@ -1046,7 +1052,7 @@ services: **Key Points:** - Mount your custom configuration file to `/opt/apache-unomi/etc/org.apache.unomi.start.cfg` -- Use the `UNOMI_AUTO_START` environment variable to specify which configuration to use +- Use the `UNOMI_START_CONFIG` environment variable to specify which configuration to use - The configuration file will override the default settings when the container starts === Customizing Apache Unomi Distribution @@ -1113,7 +1119,7 @@ services: context: . dockerfile: Dockerfile environment: - - UNOMI_AUTO_START=elasticsearch-prod + - UNOMI_START_CONFIG=elasticsearch-prod depends_on: - elasticsearch elasticsearch: diff --git a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc index a42523239..500572d03 100644 --- a/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc +++ b/manual/src/main/asciidoc/migrations/migrate-2.x-to-3.0.adoc @@ -222,8 +222,8 @@ When legacy queryBuilder IDs are used, Apache Unomi will log warning messages th **Example Warning Log:** [source] ---- -WARN - DEPRECATED: Using legacy queryBuilderId 'idsConditionESQueryBuilder' for condition type 'customIdsCondition'. -Please update your condition definition to use the new queryBuilderId 'idsConditionQueryBuilder'. +WARN - DEPRECATED: Using legacy queryBuilderId 'idsConditionESQueryBuilder' for condition type 'customIdsCondition'. +Please update your condition definition to use the new queryBuilderId 'idsConditionQueryBuilder'. Legacy mappings are deprecated and may be removed in future versions. ---- @@ -253,11 +253,11 @@ org.apache.unomi.opensearch.sslTrustAllCertificates=true ===== Docker Configuration Changes -When using Docker, you can specify the search engine backend: +When using Docker, you can specify the search engine backend using the start configuration environment variable `UNOMI_START_CONFIG`.: -- **For Elasticsearch**: `UNOMI_AUTO_START=elasticsearch` -- **For OpenSearch**: `UNOMI_AUTO_START=opensearch` -- **For custom configurations**: `UNOMI_AUTO_START=your-custom-config-name` +- **For Elasticsearch**: `UNOMI_START_CONFIG=elasticsearch` +- **For OpenSearch**: `UNOMI_START_CONFIG=opensearch` +- **For custom configurations**: `UNOMI_START_CONFIG=your-custom-config-name` ==== Migrating from Elasticsearch to OpenSearch From 0df614254f6eae6b2f04a1d3889ca5535d30b2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Wed, 26 Nov 2025 10:36:11 +0100 Subject: [PATCH 2/7] Add a layer of assembly: distribution, to replace OSGI config for a list of features to install by using a real OSGI feature. Modify the Start command to start a 'distribution' feature --- bom/artifacts/pom.xml | 15 ++ distribution/pom.xml | 229 ++++++++++++++++++ distribution/src/main/feature/feature.xml | 75 ++++++ .../main/docker/docker-compose-build-es.yml | 2 +- .../main/docker/docker-compose-build-os.yml | 2 +- docker/src/main/docker/docker-compose-es.yml | 2 +- docker/src/main/docker/docker-compose-os.yml | 2 +- kar/src/main/feature/feature.xml | 1 - pom.xml | 1 + tools/shell-commands/pom.xml | 12 +- .../org/apache/unomi/shell/actions/Start.java | 15 +- .../internal/UnomiManagementServiceImpl.java | 166 ++++++------- .../main/resources/org.apache.unomi.start.cfg | 18 -- 13 files changed, 413 insertions(+), 127 deletions(-) create mode 100644 distribution/pom.xml create mode 100644 distribution/src/main/feature/feature.xml delete mode 100644 tools/shell-commands/src/main/resources/org.apache.unomi.start.cfg diff --git a/bom/artifacts/pom.xml b/bom/artifacts/pom.xml index b0c0ec5b2..c2db4e179 100644 --- a/bom/artifacts/pom.xml +++ b/bom/artifacts/pom.xml @@ -60,6 +60,21 @@ unomi-persistence-elasticsearch-core ${project.version} + + org.apache.unomi + unomi-persistence-elasticsearch-conditions + ${project.version} + + + org.apache.unomi + unomi-persistence-opensearch-core + ${project.version} + + + org.apache.unomi + unomi-persistence-opensearch-conditions + ${project.version} + org.apache.unomi unomi-json-schema-services diff --git a/distribution/pom.xml b/distribution/pom.xml new file mode 100644 index 000000000..0c469361a --- /dev/null +++ b/distribution/pom.xml @@ -0,0 +1,229 @@ + + + + + 4.0.0 + + org.apache.unomi + unomi-root + 3.1.0-SNAPSHOT + + unomi-distribution + Apache Unomi :: Distribution + Apache Unomi Distribution's Karaf features assemblies for the Apache Unomi Context Server + kar + + + + + org.apache.unomi + unomi-bom + ${project.version} + pom + import + + + + + + + org.apache.unomi + unomi-api + + + org.apache.unomi + unomi-common + + + org.apache.unomi + unomi-wab + + + org.apache.unomi + unomi-rest + + + org.apache.unomi + unomi-metrics + + + org.apache.unomi + unomi-services + + + org.apache.unomi + unomi-scripting + + + org.apache.unomi + unomi-persistence-elasticsearch-core + + + org.apache.unomi + unomi-persistence-elasticsearch-conditions + + + org.apache.unomi + unomi-persistence-opensearch-core + + + org.apache.unomi + unomi-persistence-opensearch-conditions + + + + + org.apache.unomi + unomi-plugins-base + + + org.apache.unomi + unomi-plugins-request + + + org.apache.unomi + unomi-plugins-mail + + + org.apache.unomi + unomi-plugins-optimization-test + + + + + org.apache.unomi + cxs-lists-extension-services + + + org.apache.unomi + cxs-lists-extension-rest + + + org.apache.unomi + cxs-lists-extension-actions + + + org.apache.unomi + cxs-geonames-services + + + org.apache.unomi + cxs-geonames-rest + + + org.apache.unomi + cxs-privacy-extension-services + + + org.apache.unomi + cxs-privacy-extension-rest + + + org.apache.unomi + unomi-json-schema-services + + + org.apache.unomi + unomi-json-schema-rest + + + org.apache.unomi + shell-dev-commands + + + org.apache.unomi + unomi-web-tracker-wab + + + + joda-time + joda-time + + + + org.apache.httpcomponents + httpcore-osgi + + + org.apache.httpcomponents + httpclient-osgi + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + true + + false + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + 85 + + + + generate-features + generate-resources + + features-generate-descriptor + + + true + + + + verify + process-resources + + verify + + + + mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features + mvn:org.apache.karaf.features/enterprise/${karaf.version}/xml/features + file:${project.build.directory}/feature/feature.xml + + org.apache.karaf:apache-karaf:zip:${karaf.version} + 17 + + framework + + + + unomi-distribution-opensearch + unomi-distribution-elasticsearch + + + false + + + + + + + diff --git a/distribution/src/main/feature/feature.xml b/distribution/src/main/feature/feature.xml new file mode 100644 index 000000000..2663d2f81 --- /dev/null +++ b/distribution/src/main/feature/feature.xml @@ -0,0 +1,75 @@ + + + + + + mvn:org.apache.karaf.features/specs/${karaf.version}/xml/features + mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features + mvn:org.apache.unomi/unomi-kar/${project.version}/xml/features + + + unomi-base + unomi-startup + unomi-elasticsearch-core + unomi-persistence-core + unomi-services + unomi-rest-api + unomi-cxs-lists-extension + unomi-cxs-geonames-extension + unomi-cxs-privacy-extension + unomi-elasticsearch-conditions + unomi-plugins-base + unomi-plugins-request + unomi-plugins-mail + unomi-plugins-optimization-test + unomi-shell-dev-commands + unomi-wab + unomi-web-tracker + unomi-healthcheck + unomi-router-karaf-feature + unomi-groovy-actions + unomi-rest-ui + unomi-startup-complete + + + + unomi-base + unomi-startup + unomi-elasticsearch-core + unomi-persistence-core + unomi-services + unomi-rest-api + unomi-cxs-lists-extension + unomi-cxs-geonames-extension + unomi-cxs-privacy-extension + unomi-elasticsearch-conditions + unomi-plugins-base + unomi-plugins-request + unomi-plugins-mail + unomi-plugins-optimization-test + unomi-shell-dev-commands + unomi-wab + unomi-web-tracker + unomi-healthcheck + unomi-router-karaf-feature + unomi-groovy-actions + unomi-rest-ui + unomi-startup-complete + + + diff --git a/docker/src/main/docker/docker-compose-build-es.yml b/docker/src/main/docker/docker-compose-build-es.yml index 1143be9fc..1138b7781 100644 --- a/docker/src/main/docker/docker-compose-build-es.yml +++ b/docker/src/main/docker/docker-compose-build-es.yml @@ -36,7 +36,7 @@ services: image: apache/unomi:${project.version} environment: - UNOMI_AUTO_START=true - - UNOMI_START_CONFIG=elasticsearch + - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings diff --git a/docker/src/main/docker/docker-compose-build-os.yml b/docker/src/main/docker/docker-compose-build-os.yml index 7fda5596f..ca8c1df05 100644 --- a/docker/src/main/docker/docker-compose-build-os.yml +++ b/docker/src/main/docker/docker-compose-build-os.yml @@ -96,7 +96,7 @@ services: container_name: unomi environment: - UNOMI_AUTO_START=true - - UNOMI_START_CONFIG=opensearch + - UNOMI_DISTRIBUTION=unomi-distribution-opensearch - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} diff --git a/docker/src/main/docker/docker-compose-es.yml b/docker/src/main/docker/docker-compose-es.yml index 07d6f019b..149b177cc 100644 --- a/docker/src/main/docker/docker-compose-es.yml +++ b/docker/src/main/docker/docker-compose-es.yml @@ -42,7 +42,7 @@ services: image: apache/unomi:${project.version} environment: - UNOMI_AUTO_START=true - - UNOMI_START_CONFIG=elasticsearch + - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings diff --git a/docker/src/main/docker/docker-compose-os.yml b/docker/src/main/docker/docker-compose-os.yml index 8ee97e05f..594e057d3 100644 --- a/docker/src/main/docker/docker-compose-os.yml +++ b/docker/src/main/docker/docker-compose-os.yml @@ -80,7 +80,7 @@ services: container_name: unomi environment: - UNOMI_AUTO_START=true - - UNOMI_START_CONFIG=opensearch + - UNOMI_DISTRIBUTION=unomi-distribution-opensearch - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml index d4f83fe94..5c8406985 100644 --- a/kar/src/main/feature/feature.xml +++ b/kar/src/main/feature/feature.xml @@ -75,7 +75,6 @@ unomi-base mvn:org.apache.unomi/shell-commands/${project.version}/cfg/migration - mvn:org.apache.unomi/shell-commands/${project.version}/cfg/start mvn:org.apache.unomi/shell-commands/${project.version} diff --git a/pom.xml b/pom.xml index 54ce7cd0a..109ec9335 100644 --- a/pom.xml +++ b/pom.xml @@ -419,6 +419,7 @@ extensions/weather-update graphql samples + distribution package diff --git a/tools/shell-commands/pom.xml b/tools/shell-commands/pom.xml index fcd3a1584..ed2cf406b 100644 --- a/tools/shell-commands/pom.xml +++ b/tools/shell-commands/pom.xml @@ -90,6 +90,11 @@ org.osgi.service.metatype.annotations provided + + org.apache.felix + org.apache.felix.configadmin + provided + org.apache.commons @@ -178,13 +183,6 @@ cfg migration - - - src/main/resources/org.apache.unomi.start.cfg - - cfg - start - diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java index 3e1c3e89e..0b213abc1 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java @@ -31,20 +31,15 @@ public class Start implements Action { @Reference UnomiManagementService unomiManagementService; - @Argument(name = "startFeatures", description = "Start features configuration to use (elasticsearch/opensearch)", valueToShowInHelp = "elasticsearch") - private String selectedStartFeatures = "elasticsearch"; + @Argument(name = "distribution", description = "Distribution feature to use (unomi-distribution-elasticsearch/unomi-distribution-opensearch)", valueToShowInHelp = "unomi-distribution-elasticsearch") + private String distribution = "unomi-distribution-elasticsearch"; - @Option(name = "-i", aliases = "--install-only", description = "Only install features, don't start them", required = false, multiValued = false) + @Option(name = "-i", aliases = "--install-only", description = "Only install distribution feature's dependencies, don't start them", required = false, multiValued = false) boolean installOnly = false; public Object execute() throws Exception { - if (!selectedStartFeatures.equals("elasticsearch") && - !selectedStartFeatures.equals("opensearch")) { - System.err.println("Invalid value '"+selectedStartFeatures+"' specified for start features configuration, will default to elasticsearch"); - selectedStartFeatures = "elasticsearch"; - } - System.out.println("Starting Apache Unomi with start features configuration: " + selectedStartFeatures); - unomiManagementService.startUnomi(selectedStartFeatures, !installOnly); + System.out.println("Starting Apache Unomi distribution: " + distribution); + unomiManagementService.startUnomi(distribution, !installOnly); return null; } diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java index 87a89c1b1..c461c0653 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java @@ -17,6 +17,7 @@ package org.apache.unomi.shell.services.internal; import org.apache.commons.lang3.StringUtils; +import org.apache.karaf.features.Dependency; import org.apache.karaf.features.Feature; import org.apache.karaf.features.FeatureState; import org.apache.karaf.features.FeaturesService; @@ -24,12 +25,14 @@ import org.apache.unomi.shell.migration.MigrationService; import org.apache.unomi.shell.services.UnomiManagementService; import org.osgi.framework.BundleContext; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.*; -import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.*; import java.util.concurrent.*; @@ -81,56 +84,61 @@ * @see org.apache.karaf.features.FeaturesService * @see org.apache.karaf.features.Feature */ -@Component(service = UnomiManagementService.class, immediate = true, configurationPid = "org.apache.unomi.start", configurationPolicy = ConfigurationPolicy.REQUIRE) -@Designate(ocd = UnomiManagementServiceConfiguration.class) +@Component(service = UnomiManagementService.class, immediate = true) public class UnomiManagementServiceImpl implements UnomiManagementService { private static final Logger LOGGER = LoggerFactory.getLogger(UnomiManagementServiceImpl.class.getName()); private static final int DEFAULT_TIMEOUT = 300; // 5 minutes timeout - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - + private static final String SETUP_CONFIG_PID = "org.apache.unomi.setup"; private static final String CDP_GRAPHQL_FEATURE = "cdp-graphql-feature"; - private BundleContext bundleContext; - @Reference(cardinality = ReferenceCardinality.MANDATORY) private MigrationService migrationService; + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private ConfigurationAdmin configurationAdmin; + @Reference(cardinality = ReferenceCardinality.MANDATORY) private FeaturesService featuresService; @Reference(cardinality = ReferenceCardinality.MANDATORY) private BundleWatcher bundleWatcher; - private Map> startFeatures = new HashMap>(); - private final List installedFeatures = new ArrayList<>(); - private final List startedFeatures = new ArrayList<>(); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private BundleContext bundleContext; + private final List installedDistributionDependencies = new ArrayList<>(); + private final List startedDistributionDependencies = new ArrayList<>(); @Activate - public void init(ComponentContext componentContext, UnomiManagementServiceConfiguration config) throws Exception { - LOGGER.info("Initializing Unomi management service with configuration {}", config); + public void init(ComponentContext componentContext) throws Exception { + LOGGER.info("Initializing Unomi management service"); try { this.bundleContext = componentContext.getBundleContext(); - this.startFeatures = parseStartFeatures(config.startFeatures()); + + String distribution = getSetupDistribution(); + if (distribution == null) { + LOGGER.info("No previously setup distribution found"); + if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.distribution"))) { + distribution = bundleContext.getProperty("unomi.distribution"); + LOGGER.warn("Setting up distribution to the one provided in context: {}", distribution); + setupDistribution(distribution); + } else { + distribution = "unomi-distribution-elasticsearch"; + LOGGER.warn("No unomi.distribution provided, defaulting to '{}'", distribution); + setupDistribution(distribution); + } + } if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.autoMigrate"))) { migrationService.migrateUnomi(bundleContext.getProperty("unomi.autoMigrate"), true, null); } - String autoStart = bundleContext.getProperty("unomi.autoStart"); - if (StringUtils.isNotBlank(autoStart)) { - String resolvedAutoStart = autoStart; - if ("true".equals(autoStart)) { - resolvedAutoStart = "elasticsearch"; - } if ("elasticsearch".equals(autoStart)) { - resolvedAutoStart = "elasticsearch"; - } if ("opensearch".equals(autoStart)) { - resolvedAutoStart = "opensearch"; - } - LOGGER.info("Auto-starting unomi management service with start features configuration: {}", resolvedAutoStart); + if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.autoStart")) && + bundleContext.getProperty("unomi.autoStart").equals("true")) { + LOGGER.info("Auto-starting unomi management service with unomi distribution: {}", distribution); // Don't wait for completion during initialization - startUnomi(resolvedAutoStart, true, false); + startUnomi(distribution, true, false); } } catch (Exception e) { LOGGER.error("Error during Unomi startup:", e); @@ -138,46 +146,36 @@ public void init(ComponentContext componentContext, UnomiManagementServiceConfig } } - private List getAdditionalFeaturesToInstall() { - List featuresToInstall = new ArrayList<>(); - if (Boolean.parseBoolean(bundleContext.getProperty("org.apache.unomi.graphql.feature.activated"))) { - featuresToInstall.add(CDP_GRAPHQL_FEATURE); - bundleWatcher.addRequiredBundle("org.apache.unomi.cdp-graphql-api-impl"); - bundleWatcher.addRequiredBundle("org.apache.unomi.graphql-ui"); + private String getSetupDistribution() throws IOException { + Configuration configuration = configurationAdmin.getConfiguration(SETUP_CONFIG_PID, "?"); + Dictionary properties = configuration.getProperties(); + if (properties != null && properties.get("unomi.distribution") != null) { + return Objects.toString(properties.get("unomi.distribution"), null); } - return featuresToInstall; + return null; } - private Map> parseStartFeatures(String[] startFeaturesConfig) { - Map> startFeatures = new HashMap<>(); - if (startFeaturesConfig == null) { - return startFeatures; + private void setupDistribution(String distribution) throws IOException { + Configuration configuration = configurationAdmin.getConfiguration(SETUP_CONFIG_PID, "?"); + Dictionary properties = configuration.getProperties(); + if (properties == null) { + properties = new Hashtable<>(); } - - for (String entry : startFeaturesConfig) { - String[] parts = entry.split("="); - if (parts.length == 2) { - String key = parts[0].trim(); - List features = new ArrayList<>(Arrays.asList(parts[1].split(","))); - startFeatures.put(key, features); - } else { - LOGGER.warn("Invalid start feature entry: {}", entry); - } - } - return startFeatures; + properties.put("unomi.distribution", distribution); + configuration.update(properties); } @Override - public void startUnomi(String selectedStartFeatures, boolean mustStartFeatures) throws Exception { + public void startUnomi(String distribution, boolean mustStartFeatures) throws Exception { // Default to waiting for completion - startUnomi(selectedStartFeatures, mustStartFeatures, true); + startUnomi(distribution, mustStartFeatures, true); } @Override - public void startUnomi(String selectedStartFeatures, boolean mustStartFeatures, boolean waitForCompletion) throws Exception { + public void startUnomi(String distribution, boolean mustStartFeatures, boolean waitForCompletion) throws Exception { Future future = executor.submit(() -> { try { - doStartUnomi(selectedStartFeatures, mustStartFeatures); + doStartUnomi(distribution, mustStartFeatures); } catch (Exception e) { LOGGER.error("Error starting Unomi:", e); throw new RuntimeException(e); @@ -194,47 +192,41 @@ public void startUnomi(String selectedStartFeatures, boolean mustStartFeatures, } } - private void doStartUnomi(String selectedStartFeatures, boolean mustStartFeatures) throws Exception { - List features = startFeatures.get(selectedStartFeatures); - if (features == null || features.isEmpty()) { - LOGGER.warn("No features configured for start features configuration: {}", selectedStartFeatures); + private void doStartUnomi(String distribution, boolean mustStartDistribution) throws Exception { + if (distribution == null || distribution.isEmpty()) { + LOGGER.warn("No distribution provided, unable to start Unomi."); return; } - features.addAll(getAdditionalFeaturesToInstall()); - - LOGGER.info("Installing features for start features configuration: {}", selectedStartFeatures); - for (String featureName : features) { - try { - Feature feature = featuresService.getFeature(featureName); - if (feature == null) { - LOGGER.error("Feature not found: {}", featureName); - continue; - } + try { + Feature feature = featuresService.getFeature(distribution); + if (feature == null) { + LOGGER.error("Distribution feature not found: {}", distribution); + return; + } - if (!installedFeatures.contains(featureName)) { - LOGGER.info("Installing feature: {}", featureName); - featuresService.installFeature(featureName, EnumSet.of(FeaturesService.Option.NoAutoStartBundles)); - installedFeatures.add(featureName); + for (Dependency dependency : feature.getDependencies()) { + if (!installedDistributionDependencies.contains(dependency.getName())) { + LOGGER.info("Installing distribution feature's dependency: {}", dependency.getName()); + featuresService.installFeature(dependency.getName(), dependency.getVersion(), EnumSet.of(FeaturesService.Option.NoAutoStartBundles)); + installedDistributionDependencies.add(dependency.getName()); } - } catch (Exception e) { - LOGGER.error("Error installing feature: {}", featureName, e); } + } catch (Exception e) { + LOGGER.error("Error installing distribution: {}", distribution, e); } - if (mustStartFeatures) { - LOGGER.info("Starting features for start features configuration: {}", selectedStartFeatures); - for (String featureName : features) { + if (mustStartDistribution) { + LOGGER.info("Starting distribution: {}", distribution); + for (String featureName : installedDistributionDependencies) { try { Feature feature = featuresService.getFeature(featureName); if (feature == null) { - LOGGER.error("Feature not found: {}", featureName); + LOGGER.error("Distribution feature's dependency not found: {}", featureName); continue; } - if (mustStartFeatures) { - LOGGER.info("Starting feature: {}", featureName); - startFeature(featureName); - startedFeatures.add(featureName); // Keep track of started features - } + LOGGER.info("Starting dependency: {}", featureName); + startFeature(featureName); + startedDistributionDependencies.add(featureName); // Keep track of started distribution dependencies } catch (Exception e) { LOGGER.error("Error starting feature: {}", featureName, e); } @@ -270,11 +262,11 @@ public void stopUnomi(boolean waitForCompletion) throws Exception { } private void doStopUnomi() throws Exception { - if (startedFeatures.isEmpty()) { + if (startedDistributionDependencies.isEmpty()) { LOGGER.info("No features to stop."); } else { LOGGER.info("Stopping features in reverse order..."); - ListIterator iterator = startedFeatures.listIterator(startedFeatures.size()); + ListIterator iterator = startedDistributionDependencies.listIterator(startedDistributionDependencies.size()); while (iterator.hasPrevious()) { String featureName = iterator.previous(); try { @@ -285,13 +277,13 @@ private void doStopUnomi() throws Exception { } } - startedFeatures.clear(); // Clear the list after stopping all features + startedDistributionDependencies.clear(); // Clear the list after stopping all features } - if (installedFeatures.isEmpty()) { + if (installedDistributionDependencies.isEmpty()) { LOGGER.info("No features to uninstall."); } else { LOGGER.info("Stopping features in reverse order..."); - ListIterator iterator = installedFeatures.listIterator(installedFeatures.size()); + ListIterator iterator = installedDistributionDependencies.listIterator(installedDistributionDependencies.size()); while (iterator.hasPrevious()) { String featureName = iterator.previous(); try { @@ -301,7 +293,7 @@ private void doStopUnomi() throws Exception { LOGGER.error("Error uninstalling feature: {}", featureName, e); } } - installedFeatures.clear(); // Clear the list after stopping all features + installedDistributionDependencies.clear(); // Clear the list after stopping all features } } diff --git a/tools/shell-commands/src/main/resources/org.apache.unomi.start.cfg b/tools/shell-commands/src/main/resources/org.apache.unomi.start.cfg deleted file mode 100644 index c9158a9d6..000000000 --- a/tools/shell-commands/src/main/resources/org.apache.unomi.start.cfg +++ /dev/null @@ -1,18 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -startFeatures = [ "elasticsearch=unomi-base,unomi-startup,unomi-elasticsearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-elasticsearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete", \ - "opensearch=unomi-base,unomi-startup,unomi-opensearch-core,unomi-persistence-core,unomi-services,unomi-rest-api,unomi-cxs-lists-extension,unomi-cxs-geonames-extension,unomi-cxs-privacy-extension,unomi-opensearch-conditions,unomi-plugins-base,unomi-plugins-request,unomi-plugins-mail,unomi-plugins-optimization-test,unomi-shell-dev-commands,unomi-wab,unomi-web-tracker,unomi-healthcheck,unomi-router-karaf-feature,unomi-groovy-actions,unomi-rest-ui,unomi-startup-complete" ] From 5a66366bbf90420ec852051880b87801ef00fba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Wed, 26 Nov 2025 17:24:01 +0100 Subject: [PATCH 3/7] Finalize migration to unomi distribution usage Define a UnomiSetup OSGI config to persist distribution used at first startup --- bom/artifacts/pom.xml | 7 ++ docker/README.md | 71 ++++++++++------- .../main/docker/docker-compose-build-es.yml | 2 +- docker/src/main/docker/docker-compose-es.yml | 2 +- docker/src/main/docker/entrypoint.sh | 23 +++--- package/pom.xml | 9 +++ .../UnomiManagementServiceConfiguration.java | 52 ------------- .../internal/UnomiManagementServiceImpl.java | 50 +++++------- .../shell/services/internal/UnomiSetup.java | 77 +++++++++++++++++++ 9 files changed, 171 insertions(+), 122 deletions(-) delete mode 100644 tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceConfiguration.java create mode 100644 tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiSetup.java diff --git a/bom/artifacts/pom.xml b/bom/artifacts/pom.xml index c2db4e179..ecc78948d 100644 --- a/bom/artifacts/pom.xml +++ b/bom/artifacts/pom.xml @@ -258,6 +258,13 @@ features xml + + org.apache.unomi + unomi-distribution + ${project.version} + features + xml + diff --git a/docker/README.md b/docker/README.md index ec25a2fd8..ae1e4f93b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -45,48 +45,67 @@ If you want to run it without docker-compose you should then make sure you setup For ElasticSearch: - docker pull docker.elastic.co/elasticsearch/elasticsearch:7.4.2 - docker network create unomi - docker run --name elasticsearch --net unomi -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e cluster.name=contextElasticSearch docker.elastic.co/elasticsearch/elasticsearch:7.4.2 +```bash +docker pull docker.elastic.co/elasticsearch/elasticsearch:9.2.1 +docker network create unomi +docker run -d --name elasticsearch --net unomi -p 9200:9200 -p 9300:9300 \ + -e "discovery.type=single-node" \ + -e "xpack.security.enabled=false" \ + -e cluster.name=contextElasticSearch \ + docker.elastic.co/elasticsearch/elasticsearch:9.2.1 +``` For OpenSearch: - docker pull opensearchproject/opensearch:3.0.0 - docker network create unomi - export OPENSEARCH_ADMIN_PASSWORD=enter_your_custom_admin_password_here - docker run --name opensearch --net unomi -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} opensearchproject/opensearch:3.0.0 +```bash +docker pull opensearchproject/opensearch:3.0.0 +docker network create unomi +export OPENSEARCH_ADMIN_PASSWORD=enter_your_custom_admin_password_here +docker run -d --name opensearch --net unomi -p 9200:9200 -p 9300:9300 \ + -e "discovery.type=single-node" \ + -e OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ + opensearchproject/opensearch:3.0.0 +``` For Unomi (with ElasticSearch): - docker pull apache/unomi:3.0.0-SNAPSHOT - docker run --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 \ - apache/unomi:3.0.0-SNAPSHOT +```bash +docker pull apache/unomi:3.1.0-SNAPSHOT +docker run -d --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ + -e UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 \ + apache/unomi:3.1.0-SNAPSHOT +``` For Unomi (with OpenSearch): - docker pull apache/unomi:3.0.0-SNAPSHOT - docker run --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_PERSISTENCE_CONFIG=opensearch \ - -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \ - -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ - apache/unomi:3.0.0-SNAPSHOT +```bash +docker pull apache/unomi:3.1.0-SNAPSHOT +docker run -d --name unomi --net unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ + -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch \ + -e UNOMI_OPENSEARCH_ADDRESSES=opensearch:9200 \ + -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} + apache/unomi:3.1.0-SNAPSHOT +``` ## Using a host OS Search Engine installation (only supported on macOS & Windows) For ElasticSearch: - docker run --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_ELASTICSEARCH_ADDRESSES=host.docker.internal:9200 \ - apache/unomi:3.0.0-SNAPSHOT +```bash +docker run -d --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ + -e UNOMI_ELASTICSEARCH_ADDRESSES=host.docker.internal:9200 \ + apache/unomi:3.1.0-SNAPSHOT +``` For OpenSearch: - docker run --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ - -e UNOMI_PERSISTENCE_CONFIG=opensearch - -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \ - -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ - apache/unomi:3.0.0-SNAPSHOT +```bash +docker run -d --name unomi -p 8181:8181 -p 9443:9443 -p 8102:8102 \ + -e UNOMI_DISTRIBUTION=unomi-distribution-opensearch + -e UNOMI_OPENSEARCH_ADDRESSES=host.docker.internal:9200 \ + -e UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \ + apache/unomi:3.1.0-SNAPSHOT +``` Note: Linux doesn't support the host.docker.internal DNS lookup method yet, it should be available in an upcoming version of Docker. See https://github.com/docker/for-linux/issues/264 @@ -94,7 +113,7 @@ Note: Linux doesn't support the host.docker.internal DNS lookup method yet, it s ### Common Variables - `UNOMI_AUTO_START`: Boolean to specify if unomi auto start with karaf (defaults to `true`) -- `UNOMI_PERSISTENCE_CONFIG`: Specifies the search engine configuration (`elasticsearch` or `opensearch`, defaults to `elasticsearch`) +- `UNOMI_DISTRIBUTION`: Specifies the Unomi Distribution Feature to use (`unomi-distribution-elasticsearch` or `unomi-distribution-opensearch`, defaults to `unomi-distribution-elasticsearch`) ### ElasticSearch-specific Variables - `UNOMI_ELASTICSEARCH_ADDRESSES`: ElasticSearch host:port (default: localhost:9200) diff --git a/docker/src/main/docker/docker-compose-build-es.yml b/docker/src/main/docker/docker-compose-build-es.yml index 1138b7781..be9f882af 100644 --- a/docker/src/main/docker/docker-compose-build-es.yml +++ b/docker/src/main/docker/docker-compose-build-es.yml @@ -17,7 +17,7 @@ version: '2.4' services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3 + image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1 volumes: - unomi-3-elasticsearch-data:/usr/share/elasticsearch/data environment: diff --git a/docker/src/main/docker/docker-compose-es.yml b/docker/src/main/docker/docker-compose-es.yml index 149b177cc..d6bd05414 100644 --- a/docker/src/main/docker/docker-compose-es.yml +++ b/docker/src/main/docker/docker-compose-es.yml @@ -23,7 +23,7 @@ networks: services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3 + image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1 container_name: elasticsearch volumes: - unomi-3-elasticsearch-data:/usr/share/elasticsearch/data diff --git a/docker/src/main/docker/entrypoint.sh b/docker/src/main/docker/entrypoint.sh index 379af4d95..62e27479e 100755 --- a/docker/src/main/docker/entrypoint.sh +++ b/docker/src/main/docker/entrypoint.sh @@ -29,10 +29,9 @@ if [ "$KARAF_DEBUG" = "true" ]; then export KARAF_DEBUG=true fi -PERSISTENCE_CONFIG="${UNOMI_START_CONFIG:-elasticsearch}" -export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START} -Dunomi.startConfig=${UNOMI_START_CONFIG}" +UNOMI_DISTRIBUTION="${UNOMI_DISTRIBUTION:-unomi-distribution-elasticsearch}" +export KARAF_OPTS="-Dunomi.autoStart=${UNOMI_AUTO_START} -Dunomi.distribution=${UNOMI_DISTRIBUTION}" -echo "PERSISTENCE_CONFIG: $PERSISTENCE_CONFIG" echo "KARAF_OPTS: $KARAF_OPTS" # Function to check cluster health for a specific node @@ -48,8 +47,9 @@ check_node_health() { } # Configure connection parameters based on persistence config -if [ "$PERSISTENCE_CONFIG" = "opensearch" ]; then +if [[ "$UNOMI_DISTRIBUTION" == *opensearch* ]]; then # OpenSearch configuration + PERSISTENCE_TYPE="opensearch" if [ -z "$UNOMI_OPENSEARCH_PASSWORD" ]; then echo "Error: UNOMI_OPENSEARCH_PASSWORD must be set when using OpenSearch" exit 1 @@ -61,8 +61,9 @@ if [ "$PERSISTENCE_CONFIG" = "opensearch" ]; then curl_opts="-k -H \"${auth_header}\" -H \"Content-Type: application/json\"" # Build array of node URLs IFS=',' read -ra NODES <<< "${UNOMI_OPENSEARCH_ADDRESSES}" -elif [ "$PERSISTENCE_CONFIG" = "elasticsearch" ]; then +elif [[ "$UNOMI_DISTRIBUTION" == *elasticsearch* ]]; then # Elasticsearch configuration + PERSISTENCE_TYPE="elasticsearch" if [ "$UNOMI_ELASTICSEARCH_SSL_ENABLE" = 'true' ]; then schema='https' else @@ -77,12 +78,12 @@ elif [ "$PERSISTENCE_CONFIG" = "elasticsearch" ]; then # Build array of node URLs IFS=',' read -ra NODES <<< "${UNOMI_ELASTICSEARCH_ADDRESSES}" else - echo "Error: Unsupported persistence config: $PERSISTENCE_CONFIG" + echo "Error: unable to determine persistence type from distribution name: $UNOMI_DISTRIBUTION" exit 1 fi # Wait for search engine to be ready -echo "Waiting for ${PERSISTENCE_CONFIG} to be ready..." +echo "Waiting for ${PERSISTENCE_TYPE} to be ready..." echo "Checking nodes: ${NODES[@]}" health_check="" @@ -102,20 +103,20 @@ while ([ -z "$health_check" ] || ([ "$health_check" != 'yellow' ] && [ "$health_ done if [ -z "$health_check" ]; then - >&2 echo "${PERSISTENCE_CONFIG^} is not yet available - all nodes unreachable" + >&2 echo "${PERSISTENCE_TYPE^} is not yet available - all nodes unreachable" sleep 3 continue fi if [ "$health_check" != 'yellow' ] && [ "$health_check" != 'green' ]; then - >&2 echo "${PERSISTENCE_CONFIG^} health status: ${health_check} (waiting for yellow or green)" + >&2 echo "${PERSISTENCE_TYPE^} health status: ${health_check} (waiting for yellow or green)" sleep 3 else - >&2 echo "${PERSISTENCE_CONFIG^} health status: ${health_check}" + >&2 echo "${PERSISTENCE_TYPE^} health status: ${health_check}" fi done -echo "${PERSISTENCE_CONFIG^} is ready with health status: ${health_check}" +echo "${PERSISTENCE_TYPE^} is ready with health status: ${health_check}" # Run Unomi in current bash session exec "$UNOMI_HOME/bin/karaf" run diff --git a/package/pom.xml b/package/pom.xml index 2021983aa..02d045d84 100644 --- a/package/pom.xml +++ b/package/pom.xml @@ -117,6 +117,13 @@ xml runtime + + org.apache.unomi + unomi-distribution + features + xml + runtime + @@ -367,6 +374,8 @@ unomi-groovy-actions unomi-web-applications unomi-rest-ui + unomi-distribution-elasticsearch + unomi-distribution-opensearch 17 diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceConfiguration.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceConfiguration.java deleted file mode 100644 index fc6152985..000000000 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.unomi.shell.services.internal; - -import org.osgi.service.metatype.annotations.AttributeDefinition; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; - -/** - * OSGi metatype configuration for the Unomi Management service. - *

- * Allows specifying a list of feature sets to start when the management - * service initializes. Each entry is a string that maps a logical key to - * one or more features. - */ -@ObjectClassDefinition( - name = "Unomi Management Configuration", - description = "Configuration for Unomi Management Service" -) -public @interface UnomiManagementServiceConfiguration { - - @AttributeDefinition( - name = "Start Features", - description = "An array of strings representing start features in the format '[\"key=feature1,feature2\", \"key2:feature3\"]'." - ) - /** - * Defines one or more feature sets to start. - *

- * Each element is a string using one of these forms: - * - key=featureA,featureB (comma-separated list assigned to a key) - * - key:featureC (single feature assigned to a key) - *

- * Example: ["ui=feature1,feature2", "backend:feature3"]. - * - * @return an array of feature-set descriptors; empty by default - */ - String[] startFeatures() default ""; - -} diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java index c461c0653..478150db3 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java @@ -90,7 +90,7 @@ public class UnomiManagementServiceImpl implements UnomiManagementService { private static final Logger LOGGER = LoggerFactory.getLogger(UnomiManagementServiceImpl.class.getName()); private static final int DEFAULT_TIMEOUT = 300; // 5 minutes timeout - private static final String SETUP_CONFIG_PID = "org.apache.unomi.setup"; + private static final String UNOMI_SETUP_PID = "org.apache.unomi.setup"; private static final String CDP_GRAPHQL_FEATURE = "cdp-graphql-feature"; @Reference(cardinality = ReferenceCardinality.MANDATORY) @@ -106,7 +106,6 @@ public class UnomiManagementServiceImpl implements UnomiManagementService { private BundleWatcher bundleWatcher; private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private BundleContext bundleContext; private final List installedDistributionDependencies = new ArrayList<>(); private final List startedDistributionDependencies = new ArrayList<>(); @@ -114,19 +113,17 @@ public class UnomiManagementServiceImpl implements UnomiManagementService { public void init(ComponentContext componentContext) throws Exception { LOGGER.info("Initializing Unomi management service"); try { - this.bundleContext = componentContext.getBundleContext(); + BundleContext bundleContext = componentContext.getBundleContext(); - String distribution = getSetupDistribution(); - if (distribution == null) { + UnomiSetup setup = getUnomiSetup(); + if (setup == null) { LOGGER.info("No previously setup distribution found"); if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.distribution"))) { - distribution = bundleContext.getProperty("unomi.distribution"); - LOGGER.warn("Setting up distribution to the one provided in context: {}", distribution); - setupDistribution(distribution); + setup = initUnomiSetup(bundleContext.getProperty("unomi.distribution")); + LOGGER.info("UnomiSetup created for distribution provided from context: {}", setup.getDistribution()); } else { - distribution = "unomi-distribution-elasticsearch"; - LOGGER.warn("No unomi.distribution provided, defaulting to '{}'", distribution); - setupDistribution(distribution); + setup = initUnomiSetup("unomi-distribution-elasticsearch"); + LOGGER.info("UnomiSetup created for default distribution: {}", setup.getDistribution()); } } @@ -134,11 +131,10 @@ public void init(ComponentContext componentContext) throws Exception { migrationService.migrateUnomi(bundleContext.getProperty("unomi.autoMigrate"), true, null); } - if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.autoStart")) && - bundleContext.getProperty("unomi.autoStart").equals("true")) { - LOGGER.info("Auto-starting unomi management service with unomi distribution: {}", distribution); + if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.autoStart")) && bundleContext.getProperty("unomi.autoStart").equals("true")) { + LOGGER.info("Auto-starting unomi management service for unomi distribution: {}", setup.getDistribution()); // Don't wait for completion during initialization - startUnomi(distribution, true, false); + startUnomi(setup.getDistribution(), true, false); } } catch (Exception e) { LOGGER.error("Error during Unomi startup:", e); @@ -146,23 +142,16 @@ public void init(ComponentContext componentContext) throws Exception { } } - private String getSetupDistribution() throws IOException { - Configuration configuration = configurationAdmin.getConfiguration(SETUP_CONFIG_PID, "?"); - Dictionary properties = configuration.getProperties(); - if (properties != null && properties.get("unomi.distribution") != null) { - return Objects.toString(properties.get("unomi.distribution"), null); - } - return null; + private UnomiSetup getUnomiSetup() throws IOException { + Configuration configuration = configurationAdmin.getConfiguration(UNOMI_SETUP_PID, "?"); + return UnomiSetup.fromDictionary(configuration.getProperties()); } - private void setupDistribution(String distribution) throws IOException { - Configuration configuration = configurationAdmin.getConfiguration(SETUP_CONFIG_PID, "?"); - Dictionary properties = configuration.getProperties(); - if (properties == null) { - properties = new Hashtable<>(); - } - properties.put("unomi.distribution", distribution); - configuration.update(properties); + private UnomiSetup initUnomiSetup(String distribution) throws IOException { + Configuration configuration = configurationAdmin.getConfiguration(UNOMI_SETUP_PID, "?"); + UnomiSetup setup = UnomiSetup.init().withDistribution(distribution); + configuration.update(setup.toProperties()); + return setup; } @Override @@ -203,7 +192,6 @@ private void doStartUnomi(String distribution, boolean mustStartDistribution) th LOGGER.error("Distribution feature not found: {}", distribution); return; } - for (Dependency dependency : feature.getDependencies()) { if (!installedDistributionDependencies.contains(dependency.getName())) { LOGGER.info("Installing distribution feature's dependency: {}", dependency.getName()); diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiSetup.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiSetup.java new file mode 100644 index 000000000..d00d4361d --- /dev/null +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiSetup.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2002-2025 Jahia Solutions Group SA. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.unomi.shell.services.internal; + +import java.util.Date; +import java.util.Dictionary; +import java.util.Objects; + +/** + * @author Jerome Blanchard + */ +public class UnomiSetup { + + private String date; + private String distribution; + + public UnomiSetup() { + } + + public String getDate() { + return date; + } + + private void setDate(String date) { + this.date = date; + } + + public String getDistribution() { + return distribution; + } + + public void setDistribution(String distribution) { + this.distribution = distribution; + } + + public UnomiSetup withDistribution(String distribution) { + this.distribution = distribution; + return this; + } + + public Dictionary toProperties() { + Dictionary properties = new java.util.Hashtable<>(); + properties.put("unomi.setup.date", date); + properties.put("unomi.setup.distribution", distribution); + return properties; + } + + public static UnomiSetup init() { + UnomiSetup setup = new UnomiSetup(); + setup.setDate(new Date().toString()); + return setup; + } + + public static UnomiSetup fromDictionary(Dictionary properties) { + UnomiSetup setup = new UnomiSetup(); + if (properties == null) { + return null; + } + setup.setDate(Objects.toString(properties.get("unomi.setup.date"), null)); + setup.setDistribution(Objects.toString(properties.get("unomi.setup.distribution"), null)); + return setup; + } + +} From 6d48477db40621a0c16027305577cbaebc91bbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Wed, 26 Nov 2025 18:13:01 +0100 Subject: [PATCH 4/7] Refactor Shell commands to introduce Setup and handle start without featureSet option (but previously setup distribution feature) --- .../org/apache/unomi/shell/actions/Setup.java | 45 +++++++++++++++++++ .../org/apache/unomi/shell/actions/Start.java | 7 +-- .../org/apache/unomi/shell/actions/Stop.java | 1 - .../services/UnomiManagementService.java | 27 ++++++----- .../internal/UnomiManagementServiceImpl.java | 27 +++++++---- 5 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Setup.java diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Setup.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Setup.java new file mode 100644 index 000000000..9d4c3acd8 --- /dev/null +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Setup.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.unomi.shell.actions; + +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Option; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.apache.unomi.shell.services.UnomiManagementService; + +@Command(scope = "unomi", name = "setup", description = "This will setup some Apache Unomi runtime options") +@Service +public class Setup implements Action { + + @Reference + UnomiManagementService unomiManagementService; + + @Option(name = "-d", aliases = "--distribution", description = "Unomi Distribution feature to configure", required = true, multiValued = false) + String distribution = "unomi-distribution-elasticsearch"; + + @Option(name = "-f", aliases = "--force", description = "Force setting up distribution feature name even if already exists (use with caution)", required = false, multiValued = false) + boolean force = false; + + public Object execute() throws Exception { + System.out.println("Setting up Apache Unomi distribution: " + distribution); + unomiManagementService.setupUnomiDistribution(distribution, force); + return null; + } + +} diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java index 0b213abc1..98c5b85a9 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Start.java @@ -31,15 +31,12 @@ public class Start implements Action { @Reference UnomiManagementService unomiManagementService; - @Argument(name = "distribution", description = "Distribution feature to use (unomi-distribution-elasticsearch/unomi-distribution-opensearch)", valueToShowInHelp = "unomi-distribution-elasticsearch") - private String distribution = "unomi-distribution-elasticsearch"; - @Option(name = "-i", aliases = "--install-only", description = "Only install distribution feature's dependencies, don't start them", required = false, multiValued = false) boolean installOnly = false; public Object execute() throws Exception { - System.out.println("Starting Apache Unomi distribution: " + distribution); - unomiManagementService.startUnomi(distribution, !installOnly); + System.out.println("Starting Apache Unomi"); + unomiManagementService.startUnomi(!installOnly); return null; } diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Stop.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Stop.java index 8b8cad5e6..ca87002e5 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Stop.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Stop.java @@ -31,7 +31,6 @@ public class Stop implements Action { public Object execute() throws Exception { unomiManagementService.stopUnomi(); - return null; } diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java index a92bf2bd2..607f08729 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/UnomiManagementService.java @@ -16,6 +16,7 @@ */ package org.apache.unomi.shell.services; +import org.apache.unomi.shell.services.internal.UnomiSetup; import org.osgi.framework.BundleException; /** @@ -25,32 +26,38 @@ public interface UnomiManagementService { /** - * This method will start Apache Unomi with the specified start features configuration - * @param selectedStartFeatures the start features configuration to use + * This method will set up Unomi distribution's feature name + * @param distribution the distribution feature name to set up + * @param overwrite to force setup even if already exists + * @throws BundleException if the setup already exists and overwrite is false + */ + void setupUnomiDistribution(String distribution, boolean overwrite) throws Exception; + + /** + * This method will start Apache Unomi * @param mustStartFeatures true if features should be started, false if they should not - * @throws BundleException if there was an error starting Unomi's bundles + * @throws Exception if there was an error starting Unomi's bundles */ - void startUnomi(String selectedStartFeatures, boolean mustStartFeatures) throws Exception; + void startUnomi(boolean mustStartFeatures) throws Exception; /** - * This method will start Apache Unomi with the specified start features configuration - * @param selectedStartFeatures the start features configuration to use + * This method will start Apache Unomi * @param mustStartFeatures true if features should be started, false if they should not * @param waitForCompletion true if the method should wait for completion, false if it should not - * @throws BundleException if there was an error starting Unomi's bundles + * @throws Exception if there was an error starting Unomi's bundles */ - void startUnomi(String selectedStartFeatures, boolean mustStartFeatures, boolean waitForCompletion) throws Exception; + void startUnomi(boolean mustStartFeatures, boolean waitForCompletion) throws Exception; /** * This method will stop Apache Unomi - * @throws BundleException if there was an error stopping Unomi's bundles + * @throws Exception if there was an error stopping Unomi's bundles */ void stopUnomi() throws Exception; /** * This method will stop Apache Unomi * @param waitForCompletion true if the method should wait for completion, false if it should not - * @throws BundleException if there was an error stopping Unomi's bundles + * @throws Exception if there was an error stopping Unomi's bundles */ void stopUnomi(boolean waitForCompletion) throws Exception; } diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java index 478150db3..aaa0447b9 100644 --- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java +++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/services/internal/UnomiManagementServiceImpl.java @@ -118,11 +118,12 @@ public void init(ComponentContext componentContext) throws Exception { UnomiSetup setup = getUnomiSetup(); if (setup == null) { LOGGER.info("No previously setup distribution found"); + //We are setting a default distribution if none is set to avoid the need of calling setup manually after installation if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.distribution"))) { - setup = initUnomiSetup(bundleContext.getProperty("unomi.distribution")); + setup = createUnomiSetup(bundleContext.getProperty("unomi.distribution")); LOGGER.info("UnomiSetup created for distribution provided from context: {}", setup.getDistribution()); } else { - setup = initUnomiSetup("unomi-distribution-elasticsearch"); + setup = createUnomiSetup("unomi-distribution-elasticsearch"); LOGGER.info("UnomiSetup created for default distribution: {}", setup.getDistribution()); } } @@ -134,7 +135,7 @@ public void init(ComponentContext componentContext) throws Exception { if (StringUtils.isNotBlank(bundleContext.getProperty("unomi.autoStart")) && bundleContext.getProperty("unomi.autoStart").equals("true")) { LOGGER.info("Auto-starting unomi management service for unomi distribution: {}", setup.getDistribution()); // Don't wait for completion during initialization - startUnomi(setup.getDistribution(), true, false); + startUnomi(true, false); } } catch (Exception e) { LOGGER.error("Error during Unomi startup:", e); @@ -147,7 +148,7 @@ private UnomiSetup getUnomiSetup() throws IOException { return UnomiSetup.fromDictionary(configuration.getProperties()); } - private UnomiSetup initUnomiSetup(String distribution) throws IOException { + private UnomiSetup createUnomiSetup(String distribution) throws IOException { Configuration configuration = configurationAdmin.getConfiguration(UNOMI_SETUP_PID, "?"); UnomiSetup setup = UnomiSetup.init().withDistribution(distribution); configuration.update(setup.toProperties()); @@ -155,16 +156,26 @@ private UnomiSetup initUnomiSetup(String distribution) throws IOException { } @Override - public void startUnomi(String distribution, boolean mustStartFeatures) throws Exception { + public void setupUnomiDistribution(String distribution, boolean overwrite) throws Exception { + UnomiSetup existingSetup = getUnomiSetup(); + if (existingSetup != null && !overwrite) { + throw new IllegalStateException("Unomi distribution is already set up with distribution: " + existingSetup.getDistribution()); + } + createUnomiSetup(distribution); + } + + @Override + public void startUnomi(boolean mustStartFeatures) throws Exception { // Default to waiting for completion - startUnomi(distribution, mustStartFeatures, true); + startUnomi(mustStartFeatures, true); } @Override - public void startUnomi(String distribution, boolean mustStartFeatures, boolean waitForCompletion) throws Exception { + public void startUnomi(boolean mustStartFeatures, boolean waitForCompletion) throws Exception { + UnomiSetup setup = getUnomiSetup(); Future future = executor.submit(() -> { try { - doStartUnomi(distribution, mustStartFeatures); + doStartUnomi(setup.getDistribution(), mustStartFeatures); } catch (Exception e) { LOGGER.error("Error starting Unomi:", e); throw new RuntimeException(e); From 3d6a7dd983546429665d36c4e5eba3b18d0452a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Wed, 26 Nov 2025 23:37:17 +0100 Subject: [PATCH 5/7] Remove opensearch provider from default in healthcheck --- distribution/src/main/feature/feature.xml | 4 ++-- itests/src/test/java/org/apache/unomi/itests/BaseIT.java | 3 ++- .../src/test/java/org/apache/unomi/itests/HealthCheckIT.java | 1 + itests/src/test/resources/org.apache.unomi.healthcheck.cfg | 2 +- package/src/main/resources/etc/custom.system.properties | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/distribution/src/main/feature/feature.xml b/distribution/src/main/feature/feature.xml index 2663d2f81..8599348ab 100644 --- a/distribution/src/main/feature/feature.xml +++ b/distribution/src/main/feature/feature.xml @@ -50,14 +50,14 @@ unomi-base unomi-startup - unomi-elasticsearch-core + unomi-opensearch-core unomi-persistence-core unomi-services unomi-rest-api unomi-cxs-lists-extension unomi-cxs-geonames-extension unomi-cxs-privacy-extension - unomi-elasticsearch-conditions + unomi-opensearch-conditions unomi-plugins-base unomi-plugins-request unomi-plugins-mail diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java index d7b6de159..4e29cf64d 100644 --- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java @@ -182,7 +182,8 @@ public void waitForStartup() throws InterruptedException { } else if (SEARCH_ENGINE_OPENSEARCH.equals(searchEngine)){ LOGGER.info("Starting Unomi with opensearch search engine..."); System.out.println("==== Starting Unomi with opensearch search engine..."); - executeCommand("unomi:start " + SEARCH_ENGINE_OPENSEARCH); + executeCommand("unomi:setup -d unomi-distribution-opensearch --force true"); + executeCommand("unomi:start"); } else { LOGGER.error("Unknown search engine: " + searchEngine); throw new InterruptedException("Unknown search engine: " + searchEngine); diff --git a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java index 755d184c2..09e73bde8 100644 --- a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java @@ -61,6 +61,7 @@ public void testHealthCheck() { Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE)); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE)); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("persistence") && r.getStatus() == HealthCheckResponse.Status.LIVE)); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE)); } catch (Exception e) { LOGGER.error("Error while executing health check", e); diff --git a/itests/src/test/resources/org.apache.unomi.healthcheck.cfg b/itests/src/test/resources/org.apache.unomi.healthcheck.cfg index 9de18615b..fd41ffa7d 100644 --- a/itests/src/test/resources/org.apache.unomi.healthcheck.cfg +++ b/itests/src/test/resources/org.apache.unomi.healthcheck.cfg @@ -35,5 +35,5 @@ authentication.realm = ${org.apache.unomi.security.realm:-karaf} # Health check configuration healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-true} -healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,opensearch,unomi,persistence} +healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,unomi,persistence} healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400} diff --git a/package/src/main/resources/etc/custom.system.properties b/package/src/main/resources/etc/custom.system.properties index 7ca2c55bc..dd6e1f911 100644 --- a/package/src/main/resources/etc/custom.system.properties +++ b/package/src/main/resources/etc/custom.system.properties @@ -508,7 +508,7 @@ org.apache.unomi.healthcheck.password=${env:UNOMI_HEALTHCHECK_PASSWORD:-health} # Specify the list of health check providers (name) to use. The list is comma separated. Other providers will be ignored. # As Karaf provider is the one needed by healthcheck (always LIVE), it cannot be ignored. # -org.apache.unomi.healthcheck.providers:${env:UNOMI_HEALTHCHECK_PROVIDERS:-cluster,elasticsearch,opensearch,unomi,persistence} +org.apache.unomi.healthcheck.providers:${env:UNOMI_HEALTHCHECK_PROVIDERS:-cluster,elasticsearch,unomi,persistence} # # Specify the timeout in milliseconds for each healthcheck provider call. The default value is 400ms. # If timeout is raised, the provider is marked in ERROR. From 744db6a54c3b2e2042c00838d5774dfaa20aabca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Thu, 27 Nov 2025 00:34:55 +0100 Subject: [PATCH 6/7] fix healthcheck test --- itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java index 09e73bde8..853031f2e 100644 --- a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java @@ -57,7 +57,7 @@ public void testHealthCheck() { List response = get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {}); LOGGER.info("health check response: {}", response); Assert.assertNotNull(response); - Assert.assertEquals(4, response.size()); + Assert.assertEquals(5, response.size()); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE)); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE)); Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE)); From 2064adc169dabcbef79eb444fe2485c3ba1bb6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Thu, 27 Nov 2025 11:02:04 +0100 Subject: [PATCH 7/7] create two versions of healthcheck to fit with persistence and avoid to add specific runtime config for opensearch. --- distribution/src/main/feature/feature.xml | 4 +-- .../main/docker/docker-compose-build-es.yml | 1 - .../main/docker/docker-compose-build-os.yml | 1 - docker/src/main/docker/docker-compose-es.yml | 1 - docker/src/main/docker/docker-compose-os.yml | 1 - extensions/healthcheck/pom.xml | 11 +++++-- ...apache.unomi.healthcheck-elasticsearch.cfg | 31 +++++++++++++++++++ ...g.apache.unomi.healthcheck-opensearch.cfg} | 9 +----- .../java/org/apache/unomi/itests/BaseIT.java | 4 +-- kar/src/main/feature/feature.xml | 10 ++++-- 10 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg rename extensions/healthcheck/src/main/resources/{org.apache.unomi.healthcheck.cfg => org.apache.unomi.healthcheck-opensearch.cfg} (78%) diff --git a/distribution/src/main/feature/feature.xml b/distribution/src/main/feature/feature.xml index 8599348ab..3ad62251f 100644 --- a/distribution/src/main/feature/feature.xml +++ b/distribution/src/main/feature/feature.xml @@ -40,7 +40,7 @@ unomi-shell-dev-commands unomi-wab unomi-web-tracker - unomi-healthcheck + unomi-healthcheck-elasticsearch unomi-router-karaf-feature unomi-groovy-actions unomi-rest-ui @@ -65,7 +65,7 @@ unomi-shell-dev-commands unomi-wab unomi-web-tracker - unomi-healthcheck + unomi-healthcheck-opensearch unomi-router-karaf-feature unomi-groovy-actions unomi-rest-ui diff --git a/docker/src/main/docker/docker-compose-build-es.yml b/docker/src/main/docker/docker-compose-build-es.yml index be9f882af..454e10364 100644 --- a/docker/src/main/docker/docker-compose-build-es.yml +++ b/docker/src/main/docker/docker-compose-build-es.yml @@ -38,7 +38,6 @@ services: - UNOMI_AUTO_START=true - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 - - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-build-os.yml b/docker/src/main/docker/docker-compose-build-os.yml index ca8c1df05..fb2d27d27 100644 --- a/docker/src/main/docker/docker-compose-build-os.yml +++ b/docker/src/main/docker/docker-compose-build-os.yml @@ -100,7 +100,6 @@ services: - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} - - UNOMI_HEALTHCHECK_PROVIDERS=cluster,opensearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-es.yml b/docker/src/main/docker/docker-compose-es.yml index d6bd05414..43f3bbba5 100644 --- a/docker/src/main/docker/docker-compose-es.yml +++ b/docker/src/main/docker/docker-compose-es.yml @@ -44,7 +44,6 @@ services: - UNOMI_AUTO_START=true - UNOMI_DISTRIBUTION=unomi-distribution-elasticsearch - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200 - - UNOMI_HEALTHCHECK_PROVIDERS=cluster,elasticsearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/docker/src/main/docker/docker-compose-os.yml b/docker/src/main/docker/docker-compose-os.yml index 594e057d3..03abe5263 100644 --- a/docker/src/main/docker/docker-compose-os.yml +++ b/docker/src/main/docker/docker-compose-os.yml @@ -84,7 +84,6 @@ services: - UNOMI_OPENSEARCH_ADDRESSES=opensearch-node1:9200 - UNOMI_OPENSEARCH_USERNAME=admin - UNOMI_OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} - - UNOMI_HEALTHCHECK_PROVIDERS=cluster,opensearch,unomi,persistence # Debug settings - KARAF_DEBUG=${DEBUG:-false} - KARAF_DEBUG_PORT=${DEBUG_PORT:-5005} diff --git a/extensions/healthcheck/pom.xml b/extensions/healthcheck/pom.xml index e16bd9ac7..e1fca175d 100644 --- a/extensions/healthcheck/pom.xml +++ b/extensions/healthcheck/pom.xml @@ -150,10 +150,17 @@ - src/main/resources/org.apache.unomi.healthcheck.cfg + src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg cfg - healthcheck + healthcheck-elasticsearch + + + + src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg + + cfg + healthcheck-opensearch diff --git a/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg new file mode 100644 index 000000000..20a8c2a6b --- /dev/null +++ b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-elasticsearch.cfg @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Elasticsearch configuration +esAddresses = ${org.apache.unomi.elasticsearch.addresses:-localhost:9200} +esSSLEnabled = ${org.apache.unomi.elasticsearch.sslEnable:-false} +esLogin = ${org.apache.unomi.elasticsearch.username:-} +esPassword = ${org.apache.unomi.elasticsearch.password:-} +esHttpClient.trustAllCertificates = ${org.apache.unomi.elasticsearch.sslTrustAllCertificates:-false} + +# Security configuration +authentication.realm = ${org.apache.unomi.security.realm:-karaf} + +# Health check configuration +healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-false} +healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,unomi,persistence} +healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400} diff --git a/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg similarity index 78% rename from extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg rename to extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg index aeb6e37ee..98fef8fdf 100644 --- a/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck.cfg +++ b/extensions/healthcheck/src/main/resources/org.apache.unomi.healthcheck-opensearch.cfg @@ -15,13 +15,6 @@ # limitations under the License. # -# Elasticsearch configuration -esAddresses = ${org.apache.unomi.elasticsearch.addresses:-localhost:9200} -esSSLEnabled = ${org.apache.unomi.elasticsearch.sslEnable:-false} -esLogin = ${org.apache.unomi.elasticsearch.username:-} -esPassword = ${org.apache.unomi.elasticsearch.password:-} -esHttpClient.trustAllCertificates = ${org.apache.unomi.elasticsearch.sslTrustAllCertificates:-false} - # OpenSearch configuration osAddresses = ${org.apache.unomi.opensearch.addresses:-localhost:9200} osSSLEnabled = ${org.apache.unomi.opensearch.sslEnable:-true} @@ -35,5 +28,5 @@ authentication.realm = ${org.apache.unomi.security.realm:-karaf} # Health check configuration healthcheck.enabled = ${org.apache.unomi.healthcheck.enabled:-false} -healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,elasticsearch,unomi,persistence} +healthcheck.providers = ${org.apache.unomi.healthcheck.providers:-cluster,opensearch,unomi,persistence} healthcheck.timeout = ${org.apache.unomi.healthcheck.timeout:-400} diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java index 4e29cf64d..9bb734c91 100644 --- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java @@ -307,7 +307,7 @@ public Option[] config() { "unomi-shell-dev-commands", "unomi-wab", "unomi-web-tracker", - "unomi-healthcheck", + "unomi-healthcheck-elasticsearch", "unomi-router-karaf-feature", "unomi-groovy-actions", "unomi-rest-ui", @@ -333,7 +333,7 @@ public Option[] config() { "unomi-shell-dev-commands", "unomi-wab", "unomi-web-tracker", - "unomi-healthcheck", + "unomi-healthcheck-opensearch", "unomi-router-karaf-feature", "unomi-groovy-actions", "unomi-rest-ui", diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml index 5c8406985..ed3d3a483 100644 --- a/kar/src/main/feature/feature.xml +++ b/kar/src/main/feature/feature.xml @@ -183,9 +183,15 @@ mvn:org.apache.unomi/unomi-web-tracker-wab/${project.version} - + unomi-web-tracker - mvn:org.apache.unomi/healthcheck/${project.version}/cfg/healthcheck + mvn:org.apache.unomi/healthcheck/${project.version}/cfg/healthcheck-elasticsearch + mvn:org.apache.unomi/healthcheck/${project.version} + + + + unomi-web-tracker + mvn:org.apache.unomi/healthcheck/${project.version}/cfg/healthcheck-opensearch mvn:org.apache.unomi/healthcheck/${project.version}