Add reactive health check.

Closes gh-783
This commit is contained in:
Mark Paluch
2023-04-19 09:38:27 +02:00
parent 42cdfaa2c9
commit 6a4a2a19e8
6 changed files with 204 additions and 2 deletions

View File

@@ -64,6 +64,12 @@ public interface ReactiveVaultOperations {
*/
ReactiveVaultTransitOperations opsForTransit(String path);
/**
* @return the operations interface administrative Vault access.
* @since 3.1
*/
ReactiveVaultSysOperations opsForSys();
/**
* Read from a Vault path. Reading data using this method is suitable for API
* calls/secret backends that do not require a request body.

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2016-2023 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.vault.core;
import reactor.core.publisher.Mono;
import org.springframework.vault.VaultException;
import org.springframework.vault.support.VaultHealth;
/**
* Interface that specifies a basic set of administrative Vault operations using reactive
* infrastructure.
*
* @author Mark Paluch
* @since 3.1
*/
public interface ReactiveVaultSysOperations {
/**
* @return {@literal true} if Vault is initialized.
* @see <a href="https://www.vaultproject.io/docs/http/sys-init.html">GET
* /sys/init</a>
*/
Mono<Boolean> isInitialized() throws VaultException;
/**
* Return the health status of Vault.
* @return the {@link VaultHealth}.
* @see <a href="https://www.vaultproject.io/docs/http/sys-health.html">GET
* /sys/health</a>
*/
Mono<VaultHealth> health() throws VaultException;
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2016-2023 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.vault.core;
import java.util.Map;
import org.springframework.http.HttpEntity;
import org.springframework.util.Assert;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.support.VaultHealth;
import reactor.core.publisher.Mono;
/**
* Default implementation of {@link ReactiveVaultSysOperations}.
*
* @author Mark Paluch
*/
public class ReactiveVaultSysTemplate implements ReactiveVaultSysOperations {
private final ReactiveVaultOperations vaultOperations;
/**
* Create a new {@link ReactiveVaultSysTemplate} with the given
* {@link ReactiveVaultOperations}.
* @param vaultOperations must not be {@literal null}.
*/
public ReactiveVaultSysTemplate(ReactiveVaultOperations vaultOperations) {
Assert.notNull(vaultOperations, "ReactiveVaultOperations must not be null");
this.vaultOperations = vaultOperations;
}
@Override
public Mono<Boolean> isInitialized() {
return this.vaultOperations.doWithSession(webClient -> {
return webClient.get()
.uri("sys/init")
.header(VaultHttpHeaders.VAULT_NAMESPACE, "")
.exchangeToMono(clientResponse -> clientResponse.toEntity(Map.class))
.map(it -> (Boolean) it.getBody().get("initialized"));
});
}
@Override
public Mono<VaultHealth> health() {
return this.vaultOperations.doWithVault(webClient -> {
return webClient.get()
.uri("sys/health")
.header(VaultHttpHeaders.VAULT_NAMESPACE, "")
.exchangeToMono(clientResponse -> {
return clientResponse.toEntity(VaultSysTemplate.VaultHealthImpl.class).map(HttpEntity::getBody);
});
});
}
}

View File

@@ -227,6 +227,11 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
}));
}
@Override
public ReactiveVaultSysOperations opsForSys() {
return new ReactiveVaultSysTemplate(this);
}
@Override
public ReactiveVaultTransitOperations opsForTransit() {
return opsForTransit("transit");

View File

@@ -398,8 +398,7 @@ public class VaultSysTemplate implements VaultSysOperations {
catch (RestClientResponseException responseError) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
return OBJECT_MAPPER.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
}
catch (Exception jsonError) {
throw responseError;

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2016-2023 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.vault.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.vault.util.IntegrationTestSupport;
import org.springframework.vault.util.RequiresVaultVersion;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.*;
/**
* Integration tests for {@link ReactiveVaultSysTemplate} through
* {@link ReactiveVaultSysOperations}.
*
* @author Mark Paluch
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
class ReactiveVaultSysTemplateIntegrationTests extends IntegrationTestSupport {
@Autowired
ReactiveVaultOperations vaultOperations;
ReactiveVaultSysOperations adminOperations;
@BeforeEach
void before() {
this.adminOperations = this.vaultOperations.opsForSys();
}
@Test
@RequiresVaultVersion("0.6.1")
void shouldReportHealth() {
this.adminOperations.health().as(StepVerifier::create).assertNext(health -> {
assertThat(health.isInitialized()).isTrue();
assertThat(health.isSealed()).isFalse();
assertThat(health.isPerformanceStandby()).isFalse();
assertThat(health.isRecoveryReplicationSecondary()).isFalse();
assertThat(health.isStandby()).isFalse();
}).verifyComplete();
}
@Test
void isInitializedShouldReturnTrue() {
this.adminOperations.isInitialized().as(StepVerifier::create).expectNext(true).verifyComplete();
}
}