Skip to content

Commit 6a4a2a1

Browse files
committed
Add reactive health check.
Closes gh-783
1 parent 42cdfaa commit 6a4a2a1

File tree

6 files changed

+204
-2
lines changed

6 files changed

+204
-2
lines changed

spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ public interface ReactiveVaultOperations {
6464
*/
6565
ReactiveVaultTransitOperations opsForTransit(String path);
6666

67+
/**
68+
* @return the operations interface administrative Vault access.
69+
* @since 3.1
70+
*/
71+
ReactiveVaultSysOperations opsForSys();
72+
6773
/**
6874
* Read from a Vault path. Reading data using this method is suitable for API
6975
* calls/secret backends that do not require a request body.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2016-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import reactor.core.publisher.Mono;
19+
20+
import org.springframework.vault.VaultException;
21+
import org.springframework.vault.support.VaultHealth;
22+
23+
/**
24+
* Interface that specifies a basic set of administrative Vault operations using reactive
25+
* infrastructure.
26+
*
27+
* @author Mark Paluch
28+
* @since 3.1
29+
*/
30+
public interface ReactiveVaultSysOperations {
31+
32+
/**
33+
* @return {@literal true} if Vault is initialized.
34+
* @see <a href="https://www.vaultproject.io/docs/http/sys-init.html">GET
35+
* /sys/init</a>
36+
*/
37+
Mono<Boolean> isInitialized() throws VaultException;
38+
39+
/**
40+
* Return the health status of Vault.
41+
* @return the {@link VaultHealth}.
42+
* @see <a href="https://www.vaultproject.io/docs/http/sys-health.html">GET
43+
* /sys/health</a>
44+
*/
45+
Mono<VaultHealth> health() throws VaultException;
46+
47+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2016-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import java.util.Map;
19+
20+
import org.springframework.http.HttpEntity;
21+
import org.springframework.util.Assert;
22+
import org.springframework.vault.client.VaultHttpHeaders;
23+
import org.springframework.vault.support.VaultHealth;
24+
25+
import reactor.core.publisher.Mono;
26+
27+
/**
28+
* Default implementation of {@link ReactiveVaultSysOperations}.
29+
*
30+
* @author Mark Paluch
31+
*/
32+
public class ReactiveVaultSysTemplate implements ReactiveVaultSysOperations {
33+
34+
private final ReactiveVaultOperations vaultOperations;
35+
36+
/**
37+
* Create a new {@link ReactiveVaultSysTemplate} with the given
38+
* {@link ReactiveVaultOperations}.
39+
* @param vaultOperations must not be {@literal null}.
40+
*/
41+
public ReactiveVaultSysTemplate(ReactiveVaultOperations vaultOperations) {
42+
43+
Assert.notNull(vaultOperations, "ReactiveVaultOperations must not be null");
44+
45+
this.vaultOperations = vaultOperations;
46+
47+
}
48+
49+
@Override
50+
public Mono<Boolean> isInitialized() {
51+
52+
return this.vaultOperations.doWithSession(webClient -> {
53+
return webClient.get()
54+
.uri("sys/init")
55+
.header(VaultHttpHeaders.VAULT_NAMESPACE, "")
56+
.exchangeToMono(clientResponse -> clientResponse.toEntity(Map.class))
57+
.map(it -> (Boolean) it.getBody().get("initialized"));
58+
});
59+
}
60+
61+
@Override
62+
public Mono<VaultHealth> health() {
63+
64+
return this.vaultOperations.doWithVault(webClient -> {
65+
66+
return webClient.get()
67+
.uri("sys/health")
68+
.header(VaultHttpHeaders.VAULT_NAMESPACE, "")
69+
.exchangeToMono(clientResponse -> {
70+
return clientResponse.toEntity(VaultSysTemplate.VaultHealthImpl.class).map(HttpEntity::getBody);
71+
});
72+
});
73+
}
74+
75+
}

spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ private ExchangeFilterFunction getSessionFilter() {
227227
}));
228228
}
229229

230+
@Override
231+
public ReactiveVaultSysOperations opsForSys() {
232+
return new ReactiveVaultSysTemplate(this);
233+
}
234+
230235
@Override
231236
public ReactiveVaultTransitOperations opsForTransit() {
232237
return opsForTransit("transit");

spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,7 @@ public VaultHealth doWithRestOperations(RestOperations restOperations) {
398398
catch (RestClientResponseException responseError) {
399399

400400
try {
401-
ObjectMapper mapper = new ObjectMapper();
402-
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
401+
return OBJECT_MAPPER.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
403402
}
404403
catch (Exception jsonError) {
405404
throw responseError;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2016-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import org.junit.jupiter.api.BeforeEach;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.test.context.ContextConfiguration;
24+
import org.springframework.test.context.junit.jupiter.SpringExtension;
25+
import org.springframework.vault.util.IntegrationTestSupport;
26+
import org.springframework.vault.util.RequiresVaultVersion;
27+
28+
import reactor.test.StepVerifier;
29+
30+
import static org.assertj.core.api.Assertions.*;
31+
32+
/**
33+
* Integration tests for {@link ReactiveVaultSysTemplate} through
34+
* {@link ReactiveVaultSysOperations}.
35+
*
36+
* @author Mark Paluch
37+
*/
38+
@ExtendWith(SpringExtension.class)
39+
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
40+
class ReactiveVaultSysTemplateIntegrationTests extends IntegrationTestSupport {
41+
42+
@Autowired
43+
ReactiveVaultOperations vaultOperations;
44+
45+
ReactiveVaultSysOperations adminOperations;
46+
47+
@BeforeEach
48+
void before() {
49+
this.adminOperations = this.vaultOperations.opsForSys();
50+
}
51+
52+
@Test
53+
@RequiresVaultVersion("0.6.1")
54+
void shouldReportHealth() {
55+
56+
this.adminOperations.health().as(StepVerifier::create).assertNext(health -> {
57+
assertThat(health.isInitialized()).isTrue();
58+
assertThat(health.isSealed()).isFalse();
59+
assertThat(health.isPerformanceStandby()).isFalse();
60+
assertThat(health.isRecoveryReplicationSecondary()).isFalse();
61+
assertThat(health.isStandby()).isFalse();
62+
}).verifyComplete();
63+
}
64+
65+
@Test
66+
void isInitializedShouldReturnTrue() {
67+
this.adminOperations.isInitialized().as(StepVerifier::create).expectNext(true).verifyComplete();
68+
}
69+
70+
}

0 commit comments

Comments
 (0)