Added HealthIndicator functionality as well as health API support.

Resolves #24
This commit is contained in:
Stuart Ingram
2016-08-26 11:27:09 -04:00
parent 06e44d7c85
commit bfb4cfcf74
10 changed files with 236 additions and 2 deletions

View File

@@ -198,6 +198,12 @@ https://github.com/spring-cloud-incubator/spring-cloud-vault-config/blob/master/
chapters in the
https://github.com/spring-cloud-incubator/spring-cloud-vault-config/blob/master/docs/src/main/asciidoc/spring-cloud-vault-config.adoc[reference guide].
If the application imports the `spring-boot-starter-actuator` project, the
status of the vault server will be available via the `\health` endpoint.
The vault health indicator can be enabled or disabled through the
property `health.vault.enabled` (default `true`)
== Building
==== Build requirements for Vault
@@ -206,6 +212,20 @@ Spring Cloud Vault Config requires SSL certificates and a running
Vault instance listening on `localhost:8200`. Certificates and the Vault
setup are scripted, the scripts are located in `src/test/bash`.
The following scripts need to be run prior to building the project for the tests to pass.
$ ./src/test/bash/install_vault.sh
$ ./src/test/bash/create_certificates.sh
$ ./src/test/bash/local_run_vault.sh
Changes to the documentation should be made to the adocs found under `docs/src/main/asciidoc/`
README.adoc can be re-generated via the following
$ ./docs/src/main/ruby/generate_readme.sh > README.adoc
This script requires ruby and the asciidoctor gem installed (`gem install asciidoctor`)
:jdkversion: 1.7
=== Basic Compile and Test

View File

@@ -25,6 +25,20 @@ Spring Cloud Vault Config requires SSL certificates and a running
Vault instance listening on `localhost:8200`. Certificates and the Vault
setup are scripted, the scripts are located in `src/test/bash`.
The following scripts need to be run prior to building the project for the tests to pass.
$ ./src/test/bash/install_vault.sh
$ ./src/test/bash/create_certificates.sh
$ ./src/test/bash/local_run_vault.sh
Changes to the documentation should be made to the adocs found under `docs/src/main/asciidoc/`
README.adoc can be re-generated via the following
$ ./docs/src/main/ruby/generate_readme.sh > README.adoc
This script requires ruby and the asciidoctor gem installed (`gem install asciidoctor`)
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
== Contributing

View File

@@ -178,3 +178,9 @@ https://github.com/spring-cloud-incubator/spring-cloud-vault-config/blob/master/
https://github.com/spring-cloud-incubator/spring-cloud-vault-config/blob/master/docs/src/main/asciidoc/spring-cloud-vault-config.adoc#token-authentication[authentication]. Checkout these
chapters in the
https://github.com/spring-cloud-incubator/spring-cloud-vault-config/blob/master/docs/src/main/asciidoc/spring-cloud-vault-config.adoc[reference guide].
If the application imports the `spring-boot-starter-actuator` project, the
status of the vault server will be available via the `\health` endpoint.
The vault health indicator can be enabled or disabled through the
property `health.vault.enabled` (default `true`)

View File

@@ -21,6 +21,11 @@
<artifactId>spring-cloud-vault-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-core</artifactId>

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2016 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.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.vault.VaultBootstrapConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Stuart Ingram
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnBean(VaultBootstrapConfiguration.class)
@ConditionalOnProperty(name = "spring.cloud.vault.enabled", matchIfMissing = true)
@ConditionalOnExpression("${health.vault.enabled:true}")
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
@AutoConfigureAfter({ HealthIndicatorAutoConfiguration.class })
public class VaultConfigBootstrapHealthIndicator{
@Bean
@ConditionalOnMissingBean(name = "vaultHealthIndicator")
public HealthIndicator vaultHealthIndicator() {
return new VaultHealthIndicator();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2016 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.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.cloud.vault.VaultHealthResponse;
/**
* @author Stuart Ingram
*/
public class VaultHealthIndicator implements HealthIndicator {
@Autowired
private VaultTemplate vaultTemplate;
@Override
public Health health() {
try {
VaultHealthResponse vaultHealthResponse = vaultTemplate.health();
if(!vaultHealthResponse.isInitialized()) {
return Health.down().withDetail("Vault uninitialized",null).build();
} else if (vaultHealthResponse.isSealed()) {
return Health.down().withDetail("Vault sealed",null).build();
} else if (vaultHealthResponse.isStandby()) {
return Health.outOfService().withDetail("Vault in standby",null).build();
} else {
return Health.up().build();
}
}
catch(Exception e) {
return Health.down().build();
}
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.vault.ClientAuthentication;
import org.springframework.cloud.vault.VaultClient;
import org.springframework.cloud.vault.VaultClientResponse;
import org.springframework.cloud.vault.VaultHealthResponse;
import org.springframework.cloud.vault.VaultProperties;
import org.springframework.cloud.vault.VaultToken;
import org.springframework.util.Assert;
@@ -112,4 +113,30 @@ public class VaultTemplate implements InitializingBean, VaultOperations {
URI uri = client.buildUri(properties, pathTemplate, variables);
return sessionCallback.doWithVault(uri, vaultSession);
}
private final static String HEALTH_URL_TEMPLATE = "sys/health";
/**
* Query the current Vault service for it's health status
*
* @return A {@link VaultHealthResponse} containing the current service status.
*/
public VaultHealthResponse health() {
URI uri = client.buildUri(properties, HEALTH_URL_TEMPLATE);
return client.health(uri);
}
/**
* Check whether Vault is available (vault created and unsealed).
*
* @return
*/
public boolean isAvailable() {
try{
VaultHealthResponse health = health();
return health.isInitialized() && !health.isSealed();
} catch(Exception e) {
return false;
}
}
}

View File

@@ -1,3 +1,4 @@
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.vault.config.VaultConfigBootstrapConfiguration
org.springframework.cloud.vault.config.VaultConfigBootstrapConfiguration,\
org.springframework.cloud.vault.config.VaultConfigBootstrapHealthIndicator

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.cloud.vault;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.util.Map;
@@ -26,6 +28,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import lombok.Getter;
@@ -102,6 +105,29 @@ public class VaultClient {
createHeaders(vaultToken)));
}
/**
* Query the current Vault service for it's health status
*
* @param uri must not be {@literal null}.
* @return A {@link VaultHealthResponse} containing the current service status.
*/
public VaultHealthResponse health(URI uri) {
try {
ResponseEntity<VaultHealthResponse> healthResponse = this.restTemplate.exchange(
uri, HttpMethod.GET, null,
VaultHealthResponse.class);
return healthResponse.getBody();
} catch (HttpStatusCodeException responseError) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthResponse.class);
}
catch (Exception jsonError) {
throw responseError;
}
}
}
private VaultClientResponse exchange(URI uri, HttpMethod httpMethod,
HttpEntity<?> httpEntity) {
@@ -143,7 +169,7 @@ public class VaultClient {
* Build the Vault {@link URI} based on the given {@link VaultProperties} and
* {@code pathTemplate}. URI template variables will be expanded using
* {@code uriVariables}.
*
*
* @param properties must not be {@literal null}.
* @param pathTemplate must not be empty or {@literal null}.
* @param uriVariables must not be {@literal null}.

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2016 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;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind HTTP API responses for sys/health
*
* @author Stuart Ingram
* @author Bill Koch
*/
@Data
public class VaultHealthResponse {
private boolean initialized;
private boolean sealed;
private boolean standby;
@JsonProperty("server_time_utc")
private int serverTimeUtc;
}