Commit 86e21786 by Johannes Edmeier

Fix flaky tests

Force the connections in wiremock tests to be closed. See https://github.com/tomakehurst/wiremock/issues/485
parent 3eae8ab1
......@@ -78,11 +78,11 @@ public class AdminServerWebConfiguration {
@Bean
@ConditionalOnMissingBean
public de.codecentric.boot.admin.server.web.reactive.InstancesProxyController instancesProxyController(
InstanceRegistry instanceRegistry, InstanceWebClient instanceWebClient) {
InstanceRegistry instanceRegistry,
InstanceWebClient instanceWebClient) {
return new de.codecentric.boot.admin.server.web.reactive.InstancesProxyController(
adminServerProperties.getContextPath(), adminServerProperties.getInstanceProxy().getIgnoredHeaders(),
instanceRegistry, instanceWebClient, adminServerProperties.getMonitor().getReadTimeout());
instanceRegistry, instanceWebClient);
}
@Bean
......@@ -110,8 +110,7 @@ public class AdminServerWebConfiguration {
public InstancesProxyController instancesProxyController(InstanceRegistry instanceRegistry,
InstanceWebClient instanceWebClient) {
return new InstancesProxyController(adminServerProperties.getContextPath(),
adminServerProperties.getInstanceProxy().getIgnoredHeaders(), instanceRegistry, instanceWebClient,
adminServerProperties.getMonitor().getReadTimeout());
adminServerProperties.getInstanceProxy().getIgnoredHeaders(), instanceRegistry, instanceWebClient);
}
@Bean
......
......@@ -21,12 +21,12 @@ import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.services.InstanceRegistry;
import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
import de.codecentric.boot.admin.server.web.client.exception.ResolveEndpointException;
import io.netty.handler.timeout.ReadTimeoutException;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.net.ConnectException;
import java.net.URI;
import java.time.Duration;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
......@@ -55,21 +55,17 @@ public class AbstractInstancesProxyController {
private final InstanceRegistry registry;
private final InstanceWebClient instanceWebClient;
private final Set<String> ignoredHeaders;
private final Duration readTimeout;
private final ExchangeStrategies strategies = ExchangeStrategies.withDefaults();
public AbstractInstancesProxyController(String adminContextPath,
Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient,
Duration readTimeout) {
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
this.ignoredHeaders = Stream.concat(ignoredHeaders.stream(), Arrays.stream(HOP_BY_HOP_HEADERS))
.map(String::toLowerCase)
.collect(Collectors.toSet());
this.registry = registry;
this.instanceWebClient = instanceWebClient;
this.realRequestMappingPath = adminContextPath + REQUEST_MAPPING_PATH;
this.readTimeout = readTimeout;
}
protected Mono<ClientResponse> forward(String instanceId,
......@@ -104,17 +100,17 @@ public class AbstractInstancesProxyController {
}
}
return headersSpec.exchange().timeout(this.readTimeout, Mono.fromSupplier(() -> {
return headersSpec.exchange().onErrorResume(ReadTimeoutException.class, ex -> Mono.fromSupplier(() -> {
log.trace("Timeout for Proxy-Request for instance {} with URL '{}'", instance.getId(), uri);
return ClientResponse.create(HttpStatus.GATEWAY_TIMEOUT, strategies).build();
})).onErrorResume(ResolveEndpointException.class, ex -> Mono.fromSupplier(() -> {
log.trace("No Endpoint found for Proxy-Request for instance {} with URL '{}'", instance.getId(), uri);
return ClientResponse.create(HttpStatus.NOT_FOUND, strategies).build();
})).onErrorResume(IOException.class, ex -> Mono.fromSupplier(() -> {
log.trace("Error for Proxy-Request for instance {} with URL '{}' errored", instance.getId(), uri, ex);
log.trace("Proxy-Request for instance {} with URL '{}' errored", instance.getId(), uri, ex);
return ClientResponse.create(HttpStatus.BAD_GATEWAY, strategies).build();
})).onErrorResume(ConnectException.class, ex -> Mono.fromSupplier(() -> {
log.trace("Error for Proxy-Request for instance {} with URL '{}' timed out", instance.getId(), uri, ex);
log.trace("Connect for Proxy-Request for instance {} with URL '{}' failed", instance.getId(), uri, ex);
return ClientResponse.create(HttpStatus.BAD_GATEWAY, strategies).build();
}));
}
......
......@@ -63,14 +63,14 @@ public class InstanceWebClient {
}
public WebClient instance(Mono<Instance> instance) {
return webClient.mutate()//
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))//
return webClient.mutate()
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))
.build();
}
public WebClient instance(Instance instance) {
return webClient.mutate()//
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))//
return webClient.mutate()
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))
.build();
}
......@@ -78,8 +78,8 @@ public class InstanceWebClient {
Duration readTimeout,
WebClientCustomizer customizer) {
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis())//
.compression(true)//
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis())
.compression(true)
.afterNettyContextInit(ctx -> {
ctx.addHandlerLast(
new ReadTimeoutHandler(readTimeout.toMillis(), TimeUnit.MILLISECONDS));
......
......@@ -23,7 +23,6 @@ import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.time.Duration;
import java.util.Set;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
......@@ -41,10 +40,8 @@ import org.springframework.web.util.UriComponentsBuilder;
public class InstancesProxyController extends AbstractInstancesProxyController {
public InstancesProxyController(String adminContextPath,
Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient,
Duration readTimeout) {
super(adminContextPath, ignoredHeaders, registry, instanceWebClient, readTimeout);
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
super(adminContextPath, ignoredHeaders, registry, instanceWebClient);
}
@RequestMapping(path = REQUEST_MAPPING_PATH, method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH, RequestMethod.DELETE, RequestMethod.OPTIONS})
......
......@@ -26,7 +26,6 @@ import reactor.core.publisher.Mono;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.time.Duration;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -57,10 +56,8 @@ public class InstancesProxyController extends AbstractInstancesProxyController {
public InstancesProxyController(String adminContextPath,
Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient,
Duration readTimeout) {
super(adminContextPath, ignoredHeaders, registry, instanceWebClient, readTimeout);
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
super(adminContextPath, ignoredHeaders, registry, instanceWebClient);
}
@ResponseBody
......
......@@ -31,7 +31,14 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
......@@ -54,11 +61,8 @@ public abstract class AbstractInstancesProxyControllerIntegrationTest {
private static ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<Map<String, Object>>() {
};
@ClassRule
public static WireMockClassRule wireMock = new WireMockClassRule(options().dynamicPort()
.asynchronousResponseEnabled(true)
.asynchronousResponseThreads(10)
.jettyAcceptors(10)
.containerThreads(20));
public static WireMockClassRule wireMock = new WireMockClassRule(
options().dynamicPort().extensions(new ConnectionCloseExtension()));
private static WebTestClient client;
private static String instanceId;
......@@ -87,12 +91,12 @@ public abstract class AbstractInstancesProxyControllerIntegrationTest {
wireMock.stubFor(get(urlEqualTo("/mgmt/timeout")).willReturn(ok().withFixedDelay(10000)));
wireMock.stubFor(get(urlEqualTo("/mgmt/test")).willReturn(
ok("{ \"foo\" : \"bar\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(
ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
wireMock.stubFor(post(urlEqualTo("/mgmt/test")).willReturn(ok()));
wireMock.stubFor(delete(urlEqualTo("/mgmt/test")).willReturn(
serverError().withBody("{\"error\": \"You're doing it wrong!\"}")
.withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(
ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
instanceId = registerInstance(managementUrl);
}
......@@ -139,7 +143,10 @@ public abstract class AbstractInstancesProxyControllerIntegrationTest {
.uri("/instances/{instanceId}/actuator/test", instanceId)
.accept(ACTUATOR_V2_MEDIATYPE)
.exchange()
.expectStatus().isEqualTo(HttpStatus.OK).expectBody(String.class).isEqualTo("{ \"foo\" : \"bar\" }");
.expectStatus()
.isEqualTo(HttpStatus.OK)
.expectBody(String.class)
.isEqualTo("{ \"foo\" : \"bar\" }");
client.post()
.uri("/instances/{instanceId}/actuator/test", instanceId)
......@@ -216,3 +223,20 @@ public abstract class AbstractInstancesProxyControllerIntegrationTest {
//@formatter:on
}
}
//Force the connections to be closed...
//see https://github.com/tomakehurst/wiremock/issues/485
class ConnectionCloseExtension extends ResponseTransformer {
@Override
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
return Response.Builder.like(response)
.headers(HttpHeaders.copyOf(response.getHeaders())
.plus(new HttpHeader("Connection", "Close")))
.build();
}
@Override
public String getName() {
return "ConnectionCloseExtension";
}
}
......@@ -34,8 +34,8 @@ import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import static de.codecentric.boot.admin.server.web.client.InstanceExchangeFilterFunctions.ATTRIBUTE_ENDPOINT;
import static io.netty.handler.codec.http.HttpHeaders.Values.APPLICATION_JSON;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
public class InstanceExchangeFilterFunctionsTest {
private static final DefaultDataBufferFactory BUFFER_FACTORY = new DefaultDataBufferFactory();
......@@ -72,8 +72,7 @@ public class InstanceExchangeFilterFunctionsTest {
ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test"))
.attribute(ATTRIBUTE_ENDPOINT, "test")
.build();
ClientResponse response = ClientResponse.create(HttpStatus.OK)
.header(CONTENT_TYPE, APPLICATION_JSON)
ClientResponse response = ClientResponse.create(HttpStatus.OK).header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.body(Flux.just(ORIGINAL))
.build();
......
......@@ -7,4 +7,4 @@ spring:
enabled: false
logging:
level:
de.codecentric: DEBUG
\ No newline at end of file
de.codecentric: DEBUG
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment