From 20ff0d97e488ab1d59d97e3ffffc17e3b7677014 Mon Sep 17 00:00:00 2001 From: Mikalai Lushchytski Date: Fri, 27 Jul 2018 15:23:37 +0300 Subject: [PATCH 1/2] Add reactive health indicator for Couchbase See gh-13926 --- ...hbaseHealthIndicatorAutoConfiguration.java | 49 ++------ ...CouchbaseHealthIndicatorConfiguration.java | 68 +++++++++++ ...eReactiveHealthIndicatorConfiguration.java | 56 +++++++++ ...baseHealthIndicatorConfigurationTests.java | 71 +++++++++++ ...tiveHealthIndicatorConfigurationTests.java | 71 +++++++++++ .../spring-boot-actuator/pom.xml | 5 + .../CouchbaseReactiveHealthIndicator.java | 73 ++++++++++++ ...CouchbaseReactiveHealthIndicatorTests.java | 112 ++++++++++++++++++ 8 files changed, 466 insertions(+), 39 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java create mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java create mode 100644 spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java index 5d685be9eb..812d84933f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java @@ -16,26 +16,20 @@ package org.springframework.boot.actuate.autoconfigure.couchbase; -import java.util.Map; - import com.couchbase.client.java.Bucket; -import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; -import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration; import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for @@ -46,37 +40,14 @@ import org.springframework.data.couchbase.core.CouchbaseOperations; * @since 2.0.0 */ @Configuration -@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class }) -@ConditionalOnBean(CouchbaseOperations.class) +@ConditionalOnClass(Bucket.class) @ConditionalOnEnabledHealthIndicator("couchbase") @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) -@AutoConfigureAfter(CouchbaseDataAutoConfiguration.class) -@EnableConfigurationProperties(CouchbaseHealthIndicatorProperties.class) -public class CouchbaseHealthIndicatorAutoConfiguration extends - CompositeHealthIndicatorConfiguration { - - private final Map couchbaseOperations; - - private final CouchbaseHealthIndicatorProperties properties; - - public CouchbaseHealthIndicatorAutoConfiguration( - Map couchbaseOperations, - CouchbaseHealthIndicatorProperties properties) { - this.couchbaseOperations = couchbaseOperations; - this.properties = properties; - } - - @Bean - @ConditionalOnMissingBean(name = "couchbaseHealthIndicator") - public HealthIndicator couchbaseHealthIndicator() { - return createHealthIndicator(this.couchbaseOperations); - } - - @Override - protected CouchbaseHealthIndicator createHealthIndicator( - CouchbaseOperations couchbaseOperations) { - return new CouchbaseHealthIndicator(couchbaseOperations, - this.properties.getTimeout()); - } +@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, + CouchbaseDataAutoConfiguration.class, + CouchbaseReactiveDataAutoConfiguration.class }) +@Import({ CouchbaseHealthIndicatorConfiguration.class, + CouchbaseReactiveHealthIndicatorConfiguration.class }) +public class CouchbaseHealthIndicatorAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java new file mode 100644 index 0000000000..066c112975 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.autoconfigure.couchbase; + +import java.util.Map; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.CouchbaseOperations; + +/** + * Configuration for {@link CouchbaseHealthIndicator}. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +@Configuration +@ConditionalOnClass(CouchbaseOperations.class) +@ConditionalOnBean(CouchbaseOperations.class) +@EnableConfigurationProperties(CouchbaseHealthIndicatorProperties.class) +public class CouchbaseHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { + + private final Map couchbaseOperations; + + private final CouchbaseHealthIndicatorProperties properties; + + CouchbaseHealthIndicatorConfiguration( + Map couchbaseOperations, + CouchbaseHealthIndicatorProperties properties) { + this.couchbaseOperations = couchbaseOperations; + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean(name = "couchbaseHealthIndicator") + public HealthIndicator couchbaseHealthIndicator() { + return createHealthIndicator(this.couchbaseOperations); + } + + @Override + protected CouchbaseHealthIndicator createHealthIndicator( + CouchbaseOperations couchbaseOperations) { + return new CouchbaseHealthIndicator(couchbaseOperations, + this.properties.getTimeout()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java new file mode 100644 index 0000000000..9a85bb87c1 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.autoconfigure.couchbase; + +import java.util.Map; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthIndicatorConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +/** + * Configuration for + * {@link org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator}. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +@Configuration +@ConditionalOnClass(RxJavaCouchbaseOperations.class) +@ConditionalOnBean(RxJavaCouchbaseOperations.class) +public class CouchbaseReactiveHealthIndicatorConfiguration extends + CompositeReactiveHealthIndicatorConfiguration { + + private final Map couchbaseOperations; + + CouchbaseReactiveHealthIndicatorConfiguration( + Map couchbaseOperations) { + this.couchbaseOperations = couchbaseOperations; + } + + @Bean + @ConditionalOnMissingBean(name = "couchbaseReactiveHealthIndicator") + public ReactiveHealthIndicator couchbaseReactiveHealthIndicator() { + return createHealthIndicator(this.couchbaseOperations); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java new file mode 100644 index 0000000000..c90a6bd20f --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.autoconfigure.couchbase; + +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.CouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseHealthIndicatorConfiguration}. + * + * @author Mikalai Lushchytski + */ +public class CouchbaseHealthIndicatorConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(CouchbaseMockConfiguration.class).withConfiguration( + AutoConfigurations.of(CouchbaseHealthIndicatorAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class)); + + @Test + public void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(CouchbaseHealthIndicator.class) + .doesNotHaveBean(CouchbaseReactiveHealthIndicator.class) + .doesNotHaveBean(ApplicationHealthIndicator.class)); + } + + @Test + public void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(CouchbaseHealthIndicator.class) + .hasSingleBean(ApplicationHealthIndicator.class)); + } + + @Configuration + protected static class CouchbaseMockConfiguration { + + @Bean + public CouchbaseOperations couchbaseOperations() { + return mock(CouchbaseOperations.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java new file mode 100644 index 0000000000..04b1d61ab8 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.autoconfigure.couchbase; + +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseReactiveHealthIndicatorConfiguration}. + * + * @author Mikalai Lushchytski + */ +public class CouchbaseReactiveHealthIndicatorConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(CouchbaseMockConfiguration.class).withConfiguration( + AutoConfigurations.of(CouchbaseHealthIndicatorAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class)); + + @Test + public void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(CouchbaseReactiveHealthIndicator.class) + .doesNotHaveBean(CouchbaseHealthIndicator.class) + .doesNotHaveBean(ApplicationHealthIndicator.class)); + } + + @Test + public void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(CouchbaseReactiveHealthIndicator.class) + .hasSingleBean(ApplicationHealthIndicator.class)); + } + + @Configuration + protected static class CouchbaseMockConfiguration { + + @Bean + public RxJavaCouchbaseOperations couchbaseOperations() { + return mock(RxJavaCouchbaseOperations.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/pom.xml b/spring-boot-project/spring-boot-actuator/pom.xml index 055feabaf3..cb8d8806df 100644 --- a/spring-boot-project/spring-boot-actuator/pom.xml +++ b/spring-boot-project/spring-boot-actuator/pom.xml @@ -77,6 +77,11 @@ + + io.reactivex + rxjava-reactive-streams + true + javax.cache cache-api diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java new file mode 100644 index 0000000000..9f43994c0a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.couchbase; + +import com.couchbase.client.java.bucket.BucketInfo; +import com.couchbase.client.java.cluster.ClusterInfo; +import reactor.core.publisher.Mono; +import rx.Observable; +import rx.RxReactiveStreams; +import rx.Single; + +import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; +import org.springframework.util.StringUtils; + +/** + * A {@link org.springframework.boot.actuate.health.ReactiveHealthIndicator} for + * Couchbase. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +public class CouchbaseReactiveHealthIndicator extends AbstractReactiveHealthIndicator { + + private final RxJavaCouchbaseOperations couchbaseOperations; + + /** + * Create a new {@link CouchbaseReactiveHealthIndicator} instance. + * @param couchbaseOperations Reactive couchbase client. + */ + public CouchbaseReactiveHealthIndicator( + RxJavaCouchbaseOperations couchbaseOperations) { + this.couchbaseOperations = couchbaseOperations; + } + + @Override + protected Mono doHealthCheck(Health.Builder builder) { + ClusterInfo cluster = this.couchbaseOperations.getCouchbaseClusterInfo(); + String versions = StringUtils + .collectionToCommaDelimitedString(cluster.getAllVersions()); + Observable bucket = this.couchbaseOperations.getCouchbaseBucket() + .bucketManager().async().info(); + Single health = bucket.map(BucketInfo::nodeList) + .map(StringUtils::collectionToCommaDelimitedString) + .map((nodes) -> up(builder, versions, nodes)) + .onErrorReturn((error) -> down(builder, error)).toSingle(); + return Mono.from(RxReactiveStreams.toPublisher(health)); + } + + private Health up(Health.Builder builder, String versions, String nodes) { + return builder.up().withDetail("versions", versions).withDetail("nodes", nodes) + .build(); + } + + private Health down(Health.Builder builder, Throwable error) { + return builder.down(error).build(); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java new file mode 100644 index 0000000000..f728a42f62 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2018 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 + * + * 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.springframework.boot.actuate.couchbase; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.bucket.AsyncBucketManager; +import com.couchbase.client.java.bucket.BucketInfo; +import com.couchbase.client.java.bucket.BucketManager; +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.error.TranscodingException; +import com.couchbase.client.java.util.features.Version; +import org.junit.Test; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import rx.Observable; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseReactiveHealthIndicator}. + */ +public class CouchbaseReactiveHealthIndicatorTests { + + @Test + public void testCouchbaseIsUp() { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( + RxJavaCouchbaseOperations.class); + given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) + .willReturn(clusterInfo); + given(clusterInfo.getAllVersions()) + .willReturn(Arrays.asList(new Version(5, 5, 0), new Version(6, 0, 0))); + Bucket bucket = mock(Bucket.class); + BucketManager bucketManager = mock(BucketManager.class); + AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); + given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + given(bucket.bucketManager()).willReturn(bucketManager); + given(bucketManager.async()).willReturn(asyncBucketManager); + BucketInfo info = mock(BucketInfo.class); + given(asyncBucketManager.info()).willReturn(Observable.just(info)); + + InetAddress node1Address = mock(InetAddress.class); + InetAddress node2Address = mock(InetAddress.class); + given(info.nodeList()).willReturn(Arrays.asList(node1Address, node2Address)); + given(node1Address.toString()).willReturn("127.0.0.1"); + given(node2Address.toString()).willReturn("127.0.0.2"); + + CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( + rxJavaCouchbaseOperations); + + Mono health = couchbaseReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.UP); + assertThat(h.getDetails()).containsKeys("versions", "nodes"); + assertThat(h.getDetails().get("versions")).isEqualTo("5.5.0,6.0.0"); + assertThat(h.getDetails().get("nodes")).isEqualTo("127.0.0.1,127.0.0.2"); + }).verifyComplete(); + } + + @Test + public void testCouchbaseIsDown() { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( + RxJavaCouchbaseOperations.class); + given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) + .willReturn(clusterInfo); + given(clusterInfo.getAllVersions()) + .willReturn(Collections.singletonList(new Version(5, 5, 0))); + BucketManager bucketManager = mock(BucketManager.class); + AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); + Bucket bucket = mock(Bucket.class); + given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + given(bucket.bucketManager()).willReturn(bucketManager); + given(bucketManager.async()).willReturn(asyncBucketManager); + given(asyncBucketManager.info()) + .willReturn(Observable.error(new TranscodingException("Failure"))); + CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( + rxJavaCouchbaseOperations); + + Mono health = couchbaseReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.DOWN); + assertThat(h.getDetails()).containsOnlyKeys("error"); + assertThat(h.getDetails().get("error")) + .isEqualTo(TranscodingException.class.getName() + ": Failure"); + }).verifyComplete(); + } + +} From 91b4dc2f69561d4b5f56243d29084c12f92dad13 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 5 Oct 2018 13:50:59 +0200 Subject: [PATCH 2/2] Polish "Add reactive health indicator for Couchbase" Closes gh-13926 --- ...hbaseHealthIndicatorAutoConfiguration.java | 4 +- ...CouchbaseHealthIndicatorConfiguration.java | 5 +- ...eReactiveHealthIndicatorConfiguration.java | 23 ++++- ...HealthIndicatorAutoConfigurationTests.java | 85 ------------------- ...baseHealthIndicatorConfigurationTests.java | 15 +++- ...tiveHealthIndicatorConfigurationTests.java | 14 +++ .../spring-boot-actuator/pom.xml | 10 +-- .../CouchbaseReactiveHealthIndicator.java | 35 ++++---- ...CouchbaseReactiveHealthIndicatorTests.java | 79 ++++++++++------- .../asciidoc/production-ready-features.adoc | 3 + 10 files changed, 125 insertions(+), 148 deletions(-) delete mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java index 812d84933f..13f1899f7e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java @@ -46,8 +46,8 @@ import org.springframework.context.annotation.Import; @AutoConfigureAfter({ CouchbaseAutoConfiguration.class, CouchbaseDataAutoConfiguration.class, CouchbaseReactiveDataAutoConfiguration.class }) -@Import({ CouchbaseHealthIndicatorConfiguration.class, - CouchbaseReactiveHealthIndicatorConfiguration.class }) +@Import({ CouchbaseReactiveHealthIndicatorConfiguration.class, + CouchbaseHealthIndicatorConfiguration.class }) public class CouchbaseHealthIndicatorAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java index 066c112975..2d8f081e7b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java @@ -31,8 +31,9 @@ import org.springframework.data.couchbase.core.CouchbaseOperations; /** * Configuration for {@link CouchbaseHealthIndicator}. * - * @author Mikalai Lushchytski - * @since 2.1.0 + * @author EddĂș MelĂ©ndez + * @author Stephane Nicoll + * @since 2.0.0 */ @Configuration @ConditionalOnClass(CouchbaseOperations.class) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java index 9a85bb87c1..23ad168ad7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java @@ -17,34 +17,42 @@ package org.springframework.boot.actuate.autoconfigure.couchbase; import java.util.Map; +import reactor.core.publisher.Flux; + import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthIndicatorConfiguration; import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; import org.springframework.boot.actuate.health.ReactiveHealthIndicator; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; /** - * Configuration for - * {@link org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator}. + * Configuration for {@link CouchbaseReactiveHealthIndicator}. * * @author Mikalai Lushchytski + * @author Stephane Nicoll * @since 2.1.0 */ @Configuration -@ConditionalOnClass(RxJavaCouchbaseOperations.class) +@ConditionalOnClass({ RxJavaCouchbaseOperations.class, Flux.class }) @ConditionalOnBean(RxJavaCouchbaseOperations.class) +@EnableConfigurationProperties(CouchbaseHealthIndicatorProperties.class) public class CouchbaseReactiveHealthIndicatorConfiguration extends CompositeReactiveHealthIndicatorConfiguration { private final Map couchbaseOperations; + private final CouchbaseHealthIndicatorProperties properties; + CouchbaseReactiveHealthIndicatorConfiguration( - Map couchbaseOperations) { + Map couchbaseOperations, + CouchbaseHealthIndicatorProperties properties) { this.couchbaseOperations = couchbaseOperations; + this.properties = properties; } @Bean @@ -53,4 +61,11 @@ public class CouchbaseReactiveHealthIndicatorConfiguration extends return createHealthIndicator(this.couchbaseOperations); } + @Override + protected CouchbaseReactiveHealthIndicator createHealthIndicator( + RxJavaCouchbaseOperations couchbaseOperations) { + return new CouchbaseReactiveHealthIndicator(couchbaseOperations, + this.properties.getTimeout()); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java deleted file mode 100644 index 21fce0037b..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012-2018 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 - * - * 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.springframework.boot.actuate.autoconfigure.couchbase; - -import org.junit.Test; - -import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; -import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; -import org.springframework.boot.actuate.health.ApplicationHealthIndicator; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.test.util.ReflectionTestUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link CouchbaseHealthIndicatorAutoConfiguration}. - * - * @author Phillip Webb - * @author Stephane Nicoll - */ -public class CouchbaseHealthIndicatorAutoConfigurationTests { - - private ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class, - CouchbaseHealthIndicatorAutoConfiguration.class, - HealthIndicatorAutoConfiguration.class)); - - @Test - public void runShouldCreateIndicator() { - this.contextRunner.run((context) -> assertThat(context) - .hasSingleBean(CouchbaseHealthIndicator.class) - .doesNotHaveBean(ApplicationHealthIndicator.class)); - } - - @Test - public void runWithCustomTimeoutShouldCreateIndicator() { - this.contextRunner.withPropertyValues("management.health.couchbase.timeout=2s") - .run((context) -> { - assertThat(context).hasSingleBean(CouchbaseHealthIndicator.class); - assertThat(ReflectionTestUtils.getField( - context.getBean(CouchbaseHealthIndicator.class), "timeout")) - .isEqualTo(2000L); - }); - } - - @Test - public void runWhenDisabledShouldNotCreateIndicator() { - this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") - .run((context) -> assertThat(context) - .doesNotHaveBean(CouchbaseHealthIndicator.class) - .hasSingleBean(ApplicationHealthIndicator.class)); - } - - @Configuration - @AutoConfigureBefore(CouchbaseHealthIndicatorAutoConfiguration.class) - protected static class CouchbaseConfiguration { - - @Bean - public CouchbaseOperations couchbaseOperations() { - return mock(CouchbaseOperations.class); - } - - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java index c90a6bd20f..b8ca7c46d0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java @@ -26,6 +26,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -33,7 +34,8 @@ import static org.mockito.Mockito.mock; /** * Tests for {@link CouchbaseHealthIndicatorConfiguration}. * - * @author Mikalai Lushchytski + * @author Phillip Webb + * @author Stephane Nicoll */ public class CouchbaseHealthIndicatorConfigurationTests { @@ -50,6 +52,17 @@ public class CouchbaseHealthIndicatorConfigurationTests { .doesNotHaveBean(ApplicationHealthIndicator.class)); } + @Test + public void runWithCustomTimeoutShouldCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.timeout=2s") + .run((context) -> { + assertThat(context).hasSingleBean(CouchbaseHealthIndicator.class); + assertThat(ReflectionTestUtils.getField( + context.getBean(CouchbaseHealthIndicator.class), "timeout")) + .isEqualTo(2000L); + }); + } + @Test public void runWhenDisabledShouldNotCreateIndicator() { this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java index 04b1d61ab8..33385c7f8c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java @@ -15,6 +15,8 @@ */ package org.springframework.boot.actuate.autoconfigure.couchbase; +import java.time.Duration; + import org.junit.Test; import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; @@ -50,6 +52,18 @@ public class CouchbaseReactiveHealthIndicatorConfigurationTests { .doesNotHaveBean(ApplicationHealthIndicator.class)); } + @Test + public void runWithCustomTimeoutShouldCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.timeout=2s") + .run((context) -> { + assertThat(context) + .hasSingleBean(CouchbaseReactiveHealthIndicator.class); + assertThat(context.getBean(CouchbaseReactiveHealthIndicator.class)) + .hasFieldOrPropertyWithValue("timeout", + Duration.ofSeconds(2)); + }); + } + @Test public void runWhenDisabledShouldNotCreateIndicator() { this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") diff --git a/spring-boot-project/spring-boot-actuator/pom.xml b/spring-boot-project/spring-boot-actuator/pom.xml index cb8d8806df..ba0b2dd341 100644 --- a/spring-boot-project/spring-boot-actuator/pom.xml +++ b/spring-boot-project/spring-boot-actuator/pom.xml @@ -61,6 +61,11 @@ micrometer-registry-prometheus true + + io.reactivex + rxjava-reactive-streams + true + io.searchbox jest @@ -77,11 +82,6 @@ - - io.reactivex - rxjava-reactive-streams - true - javax.cache cache-api diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java index 9f43994c0a..218cb26a2a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java @@ -15,6 +15,9 @@ */ package org.springframework.boot.actuate.couchbase; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + import com.couchbase.client.java.bucket.BucketInfo; import com.couchbase.client.java.cluster.ClusterInfo; import reactor.core.publisher.Mono; @@ -24,27 +27,32 @@ import rx.Single; import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; import org.springframework.util.StringUtils; /** - * A {@link org.springframework.boot.actuate.health.ReactiveHealthIndicator} for - * Couchbase. + * A {@link ReactiveHealthIndicator} for Couchbase. * * @author Mikalai Lushchytski + * @author Stephane Nicoll * @since 2.1.0 */ public class CouchbaseReactiveHealthIndicator extends AbstractReactiveHealthIndicator { private final RxJavaCouchbaseOperations couchbaseOperations; + private final Duration timeout; + /** * Create a new {@link CouchbaseReactiveHealthIndicator} instance. - * @param couchbaseOperations Reactive couchbase client. + * @param couchbaseOperations the reactive couchbase operations + * @param timeout the request timeout */ - public CouchbaseReactiveHealthIndicator( - RxJavaCouchbaseOperations couchbaseOperations) { + public CouchbaseReactiveHealthIndicator(RxJavaCouchbaseOperations couchbaseOperations, + Duration timeout) { this.couchbaseOperations = couchbaseOperations; + this.timeout = timeout; } @Override @@ -53,21 +61,14 @@ public class CouchbaseReactiveHealthIndicator extends AbstractReactiveHealthIndi String versions = StringUtils .collectionToCommaDelimitedString(cluster.getAllVersions()); Observable bucket = this.couchbaseOperations.getCouchbaseBucket() - .bucketManager().async().info(); + .bucketManager().async().info() + .timeout(this.timeout.toMillis(), TimeUnit.MILLISECONDS); Single health = bucket.map(BucketInfo::nodeList) .map(StringUtils::collectionToCommaDelimitedString) - .map((nodes) -> up(builder, versions, nodes)) - .onErrorReturn((error) -> down(builder, error)).toSingle(); + .map((nodes) -> builder.up().withDetail("versions", versions) + .withDetail("nodes", nodes).build()) + .toSingle(); return Mono.from(RxReactiveStreams.toPublisher(health)); } - private Health up(Health.Builder builder, String versions, String nodes) { - return builder.up().withDetail("versions", versions).withDetail("nodes", nodes) - .build(); - } - - private Health down(Health.Builder builder, Throwable error) { - return builder.down(error).build(); - } - } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java index f728a42f62..4edd07b700 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java @@ -16,8 +16,10 @@ package org.springframework.boot.actuate.couchbase; import java.net.InetAddress; +import java.time.Duration; import java.util.Arrays; -import java.util.Collections; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.bucket.AsyncBucketManager; @@ -45,32 +47,20 @@ import static org.mockito.Mockito.mock; public class CouchbaseReactiveHealthIndicatorTests { @Test - public void testCouchbaseIsUp() { - ClusterInfo clusterInfo = mock(ClusterInfo.class); + public void couchbaseIsUp() { RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( RxJavaCouchbaseOperations.class); - given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) - .willReturn(clusterInfo); - given(clusterInfo.getAllVersions()) - .willReturn(Arrays.asList(new Version(5, 5, 0), new Version(6, 0, 0))); - Bucket bucket = mock(Bucket.class); - BucketManager bucketManager = mock(BucketManager.class); - AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); - given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); - given(bucket.bucketManager()).willReturn(bucketManager); - given(bucketManager.async()).willReturn(asyncBucketManager); + AsyncBucketManager asyncBucketManager = mockAsyncBucketManager( + rxJavaCouchbaseOperations); BucketInfo info = mock(BucketInfo.class); - given(asyncBucketManager.info()).willReturn(Observable.just(info)); - InetAddress node1Address = mock(InetAddress.class); InetAddress node2Address = mock(InetAddress.class); given(info.nodeList()).willReturn(Arrays.asList(node1Address, node2Address)); given(node1Address.toString()).willReturn("127.0.0.1"); given(node2Address.toString()).willReturn("127.0.0.2"); - + given(asyncBucketManager.info()).willReturn(Observable.just(info)); CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( - rxJavaCouchbaseOperations); - + rxJavaCouchbaseOperations, Duration.ofSeconds(2)); Mono health = couchbaseReactiveHealthIndicator.health(); StepVerifier.create(health).consumeNextWith((h) -> { assertThat(h.getStatus()).isEqualTo(Status.UP); @@ -81,25 +71,34 @@ public class CouchbaseReactiveHealthIndicatorTests { } @Test - public void testCouchbaseIsDown() { - ClusterInfo clusterInfo = mock(ClusterInfo.class); + public void couchbaseTimeout() { RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( RxJavaCouchbaseOperations.class); - given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) - .willReturn(clusterInfo); - given(clusterInfo.getAllVersions()) - .willReturn(Collections.singletonList(new Version(5, 5, 0))); - BucketManager bucketManager = mock(BucketManager.class); - AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); - Bucket bucket = mock(Bucket.class); - given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); - given(bucket.bucketManager()).willReturn(bucketManager); - given(bucketManager.async()).willReturn(asyncBucketManager); + AsyncBucketManager asyncBucketManager = mockAsyncBucketManager( + rxJavaCouchbaseOperations); + given(asyncBucketManager.info()).willReturn( + Observable.just(mock(BucketInfo.class)).delay(20, TimeUnit.MILLISECONDS)); + CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( + rxJavaCouchbaseOperations, Duration.ofMillis(10)); + Mono health = couchbaseReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.DOWN); + assertThat(h.getDetails()).containsOnlyKeys("error"); + assertThat(h.getDetails().get("error")).asString() + .contains(TimeoutException.class.getName()); + }).verifyComplete(); + } + + @Test + public void couchbaseIsDown() { + RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( + RxJavaCouchbaseOperations.class); + AsyncBucketManager asyncBucketManager = mockAsyncBucketManager( + rxJavaCouchbaseOperations); given(asyncBucketManager.info()) .willReturn(Observable.error(new TranscodingException("Failure"))); CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( - rxJavaCouchbaseOperations); - + rxJavaCouchbaseOperations, Duration.ofSeconds(2)); Mono health = couchbaseReactiveHealthIndicator.health(); StepVerifier.create(health).consumeNextWith((h) -> { assertThat(h.getStatus()).isEqualTo(Status.DOWN); @@ -109,4 +108,20 @@ public class CouchbaseReactiveHealthIndicatorTests { }).verifyComplete(); } + private AsyncBucketManager mockAsyncBucketManager( + RxJavaCouchbaseOperations rxJavaCouchbaseOperations) { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) + .willReturn(clusterInfo); + given(clusterInfo.getAllVersions()) + .willReturn(Arrays.asList(new Version(5, 5, 0), new Version(6, 0, 0))); + Bucket bucket = mock(Bucket.class); + BucketManager bucketManager = mock(BucketManager.class); + AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); + given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + given(bucket.bucketManager()).willReturn(bucketManager); + given(bucketManager.async()).willReturn(asyncBucketManager); + return asyncBucketManager; + } + } diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index ac8e88c00d..98386320af 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -926,6 +926,9 @@ appropriate: |{sc-spring-boot-actuator}/cassandra/CassandraReactiveHealthIndicator.{sc-ext}[`CassandraReactiveHealthIndicator`] |Checks that a Cassandra database is up. +|{sc-spring-boot-actuator}/couchbase/CouchbaseReactiveHealthIndicator.{sc-ext}[`CouchbaseReactiveHealthIndicator`] +|Checks that a Couchbase cluster is up. + |{sc-spring-boot-actuator}/mongo/MongoReactiveHealthIndicator.{sc-ext}[`MongoReactiveHealthIndicator`] |Checks that a Mongo database is up.