From ba4dab807cc1fa0ed5d71947efcaf905ae8c2d9a Mon Sep 17 00:00:00 2001 From: rng Date: Mon, 2 Feb 2026 16:59:14 +1100 Subject: [PATCH 1/3] Update health endpoint --- .../core/configuration/ActuatorConfig.java | 63 +++++++++++++++++++ .../server/core/configuration/Config.java | 2 +- .../configuration/ElasticSearchConfig.java | 2 +- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java new file mode 100644 index 00000000..b9036dce --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java @@ -0,0 +1,63 @@ +package au.org.aodn.ogcapi.server.core.configuration; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ActuatorConfig { + + @Bean + public HealthIndicator elasticsearchHealth( + @Value("${elasticsearch.vocabs_index.name}") String vocabIndexName, + @Value("${elasticsearch.index.name}") String indexName, + @Value("${elasticsearch.cloud_optimized_index.name}") String coIndexName, + ElasticsearchClient client) { + return () -> { + try { + // Is elastic up and run? + String status = client.cluster().health(r -> r).status().toString(); + if ("yellow".equalsIgnoreCase(status)) { + return Health.status("degraded") + .withDetail("reason", "elastic status yellow") + .build(); + } + if ("red".equalsIgnoreCase(status)) { + return Health.status("degraded") + .withDetail("reason", "elastic status red") + .build(); + } + + // Now if our target index exist? + if(!client.indices().exists(ExistsRequest.of(b -> b.index(indexName))).value()) { + return Health.down() + .withDetail("reason", "require index missing") + .build(); + } + + if(!client.indices().exists(ExistsRequest.of(b -> b.index(coIndexName))).value()) { + return Health.status("degraded") + .withDetail("reason", "require cloud optimized index missing") + .build(); + } + + if(!client.indices().exists(ExistsRequest.of(b -> b.index(vocabIndexName))).value()) { + return Health.status("degraded") + .withDetail("reason", "require vocab index missing") + .build(); + } + + return Health.up().build(); + } catch (Exception e) { + return Health.down() + .withDetail("reason", "elastic connection error") + .withException(e) + .build(); + } + }; + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/Config.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/Config.java index 723b3f97..4ba8baef 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/Config.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/Config.java @@ -21,7 +21,7 @@ public class Config { ObjectMapper mapper; @Autowired - public void initConstrucUtils(ObjectMapper mapper) { + public void initConstructUtils(ObjectMapper mapper) { ConstructUtils.setObjectMapper(mapper); } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java index 3bdcabc3..30f42b73 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java @@ -56,7 +56,7 @@ public ElasticsearchClient geoNetworkElasticsearchClient(RestClientTransport tra * @param indexName - The elastic index name that store the STAC from es-indexer * @param pageSize - Do not set this value too high, say 5000 will crash elastic search * @param searchAsYouTypeSize - The number of search result return for search as you type - * @return + * @return The search object */ @Bean public Search createElasticSearch(ElasticsearchClient client, From ac752f8de3d5161c9413ef1dc01b6692ee059931 Mon Sep 17 00:00:00 2001 From: rng Date: Tue, 3 Feb 2026 08:14:00 +1100 Subject: [PATCH 2/3] Update health endpoints --- .../core/configuration/ActuatorConfig.java | 35 ++++++++++--------- .../core/model/enumeration/Constants.java | 5 --- .../core/model/enumeration/ErrorCode.java | 21 +++++++++++ server/src/main/resources/application.yaml | 4 +++ 4 files changed, 43 insertions(+), 22 deletions(-) delete mode 100644 server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/Constants.java create mode 100644 server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java index b9036dce..7e6ae506 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ActuatorConfig.java @@ -1,5 +1,6 @@ package au.org.aodn.ogcapi.server.core.configuration; +import au.org.aodn.ogcapi.server.core.model.enumeration.ErrorCode; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.indices.ExistsRequest; import org.springframework.beans.factory.annotation.Value; @@ -12,7 +13,7 @@ public class ActuatorConfig { @Bean - public HealthIndicator elasticsearchHealth( + public HealthIndicator ogcApiHealth( @Value("${elasticsearch.vocabs_index.name}") String vocabIndexName, @Value("${elasticsearch.index.name}") String indexName, @Value("${elasticsearch.cloud_optimized_index.name}") String coIndexName, @@ -21,40 +22,40 @@ public HealthIndicator elasticsearchHealth( try { // Is elastic up and run? String status = client.cluster().health(r -> r).status().toString(); - if ("yellow".equalsIgnoreCase(status)) { - return Health.status("degraded") - .withDetail("reason", "elastic status yellow") - .build(); - } - if ("red".equalsIgnoreCase(status)) { - return Health.status("degraded") - .withDetail("reason", "elastic status red") + if ("yellow".equalsIgnoreCase(status) || "red".equalsIgnoreCase(status)) { + return Health.status(ErrorCode.ELASTICSEARCH_UNAVAILABLE.getStatus()) + .withDetail("reason", ErrorCode.ELASTICSEARCH_UNAVAILABLE.getMessage()) + .withDetail("code", ErrorCode.ELASTICSEARCH_UNAVAILABLE.getCode()) .build(); } // Now if our target index exist? if(!client.indices().exists(ExistsRequest.of(b -> b.index(indexName))).value()) { - return Health.down() - .withDetail("reason", "require index missing") + return Health.status(ErrorCode.MISSING_CORE_INDEX.getStatus()) + .withDetail("reason", ErrorCode.MISSING_CORE_INDEX.getMessage()) + .withDetail("code", ErrorCode.MISSING_CORE_INDEX.getCode()) .build(); } if(!client.indices().exists(ExistsRequest.of(b -> b.index(coIndexName))).value()) { - return Health.status("degraded") - .withDetail("reason", "require cloud optimized index missing") + return Health.status(ErrorCode.MISSING_CO_CORE_INDEX.getStatus()) + .withDetail("reason", ErrorCode.MISSING_CO_CORE_INDEX.getMessage()) + .withDetail("code", ErrorCode.MISSING_CO_CORE_INDEX.getCode()) .build(); } if(!client.indices().exists(ExistsRequest.of(b -> b.index(vocabIndexName))).value()) { - return Health.status("degraded") - .withDetail("reason", "require vocab index missing") + return Health.status(ErrorCode.MISSING_VOCAB_INDEX.getStatus()) + .withDetail("reason", ErrorCode.MISSING_VOCAB_INDEX.getMessage()) + .withDetail("code", ErrorCode.MISSING_VOCAB_INDEX.getCode()) .build(); } return Health.up().build(); } catch (Exception e) { - return Health.down() - .withDetail("reason", "elastic connection error") + return Health.status(ErrorCode.ELASTICSEARCH_UNAVAILABLE.getStatus()) + .withDetail("reason", ErrorCode.ELASTICSEARCH_UNAVAILABLE.getMessage()) + .withDetail("code", ErrorCode.ELASTICSEARCH_UNAVAILABLE.getCode()) .withException(e) .build(); } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/Constants.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/Constants.java deleted file mode 100644 index 672f2b23..00000000 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/Constants.java +++ /dev/null @@ -1,5 +0,0 @@ -package au.org.aodn.ogcapi.server.core.model.enumeration; - -public class Constants { - public static final String MEDIA_TYPE_MAPBOX = "vnd.mapbox-vector-tile"; -} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java new file mode 100644 index 00000000..d19eff32 --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java @@ -0,0 +1,21 @@ +package au.org.aodn.ogcapi.server.core.model.enumeration; + +import lombok.Getter; + +@Getter +public enum ErrorCode { + ELASTICSEARCH_UNAVAILABLE("DEGRADED", "E1000", "Dependency unavailable"), + MISSING_CO_CORE_INDEX("DEGRADED", "E1001","Missing cloud optimized index"), + MISSING_VOCAB_INDEX("DEGRADED", "E1002","Missing vocab index"), + MISSING_CORE_INDEX("DEGRADED", "E1003","Missing core index"); + + private final String status; + private final String code; + private final String message; + + ErrorCode(String status, String code, String message) { + this.status = status; + this.code = code; + this.message = message; + } +} diff --git a/server/src/main/resources/application.yaml b/server/src/main/resources/application.yaml index 94cfe602..e8768374 100644 --- a/server/src/main/resources/application.yaml +++ b/server/src/main/resources/application.yaml @@ -456,12 +456,16 @@ management: health: elasticsearch: enabled: false + diskspace: + enabled: false endpoints: web: base-path: /api/v1/ogc/manage exposure: include: "health,info" endpoint: + health: + show-details: always logfile: external-file: /tmp/logs/ogcapi.log # Actuator info point From f293563543c97e04c6dacff67b88b6fb3e8d9223 Mon Sep 17 00:00:00 2001 From: rng Date: Tue, 3 Feb 2026 08:24:21 +1100 Subject: [PATCH 3/3] Fix incorrect status --- .../aodn/ogcapi/server/core/model/enumeration/ErrorCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java index d19eff32..34bf525b 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/ErrorCode.java @@ -4,10 +4,10 @@ @Getter public enum ErrorCode { - ELASTICSEARCH_UNAVAILABLE("DEGRADED", "E1000", "Dependency unavailable"), + ELASTICSEARCH_UNAVAILABLE("DOWN", "E1000", "Dependency unavailable"), MISSING_CO_CORE_INDEX("DEGRADED", "E1001","Missing cloud optimized index"), MISSING_VOCAB_INDEX("DEGRADED", "E1002","Missing vocab index"), - MISSING_CORE_INDEX("DEGRADED", "E1003","Missing core index"); + MISSING_CORE_INDEX("DOWN", "E1003","Missing core index"); private final String status; private final String code;