Merge pull request #13864 from ayudovin:add-health-indicator-for-reactive-cassandra
* pr/13864: Polish "Add reactive health indicator for Cassandra" Add reactive health indicator for Cassandrapull/13873/merge
commit
7bc1863871
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.cassandra;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
|
||||
/**
|
||||
* Configuration for {@link CassandraHealthIndicator}.
|
||||
*
|
||||
* @author Julien Dubois
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(CassandraOperations.class)
|
||||
@ConditionalOnBean(CassandraOperations.class)
|
||||
class CassandraHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<CassandraHealthIndicator, CassandraOperations> {
|
||||
|
||||
private final Map<String, CassandraOperations> cassandraOperations;
|
||||
|
||||
CassandraHealthIndicatorConfiguration(
|
||||
Map<String, CassandraOperations> cassandraOperations) {
|
||||
this.cassandraOperations = cassandraOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "cassandraHealthIndicator")
|
||||
public HealthIndicator cassandraHealthIndicator() {
|
||||
return createHealthIndicator(this.cassandraOperations);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.cassandra;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthIndicatorConfiguration;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator;
|
||||
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.cassandra.core.ReactiveCassandraOperations;
|
||||
|
||||
/**
|
||||
* Configuration for {@link CassandraReactiveHealthIndicator}.
|
||||
*
|
||||
* @author Artsiom Yudovin
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(ReactiveCassandraOperations.class)
|
||||
@ConditionalOnBean(ReactiveCassandraOperations.class)
|
||||
class CassandraReactiveHealthIndicatorConfiguration extends
|
||||
CompositeReactiveHealthIndicatorConfiguration<CassandraReactiveHealthIndicator, ReactiveCassandraOperations> {
|
||||
|
||||
private final Map<String, ReactiveCassandraOperations> reactiveCassandraOperations;
|
||||
|
||||
CassandraReactiveHealthIndicatorConfiguration(
|
||||
Map<String, ReactiveCassandraOperations> reactiveCassandraOperations) {
|
||||
this.reactiveCassandraOperations = reactiveCassandraOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "cassandraReactiveHealthIndicator")
|
||||
public ReactiveHealthIndicator cassandraHealthIndicator() {
|
||||
return createHealthIndicator(this.reactiveCassandraOperations);
|
||||
}
|
||||
|
||||
}
|
@ -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.autoconfigure.cassandra;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator;
|
||||
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.cassandra.core.ReactiveCassandraOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link CassandraReactiveHealthIndicatorConfiguration}.
|
||||
*
|
||||
* @author Artsiom Yudovin
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CassandraReactiveHealthIndicatorConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(CassandraMockConfiguration.class).withConfiguration(
|
||||
AutoConfigurations.of(CassandraHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void runShouldCreateIndicator() {
|
||||
this.contextRunner.run((context) -> assertThat(context)
|
||||
.hasSingleBean(CassandraReactiveHealthIndicator.class)
|
||||
.doesNotHaveBean(CassandraHealthIndicator.class)
|
||||
.doesNotHaveBean(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runWhenDisabledShouldNotCreateIndicator() {
|
||||
this.contextRunner.withPropertyValues("management.health.cassandra.enabled:false")
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(CassandraReactiveHealthIndicator.class)
|
||||
.hasSingleBean(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class CassandraMockConfiguration {
|
||||
|
||||
@Bean
|
||||
public ReactiveCassandraOperations cassandraOperations() {
|
||||
return mock(ReactiveCassandraOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.cassandra;
|
||||
|
||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||
import com.datastax.driver.core.querybuilder.Select;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
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.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveHealthIndicator} for Cassandra.
|
||||
*
|
||||
* @author Artsiom Yudovin
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class CassandraReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
|
||||
|
||||
private final ReactiveCassandraOperations reactiveCassandraOperations;
|
||||
|
||||
/**
|
||||
* Create a new {@link CassandraHealthIndicator} instance.
|
||||
* @param reactiveCassandraOperations the Cassandra operations
|
||||
*/
|
||||
public CassandraReactiveHealthIndicator(
|
||||
ReactiveCassandraOperations reactiveCassandraOperations) {
|
||||
Assert.notNull(reactiveCassandraOperations,
|
||||
"ReactiveCassandraOperations must not be null");
|
||||
this.reactiveCassandraOperations = reactiveCassandraOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Health> doHealthCheck(Health.Builder builder) {
|
||||
Select select = QueryBuilder.select("release_version").from("system", "local");
|
||||
return this.reactiveCassandraOperations.getReactiveCqlOperations()
|
||||
.queryForObject(select, String.class)
|
||||
.map((version) -> builder.up().withDetail("version", version).build())
|
||||
.single();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.cassandra;
|
||||
|
||||
import com.datastax.driver.core.querybuilder.Select;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.data.cassandra.CassandraInternalException;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link CassandraReactiveHealthIndicatorTests}.
|
||||
*
|
||||
* @author Artsiom Yudovin
|
||||
*/
|
||||
public class CassandraReactiveHealthIndicatorTests {
|
||||
|
||||
@Test
|
||||
public void testCassandraIsUp() {
|
||||
ReactiveCqlOperations reactiveCqlOperations = mock(ReactiveCqlOperations.class);
|
||||
given(reactiveCqlOperations.queryForObject(any(Select.class), eq(String.class)))
|
||||
.willReturn(Mono.just("6.0.0"));
|
||||
ReactiveCassandraOperations reactiveCassandraOperations = mock(
|
||||
ReactiveCassandraOperations.class);
|
||||
given(reactiveCassandraOperations.getReactiveCqlOperations())
|
||||
.willReturn(reactiveCqlOperations);
|
||||
|
||||
CassandraReactiveHealthIndicator cassandraReactiveHealthIndicator = new CassandraReactiveHealthIndicator(
|
||||
reactiveCassandraOperations);
|
||||
Mono<Health> health = cassandraReactiveHealthIndicator.health();
|
||||
StepVerifier.create(health).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("version");
|
||||
assertThat(h.getDetails().get("version")).isEqualTo("6.0.0");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCassandraIsDown() {
|
||||
ReactiveCassandraOperations reactiveCassandraOperations = mock(
|
||||
ReactiveCassandraOperations.class);
|
||||
given(reactiveCassandraOperations.getReactiveCqlOperations())
|
||||
.willThrow(new CassandraInternalException("Connection failed"));
|
||||
|
||||
CassandraReactiveHealthIndicator cassandraReactiveHealthIndicator = new CassandraReactiveHealthIndicator(
|
||||
reactiveCassandraOperations);
|
||||
Mono<Health> health = cassandraReactiveHealthIndicator.health();
|
||||
StepVerifier.create(health).consumeNextWith((h) -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.DOWN);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("error");
|
||||
assertThat(h.getDetails().get("error")).isEqualTo(
|
||||
CassandraInternalException.class.getName() + ": Connection failed");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue