diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientStatusMappingProperties.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientStatusMappingProperties.java new file mode 100644 index 000000000..d7606d48c --- /dev/null +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientStatusMappingProperties.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-present the original author or authors. + * + * 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 + * + * https://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.springframework.cloud.netflix.eureka; + +import java.util.HashMap; +import java.util.Map; + +import com.netflix.appinfo.InstanceInfo.InstanceStatus; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("eureka.client.status.mapping") +public class EurekaClientStatusMappingProperties { + + private Map mapping = new HashMap<>(); + + public Map getMapping() { + return mapping; + } + + public void setMapping(Map mapping) { + this.mapping = mapping; + } + +} diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.java index ff89ef7f9..fbb2106ab 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -83,6 +84,8 @@ public class EurekaHealthCheckHandler private final Map healthContributors = new HashMap<>(); + private final EurekaClientStatusMappingProperties properties; + /** * {@code true} until the context is stopped. */ @@ -91,9 +94,14 @@ public class EurekaHealthCheckHandler private final Map reactiveHealthContributors = new HashMap<>(); public EurekaHealthCheckHandler(StatusAggregator statusAggregator) { + this(statusAggregator, new EurekaClientStatusMappingProperties()); + } + + public EurekaHealthCheckHandler(StatusAggregator statusAggregator, EurekaClientStatusMappingProperties properties) { + this.statusAggregator = statusAggregator; + this.properties = properties; Assert.notNull(statusAggregator, "StatusAggregator must not be null"); - } @Override @@ -179,9 +187,27 @@ else if (contributor instanceof ReactiveHealthIndicator) { } protected InstanceStatus mapToInstanceStatus(Status status) { + + if (status == null) { + return InstanceStatus.UNKNOWN; + } + + String statusCode = status.getCode().toLowerCase(Locale.ROOT); + + // 🔥 Custom mapping (case-insensitive) + if (properties != null && properties.getMapping() != null) { + for (Map.Entry entry : properties.getMapping().entrySet()) { + if (entry.getKey().equalsIgnoreCase(statusCode)) { + return entry.getValue(); + } + } + } + + // 🔁 Default mapping if (!STATUS_MAPPING.containsKey(status)) { return InstanceStatus.UNKNOWN; } + return STATUS_MAPPING.get(status); } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandlerTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandlerTests.java index f4eacdee1..4a64b14e0 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandlerTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandlerTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.health.contributor.HealthContributors; import org.springframework.boot.health.contributor.HealthIndicator; import org.springframework.boot.health.contributor.ReactiveHealthIndicator; +import org.springframework.boot.health.contributor.Status; import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator; import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthContributor; import org.springframework.cloud.client.discovery.health.DiscoveryHealthIndicator; @@ -161,6 +162,31 @@ void testCompositeComponentsOneDown() { assertThat(status).isEqualTo(InstanceStatus.DOWN); } + @Test + void testCustomStatusMapping() { + + EurekaClientStatusMappingProperties props = new EurekaClientStatusMappingProperties(); + props.getMapping().put("fatal", InstanceStatus.OUT_OF_SERVICE); + + EurekaHealthCheckHandler handler = new EurekaHealthCheckHandler(new SimpleStatusAggregator(), props); + + Status status = new Status("fatal"); + + InstanceStatus result = handler.mapToInstanceStatus(status); + + assertThat(result).isEqualTo(InstanceStatus.OUT_OF_SERVICE); + } + + @Test + void testNullStatusReturnsUnknown() { + + EurekaHealthCheckHandler handler = new EurekaHealthCheckHandler(new SimpleStatusAggregator()); + + InstanceStatus result = handler.mapToInstanceStatus(null); + + assertThat(result).isEqualTo(InstanceStatus.UNKNOWN); + } + private void initialize(Class... configurations) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(configurations); healthCheckHandler.setApplicationContext(applicationContext);