diff --git a/integration-tests/application-server-integration-tests/pom.xml b/integration-tests/application-server-integration-tests/pom.xml index 65c26ec375..435438031f 100644 --- a/integration-tests/application-server-integration-tests/pom.xml +++ b/integration-tests/application-server-integration-tests/pom.xml @@ -129,27 +129,11 @@ 1.6 test - - org.mock-server - mockserver-client-java - 5.14.0 - test - com.networknt json-schema-validator ${version.json-schema-validator} test - - - - com.fasterxml.jackson.core - jackson-databind - - org.apache.commons diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AbstractServletContainerIntegrationTest.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AbstractServletContainerIntegrationTest.java index 935a33ab44..fba18d517b 100644 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AbstractServletContainerIntegrationTest.java +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AbstractServletContainerIntegrationTest.java @@ -36,9 +36,6 @@ import okhttp3.logging.HttpLoggingInterceptor; import org.junit.After; import org.junit.Test; -import org.mockserver.model.ClearType; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.MountableFile; @@ -59,9 +56,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import static co.elastic.apm.agent.report.IntakeV2ReportingEventHandler.INTAKE_V2_URL; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; /** * When you want to execute the test in the IDE, execute {@code mvn clean package} before. @@ -84,18 +79,17 @@ public abstract class AbstractServletContainerIntegrationTest { @Nullable private static final String AGENT_VERSION_TO_DOWNLOAD_FROM_MAVEN = null; - private static final MockServerContainer mockServerContainer; + private static final MockApmServerContainer mockServerContainer; private static final OkHttpClient httpClient; static { - mockServerContainer = new MockServerContainer() + mockServerContainer = new MockApmServerContainer() .withNetworkAliases("apm-server") - .waitingFor(Wait.forHttp(MockServerContainer.HEALTH_ENDPOINT).forStatusCode(200)) .withNetwork(Network.SHARED); if (JavaExecutable.isDebugging()) { - mockServerContainer.withLogConsumer(TestContainersUtils.createSlf4jLogConsumer(MockServerContainer.class)); + mockServerContainer.withLogConsumer(TestContainersUtils.createSlf4jLogConsumer(MockApmServerContainer.class)); } final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(logger::info); @@ -106,8 +100,6 @@ public abstract class AbstractServletContainerIntegrationTest { .build(); mockServerContainer.start(); - mockServerContainer.getClient().when(request(INTAKE_V2_URL)).respond(HttpResponse.response().withStatusCode(200)); - mockServerContainer.getClient().when(request("/")).respond(HttpResponse.response().withStatusCode(200)); } private final MockReporter mockReporter = new MockReporter(); @@ -150,7 +142,7 @@ protected AbstractServletContainerIntegrationTest(AgentTestContainer.AppServer c container .withNetwork(Network.SHARED) - .withEnv("ELASTIC_APM_SERVER_URL", "http://apm-server:1080") + .withEnv("ELASTIC_APM_SERVER_URL", "http://apm-server:8080") .withEnv("ELASTIC_APM_IGNORE_URLS", ignoreUrlConfig) .withEnv("ELASTIC_APM_REPORT_SYNC", "true") .withEnv("ELASTIC_APM_LOG_LEVEL", "DEBUG") @@ -247,7 +239,7 @@ public void testAllScenarios() throws Exception { } public void clearMockServerLog() { - mockServerContainer.getClient().clear(HttpRequest.request(), ClearType.LOG); + mockServerContainer.clearRecorded(); } public JsonNode assertTransactionReported(String pathToTest, int expectedResponseCode) { @@ -423,8 +415,7 @@ public List getEvents(String eventType) { try { final List events = new ArrayList<>(); final ObjectMapper objectMapper = new ObjectMapper(); - for (HttpRequest httpRequest : mockServerContainer.getClient().retrieveRecordedRequests(request(INTAKE_V2_URL))) { - final String bodyAsString = httpRequest.getBodyAsString(); + for (String bodyAsString : mockServerContainer.getRecordedRequestBodies()) { for (String ndJsonLine : bodyAsString.split("\n")) { final JsonNode ndJson = objectMapper.readTree(ndJsonLine); if (ndJson.get(eventType) != null) { diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockApmServerContainer.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockApmServerContainer.java new file mode 100644 index 0000000000..5198f70f8d --- /dev/null +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockApmServerContainer.java @@ -0,0 +1,88 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 + * + * http://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 co.elastic.apm.servlet; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; +import com.github.tomakehurst.wiremock.matching.UrlPattern; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +import static com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder.responseDefinition; +import static com.github.tomakehurst.wiremock.client.WireMock.any; + +public class MockApmServerContainer extends GenericContainer { + + private WireMock wireMock; + + public MockApmServerContainer() { + super("wiremock/wiremock:3.13.2"); + addExposedPorts(8080); + waitStrategy = Wait.forHealthcheck(); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + super.containerIsStarted(containerInfo); + wireMock = WireMock.create() + .host(getHost()) + .port(getMappedPort(8080)) + .build(); + + wireMock.register(any(UrlPattern.ANY).willReturn(responseDefinition().withStatus(200))); + } + + public List getRecordedRequestBodies() { + return wireMock.find(RequestPatternBuilder.newRequestPattern()) + .stream() + // wiremock provides the raw request body without any decompression so we have to do it explicitly ourselves + .map(action -> decompressZlib(action.getBody())) + .collect(Collectors.toList()); + } + + private static String decompressZlib(byte[] input) { + Inflater inflater = new Inflater(); + inflater.setInput(input); + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + while (!inflater.finished()) { + int count = inflater.inflate(buffer); + output.write(buffer, 0, count); + } + inflater.end(); + return output.toString(StandardCharsets.UTF_8); + } catch (DataFormatException e) { + throw new IllegalStateException(e); + } + } + + public void clearRecorded() { + wireMock.resetRequests(); + } + +} diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockServerContainer.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockServerContainer.java deleted file mode 100644 index 4ad98c69f5..0000000000 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/MockServerContainer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you 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 - * - * http://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 co.elastic.apm.servlet; - -import com.github.dockerjava.api.command.InspectContainerResponse; -import org.mockserver.client.MockServerClient; -import org.testcontainers.containers.GenericContainer; - -/** - * Acts as a mock for the APM-server. - */ -public class MockServerContainer extends GenericContainer { - - public static final String HEALTH_ENDPOINT = "/mockserver/status"; - private MockServerClient client; - - public MockServerContainer() { - super("mockserver/mockserver:5.14.0"); - addEnv("MOCKSERVER_LIVENESS_HTTP_GET_PATH", HEALTH_ENDPOINT); - addExposedPorts(1080); - } - - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo) { - super.containerIsStarted(containerInfo); - client = new MockServerClient(getContainerIpAddress(), getFirstMappedPort()); - } - - public MockServerClient getClient() { - return client; - } -} diff --git a/integration-tests/quarkus/pom.xml b/integration-tests/quarkus/pom.xml index 75b12aff3a..bd0ae4fbb3 100644 --- a/integration-tests/quarkus/pom.xml +++ b/integration-tests/quarkus/pom.xml @@ -37,7 +37,6 @@ import - com.fasterxml.jackson jackson-bom 2.14.1 @@ -70,18 +69,6 @@ 1.2 test - - org.testcontainers - mockserver - ${version.testcontainers} - test - - - org.mock-server - mockserver-client-java - 5.14.0 - test - com.jayway.jsonpath json-path diff --git a/integration-tests/quarkus/quarkus-jaxrs-base/src/test/java/co/elastic/apm/quarkus/jaxrs/AbstractQuarkusJaxRSTest.java b/integration-tests/quarkus/quarkus-jaxrs-base/src/test/java/co/elastic/apm/quarkus/jaxrs/AbstractQuarkusJaxRSTest.java index 649bcb6961..3ab6900606 100644 --- a/integration-tests/quarkus/quarkus-jaxrs-base/src/test/java/co/elastic/apm/quarkus/jaxrs/AbstractQuarkusJaxRSTest.java +++ b/integration-tests/quarkus/quarkus-jaxrs-base/src/test/java/co/elastic/apm/quarkus/jaxrs/AbstractQuarkusJaxRSTest.java @@ -19,59 +19,74 @@ package co.elastic.apm.quarkus.jaxrs; import co.elastic.apm.agent.test.AgentFileAccessor; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; import com.jayway.jsonpath.JsonPath; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.ClearType; -import org.mockserver.model.HttpRequest; import org.testcontainers.Testcontainers; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.MockServerContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import static com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder.responseDefinition; +import static com.github.tomakehurst.wiremock.client.WireMock.any; import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; public abstract class AbstractQuarkusJaxRSTest { - private static final int DEBUG_PORT = -1; // set to something else (e.g. 5005) to enable remote debugging of the quarkus app + // set to something else (e.g. 5005) to enable remote debugging of the quarkus app + private static final int DEBUG_PORT = -1; + // set to true to enable verbose container debug logging + private static final boolean DEBUG_LOG = false; - - private static MockServerContainer mockServer; - - private static MockServerClient mockServerClient; + private static final int APM_SERVER_PORT = 8080; + private static final String APM_SERVER_HOST = "apm-server"; + private static final int APP_PORT = 8080; + private static final String INTAKE_V2_EVENTS = "/intake/v2/events"; + private static GenericContainer mockApmServer; + private static WireMock wireMock; private static GenericContainer app; - @BeforeAll static void setUpAppAndApmServer() { Network network = Network.newNetwork(); - mockServer = new MockServerContainer(DockerImageName - .parse("mockserver/mockserver") - .withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion())) + mockApmServer = new GenericContainer<>("wiremock/wiremock:3.13.2") .withNetwork(network) - .withNetworkAliases("apm-server"); - mockServer.start(); - mockServerClient = new MockServerClient(mockServer.getHost(), mockServer.getServerPort()); - mockServerClient.when(request("/")).respond(response().withStatusCode(200).withBody(json("{\"version\": \"7.13.0\"}"))); - mockServerClient.when(request("/config/v1/agents")).respond(response().withStatusCode(403)); - mockServerClient.when(request("/intake/v2/events")).respond(response().withStatusCode(200)); + .withExposedPorts(APM_SERVER_PORT) + .waitingFor(Wait.forHealthcheck()) + .withNetworkAliases(APM_SERVER_HOST); + + if (DEBUG_LOG) { + mockApmServer.withLogConsumer(outputFrame -> System.out.print(outputFrame.getUtf8String())); + } + + mockApmServer.start(); + + wireMock = WireMock.create() + .host(mockApmServer.getHost()) + .port(mockApmServer.getMappedPort(APM_SERVER_PORT)) + .build(); + + wireMock.register(any(urlEqualTo("/")).willReturn(responseDefinition().withBody("{\"version\": \"7.13.0\"}").withStatus(200))); + wireMock.register(any(urlEqualTo("/config/v1/agents")).willReturn(responseDefinition().withStatus(403))); + wireMock.register(any(urlEqualTo(INTAKE_V2_EVENTS)).willReturn(responseDefinition().withStatus(200))); if (DEBUG_PORT > 0) { Testcontainers.exposeHostPorts(DEBUG_PORT); @@ -87,17 +102,23 @@ static void setUpAppAndApmServer() { .withCommand(cmd) .withCopyFileToContainer(MountableFile.forHostPath(AgentFileAccessor.getPathToJavaagent()), "/tmp/elastic-apm-agent.jar") .withCopyFileToContainer(MountableFile.forHostPath("target/quarkus-app"), "/srv/quarkus-app") - .withEnv("ELASTIC_APM_SERVER_URL", "http://apm-server:" + MockServerContainer.PORT) + .withEnv("ELASTIC_APM_SERVER_URL", "http://" + APM_SERVER_HOST + ":" + APM_SERVER_PORT) .withEnv("ELASTIC_APM_REPORT_SYNC", "true") + .withEnv("ELASTIC_APM_CLOUD_PROVIDER", "none") .withEnv("ELASTIC_APM_DISABLE_METRICS", "true") .withEnv("ELASTIC_APM_LOG_LEVEL", "DEBUG") .withEnv("ELASTIC_APM_APPLICATION_PACKAGES", "co.elastic.apm.quarkus.jaxrs") .withEnv("ELASTIC_APM_ENABLE_EXPERIMENTAL_INSTRUMENTATIONS", "true") - .withEnv("QUARKUS_HTTP_PORT", "8080") + .withEnv("QUARKUS_HTTP_PORT", Integer.toString(APP_PORT)) .withNetwork(network) - .withExposedPorts(8080) + .withExposedPorts(APP_PORT) .waitingFor(Wait.forLogMessage(".*Installed features.*", 1)) - .dependsOn(mockServer); + .dependsOn(mockApmServer); + + if (DEBUG_LOG) { + app.withLogConsumer(outputFrame -> System.out.print(outputFrame.getUtf8String())); + } + app.start(); } @@ -106,29 +127,31 @@ public static void destroyContainers() { if (app != null) { app.stop(); } - if (mockServer != null) { - mockServer.stop(); + if (mockApmServer != null) { + mockApmServer.stop(); } } @AfterEach void clearMockServerLog() { - mockServerClient.clear(request(), ClearType.LOG); + wireMock.resetRequests(); } private static List> getReportedTransactions() { - return Arrays.stream(mockServerClient.retrieveRecordedRequests(request("/intake/v2/events"))) - .map(HttpRequest::getBodyAsString) - .flatMap(s -> Arrays.stream(s.split("\r?\n"))) + return wireMock.find(RequestPatternBuilder.newRequestPattern().withUrl(INTAKE_V2_EVENTS)).stream() + // wiremock provides the raw request body without any decompression so we have to do it explicitly ourselves + .map(action -> decompressZlib(action.getBody())) + .flatMap(requestBody -> Arrays.stream(requestBody.split("\r?\n"))) .map(JsonPath::parse) .flatMap(dc -> ((List>) dc.read("$[?(@.transaction)].transaction")).stream()) .collect(Collectors.toList()); } @Test - public void greetingShouldReturnDefaultMessage() { + public void + greetingShouldReturnDefaultMessage() { given() - .baseUri("http://" + app.getHost() + ":" + app.getMappedPort(8080)) + .baseUri("http://" + app.getHost() + ":" + app.getMappedPort(APP_PORT)) .when() .get("/") .then() @@ -146,4 +169,21 @@ public void greetingShouldReturnDefaultMessage() { assertThat((String) JsonPath.read(transaction, "$.context.service.framework.name")).isEqualTo("JAX-RS"); assertThat((String) JsonPath.read(transaction, "$.context.service.framework.version")).isEqualTo("2.0.1.Final"); } + + private static String decompressZlib(byte[] input) { + Inflater inflater = new Inflater(); + inflater.setInput(input); + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + while (!inflater.finished()) { + int count = inflater.inflate(buffer); + output.write(buffer, 0, count); + } + inflater.end(); + return output.toString(StandardCharsets.UTF_8); + } catch (DataFormatException e) { + throw new IllegalStateException(e); + } + } } diff --git a/pom.xml b/pom.xml index d64f0a0970..0ed73a9dd6 100644 --- a/pom.xml +++ b/pom.xml @@ -76,10 +76,6 @@ ${maven.compiler.testTarget} false - true true