Skip to content

Commit

Permalink
Add reactive health check.
Browse files Browse the repository at this point in the history
Closes gh-783
  • Loading branch information
mp911de committed Apr 19, 2023
1 parent 42cdfaa commit 6a4a2a1
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
@@ -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);
});
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ private ExchangeFilterFunction getSessionFilter() {
}));
}

@Override
public ReactiveVaultSysOperations opsForSys() {
return new ReactiveVaultSysTemplate(this);
}

@Override
public ReactiveVaultTransitOperations opsForTransit() {
return opsForTransit("transit");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,7 @@ public VaultHealth doWithRestOperations(RestOperations restOperations) {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}

0 comments on commit 6a4a2a1

Please sign in to comment.