Bypass Vault namespace for healthchecks
Apparently sys/init and sys/health are not available from within a namespace. Closes gh-386.
This commit is contained in:
@@ -28,6 +28,7 @@ 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.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.core.ReactiveVaultOperations;
|
||||
import org.springframework.vault.support.VaultHealth;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
@@ -72,7 +73,8 @@ public class VaultReactiveHealthIndicator extends AbstractReactiveHealthIndicato
|
||||
protected Mono<Health> doHealthCheck(Builder builder) {
|
||||
|
||||
return this.vaultOperations
|
||||
.doWithSession((it) -> it.get().uri("sys/health").exchange())
|
||||
.doWithSession((it) -> it.get().uri("sys/health")
|
||||
.header(VaultHttpHeaders.VAULT_NAMESPACE, "").exchange())
|
||||
.flatMap((it) -> it.bodyToMono(VaultHealthImpl.class))
|
||||
.onErrorResume(WebClientResponseException.class,
|
||||
VaultReactiveHealthIndicator::deserializeError)
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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
|
||||
*
|
||||
* https://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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.cloud.vault.util.Settings;
|
||||
import org.springframework.cloud.vault.util.TestRestTemplateFactory;
|
||||
import org.springframework.cloud.vault.util.VaultRule;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.client.ClientHttpConnectorFactory;
|
||||
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.client.RestTemplateBuilder;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
import org.springframework.vault.core.VaultSysOperations;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.Policy;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.vault.support.VaultTokenRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for Vault's namespace feature.
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class VaultNamespaceTests {
|
||||
|
||||
@ClassRule
|
||||
public static VaultRule vaultRule = new VaultRule();
|
||||
|
||||
static final Policy POLICY = Policy.of(Policy.Rule.builder().path("/*")
|
||||
.capabilities(Policy.BuiltinCapabilities.READ,
|
||||
Policy.BuiltinCapabilities.CREATE, Policy.BuiltinCapabilities.UPDATE)
|
||||
.build());
|
||||
|
||||
RestTemplateBuilder maketingRestTemplate;
|
||||
|
||||
WebClientBuilder marketingWebClientBuilder = WebClientBuilder.builder()
|
||||
.httpConnector(ClientHttpConnectorFactory.create(new ClientOptions(),
|
||||
Settings.createSslConfiguration()))
|
||||
.endpoint(TestRestTemplateFactory.TEST_VAULT_ENDPOINT)
|
||||
.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, "marketing");
|
||||
|
||||
String marketingToken;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
Assume.assumeTrue("Namespaces require enterprise version",
|
||||
this.vaultRule.prepare().getVersion().isEnterprise());
|
||||
|
||||
List<String> namespaces = new ArrayList<>(Arrays.asList("dev/", "marketing/"));
|
||||
List<String> list = this.vaultRule.prepare().getVaultOperations()
|
||||
.list("sys/namespaces");
|
||||
namespaces.removeAll(list);
|
||||
|
||||
for (String namespace : namespaces) {
|
||||
this.vaultRule.prepare().getVaultOperations()
|
||||
.write("sys/namespaces/" + namespace.replaceAll("/", ""));
|
||||
}
|
||||
|
||||
this.maketingRestTemplate = RestTemplateBuilder.builder()
|
||||
.requestFactory(ClientHttpRequestFactoryFactory
|
||||
.create(new ClientOptions(), Settings.createSslConfiguration()))
|
||||
.endpoint(TestRestTemplateFactory.TEST_VAULT_ENDPOINT)
|
||||
.defaultHeader(VaultHttpHeaders.VAULT_NAMESPACE, "marketing");
|
||||
|
||||
VaultTemplate marketing = new VaultTemplate(this.maketingRestTemplate,
|
||||
new SimpleSessionManager(new TokenAuthentication(Settings.token())));
|
||||
|
||||
mountKv(marketing, "marketing-secrets");
|
||||
marketing.opsForSys().createOrUpdatePolicy("relaxed", POLICY);
|
||||
this.marketingToken = marketing.opsForToken()
|
||||
.create(VaultTokenRequest.builder().withPolicy("relaxed").build())
|
||||
.getToken().getToken();
|
||||
}
|
||||
|
||||
private void mountKv(VaultTemplate template, String path) {
|
||||
|
||||
VaultSysOperations vaultSysOperations = template.opsForSys();
|
||||
|
||||
Map<String, VaultMount> mounts = vaultSysOperations.getMounts();
|
||||
|
||||
if (!mounts.containsKey(path + "/")) {
|
||||
vaultSysOperations.mount(path, VaultMount.builder().type("kv")
|
||||
.options(Collections.singletonMap("version", "1")).build());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReportHealth() {
|
||||
|
||||
VaultTemplate marketing = new VaultTemplate(this.maketingRestTemplate,
|
||||
new SimpleSessionManager(new TokenAuthentication(this.marketingToken)));
|
||||
|
||||
Health.Builder builder = Health.unknown();
|
||||
new VaultHealthIndicator(marketing).doHealthCheck(builder);
|
||||
|
||||
assertThat(builder.build().getStatus()).isEqualTo(Status.UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReportReactiveHealth() {
|
||||
|
||||
ReactiveVaultTemplate reactiveMarketing = new ReactiveVaultTemplate(
|
||||
this.marketingWebClientBuilder,
|
||||
() -> Mono.just(VaultToken.of(this.marketingToken)));
|
||||
|
||||
Health.Builder builder = Health.unknown();
|
||||
|
||||
new VaultReactiveHealthIndicator(reactiveMarketing).doHealthCheck(builder)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(actual -> assertThat(actual.getStatus()).isEqualTo(Status.UP))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.vault.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -39,12 +40,14 @@ public final class Version implements Comparable<Version> {
|
||||
|
||||
private final int build;
|
||||
|
||||
private final boolean enterprise;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Version} from the given integer values. At least one value has
|
||||
* to be given but a maximum of 4.
|
||||
* @param parts must not be {@literal null} or empty.
|
||||
*/
|
||||
private Version(int... parts) {
|
||||
private Version(boolean enterprise, int... parts) {
|
||||
|
||||
Assert.notNull(parts, "Parts must not be null!");
|
||||
Assert.isTrue(parts.length > 0 && parts.length < 5,
|
||||
@@ -54,6 +57,7 @@ public final class Version implements Comparable<Version> {
|
||||
this.minor = parts.length > 1 ? parts[1] : 0;
|
||||
this.bugfix = parts.length > 2 ? parts[2] : 0;
|
||||
this.build = parts.length > 3 ? parts[3] : 0;
|
||||
this.enterprise = enterprise;
|
||||
|
||||
Assert.isTrue(this.major >= 0, "Major version must be greater or equal zero!");
|
||||
Assert.isTrue(this.minor >= 0, "Minor version must be greater or equal zero!");
|
||||
@@ -72,6 +76,7 @@ public final class Version implements Comparable<Version> {
|
||||
|
||||
String[] parts = version.trim().split("\\.");
|
||||
int[] intParts = new int[parts.length];
|
||||
boolean enterprise = version.endsWith("+ent");
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
|
||||
@@ -89,7 +94,7 @@ public final class Version implements Comparable<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
return new Version(intParts);
|
||||
return new Version(enterprise, intParts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,42 +174,8 @@ public final class Version implements Comparable<Version> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Version)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Version that = (Version) obj;
|
||||
|
||||
return this.major == that.major && this.minor == that.minor
|
||||
&& this.bugfix == that.bugfix && this.build == that.build;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
result += 31 * this.major;
|
||||
result += 31 * this.minor;
|
||||
result += 31 * this.bugfix;
|
||||
result += 31 * this.build;
|
||||
return result;
|
||||
public boolean isEnterprise() {
|
||||
return this.enterprise;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -215,7 +186,7 @@ public final class Version implements Comparable<Version> {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
List<Integer> digits = new ArrayList<>();
|
||||
List<Integer> digits = new ArrayList<Integer>();
|
||||
digits.add(this.major);
|
||||
digits.add(this.minor);
|
||||
|
||||
@@ -227,7 +198,28 @@ public final class Version implements Comparable<Version> {
|
||||
digits.add(this.build);
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(digits, ".");
|
||||
return StringUtils.collectionToDelimitedString(digits, ".")
|
||||
+ (isEnterprise() ? "+ent" : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Version)) {
|
||||
return false;
|
||||
}
|
||||
Version version = (Version) o;
|
||||
return this.major == version.major && this.minor == version.minor
|
||||
&& this.bugfix == version.bugfix && this.build == version.build
|
||||
&& this.enterprise == version.enterprise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.major, this.minor, this.bugfix, this.build,
|
||||
this.enterprise);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<description>Spring Cloud Vault Dependencies</description>
|
||||
|
||||
<properties>
|
||||
<spring-vault.version>2.2.1.RELEASE</spring-vault.version>
|
||||
<spring-vault.version>2.2.2.BUILD-SNAPSHOT</spring-vault.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
Reference in New Issue
Block a user