From d1e710bd7f6fa7832e9c34bdd06e2d0d13ad2ec3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Sat, 27 Aug 2016 07:51:05 +0200 Subject: [PATCH] Polishing Reformat code. Use state key for health indicator details. Add health method declaration to VaultOperations. Switch actuator dependency to optional dependency. Add tests. Original pull request: gh-29 Fixes gh-24 --- spring-cloud-vault-config/pom.xml | 1 + .../VaultConfigBootstrapHealthIndicator.java | 3 +- .../vault/config/VaultHealthIndicator.java | 27 +++-- .../cloud/vault/config/VaultOperations.java | 8 ++ .../cloud/vault/config/VaultTemplate.java | 29 +---- .../config/VaultHealthIndicatorUnitTests.java | 108 ++++++++++++++++++ .../cloud/vault/VaultHealthResponse.java | 4 +- 7 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultHealthIndicatorUnitTests.java diff --git a/spring-cloud-vault-config/pom.xml b/spring-cloud-vault-config/pom.xml index edd34532..752cf3a8 100644 --- a/spring-cloud-vault-config/pom.xml +++ b/spring-cloud-vault-config/pom.xml @@ -24,6 +24,7 @@ org.springframework.boot spring-boot-starter-actuator + true diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigBootstrapHealthIndicator.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigBootstrapHealthIndicator.java index 501136c7..9ddb517f 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigBootstrapHealthIndicator.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultConfigBootstrapHealthIndicator.java @@ -39,7 +39,8 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnExpression("${health.vault.enabled:true}") @AutoConfigureBefore({ EndpointAutoConfiguration.class }) @AutoConfigureAfter({ HealthIndicatorAutoConfiguration.class }) -public class VaultConfigBootstrapHealthIndicator{ +public class VaultConfigBootstrapHealthIndicator { + @Bean @ConditionalOnMissingBean(name = "vaultHealthIndicator") public HealthIndicator vaultHealthIndicator() { diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultHealthIndicator.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultHealthIndicator.java index a4cb888e..d9e03ea7 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultHealthIndicator.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultHealthIndicator.java @@ -24,27 +24,34 @@ 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(); + + if (!vaultHealthResponse.isInitialized()) { + return Health.down().withDetail("state", "Vault uninitialized").build(); } + + if (vaultHealthResponse.isSealed()) { + return Health.down().withDetail("state", "Vault sealed").build(); + } + + if (vaultHealthResponse.isStandby()) { + return Health.outOfService().withDetail("state", "Vault in standby").build(); + } + + return Health.up().build(); } - catch(Exception e) { + catch (Exception e) { return Health.down().build(); } - } } diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultOperations.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultOperations.java index b801590c..a4fa56bd 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultOperations.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultOperations.java @@ -19,6 +19,7 @@ import java.net.URI; import java.util.Map; import org.springframework.cloud.vault.VaultClientResponse; +import org.springframework.cloud.vault.VaultHealthResponse; /** * Interface that specified a basic set of Vault operations, implemented by @@ -57,6 +58,13 @@ public interface VaultOperations { T doWithVault(String pathTemplate, Map variables, SessionCallback sessionCallback); + /** + * Query the current Vault service for it's health status. + * + * @return A {@link VaultHealthResponse} containing the current service status. + */ + VaultHealthResponse health(); + /** * Callback to execute actions within an authenticated {@link VaultSession}. * diff --git a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultTemplate.java b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultTemplate.java index 95e009a4..ca6b6230 100644 --- a/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultTemplate.java +++ b/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultTemplate.java @@ -35,6 +35,8 @@ import org.springframework.util.Assert; */ public class VaultTemplate implements InitializingBean, VaultOperations { + private final static String HEALTH_URL_TEMPLATE = "sys/health"; + private final VaultProperties properties; private final VaultClient client; private final ClientAuthentication clientAuthentication; @@ -100,7 +102,7 @@ public class VaultTemplate implements InitializingBean, VaultOperations { Assert.notNull(sessionCallback, "SessionCallback must not be null!"); - URI uri = client.buildUri(properties, path); + URI uri = VaultClient.buildUri(properties, path); return sessionCallback.doWithVault(uri, vaultSession); } @@ -114,29 +116,10 @@ public class VaultTemplate implements InitializingBean, VaultOperations { 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. - */ + @Override public VaultHealthResponse health() { - URI uri = client.buildUri(properties, HEALTH_URL_TEMPLATE); + + URI uri = VaultClient.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; - } - } } diff --git a/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultHealthIndicatorUnitTests.java b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultHealthIndicatorUnitTests.java new file mode 100644 index 00000000..d2c463e9 --- /dev/null +++ b/spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultHealthIndicatorUnitTests.java @@ -0,0 +1,108 @@ +/* + * 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 static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.vault.VaultHealthResponse; + +/** + * @author Mark Paluch + */ +@RunWith(MockitoJUnitRunner.class) +public class VaultHealthIndicatorUnitTests { + + @InjectMocks + VaultHealthIndicator healthIndicator = new VaultHealthIndicator(); + + @Mock + VaultTemplate vaultTemplate; + + @Test + public void shouldReportHealthyService() throws Exception { + + VaultHealthResponse healthResponse = new VaultHealthResponse(); + healthResponse.setInitialized(true); + + when(vaultTemplate.health()).thenReturn(healthResponse); + + Health health = healthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()).isEmpty(); + } + + @Test + public void shouldReportSealedService() throws Exception { + + VaultHealthResponse healthResponse = new VaultHealthResponse(); + healthResponse.setInitialized(true); + healthResponse.setSealed(true); + + when(vaultTemplate.health()).thenReturn(healthResponse); + + Health health = healthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).containsEntry("state", "Vault sealed"); + } + + @Test + public void shouldReportUninitializedService() throws Exception { + + VaultHealthResponse healthResponse = new VaultHealthResponse(); + + when(vaultTemplate.health()).thenReturn(healthResponse); + + Health health = healthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).containsEntry("state", "Vault uninitialized"); + } + + @Test + public void shouldReportStandbyService() throws Exception { + + VaultHealthResponse healthResponse = new VaultHealthResponse(); + healthResponse.setInitialized(true); + healthResponse.setStandby(true); + + when(vaultTemplate.health()).thenReturn(healthResponse); + + Health health = healthIndicator.health(); + + assertThat(health.getStatus()).isEqualTo(Status.OUT_OF_SERVICE); + assertThat(health.getDetails()).containsEntry("state", "Vault in standby"); + } + + @Test + public void exceptionsShouldReportDownStatus() throws Exception { + + when(vaultTemplate.health()).thenThrow(new IllegalStateException()); + + Health health = healthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).isEmpty(); + } +} \ No newline at end of file diff --git a/spring-cloud-vault-core/src/main/java/org/springframework/cloud/vault/VaultHealthResponse.java b/spring-cloud-vault-core/src/main/java/org/springframework/cloud/vault/VaultHealthResponse.java index 8ce90c2b..50bfb1a4 100644 --- a/spring-cloud-vault-core/src/main/java/org/springframework/cloud/vault/VaultHealthResponse.java +++ b/spring-cloud-vault-core/src/main/java/org/springframework/cloud/vault/VaultHealthResponse.java @@ -21,16 +21,18 @@ import lombok.Data; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Value object to bind HTTP API responses for sys/health + * 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; }