Skip to content

Commit

Permalink
[UNOMI-854] Add a HealthCheck Endpoint (#698)
Browse files Browse the repository at this point in the history
* UNOMI-854: Add a healthcheck extension

* UNOMI-854: Add configuration to healthcheck

* UNOMI-854: Integrate authentication for servlet using JAAS and realm config

* UNOMI-854: Add documentation (javadoc and adoc) for the healthcheck extension

* UNOMI-854: Include Integration Test

* UNOMI-854: Change log level to debug for some unrelevant runtime logs, add timeout default response, add exception details in logs.

* UNOMI-853: Avoid using 2 testsuite because of conflict.

* UNOMI-854: Add a healthcheck extension

* UNOMI-854: Add configuration to healthcheck

* UNOMI-854: Integrate authentication for servlet using JAAS and realm config

* UNOMI-854: Include Integration Test

* UNOMI-854: Update config capabilities, use code 206 when all checks are not LIVE, fix order of checks.

* UNOMI-854: Include small value cache to ensure better memory consumption and avoid DoS.

* UNOMI-854: Missed test suite removal.
  • Loading branch information
jayblanc authored Oct 17, 2024
1 parent 64aa2a8 commit c5a2030
Show file tree
Hide file tree
Showing 23 changed files with 1,730 additions and 10 deletions.
85 changes: 85 additions & 0 deletions extensions/healthcheck/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.
*/

# Health Check Extension

The Health Check extension provides a simple health check endpoint that can be used to determine if the server is up and running.
The health check endpoint is available at

```
/health/check
```
and returns a simple JSON response that includes all health check provider responses.

Basic Http Authentication is enabled by default for the health check endpoint. The user needs to have the role `health` to access the endpoint. Users and roles can be configured in the etc/users.properties file. By default a user health/health is configured.

The healthcheck is available even if unomi is not started. It gives health information about :
- Karaf (as soon as the karaf container is started)
- Elasticsearch (connection to elasticsearch cluster and its health)
- Unomi (unomi bundles status)
- Persistence (unomi to elasticsearch binding)
- Cluster health (unomi cluster status and nodes information)

All healthcheck can have a status :
- DOWN (service is not available)
- UP (service is up but does not respond to request (starting or misconfigured))
- LIVE (service is ready to serve request)
- ERROR (an error occurred during service health check)

Any subsystem health check have a timeout of 500ms where check is cancelled and will be returned as error.

Typical response to /health/check when unomi NOT started is :

```json
[
{
"name":"karaf",
"status":"LIVE",
"collectingTime":0
},
{
"name":"cluster",
"status":"DOWN",
"collectingTime":0
},
{
"name":"elasticsearch",
"status":"LIVE",
"collectingTime":6
},
{
"name":"persistence",
"status":"DOWN",
"collectingTime":0
},
{
"name":"unomi",
"status":"DOWN",
"collectingTime":0
}
]
```

## Configuration

Configuration is located in the file etc/org.apache.unomi.healthcheck.cfg

Extension can be disabled by setting the property `enabled` to `false`. An environment variable can be used to set this property : UNOMI_HEALTHCHECK_ENABLED

By default, all healthcheck providers are included but the list of those included providers can be customized by setting the property `providers` with a comma separated list of provider names. An environment variable can be used to set this property : UNOMI_HEALTHCHECK_PROVIDERS

The timeout used for each health check can be set by setting the property `timeout` to the desired value in milliseconds. An environment variable can be used to set this property : UNOMI_HEALTHCHECK_TIMEOUT
141 changes: 141 additions & 0 deletions extensions/healthcheck/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.unomi</groupId>
<artifactId>unomi-extensions</artifactId>
<version>2.6.0-SNAPSHOT</version>
</parent>

<artifactId>healthcheck</artifactId>
<name>Apache Unomi :: Extensions :: HealthCheck</name>
<description>Apache Unomi HealthCheck extension that provide liveliness information about unomi</description>
<packaging>bundle</packaging>

<dependencies>
<dependency>
<groupId>org.apache.unomi</groupId>
<artifactId>unomi-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.unomi</groupId>
<artifactId>unomi-lifecycle-watcher</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.unomi</groupId>
<artifactId>unomi-persistence-spi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.unomi</groupId>
<artifactId>shell-commands</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-osgi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.karaf.jaas</groupId>
<artifactId>org.apache.karaf.jaas.boot</artifactId>
<version>${karaf.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Import-Package>
sun.misc;resolution:=optional,
*
</Import-Package>
<Export-Package>
org.apache.unomi.healthcheck;version=${project.version},
org.osgi.service.useradmin;version=1.1.0
</Export-Package>
<_dsannotations>
org.apache.unomi.healthcheck.*,
org.apache.unomi.healthcheck.provider.*,
org.apache.unomi.healthcheck.servlet.*,
</_dsannotations>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>
src/main/resources/org.apache.unomi.healthcheck.cfg
</file>
<type>cfg</type>
<classifier>healthcheck</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Health check configuration.
*/
@Component(immediate = true, service = HealthCheckConfig.class, configurationPid = {"org.apache.unomi.healthcheck"})
public class HealthCheckConfig {

private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckConfig.class.getName());

public static final String CONFIG_ES_ADDRESSES = "esAddresses";
public static final String CONFIG_ES_SSL_ENABLED = "esSSLEnabled";
public static final String CONFIG_ES_LOGIN = "esLogin";
public static final String CONFIG_ES_PASSWORD = "esPassword";
public static final String CONFIG_TRUST_ALL_CERTIFICATES = "httpClient.trustAllCertificates";
public static final String CONFIG_AUTH_REALM = "authentication.realm";
public static final String ENABLED = "healthcheck.enabled";
public static final String PROVIDERS = "healthcheck.providers";
public static final String TIMEOUT = "healthcheck.timeout";

private Map<String, String> config = new HashMap<>();
private boolean enabled = true;
private List<String> enabledProviders = new ArrayList<>();
private int timeout = 400;

@Activate
@Modified
public void modified(Map<String, String> config) {
LOGGER.info("Updating healthcheck configuration, config size: {}", config.size());
this.setConfig(config);
this.setEnabled(config.getOrDefault(ENABLED, "true").equalsIgnoreCase("true"));
this.setEnabledProviders(config.getOrDefault(PROVIDERS, "").isEmpty() ? new ArrayList<>() : List.of(config.get(PROVIDERS).split(",")));
this.setTimeout(Integer.parseInt(config.getOrDefault(TIMEOUT, "400")));
}

public String get(String configKey) {
return this.config.get(configKey);
}

public boolean isEnabled() {
return enabled;
}

public List<String> getEnabledProviders() {
return enabledProviders;
}

public int getTimeout() {
return timeout;
}

public void setConfig(Map<String, String> config) {
this.config = config;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public void setEnabledProviders(List<String> enabledProviders) {
this.enabledProviders = enabledProviders;
}

public void setTimeout(int timeout) {
this.timeout = timeout;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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;

public interface HealthCheckProvider {

String name();

HealthCheckResponse execute();

default HealthCheckResponse timeout() {
return new HealthCheckResponse.Builder().name(name()).withData("error.cause", "timeout").error().build();
}

}
Loading

0 comments on commit c5a2030

Please sign in to comment.