Add reactive health indicator.

Closes gh-221.
This commit is contained in:
Mark Paluch
2018-06-15 15:11:35 +02:00
parent 2b006895f0
commit 97951d95d1
6 changed files with 262 additions and 42 deletions

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cloud.vault.config;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.util.StringUtils;
import org.springframework.vault.core.VaultOperations;
@@ -28,7 +27,7 @@ import org.springframework.vault.support.VaultHealth;
* @author Stuart Ingram
* @author Mark Paluch
*/
public class VaultHealthIndicator implements HealthIndicator {
public class VaultHealthIndicator extends AbstractHealthIndicator {
private final VaultOperations vaultOperations;
@@ -36,41 +35,38 @@ public class VaultHealthIndicator implements HealthIndicator {
this.vaultOperations = vaultOperations;
}
@Override
public Health health() {
protected void doHealthCheck(Builder builder) {
try {
VaultHealth vaultHealthResponse = vaultOperations.opsForSys().health();
Builder healthBuilder = getHealthBuilder(vaultHealthResponse);
if (!vaultHealthResponse.isInitialized()) {
builder.down().withDetail("state", "Vault uninitialized");
}
else
if (StringUtils.hasText(vaultHealthResponse.getVersion())) {
healthBuilder = healthBuilder.withDetail("version",
vaultHealthResponse.getVersion());
if (vaultHealthResponse.isSealed()) {
builder.down().withDetail("state", "Vault sealed");
}
else
if (vaultHealthResponse.isStandby()) {
builder.up().withDetail("state", "Vault in standby");
}
else {
builder.up();
}
return healthBuilder.build();
if (StringUtils.hasText(vaultHealthResponse.getVersion())) {
builder.withDetail("version",
vaultHealthResponse.getVersion());
}
}
catch (Exception e) {
return Health.down(e).build();
builder.down(e);
}
}
private Builder getHealthBuilder(VaultHealth vaultHealthResponse) {
if (!vaultHealthResponse.isInitialized()) {
return Health.down().withDetail("state", "Vault uninitialized");
}
if (vaultHealthResponse.isSealed()) {
return Health.down().withDetail("state", "Vault sealed");
}
if (vaultHealthResponse.isStandby()) {
return Health.up().withDetail("state", "Vault in standby");
}
return Health.up();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2016-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.cloud.vault.config;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Vault providing beans for the application context.
*
* @author Mark Paluch
* @since 2.0
*/
@Configuration
@ConditionalOnEnabledHealthIndicator("vault")
@ConditionalOnBean(VaultBootstrapConfiguration.class)
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
@Import({ VaultHealthIndicatorConfiguration.class,
VaultReactiveHealthIndicatorConfiguration.class })
public class VaultHealthIndicatorAutoConfiguration {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 the original author or authors.
* Copyright 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.
@@ -18,33 +18,23 @@ package org.springframework.cloud.vault.config;
import java.util.Map;
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.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.core.VaultOperations;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Vault providing beans for the application context.
* Configuration for {@link VaultHealthIndicator}.
*
* @author Stuart Ingram
* @author Mark Paluch
* @since 1.1
*/
@Configuration
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnBean(VaultBootstrapConfiguration.class)
@ConditionalOnEnabledHealthIndicator("vault")
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
public class VaultHealthIndicatorConfiguration extends
@ConditionalOnBean(VaultOperations.class)
class VaultHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<VaultHealthIndicator, VaultOperations> {
private final Map<String, VaultOperations> vaultTemplates;

View File

@@ -0,0 +1,134 @@
/*
* Copyright 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.cloud.vault.config;
import java.lang.reflect.UndeclaredThrowableException;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
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.Health.Builder;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.vault.core.ReactiveVaultOperations;
import org.springframework.vault.support.VaultHealth;
import org.springframework.web.reactive.function.client.WebClientResponseException;
/**
* Reactive health indicator reporting Vault's availability.
*
* @author Mark Paluch
* @since 2.0
*/
public class VaultReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
private final ReactiveVaultOperations vaultOperations;
public VaultReactiveHealthIndicator(ReactiveVaultOperations vaultOperations) {
this.vaultOperations = vaultOperations;
}
@Override
protected Mono<Health> doHealthCheck(Builder builder) {
try {
return vaultOperations
.doWithSession(it -> it.get().uri("sys/health").exchange())
.flatMap(it -> it.bodyToMono(VaultHealthImpl.class))
.onErrorResume(WebClientResponseException.class,
VaultReactiveHealthIndicator::deserializeError)
.map(vaultHealthResponse -> {
return getHealth(builder, vaultHealthResponse);
});
}
catch (Exception e) {
return Mono.just(builder.down(e).build());
}
}
private static Mono<? extends VaultHealthImpl> deserializeError(
WebClientResponseException e) {
try {
ObjectMapper mapper = new ObjectMapper();
return Mono.just(mapper.readValue(e.getResponseBodyAsByteArray(),
VaultHealthImpl.class));
}
catch (Exception jsonError) {
UndeclaredThrowableException t = new UndeclaredThrowableException(jsonError);
t.addSuppressed(e);
return Mono.error(t);
}
}
private static Health getHealth(Builder builder, VaultHealthImpl vaultHealthResponse) {
if (!vaultHealthResponse.isInitialized()) {
builder.withDetail("state", "Vault uninitialized");
}
else
if (vaultHealthResponse.isSealed()) {
builder.down().withDetail("state", "Vault sealed");
}
else
if (vaultHealthResponse.isStandby()) {
builder.up().withDetail("state", "Vault in standby");
}
else {
builder.up();
}
if (StringUtils.hasText(vaultHealthResponse.getVersion())) {
builder.withDetail("version", vaultHealthResponse.getVersion());
}
return builder.build();
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
static class VaultHealthImpl implements VaultHealth {
private final boolean initialized;
private final boolean sealed;
private final boolean standby;
private final int serverTimeUtc;
@Nullable
private final String version;
private VaultHealthImpl(@JsonProperty("initialized") boolean initialized,
@JsonProperty("sealed") boolean sealed,
@JsonProperty("standby") boolean standby,
@JsonProperty("server_time_utc") int serverTimeUtc,
@Nullable @JsonProperty("version") String version) {
this.initialized = initialized;
this.sealed = sealed;
this.standby = standby;
this.serverTimeUtc = serverTimeUtc;
this.version = version;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 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.cloud.vault.config;
import java.util.Map;
import reactor.core.publisher.Flux;
import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthIndicatorConfiguration;
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.vault.core.ReactiveVaultOperations;
/**
* Configuration for {@link VaultReactiveHealthIndicator}.
*
* @author Mark Paluch
* @since 2.0
*/
@Configuration
@ConditionalOnClass(Flux.class)
@ConditionalOnBean(ReactiveVaultOperations.class)
class VaultReactiveHealthIndicatorConfiguration
extends
CompositeReactiveHealthIndicatorConfiguration<VaultReactiveHealthIndicator, ReactiveVaultOperations> {
private final Map<String, ReactiveVaultOperations> reactiveVaultTemplates;
public VaultReactiveHealthIndicatorConfiguration(
Map<String, ReactiveVaultOperations> reactiveVaultTemplates) {
this.reactiveVaultTemplates = reactiveVaultTemplates;
}
@Bean
@ConditionalOnMissingBean(name = { "vaultReactiveHealthIndicator" })
public ReactiveHealthIndicator vaultHealthIndicator() {
return this.createHealthIndicator(this.reactiveVaultTemplates);
}
}

View File

@@ -1,6 +1,6 @@
# Auto-Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.vault.config.VaultHealthIndicatorConfiguration
org.springframework.cloud.vault.config.VaultHealthIndicatorAutoConfiguration
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.vault.config.DiscoveryClientVaultBootstrapConfiguration,\