Fix Mongo health indicators when using the strict V1 API
Closes gh-41101
This commit is contained in:
		
							parent
							
								
									60f8939e26
								
							
						
					
					
						commit
						31f967723d
					
				| 
						 | 
				
			
			@ -99,6 +99,7 @@ dependencies {
 | 
			
		|||
	testImplementation("org.springframework:spring-test")
 | 
			
		||||
	testImplementation("com.squareup.okhttp3:mockwebserver")
 | 
			
		||||
	testImplementation("org.testcontainers:junit-jupiter")
 | 
			
		||||
	testImplementation("org.testcontainers:mongodb")
 | 
			
		||||
	testImplementation("org.testcontainers:neo4j")
 | 
			
		||||
	testImplementation("org.testcontainers:testcontainers")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2022 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2024 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ public class MongoHealthIndicator extends AbstractHealthIndicator {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void doHealthCheck(Health.Builder builder) throws Exception {
 | 
			
		||||
		Document result = this.mongoTemplate.executeCommand("{ isMaster: 1 }");
 | 
			
		||||
		Document result = this.mongoTemplate.executeCommand("{ hello: 1 }");
 | 
			
		||||
		builder.up().withDetail("maxWireVersion", result.getInteger("maxWireVersion"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2022 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2024 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ public class MongoReactiveHealthIndicator extends AbstractReactiveHealthIndicato
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected Mono<Health> doHealthCheck(Health.Builder builder) {
 | 
			
		||||
		Mono<Document> buildInfo = this.reactiveMongoTemplate.executeCommand("{ isMaster: 1 }");
 | 
			
		||||
		Mono<Document> buildInfo = this.reactiveMongoTemplate.executeCommand("{ hello: 1 }");
 | 
			
		||||
		return buildInfo.map((document) -> up(builder, document));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2024 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.boot.actuate.mongo;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
 | 
			
		||||
import com.mongodb.ConnectionString;
 | 
			
		||||
import com.mongodb.MongoClientSettings;
 | 
			
		||||
import com.mongodb.MongoClientSettings.Builder;
 | 
			
		||||
import com.mongodb.ServerApi;
 | 
			
		||||
import com.mongodb.ServerApiVersion;
 | 
			
		||||
import com.mongodb.client.MongoClient;
 | 
			
		||||
import com.mongodb.client.MongoClients;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.testcontainers.containers.MongoDBContainer;
 | 
			
		||||
import org.testcontainers.junit.jupiter.Container;
 | 
			
		||||
import org.testcontainers.junit.jupiter.Testcontainers;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.actuate.data.mongo.MongoHealthIndicator;
 | 
			
		||||
import org.springframework.boot.actuate.health.Health;
 | 
			
		||||
import org.springframework.boot.actuate.health.Status;
 | 
			
		||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
 | 
			
		||||
import org.springframework.data.mongodb.core.MongoTemplate;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Integration tests for {@link MongoHealthIndicator}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Andy Wilkinson
 | 
			
		||||
 */
 | 
			
		||||
@Testcontainers(disabledWithoutDocker = true)
 | 
			
		||||
class MongoHealthIndicatorIntegrationTests {
 | 
			
		||||
 | 
			
		||||
	@Container
 | 
			
		||||
	static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
 | 
			
		||||
		.withStartupTimeout(Duration.ofMinutes(2));
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void standardApi() {
 | 
			
		||||
		Health health = mongoHealth();
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void strictV1Api() {
 | 
			
		||||
		Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build());
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Health mongoHealth() {
 | 
			
		||||
		return mongoHealth(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Health mongoHealth(ServerApi serverApi) {
 | 
			
		||||
		Builder settingsBuilder = MongoClientSettings.builder()
 | 
			
		||||
			.applyConnectionString(new ConnectionString(mongo.getConnectionString()));
 | 
			
		||||
		if (serverApi != null) {
 | 
			
		||||
			settingsBuilder.serverApi(serverApi);
 | 
			
		||||
		}
 | 
			
		||||
		MongoClientSettings settings = settingsBuilder.build();
 | 
			
		||||
		MongoClient mongoClient = MongoClients.create(settings);
 | 
			
		||||
		MongoHealthIndicator healthIndicator = new MongoHealthIndicator(new MongoTemplate(mongoClient, "db"));
 | 
			
		||||
		return healthIndicator.getHealth(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2023 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2024 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -42,24 +42,24 @@ class MongoHealthIndicatorTests {
 | 
			
		|||
		Document commandResult = mock(Document.class);
 | 
			
		||||
		given(commandResult.getInteger("maxWireVersion")).willReturn(10);
 | 
			
		||||
		MongoTemplate mongoTemplate = mock(MongoTemplate.class);
 | 
			
		||||
		given(mongoTemplate.executeCommand("{ isMaster: 1 }")).willReturn(commandResult);
 | 
			
		||||
		given(mongoTemplate.executeCommand("{ hello: 1 }")).willReturn(commandResult);
 | 
			
		||||
		MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
 | 
			
		||||
		Health health = healthIndicator.health();
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
			
		||||
		assertThat(health.getDetails()).containsEntry("maxWireVersion", 10);
 | 
			
		||||
		then(commandResult).should().getInteger("maxWireVersion");
 | 
			
		||||
		then(mongoTemplate).should().executeCommand("{ isMaster: 1 }");
 | 
			
		||||
		then(mongoTemplate).should().executeCommand("{ hello: 1 }");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void mongoIsDown() {
 | 
			
		||||
		MongoTemplate mongoTemplate = mock(MongoTemplate.class);
 | 
			
		||||
		given(mongoTemplate.executeCommand("{ isMaster: 1 }")).willThrow(new MongoException("Connection failed"));
 | 
			
		||||
		given(mongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed"));
 | 
			
		||||
		MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
 | 
			
		||||
		Health health = healthIndicator.health();
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.DOWN);
 | 
			
		||||
		assertThat((String) health.getDetails().get("error")).contains("Connection failed");
 | 
			
		||||
		then(mongoTemplate).should().executeCommand("{ isMaster: 1 }");
 | 
			
		||||
		then(mongoTemplate).should().executeCommand("{ hello: 1 }");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2024 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.boot.actuate.mongo;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
 | 
			
		||||
import com.mongodb.ConnectionString;
 | 
			
		||||
import com.mongodb.MongoClientSettings;
 | 
			
		||||
import com.mongodb.MongoClientSettings.Builder;
 | 
			
		||||
import com.mongodb.ServerApi;
 | 
			
		||||
import com.mongodb.ServerApiVersion;
 | 
			
		||||
import com.mongodb.reactivestreams.client.MongoClient;
 | 
			
		||||
import com.mongodb.reactivestreams.client.MongoClients;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.testcontainers.containers.MongoDBContainer;
 | 
			
		||||
import org.testcontainers.junit.jupiter.Container;
 | 
			
		||||
import org.testcontainers.junit.jupiter.Testcontainers;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.actuate.data.mongo.MongoReactiveHealthIndicator;
 | 
			
		||||
import org.springframework.boot.actuate.health.Health;
 | 
			
		||||
import org.springframework.boot.actuate.health.Status;
 | 
			
		||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
 | 
			
		||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Integration tests for {@link MongoReactiveHealthIndicator}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Andy Wilkinson
 | 
			
		||||
 */
 | 
			
		||||
@Testcontainers(disabledWithoutDocker = true)
 | 
			
		||||
class MongoReactiveHealthIndicatorIntegrationTests {
 | 
			
		||||
 | 
			
		||||
	@Container
 | 
			
		||||
	static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
 | 
			
		||||
		.withStartupTimeout(Duration.ofMinutes(2));
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void standardApi() {
 | 
			
		||||
		Health health = mongoHealth();
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void strictV1Api() {
 | 
			
		||||
		Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build());
 | 
			
		||||
		assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Health mongoHealth() {
 | 
			
		||||
		return mongoHealth(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Health mongoHealth(ServerApi serverApi) {
 | 
			
		||||
		Builder settingsBuilder = MongoClientSettings.builder()
 | 
			
		||||
			.applyConnectionString(new ConnectionString(mongo.getConnectionString()));
 | 
			
		||||
		if (serverApi != null) {
 | 
			
		||||
			settingsBuilder.serverApi(serverApi);
 | 
			
		||||
		}
 | 
			
		||||
		MongoClientSettings settings = settingsBuilder.build();
 | 
			
		||||
		MongoClient mongoClient = MongoClients.create(settings);
 | 
			
		||||
		MongoReactiveHealthIndicator healthIndicator = new MongoReactiveHealthIndicator(
 | 
			
		||||
				new ReactiveMongoTemplate(mongoClient, "db"));
 | 
			
		||||
		return healthIndicator.getHealth(true).block(Duration.ofSeconds(30));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2023 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2024 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ class MongoReactiveHealthIndicatorTests {
 | 
			
		|||
		Document buildInfo = mock(Document.class);
 | 
			
		||||
		given(buildInfo.getInteger("maxWireVersion")).willReturn(10);
 | 
			
		||||
		ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class);
 | 
			
		||||
		given(reactiveMongoTemplate.executeCommand("{ isMaster: 1 }")).willReturn(Mono.just(buildInfo));
 | 
			
		||||
		given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willReturn(Mono.just(buildInfo));
 | 
			
		||||
		MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(
 | 
			
		||||
				reactiveMongoTemplate);
 | 
			
		||||
		Mono<Health> health = mongoReactiveHealthIndicator.health();
 | 
			
		||||
| 
						 | 
				
			
			@ -59,8 +59,7 @@ class MongoReactiveHealthIndicatorTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	void testMongoIsDown() {
 | 
			
		||||
		ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class);
 | 
			
		||||
		given(reactiveMongoTemplate.executeCommand("{ isMaster: 1 }"))
 | 
			
		||||
			.willThrow(new MongoException("Connection failed"));
 | 
			
		||||
		given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed"));
 | 
			
		||||
		MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(
 | 
			
		||||
				reactiveMongoTemplate);
 | 
			
		||||
		Mono<Health> health = mongoReactiveHealthIndicator.health();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2023 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2024 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -80,14 +80,6 @@ class SampleSessionMongoApplicationTests {
 | 
			
		|||
		assertThat(sessions).hasSize(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void health() {
 | 
			
		||||
		ResponseEntity<String> entity = this.restTemplate
 | 
			
		||||
			.getForEntity("http://localhost:" + this.port + "/actuator/health", String.class);
 | 
			
		||||
		assertThat(entity.getBody()).contains("\"status\":\"UP\"");
 | 
			
		||||
		assertThat(entity.getBody()).contains("maxWireVersion");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String performLogin() {
 | 
			
		||||
		HttpHeaders headers = new HttpHeaders();
 | 
			
		||||
		headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue