From 4a6d7027f04f40e2dfe0aaa2a3f5bc6373e37e30 Mon Sep 17 00:00:00 2001 From: bono007 Date: Sun, 12 Jul 2020 09:55:13 -0500 Subject: [PATCH] Allow ConsulHealthIndicator to bypass the services query Fixes gh-646 --- docs/src/main/asciidoc/_configprops.adoc | 6 +- .../main/asciidoc/spring-cloud-consul.adoc | 88 +++++++++----- .../cloud/consul/ConsulAutoConfiguration.java | 6 +- .../cloud/consul/ConsulHealthIndicator.java | 20 ++-- .../ConsulHealthIndicatorProperties.java | 54 +++++++++ .../consul/ConsulAutoConfigurationTests.java | 113 ++++++++++++++---- .../consul/ConsulHealthIndicatorDownTest.java | 30 ++++- ...onsulHealthIndicatorLightweightUpTest.java | 73 +++++++++++ .../consul/ConsulHealthIndicatorUpTest.java | 14 ++- 9 files changed, 330 insertions(+), 74 deletions(-) create mode 100644 spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicatorProperties.java create mode 100644 spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorLightweightUpTest.java diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 58b353ba..67f77d5c 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -33,8 +33,8 @@ |spring.cloud.consul.discovery.health-check-url | | Custom health check url to override default. |spring.cloud.consul.discovery.heartbeat.enabled | false | |spring.cloud.consul.discovery.heartbeat.interval-ratio | | -|spring.cloud.consul.discovery.heartbeat.ttl-unit | s | -|spring.cloud.consul.discovery.heartbeat.ttl-value | 30 | +|spring.cloud.consul.discovery.heartbeat.ttl-unit | s | +|spring.cloud.consul.discovery.heartbeat.ttl-value | 30 | |spring.cloud.consul.discovery.hostname | | Hostname to use when accessing server. |spring.cloud.consul.discovery.include-hostname-in-instance-id | false | Whether hostname is included into the default instance id when registering service. |spring.cloud.consul.discovery.instance-group | | Service instance group. @@ -77,4 +77,4 @@ |spring.cloud.consul.tls.key-store-password | | Password to an external keystore. |spring.cloud.consul.tls.key-store-path | | Path to an external keystore. -|=== \ No newline at end of file +|=== diff --git a/docs/src/main/asciidoc/spring-cloud-consul.adoc b/docs/src/main/asciidoc/spring-cloud-consul.adoc index b7fd53a8..82107fce 100644 --- a/docs/src/main/asciidoc/spring-cloud-consul.adoc +++ b/docs/src/main/asciidoc/spring-cloud-consul.adoc @@ -31,7 +31,7 @@ To activate Consul Service Discovery use the starter with group `org.springframe === Registering with Consul -When a client registers with Consul, it provides meta-data about itself such as host and port, id, name and tags. An HTTP https://www.consul.io/docs/agent/checks.html[Check] is created by default that Consul hits the `/health` endpoint every 10 seconds. If the health check fails, the service instance is marked as critical. +When a client registers with Consul, it provides meta-data about itself such as host and port, id, name and tags. An HTTP https://www.consul.io/docs/agent/checks.html[Check] is created by default that Consul hits the `/actuator/health` endpoint every 10 seconds. If the health check fails, the service instance is marked as critical. Example Consul client: @@ -149,9 +149,13 @@ spring.cloud.consul.discovery.management-suffix spring.cloud.consul.discovery.management-tags ---- -=== HTTP Health Check +==== HTTP Health Check -The health check for a Consul instance defaults to "/health", which is the default locations of a useful endpoint in a Spring Boot Actuator application. You need to change these, even for an Actuator application if you use a non-default context path or servlet path (e.g. `server.servletPath=/foo`) or management endpoint path (e.g. `management.server.servlet.context-path=/admin`). The interval that Consul uses to check the health endpoint may also be configured. "10s" and "1m" represent 10 seconds and 1 minute respectively. Example: +The health check for a Consul instance defaults to "/actuator/health", which is the default location of the health endpoint in a Spring Boot Actuator application. You need to change this, even for an Actuator application, if you use a non-default context path or servlet path (e.g. `server.servletPath=/foo`) or management endpoint path (e.g. `management.server.servlet.context-path=/admin`). + +The interval that Consul uses to check the health endpoint may also be configured. "10s" and "1m" represent 10 seconds and 1 minute respectively. + +This example illustrates the above (see the `spring.cloud.consul.discovery.health-check-*` properties in link:appendix.html[the appendix page] for more options). .application.yml ---- @@ -159,11 +163,57 @@ spring: cloud: consul: discovery: - healthCheckPath: ${management.server.servlet.context-path}/health + healthCheckPath: ${management.server.servlet.context-path}/actuator/health healthCheckInterval: 15s ---- -You can disable the health check by setting `management.health.consul.enabled=false`. +You can disable the HTTP health check entirely by setting `spring.cloud.consul.discovery.register-health-check=false`. + +===== Applying Headers +Headers can be applied to health check requests. For example, if you're trying to register a https://cloud.spring.io/spring-cloud-config/[Spring Cloud Config] server that uses https://github.com/spring-cloud/spring-cloud-config/blob/master/docs/src/main/asciidoc/spring-cloud-config.adoc#vault-backend[Vault Backend]: + +.application.yml +---- +spring: + cloud: + consul: + discovery: + health-check-headers: + X-Config-Token: 6442e58b-d1ea-182e-cfa5-cf9cddef0722 +---- + +According to the HTTP standard, each header can have more than one values, in which case, an array can be supplied: + +.application.yml +---- +spring: + cloud: + consul: + discovery: + health-check-headers: + X-Config-Token: + - "6442e58b-d1ea-182e-cfa5-cf9cddef0722" + - "Some other value" +---- + +==== Actuator Health Indicator(s) +If the service instance is a Spring Boot Actuator application, it may be provided the following Actuator health indicators. + +===== DiscoveryClientHealthIndicator +When Consul Service Discovery is active, a https://cloud.spring.io/spring-cloud-commons/2.2.x/reference/html/#health-indicator[DiscoverClientHealthIndicator] is configured and made available to the Actuator health endpoint. +See https://cloud.spring.io/spring-cloud-commons/2.2.x/reference/html/#health-indicator[here] for configuration options. + +===== ConsulHealthIndicator +An indicator is configured that verifies the health of the `ConsulClient`. + +By default, it retrieves the Consul leader node status and all registered services. +In deployments that have many registered services it may be costly to retrieve all services on every health check. +To skip the service retrieval and only check the leader node status set `spring.cloud.consul.health-indicator.include-services-query=false`. + +To disable the indicator set `management.health.consul.enabled=false`. + +WARNING: When the application runs in https://cloud.spring.io/spring-cloud-commons/2.2.x/reference/html/#the-bootstrap-application-context[bootstrap context mode] (the default), +this indicator is loaded into the bootstrap context and is not made available to the Actuator health endpoint. ==== Metadata and Consul tags @@ -221,34 +271,6 @@ spring: With this metadata, and multiple service instances deployed on localhost, the random value will kick in there to make the instance unique. In Cloudfoundry the `vcap.application.instance_id` will be populated automatically in a Spring Boot application, so the random value will not be needed. -==== Applying Headers to Health Check Requests - -Headers can be applied to health check requests. For example, if you're trying to register a https://cloud.spring.io/spring-cloud-config/[Spring Cloud Config] server that uses https://github.com/spring-cloud/spring-cloud-config/blob/master/docs/src/main/asciidoc/spring-cloud-config.adoc#vault-backend[Vault Backend]: - -.application.yml ----- -spring: - cloud: - consul: - discovery: - health-check-headers: - X-Config-Token: 6442e58b-d1ea-182e-cfa5-cf9cddef0722 ----- - -According to the HTTP standard, each header can have more than one values, in which case, an array can be supplied: - -.application.yml ----- -spring: - cloud: - consul: - discovery: - health-check-headers: - X-Config-Token: - - "6442e58b-d1ea-182e-cfa5-cf9cddef0722" - - "Some other value" ----- - === Looking up services ==== Using Load-balancer diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java index b2cb0ceb..18fe1e59 100644 --- a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java +++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java @@ -71,6 +71,7 @@ public class ConsulAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Endpoint.class) + @EnableConfigurationProperties(ConsulHealthIndicatorProperties.class) protected static class ConsulHealthConfig { @Bean @@ -83,8 +84,9 @@ public class ConsulAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledHealthIndicator("consul") - public ConsulHealthIndicator consulHealthIndicator(ConsulClient consulClient) { - return new ConsulHealthIndicator(consulClient); + public ConsulHealthIndicator consulHealthIndicator(ConsulClient consulClient, + ConsulHealthIndicatorProperties properties) { + return new ConsulHealthIndicator(consulClient, properties); } } diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicator.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicator.java index 3f364dd1..086e48b3 100644 --- a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicator.java +++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicator.java @@ -34,18 +34,24 @@ public class ConsulHealthIndicator extends AbstractHealthIndicator { private ConsulClient consul; - public ConsulHealthIndicator(ConsulClient consul) { + private ConsulHealthIndicatorProperties properties; + + public ConsulHealthIndicator(ConsulClient consul, + ConsulHealthIndicatorProperties properties) { this.consul = consul; + this.properties = properties; } @Override - protected void doHealthCheck(Health.Builder builder) throws Exception { + protected void doHealthCheck(Health.Builder builder) { final Response leaderStatus = this.consul.getStatusLeader(); - final Response>> services = this.consul - .getCatalogServices(CatalogServicesRequest.newBuilder() - .setQueryParams(QueryParams.DEFAULT).build()); - builder.up().withDetail("leader", leaderStatus.getValue()).withDetail("services", - services.getValue()); + builder.up().withDetail("leader", leaderStatus.getValue()); + if (properties.isIncludeServicesQuery()) { + final Response>> services = this.consul + .getCatalogServices(CatalogServicesRequest.newBuilder() + .setQueryParams(QueryParams.DEFAULT).build()); + builder.withDetail("services", services.getValue()); + } } } diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicatorProperties.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicatorProperties.java new file mode 100644 index 00000000..d9e4daf8 --- /dev/null +++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulHealthIndicatorProperties.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2019 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.consul; + +import com.ecwid.consul.v1.ConsulClient; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.style.ToStringCreator; + +/** + * Configuration properties for {@link ConsulHealthIndicator}. + * + * @author Chris Bono + */ +@ConfigurationProperties("spring.cloud.consul.health-indicator") +public class ConsulHealthIndicatorProperties { + + /** + * Whether or not the indicator should include a query for all registered services + * during its execution. When set to {@code false} the indicator only uses the lighter + * {@link ConsulClient#getStatusLeader()}. This can be helpful in large deployments + * where the number of services returned makes the operation unnecessarily heavy. + */ + private boolean includeServicesQuery = true; + + boolean isIncludeServicesQuery() { + return includeServicesQuery; + } + + void setIncludeServicesQuery(boolean includeServicesQuery) { + this.includeServicesQuery = includeServicesQuery; + } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("includeServicesQuery", this.includeServicesQuery).toString(); + } + +} diff --git a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulAutoConfigurationTests.java b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulAutoConfigurationTests.java index 53331c74..4d56c70a 100644 --- a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulAutoConfigurationTests.java +++ b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulAutoConfigurationTests.java @@ -22,46 +22,107 @@ import com.ecwid.consul.v1.ConsulClient; import com.ecwid.consul.v1.ConsulRawClient; import com.ecwid.consul.v1.catalog.CatalogConsulClient; import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.consul.test.ConsulTestcontainers; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(SpringRunner.class) -@SpringBootTest(properties = { "spring.cloud.consul.tls.key-store-instance-type=JKS", - "spring.cloud.consul.tls.key-store-path=src/test/resources/server.jks", - "spring.cloud.consul.tls.key-store-password=letmein", - "spring.cloud.consul.tls.certificate-path=src/test/resources/trustStore.jks", - "spring.cloud.consul.tls.certificate-password=change_me" }) -@ContextConfiguration(initializers = ConsulTestcontainers.class) +/** + * Auto-configuration integration tests for {@link ConsulAutoConfiguration}. + * + * @author Chris Bono + */ public class ConsulAutoConfigurationTests { - @Autowired - private ConsulClient consulClient; + private final ApplicationContextRunner appContextRunner = new ApplicationContextRunner() + .withInitializer(new ConsulTestcontainers()) + .withConfiguration(AutoConfigurations.of(ConsulAutoConfiguration.class)); + + @Test + public void defaultConfiguration() { + appContextRunner.run(context -> assertThat(context).hasNotFailed() + .hasSingleBean(ConsulProperties.class).hasSingleBean(ConsulClient.class) + .hasSingleBean(ConsulHealthIndicator.class) + .doesNotHaveBean(ConsulEndpoint.class)); + } + + @Test + public void consulDisabled() { + appContextRunner.withPropertyValues("spring.cloud.consul.enabled=false") + .run(context -> assertThat(context).hasNotFailed() + .doesNotHaveBean(ConsulProperties.class) + .doesNotHaveBean(ConsulClient.class) + .doesNotHaveBean(ConsulHealthIndicator.class) + .doesNotHaveBean(ConsulEndpoint.class)); + } @Test public void tlsConfigured() { - CatalogConsulClient client = (CatalogConsulClient) ReflectionTestUtils - .getField(this.consulClient, "catalogClient"); - ConsulRawClient rawClient = (ConsulRawClient) ReflectionTestUtils.getField(client, - "rawClient"); - HttpTransport httpTransport = (HttpTransport) ReflectionTestUtils - .getField(rawClient, "httpTransport"); - assertThat(httpTransport).isInstanceOf(DefaultHttpsTransport.class); + appContextRunner.withPropertyValues( + "spring.cloud.consul.tls.key-store-instance-type=JKS", + "spring.cloud.consul.tls.key-store-path=src/test/resources/server.jks", + "spring.cloud.consul.tls.key-store-password=letmein", + "spring.cloud.consul.tls.certificate-path=src/test/resources/trustStore.jks", + "spring.cloud.consul.tls.certificate-password=change_me").run(context -> { + assertThat(context).hasNotFailed().hasSingleBean(ConsulClient.class); + + ConsulClient consulClient = context.getBean(ConsulClient.class); + CatalogConsulClient catalogClient = (CatalogConsulClient) ReflectionTestUtils + .getField(consulClient, "catalogClient"); + ConsulRawClient rawClient = (ConsulRawClient) ReflectionTestUtils + .getField(catalogClient, "rawClient"); + HttpTransport httpTransport = (HttpTransport) ReflectionTestUtils + .getField(rawClient, "httpTransport"); + assertThat(httpTransport).isInstanceOf(DefaultHttpsTransport.class); + }); } - @EnableAutoConfiguration - @SpringBootConfiguration - protected static class TestConfig { + @Test + public void nonActuatorAppGetsNoEndpointOrHealthIndicator() { + appContextRunner.withClassLoader(new FilteredClassLoader(Endpoint.class)) + .withPropertyValues("management.endpoints.web.exposure.include=consul") + .run(context -> assertThat(context).hasNotFailed() + .doesNotHaveBean(ConsulHealthIndicator.class) + .doesNotHaveBean(ConsulEndpoint.class)); + } + @Test + public void consulEndpointAvailable() { + appContextRunner + .withPropertyValues("management.endpoints.web.exposure.include=consul") + .run(context -> assertThat(context).hasNotFailed() + .hasSingleBean(ConsulEndpoint.class)); + } + + @Test + public void consulEndpointAvailableButDisabled() { + appContextRunner + .withPropertyValues("management.endpoints.web.exposure.include=consul", + "management.endpoint.consul.enabled=false") + .run(context -> assertThat(context).hasNotFailed() + .doesNotHaveBean(ConsulEndpoint.class)); + } + + @Test + public void consulEndpointDisabled() { + appContextRunner.withPropertyValues("spring.cloud.consul.enabled=false") + .run(context -> assertThat(context).hasNotFailed() + .doesNotHaveBean(ConsulProperties.class) + .doesNotHaveBean(ConsulClient.class) + .doesNotHaveBean(ConsulHealthIndicator.class) + .doesNotHaveBean(ConsulEndpoint.class)); + } + + @Test + public void consulHealthIndicatorDisabled() { + appContextRunner.withPropertyValues("management.health.consul.enabled=false") + .run(context -> assertThat(context).hasNotFailed() + .doesNotHaveBean(ConsulHealthIndicator.class)); } } diff --git a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorDownTest.java b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorDownTest.java index cd610a16..25d471b6 100644 --- a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorDownTest.java +++ b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorDownTest.java @@ -16,6 +16,9 @@ package org.springframework.cloud.consul; +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.catalog.CatalogServicesRequest; import org.junit.Test; import org.junit.runner.RunWith; @@ -25,24 +28,47 @@ import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.Status; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** + * Integration test for {@link ConsulHealthIndicator} when its in the DOWN status. + * * @author Lomesh Patel (lomeshpatel) */ @RunWith(SpringRunner.class) -@SpringBootTest(properties = "spring.cloud.consul.host=invalidhost") +@SpringBootTest public class ConsulHealthIndicatorDownTest { + @MockBean + private ConsulClient consulClient; + @Autowired private HealthEndpoint healthEndpoint; @Test - public void doHealthCheck() { + public void statusIsDownWhenConsulClientFailsToGetLeaderStatus() { + when(consulClient.getStatusLeader()).thenThrow(new RuntimeException("no leader")); assertThat(this.healthEndpoint.health().getStatus()) .as("health status was not DOWN").isEqualTo(Status.DOWN); + verify(consulClient).getStatusLeader(); + } + + @Test + public void statusIsDownWhenConsulClientFailsToGetServices() { + Response leaderStatus = new Response<>("OK", 5150L, true, + System.currentTimeMillis()); + when(consulClient.getStatusLeader()).thenReturn(leaderStatus); + when(consulClient.getCatalogServices(any(CatalogServicesRequest.class))) + .thenThrow(new RuntimeException("no services")); + assertThat(this.healthEndpoint.health().getStatus()) + .as("health status was not DOWN").isEqualTo(Status.DOWN); + verify(consulClient).getCatalogServices(any(CatalogServicesRequest.class)); } @EnableAutoConfiguration diff --git a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorLightweightUpTest.java b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorLightweightUpTest.java new file mode 100644 index 00000000..7cf1df79 --- /dev/null +++ b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorLightweightUpTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2019 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.consul; + +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.catalog.CatalogServicesRequest; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.actuate.health.HealthEndpoint; +import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.consul.test.ConsulTestcontainers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Integration test for {@link ConsulHealthIndicator} using its lightweight check when its + * in the UP status. + * + * @author Lomesh Patel (lomeshpatel) + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + properties = "spring.cloud.consul.health-indicator.include-services-query=false") +@ContextConfiguration(initializers = ConsulTestcontainers.class) +public class ConsulHealthIndicatorLightweightUpTest { + + @SpyBean + private ConsulClient consulClient; + + @Autowired + private HealthEndpoint healthEndpoint; + + @Test + public void statusIsUp() { + assertThat(this.healthEndpoint.health().getStatus()) + .as("health status was not UP").isEqualTo(Status.UP); + verify(consulClient).getStatusLeader(); + verify(consulClient, never()) + .getCatalogServices(any(CatalogServicesRequest.class)); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + protected static class TestConfig { + + } + +} diff --git a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorUpTest.java b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorUpTest.java index 6d63577e..a4716e62 100644 --- a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorUpTest.java +++ b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/ConsulHealthIndicatorUpTest.java @@ -16,6 +16,8 @@ package org.springframework.cloud.consul; +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.catalog.CatalogServicesRequest; import org.junit.Test; import org.junit.runner.RunWith; @@ -25,13 +27,18 @@ import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.Status; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.cloud.consul.test.ConsulTestcontainers; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; /** + * Integration test for {@link ConsulHealthIndicator} when its in the UP status. + * * @author Lomesh Patel (lomeshpatel) */ @RunWith(SpringRunner.class) @@ -39,13 +46,18 @@ import static org.assertj.core.api.Assertions.assertThat; @ContextConfiguration(initializers = ConsulTestcontainers.class) public class ConsulHealthIndicatorUpTest { + @SpyBean + private ConsulClient consulClient; + @Autowired private HealthEndpoint healthEndpoint; @Test - public void doHealthCheck() { + public void statusIsUp() { assertThat(this.healthEndpoint.health().getStatus()) .as("health status was not UP").isEqualTo(Status.UP); + verify(consulClient).getStatusLeader(); + verify(consulClient).getCatalogServices(any(CatalogServicesRequest.class)); } @EnableAutoConfiguration